找了半天找不到中文的教學,底子不好的我,只找到一篇日文著作看起來比較清楚,順便當作練習日文來邊翻譯邊學習chainer嚕。
此為翻譯文章,非本人著作,同時本人也不是專業翻譯,翻錯是正常的。
來源:
【機械学習】ディープラーニング フレームワークChainerを試しながら解説してみる。
使用現在熱門的深度學習的框架:Chainer來做辨識手寫數字的範例程式碼。這邊想稍微記錄一些關於程式碼內容相關的解說。
(本內容的程式碼的全文已經在GitHub上上傳了。[建議使用PC觀看])
總而言之、因為安裝上非常簡單,推薦Chainer搭配Python來使用,即使是實作封閉代碼也相當不錯。
主要的參考網站有:
Chainer官網
Chainer的GitHub連結
Chainer的教程和文件
1. 安裝
首先,就先安裝吧,安裝內容是根據Chainer的GitHub上的”Requirements” 中寫的必要軟體和library。
pip install chainer
執行它。
就這樣安裝完了。超簡單!以前在將Caffe安裝在Mac時遇到的苦戰就像假的一樣。
假如,安裝有問題的話cvl-robot著作的「DeepLearningライブラリのChainerがすごい、らしい」中將必要的library等等的安裝內容詳細記錄下來了,十分方便好用。
2.入手範例程式碼
下方連結的GitHub目錄中有一份完成的手寫數字辨識的範例檔:train_mnist.py。這個範例程式嘗試使用Chainer中的順向傳播神經網路來做分類
https://github.com/pfnet/chainer/tree/master/examples/mnist
┗ train_mnist.py
我(原文作者)想為在這個程式碼上加點註解,還有將其中一部份的流程用圖像表示。
3.來看看範例檔
這次,我(原文作者)的環境是用Macbook Air(OS X ver10.10.2)來測試,可能會因為環境有點不一樣,而會有輸出後有點差異,就適當的參考一下就好,另外這邊因為環境的關係是用GPU來計算,CPU相關的地方的程式碼就省略不寫了。
(翻譯者我是用ubuntu 16,測試可用)
3-1.準備
首先就是先將有需要的library安裝一下。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_mldata
from chainer import cuda, Variable, FunctionSet, optimizers
import chainer.functions as F
import sys
plt.style.use('ggplot')
再來是將各種變數定義和設定。
# 使用隨機梯度下降法來學習的時候,每一次的批量大小
batchsize = 100
# 反覆學習的次數
n_epoch = 20
# 中間層的數量
n_units = 1000
通過使用Scikit Learn將MNIST中的手寫數字檔案下載下來。
# MNIST中的手寫數字資料下載
# #HOME/scikit_learn_data/mldata/mnist-original.mat にキャッシュされる
print 'fetch MNIST dataset'
mnist = fetch_mldata('MNIST original')
# mnist.data : 70,000件的784位元的矢量數據
mnist.data = mnist.data.astype(np.float32)
mnist.data /= 255 # 轉換成0-1的資料
# mnist.target : 正解資料
mnist.target = mnist.target.astype(np.int32)
把其中三個來出來畫畫看。
# 將手寫數字資料繪圖功能
def draw_digit(data):
size = 28
plt.figure(figsize=(2.5, 3))
X, Y = np.meshgrid(range(size),range(size))
Z = data.reshape(size,size) # convert from vector to 28x28 matrix
Z = Z[::-1,:] # flip vertical
plt.xlim(0,27)
plt.ylim(0,27)
plt.pcolor(X, Y, Z)
plt.gray()
plt.tick_params(labelbottom="off")
plt.tick_params(labelleft="off")
plt.show()
draw_digit(mnist.data[5])
draw_digit(mnist.data[12345])
draw_digit(mnist.data[33456])
資料為28×28, 784位元的矢量數據。
將資料集分成學習用和檢驗用兩份。
# 學習用資料為N個、檢驗用資料則設定為剩下的數量
N = 60000
x_train, x_test = np.split(mnist.data, [N])
y_train, y_test = np.split(mnist.target, [N])
N_test = y_test.size
3.2 模型定義
最後是模型定義啦。這邊開始才是重點,開始使用Chainer的類別和方法。
# Prepare multi-layer perceptron model
# 多層感知模型設定
# 輸入 784次元、輸出 10次元
model = FunctionSet(l1=F.Linear(784, n_units),
l2=F.Linear(n_units, n_units),
l3=F.Linear(n_units, 10))
輸入的手寫數字資料為784位元,所以輸入的因子就是784個,這次中間層我們設定是1000。輸出則是想要辨識出來的十個數字(0-9)。下方是這次模組的圖解。
順向傳播的構造為下方定義的forward()方法。
# Neural net architecture
# 神經網路構造
def forward(x_data, y_data, train=True):
x, t = Variable(x_data), Variable(y_data)
h1 = F.dropout(F.relu(model.l1(x)), train=train)
h2 = F.dropout(F.relu(model.l2(h1)), train=train)
y = model.l3(h2)
# 根據多類別分類的計算誤差方法:softmax方法
# 使用交叉熵方法來將誤差導出
return F.softmax_cross_entropy(y, t), F.accuracy(y, t)
這邊來說明下使用的各個方法。
Chainer的做法為將資料排列變換為Chainer中的Variable類別對象
x, t = Variable(x_data), Variable(y_data)
激活方法不是使用SIG模組方法,而是使用F.relu()方法。
F.relu(model.l1(x))
這個F.relu()是正規化方法 – Rectified Linear Unit function
f(x)=max(0,x)f(x)=max(0,x)
也就是說,
這種感覺。
畫圖碼在這裡。
# F.relu測試
x_data = np.linspace(-10, 10, 100, dtype=np.float32)
x = Variable(x_data)
y = F.relu(x)
plt.figure(figsize=(7,5))
plt.ylim(-2,10)
plt.plot(x.data, y.data)
plt.show()
是個簡單的方法。這個主要是為了計算量小、要求速度的情況設計
再來,這個relu()的輸出是要求F.dropout()作為輸入。
F.dropout(F.relu(model.l1(x)), train=train)
F.dropout()是Dropout: A Simple Way to Prevent Neural Networks from Overfitting中推薦的計算方式,可以防止系統中間層過度學習。
稍微來試試看吧。
# dropout(x, ratio=0.5, train=True) テスト
# x: 輸入值
# ratio: 輸出為0的機率
# train: False的情況下x會保持原樣返回
# return: ratioの確率で0を、1−ratioの確率で,x*(1/(1-ratio))の値を返す
n = 50
v_sum = 0
for i in range(n):
x_data = np.array([1,2,3,4,5,6], dtype=np.float32)
x = Variable(x_data)
dr = F.dropout(x, ratio=0.6,train=True)
for j in range(6):
sys.stdout.write( str(dr.data[j]) + ', ' )
print("")
v_sum += dr.data
# 輸出的平均與x_data大致一致
sys.stdout.write( str((v_sum/float(n))) )
2.5, 5.0, 7.5, 0.0, 0.0, 0.0,
2.5, 5.0, 7.5, 10.0, 0.0, 15.0,
0.0, 5.0, 7.5, 10.0, 12.5, 15.0,
・・・
0.0, 0.0, 7.5, 10.0, 0.0, 0.0,
2.5, 0.0, 7.5, 10.0, 0.0, 15.0,
[ 0.94999999 2.29999995 3. 3.5999999 7.25 5.69999981]
將該陣列[1,2,3,4,5,6]傳入F.dropout()方法,現在ratio是指dropout機率,ratio設定為0.6,也就是60%的機率會dropout並且輸出 0 。也就是40%機率會輸出值,這個時候,因為輸出值降低到只有40%,為了補足所以將值要乘上2.5後再輸出,也就是
(0×0.6+2.5×0.4)=1
然後用平均過後的新數值更新為舊數值,而上面的例子中最後一行是輸出的平均,將[1,2,3,4,5,6]輸入後經過五十回反覆計算後得到的最相近值。
在經過一層同樣的程序,最後輸出的輸出值就是y了
h2 = F.dropout(F.relu(model.l2(h1)), train=train)
y = model.l3(h2)
使用最後輸出的值和F.softmax_cross_entropy()方法來導出誤差,然後用和 F.accuracy()方法來計算出精確程度。
# 根據多類別分類的計算誤差方法:softmax方法
# 使用交叉熵方法來將誤差導出
return F.softmax_cross_entropy(y, t), F.accuracy(y, t)
ソフトマックス関数ですが、
のように定義される関数で、この関数を挟むことでy1,⋯,y10の10個の出力の総和が1となり、出力を確率として解釈することが可能になります。
なぜexp()exp()関数が使われているかというと、値がマイナスにならないように、ということと自分は理解しています。
おなじみexp()exp()関数は
のような形なので、マイナスの値を取りません。これにより値がマイナスにならず、かつ総和が1ということになり、確率と解釈できるということですね。
さっきのソフトマックス関数の出力値Ykを用いて交差エントロピー関数は
と表現されます。
Chainerのコードで言うと、
https://github.com/pfnet/chainer/blob/master/chainer/functions/softmax_cross_entropy.py
にある、
def forward_cpu(self, inputs):
x, t = inputs
self.y, = Softmax().forward_cpu((x,))
return -numpy.log(self.y[xrange(len(t)), t]).sum(keepdims=True) / t.size,
に相当します。
また、F.accuracy(y, t)
は出力と、教師データを照合して正答率を返しています。
3.3 Optimizer的設定
現在,模組決定好後該移到訓練的步驟了。
這邊最優化方式是使用Adam。
# Setup optimizer
optimizer = optimizers.Adam()
optimizer.setup(model.collect_parameters())
Adam的相關介紹可以到echizen_tm寫的30分でわかるAdam裡了解。
4.訓練的執行與結果
以上的準備後,透過小批量的學習來執行手寫數字的辨識,來看看這樣的辨識結果準度如何。
train_loss = []
train_acc = []
test_loss = []
test_acc = []
l1_W = []
l2_W = []
l3_W = []
# Learning loop
for epoch in xrange(1, n_epoch+1):
print 'epoch', epoch
# training
# 將依序的N個值用隨機的方式重新排列
perm = np.random.permutation(N)
sum_accuracy = 0
sum_loss = 0
# 將0~N個資料進行批量學習
for i in xrange(0, N, batchsize):
x_batch = x_train[perm[i:i+batchsize]]
y_batch = y_train[perm[i:i+batchsize]]
# 梯度初始化
optimizer.zero_grads()
# 通過正向傳播將誤差和準確度計算出來
loss, acc = forward(x_batch, y_batch)
# 通過逆向傳播計算梯度
loss.backward()
optimizer.update()
train_loss.append(loss.data)
train_acc.append(acc.data)
sum_loss += float(cuda.to_cpu(loss.data)) * batchsize
sum_accuracy += float(cuda.to_cpu(acc.data)) * batchsize
# 將訓練資料的誤差值與準確度表示出來
print 'train mean loss={}, accuracy={}'.format(sum_loss / N, sum_accuracy / N)
# evaluation
# 將測試資料的誤差與準確度計算出後用來了解概括上的性能
sum_accuracy = 0
sum_loss = 0
for i in xrange(0, N_test, batchsize):
x_batch = x_test[i:i+batchsize]
y_batch = y_test[i:i+batchsize]
# 通過正向傳播將誤差和準確度計算出來
loss, acc = forward(x_batch, y_batch, train=False)
test_loss.append(loss.data)
test_acc.append(acc.data)
sum_loss += float(cuda.to_cpu(loss.data)) * batchsize
sum_accuracy += float(cuda.to_cpu(acc.data)) * batchsize
# 將測試資料的誤差值與準確度表示出來
print 'test mean loss={}, accuracy={}'.format(sum_loss / N_test, sum_accuracy / N_test)
# 將學習後的參數記錄起來
l1_W.append(model.l1.W)
l2_W.append(model.l2.W)
l3_W.append(model.l3.W)
# 將準確度與誤差畫成圖像
plt.figure(figsize=(8,6))
plt.plot(range(len(train_acc)), train_acc)
plt.plot(range(len(test_acc)), test_acc)
plt.legend(["train_acc","test_acc"],loc=4)
plt.title("Accuracy of digit recognition.")
plt.plot()
每一層epoch的摘要結果如下。20回學習得到98.5%左右的高準確度辨識。
epoch 1
train mean loss=0.278375425202, accuracy=0.914966667456
test mean loss=0.11533634907, accuracy=0.964300005436
epoch 2
train mean loss=0.137060894324, accuracy=0.958216670454
test mean loss=0.0765812527167, accuracy=0.976100009084
epoch 3
train mean loss=0.107826075749, accuracy=0.966816672881
test mean loss=0.0749603212342, accuracy=0.97770000577
epoch 4
train mean loss=0.0939164237926, accuracy=0.970616674324
test mean loss=0.0672153823725, accuracy=0.980000005364
epoch 5
train mean loss=0.0831089563683, accuracy=0.973950009048
test mean loss=0.0705943618687, accuracy=0.980100004673
epoch 6
train mean loss=0.0752325405277, accuracy=0.976883343955
test mean loss=0.0732760328815, accuracy=0.977900006771
epoch 7
train mean loss=0.0719517664274, accuracy=0.977383343875
test mean loss=0.063611669606, accuracy=0.981900005937
epoch 8
train mean loss=0.0683009948514, accuracy=0.978566677173
test mean loss=0.0604036964733, accuracy=0.981400005221
epoch 9
train mean loss=0.0621755663728, accuracy=0.980550010701
test mean loss=0.0591542539285, accuracy=0.982400006652
epoch 10
train mean loss=0.0618313539471, accuracy=0.981183344225
test mean loss=0.0693172766063, accuracy=0.982900006175
epoch 11
train mean loss=0.0583098273944, accuracy=0.982000010014
test mean loss=0.0668152360269, accuracy=0.981600006819
epoch 12
train mean loss=0.054178619228, accuracy=0.983533344865
test mean loss=0.0614466062452, accuracy=0.982900005579
epoch 13
train mean loss=0.0532431817259, accuracy=0.98390001148
test mean loss=0.060112986485, accuracy=0.98400000751
epoch 14
train mean loss=0.0538122716064, accuracy=0.983266676267
test mean loss=0.0624165921964, accuracy=0.983300005198
epoch 15
train mean loss=0.0501562882114, accuracy=0.983833344777
test mean loss=0.0688113694015, accuracy=0.98310000658
epoch 16
train mean loss=0.0513108611095, accuracy=0.984533343514
test mean loss=0.0724038232205, accuracy=0.982200007439
epoch 17
train mean loss=0.0471463404785, accuracy=0.985666677058
test mean loss=0.0612579581685, accuracy=0.983600008488
epoch 18
train mean loss=0.0460166006556, accuracy=0.986050010125
test mean loss=0.0654888718335, accuracy=0.984400007725
epoch 19
train mean loss=0.0458772557077, accuracy=0.986433342795
test mean loss=0.0602016936944, accuracy=0.984400007129
epoch 20
train mean loss=0.046333729005, accuracy=0.986433343093
test mean loss=0.0621869922416, accuracy=0.985100006461
各個批量的辨識準確度和誤差繪成圖後如下。紅色是訓練資料,藍色是測試資料
以前有寫過一個【機械学習】k-nearest neighbor method(k最近傍法)を自力でpythonで書いて、手書き数字の認識をする的文章,同樣是手寫數字判斷,那個時候的準確度大概是97%左右,這個準確度稍微更高了一些些。
這個Chainer全部是用Python進行操作,Pythonista也是非常令人開心的框架。另外,這個應該還不能算是”Deep” learning,僅能算是 前饋神經網路,如果可以,最近應該會再寫一篇關於「深度」的文章。
5.驗證
我們來試試看辨識100個手寫數字影像。
隨機抽出100影像資料來測試後,結果是基本上都答對了,試了幾回100個影像辨識測試,看到了一個錯誤的辨識結果,將這個例子貼在下面,不知為什麼,人就是會有想測試的心態(笑)。
plt.style.use('fivethirtyeight')
def draw_digit3(data, n, ans, recog):
size = 28
plt.subplot(10, 10, n)
Z = data.reshape(size,size) # convert from vector to 28x28 matrix
Z = Z[::-1,:] # flip vertical
plt.xlim(0,27)
plt.ylim(0,27)
plt.pcolor(Z)
plt.title("ans=%d, recog=%d"%(ans,recog), size=8)
plt.gray()
plt.tick_params(labelbottom="off")
plt.tick_params(labelleft="off")
plt.figure(figsize=(15,15))
cnt = 0
for idx in np.random.permutation(N)[:100]:
xxx = x_train[idx].astype(np.float32)
h1 = F.dropout(F.relu(model.l1(Variable(xxx.reshape(1,784)))), train=False)
h2 = F.dropout(F.relu(model.l2(h1)), train=False)
y = model.l3(h2)
cnt+=1
draw_digit3(x_train[idx], cnt, y_train[idx], np.argmax(y.data))
plt.show
2 thoughts on “chainer 使用範例 教學”