• トップ
  • ブログ一覧
  • 【第2回】ResponderとKerasを使って機械学習Webアプリケーションを作ってみた【ネットワーク作成編】
  • 【第2回】ResponderとKerasを使って機械学習Webアプリケーションを作ってみた【ネットワーク作成編】

    広告メディア事業部広告メディア事業部
    2019.11.05

    IT技術

    第2回~ResponderとKerasで機械学習アプリケーションを作りたい!~

    この第2回記事は、「ResponderとKerasを使って機械学習Webアプリケーションを作ってみる」という連載記事になります。

    「なぜこの連載を始めたか」というと理由は3つあります。

    1. 1. Responderが面白く、他にもWebアプリを作ってみたい!
    2. 2. Responderの非同期処理を使ってみたい!
    3. 3. せっかくのPythonのフレームワークなので機械学習と組み合わせたい!

    特に、2番目の「非同期処理」については、以前連載していた「Responderを使ってDjangoチュートリアルをやってみた」の記事では紹介しきれませんでした。

    連載終了後、「機械学習と相性が良いのでは!?」と思い立ち、「Responder」と「機械学習」を絡めた記事を書くことを決めました。

    簡単なアプリケーションではありますが、Responderのさらに詳しい使い方が分かっていただける記事になるはずです!

    まずは第1回をお読みください

    featureImg2019.10.29【第1回】ResponderとKerasを使って機械学習Webアプリケーションを作ってみた【大枠作成編】第1回~ResponderとKerasで機械学習アプリケーションを作りたい!~今、人気に火が着きつつある Python...

    ネットワークを作成する

    今回は前回、未実装で終わったclass CreateController から作成していきます。

    こんなものを作ります

    この記事で作成するページは、以下のページです。

    ネットワークをユーザが簡単に作成できるようなUIを目指します。

    そのため、JavaScript で、自由にネットワークの層を追加し、モデルを構築していきます。

    CreateControllerを作る

    それでは、早速作っていきましょう!

    ネットワーク作成ページでは、トップページと同じように、GETのみの実装です。

    処理としては簡単で、「http://127.0.0.1:5042/create/yokuwakaran_dataset」など、「想定外のURLへの対処(404エラー)」と「必要なデータをビューに渡す」といった形です。

    実装

    したがって、以下のように実装しました。

    ネットワーク作成では、データセットによって入力層と出力層のニューロン数は固定なので、コントローラ側で判断してしまいます。

    1class CreateController:
    2    async def on_get(self, req, resp, dataset):
    3
    4        if dataset not in ['mnist', 'iris', 'wine']:
    5            api.redirect(resp, '/404')
    6            return
    7
    8        title = 'ネットワークを作成【' + dataset + '】'
    9        # 入力サイズ
    10        input_length = 784 if dataset == 'mnist' else 4 if dataset == 'iris' else 13
    11        # 出力サイズ(クラス数)
    12        output_length = 10 if dataset == 'mnist' else 3
    13
    14        resp.html = api.template('create.html',
    15                                 dataset=dataset,
    16                                 input_length=input_length,
    17                                 output_length=output_length,
    18                                 title=title)

    404エラーページの作成

    先に、404エラーページを作成しておきましょう。

    まずは、urls.py に以下を追記します。

    1api.add_route('/404', NotFoundController)

    次に、簡単にコントローラも記述します。

    1class NotFoundController:
    2    async def on_get(self, req, resp):
    3        title = '404 Not Found'
    4        resp.html = api.template('404.html', title=title)

    あとは、ビューページを作るだけで、先程の404エラー処理は完了です。

    1{% extends "layout.html" %}
    2
    3{% block content %}
    4<br>
    5<h1>404 Error: Not Found</h1>
    6<hr>
    7<p>申し訳ありません。お探しのページは見つかりませんでした。</p>
    8<br>
    9<a href="/" class="btn btn-primary">トップページへ戻る</a>
    10
    11{% endblock %}

    createビューを作る

    コントローラはできたので、ビューを作ります。

    ここが本アプリケーションの大事なページです。

    前提条件

    まず、前提条件として

    1. 入力層と出力層は固定
    2. 中間層はある程度自由に追加可能
    3. パラメータの調整もある程度可能

    にします。

    「1」と「3」は、Responder と htmlのフォームでどうにかなりますが、「2」についてはブラウザ側で処理しなければならず、Python は関与できません

    そこで、JavaScript とうまく連携させましょう!

    まずはHTMLで作成可能な部分を作る

    「1」と「3」は、HTMLで作成可能ですので、まずは作ってしまいましょう。

    コントローラ側で入力層および出力層のニューロン数はビューに渡しているので、それを展開するだけです。

    またJavaScript部分は、まだ実装していませんが、中間層ニューロンを追加するボタンだけは先に作ってしまいましょう。

    複数同じようなボタンを配置するので、Jinja2 のフィルタに登録してしまいましょう!

    1# jinja_env.py に追記
    2def fc_filter(neurons):  
    3    """
    4    全結合中間層の追加フィルタ
    5    :param neurons: 
    6    :return: 
    7    """
    8    neurons = str(neurons)
    9    return '<button class="btn btn-secondary btn-sm" onclick="add_fc(' + neurons + ')">' + neurons + ' neurons</button>'
    1# urls.py に追記
    2api.jinja_env.filters.update(
    3    static=jinja_env.static_filter,
    4    image=jinja_env.image_filter,
    5    css=jinja_env.css_filter,
    6    script=jinja_env.script_filter,
    7    badge=jinja_env.badge_filter,
    8    badge_active=jinja_env.badge_active_filter,
    9    fc=jinja_env.fc_filter,  # フィルタを追加
    10)

    次に、ビュー(tenplates/create.html)を作ります。

    1{% extends "layout.html" %}
    2
    3{% block content %}
    4<br>
    5<h1>ネットワークを作成しましょう</h1>
    6<hr>
    7<a href="/" class="btn btn-primary">もどる</a>
    8<br>
    9<br>
    10{% autoescape false %}
    11{{ 'データセットを選択' | badge }}
    12{{ 'ネットワークを作成' | badge_active }}
    13{{ '学習' | badge }}
    14{{ '結果' | badge }}
    15
    16
    17<br><br>
    18
    19<p>
    20    選択したデータセット :<br>
    21    {{ dataset }}
    22</p>
    23<p>
    24    全結合層を追加する :<br>
    25    {{ 10 | fc }}
    26    {{ 50 | fc }}
    27    {{ 100 | fc }}
    28    {{ 300 | fc }}
    29    {{ 500 | fc }}
    30    {{ 1000 | fc }}
    31    {{ 2000 | fc }}
    32    {{ 3000 | fc }}
    33    <a href="" class="btn btn-danger btn-sm">設定取り消し</a>
    34</p>
    35<br>
    36<br>
    37<hr>
    38<form action="/learn/{{ dataset }}" method="post">
    39    <div class="row">
    40        
    41        <!--   ここからネットワーク作成情報     -->
    42        <div class="col-md-8">
    43            <div class="text-center">
    44                <h4>現在のネットワーク構成</h4>
    45                <div class="card" style="display: inline-block">
    46                    <div class="card-body">
    47                        Input : {{ input_length }}
    48                    </div>
    49                </div>
    50                <input type="hidden" name="input" value="{{ input_length }}">
    51
    52                <br>|<br>
    53
    54                <div id="add_layer"><!-- ここに中間層が追加される --></div>
    55
    56                <div class="card" style="display: inline-block">
    57                    <div class="card-body">
    58                        Output (Class size) : {{ output_length }}
    59                    </div>
    60                </div>
    61                <input type="hidden" name="output" value="{{ output_length }}">
    62
    63            </div>
    64        </div>
    65        
    66        <!--    ここからパラメータ入力フォーム    -->
    67        <div class="col-md-4">
    68            <div class="text-center">
    69                <h4>パラメータ設定</h4>
    70                <p>学習回数 : epoch = <input type="text" name="epoch" size="4" maxlength="4" value="10"></p>
    71                <p>初期学習率 : η = <input type="text" name="eta" size="6" maxlength="6" value="0.01"></p>
    72                <p>
    73                    学習率減衰 : dec = <input type="text" name="decay" size="8" maxlength="8" value="1e-6">
    74                    <br>
    75                    (各学習ごとに学習率を減衰させる定数)
    76                </p>
    77                <p>
    78                    Momentum : α = <input type="text" name="momentum" size="6" maxlength="6" value="0.9">
    79                    <br>
    80                    (Momentum SGDの慣性項)</p>
    81            </div>
    82        </div>
    83        
    84    </div>
    85    <br><br><br>
    86    <div class="text-center">
    87        <button href="#" type="submit" class="btn btn-primary">このネットワークで学習を行う</button>
    88    </div>
    89</form>
    90
    91{% endautoescape %}
    92
    93{% endblock %}

    これで、ネットワーク作成ページの雛形は完成です。

    JavaScriptで中間層を自由に追加する

    そうしたら、先程書いたビューの<div id="add_layer"><!-- ここに中間層が追加される --></div> の部分にボタン({{ 500 | fc }} など)が押されたときに中間層が追加されるようにします。

    関数名は、add_fc(neurons) です。

    早速、先程の create.html の{% endblock %} の前に以下のような JavaScript を追記します。

    1<script>
    2    function add_fc(neurons)
    3    {
    4        var div_element = document.createElement("div");
    5        div_element.innerHTML = '<div class="card bg-secondary text-white" style="display: inline-block">' + 
    6                                    '<div class="card-body">' +
    7                                        'Full Connection : ' + neurons +
    8                                    '</div>' +
    9                                '</div>' +
    10                                '<input type="hidden" name="fc[]" value="' + neurons + '"><br>|<br>';
    11        var parent_object = document.getElementById("add_layer");
    12        parent_object.appendChild(div_element);
    13    }
    14</script>

    処理内容は、そんなに難しくありません。

    「add_layer」ID を持つ divタグを探して、文字列を.innerHTML で追加しているだけです。

    確認

    実際に動かしてみると、うまく中間層がボタンによって追加されていることが分かります。

    あとは、ここでデータを送信し、Python側で機械学習を行います

    学習部のルーティング

    先ほどのビューでPOST送信されたデータは、「/learn/{dataset}」というURLで受け取り、「LearnController」で処理することにします。

    まずは、ルーティングしましょう。

    1# urls.py 追記
    2api.add_route('/learn/{dataset}', LearnController)
    1class LearnController:
    2    async def on_get(self, req, resp, dataset):
    3        api.redirect(resp, '/404')
    4        return
    5
    6    async def on_post(self, req, resp, dataset):
    7        pass  # POST処理

    GETで受け取った場合は対応しないので、404エラーページにリダイレクトします。

    POST処理については本アプリケーションで重要な部分になりますので、実装は次回にします!(中途半端ですみません)

    第3回へつづく!

    今回は、ネットワーク作成ページを作成しました。

    今回は、ほとんどビューのコードが占めてしまいましたが、ここまでくると Responder のルーティング作業も慣れてくるかと思います。

    次回は、いよいよアプリの核を作っていきます!

    Responder のバックグラウンド処理も利用していきますので、お楽しみに!

    関連記事

    featureImg2019.11.14【第3回】ResponderとKerasを使って機械学習Webアプリケーションを作ってみた【非同期処理編】第3回~ResponderとKerasで機械学習アプリケーションを作りたい!~この記事は、「ResponderとKer...

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
    featureImg2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...
    featureImg2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...

    Kerasのオススメ本

    直感 Deep Learning ―Python×Kerasでアイデアを形にするレシピ
    直感 Deep Learning ―Python×Kerasでアイデアを形にするレシピ

     

    広告メディア事業部

    広告メディア事業部

    おすすめ記事