【第2回】ResponderとKerasを使って機械学習Webアプリケーションを作ってみた【ネットワーク作成編】
IT技術
第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エラー)」と「必要なデータをビューに渡す」といった形です。
実装
したがって、以下のように実装しました。
ネットワーク作成では、データセットによって入力層と出力層のニューロン数は固定なので、コントローラ側で判断してしまいます。
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 のバックグラウンド処理も利用していきますので、お楽しみに!
関連記事
こちらの記事もオススメ!
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...
2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...
Kerasのオススメ本
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit