比官方更簡潔的Tensorflow入門教程
宣告:
Tensorflow入門
Tensorflow graphs
Tensorflow是基於graph的平行計算模型。關於graph的理解可以參考官方文件。舉個例子,計算,我們可以將算式拆分成一下:
d = b + c
e = c + 2
a = d * e
轉換成graph後的形式為:
講一個簡單的算式搞成這樣確實大材小用,但是我們可以通過這個例子發現:和是不相關的,也就是可以平行計算。對於更復雜的CNN和RNN,graph的平行計算的能力將得到更好的展現。
實際中,基於Tensorflow構建的三層(單隱層)神經網路如下圖所示:
Tensorflow data flow graph
上圖中,圓形或方形的節點被稱為node,在node中流動的資料流被稱為張量(tensor)。更多關於tensor的描述見官方文件。
0階張量 == 標量
1階張量 == 向量(一維陣列)
2階張量 == 二維陣列
…
n階張量 == n維陣列
tensor與node之間的關係:
如果輸入tensor的維度是,表示有5000個訓練樣本,每個樣本有64個特徵,則輸入層必須有64個node來接受這些特徵。
上圖表示的三層網路包括:輸入層(圖中的input)、隱藏層(這裡取名為ReLU layer表示它的啟用函式是ReLU)、輸出層(圖中的Logit Layer)。
可以看到,每一層中都有相關tensor流入Gradient節點計算梯度,然後這些梯度tensor進入SGD Trainer節點進行網路優化(也就是update網路引數)。
Tensorflow正是通過graph表示神經網路,實現網路的平行計算,提高效率。下面我們將通過一個簡單的例子來介紹TensorFlow的基礎語法。
A Simple TensorFlow example
用Tensorflow計算, 1. 定義資料:
import tensorflow as tf
# 首先,建立一個TensorFlow常量=>2
const = tf.constant(2.0 , name='const')
# 建立TensorFlow變數b和c
b = tf.Variable(2.0, name='b')
c = tf.Variable(1.0, dtype=tf.float32, name='c')
如上,TensorFlow中,使用tf.constant()
定義常量,使用tf.Variable()
定義變數。Tensorflow可以自動進行資料型別檢測,比如:賦值2.0就預設為tf.float32
,但最好還是顯式地定義。更多關於TensorFlow資料型別的介紹檢視官方文件。
2. 定義運算(也稱TensorFlow operation):
# 建立operation
d = tf.add(b, c, name='d')
e = tf.add(c, const, name='e')
a = tf.multiply(d, e, name='a')
發現了沒,在TensorFlow中,都有其特殊的函式表示。實際上,TensorFlow定義了足夠多的函式來表示所有的數學運算,當然也對部分數學運算進行了運算子過載,但保險起見,我還是建議你使用函式代替運算子。
!!TensorFlow中所有的變數必須經過初始化才能使用,初始化方式分兩步:
- 定義初始化operation
- 執行初始化operation
# 1. 定義init operation
init_op = tf.global_variables_initializer()
以上已經完成TensorFlow graph的搭建,下一步即計算並輸出。
執行graph需要先呼叫tf.Session()
函式建立一個會話(session)。session就是我們與graph互動的handle。更多關於session的介紹見官方文件。
# session
with tf.Session() as sess:
# 2. 執行init operation
sess.run(init_op)
# 計算
a_out = sess.run(a)
print("Variable a is {}".format(a_out))
值得一提的是,TensorFlow有一個極好的視覺化工具TensorBoard,詳見官方文件。將上面例子的graph視覺化之後的結果為:
The TensorFlow placeholder
對上面例子的改進:使變數b可以接收任意值。TensorFlow中接收值的方式為佔位符(placeholder),通過tf.placeholder()
建立。
# 建立placeholder
b = tf.placeholder(tf.float32, [None, 1], name='b')
第二個引數值為[None, 1],其中None表示不確定,即不確定第一個維度的大小,第一維可以是任意大小。特別對應tensor數量(或者樣本數量),輸入的tensor數目可以是32、64…
現在,如果得到計算結果,需要在執行過程中feed佔位符b的值,具體為將a_out = sess.run(a)
改為:
a_out = sess.run(a, feed_dict={b: np.arange(0, 10)[:, np.newaxis]})
輸出:
Variable a is [[ 3.]
[ 6.]
[ 9.]
[ 12.]
[ 15.]
[ 18.]
[ 21.]
[ 24.]
[ 27.]
[ 30.]]
A Neural Network Example
神經網路的例子,資料集為MNIST資料集。
1. 載入資料:
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
one_hot=True
表示對label進行one-hot編碼,比如標籤4可以表示為[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]。這是神經網路輸出層要求的格式。
Setting things up
2. 定義超引數和placeholder
# 超引數
learning_rate = 0.5
epochs = 10
batch_size = 100
# placeholder
# 輸入圖片為28 x 28 畫素 = 784
x = tf.placeholder(tf.float32, [None, 784])
# 輸出為0-9的one-hot編碼
y = tf.placeholder(tf.float32, [None, 10])
再次強調,[None, 784]中的None表示任意值,特別對應tensor數目。
3. 定義引數w和b
# hidden layer => w, b
W1 = tf.Variable(tf.random_normal([784, 300], stddev=0.03), name='W1')
b1 = tf.Variable(tf.random_normal([300]), name='b1')
# output layer => w, b
W2 = tf.Variable(tf.random_normal([300, 10], stddev=0.03), name='W2')
b2 = tf.Variable(tf.random_normal([10]), name='b2')
在這裡,要了解全連線層的兩個引數w和b都是需要隨機初始化的,tf.random_normal()
生成正態分佈的隨機數。
4. 構造隱層網路
# hidden layer
hidden_out = tf.add(tf.matmul(x, W1), b1)
hidden_out = tf.nn.relu(hidden_out)
上面程式碼對應於公式:
5. 構造輸出(預測值)
# 計算輸出
y_ = tf.nn.softmax(tf.add(tf.matmul(hidden_out, W2), b2))
對於單標籤多分類任務,輸出層的啟用函式都是tf.nn.softmax()
。更多關於softmax的知識見維基百科。
6. BP部分—定義loss
損失為交叉熵,公式為
公式分為兩步:
- 對n個標籤計算交叉熵
- 對m個樣本取平均
y_clipped = tf.clip_by_value(y_, 1e-10, 0.9999999)
cross_entropy = -tf.reduce_mean(tf.reduce_sum(y * tf.log(y_clipped) + (1 - y) * tf.log(1 - y_clipped), axis=1))
7. BP部分—定義優化演算法
# 建立優化器,確定優化目標
optimizer = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimizer(cross_entropy)
TensorFlow中更多優化演算法詳見官方文件。
8. 定義初始化operation和準確率node
# init operator
init_op = tf.global_variables_initializer()
# 建立準確率節點
correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
correct_predicion
會返回一個的tensor,tensor的值為True/False表示是否正確預測。
Setting up the trianing
9. 開始訓練
# 建立session
with tf.Session() as sess:
# 變數初始化
sess.run(init)
total_batch = int(len(mnist.train.labels) / batch_size)
for epoch in range(epochs):
avg_cost = 0
for i in range(total_batch):
batch_x, batch_y = mnist.train.next_batch(batch_size=batch_size)
_, c = sess.run([optimizer, cross_entropy], feed_dict={x: batch_x, y: batch_y})
avg_cost += c / total_batch
print("Epoch:", (epoch + 1), "cost = ", "{:.3f}".format(avg_cost))
print(sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels}))
輸出:
Epoch: 1 cost = 0.586
Epoch: 2 cost = 0.213
Epoch: 3 cost = 0.150
Epoch: 4 cost = 0.113
Epoch: 5 cost = 0.094
Epoch: 6 cost = 0.073
Epoch: 7 cost = 0.058
Epoch: 8 cost = 0.045
Epoch: 9 cost = 0.036
Epoch: 10 cost = 0.027
Training complete!
0.9787
通過TensorBoard視覺化訓練過程: