おれの技術日記

元はJava+SQLがメインのエンジニア、フロントエンドは軽くかじった程度で苦手。最近忘れっぽいので覚えたことをいろいろメモするためにブログ開始。

Machene Learning Crash Course 4 - First Steps with TensorFlow #2

さあ、用語も覚えてついにTensorFlow!!
と思ったが、どうやらここからさらに先に進むためにはpandasを学ぶことは避けて通れないようである。pandasはSeriesとDataFrameという2つのデータ構造を提供するためのライブラリ、Seriesはちょっと便利な1次元配列でDataFrameはちょっと便利な2次元配列。

city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento'])
population = pd.Series([852469, 1015785, 485199])

pd.DataFrame({ 'City name': city_names, 'Population': population })

のように自分でSeriesやDataFrameの変数を作ることもできるけれど、大抵はこんな感じでガツンとデータを読み込むんです。

california_housing_dataframe = pd.read_csv("https://download.mlcc.google.com/mledu-datasets/california_housing_train.csv", sep=",")

そして、読み込んだデータを色々したいときはこんな感じ。

california_housing_dataframe.head() #最初の数行を表示する
california_housing_dataframe.describe() #各カラムの中央値や平均値・最大最小値などを表示する
california_housing_dataframe.['longitude'][0] #longitudeカラムの最初のレコードを取得する
california_housing_dataframe.['longitude']*2 #longitudeカラムの全部の値に2をかける

データへのアクセスは連想配列っぽい。まあとにかく、便利な配列ということがわかってれば前に進めそう。
牛歩のような進捗である・・・

Machene Learning Crash Course 4 - First Steps with TensorFlow #1

ついに来たTensorFlow、名前はよく聞くしML=TensorFlow的なイメージはあるけれど全然中身はわからないという。しかもたぶんガンガン機能追加とかされてるから適当に日本語サイトをググっても常に古めの情報になってしまうんでないかという懸念から更に億劫になって結局何もやらないという。今日はこの悪循環をついに断ち切ろうと思う。

First Steps with TensorFlow: Toolkit  |  Machine Learning Crash Course  |  Google DevelopersTensorFlow Guide  |  TensorFlowを見比べると、どうやらC++カーネルに対して複数レイヤーでのラッピングが行われた構成になっていて、実際のプログラミングにあたってはLow Level APIとHigh Level APIの両方が使えるようであることがわかる。多分Low Level APIを使って自前でモデルを組むこともできるし、やりたいことが結構一般的な話ならばHigh Level APIを使ってサクッと実現できるよってことなんじゃなかろうか。とはいえ、これ以外にも画像認識のためのVision APIやら文字起こしのためのSpeech APIみたいなものがGoogle Cloudから提供されていることを考えると、オーディエンスのレベルに応じて多くの抽象化レイヤーを提供してる感じ。ちなみにLow Level APIのことをTensorFlow Coreともいったりするみたい。推奨としてはまずいちばんHigh LevelなAPIを使用して、必要に応じてひとつずつ下っていけとのこと。このコースでもEstimatorがメインらしい。

ここで用語の確認。

  • tensor : n次元配列のこと、0次元配列(=通常の数値とか文字列とか)も一応この定義によるとtensorの一部らしい。つまり変数とか定数とか、とにかくそういうのは全部tensorぽい。
  • rank : tensorの次元数。rank 1 tensorというと1次元配列。
  • shape : 配列に入ってる要素の数。

さらに、以下のような図がよく機械学習では出てくるけれど(qiita.comのリンク)、ここで
https://camo.qiitausercontent.com/da8208bf5b32d3f382241c08aaffe5b7a02b5fa4/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f32353939302f32326632353639372d653534652d353932382d646566632d6561323063643037373138372e706e67

  • graph : このフローチャートみたいな図のこと(処理の流れ)
  • operation : 各ノード(○)。データに対する操作。

で各ノードをつないでいる棒がtensorらしい。で、この処理を実際に実行する場合にはsessionを作成して行う模様。多分graphとsessionはオブジェクト指向のclassとinstanceみたいな関係なんだろう。

さらにさらに、ここらへんのアイテムはhyperparameterというらしい。

  • steps : iteration回数。1stepは1batchのlossを計算し、これをウェイトの調整に使う。
  • batch size : 前回も出てきたけれど、一回のstepで使用するexample数。

なので、
total number of trained examples = batch size * steps
となる。

なお、これに関連する用語で

  • period : レポーティングの粒度をコントロールする変数。もしstepが70でperoidが7なら10stepごとにlossが出力される。この値は通常変更しないらしい。(レポーティング用の変数なのでモデルの精度とかには特に影響しない)

Machene Learning Crash Course 3 - Reducing Loss

機械学習はiterative approachによって損失を減らす。iterative approachというのは若干直訳しにくいところがあるけれども、まあ要はたくさんのsampleを処理することによって徐々に損失の少ないweightとbiasによせていく、ということだろう。で、ずっと計算し続けていくとどこかのタイミングでもうweightとbiasがほぼ変わらない段階にたどり着いたら、そのモデルはconverged(収束)したと言える。

ではどのようにモデルをconvergeさせるか。
 y' = b + w_1x_1
という最もシンプルなモデルを考えた時に、w1の全ての値についてその損失を計算すると以下のような値の分布になる。
f:id:kuniaki12:20180815164555p:plain
この分布は唯一つの最小値を持ちそこでは傾きが0になるが、まさにそこがこのモデルの収束点である。
(と元の記事では書いてあるけれど、うっかりこれがn次関数で複数の極小点を持つようなexampleだったらどうするんだろう・・・)

このconvergenceをより効率的に求めるために使われる手法がgradient descent(最急降下法)というもの。
これは、このグラフの中でどこか1点を最初に決めて、そこから点をちょっとずつ動かしつつ計算をしてconvergenceを探すアプローチらしい。てかこれなら微分で一発なのでは??これは扱っているモデルがシンプルだからなのか?まあとにかく、実際にTensorFlowを使う際にはこの部分は全部カプセル化されてるから気にする必要ないらしい。

で、次にこの「ちょっとずつ」動かすと言った場合に実際のところどの程度点を移動させるんだろうか。というのがlearning rate(学習率、step sizeとも言うらしい)。このlearning rateのチューニングがキモで、ここを小さくしすぎると永遠にconvergenceに届かないしこれを大きくしてしまうとconvergenceを通り越してしまう(Optimizing Learning Rate  |  Machine Learning Crash Course  |  Google Developersで試してみるとわかる)。現実にはlearning rateを最適化することよりもconvergenceにたどり着くことが目的なので、そこそこのlearning rateを見つけるのが重要らしい。(ただここで具体的にどうやってそのそこそこなlearning rateを見つけるかという話には至っていない)

このgradient descentを行う際に一度のiterationで勾配を計算するのに使用するexampleの数をバッチというらしい。ここまでの説明だと上のようなグラフを書くのに使用するexample数って聞こえると思うんだけど、そのさきを読むと

  • Stochastic gradient descent(確率的勾配降下法、一度のiterationで1つのexampleを使用する=バッチサイズ1)
  • Mini-batch stochastic gradient descent(バッチサイズを10~1000としたもの)

らしい。バッチサイズを1にしたらそもそもグラフとか書けないし、となると結局バッチサイズというのはなんなんだ??という疑問が残る。でも機械学習の仕組み (Vol.4)を見るとややわかりやすいが、やはりここでいうバッチサイズはexample数っぽい。何かしら数学的な調整がうまいこと効いてより少ない計算量でより正確なモデル構築ができるらしい。(全然ピンときてないけど汗)

Machene Learning Crash Course 2 - Descending into ML

教師あり学習(Supervised ML)においてモデルを作るというと、そのモデルは
 y' = b + w_1x_1 + w_2x_2 + w_3x_3 + ・・・ + w_nx_n
という式で表される(nはfeatureの数)ので、

  • モデルに含めるfeatureを決定する
  • トレーニングデータ(labeled examples)を使ってw(weight)とb(bias)を決定する

というステップになる。例えば前回の気温とアイスクリームの売れ行きの例で言えば、featureは気温のみなので
 y' = b + w_1x_1
となる。

そして、このモデルと実際のデータの乖離をloss(損失)という。下図で矢印の大きさがloss(すいませんこのimage、本家からお借りしました)。左のモデルよりも右のモデルのほうが損失が少ないのでよりよいモデルであるといえる。
f:id:kuniaki12:20180710162314p:plain


モデルの損失を最小化する手法は損失関数と呼ばれ(多分)、いろいろな種類があるけれどここでは二乗損失(squared less, L2 loss)がチラッと紹介されている。この損失関数は平均二乗誤差(mean square error, MSE)を最小化するようにするということなんだろう、

 MSE = \frac{1}{N} \sum_{(x,y)\in D} (y - prediction(x))^2

という禍々しい数式が書いてあるけれど、これはよく見ると単純に
featureの各値についてモデルで算出されたpredictionと実際の値の差分を二乗して、その平均を算出するというだけの話だった。

ちなみにこの方式で計算すると、前回のアイスクリームの売上と気温の関係式は
y' = 1.217582418x1 + 30.16923077
というモデルが構築できた。モデル構築というと物々しいけれど、単純にExcelの関数でちょいちょい計算しただけ(汗)。

Machine Learning Crash Course 1 - Framing

一念発起して、Googleが無償公開しているMachine Learning Crash Courseに挑戦しつつその記録をここに残すことにした。
英語で学ぶのは億劫ではあるものの、仕事の半分くらいは結局英語なのでMLに関しても英語で説明・議論できなければ意味がないので、その意味ではこの領域の英語も合わせて学べる点で一石二鳥(やる気さえ続けば)。

というわけで、まずは一番最初の章、Framingから。
ここは簡単な用語の説明がほとんどなので簡単。

  • Label:yと呼ばれる、予測対象の値
  • Feature:x1-xn、入力変数。
  • Example:DBで言うところのレコード、前はsampleって聞いたような気がしたけど・・・
    • Labeled example:labelとfeatureの両方を持ったexample、機械学習ではトレーニングデータとも呼ばれる
    • Unlabeled example:featureのみを持ったexample、このレコードに関してlabelはモデルを使って予測される
  • Model:labelとfeatureの関連を定義したもの
    • Training:モデルを構築すること
    • Inference:モデルを使ってlabelを予測すること、ちなみに予測されたlabelはトレーニングデータに含まれるlabelと区別するためにy'と表記される
  • Regression Model:回帰モデル、連続した値を予測するのに使用される
  • Classification Model:分類モデル、非連続の値を予測するのに使用される

ちなみにトレーニングデータ(labeled example)を用いてモデルを構築し、それをもとに未知のlabelを予測する仕組みをsupervised ML(教師あり学習)という。

例えば気温とアイスクリームの売れ行きの関連を表現するとして、高校で習ったy=ax+bみたいな式を考えた時に(実際はy'=b + wxと書く)過去のトレンドからだいたい以下のような図を見いだせたとすると、
f:id:kuniaki12:20180710170901p:plain

  • Label:アイスクリームの売れ行き
  • Feature:気温
  • Labeled example:個々の青い点
  • Unlabeled example:未来の売上(例えば明日は25度だから60個くらい売れそうだな、みたいな)
  • Model:回帰直線

となる。

このあたりの用語は数学と統計と機械学習で微妙に異なっていて、更に英語となるとややこしいけど、まあ慣れかな。

おまけ、データはこんな感じ。

気温 売上
18 53
19 51
20 52
21 56
22 58
23 58
24 61
25 63
26 63
27 63
28 63
29 68
30 67
31 64

Gmailでメールの振り分けを設定する方法

Outlookに慣れた人がGmailを仕事で使おうとすると戸惑う場合が多いと思う。なんせフォルダという概念がない。代わりにラベルというものがあるようなんだけれど「ラベル?おれはそういうのじゃなくてフォルダ分けしたいだけなんだけど」と思ったりする。ラベルとフォルダの違いは、ひとつのメールに対してラベルなら複数設定ができるがひとつのメールは複数のフォルダに所属できないということだ。

自分の場合はOutlookでは基本的なこんな感じで振り分けを行う。

  • XXXプロジェクトのメーリングリストあては「XXXプロジェクト」というフォルダに
  • 日本チーム全体のメールは「日本チーム」というフォルダに
  • システムから自動配信されるエラー通知メールなんかはエラー通知に
  • YYYサービスのメルマガとかほぼゴミみたいなメールは「ゴミ箱一歩手前」に

すると受信トレイに残ったものは従来のルールに当てはまらないものなので必ず目を通すべきものとなるので、受信トレイは常に空であるのが正常という状態を作る。(「to me」みたいな自分宛てのフォルダを作ることはしない)

この、「どのフォルダにも属さないメールのみを一覧表示する」というのがGmailでできなくて悩ましかった点なのだが、ようやくやり方を発見したので記録しておく。

1.ラベルの自動付加を設定する

まずは各メールにラベルが適切に付加されるよう設定をする。ラベルをつけたいメールを開いて右上のメニューから「メールの自動振り分け設定」を選択する。
f:id:kuniaki12:20180105134955p:plain:w300
その上で、ポップアップ下部の「この検索条件でフィルタを設定」をクリックし、
f:id:kuniaki12:20180105135214p:plain:w300
上から5番目にある「ラベルを付ける」にチェックを入れて最下部の「フィルタを作成」をクリックする。
f:id:kuniaki12:20180105135312p:plain:w300

2.マルチ受信トレイを有効化する

右上ギアメニューより「設定」>「Labs」を選択すると中段あたりに「マルチ受信トレイ」というものがあるので、これを有効化して「変更を保存」ボタンをクリックする。
f:id:kuniaki12:20180105135909p:plain:w400

次に右上ギアメニューより「設定」>「マルチ受信トレイ」を選び、パネル0の検索キーワードに"has:nouserlabels"と入れる。(パネル1の値は消してしまってOK)
f:id:kuniaki12:20180105140409p:plain:w400

3.振り分けルールを管理する

振り分けルールを追加しすぎてよくわからなくなってきた場合は右上ギアメニューより「設定」>「フィルタとブロック中のアドレス」を見ることで一覧で確認できる。


これで、一番上にはなんのラベルもついていないメールのみが表示される。(≒Outlookで言うところの受信フォルダ)
ちなみにこの"has:nouserlabels"というのはGmailがサポートしている種々の検索クエリのうちの一つで、それ以外にも以下のリンクのように数多くのクエリが存在するのでこれを使いこなすとかなり便利そうではある。
support.google.com

あと、フィルタとか振り分けとかUI上で用語が異なるのがちょっと気になる。

バスケット分析(併売分析)のためのSQL

いわゆる売上トランザクションテーブル、こんなデータから

OrderID Date ProductID ・・・
0000001 2017-07-01 A001 ・・・
0000001 2017-07-01 A002 ・・・
0000002 2017-07-02 A003 ・・・
0000003 2017-07-02 A001 ・・・
0000003 2017-07-02 A002 ・・・
0000003 2017-07-02 A004 ・・・

ある商品を買った人がほかの商品をどれだけ併売しているかを調べるためのこんなテーブルを作る。

YearMonth MainProduct SubProduct # of Purchase
2017-07 A001 A002 2
2017-07 A001 A004 1
2017-07 A002 A001 2
2017-07 A004 A001 1


こんな感じでSQLを書く。書いてしまうと普通のクエリなんだけど、これがひらめいたときは結構感動した。

SELECT
   date_format(a.Date, '%Y-%m') YearMonth
   ,a.ProductID MainProduct
   ,b.ProductID SubProduct
   ,count(*) `# of Purchase`
FROM
   Sales_T a
   ,Sales_T b 
WHERE
   a.OrderID = b.OrderID 
   and a.ProductID <> b.ProductID
GROUP BY
   date_format(a.Date, '%Y-%m')
   ,a.ProductID
   ,b.ProductID