【入門】Titanic号乗客の生死を予想しよう！(4)

# はじめまして
始めまして！株式会社 Panta Rheiの「かず」と申します。[pandasista](https://x.com/sotsogprinciple) という名前でXをやっております。普段はデータをねりねりしたりCEOをしたりしています。

![IMG_0587.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540865/dd51afda-2b66-ccba-f810-4efdac1860d4.jpeg)
本記事は4年前に勉強会で用いた記事になります！
ぜひいいねを押してってください🙌

# 導入

本記事の構成は4部となっており
1. <B>[Titanicデータセットと探索的データ分析(EDA)](https://qiita.com/realmadridmarcelo/items/f64431a25af3c87a3552)</B>
1. <B>[探索的データ分析(EDA)の続きと特徴量エンジニアリング](https://qiita.com/realmadridmarcelo/items/9cd476d839fb9c36554b)</B>
1. <B>[特徴量エンジニアリングの続きとAIモデルへの適用](https://qiita.com/realmadridmarcelo/items/507b659a830e2c3928d6)</B>
1. <B>[djangoを用いた簡単なTitanic乗客生死予測アプリケーションの作成](https://qiita.com/realmadridmarcelo/items/ec5ca723ec935ee90d75)</B>

という内容でお届けしています。
本記事は4章目！作成したAIモデルをdjangoに乗せて予測アプリケーションを作ってみるという試みです！

以下の記事を読んだ後に本Titanicシリーズを読むと段階的に理解ができます。
1. [【実装】　ボストンの住宅価格推測AIを作ろう【前編】](https://qiita.com/realmadridmarcelo/items/0bfcbde3e4092ea5c86e)
1. [【実装】　ボストンの住宅価格推測AIを作ろう【後編】](https://qiita.com/realmadridmarcelo/items/0ac4490de2007e43f65c)


# Kaggleのその先
精度はともかく、前章でKaggleにアップロードすることができました。サブミットすることが一番偉いです。

しかし、AIモデルを作ってKaggleに提出したはいいものの、実際のアプリケーションでどのように動くかでイメージしてみたい！って方はそれなりにいると思います。本記事では、この「タイタニック号」モデルを使って<B>簡単なWebアプリ作成</B>に挑戦してもらいます！

# 設計要件
「<B>乗客の特徴を入力</B>」
トップページで乗客の情報をフィールドに入力させましょう。

「<B>乗客の生死を予測して出力</B>」
トップページにPOSTでアクセスして予測を出力しましょう。

# Pickle
AIモデルをWebに実装する上でしばしば出てくるモジュールの一つに「<B>Pickle</B>」です。<B>インスタンスやオブジェクトをそのままファイルとして保存</B>することができます。

値を入力してもらった後に、そこからtrain_dataを前処理して標準化してclf.fitして最後に入力データに対してclf.predictしてその値を返す、では時間がかかりすぎてしまいます。

pickleはその学習済clfをファイルにしてしまい、web上ではそれを呼び出すだけで予測値を出せる、というふうにしたいです。さて実装していきましょう。

```python:INPUT
import pickle

with open('LinearSVC.pickle', 'wb') as f:
    pickle.dump(clf, f)
```

これをjupyter上で実行すると同じディレクトリに'LinearSVC.pickle'というファイルができたと思います。これはpythonやその環境特有のバイナリファイルでpythonのpickleでしか中身を見ることができません。今度はこれを読み出してみましょう。

```python:INPUT:
with open('LinearSVC.pickle', 'rb') as f:
    clf_pickle = pickle.load(f)

clf_pickle
```


```python:OUTPUT
LinearSVC(C=10, class_weight='balanced', max_iter=10000, random_state=0)
```

呼び出すことに成功しました。これをDjangoのアプリケーションディレクトリに移動するので、一旦はこのままにしておきましょう。同様に標準化インスタンスもpickle化しておきます。

```python:INPUT
with open('LinearSVC_SC.pickle', 'wb') as f:
    pickle.dump(sc, f)
```

jupyterのホーム画面に以下の様に保存されていたら成功です。
<img width="1333" alt="スクリーンショット 2020-05-29 16.57.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540865/51a5404b-a885-9370-75ef-e163e6035796.png">
これで、web上でAIを呼び出した時にいちいちtrain_dataでclfを学習する必要がなくなりました。

#仮想環境を作る/django起動
AnacondaNavigatorで仮想環境を作ってください。名前は「Titanic」とかで大丈夫です。そしてその仮想環境のターミナルで以下の様に入力してください。

```python:INPUT
conda install django
conda install pandas
conda install scikit-learn
```

これが終了したらAnaconda上からdjangoプロジェクトを起動しましょう。djangoプロジェクトを作成するディレクトリに移動してください。

```terminal
django-admin startproject titanic
```

※実は筆者は仮想環境をanacondaではなくvirtualenvで用意しているのでスクショは載せれなかったです。ご容赦くださいmm

プロジェクトが作成できたらターミナルに以下の様に入力してください。

```terminal
cd titanic
```


```terminal
python manage.py startapp titanicai
```

ここら辺はdjangoある程度分かる前提でささっといきます。これでアプリケーションを作りました。そのあとはVSCodeを開いて、titanicプロジェクトを開きましょう。

まずは、titanic/urls.pyに行って以下の様に書き換えましょう。

```python:INPUT
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('titanicai/', include('titanicai.urls'))  # 書き加えたところ
]
```

さらにtitanic/settings.pyの以下の部分に以下の文字を加えてください。
![スクリーンショット 2020-05-29 17.47.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540865/65f4fbbe-6557-49b6-516c-facdeaab9ec6.png)
INSTALLED_APPSのリストに'titanicai'を追加するだけです。

またtitanic/titanicaiのディレクトリに以下の名前のファイルを作成してください。

- forms.py
- ai.py
- templates/titanicai/index.html
  (templatesフォルダを作成してその下にtitanicaiフォルダを作成してさらにその下にindex.htmlというファイルを作る)
- static/titanicai/css/style.css
(staticフォルダ作成してそのしその下にtitanicaiフォルダを作成してさらにその下にcssフォルダを作成して最後にcss.styleというファイルを作る)

さて、ここからdjangoを一気にいじっていきます！まずhtmlから。

```html:templates/titanicai/index.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>{{title}}</title>
    <link rel='stylesheet' type='text/css' href='{% static "titanicai/css/style.css" %}' />
</head>
<body>
    <h1>{{title}}</h1>
    <p>{{message|safe}}</p>
    <table>
    <form action="{% url 'index' %}" method="post">
        {% csrf_token %}
        {{ form.as_table }}
        <tr><td></td><td><input type="submit" value="predict"></td></tr>
    </form>
    </table>
</body>
</html>
```

シンプルなhtmlです。cssを読み込んで、csrf_tokenをformの前に置いて、djangoのformをhtmlのなかに組み込んでいます。次にcssです。

```css:static/titanicai/css/style.css
body {
    color:gray;
    font-size:16pt;
}

h1 {
    color:red;
    opacity:0.2;
    font-size:60pt;
    margin-top:-20px;
    margin-bottom:0px;
    text-align:right;
}

p {
    margin:10px;

}

a {
    color:blue;
    text-decoration: none;
}
```

お気持ち程度のCSSですね。筆者はフロントエンドのセンスがなくデザインも下手です(断言)。よって込み入ったhtml/CSSを書くととんでもないUIになるという理由で、今回は簡素なCSSにしてあります。(<B>各人の得意分野によって協力し合うことが大切ってことですね！()</B>)

さて次はforms.pyを書き換えていきましょう。例によってdjango.formsクラスを継承していきます。

```python:forms.py
from django import forms

class TitanicForm(forms.Form):
    Id = forms.IntegerField(label='Id')
    Pclass = forms.IntegerField(label='Pclass', required=True)
    Name = forms.CharField(label='Name', required=False)
    Sex = forms.ChoiceField(label='Sex', choices=[(1, 'male'), (0, 'female')], required=True)
    Age = forms.IntegerField(label='Age', required=False)
    SibSp = forms.IntegerField(label='SibSp', required=True)
    Parch = forms.IntegerField(label='Parch', required=True)
    Ticket = forms.CharField(label='Ticket',required=False)
    Fare = forms.FloatField(label='Fare', required=False)
    Cabin = forms.CharField(label='Cabin', required=False)
    Embarked = forms.ChoiceField(label='Embarked', choices=[('S', 'S'), ('C', 'C'), ('Q', 'Q')])
```

これで、タイタニック乗客の性質を入力するフォームが完成しました。続いてtitanic/urls.pyをいじっていきましょう。

```python:titanicai/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path('', views.index, name='index'),
]
```

今回は複数ページを用意するわけではなくトップページを書き換えるだけなのでpathは一つだけです。どんどんいきましょう。続いてviews.pyです。

```python:titanicai/views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import TitanicForm
from .ai import AiModels

def index(request):
    params = {
        'title': 'タイタニックLinearSVCによる予想',
        'message': 'タイタニック号の乗客の特性を入力してください',
        'form': TitanicForm()
    }

    if (request.method == 'POST'):
        data = [[
            request.POST['Id'],
            request.POST['Pclass'],
            request.POST['Name'],
            request.POST['Sex'],
            request.POST['Age'],
            request.POST['SibSp'],
            request.POST['Parch'],
            request.POST['Ticket'],
            request.POST['Fare'],
            request.POST['Cabin'],
            request.POST['Embarked']
            ]]

        params['message'] = 'PassengerId' + request.POST['Id'] + \
            '<br>Pclass: ' + request.POST['Pclass'] + \
            '<br>Name: ' + request.POST['Name'] + \
            '<br>Sex: ' + request.POST['Sex'] + \
            '<br>Age: ' + request.POST['Age'] + \
            '<br>SibSp: ' + request.POST['SibSp'] + \
            '<br>Parch; ' + request.POST['Parch'] + \
            '<br>Ticket: ' + request.POST['Ticket'] + \
            '<br>Fare: ' + request.POST['Fare'] + \
            '<br>Cabin: ' + request.POST['Cabin'] + \
            '<br>Embarked: ' + request.POST['Embarked'] + \
            '<br>なら乗客の生死は…！！！   ' + \
            '<br>' + str(AiModels(data))

        params['form'] = TitanicForm(request.POST)
    return render(request, 'titanicai/index.html', params)
```

viewsでどんなことをするかを書いています。POSTで特性を入力されたら異なるviewを返す様に関数を設計しています。本来ならPostとgetで異なるClassを用意するところですが今回は簡単のため一つの関数で条件分岐しています。

最後にai.pyをいじっていきます。その前に事前準備として先ほどpickle化したファイルをこのアプリの」中に移動してきましょう。
![スクリーンショット 2020-05-29 18.01.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540865/f83ce6ac-83a9-2356-35aa-5b822a176082.png)
こんな感じになってたら成功です。「移動」でもコピぺでもカットアンドペーストでも大丈夫です。とりあえず持ってきましょう。次にコードです。

```python:titanicai/ai.py
import pickle
import pandas as pd



def AiModels(data):

    with open('titanicai/LinearSVC.pickle', 'rb') as f:
        clf_pickle = pickle.load(f)
    
    with open('titanicai/LinearSVC_SC.pickle', 'rb') as f:
        sc_pickle = pickle.load(f)
    
    _df = pd.DataFrame(data, columns=['PassengerId', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp','Parch', 'Ticket', 'Fare', 'Cabin', 'Embarked'])
    _df = _df.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1)  # 不要カラムの削除
    _df['Age'] = _df['Age'].fillna(28.0)  # Ageカラムの欠損値埋め
    _df['Pclass'] = pd.Categorical(_df['Pclass'], categories=[1, 2, 3])
    _df['Embarked'] = pd.Categorical(_df['Embarked'], categories=['C', 'Q', 'S'])
    _df['Sex'] = pd.Categorical(_df['Sex'], categories=['female', 'male'])
    _df = pd.get_dummies(_df, columns=['Sex'], drop_first=True)
    _df = pd.get_dummies(_df, columns=['Pclass', 'Embarked'])

    X_std = sc_pickle.transform(_df)  #標準化
    answer = clf_pickle.predict(X_std)  #jupyterで作った学習済モデルで予測

    return answer[0]
```

はい、では実際にターミナル上に```python manage.py runserver```でサーバを起動して```http://127.0.0.1:8000/titanicai/```にアクセスしてどんな感じになるか見てみましょう。

<img width="1440" alt="スクリーンショット 2020-05-29 18.08.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540865/82f5993f-c252-d394-d53a-43d6ee319574.png">

こんな感じになれば成功です。実際に値を代入してみて、予測結果がどうなるかを見てみてください。

以上で簡単なAIのweb導入の入門としようと思います。

# Titanic号乗客の生死を予想しよう！の最後に

AIの作り方から簡単なWeb実装まで一気通貫に実装してみました。
現在はDeepでポン、と言われるように深層学習が一般的です。
またOpenAIをはじめとした生成系AIのAPIを活用することもよくあります。
その技術を深く知って活用するためにも、本入門記事が皆様のお役に立てれば幸いです。

株式会社 Panta Rhei CEO かず

# 紹介
[株式会社 Panta Rhei](https://www.pantarhei.co.jp)では「Everything Analysable」を標榜とし、世の中にあるあらゆる定性的な対象をAIによって定量化する事業をおこなっております。
AIに召し上がっていただく綺麗なデータを作る、データフローを整形するなどの課題や、そもそものAIの内製化等、挑戦したい試みがあればぜひご一報ください！