初めてのTensorFlow - イントロダクションとしての線形回帰

**（追記: 改訂版の記事を投稿しました - http://qiita.com/TomokIshii/items/0a7041ad337f68f71286 ）**


先週(2015/11/9)，Deep LearninのFramework の"TensorFlow"が公開されたが，ドキュメントの説明「MNIST（手書き数字の分類）は機械学習の"Hello World" である．」という箇所に納得がいかない．CourseraのMachine Learning (Stanford)でもそうだったが，機械学習を初歩から学ぶ場合，やはり最初は Linear Regression（線形回帰）と，個人的に考える．

本記事では，最初にLinear Regression（線形回帰）のコードを調べ，次に Logistic Regression（ロジスティック回帰）のコードを作成して，TensorFlowの雰囲気をつかむことにする．

## Linear Regression（線形回帰）
TensorFlowの先駆け（公開が先）である"Theano"について，本家ドキュメントのTutorialのほかに Newmu Theano-Tutorials コードというのがGitHubに公開されている． 
(https://github.com/Newmu/Theano-Tutorials)   

いくつかの短いコードが掲載されており，順を追って"Theano"への理解を深めることができるようになっている．0_multiply.py （かけ算）の次に 1_linear_regression.py （線形回帰のコード）が掲載されているが，このTheano版をベースに，TensorFlow版のコードを作っていく．
 
まずTheano版のコードである．

```python
# シンボリック変数定義 "T" は theano.tensor の置き換え
X = T.scalar()
Y = T.scalar()
def model(X, w):
    return X * w
# 共有変数の宣言
w = theano.shared(np.asarray(0., dtype=theano.config.floatX))
y = model(X, w)
# グラフ定義
cost = T.mean(T.sqr(y - Y))
gradient = T.grad(cost=cost, wrt=w)
updates = [[w, w - gradient * 0.01]]
# 関数
train = theano.function(inputs=[X, Y], outputs=cost, updates=updates, allow_input_downcast=True)
# ループ計算による学習
for i in range(100):
    for x, y in zip(trX, trY):
        train(x, y)

```

次にTensorFlow版を詳しく見ていく．

まず，関係モジュールをimportした後，使用するデータを準備する．（Theano版は切片bを持たないテスト用データでしたが，ここでは b (=3.0)を含むようにしています．）

```py
import numpy as np
import tensorflow as tf

trX = np.linspace(-1, 1, 101)
trY = 2 * trX + 3 + np.random.randn(*trX.shape) * 0.33

def lin_model(X, w, b):
    return X * w + b

```

lin_model() は回帰モデルとして使う関数である．（パラメータに傾きwと切片b を持つ線形関数．）

次に必要な変数を用意する．

```py
w = tf.Variable([0.])
b = tf.Variable([0.])

x = tf.placeholder(tf.float32, shape=(101))
y = tf.placeholder(tf.float32, shape=(101))
```

w, b は，通常の変数．初期値 [0.] とした．
x, y は，TensorFlow独特の用語(terminology)であるがプレースホルダーで用意する．プレースホルダー（置き場所）なので，これを宣言した時点で実態はなく，この後のプログラム処理で実際の値がアサインされる．

それから重要な，Graphと呼ばれる変数の関係性の記述を行う．

```py
y_hypo = lin_model(x, w, b)
cost = tf.reduce_mean(tf.square(y_hypo - y))

```

cost は，自分のモデルと実データの差異に対応するコスト関数である．"tf.reduce_mean()" は平均を計算する関数である．（別に "mean()"でもいいと思うのだが，TensorFlowでは何故か"reduce_mean()"となっている... Reduction 関数グループに入っているようだ．）

それからパラメータ探索の計算のためのOptimizer指定とそのメソッドを定義する．

```py
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

```

ここでは勾配降下法のOptimizerを指定し，引数として学習率(Learning Rate) 0.01を与えている．

### Optimizerについて
さて気になるOptimizerであるが，TensorFlowでは現在，次のものがサポートされている．  
http://www.tensorflow.org/api_docs/python/train.html#optimizers

|   Optimizer name  |  Description   |
|:-------------------|:---------------|
| GradientDescentOptimizer | 勾配降下法によるオプティマイザー
| AdagradOptimizer |  AdaGrad法によるオプティマイザー |
| MomentumOptimizer | モメンタム法によるオプティマイザー  |
| AdamOptimizer | Adam法 （これも有名ですね．）|
| FtrlOptimizer |（初めて耳にしました）"Follow the Regularized Leader" アルゴリズム  |
| RMSPropOptimizer | （初めて耳にしました）学習率の調整を自動化したアルゴリズム  |

Optimizerにより，設定しなければならないパラメータが異なるが，今回は，基本となる GradientDescentOptimizerを使うこととする．（必要なパラメータ学習率は，上記の通り設定した．）

## Linear Regression（線形回帰）（続き）

さて，ほぼ準備は整ったので，メインの計算部分を示す Session を開始する．但し，Sessionを開始する前に変数(Variables)の初期化を行わなければならない．これは"Theano"では無かった"TensorFlow"にユニークなものである．

```py
# Initializing
init = tf.initialize_all_variables()

```

「Sessionの開始前にVariablesを初期化する」ことは，結構，失念しがちなルールと思われる．一応，確認しておくと，
- tf.Variable() の変数は，初期化が**必要**．
- tf.placeholder() の変数は，初期化**不要**．（あとで実体とアサインされる）
- tf.constant の変数（定数）は，初期化**不要**．
とのことである．上のリストでは，initialize_all_variable()を用いて変数を初期化している．

Session 部は，以下の通り．

```py
# Train
with tf.Session() as sess:
    sess.run(init)

    for i in range(1001):
        sess.run(train_step, feed_dict={x: trX, y: trY})
        if i % 100 == 0:
            print "%5d:(w,b)=(%10.4f, %10.4f)" % (i, sess.run(w), sess.run(b))

```

Sessoinを動かすためのプログラムの書き方にいくつか種類があるようだが，上記のように"with" 文で囲むことでSessionの区切りが明確になり，かつ"with"を抜けるところで，自動的に開かれたSessionはclose()されるので便利である．

一つTensorFlowにおけるTrainデータの与え方が難しいところである．データの与え方 (Data Feeding)は学習の進め方で処理が変わってくる部分であるが，上のリストの通り，"feed-dict" を使うことになるので覚えておきたい．(ここの部分は，まだ勉強中です...）

以上で，回帰パラメータが算出される．（真値 w=2.0, b=3.0に使づく様子が示される．）


```text
    0:(w,b)=(    0.0135,     0.0599)
  100:(w,b)=(    0.9872,     2.6047)
  200:(w,b)=(    1.4793,     2.9421)
  300:(w,b)=(    1.7280,     2.9869)
  400:(w,b)=(    1.8538,     2.9928)
  500:(w,b)=(    1.9173,     2.9936)
  600:(w,b)=(    1.9494,     2.9937)
  700:(w,b)=(    1.9657,     2.9937)
  800:(w,b)=(    1.9739,     2.9937)
  900:(w,b)=(    1.9780,     2.9937)
 1000:(w,b)=(    1.9801,     2.9937)

 ```
 
 以上まとめて，再度プログラムを掲載する．（約30行のプログラムになりました．）
 
 ```py
import numpy as np
import tensorflow as tf

trX = np.linspace(-1, 1, 101)
trY = 2 * trX + 3 + np.random.randn(*trX.shape) * 0.33

def lin_model(X, w, b):
    return X * w + b

w = tf.Variable([0.])
b = tf.Variable([0.])

x = tf.placeholder(tf.float32, shape=(101))
y = tf.placeholder(tf.float32, shape=(101))
y_hypo = lin_model(x, w, b)

cost = tf.reduce_mean(tf.square(y_hypo - y))
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

# Initializing
init = tf.initialize_all_variables()

# Train
with tf.Session() as sess:
    sess.run(init)

    for i in range(1001):
        sess.run(train_step, feed_dict={x: trX, y: trY})
        if i % 100 == 0:
            print "%5d:(w,b)=(%10.4f, %10.4f)" % (i, sess.run(w), sess.run(b))
 
 ```
 

### Interactive Session について

 前記のリストは，（短いながらも）バッチ処理としてSessionを実行するプログラムである．これとは別に Interactive Session という，その名の通りインタラクティブに動かすための機能をTensorFlowはサポートしている．
 http://tensorflow.org/get_started/basic_usage.html#interactive-usage
 
 これは，IPython と相性がいい，すなわち Jupyter Notebook と相性がいいということなので，早速，試してみた．
 
 （こんな感じです...）
![tensorflow_notebook1.PNG](https://qiita-image-store.s3.amazonaws.com/0/74152/9b98d907-ee9d-52d1-96fa-e47625357b74.png)
 
 これからTensorFlowを勉強しようという時には，このような使い方が便利と思われる．
 （反対に長時間にわたるプログラムを実行するケースでは，この Interactive Session は向きません．）
 
## Logistic Regression （ロジスティック回帰）
 上記Linear Regressionのコードをベースに，Logistic Regressionのコードを作成した．なお，このコードは2クラス分類のためのもので，多クラス用ではない．（ご承知の通り，MNIST は，多クラス(10クラス）分類を行う問題を行う Softmax 回帰を用いている．）
 
 ```py
 def lin_model(X, w, b):
    return X * w + b

w = tf.Variable([0.])
b = tf.Variable([0.])

x = tf.placeholder(tf.float32, shape=(mlen))
y = tf.placeholder(tf.float32, shape=(mlen))

p_1 = lin_model(x, w, b)
x_entropy = tf.nn.sigmoid_cross_entropy_with_logits(p_1, y, name='xentropy')
loss = tf.reduce_mean(x_entropy, name='xentropy_mean')

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(loss)

```

Linear RegressionではMean Square Error(MSE)で定義していたコスト関数を，Logistic Regressionでは cross entropy に置き換えている．上リストの後半部にある "tf.nn.sigmoid_cross_entropy_with_logits()" は，cross entorpyを計算する関数である．（"TensorFlow"は，長い名前の関数が多い...）

変数(Variables)の初期化，Sessionの進め方は前のコードと同様である．

以上，線形回帰，ロジスティック回帰のコードを見てきた．ここまでくれば MLP (Multi Layer Perceptron) のコード（例えば MNIST）にスムーズに取り掛かれるのではないだろうか？
 
## 参考文献 (web site)
- TensorFlow web site http://www.tensorflow.org/
- Newmu Theano-Tutorials - GitHub [https://github.com/Newmu/Theano-Tutorials](https://github.com/Newmu/Theano-Tutorials)
- TensorFlowで回帰をやってみる - Qiita  
 [http://qiita.com/syoamakase/items/db883d7ebad7a2220233](http://qiita.com/syoamakase/items/db883d7ebad7a2220233)
- Coursera / Machine Learningの教材を２度楽しむ - Qiita  
 [http://qiita.com/TomokIshii/items/b22a3681cb17836c8f6e](http://qiita.com/TomokIshii/items/b22a3681cb17836c8f6e)

 （注）TenforFlowのドキュメントはまだ版が浅いせいか，頻繁にupdateが行われているようです．リンク切れありましたら，ご容赦ください．