今年來人工智能的概念越來越火,AlphaGo以4:1擊敗李世石更是起到推波助瀾的作用。作為一個開挖掘機的菜鳥,深深感到不學習一下deep learning早晚要被淘汰。
既然要開始學,當然是搭一個深度神經網絡跑幾個數據集感受一下作為入門最直觀了。自己寫代碼實現的話debug的過程和運行效率都會很憂傷,我也不知道怎么調用GPU… 所以還是站在巨人的肩膀上,用現成的框架吧。粗略了解一下,現在比較知名的有caffe、mxnet、tensorflow等等。選哪個呢?對我來說選擇的標準就兩個,第一要容易安裝(想盡快入門的迫切心情實在難以忍受一大堆的配置安裝…);第二文檔要齊全(這應該是廢話 - -)。這幾個大名鼎鼎的框架文檔都是比較齊全的,那就看最容易安裝的。看了幾個文檔,tensorflow算是最容易安裝的了。基本就是pip intall 給定的URL
就可以了。安裝方式的文檔可以在tensorflow安裝教程上查看。
tensorflow基本概念與用法
tensorflow直譯過來就是張量流。去年google剛推出tensorflow的時候我就納悶,為什么深度學習會牽扯到張量,以前學彈塑性力學的時候就是一大堆張量看的很煩…不過還好要理解tensorflow里的tensor完全不用理會那些。先來看一下官方文檔的說明:
class tf.Tensor
Represents a value produced by an Operation.
A Tensor is a symbolic handle to one of the outputs of an Operation. It does not hold the values of that operation's output, but instead provides a means of computing those values in a TensorFlow Session.
首先,Tensor代表了執行一個操作(運算)所產生的值。其次,一個Tensor實例并不會保存具體的值,而只是代表了產生這些值的運算方式。好像有些拗口,也就是說假如有一個加法操作add,令c = add(1,1)。那么c就是一個tensor實例了,代表了1+1的結果,但是它并沒有存儲2這個具體的值,它只知道它代表1+1這個運算。從這里也可以看出,tensorflow里的api都是惰性求值,等真正需要知道具體的值的時候,才會執行計算,其他時候都是在定義計算的過程。
Tensor可以代表從常數一直到N維數組的值。
Flow指的是,指的是tensorflow這套框架里的數據傳遞全部都是tensor,也就是運算的輸入,輸出都是tensor。
常用操作
這里只是簡單介紹一下在后面定義卷積神經網絡的時候會用到的東西。想要了解更詳細的內容還得參考官網上的文檔。
首先import tensorflow as tf,后面的tf就代表tensorflow啦。
常數
tf.constant 是一個Operation,用來產生常數,可以產生scalar與N-D array. a是一個tensor,代表了由constant這個Operation所產生的標量常數值的過程。 b就是代表了產生一個2*2的array的過程。
1
2
|
a = tf.constant( 3 ) b = tf.constant( 3 ,shape = [ 2 , 2 ]) |
變量
變量代表了神經網絡中的參數,在優化計算的過程中需要被改變。tf.Variable當然也是一個Operation,用來產生一個變量,構造函數需要傳入一個Tensor對象,傳入的這個Tensor對象就決定了這個變量的值的類型(float 或 int)與shape。
變量雖然與Tensor有不同的類型,但是在計算過程中是與Tensor一樣可以作為輸入輸出的。(可以理解為Tensor的派生類,但是實際上可能并不是這樣,我還沒有看源碼)
變量在使用前都必須初始化。
1
|
w = tf.Variable(b) |
Operation
其實Operation不應該單獨拿出來說,因為之前的tf.constant和tf.Variable都是Op,不過還是說一下常規的操作,比如tf.matmul執行矩陣計算,tf.conv2d用于卷積計算,Op的詳細用法以及其他的Op可以參考api文檔。
1
2
|
tf.matmul(m,n) tf.conv2d(...) |
TensorFlow的計算由不同的Operation組成,比如下圖
定義了6*(3+5)這個計算過程。6、3、5其實也是Op,這在前面介紹過了。
卷積神經網絡用于人臉關鍵點識別
寫到這里終于要開始進入正題了,先從CNN做起吧。Tensorflow的tutorial里面有介紹用CNN(卷積神經網絡)來識別手寫數字,直接把那里的代碼copy下來跑一遍也是可以的。但是那比較沒有意思,kaggle上有一個人臉關鍵點識別的比賽,有數據集也比較有意思,就拿這個來練手了。
定義卷積神經網絡
首先是定義網絡結構,在這個例子里我用了3個卷積層,第一個卷積層用3∗3的卷積核,后面兩個用2∗2的卷積核。每個卷積層后面都跟max_pool池化層,之后再跟3個全連接層(兩個隱層一個輸出層)。每個卷積層的feature_map分別用32、64、128。
產生權值的函數代碼如下
1
2
3
4
5
6
7
8
9
|
#根據給定的shape定義并初始化卷積核的權值變量 def weight_variable(shape): initial = tf.truncated_normal(shape, stddev = 0.1 ) return tf.Variable(initial) #根據shape初始化bias變量 def bias_variable(shape): initial = tf.constant( 0.1 , shape = shape) return tf.Variable(initial) |
定義卷積運算的代碼如下。對tf.nn.con2d()的參數還是要說明一下
1. x是輸入的樣本,在這里就是圖像。x的shape=[batch, height, width, channels]。
- - batch是輸入樣本的數量
- - height, width是每張圖像的高和寬
- - channels是輸入的通道,比如初始輸入的圖像是灰度圖,那么channels=1,如果是rgb,那么channels=3。對于第二層卷積層,channels=32。
2. W表示卷積核的參數,shape的含義是[height,width,in_channels,out_channels]。
3. strides參數表示的是卷積核在輸入x的各個維度下移動的步長。了解CNN的都知道,在寬和高方向stride的大小決定了卷積后圖像的size。這里為什么有4個維度呢?因為strides對應的是輸入x的維度,所以strides第一個參數表示在batch方向移動的步長,第四個參數表示在channels上移動的步長,這兩個參數都設置為1就好。重點就是第二個,第三個參數的意義,也就是在height于width方向上的步長,這里也都設置為1。
4. padding參數用來控制圖片的邊距,'SAME'表示卷積后的圖片與原圖片大小相同,'VALID'的話卷積以后圖像的高為Heightout=Height原圖−Height卷積核+1/StrideHeight, 寬也同理。
1
2
|
def conv2d(x,W): return tf.nn.cov2d(x,W,strides = [ 1 , 1 , 1 , 1 ],padding = 'VALID' ) |
接著是定義池化層的代碼,這里用2∗2的max_pool。參數ksize定義pool窗口的大小,每個維度的意義與之前的strides相同,所以實際上我們設置第二個,第三個維度就可以了。
1
2
|
def max_pool_2x2(x): return tf.nn.max_pool(x, ksize = [ 1 , 2 , 2 , 1 ],strides = [ 1 , 2 , 2 , 1 ], padding = 'SAME' ) |
定義好產生權重、卷積、池化的函數以后就要開始組裝這個卷積神經網絡了。定義之前再定義一下輸入樣本x與對應的目標值y_。這里用了tf.placeholder表示此時的x與y_是指定shape的站位符,之后在定義網絡結構的時候并不需要真的輸入了具體的樣本,只要在求值的時候feed進去就可以了。激活函數用relu,api也就是tf.nn.relu。
keep_prob是最后dropout的參數,dropout的目的是為了抗過擬合。
rmse是損失函數,因為這里的目的是為了檢測人臉關鍵點的位置,是回歸問題,所以用root-mean-square-error。并且最后的輸出層不需要套softmax,直接輸出y值就可以了。
這樣就組裝好了一個卷積神經網絡。后續的步驟就是根據輸入樣本來train這些參數啦。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
x = tf.placeholder( "float" , shape = [ None , 96 , 96 , 1 ]) y_ = tf.placeholder( "float" , shape = [ None , 30 ]) keep_prob = tf.placeholder( "float" ) def model(): W_conv1 = weight_variable([ 3 , 3 , 1 , 32 ]) b_conv1 = bias_variable([ 32 ]) h_conv1 = tf.nn.relu(conv2d(x, W_conv1) + b_conv1) h_pool1 = max_pool_2x2(h_conv1) W_conv2 = weight_variable([ 2 , 2 , 32 , 64 ]) b_conv2 = bias_variable([ 64 ]) h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) h_pool2 = max_pool_2x2(h_conv2) W_conv3 = weight_variable([ 2 , 2 , 64 , 128 ]) b_conv3 = bias_variable([ 128 ]) h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3) h_pool3 = max_pool_2x2(h_conv3) W_fc1 = weight_variable([ 11 * 11 * 128 , 500 ]) b_fc1 = bias_variable([ 500 ]) h_pool3_flat = tf.reshape(h_pool3, [ - 1 , 11 * 11 * 128 ]) h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1) W_fc2 = weight_variable([ 500 , 500 ]) b_fc2 = bias_variable([ 500 ]) h_fc2 = tf.nn.relu(tf.matmul(h_fc1, W_fc2) + b_fc2) h_fc2_drop = tf.nn.dropout(h_fc2, keep_prob) W_fc3 = weight_variable([ 500 , 30 ]) b_fc3 = bias_variable([ 30 ]) y_conv = tf.matmul(h_fc2_drop, W_fc3) + b_fc3 rmse = tf.sqrt(tf.reduce_mean(tf.square(y_ - y_conv))) return y_conv, rmse |
訓練卷積神經網絡
讀取訓練數據
定義好卷積神經網絡的結構之后,就要開始訓練。訓練首先是要讀取訓練樣本。下面的代碼用于讀取樣本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
import pandas as pd import numpy as np TRAIN_FILE = 'training.csv' TEST_FILE = 'test.csv' SAVE_PATH = 'model' VALIDATION_SIZE = 100 #驗證集大小 EPOCHS = 100 #迭代次數 BATCH_SIZE = 64 #每個batch大小,稍微大一點的batch會更穩定 EARLY_STOP_PATIENCE = 10 #控制early stopping的參數 def input_data(test = False ): file_name = TEST_FILE if test else TRAIN_FILE df = pd.read_csv(file_name) cols = df.columns[: - 1 ] #dropna()是丟棄有缺失數據的樣本,這樣最后7000多個樣本只剩2140個可用的。 df = df.dropna() df[ 'Image' ] = df[ 'Image' ]. apply ( lambda img: np.fromstring(img, sep = ' ' ) / 255.0 ) X = np.vstack(df[ 'Image' ]) X = X.reshape(( - 1 , 96 , 96 , 1 )) if test: y = None else : y = df[cols].values / 96.0 #將y值縮放到[0,1]區間 return X, y #最后生成提交結果的時候要用到 keypoint_index = { 'left_eye_center_x' : 0 , 'left_eye_center_y' : 1 , 'right_eye_center_x' : 2 , 'right_eye_center_y' : 3 , 'left_eye_inner_corner_x' : 4 , 'left_eye_inner_corner_y' : 5 , 'left_eye_outer_corner_x' : 6 , 'left_eye_outer_corner_y' : 7 , 'right_eye_inner_corner_x' : 8 , 'right_eye_inner_corner_y' : 9 , 'right_eye_outer_corner_x' : 10 , 'right_eye_outer_corner_y' : 11 , 'left_eyebrow_inner_end_x' : 12 , 'left_eyebrow_inner_end_y' : 13 , 'left_eyebrow_outer_end_x' : 14 , 'left_eyebrow_outer_end_y' : 15 , 'right_eyebrow_inner_end_x' : 16 , 'right_eyebrow_inner_end_y' : 17 , 'right_eyebrow_outer_end_x' : 18 , 'right_eyebrow_outer_end_y' : 19 , 'nose_tip_x' : 20 , 'nose_tip_y' : 21 , 'mouth_left_corner_x' : 22 , 'mouth_left_corner_y' : 23 , 'mouth_right_corner_x' : 24 , 'mouth_right_corner_y' : 25 , 'mouth_center_top_lip_x' : 26 , 'mouth_center_top_lip_y' : 27 , 'mouth_center_bottom_lip_x' : 28 , 'mouth_center_bottom_lip_y' : 29 } |
開始訓練
執行訓練的代碼如下,save_model用于保存當前訓練得到在驗證集上loss最小的模型,方便以后直接拿來用。
tf.InteractiveSession()用來生成一個Session,(好像是廢話…)。Session相當于一個引擎,TensorFlow框架要真正的進行計算,都要通過Session引擎來啟動。
tf.train.AdamOptimizer是優化的算法,Adam的收斂速度會比較快,1e-3是learning rate,這里先簡單的用固定的。minimize就是要最小化的目標,當然是最小化均方根誤差了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
def save_model(saver,sess,save_path): path = saver.save(sess, save_path) print 'model save in :{0}' . format (path) if __name__ = = '__main__' : sess = tf.InteractiveSession() y_conv, rmse = model() train_step = tf.train.AdamOptimizer( 1e - 3 ).minimize(rmse) #變量都要初始化 sess.run(tf.initialize_all_variables()) X,y = input_data() X_valid, y_valid = X[:VALIDATION_SIZE], y[:VALIDATION_SIZE] X_train, y_train = X[VALIDATION_SIZE:], y[VALIDATION_SIZE:] best_validation_loss = 1000000.0 current_epoch = 0 TRAIN_SIZE = X_train.shape[ 0 ] train_index = range (TRAIN_SIZE) random.shuffle(train_index) X_train, y_train = X_train[train_index], y_train[train_index] saver = tf.train.Saver() print 'begin training..., train dataset size:{0}' . format (TRAIN_SIZE) for i in xrange (EPOCHS): random.shuffle(train_index) #每個epoch都shuffle一下效果更好 X_train, y_train = X_train[train_index], y_train[train_index] for j in xrange ( 0 ,TRAIN_SIZE,BATCH_SIZE): print 'epoch {0}, train {1} samples done...' . format (i,j) train_step.run(feed_dict = {x:X_train[j:j + BATCH_SIZE], y_:y_train[j:j + BATCH_SIZE], keep_prob: 0.5 }) #電腦太渣,用所有訓練樣本計算train_loss居然死機,只好注釋了。 #train_loss = rmse.eval(feed_dict={x:X_train, y_:y_train, keep_prob: 1.0}) validation_loss = rmse. eval (feed_dict = {x:X_valid, y_:y_valid, keep_prob: 1.0 }) print 'epoch {0} done! validation loss:{1}' . format (i, validation_loss * 96.0 ) if validation_loss < best_validation_loss: best_validation_loss = validation_loss current_epoch = i save_model(saver,sess,SAVE_PATH) #即時保存最好的結果 elif (i - current_epoch) > = EARLY_STOP_PATIENCE: print 'early stopping' break |
在測試集上預測
下面的代碼用于預測test.csv里面的人臉關鍵點,最后的y值要乘以96,因為之前縮放到[0,1]區間了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
X,y = input_data(test = True ) y_pred = [] TEST_SIZE = X.shape[ 0 ] for j in xrange ( 0 ,TEST_SIZE,BATCH_SIZE): y_batch = y_conv. eval (feed_dict = {x:X[j:j + BATCH_SIZE], keep_prob: 1.0 }) y_pred.extend(y_batch) print 'predict test image done!' output_file = open ( 'submit.csv' , 'w' ) output_file.write( 'RowId,Location\n' ) IdLookupTable = open ( 'IdLookupTable.csv' ) IdLookupTable.readline() for line in IdLookupTable: RowId,ImageId,FeatureName = line.rstrip().split( ',' ) image_index = int (ImageId) - 1 feature_index = keypoint_index[FeatureName] feature_location = y_pred[image_index][feature_index] * 96 output_file.write( '{0},{1}\n' . format (RowId,feature_location)) output_file.close() IdLookupTable.close() |
結果
用這個結構的卷積神經網絡訓練出來的模型,在測試集上預測的結果提交以后的成績是3.4144,在kaggle的leaderboard上是41名,初試CNN,感覺還可以了。這只是數據,還是找一些現實的照片來試試這個模型如何,所以我找了一張anglababy的,標識出來的關鍵點感覺還算靠譜。基于TensorFlow的卷積神經網絡先寫到這了,有什么遺漏的想起來再補充,之后對深度學習更了解了,再寫寫CNN的原理,bp的推導過程之類的。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://blog.csdn.net/thriving_fcl/article/details/50909109