文章目錄
聲明
tensorflow入門
之前,一直使用numpy來編寫神經網絡,現在將一步步使用深度學習框架來構建屬於自己的神經網絡,將學習tensorflow框架。
- 初始化變量
- 建立一個會話
- 訓練的算法
- 實現一個神經網絡
導入tensorflow庫
import math
import numpy as np
import h5py
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.python.framework import ops
import tf_utils
import time
%matplotlib inline
np.random.seed(1)
我們來計算損失:
y_hat = tf.constant(36,name='y_hat')
y = tf.constant(39,name='y')
loss = tf.Variable((y-y_hat)**2,name='loss')
init = tf.global_variables_initializer()
with tf.Session() as session:
session.run(init)
print(session.run(loss))
結果
9
對於tensorflow的代碼實現而言,結構如下:
- 創建tensorflow變量(此時,尚未直接計算)
- 實現tensorflow變量之間的操作定義
- 初始化tensorflow變量
- 創建session
- 運行session,此時,之前編寫操作都會在這一步運行
因此,當我們爲損失函數創建一個變量時,我們簡單地將損失函數定義爲其他數量的函數,但並沒有評估它的價值,爲了評估它,我們需要運行init=tf.global_variabbles_initializer()
,初始化損失變量,在最後一行,我們最後能夠評估損失的值並打印它的值。
總結:初始化變量,然後創建一個session來運行它。
佔位符:佔位符是一個對象,它的值只能在稍後指定,要指定佔位符的值,可以使用一個feed字典(feed_dict變量)來傳入,接下來,爲x創建一個佔位符,這將允許我們在稍後運行會話時傳入一個數字。
session = tf.Session()
x = tf.placeholder(tf.int64,name='x')
print(session.run(2*x,feed_dict={x:3}))
session.close()
結果
6
1.1、線性函數
計算Y=WX+b,W維度是4,3;X維度是3,1;b維度是4,1。定義一個維度是3,1的常量X:
X = tf.constant(np.random.randn(3,1),name='X')
def linear_function():
"""
實現一個線性功能:
初始化W,類型爲tensor的隨機變量,維度爲(4,3)
初始化X,類型爲tensor的隨機變量,維度爲(3,1)
初始化b,類型爲tensor的隨機變量,維度爲(4,1)
返回:
result - 運行了session後的結果,運行的是Y = WX + b
"""
np.random.seed(1)
X = np.random.randn(3,1)
W = np.random.randn(4,3)
b = np.random.randn(4,1)
Y = tf.add(tf.matmul(W,X),b)
sess = tf.Session()
result = sess.run(Y)
sess.close()
return result
測試
print(linear_function())
結果
[[-2.15657382]
[ 2.95891446]
[-1.08926781]
[-0.84538042]]
1.2、計算sigmoid
我們已經實現了線性函數,tensorflow提供了多種常用的神經網絡的函數,比如tf.softmax和tf.sigmoid。
- tf.placeholder(tf.float32,name=’…’)
- tf.sigmoid(…)
- sess.run(…,feed_dict={x:z})
可是使用兩種方法來創建並使用session
方法一:
sess = tf.Session()
result = sess.run(...,feed_dict = {...})
sess.close()
方法二:
with tf.Session as sess:
result = sess.run(...,feed_dict = {...})
def sigmoid(z):
"""
實現使用sigmoid函數計算z
參數:
z - 輸入的值,標量或矢量
返回:
result - 用sigmoid計算z的值
"""
x = tf.placeholder(tf.float32,name='x')
sigmoid = tf.sigmoid(x)
with tf.Session() as sess:
result = sess.run(sigmoid,feed_dict={x:2})
return result
測試
print(sigmoid(12))
結果
0.880797
計算成本
tf.nn.sigmoid_cross_entropy_with_logits(logits=... , labels=...)
使用獨熱編碼(0、1編碼)
在深度學習中y向量的維度是從0到C-1。C是指分類的類別數量,如果C=4,那麼對y而言你可能需要有以下的轉換方式:
這是獨熱編碼(‘one hot’ encoding),因爲在轉換後的表示中,每列的一個元素是‘hot’(意思是設置爲1)。要在numpy中進行這種轉換,可能需要編寫幾行代碼。在tensorflow中,只需使用一行代碼:
tf.one_hot(labels,depth,axis)
def one_hot_matrix(labels,C):
"""
創建一個矩陣,其中第i行對應第i個類別,第j列對應第j個訓練樣本
所以如果第j個樣本對應着第i個標籤,那麼entry(i,j)將會是1
參數:
labels - 標籤向量
C - 分類數
返回:
one_hot - 獨熱矩陣
"""
C = tf.constant(C,name='C')
one_hot_matrix = tf.one_hot(indices=labels,depth=C,axis=0)
sess = tf.Session()
one_hot = sess.run(one_hot_matrix)
sess.close()
return one_hot
測試
labels = np.array([1,2,3,0,2,1])
one_hot = one_hot_matrix(labels,C=4)
print(str(one_hot))
結果
[[0. 0. 0. 1. 0. 0.]
[1. 0. 0. 0. 0. 1.]
[0. 1. 0. 0. 1. 0.]
[0. 0. 1. 0. 0. 0.]]
初始化爲0和1
def ones(shape):
"""
創建一個維度爲shape的變量,其值全爲1
參數:
shape - 你要創建的數組的維度
返回:
ones - 只包含1的數組
"""
ones = tf.ones(shape)
sess = tf.Session()
ones = sess.run(ones)
sess.close()
return ones
測試
print(ones([3]))
結果
[1. 1. 1.]
使用tensorflow構建第一個三層的神經網絡
使用tensorflow構建一個神經網絡,實現模型需要做以下兩個步驟:
- 創建計算圖
- 運行計算圖
解決的問題
建立一個算法,使有語音障礙的人與不懂手語的人交流。
- 訓練集:有從0到5的數字的1080張圖片(64x64像素),每個數字擁有180張圖片。
- 測試集:有從0到5的數字的120張圖片(64x64像素),每個數字擁有5張圖片。
加載數據
X_train_orig,Y_train_orig,X_test_orig,Y_test_orig,classes = tf_utils.load_dataset()
測試
index = 11
plt.imshow(X_train_orig[index])
print("Y = "+str(np.squeeze(Y_train_orig[:,index])))
結果
Y = 1
我們要對數據集進行扁平化,然後再除以255以歸一化數據,此外,我們需要把每個標籤轉化爲獨熱向量,像上面的圖一樣。
X_train_flatten = X_train_orig.reshape(X_train_orig.shape[0],-1).T
X_test_flatten = X_train_orig.reshape(X_test_orig.shape[0],-1).T
X_train = X_train_flatten/255
X_test = X_test_flatten/255
Y_train = tf_utils.convert_to_one_hot(Y_train_orig,6)
Y_test = tf_utils.convert_to_one_hot(Y_test_orig,6)
print(X_train.shape[1])
print(X_test.shape[1])
print(X_train.shape)
print(Y_train.shape)
print(X_test.shape)
print(Y_test.shape)
結果
1080
120
(12288, 1080)
(6, 1080)
(110592, 120)
(6, 120)
模型:linear->relu->linear->relu->linear->softmax,sigmoid。
輸出層已經轉化爲softmax,當有兩個以上的類時,一個softmax層將sigmoid一般化。
2.1、創建placeholders
爲X和Y創建佔位符,這將允許稍後在運行會話時傳遞您的訓練數據。
def create_placeholders(n_x,n_y):
"""
爲Tensorflow會話創建佔位符
參數:
n_x - 一個實數,圖片向量的大小(64*64*3 = 12288)
n_y - 一個實數,分類樹(從0到5,所以n_y = 6)
返回:
X - 一個數據輸入的佔位符,維度爲[n_x,None],dtype = 'float'
Y - 一個對應輸入的標籤的佔位符,維度爲[n_Y,None],dtype = 'float'
提示:
使用None,因爲它讓我們可以靈活處理佔位符提供的樣本數量。事實上,測試/訓練期間的樣本數量是不同的
"""
X = tf.placeholder(tf.float32,[n_x,None],name='X')
Y = tf.placeholder(tf.float32,[n_y,None],name='Y')
return X,Y
測試
X,Y = create_placeholders(12288,6)
print(X,Y)
結果
Tensor("X:0", shape=(12288, ?), dtype=float32) Tensor("Y:0", shape=(6, ?), dtype=float32)
2.2、初始化參數
初始化tensorflow中的參數,將使用Xavier初始化權重和用零來初始化偏差,比如:
W1 = tf.get_variable("W1",[25,12288],initializer = tf.contrib.layers.xavier_initializer(seed = 1))
b1 = tf.get_variable('b1',[25,1],initializer = tf.zeros_initializer())
注:tf.Variable()
每次都在創建新對象,對於get_variable()
來說,對於已經創建的變量對象,就把那個對象返回,如果沒有創建變量對象的話,就創建一個新的。
def initialize_parameters():
"""
初始化神經網絡的參數,參數的維度如下:
W1 - [25,12288]
b1 - [25,1]
W2 - [12,25]
b2 - [12,1]
W3 - [6,12]
b3 - [6,1]
返回:
parameters - 包含了W和b的字典
"""
tf.set_random_seed(1)
W1 = tf.get_variable("W1",[25,12288],initializer=tf.contrib.layers.xavier_initializer(seed=1))
b1 = tf.get_variable("b1",[25,1],initializer=tf.zeros_initializer())
W2 = tf.get_variable("W2",[12,25],initializer=tf.contrib.layers.xavier_initializer(seed=1))
b2 = tf.get_variable("b2",[12,1],initializer=tf.zeros_initializer())
W3 = tf.get_variable("W3",[6,12],initializer=tf.contrib.layers.xavier_initializer(seed=1))
b3 = tf.get_variable("b3",[6,1],initializer=tf.zeros_initializer())
parameters = {
'W1':W1,
'b1':b1,
'W2':W2,
'b2':b2,
'W3':W3,
'b3':b3
}
return parameters
測試
tf.reset_default_graph()
with tf.Session() as sess:
parameters = initialize_parameters()
print(parameters["W1"])
print(parameters["b1"])
print(parameters["W2"])
print(parameters["b2"])
結果
<tf.Variable 'W1:0' shape=(25, 12288) dtype=float32_ref>
<tf.Variable 'b1:0' shape=(25, 1) dtype=float32_ref>
<tf.Variable 'W2:0' shape=(12, 25) dtype=float32_ref>
<tf.Variable 'b2:0' shape=(12, 1) dtype=float32_ref>
這些參數只有物理空間,但還未被賦值,這是因爲沒有通過session執行。
2.3、前向傳播
def forward_propagation(X,parameters):
"""
實現一個模型的前向傳播,模型結構爲linear->relu->linear->relu->linear->sigmoid
參數:
X - 輸入數據的佔位符,維度爲(輸入節點數量,樣本數量)
parameters - 包含了W和b的參數的字典
返回:
Z3 - 最後一個linear節點的輸出
"""
W1,b1,W2,b2,W3,b3 = parameters["W1"],parameters["b1"],parameters["W2"],parameters["b2"],parameters["W3"],parameters["b3"]
Z1 = tf.add(tf.matmul(W1,X),b1)
A1 = tf.nn.relu(Z1)
Z2 = tf.add(tf.matmul(W2,A1),b2)
A2 = tf.nn.relu(Z2)
Z3 = tf.add(tf.matmul(W3,A2),b3)
return Z3
測試
tf.reset_default_graph()
with tf.Session() as sess:
X,Y = create_placeholders(12288,6)
parameters = initialize_parameters()
Z3 = forward_propagation(X,parameters)
print(Z3)
結果
Tensor("Add_2:0", shape=(6, ?), dtype=float32)
2.4、計算成本
tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits-... , labels= ...))
def compute_cost(Z3,Y):
"""
計算成本
參數:
Z3 - 前向傳播的結果
Y - 標籤,一個佔位符,和Z3的維度相同
返回:
cost - 成本值
"""
logits = tf.transpose(Z3)
labels = tf.transpose(Y)
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=logits,labels=labels))
return cost
測試
tf.reset_default_graph()
with tf.Session() as sess:
X,Y = create_placeholders(12288,6)
parameters = initialize_parameters()
Z3 = forward_propagation(X,parameters)
cost = compute_cost(Z3,Y)
print(cost)
結果
Tensor("Mean:0", shape=(), dtype=float32)
2.5、反向傳播&更新參數
計算成本函數後,將創建一個‘optimizer’對象,運行tf.session時,必須將此對象與成本函數一起調用,當被調用時,它將使用所選擇的方法和學習速率對給定成本進行優化。
optimizer = tf.train.GradientDescentOptimizer(learning_rate = learning_rate).minimize(cost)
_ , c = sess.run([optimizer,cost],feed_dict={X:mini_batch_X,Y:mini_batch_Y})
編寫代碼時,經常使用_
作爲一次性變量來存儲我們稍後不需要使用的值。這裏,_
具有我們不需要的優化器的評估值(並且c取值爲成本變量的值)。
2.6、構建模型
def model(X_train,Y_train,X_test,Y_test,learning_rate=0.0001,num_epochs=1500,minibatch_size=32,print_cost=True,is_plot=False):
"""
實現一個三層的tensorflow神經網絡,linear->relu->linear->relu->linear->sigmoid
參數:
X_train - 訓練集,維度爲(輸入大小(輸入節點數量)=12288,樣本數量=1080)
Y_train - 訓練集分類數量,維度爲(輸入大小(輸出節點數量)=6,樣本數量=1080)
X_test - 測試集,維度爲(輸入大小(輸入節點數量)=12288,樣本數量=120)
Y_test - 測試集分類數量,維度爲(輸出大小(輸出節點數量)=6,樣本數量=120)
learning_rate - 學習速率
num_epochs - 整個訓練集的遍歷次數
minibatch_size - 每個小批量數據集的大小
print_cost - 是否打印成本,每100次打印一次
is_plot - 是否還繪製曲線圖
"""
ops.reset_default_graph()
tf.set_random_seed(1)
seed = 3
(n_x,m) = X_train.shape
n_y = Y_train.shape[0]
costs = []
X,Y = create_placeholders(n_x,n_y)
parameters = initialize_parameters()
Z3 = forward_propagation(X,parameters)
cost = compute_cost(Z3,Y)
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for epoch in range(num_epochs):
epoch_cost = 0
num_minibatches = int(m/minibatch_size)
seed = seed + 1
minibatches = tf_utils.random_mini_batches(X_train,Y_train,minibatch_size,seed)
for minibatch in minibatches:
(minibatch_X,minibatch_Y) = minibatch
_ , minibatch_cost = sess.run([optimizer,cost],feed_dict={X:minibatch_X,Y:minibatch_Y})
epoch_cost = epoch_cost + minibatch_cost/num_minibatches
if epoch%5==0:
costs.append(epoch_cost)
if print_cost and epoch%100==0:
print('epoch=',epoch,',epoch_cost=',epoch_cost)
if is_plot:
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iterations(per tens)')
plt.title('learning rate = '+str(learning_rate))
plt.show()
parameters = sess.run(parameters)
print('參數已經保存到session')
correct_prediction = tf.equal(tf.argmax(Z3),tf.argmax(Y))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,'float'))
print(accuracy.eval({X:X_train,Y:Y_train}))
print(accuracy.eval({X:X_test,Y:Y_test}))
return parameters
測試,這次運行的時間大約在5-8分鐘,如果在epoch=100時,你的epoch_cost=1.01645776539和我相差過大,那麼就立即停止,回頭檢查一下哪裏出了問題。
start_time = time.clock()
parameters = model(X_train,Y_train,X_test,Y_test)
end_time = time.clock()
print(end_time-start_time)
結果
epoch= 0 ,epoch_cost= 1.8557018944711399
epoch= 100 ,epoch_cost= 1.0164577653913787
epoch= 200 ,epoch_cost= 0.7331023794231992
epoch= 300 ,epoch_cost= 0.5729389362262957
epoch= 400 ,epoch_cost= 0.4687735786040625
epoch= 500 ,epoch_cost= 0.38102111130049726
epoch= 600 ,epoch_cost= 0.31382677862138464
epoch= 700 ,epoch_cost= 0.25428046060330944
epoch= 800 ,epoch_cost= 0.2037993425672704
epoch= 900 ,epoch_cost= 0.16651199329080005
epoch= 1000 ,epoch_cost= 0.14093692171754257
epoch= 1100 ,epoch_cost= 0.10775012974486205
epoch= 1200 ,epoch_cost= 0.08629942504745539
epoch= 1300 ,epoch_cost= 0.060948541613690774
epoch= 1400 ,epoch_cost= 0.050934410343567535
參數已經保存到session
測試集的準確率: 0.725
CPU的執行時間 = 482.19651398680486 秒
0.9990741
現在,該算法已經可以識別0-5的手勢符號了,準確率在72.5%。模型看起來足夠打了,可以適應訓練集,但是考慮到訓練與測試的差異,也完全可以嘗試添加L2或者dropout來減少過擬合,將session視爲一組代碼來訓練模型,在每個minibatch上運行會話時,都會訓練我們的參數,總的來說,已經運行了很多次(1500代),直到你獲得訓練有素的參數。
2.7、測試你自己的圖片
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
my_image = '1.png'
fileName1 = my_image
image1 = mpimg.imread(fileName1)
plt.imshow(image1)
my_image1 = image1.reshape((1,64*64*3)).T
my_image_prediction = tf_utils.predict(my_image1,parameters)
print(np.squeeze(my_image_prediction))
結果
1