
【第2回】ResponderとKerasを使って機械学習Webアプリケーションを作ってみた【ネットワーク作成編】
2021.12.20
第2回~ResponderとKerasで機械学習アプリケーションを作りたい!~
この第2回記事は、「ResponderとKerasを使って機械学習Webアプリケーションを作ってみる」という連載記事になります。
「なぜこの連載を始めたか」というと理由は3つあります。
- 1. Responderが面白く、他にもWebアプリを作ってみたい!
- 2. Responderの非同期処理を使ってみたい!
- 3. せっかくのPythonのフレームワークなので機械学習と組み合わせたい!
特に、2番目の「非同期処理」については、以前連載していた「Responderを使ってDjangoチュートリアルをやってみた」の記事では紹介しきれませんでした。
連載終了後、「機械学習と相性が良いのでは!?」と思い立ち、「Responder」と「機械学習」を絡めた記事を書くことを決めました。
簡単なアプリケーションではありますが、Responderのさらに詳しい使い方が分かっていただける記事になるはずです!
まずは第1回をお読みください
ネットワークを作成する
今回は前回、未実装で終わった class CreateController から作成していきます。
こんなものを作ります
この記事で作成するページは、以下のページです。

ネットワークをユーザが簡単に作成できるようなUIを目指します。
そのため、JavaScript で、自由にネットワークの層を追加し、モデルを構築していきます。

CreateControllerを作る
それでは、早速作っていきましょう!
ネットワーク作成ページでは、トップページと同じように、GETのみの実装です。
処理としては簡単で、「http://127.0.0.1:5042/create/yokuwakaran_dataset」など、「想定外のURLへの対処(404エラー)」と「必要なデータをビューに渡す」といった形です。
実装
したがって、以下のように実装しました。
ネットワーク作成では、データセットによって入力層と出力層のニューロン数は固定なので、コントローラ側で判断してしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class CreateController: async def on_get(self, req, resp, dataset): if dataset not in ['mnist', 'iris', 'wine']: api.redirect(resp, '/404') return title = 'ネットワークを作成【' + dataset + '】' # 入力サイズ input_length = 784 if dataset == 'mnist' else 4 if dataset == 'iris' else 13 # 出力サイズ(クラス数) output_length = 10 if dataset == 'mnist' else 3 resp.html = api.template('create.html', dataset=dataset, input_length=input_length, output_length=output_length, title=title) |
404エラーページの作成
先に、404エラーページを作成しておきましょう。
まずは、urls.py に以下を追記します。
1 | api.add_route('/404', NotFoundController) |
次に、簡単にコントローラも記述します。
1 2 3 4 | class NotFoundController: async def on_get(self, req, resp): title = '404 Not Found' resp.html = api.template('404.html', title=title) |
あとは、ビューページを作るだけで、先程の404エラー処理は完了です。
1 2 3 4 5 6 7 8 9 10 11 | {% extends "layout.html" %} {% block content %} <br> <h1>404 Error: Not Found</h1> <hr> <p>申し訳ありません。お探しのページは見つかりませんでした。</p> <br> <a href="/" class="btn btn-primary">トップページへ戻る</a> {% endblock %} |
createビューを作る
コントローラはできたので、ビューを作ります。
ここが本アプリケーションの大事なページです。
前提条件
まず、前提条件として
1. 入力層と出力層は固定
2. 中間層はある程度自由に追加可能
3. パラメータの調整もある程度可能
にします。
「1」と「3」は、Responder と htmlのフォームでどうにかなりますが、「2」についてはブラウザ側で処理しなければならず、Python は関与できません。
そこで、JavaScript とうまく連携させましょう!
まずはHTMLで作成可能な部分を作る
「1」と「3」は、HTMLで作成可能ですので、まずは作ってしまいましょう。
コントローラ側で入力層および出力層のニューロン数はビューに渡しているので、それを展開するだけです。
またJavaScript部分は、まだ実装していませんが、中間層ニューロンを追加するボタンだけは先に作ってしまいましょう。
複数同じようなボタンを配置するので、Jinja2 のフィルタに登録してしまいましょう!
1 2 3 4 5 6 7 8 9 | # jinja_env.py に追記 def fc_filter(neurons): """ 全結合中間層の追加フィルタ :param neurons: :return: """ neurons = str(neurons) return '<button class="btn btn-secondary btn-sm" onclick="add_fc(' + neurons + ')">' + neurons + ' neurons</button>' |
1 2 3 4 5 6 7 8 9 10 | # urls.py に追記 api.jinja_env.filters.update( static=jinja_env.static_filter, image=jinja_env.image_filter, css=jinja_env.css_filter, script=jinja_env.script_filter, badge=jinja_env.badge_filter, badge_active=jinja_env.badge_active_filter, fc=jinja_env.fc_filter, # フィルタを追加 ) |
次に、ビュー(tenplates/create.html)を作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | {% extends "layout.html" %} {% block content %} <br> <h1>ネットワークを作成しましょう</h1> <hr> <a href="/" class="btn btn-primary">もどる</a> <br> <br> {% autoescape false %} {{ 'データセットを選択' | badge }} {{ 'ネットワークを作成' | badge_active }} {{ '学習' | badge }} {{ '結果' | badge }} <br><br> <p> 選択したデータセット :<br> {{ dataset }} </p> <p> 全結合層を追加する :<br> {{ 10 | fc }} {{ 50 | fc }} {{ 100 | fc }} {{ 300 | fc }} {{ 500 | fc }} {{ 1000 | fc }} {{ 2000 | fc }} {{ 3000 | fc }} <a href="" class="btn btn-danger btn-sm">設定取り消し</a> </p> <br> <br> <hr> <form action="/learn/{{ dataset }}" method="post"> <div class="row"> <!-- ここからネットワーク作成情報 --> <div class="col-md-8"> <div class="text-center"> <h4>現在のネットワーク構成</h4> <div class="card" style="display: inline-block"> <div class="card-body"> Input : {{ input_length }} </div> </div> <input type="hidden" name="input" value="{{ input_length }}"> <br>|<br> <div id="add_layer"><!-- ここに中間層が追加される --></div> <div class="card" style="display: inline-block"> <div class="card-body"> Output (Class size) : {{ output_length }} </div> </div> <input type="hidden" name="output" value="{{ output_length }}"> </div> </div> <!-- ここからパラメータ入力フォーム --> <div class="col-md-4"> <div class="text-center"> <h4>パラメータ設定</h4> <p>学習回数 : epoch = <input type="text" name="epoch" size="4" maxlength="4" value="10"></p> <p>初期学習率 : η = <input type="text" name="eta" size="6" maxlength="6" value="0.01"></p> <p> 学習率減衰 : dec = <input type="text" name="decay" size="8" maxlength="8" value="1e-6"> <br> (各学習ごとに学習率を減衰させる定数) </p> <p> Momentum : α = <input type="text" name="momentum" size="6" maxlength="6" value="0.9"> <br> (Momentum SGDの慣性項)</p> </div> </div> </div> <br><br><br> <div class="text-center"> <button href="#" type="submit" class="btn btn-primary">このネットワークで学習を行う</button> </div> </form> {% endautoescape %} {% endblock %} |
これで、ネットワーク作成ページの雛形は完成です。
JavaScriptで中間層を自由に追加する
そうしたら、先程書いたビューの <div id="add_layer"><!-- ここに中間層が追加される --></div> の部分にボタン( {{ 500 | fc }} など)が押されたときに中間層が追加されるようにします。
関数名は、 add_fc(neurons) です。
早速、先程の create.html の {% endblock %} の前に以下のような JavaScript を追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <script> function add_fc(neurons) { var div_element = document.createElement("div"); div_element.innerHTML = '<div class="card bg-secondary text-white" style="display: inline-block">' + '<div class="card-body">' + 'Full Connection : ' + neurons + '</div>' + '</div>' + '<input type="hidden" name="fc[]" value="' + neurons + '"><br>|<br>'; var parent_object = document.getElementById("add_layer"); parent_object.appendChild(div_element); } </script> |
処理内容は、そんなに難しくありません。
「add_layer」ID を持つ divタグを探して、文字列を .innerHTML で追加しているだけです。
確認
実際に動かしてみると、うまく中間層がボタンによって追加されていることが分かります。

あとは、ここでデータを送信し、Python側で機械学習を行います。
学習部のルーティング
先ほどのビューでPOST送信されたデータは、「/learn/{dataset}」というURLで受け取り、「LearnController」で処理することにします。
まずは、ルーティングしましょう。
1 2 | # urls.py 追記 api.add_route('/learn/{dataset}', LearnController) |
1 2 3 4 5 6 7 | class LearnController: async def on_get(self, req, resp, dataset): api.redirect(resp, '/404') return async def on_post(self, req, resp, dataset): pass # POST処理 |
GETで受け取った場合は対応しないので、404エラーページにリダイレクトします。
POST処理については、本アプリケーションで重要な部分になりますので、実装は次回にします!(中途半端ですみません)
第3回へつづく!
今回は、ネットワーク作成ページを作成しました。
今回は、ほとんどビューのコードが占めてしまいましたが、ここまでくると Responder のルーティング作業も慣れてくるかと思います。
次回は、いよいよアプリの核を作っていきます!
Responder のバックグラウンド処理も利用していきますので、お楽しみに!
関連記事
こちらの記事もオススメ!
Kerasのオススメ本
書いた人はこんな人

- 「好きを仕事にするエンジニア集団」の(株)ライトコードです!
ライトコードは、福岡、東京、大阪の3拠点で事業展開するIT企業です。
現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。
いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。
システム開発依頼・お見積もり大歓迎!
また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」「WEBディレクター」を積極採用中です!
インターンや新卒採用も行っております。
以下よりご応募をお待ちしております!
https://rightcode.co.jp/recruit
ライトコードの日常12月 1, 2023ライトコードクエスト〜東京オフィス歴史編〜
ITエンタメ10月 13, 2023Netflixの成功はレコメンドエンジン?
ライトコードの日常8月 30, 2023退職者の最終出社日に密着してみた!
ITエンタメ8月 3, 2023世界初の量産型ポータブルコンピュータを開発したのに倒産!?アダム・オズボーン