Tensorflow快速入門
PART
01 Tensorflow簡介
引言
實踐深度學習肯定要至少學習並掌握一個深度學習框架。這裡我們介紹一個最流行的深度學習框架:Tensorflow。Tensorflow是谷歌公司在2015年9月開源的一個深度學習框架。雖然我們稱Tensorflow為一種深度學習框架,但是看看官網:
圖1 Tensorflow官網介面
可以看到,從功能上看,Tensorflow定義為專為機器智慧打造的開源軟體庫。而從內部機制上,Tensorflow定義為一個使用資料流圖進行數值計算的開源軟體庫。這說明Tensorflow的功能遠不是隻應用在深度學習領域,但是通常我們還是用Tensorflow來搭建深度學習模型。其他流行的深度學習框架也有很多,如PyTorch, MXnet, Theano,Caffe等,還有根據這些框架衍生出來的高階深度學習框架,如Keras, TFLearn, TensorLayer等。其實各個深度學習框架都有自己獨特的優勢,網上也有針對各個框架的對比。但是,不管在學術界還是在工業界,Tensorflow絕對是學習深度學習的一個較好的選擇,這裡談一下其優勢:
1平臺支援性良好,無論是Windows, Linux, 和macOS等系統,還是IOS和Android;
2提供簡單且靈活的Python API介面,內部使用C++進行優化;
3豐富的運算元,可以很容易搭建各種深度學習模型,如CNN和RNN模型;
4提供視覺化工具TensorBoard,這個是TF獨有的優勢;
5支援CPU和GPU,支援分散式多機多卡訓練;
總之,TF的優勢還是挺多的,基本上的需求都可以滿足,畢竟背後有強大的谷歌大佬在維護。接下來,就從入門TF開始吧。
PART
02 Tensorflow 概念介紹
張量對於任何深度學習框架,你都要先了解張量(Tensor)的概念,張量可以看成是向量和矩陣的衍生。向量是一維的,而矩陣是二維的,對於張量其可以是任何維度的。一般情況下,你要懂得張量的兩個屬性:形狀(shape)和秩(rank)。秩很好理解,就是有多少個維度;而形狀是指的每個維度的大小。下面是常見的張量的形象圖表示:
圖2
形象的張量圖
前面說過,從內部機制上來說,TF就是建立資料流圖來進行數值計算。所以,當你使用TF來搭建模型時,其實主要涉及兩個方面:根據模型建立計算圖,然後送入資料執行計算圖得到結果。計算圖computational graph是TF中很重要的一個概念,其是由一系列節點(nodes)組成的圖模型,每個節點對應的是TF的一個運算元(operation)。每個運算元會有輸入與輸出,並且輸入和輸出都是張量。所以我們使用TF的運算元可以構建自己的深度學習模型,其背後就是一個計算圖。還有一點這個計算圖是靜態的,意思是這個計算圖每個節點接收什麼樣的張量和輸出什麼樣的張量已經固定下來。要執行這個計算圖,你需要開啟一個會話(session),在session中這個計算圖才可以真正執行。我們用一個簡單的例子來說明圖模型的機理,首先定義一個計算圖:
import tensorflow as tf
a = tf.constant(5, name="input_a")
b = tf.constant(3, name="input_b")
c = tf.multiply(a, b, name="mul_c")
d = tf.add(a, b, name="add_d")
e = tf.add(c, d, name="add_e")
程式碼很簡單,我們引入TF庫,然後利用TF的基本算術運算元(和Numpy介面一致)建立了一個簡單的計算圖如下圖所示。其中每個圓圈代表的是每個運算元定義的節點。節點之間的邊就是張量,這裡其實都是標量(scalers)。
圖3 計算圖例項
我們開啟會話並執行這個計算圖得到e:
sess = tf.Session() # 建立會話print(sess.run(e)) # 執行計算圖得到esess.close() # 關閉會話
當我們想得到e的值時,TF會根據建立好的計算圖查詢e節點所有的依賴節點,這可以看成一個抽取子計算圖的過程。得到這個子計算圖就可以從a和b節點開始計算最終得到e的值。還有一點TF會對這個計算過程做並行化優化,不過這都是底層的事了。TF更靈活的一點你可以指定各個節點在哪個具體的裝置上執行,如CPU和GPU。理解好計算圖,就掌握了TF的基本運作原理,下面說說TF中重要的幾個概念。
TF建立張量對於TF的計算圖,所有的資料都是張量。TF也提供了建立一些常用張量的函式,並且這些函式和Numpy的介面是一致的。如建立全0或者全1的張量:
# tf.zeros(shape, dtype=tf.float32, name=None)a = tf.zeros([2, 3], tf.int32) # [[0, 0, 0], [0, 0, 0]]# tf.ones(shape, dtype=tf.float32, name=None)b = tf.ones([2, 3], tf.int32) # [[1, 1, 1], [1, 1, 1]]
建立張量時一般情況下,需要指定張量的shape,資料型別以及名字。其中資料型別是和Numpy是基本對應起來的。TF同時也提供了隨機產生的張量,如常用的高斯分佈:
#
tf.random_normal(shape, mean=0.0, stddev=1.0, dtype=tf.float32, seed=None, name=None)
a = tf.random_normal([5, 5], mean=0.0, stddev=1.0) # 均值為0,標準差為1的高斯分佈
# tf.random_uniform(shape, minval=0, maxval=None, dtype=tf.float32, seed=None, name = None)
b = tf.random_uniform([5, 5], minval=0, maxval=1)
# [0, 1]內的均勻分佈
在TF中,上面這些張量的用處一般是用於初始化模型的引數,後面我們會談到。但是有時候,你可以想建立一些自己定義的常量張量,這時可以使用tf.constant:
# tf.constant(value, dtype=None, shape=None, name='Const', verify_shape=False)a = tf.constant([[0, 1], [2, 3]]) # [[0, 1], [2, 3]]
對於cosntant張量來說,如果你不指定資料型別,那麼會根據輸入的資料自動推斷。對於constant張量,一般是不會使用的,因為constant張量不夠靈活,而且它如果太大,會導致你定義的計算圖太龐大。那麼當我們想訓練模型時,怎麼樣可以送入自己的資料呢?TF提供了一個特殊的運算元:tf.placeholder。翻譯成中文就是佔位符,其含義是你可以先定義張量的shape和資料型別,但是具體的資料可以等執行計算圖時再送入,這是比較靈活的。還是看例子:
# 定義一個佔位張量a = tf.placeholder(tf.int32, [3,])
# 定義一個costant張量b = tf.constant([1, 1, 1])
# 計算a+bc = a + b
with tf.Session() as sess:
# 給佔位張量送入資料,並執行計算圖print(sess.run(c, feed_dict={a: [1, 2, 3]})) #
[2, 3, 4]
一般情況下可以使用placeholder封裝訓練資料,你只需要定義訓練資料的佔位張量,在真正訓練時送入真實的訓練樣本就可以了。
變數對於模型來說,最重要的一部分是模型引數,我們說訓練模型,就是找到最優的模型引數。TF中提供了一個變數類:tf.Variable,主要是用來定義模型引數。雖然這是一個類,你可以將其看成一個帶有狀態的張量,就是儲存的實際值是可以被改變的。定義一個變數時,你需要提供初始值,有以下幾種方式:
a = tf.Variable([[2, 3], [1, 2]]) # 初始值為[[2, 3], [1, 2]]b = tf.Variable((tf.zeros([10, 10]))) # 初始值為全0,shape為[10,10]的張量c = tf.Variable(tf.random_normal([5, 5], mean=0.0, stddev=1.0)) # 初始值為標準正態分佈,shape為[5, 5]的張量
對於定義的變數,實際的靜態計算圖中並沒有實際儲存值,所以在使用前一定要進行初始化,這裡有一個快捷方式,把定義的所有變數都初始化:
init = tf.global_variables_initializer() # 初始化所有變數的運算元with tf.Session() as sess:
sess.run(init) # 執行初始化,此時變數被填值
變數是有狀態的,那麼怎麼是去改變其值呢?變數提供了一個方法:assign,舉例說明:
a = tf.Variable([1, 1])
b = tf.Variable([2, 2])
assign_op = a.assign(b) #
a的值用b替換init = tf.global_variables_initializer() # 初始化所有變數的運算元with tf.Session() as sess:
sess.run(init) # 執行初始化,此時變數被填值print(sess.run(a))
# [1, 1]sess.run(assign_op) # 執行assignprint(sess.run(a))
# [2, 2]
TF實現了自動梯度的功能,前面的文章我們說過自動梯度有很多實現方式,TF是基於反向模式的自動梯度,或者說大家說的BP演算法。簡單地說一下,就是我們建立模型時一般是一個前向計算圖,其實每個運算元都實現了反向過程,那麼是很容易自動建立反向計算圖。這樣,就可以實現自動梯度了。TF中計算梯度的函式是tf.gradient,還是例子說話:
x = tf.constant([2.0, 1.0])
y = tf.constant([1.0, 2.0])
z = x * y + x * x
dx, dy = tf.gradients(z, [x, y]) # 求z關於x,y的導數with tf.Session() as sess:
dx_v, dy_v = sess.run([dx, dy])
print(dx_v) #
[5.0, 4.0]print(dy_v)
# [2.0, 1.0]
但是其實我們一般用不到這個函式,這是因為TF提供了各種各樣的優化器,如GradientDescentOptimizer, AdamOptimizer等,這些優化器內部封裝了梯度的計算,並且實現了引數的更新。下面的例子會使用。
PART
03 Tensorflow實戰
使用TF實現LR模型最後我們使用TF實現LR模型用於MNIST資料集上,程式碼如下:
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
# 載入mnist資料集mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
# 定義輸入與輸出x = tf.placeholder(tf.float32, [None, 784])
y = tf.placeholder(tf.float32, [None, 10])
# 定義模型引數W = tf.Variable(tf.random_normal([784, 10]))
b = tf.Variable(tf.zeros([10,]))
# 定義模型output = tf.nn.xw_plus_b(x, W, b)
prob = tf.nn.softmax(output)
# 定義loss,採用交叉熵cross_entropy = -tf.reduce_mean(tf.reduce_sum(y * tf.log(prob), axis=1))
# 定義優化器learning_rate = 1e-04
train_op = tf.train.AdamOptimizer(learning_rate).minimize(cross_entropy)
# 定義精確度correct_pred = tf.equal(tf.argmax(y, axis=1),
tf.argmax(output, axis=1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# 訓練引數steps = 10000
batch_size = 128
with tf.Session() as sess:
sess.run(tf.global_variables_initializer()) # 初始化引數# 開始訓練for i in range(steps):
xs, ys = mnist.train.next_batch(batch_size)
# 執行訓練_, l = sess.run([train_op, cross_entropy], feed_dict={x:xs,
y:ys})
if i % 1000 == 0:
print("Steps
%d , loss: %f" % (i, l))
# 測試集上測試print(sess.run(accuracy, feed_dict={x: mnist.test.images,
y: mnist.test.labels}))
PART
04 結束
總結一篇短而精悍的小文肯定不能把TF所有的方面展示出來,但是這是一個起點,盡情地使用Tensorflow吧!
參考資料1. 官網: https://www.tensorflow.org
2. CS 20SI: Tensorflow for Deep Learning Research: http://web.stanford.edu/class/cs20si/.
