深度學習第二週--第三課tensorflow入門

聲明

本文參考何寬念師

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的代碼實現而言,結構如下:

  1. 創建tensorflow變量(此時,尚未直接計算)
  2. 實現tensorflow變量之間的操作定義
  3. 初始化tensorflow變量
  4. 創建session
  5. 運行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構建一個神經網絡,實現模型需要做以下兩個步驟:

  1. 創建計算圖
  2. 運行計算圖

解決的問題

建立一個算法,使有語音障礙的人與不懂手語的人交流。

  • 訓練集:有從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.196513986804860.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

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章