機械学習勉強してるよって言いたい - その1

# はじめに

かねてより機械学習とかは本等で勉強していた。
が、まともにアウトプットしていないので、何も言えなかった。
「ほら、やってんでしょ？」って言いたいので、とりあえずガリガリ書いてゆく。

とりあえずHelloWorldのところまでいったん記事にして、
次回以降で

 + AutoEncoder
 + CNN
 + w2v
 + RNN

などのに触れていきたい。

尚、本記事では機械学習の理論には触れない。
活性化関数とか、損失関数とか、伝播とか。
理論の部分は、猫も杓子も機械学習のご時世なので本買って読むが吉。
自分が説明するよりそっちのほうが正確でわかりやすいと思う。

おすすめはオライリー。本読めば理論とかは何となくわかる。
読むと「重みの最適化」とか、それっぽい人が言ってる意味が分かってくる。

# まずは環境構築

いろいろ面倒なのでDockerでやる。

今回使った環境は

 + CentOS 7
 + docker 17.06.0-ce

## コンテナ作成

こんな感じで作った。

``` Dockerfile
FROM python:3.6-stretch

RUN apt-get update \
  && apt-get -y install \
    vim \
  && pip install \
    chainer==2.0.0 \
    numpy \
    sklearn \
    scipy

COPY ./docker-entrypoint.sh /docker-entrypoint.sh
COPY ./app /app

ENTRYPOINT [ "/docker-entrypoint.sh" ]
```

entrypointはこんな感じ(地獄に落とすだけの超シンプル仕様)

``` bash
#!/bin/bash

tail -f /dev/null
```

後はビルドしてコンソール開くだけ。
いつかまとめてアップする。多分。

# Chainerで使うpythonの基本の基本 = 配列

配列に関する処理は「反射で書けなきゃ！」って言うぐらい出てくるので、
この際覚えておいたほうが良い。(詳細は、割愛)

 + 生成
 + 加工
 + 演算
 + 保存と読み込み

行列の演算（内積とか内積）は何となくイメージでイケてれば良いと思ってる。

例

``` python
import numpy as np
a = np.arange(6).(2, 3)
# [0, 1, 2][3, 4, 5]
b = np.arange(6).reshape(3, 2)
# [0, 1][2, 3][4, 5]
a.dot(b)
# [10, 13][28, 40] = [0*0+2*1+4*2, 0*1+1*3+2*5][3*0+4*2+5*4, 3*1+4*3+5*5]
```

# Chainerの基礎知識

## Variable

NN上のノードの値を表す。Variableの演算結果もVariable。
Variableで計算した結果を逆伝播させるのは下記のようになる。

``` python
import numpy as np
from chainer import Variable
x1 = Variable(np.array([1]).astype(np.float32))
print(x1)
z = x1 ** 2
print(z.data)
z.backward()
print(x1.grad)
```
## functions

いろいろ便利な関数群。sinとかsigmoidとか。

``` python
from chainer import Variable
import chainer.functions as F
x = Variable(np.array([1]).astype(np.float32))
print(F.sin(x))
print(F.sigmoid(x).data)
print(F.cos(x))
```

## links

chainerにパラメタを推定させるfunctions。
ノード間の計算といったほうがしっくりくるかも。
3層から4層への線形だとこんな感じ

``` python
import chainer.links as L

# y = wx + b
h = L.Linear(3, 4)
print(h)
print(h.w.data)
print(h.b.data)
x = Variable(np.array(range(6)).astype(np.float32).reshape(2, 3))
print(x.data)
y = h(x)
print(y.data)
```

## Chain

よく言われる「モデル」を表す。
ここまでくるとよく見る感じの形になる。

``` python
from chainer import Chain
import chainer.links as L
import chainer.functions as F

class MyChain(Chain):
  def __init__(self):
    super(MyChain, self).__init__(
      l1 = L.Linear(4, 3),
      l2 = L.Linear(3, 3),)

  def __call__(self, x, y):
    fv = self.fwd(x, y)
    loss = F.mean_squared_error(fv, y)
    return loss

  def fwd(eslf, x, y):
    return F.sigmoid(self.l1(x))
```

## optimizers

重みの最適を行うためのパラメタ更新を行う。
自力で書けない事もないだろうけど、実質割に合わないのでoptimizerを使う感じ。
一気に説明臭くなってきた。

ここまでのを合わせてみるとこんな感じになる。

``` python
import numpy as np
from chainer import Chain, Variable, optimizers
import chainer.links as L
import chainer.functions as F

class MyChain(Chain):
  def __init__(self):
    super(MyChain, self).__init__(
    l1 = L.Linear(4, 3),
    l2 = L.Linear(3, 3),)

  def __call__(self, x, y):
    fv = self.fwd(x, y)
    loss = F.mean_squared_error(fv, y)
    return loss

  def fwd(self, x, y):
    return F.sigmoid(self.l1(x))

model = MyChain()
optimizer = optimizers.SGD()
optimizer.setup(model)

model.cleargrads()

x = Variable(np.array(range(12)).astype(np.float32).reshape(3, 4))
h = L.Linear(4, 3)
y = h(x)
loss = model(x, y)
loss.backward()
optimizer.update()
```

# ようやっとこさHelloWorld

長かった。
そしてここが機械学習入門の面倒くさいところ。
HelloWorldまでが長い&意味わかってないとHelloWorldなのかどうか判断つかない。

どの言語にしてもなんにしてもHelloWorld。
Cで言う所のprintf,
Streamingで言う所のWordCount,
bashで言う所のecho,etc...
そして、われらが機械学習のHelloWorldと言えばIris(*1)。

*1: 個人的な思い込みです。

できる限り何となくわかる感じで書くけど、「意味わかんねぇよ」って時は編集リクエストで対応したい。

で、やる事は
```アヤメの教師ありデータを読み込んで、アヤメを分類する。````

まずはデータ作成

 + トレーニング用のデータ
 + テスト用のデータ

``` python
# 我らがアヤメデータ
iris = datasets.load_iris()
X = iris.data.astype(np.float32)
Y = iris.target
N = Y.size
# 出力ベクトルは3
Y2 = np.zeros(3 * N).reshape(N, 3).astype(np.float32)
for i in range(N):
  Y2[i, Y[i]] = 1.0

index = np.arange(N)
# トレーニング用データ
xtrain = X[index[index % 2 != 0], :]
ytrain = Y2[index[index % 2 != 0], :]
# テスト用データ
xtest = X[index[index % 2 == 0], :]
yans = Y[index[index % 2 == 0]]
```

Chain,optimizerは前述と同様。
パラメタ10000回更新（学習）。

``` python
for i in range(10000):
  x = Variable(xtrain)
  y = Variable(ytrain)
  model.cleargrads()
  loss = model(x, y)
  loss.backward()
  optimizer.update()
```

テスト用データを使って、学習させたモデルに実際計算させてみる

``` python
xt = Variable(xtest)
yy = model.fwd(xt)
answer = yy.data
```

あとは事前に作っておいたテスト用データの正解とぶつけるだけ！
やったね！

# 補足

実は今回のHelloWorldではミニバッチやTrainerなど、よくHelloWorldに登場すべきものが存在しない。
次回以降は当たり前のように出てくると思うので、その都度軽い説明を入れる予定。

# 参考

 + Chainer v2による実践深層学習