Tensorflow 實戰 Google 深度學習框架 | 學習筆記(一)

Shoot on the moon and if you miss you will still be among the stars.

Caicloud Github :tensorflow-tutorialhttps://github.com/caicloud/tensorflow-tutorial

原本 tutorial 使用的 Tensorflow 最新版本是 1.4.0 ,本人使用的 1.5.0 版本,所以部分代碼會略有不同,該筆記僅爲個人學習,理解使用。如有錯誤,還望批評指教。—-ZJ

CSDN:http://blog.csdn.net/JUNJUN_ZHAO/article/details/79594791

3.1 計算圖的使用

import tensorflow as tf

tf.__version__
'1.5.0'
'''
3.12 計算圖的使用

'''

import tensorflow as tf 

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.0], name='b')

result = a + b

print(result)
Tensor("add:0", shape=(2,), dtype=float32)
print(a.graph is tf.get_default_graph())
True
  1. 定義兩個不同的圖

除了默認的計算圖,Tensorflow 支持通過 tf.Graph() 函數來生成新的計算圖。

import tensorflow as tf

g1 = tf.Graph()
with g1.as_default():
    # 在計算圖 g1 中定義變量“v”,並設置初始值爲 0
    v = tf.get_variable('v', shape=[1], initializer=tf.zeros_initializer)

g2 = tf.Graph()
with g2.as_default():
    # 在計算圖 g2 中定義變量“v”設置初始值爲 1
    v = tf.get_variable('v', shape=[1], initializer=tf.ones_initializer)

#  在計算圖 g1 中,讀取變量‘v’的值
with tf.Session(graph=g1) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope('', reuse=True):
        # 在計算圖 g1 中,變量 ‘v’的值取值應該爲 0,所以下面這行會輸出[0.]
        print(sess.run(tf.get_variable('v')))

#  在計算圖 g2 中,讀取變量‘v’的值    
with tf.Session(graph=g2) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope('',reuse=True):
        # 在計算圖 g2 中,變量 ‘v’的值取值應該爲 1,所以下面這行會輸出[1.]
        print(sess.run(tf.get_variable('v')))
[0.]
[1.]
import tensorflow as tf

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.0], name='b')

g  = tf.Graph()
with g.device('/gpu:0'):
    result = a + b

print(result)
Tensor("add_1:0", shape=(2,), dtype=float32)

3.2 Tensorflow 數據模型—— 張量

第 n 階張量,理解爲 n 維數組。

與 numpy 不同,Tensorflow 計算的結果不是一個具體的數,而是一個張量的結構,主要保存三個屬性:名字(name),維度(shape)和類型(type).

注意:類型要匹配

import tensorflow as tf

a = tf.constant([1.0, 2.0], name='a')
b = tf.constant([2.0, 3.4], name='b')
c = tf.constant([4, 5], name='c')
d = tf.constant([4, 5], name='d', dtype=tf.float32)

result = a + b

# 類型不匹配 會報錯
# result1 = a + c 

# 指定類型 dtype=tf.float32 正常運行
result2 = a + d 

print (result)
print (result2)

sess = tf.InteractiveSession()
print(result.eval())
print(result2.eval())
sess.close()


Tensor("add_2:0", shape=(2,), dtype=float32)
Tensor("add_3:0", shape=(2,), dtype=float32)
[3.  5.4]
[5. 7.]

add_2:0 :說明 result 這個張量是輸出節點‘add’輸出的第 3 個結果,編號從 0 開始

3.3 Tensorflow 運行模型——會話

使用 Session 來執行定義好的運算,Session 擁有並管理 Tensorflow 程序運行時的所有資源,當所有計算完成之後需要關閉會話來幫助系統回收資源,否則就可能出現資源泄露的問題。

3.3.1 創建和關閉會話

# 創建一個會話。
sess = tf.Session()

# 使用會話得到之前計算的結果。
print(sess.run(result))

# 關閉會話使得本次運行中使用到的資源可以被釋放。
sess.close()
[3.  5.4]

上面這種模式是,在所有計算完成之後,明確調用 Session.close() 函數來關閉會話,但是,若程序因異常而退出,Session.close() 函數可能就不會被執行到,從而導致資源泄露。

解決這個問題:則通過 Python 的上下文管理器 with 來使用 Session

3.3.2 使用 with statement 來創建會話

# 創建一個 Session ,並通過 Python 的上下文管理器 with 來管理 Session
with tf.Session() as sess:
    print(sess.run(result))


# 不需要調用 sess.close() 函數,當上下文退出時,會話關閉和資源釋放將會自用完成。
[3.  5.4]

3.3.3 指定默認會話

  • Tensorflow 會自動生成一個默認的計算圖 (Graph),如果沒有特殊指定,運算會加到這個默認的計算圖中。
  • Session 有類似的機制,但是 Tensorflow 不會自動生成默認的 Session ,需要 手動指定。
  • tf.Tensor.eval() 可以用來計算一個張量的取值。
print(result)

sess = tf.Session()3.3 指定默認會話
with sess.as_default():
    print(result)
    print(result.eval())
Tensor("add_2:0", shape=(2,), dtype=float32)
Tensor("add_2:0", shape=(2,), dtype=float32)
[3.  5.4]
sess = tf.Session()

# 下面的兩個命令有相同的功能。

print(sess.run(result))
print(result.eval(session=sess))
sess.close()
[3.  5.4]
[3.  5.4]

3.3.4 使用 tf.InteractiveSession 構建會話

在交互式環境下,(Python 腳本 或 jupyter notebook),通過設置默認的會話方式,可以更方便的獲取張量的取值。

sess = tf.InteractiveSession()
print(result.eval)
print(result.eval())
sess.close()
<bound method Tensor.eval of <tf.Tensor 'add_2:0' shape=(2,) dtype=float32>>
[3.  5.4]

3.3.5 通過 ConfigProto 配置會話 Session

之前都是使用的默認的 session 的配置,我們也可以通過 ConfigProto 對 session 進行特殊的配置,來實現不同的需求。

config = tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)
sess1 = tf.InteractiveSession(config=config)
sess2 = tf.Session(config=config)
sess1.close()
sess2.close()
  • 通過 ConfigProto 可以配置並行的線程數,GPU 分配策略,運算超時時間等參數。
  • allow_soft_placement=True 時(默認爲False),以下三個條件成立時,GPU 運算可以放到 CPU 上運行。
    • 運算無法在 GPU 執行
    • 沒有指定的 GPU 資源,1. 沒有 GPU 2.指定第二個,實際上只有 1個
    • 運算輸入包含對 CPU 計算結果的引用
  • log_device_placement=True 時(默認爲 False 減少日誌量),爲 True時,日誌中會記錄每個節點被安排在哪個設備上以方便調試。

3.4 Tensorflow 實現神經網絡

3.4.1 Tensorflow 遊樂場及神經原理簡介

Tinker With a Neural Network in Your Browser.:http://playground.tensorflow.org

通過 Browser 可以訓練簡單的神經網絡,並實現了可視化訓練過程。

中間省略看原書。

使用神經網絡解決分類問題主要步驟:

  1. 提取問題中實體的特徵向量作爲神經網絡的輸入。
  2. 定義神經網絡的結構,並定義如何從神經網絡的輸入得到輸出,也就是前向傳播算法。
  3. 通過訓練數據來調整神經網絡中參數的取值,也就是訓練神經網絡的過程。
  4. 使用訓練好的神經網絡來預測未知的數據。

3.4.2 前向傳播算法(Forward propogation)

介紹最簡單的全連接網絡結構的前向傳播算法。略

3.4.3 三層簡單神經網絡

這裏寫圖片描述

import tensorflow as tf

# 1. 定義 w1, w2 兩個變量,seed=1 設定隨機種子
# tf.random_normal 隨機生成 2*3 大小的矩陣,服從正太分佈 均值 mean,標準差 stddev = 1
# 一定要注意矩陣的大小
w1 = tf.Variable(tf.random_normal([2,3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3,1], stddev=1, seed=1))

# 2. 將輸入的特徵向量定義爲一個常量,x 維度(1,2)
x = tf.constant([[0.7, 0.9]])

# 3.定義前向傳播算法的神經網絡
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

# 分別初始化 w1,w2 兩個變量
# 4.調用會話輸出結果
with tf.Session() as sess:
    sess.run((w1.initializer, w2.initializer))
    print(sess.run(y))

# sess = tf.Session()
# sess.run(w1.initializer)  
# sess.run(w2.initializer)  
# print(sess.run(y))  
# sess.close()
E:\ProgramData\Anaconda3\lib\site-packages\h5py\__init__.py:36: FutureWarning: Conversion of the second argument of issubdtype from `float` to `np.floating` is deprecated. In future, it will be treated as `np.float64 == np.dtype(float).type`.
  from ._conv import register_converters as _register_converters


[[3.957578]]

注意:

運行上面代碼時,出現下面的錯誤:

InternalError: Blas GEMM launch failed : a.shape=(1, 2), b.shape=(2, 3), m=1, n=3, k=2 [[Node: MatMul = MatMul[T=DT_FLOAT, transpose_a=false, transpose_b=false, _device=”/job:localhost/replica:0/task:0/device:GPU:0”](Const, Variable/read)]]

解決方法:

最後發現是運行着多個 jupyter notebook ,在學習練習的過程中,沒有及時關閉 session 導致,看到系統資源佔用比高達 75%,然後 將沒用的都關閉後,就降到了 23%。

多半是 session 用畢沒有及時 close, 導致系統和 GPU 的很大部分被佔用過卻沒有歸還, 當前資源便不夠了; 或是多個 session 爭用 GPU.


  • tf.random_normal 隨機生成 2*3 大小的矩陣,服從正太分佈 均值 mean,標準差 stddev
  • 上面給 w1,w2 賦值時,使用的是 initializer ,當變量多的時候這樣就會很麻煩,所以 以後可以使用 tf.global_variables_initializer() 函數來完成初始化所有變量的過程。
w1 = tf.Variable(tf.random_normal([2,3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3,1], stddev=1, seed=1))

top = tf.global_variables_initializer()
with tf.Session() as sess:
    sess.run(top)
    print('w1:\n',w1.eval(),'\nw2:\n',w2.eval())
w1:
 [[-0.8113182   1.4845988   0.06532937]
 [-2.4427042   0.0992484   0.5912243 ]] 
w2:
 [[-0.8113182 ]
 [ 1.4845988 ]
 [ 0.06532937]]

3.4.4 通過 Tensorflow 訓練神經網絡模型

這裏寫圖片描述

使用 placeholder:

首先,每生成一個常量,計算圖就會增加一個節點,一般來說,一個神經網絡訓練過程會經過幾百萬輪甚至幾億輪的迭代。若都是常量的話,則計算圖會非常的大,且利用率低。

所以使用 placeholder ,相當於定義一個位置,這個位置中的數據,在運行程序時再指定。其類型不可變,維度可變。

feed_dict={x:[[0.7,0.9]]} feed_dict 餵給的輸入數據,字典類型,再去指定 x 鍵,對應的值

# 定義 placeholder 作爲存放輸入數據的地方,必須指定類型,
# 維度不一定要指定,但是,如果確定維度,可以避免出 bug
x = tf.placeholder(tf.float32, shape=(1,2), name='input')
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    # feed_dict 餵給的 輸入數據    
    print(sess.run(y, feed_dict={x:[[0.7,0.9]]}))
[[3.957578]]
'''
增加多個輸入

'''
x = tf.placeholder(tf.float32, shape=(3,2), name='input')
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(y, feed_dict={x:[[0.7,0.9],[0.1,0.4],[0.5,0.8]]}))
[[3.957578 ]
 [1.1537654]
 [3.1674924]]

3.4.5 完整神經網絡樣例程序

在一個模擬數據集上訓練神經網絡,下面是一個完整的程序來訓練神經網絡解決二分類問題。

import tensorflow as tf

# numpy 是一個科學計算的工具包,這裏通過 numpy 工具包生成模擬數據集
from numpy.random import RandomState

# 定義訓練數據 batch 的大小
batch_size = 8

# 定義神經網絡的參數
w1 = tf.Variable(tf.random_normal([2, 3], stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal([3, 1], stddev=1, seed=1))

# 在 shape 的維度上使用 None 可以方便使用不大的 batch 的大小,在訓練時把數據分成比較小的 batch 
# 將大量數據放進一個 batch 會導致內存溢出
x = tf.placeholder(tf.float32, shape=(None, 2), name='x-input')
y_ = tf.placeholder(tf.float32, shape=(None, 1), name='y-input')


# 定義神經網絡的前向傳播過程
a = tf.matmul(x, w1)
y = tf.matmul(a, w2)
y = tf.sigmoid(y)

# 定義損失函數和反向傳播算法
cross_entropy = -tf.reduce_mean(y_ * tf.log(tf.clip_by_value(y, 1e-10, 1.0))
                                + (1 - y_) * tf.log(tf.clip_by_value(1-y, 1e-10, 1.0)))


train_step = tf.train.AdamOptimizer(0.001).minimize(cross_entropy)

# 通過隨機數生成一個模擬數據集
rdm = RandomState(1)
data_size = 128
X = rdm.rand(data_size, 2)


# 定義規則來給出樣本的標籤,x1 + x2 < 1 .爲正樣本 (1),其他爲 負樣本(0)
Y = [[int(x1+x2 < 1)]  for (x1, x2) in X ]


# 創建 Session 來運行 tf 程序
with tf.Session() as sess:
    # 初始化變量
    sess.run(tf.global_variables_initializer())

    # 輸出目前(未經訓練)的參數取值。
    print(sess.run(w1))
    print(sess.run(w2))
    print("\n")
    # 設定訓練的 輪數 

    STEPS = 5000
    for i in range(STEPS):
        # 每次選取 batch_size 個樣本進行訓練
        start = (i*batch_size) % data_size
        end = (i*batch_size) % data_size + batch_size
        # 通過選取的樣本 訓練神經網絡,並更新參數
        sess.run([train_step, y, y_], feed_dict={x:X[start:end], y_:Y[start:end]})
        # 每隔一段時間,計算在所有數據上的交叉熵並輸出
        if i % 1000 ==0:
            '''
            隨着訓練的進行,交叉熵是逐漸變小的,越小則越說明,
            預測值與真實值之間的差距越小

            '''
            total_cross_entropy = sess.run(cross_entropy, feed_dict={x:X, y_:Y})
            print("After %d training step(s), cross entropy on all data is %g" % (i, total_cross_entropy))


    '''
    訓練完後,神經網絡參數的值,w1 ,w2 的值已經發生了變化,也就是訓練的結果。

    它使得這個神經網絡可以更好的擬合提供的訓練數據。具有更好的泛化能力。 
    '''
    print("\n")
    print(sess.run(w1))
    print(sess.run(w2))    


[[-0.8113182   1.4845988   0.06532937]
 [-2.4427042   0.0992484   0.5912243 ]]
[[-0.8113182 ]
 [ 1.4845988 ]
 [ 0.06532937]]


After 0 training step(s), cross entropy on all data is 1.89805
After 1000 training step(s), cross entropy on all data is 0.655075
After 2000 training step(s), cross entropy on all data is 0.626172
After 3000 training step(s), cross entropy on all data is 0.615096
After 4000 training step(s), cross entropy on all data is 0.610309


[[ 0.02476974  0.56948686  1.6921943 ]
 [-2.1977353  -0.23668927  1.1143897 ]]
[[-0.45544702]
 [ 0.49110925]
 [-0.98110336]]

總結:

訓練神經網絡的過程主要可以分成 三步:

  1. 定義神經網絡的結構和前向傳播的輸出結果

  2. 定義損失函數以及選擇反向傳播優化的算法(常用優化算法如下)

    • tf.train.GradientDescentOptimizer
    • tf.train.AdamOptimizer
    • tf.train.MomentumOptimizer
  3. 定義 Session 並在訓練數據上反覆運行反向傳播優化的算法。


補充知識點:

  • batchsize:批大小。在深度學習中,一般採用 SGD 訓練,即每次訓練在訓練集中取batchsize個樣本訓練;

  • iteration:迭代,1 個 iteration 等於使用 batchsize 個樣本訓練一次;

  • 一個迭代 = 一個正向通過 + 一個反向通過
  • epoch:迭代次數,1 個 epoch 等於使用訓練集中的全部樣本訓練一次;
  • 一個epoch = 所有訓練樣本的一個正向傳遞和一個反向傳遞

舉個例子,訓練集有 1000 個樣本,batchsize = 10,那麼:

訓練完整個樣本集需要:

100次 iteration,1次 epoch。

發佈了186 篇原創文章 · 獲贊 44 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章