• トップ
  • ブログ一覧
  • 【第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でアイデアを形にするレシピ

     

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    メディアチーム
    メディアチーム
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background