エンジニアMのAI学習記録(2018年4月分)

2018年04月06日
こちらは、Webに関連するエンジニア向けの記事です。
当社のWeb関連技術の公開と採用活動のために掲載しています。

(2018年4月20日更新)

みなさんこんにちは、エンジニアのMです。
これは当社の技術やAIに興味のある方に向けた日誌になります。
今月も毎週更新していきます、よろしくお願いします。

4月2日~4月6日

■ HTML+CSS+Javascript+αの勉強

FastTextを使った検索の精度向上が行き詰まりつつあるので、Webに関する技術について勉強しつつ、アイデアが降りてくるのを待つことにした。

参考書

  • フロントエンドエンジニア養成読本(技術評論社・ISBN 978-4-7741-6578-3)
  • スマートフォンのためのHTML5アプリケーション開発ガイド(ソシム・ISBN 978-4-88337-728-2)

参考リンク(一部)

  • 脱jQueryのためにしたこと – ICS MEDIA [リンク]
  • 第1回 TypeScriptの概要:TypeScriptで学ぶJavaScript入門 – @IT [リンク]
  • Reactを使うとなぜjQueryが要らなくなるのか – Qiita [リンク]
  • Angular 2/4が狭量で遅すぎる理由 | POSTD [リンク]
  • javascript – Chrome can’t load web worker – Stack Overflow [リンク]
  • 【jQuery入門】jQueryにおける「$(ドルマーク)」の意味 | UX MILK [リンク]
  • 出来る限り短く説明するReact.js入門 – Qiita [リンク]
  • JavaScriptでファイル操作!? File APIを使いこなそう:連載:人気順に説明する初めてのHTML5開発 – @IT [リンク]
  • 多忙な JavaScript 開発者のための ECMAScript 6 ガイド、第 1 回: 新しい JavaScript 内での変数宣言およびその他の新仕様の紹介 [リンク]
  • Javascriptのbind関数と部分適用 〜 JSおくのほそ道 #015 – Qiita [リンク]
  • JavaScriptの「this」は「4種類」?? – Qiita [リンク]
  • JavaScriptを捨ててPythonでWebフロントエンドを書いてみよう! – Qiita [リンク]
  • もう迷わない!人気JavaScriptフレームワーク、ライブラリー、ツール総まとめ – WPJ [リンク]
  • もうjQueryには頼らない!素のJavaScriptでDOMを操作するための基礎知識 – WPJ [リンク]

しれっと「JavaScriptを捨てて」というリンクが混じっているが、これはPythonの知識を使い回せないかと検索して出てきたものである。
Python → JavaScriptの変換を経由するため、動作が非常に遅くもっさりしているのが残念だった。世の中そんなに甘くない。
詰め込みすぎて Vanilla.js(素のJS)の記法が曖昧になりつつあるが、書いていくうちに慣れるだろう。
どちらかというとHTMLとCSSを覚えるほうが時間がかかるかもしれない。タグの種類、使い方、きれいなレイアウトの作り方などはまた別の本を参考にする必要がありそうだ。

■ サンプルコードを改変してみた

「HTML5アプリケーション開発ガイド」にあった15パズルのコードを改変して、

  • ライツアウト – Wikipedia [リンク]

を作ってみた。




<style> * { margin:0; padding:0; position:absolute; }<br /> #title<br /> {<br /> background-color: blue; color: white;<br /> padding: 3px; font-size: 14px;<br /> width: 314px; height: 14px;<br /> }<br /> #game_canvas<br /> {<br /> top: 20px; width: 320px; height: 320px;<br /> }<br /> #shaffle_btn {<br /> font-size: 20px;<br /> top: 350px;<br /> margin: 8px; padding: 4px;<br /> width: 100px;<br /> }<br /> </style>   <div id="title">Lights</div> <canvas id="game_canvas" width="320" height="320"></canvas> <button id="shaffle_btn">シャッフル</button> <script type="text/javascript"> function $(id){ return document.getElementById(id); } // 設定 //var main_image = "hako.png"; var panels = []; var cell_w = 320 / 5; var cell_h = 320 / 5; var pos_array = [[-1, 0],[1, 0],[0, -1],[0, 1]]; // 上下左右の座標 // Canvasのコンテキストを取得する var canvas = document.getElementById("game_canvas"); var con = canvas.getContext("2d"); // 初期化 window.onload = function(){ shafflePanel(); $("shaffle_btn").onclick = function(){ shafflePanel() }; } // 配置を初期化する function shafflePanel(){ for(var i = 0; i < 25; i++){ panels[i] = 0; } for(var j = 0; j < 25; j++){ var r = Math.floor(Math.random() * 25); changePanelColor((r % 5), (Math.floor(r / 5))) } drawPanels(); } // パネルを描画する function drawPanels(){ for(var i = 0; i < 25; i++){ var tx = (i % 5) * cell_w; var ty = Math.floor(i / 5) * cell_h; con.beginPath(); switch(panels[i]){ case 1: con.fillStyle = "#EEEE00"; break; default: con.fillStyle = "#333333"; break; } con.strokeStyle = "#111111"; con.fillRect(tx, ty, cell_w, cell_h); con.lineWidth = 2; con.rect(tx, ty, cell_w, cell_h); con.stroke(); } } // タッチイベントに反応する canvas.ontouchstart = function(e){ if (e.touches.length > 0){ var t = e.touches[0]; checkPanelXY(t.clientX, t.clientY); } e.preventDefault(); }; canvas.onmousedown = function(e){ checkPanelXY(e.clientX, e.clientY); }; function checkPanelXY(sx,sy){ // タップされたパネルを取得する var col = Math.floor(sx / cell_w); var row = Math.floor((sy-20) / cell_h); changePanelColor(col, row) } function changePanelColor(col, row){ var no = row * 5 + col; panels[no] = (panels[no] + 1) % 2 for(var i = 0; i < pos_array.length; i++){ var row2 = pos_array[i][0] + row; var col2 = pos_array[i][1] + col; if(col2 >= 0 && row2 >= 0 && col2 < 5 && row2 < 5){ var no2 = row2 * 5 + col2 panels[no2] = (panels[no2] + 1) % 2 } } drawPanels(); } </script>

当初は15パズルを改変するなら「箱入り娘」はどうかと思ったが、穴が2箇所あって移動先が不明になるケースが発生するのでやめた。
(サンプルコードにある穴の隣をクリックして移動させる方法では、どちらに移動するのか分からないということが起きる。)
DOMの操作をするほうが勉強になるかとは思ったが、それはまた来週以降とする。


4月9日~4月13日

■ FastTextで少しだけ検証

JavaScriptの勉強やReactの勉強は一旦優先順位を下げ、Djangoを勉強中。
サーバー側の話は覚えることが多くて非常に難しい。
一つずつクリアしていこうと思う。
その息抜きというわけでもないが、今後に向けて少しFastTextで実験した。

■ pyfasttextとgensim版fasttextで精度が違う?

以前pyfasttextとgensimのそれぞれでFastTextを実行した際に、gensimのほうが精度が低いということがあった。
パラメータもきちんと揃えてあったはず…だったのだが、確認したらsample(出現頻度の閾値)のオーダーが一つ違っていた。
これを揃えてやったところほぼ同じ精度になった。

■ なぜgensimでFastText?

pyfasttextは、どうもモデルとベクトルデータの両方を出力するのを止められないようで、毎回700MB~2GB程度のファイルを保存する。
毎回10秒~30秒程待たされるのがどうにも気になったので、gensimを使えないかと思った。pyfasttextでファイル出力を抑制できるならそれで良かったのだが…。
gensimだとなぜか精度が悪い問題を解決できたので、gensimを使っていく方向で良さそうだ。

■ sampleを調整してみる

pyfasttextのデフォルトは 0.0001 となっている。(gensimのデフォルトは 0.001)
min_count=1であってもここで切られる単語があるわけなので、以前にした「全単語を含めたほうが良い」、というような説明は誤りであった。各種パラメータについて今後はもっと気を配ろうと思う。
実際に試したところ、min_count=1 かつ sample=0.00001 の時、精度は著しく悪化した。
逆にgensimのデフォルトである0.001でも悪化していたので、今回の例では sample=0.0001 が良い設定だったわけだ。

■ 結論

パラメータはよく確認するべきである。特にデフォルト値は見落としがちなので気をつけたい。
pyfasttextとgensimで精度の差は見られなくなったので、用途に合わせて使い分けられそうだ。gensimならWord2VecやDoc2Vecも実行できるので、色々試すならこちらのほうが便利。ファイルも必要なところだけ保存できる。


4月16日~4月20日

■ Djangoについて学ぶ

  • さぁ始めましょう。 | Django documentation | Django [リンク]
  • 適当に作る python Djnago REST frameworkで作る初めてのAPI – Qiita [リンク]
  • Django REST Frameworkを使って爆速でAPIを実装する – Qiita [リンク]

Djangoのチュートリアルと、より実際の運用に近い例を通じて基本的な使い方について学ぶ。
今回はサーバーを動作させるのでWSL上のUbuntuではなく、VirtualBox上のCentOS7を使う。
以下、途中で行き詰まったりした点についてのみ記述していく。

環境は以下の通り。

  • CentOS: 7.4.1708
  • Django: 2.0.4
  • django-filter: 1.1.0
  • djangorestframework: 3.8.2
  • djangorestframework-jwt: 1.11.0
  • MySQL: 5.7.21
  • Python: 3.6.2
  • nginx: 1.13.12

■ MySQLのインストールができない

データベースとしてSQLiteではなく、MySQLを使うためにインストールをするのだがこれが上手くいかず。

Error: Package: mysql-community-server-5.7.21-1.el6.x86_64 (mysql57-community)
Requires: libsasl2.so.2()(64bit)

  • MySQL – centos7にてmysqlがインストールができない(114533)|teratail [リンク]

問題点は2つ。

  • el6(CentOS6)のパッケージを入れようとしている。
  • 「libsasl2.so.2」が見つからない

この質問に対する回答に従って解決。
yumのキャッシュをクリアする方法は今後も使う機会がありそうだ。

■ Django-filter


REST_FRAMEWORK = { # フィルター追加 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',), # ページネーション追加 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': 3, 'DEFAULT_RENDERER_CLASSES': ( 'diary.renderers.UTF8CharsetJSONRenderer', ) }

Diary用API実装の途中でフィルターを実装する場面があるのだが、そのままではうまくいかず。

'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),

と、Django-filterのドキュメントに従うことでうまく動作した。

そもそもDjangoのバージョンが違うので、「urlpatterns」の指定で「url」ではなく「path」を使ったりとここ以前にも読み替えている箇所がある。
そのままで通ったところでも、今では古い書き方になっている箇所があるかもしれない。