データサイエンティスト特論が良かった

はじめに

データサイエンティスト特論という大学院の授業を取りました。久しぶりに学びの多い授業だったので忘れないうちに学んだことなどをまとめておこうと思います。

どんな講義?

その名の通りデータサイエンスする、土曜に3時限ぶっ通し×5回やるタイプの集中講義。学外に募集をかけてるらしく、電通大の大学院生以外にも社会人(多い)や他大生の人もいました。 前半の2回はKaggleを使って課題を解くタイプのデータ分析、後半の3回は4人程度のチームになって、企業の実際のデータを使ってビジネスアイデアを考えるというもの。 どんなデータだったかというのは提供していただいている企業からの守秘義務があるので詳しく話せませんが、顧客のデータから

何をやった?

守秘義務があるので詳しくは言えませんが、大まかに言うと要因分析をしました。 いくつか手法を試す中で、自分がロジスティック回帰を、もう一人の方がXGBoostを試す、という役割でした。

学んだこと

分析技術的なこと

  • 学習データに連続量でないもの(ex: idや日付など)が含まれている場合はone-hot encodingする必要がある。
    • ただし、種類が多すぎる場合にone-hotするとメモリが足りなくなることがあるので、その場合は値を切り捨てるor減らすテクニックが必要
    • ロジスティック回帰の場合はもちろん、XGBoostの場合にも必要
  • ロジスティック回帰の場合は変数の重みに差が出ないよう、標準化する必要がある。
    • 最初、正規化してしまっていた。なぜ標準化にする必要があるかというと、データの中にとびぬけて大きいパラメータがあった場合にそれに引っ張られて他のパラメータ群の相対的な差異が小さすぎるようになってしまうから。
  • ロジスティック回帰で変数重要度を測る場合は、係数の大きさで比較すればいい
    • 得られた係数には負の値のものもあり扱いに迷ったが、モデルが正の方向に傾くのに寄与する要因が知りたかったので単純に大きい順に見ればいいと知った。(教えてもらった)
  • ロジスティック回帰はフィッティングしすぎないために正則化すると良い
    • L1正則化とL2正則化があり、前者は余分な説明変数を省く効果が、後者はモデルの過学習を防ぐ効果がある
    • scikit-learnに付属するロジスティック回帰のメソッドはデフォルトでL2正則化になっている罠(TAの人も愚痴ってた)
    • ちなみにこの「正則化」は「正則行列」の「正則」とは意味が違う

ビジネスアイデアのこと

自分の勝手な判定ですが、大きく以下の3種類のアプローチがあったと思います。

  1. 客観的情報からの仮説→アイデア→アイデアの検証

    • 客観的情報というのは、企業から渡されたデータではなく、例えばその企業の業績情報や、自分達がユーザとなってみての感想などです。
    • 例えばですが、「客足が減っている」や「メニューが高く感じる」といった情報があったときに、まずその原因について「近隣に競合店舗ができたからだ」などと仮説を立てて見る方法です。
    • その仮説から問題を解決するためのアイデアを出します。
    • データは、そのアイデアを実現するための「材料」として使っていました。
    • 一番多かったです。
  2. イデア→アイデアの検証

    • 自分たちがユーザになってみる前段階はあるものの、基本的に前調査はそれぐらいでアイデアを出してしまい、その検証をしてみるという流れでした。
    • このチームもデータは、そのアイデアを実現するための「材料」として使っていました。
    • 2チームぐらいありました。
  3. データ分析→アイデア(自分たち)

    • まず与えられたデータから何が言えるんだろうか、という考えで、最初からデータ分析してみます。
    • 自分たちのチームと、最後にデモがあったTAの方がこの方法でした。
    • ただ、TAの方は「ユーザの分布」など様々な観点(守秘義務があるから詳しくは言えない...)からデータを見ていたのが違いだと思います。
    • 自分たちは「変数重要度」しか見ていませんでした。
    • 変数重要度を正しく求めるためにAUC(モデルの精度)を上げる、というプロセスはよかったと思いますが、変数重要度だけに囚われてしまっていたのが残念だったとかなと思います。
    • データをざっと見て分からないときには「極端な理想の世界を考える」ことから課題を見つける場合もあるそうです。

1や2は研究などでもよくやる手法で、一番着実だと思いますが、2の方法もいいと思いました。 2の方法のメリットは他の方法では思いつかないアイデアが思いつくことだと思います。アイデアのもとになるのはデータだけでなく、その人の感性などもあるからです。 実際、企業の方から「仕事ではこういうアイデアは中々出ない。でも面白そうだから実際に採用したいね」という声をもらってました。

また、自分たちは3の方法だったので、他のチームの発表を聞いてデータの二面性というものを感じました。 1や2の方法はデータの「材料」としての面を、3の方法はデータの「分析対象」としての面を利用していたと思います。 「この材料で何ができるだろうか?」という考えと「こう分析されたからこうしたらどうだろう?」というアプローチの違いで生まれるアイデアも変わってくるのが面白いと思いました。

まとめ

土曜日に3時限ぶっ続けで授業はつらかった一方、実際に手を動かす講義だったこともあり学ぶものは多かったし楽しかったです。 研究や就職後にも役立つ内容だと思うので、これを見て興味を持った方は受けてみることをおすすめします。 (ちなみに社会人が取ると30万円くらいかかるそうです。)

電通大生にThree.jsを布教したい

こちらはUEC Advent Calendar8日目の記事です。

前日の記事はこちら

TL;DR

環境構築がめっちゃ楽なThree.jsを広めたい&ミクさんを動かしてみたので見てほしい

はじめに

前回の記事はKeiuお さんによる「Hack U 2019 FUKUOKA」について。自分も先日JPHacksというハッカソンに参加してきたのでわかるんですが、他の大学の技術力高い学生の発表が聞けたりして楽しいんですよね。技術好きな全電通大生ハッカソン参加しよう。 次回はCra2yPierr0t さんによる「マジカル☆FPGA」。いったいどんな感じにマジカルなのか。わくわくするタイトルです。

電通大生の皆さんは、ゲームを作ってみたいと思ったことがある方が多いのではないかと思います。 ゲームを作ろうと思ったとき、どんなツールを思い浮かべますか?Unity?Unreal?でも、僕はThree.js。 どうしておすすめするかというと、環境構築がめっちゃ楽だからです。

プログラムもシンプルで理解しやすいと思います(コナミ)。 あとJavascriptだけで動くのでGithub pagesとかで公開できたりとお手軽です。

環境構築

  1. サンプルプロジェクトをクローンしてきます。
    • プロジェクトといってもhtmlが一枚あるだけなのでコピペでも大丈夫です。
  2. Pythonをインストールします。
    • インストール方法についてはこちらで詳しく解説されています。
    • 3系にしましょう。
  3. プロジェクトのあるディレクトリでpython -m http.server 8000コマンドを入力します。
    • これは、プロジェクトのディレクトリをドキュメントルートとして起動する、単純なhttpサーバーです。
  4. ブラウザでlocalhost:8000を開く。
    • 下のような画面が開くはずです。

これにて開発の準備(環境構築)は終了です。お疲れ様でした!

簡単でしょう?Unityで挫折した自分でもこれなら大丈夫でした()

改造してみる

index.htmlを見ると、init()とanimate()という二つの関数が順番に動いていることがわかると思います。

  1. init()でシーン・物体・ライト・カメラ・レンダラなどを用意
  2. animate()を再帰的に呼び出して、レンダリングを繰り返す

という流れになっています。

では、今表示されている立方体がキー入力で回転するように改造してみましょう。 JavaScriptではwindow.document.onkeydownというオブジェクトにキー入力があったときの動作を指定できます。 なのでanimate()内で

    // 箱を回転させる
    window.document.onkeydown = function(event){
        if (event.key === 'Enter') box.rotation.x += 0.01;
    };

のようにして、「Enter」キーが入力されたときに、boxのx軸回転を加算してあげるようにします。 これがanime()実行毎(毎フレーム)動くので、「Enter」キーを入力すれば回転するようになるはずです。

ミクさんを歩かせてみた

応用例?としてMMDモデルのミクさんを歩かせてみたデモを貼っておきます。 https://one-color-low.github.io/LoadAvatar/

基本は上でやった原理と同じです。(実はこれに2週間ぐらいかかったのはないしょ)

WASDで動きます。F12すればコードが見れると思います。

おわりに

しょぼいサンプルを載せましたが、公式にはもっと面白い作成がいっぱいあります。 今回は環境構築の簡単さに注目しましたが、他にもThree.jsでやるメリットってあると思います。Webで3Dを表示できることを活かして、3Dモデルで商品紹介ページを作るとか、 自分のHPをオープンワールドのゲーム空間にしてしまうとか。 良かったら活用してみてください!!(何様だ)

TensorFlow機械学習クックブックを読む

はじめに

p338-341の遺伝的アルゴリズムを実装するコードを読んでいて知った単語や関数などをまとめた

用語

機械学習

  • インスタンス: とりあえず用意した箱みたいな
  • データセット: インスタンスの集まり
  • ハイパーパラメータ :
    • 数値的に決定できないパラメータ
    • ex) 最適化手法に何を使うかなど
  • 活性化関数:
    • 出力を正規化するためにかける関数
    • ex) ある値以上で確率80%として出力する。など
  • モデル: ネットワークそのもの
  • レーニングセット ⇔ テストセット
    • レーニングセット: モデルのトレーニングに使用
    • テストセット: バリデーションに使う
  • プレースホルダー: 空の配列とか
  • 損失関数: CNNの場合は(y_actual - y_pred)^2とか
  • バッチトレーニング ⇔ 確率的トレーニン
    • 使ってる最適化手法による違い
    • バッチ: 普通の勾配法。ゆっくり下がってく。
      • バッチサイズ: 観測データの数?
    • 確率的: SGDってやつ。めっちゃ振れる。
  • L1損失 ⇔ L2損失
    • |y_actual - y_pred| ⇔ |y_actual - y_pred|^2
    • L2は目的値の近くでとがった急なカーブを描いていることが特徴

遺伝的アルゴリズム

  • crossover: 交叉
    • 親を組み合わせて子を作る
    • 一点交叉、二点交叉などがある(バイナリコーディングと実数値コーディングで違う)
  • mutation: 突然変異
    • 変なのも親に入れてみる的な
  • features: 特徴量の数
    • 設計パラメータの数とも言える
  • generations: 世代数
  • selection: 淘汰(の割合)

一般的

  • スライス
    • Python用語。配列の一部を切り取る
    • ex) s = "Python"とすれば、s[2:5] → "ytho"

関数

TensorFlow

  • tf.Variable(tensor)
  • tf.square(x)
    • x2
  • tf.subtract(x, y)
    • x-y
  • tf.reduce_mean(tensor, axis)
    • tensorの要素から平均を求め、axisの次元に圧縮する
    • すべての評価関数に関して同じ重み付けにするためと思われる
  • tf.reduce_min(tensor, axis)
    • tensorの要素から最小値を求め、axisの次元に圧縮する
  • tf.nn.top_k(input, k)
    • inputの中からトップkまでの値を取り出す
    • つまりソートからのスライス
    • return 取り出した値, 取り出した値のinputの中でのインデックス
  • tf.arg_min(input, dimension)
    • inputの中の最小値(のインデックス)を求めて返す
    • ベクトルから選ぶならdimensionは0
  • tf.gather(param, indices)
    • param配列の中から、インデックス(複数)に対応したものを取り出す
  • tf.slice(input, begin, size)
    • テンソルは配列0~i番目までの配列の集合だとすると、
    • begin=[k, x, y]とすると、k番目の配列の(x,y)座標を始点とする、という意味になる。
    • size=[a, b, c]とすると、beginから配列方向にa, x方向にb, y方向にc広がった部分をスライスする、という意味になる。
    • 分かりずらいけどを見たら分かるはず。
  • tf.concat(values, axis)
    • values=[x, y]をaxisの部分で連結する。
    • axis=0ならxとyの境目が無くなる
    • axis=1ならxの中、yの中で境目が無くなる
    • そんな感じ

Numpy

  • np.arrange(start, stop, step, dtype)
    • startからstopまで等差stepの数列を作成する。
    • stepを省略したら等差1
    • startを省略したらstopの数だけ
    • dtypeは自動選択されるが、int, float32のように指定することも可
  • np.linspace(start, stop, num)
    • startからstopまでnum個の数列を作成する。
  • np.random.randn(size)
    • 平均0, 分散1の正規分布(標準正規分布)に従う乱数行列作成
    • sizeに行列のサイズを指定。ex) np.random.randn(3, 3)
  • np.random.normal(loc, scale, size)
    • 平均loc, 分散scaleの正規分布に従う乱数発生
  • np.random.choice(a, size)
    • 配列からsizeぶんランダムに選んで、そのインデックスを返す

Webhook完全に理解した

はじめに

バイトでLINE Messaging APIとかいじってたらこれが出てきて、最初納得いかなかったけど図にしたら簡単だったのでメモ

Webhock

f:id:gakki-uec15:20191007125138p:plain
Webhockと普通の場合の比較

Githubでcommitしたタイミングでslackに通知を送る場合を考えます。

普通の場合はGithubのプログラム内に「slackに通知を送る」という機能を持たせないといけないです。でも、こんな機能を全部つけていったらきりがないですよね?

ところでこの「commitしたタイミングでslackに通知を送る」ときにGithub側がする必要があるのは

① commitがあったタイミングを伝える

② commit内容を伝える

の二つです。この二つを伝えるだけなら、slackに通知を送るプログラムにPOSTで送ればよくない?というのがWebhookの考え方です。

つまり、

  1. commitがあったタイミングで通知送信プログラムのURLにcommit内容をPOSTを送信
  2. 通知送信プログラムはPOSTを受信するとそのPOSTの内容に基づいてslsckに通知を送信

という感じで、Github側が特にslack用のプログラムなど用意することなく通知が送れるようになるのです。

まとめ

要するにPOSTしてるだけ。完全に理解した。

参考文献

qiita.com

クロスドメインメッセージング完全に理解した

はじめに

クロスドメインメッセージングとは、その名の通り異なるドメイン間でデータをやり取りする方法です。 これを実現するためにはiframeを使わないといけないらしいんですが、既存の説明ではいまいち納得行かなかったので自分なりに解釈してまとめました。

iframeを使う理由

f:id:gakki-uec15:20191007013253p:plain
クロスドメインメッセージングでiframeを使う理由

1. クロスドメイン制約

jsがサイトS1とS2、両方にアクセスできるのは明らかにダメです。S1のデータがjs経由でS2に流れてしまいますからね。

なのでS1のjs(js1)はS1だけに、S2のjs(js2)はS2だけにアクセスできるようにしました。これがクロスドメイン制約です。

2. postMessaging

1.でjs1とjs2を完全に分離したけど、js2が「データほしい!」(request)と言って、js1が「いいよ!!」(response)と言ったときは通信できてもいいですよね? なのでpostMessagingという通信手段ができました。これは、あるドメインからあるドメインにrequestを送って、相手が良ければresponseを返す、というものです。

3. iframe

2.で通信手段ができたけど、まだjs2はjs1にrequestを送れません。なぜなら今の状態ではjs2はjs1の居場所がわからないからです。 ドメインを指定してあげても、javascriptとして参照できるのは同じwindowのオブジェクトだけだからです。

そこでjs2はjs1を自分のwindowにiframeとして召喚します。 そうするとjs2はjs1がiframeにいることが分かり、お願い(request)ができるようになるわけです。

request側(js2側)

<!DOCTYPE html>
<html>

<head>
    <title>js2</title>
    <meta charset="utf-8">
</head>

<body>

    <iframe id="ifm" src="http://localhost:8001" width="500" height="100"></iframe>

    <script>
        // Send Request
        window.onload = function(){
            var ifm = document.getElementById("ifm").contentWindow;
            ifm.postMessage("Request Message", "http://localhost:8001");    // ifm にあるlocalhost8001に対してrequest
        }

        // Listen Response
        window.addEventListener("message", function(event){
            console.log(event);
        }, false);

    </script>

    <p>This is launched in localhost:8000</p>

</body>

</html>

response側(js1側)

<!DOCTYPE html>
<html>

<head>
    <title>js1</title>
    <meta charset="utf-8">
</head>

<body>

    <script>
        window.addEventListener("message", function(event){
            console.log(event)
            event.source.postMessage("Response Message", event.origin)  // あくまでこのメッセージしか渡さない(保存してる変数全部見えるとかない)
        }, false)
    </script>

    <p>This is launched in localhost:8001</p>

</body>

</html>

まとめ

クロスドメインメッセージング完全に理解した。

参考文献

https://tsmatz.wordpress.com/2011/06/24/jsonp-cross-domain/ https://qiita.com/yasumodev/items/d339a875b4b9bf65d156 https://developer.mozilla.org/ja/docs/Web/API/Window/postMessage

MMDLoaderでローカル(IndexedDB)に保存したモデルを表示するテストを作ってみた

はじめに

Three.jsにはMMDLoaderという、MMDモデル(pmd, pmxファイル)を読み込んで表示してくれるライブラリがあります。

これ使ってMMDモデルを表示するARアプリなんかを作っている方もいるらしいです。→ https://qiita.com/jyuko/items/727a4f90e914a3932be2

しかし、MMDモデルの著作権の関係でデモページを公開できないようです。

そこで、MMDモデルはページにアクセスする人自身が用意してアプリに直接アタッチできるようにできないか、ということを試してみようと思いました。

簡単なのは一旦サーバーにアップロードする方法ですが、これはサーバーにモデルファイルが残ってしまい著作権的にグレーな気がします。 また、使うのはあくまでユーザ一人なので、使うときにいちいちサーバーから引っ張ってくる、というのは効率が悪いと思います。

そこで今回は、ブラウザからアクセスできるローカルのストレージであるIndexedDBにモデルを保存する仕組みを作ってみました。

何をしているか

MMDSaver.js

  1. フォームをリスンしておき、イベントがあればIndexedDBのトランザクションを開く
  2. ArrayBufferに変換して保存
  3. 保存が終わったら取り出す
  4. 取り出したモデルを修正版MMDLoaderに渡し、返ってきたmeshをsceneに追加
  5. レンダリング

MMDLoader

修正前

MMDLoaderのロード系メソッドは第一引数に静的ファイルのパスを渡す仕様になっています。MMDLoader内ではこのパスに基づいて xhrリクエストをサーバーに送信し、レスポンスとしてArrayBuffer形式のモデルファイルを受け取っています。 このことから、第一引数に直接ArrayBufferを渡し、xhrリクエストを送る処理を飛ばすようにすればいけると思いました。

修正後

MMDLoaderのロード系メソッドは第一引数に静的ファイルのパスを渡す仕様になっています。なのでそのままblobなどを渡してみてもエラーになります。 そこで

  1. 第一引数がblobのインスタンスの場合は分岐
  2. blobをpmd/pmxParserで処理可能なArrayBufferに変換
  3. pmd/pmxParserでArrayBufferをビルドできる形式に変換

するように修正しています。

データの変換

いろいろいじっています。複雑なので図にしてみました。

f:id:gakki-uec15:20190930163327p:plain
モデルデータの変換手順
この中で

  1. まず、IndexedDBはオブジェクト形式でしか保存できないため、バイナリ(と思われる)pmd/pmxファイルをArrayBufferに変換
  2. MMDLoaderに渡すとき、ArrayBufferのままだとなぜか失敗したので、一度blobに変換

という理由で処理が入っています。

結果

デモページを用意しました。

フォームからpmd/pmxファイルを選択すると、そのモデルに切り替わります。(pmd/pmxファイルを持っていない方は「MMDモデル 配布」とかでググってください)

また、そのモデルはIndexedDBに残っているのでページをリロードしても最後に登録したモデルファイルがそのまま表示されます。

もちろんサーバーにモデルファイルが送信されることはないので著作権的にも大丈夫(なはず)です。

Github

https://github.com/one-color-low/MMDSaver

今後やりたいこと

  • テクスチャを張れるように
    • さすがにテクスチャなしはさみしい
    • pmdファイルの内部にテクスチャがパスとして入ってるっぽくて、IndexedDB内にどう保存するかがムズい
  • クロスドメイン対応
    • このモデルアップロードページを起点として、ほかのアプリでも簡単にローカルのモデルファイルを呼び出せるようにしたい。
    • しかし、IndexedDBは同じドメインからしかアクセスできない。
    • そこで、このページをサーバー化し、別のアプリに送信できるようにする。
    • https://news.mynavi.jp/article/20100909-localstrage-on-many-domains/ ← こんな感じ

まとめ

「MMDLoaderを使ったWebアプリを作ってみたけど公開できない!」という方の助けになれば幸いです。 また、最終的にはMMDモデルを使ってWebで動くオープンワールドのゲームみたいのを作ってみたいと思っています。

javascriptのFileReaderではまった話

なにがあった

「FileReaderでファイルを読み込み(IndexedDBに)保存」→ 「保存されたファイルを取り出し」という処理をしたいのに、 そのままの順番で書くと「取り出し」→「保存」になってしまった。

原因

FileReaderは非同期でファイルを読み込むらしい。ファイルの読み込みは時間がかかる処理なので、「空いてる時間で次のことやっていいよー」とするらしい。ありがた迷惑だ。

対策

FileReader.onload に取り出し処理を書けばいい。

Before

var reader = new FileReader(); 

// ファイルをArrayBufferで保存 
reader.readAsArrayBuffer(xxx); // 

保存されたファイルを取り出す 
getSavedFile(); 

After

 var reader = new FileReader(); 

// ファイルをArrayBufferで保存 
reader.readAsArrayBuffer(xxx); 

reader.onload = function(e){ 
                             getSavedFile(); 
                           } 

ちなみにFileReaderSync()というのもあって、それで同期的にできるのかな?とも思ったが、これはWebWorkerをつかって同期的に(並列に?)読み込むものらしい。なので今回の用途では使えなかった。

まとめ

非同期処理めんどくさいなーと思う一方、javascriptはシングルスレッドで重い処理をうまいことこなす工夫がされてるんだなと感心した。 同じような非同期のAPIもあるらしいので今後注意して使いたい。