1. 程式人生 > >Tensorflow實戰Google深度學習框架 第1-3章總結

Tensorflow實戰Google深度學習框架 第1-3章總結

Tensorflow實戰Google深度學習框架

第1章 深度學習簡介

  • 深度學習在NLP上的應用: 1.語言模型 2.機器翻譯 3.詞性標註 4.實體識別 5.情感分析 6.廣告推薦 7.搜尋排序

  • 語料庫: WordNet, ConceptNet, FrameNet 概念:將我們平時說的句子,一些文學作品的語句段落,報刊雜誌上出現過的語句段落等在現實生活中真實出現過的語言材料整理在一起,形成一個語料庫,以便在做研究時可以從中取材獲得到資料佐證。 特點:語料庫中存放的是在語言的實際使用中真實出現過的語言材料;語料庫是以電子計算機為載體承載語言知識的基礎資源;真實語料需要經過加工(分析和處理),才能成為有用的資源。

第2章 Tensorflow環境搭建

  • Tensorflow主要依賴包: Protocol Buffer, Bazel

  • Protocol Buffer 處理結構化資料的工具 結構化資料:擁有多種屬性的資料 ‘name’ : ‘Tom’, ‘id’ : 1234 ‘email’ : ‘[email protected]’ 上面的使用者資訊就是一個結構化資料。要將結構化持久化或者進行網路傳輸時,需要將它們先序列化。序列化是將結構化的資料變成資料流的格式,簡單地說就是變成一個字串。將結構化的資料序列化,並從序列化之後的資料流中還原出原來的結構化資料,通稱為處理結構化資料。這就是Protocol Buffer解決的主要問題。 除Protocol Buffer外,XML, JSON

    是兩種比較常用的結構化資料處理工具,比如上面的使用者資訊使用XML格式表達,那麼資料的格式為: XML格式: <user> ‘name’ : ‘Tom’, ‘id’ : 1234 ‘email’ : ‘[email protected]’ </user> JSON格式: { ‘name’ : ‘Tom’, ‘id’ : 1234 ‘email’ : ‘[email protected]’ } Protocol Buffer格式的資料和XML或者JSON格式的資料有比較大的差別。首先,Protocol Buffer序列化之後得到的資料不是可讀的字串,而是二進位制流。其次,XML或者JSON格式的資料資訊都包含在了序列化之後的資料中,不需要任何其他資訊就能還原序列化之後的資料。但使用Protocol Buffer時需要先定義資料的格式。還原一個序列化之後的資料將需要使用到這個定義好的資料格式。以下程式碼是上面使用者資訊樣例的資料格式定義檔案。因為這樣的差別,Protocol Buffer序列化出來的資料要比XML或者JSON格式的資料小3到10倍,解析時間要快到20-100倍
    message user{ optional string name = 1; required int32 id = 2; repeated string email = 3; } Protocol Buffer定義的資料格式的檔案一般儲存在.proto檔案中。每一個message代表了一類結構化的資料。message中定義了每一個屬性的型別和名字。在message中,Protocol Buffer定義了一個屬性是必需的(required)還是可選的(optional)或者是可重複的(repeated)。

  • Bazel Bazel是從Google開源的自動化構建工具,谷歌內部絕大部分的應用都是通過它來編譯的。 專案空間是Bazel的一個基本概念。一個專案框架可以簡單的理解為一個資料夾,在這個資料夾中包含了編譯一個軟體所需要的原始碼以及輸出編譯結果的軟連線地址。一個專案空間可以包含一個或多個應用。一個專案空間所對應的資料夾是這個專案的根目錄,在這個根目錄中需要有一個WORKSPACE檔案,此檔案定義了對外部資源的依賴關係。 在一個專案空間內,Bazel通過BUILD檔案來找到需要編譯的目標。Bazel的編譯方式是事先定義好的。Bazel對Python支援的編譯方式只有三種:py_binary, py_library, py_test. 其中py_binary將Python程式編譯為可執行檔案,py_test編譯Python測試程式,py_library將Python程式編譯成庫函式供其他py_binary或py_test呼叫。 BUILD檔案是由一系列編譯目標組成的。定義編譯目標的先後順序不會影響編譯結果。

第3章 Tensorflow入門

核心概念

  • Tensorflow中的計算可以表示為一個有向圖,其中每一個運算操作將作為一個節點,節點與節點之間的連線稱為邊。這個計算圖描述了資料的計算流程,它也負責維護和更新狀態,使用者可以對計算圖的分支進行條件控制或迴圈操作。計算圖中每一個節點可以有任意多個輸入和任意多個輸出,每一個節點描述了一種運算操作,節點可以算是運算操作的例項化。在計算圖的邊中流動的資料被稱為張量(tensor),而tensor的資料型別,可以是事先定義的,也可以根據計算圖的結構推斷得到的。有一類特殊的邊中沒有資料流動,這種邊是依賴控制,作用是讓他的起始節點執行完之後再執行目標節點,使用者可以使用這樣的邊進行靈活的條件控制,比如限制記憶體使用的最高峰值。

  • Tensorflow程式一般分為兩個階段。第一個階段需要定義計算圖中所有的計算;第二階段為執行階段。

# 定義階段,這個過程中,TensorFlow會自動將定義的計算轉化為計算圖上的節點
import tensorflow as tf
a = tf.constant([1, 2], name = 'a')
b = tf.constant([3, 4], name = 'b')
result = a + b
# 執行階段
with tf.Session() as sess:
    print(sess.run(result))
  • 在Tensorflow中,系統會維護一個預設的計算圖,通過tf.get_default_graph函式可以獲取當前預設的計算圖
# 通過a.graph可以檢視張量所屬的計算圖,如果沒有特定指定,張量的計算圖屬於預設的計算圖
print(a.graph is tf.get_default_graph())   # True
  • 除了使用預設的計算圖,TensorFlow支援通過tf.Graph()函式來生成新的計算圖,不同計算圖上的張量和運算都不會共享。
import tensorflow as tf

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

g2 = tf.Graph()    # 定義g2計算圖
with g2.as_default():
    # 在計算圖g1中定義變數'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):
        print(sess.run(tf.get_variable('v')))   # 輸出[0.]

# 在計算圖g2中讀取變數'v'的值        
with tf.Session(graph=g2) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope('', reuse=True):
        print(sess.run(tf.get_variable('v')))   # 輸出[1.]

with tf.Session(graph=g1) as sess:
    tf.global_variables_initializer().run()
    with tf.variable_scope('zyy'):
        v = tf.get_variable('v', [1])
    with tf.variable_scope('zyy', reuse=True):
        vv = tf.get_variable('v', [1])
    print(v == vv)   # 輸出True
    with tf.variable_scope('zyy', reuse=True, initializer=tf.zeros_initializer()):
        vvv = tf.get_variable('v', [1])
    print(vv == vvv)   # 輸出True

    with tf.variable_scope('zyy'):
        v = tf.get_variable('k', [2, 3], initializer=tf.random_normal_initializer(mean=0, stddev=1))

TensorFlow計算模型——張量

張量的概念

  • 所有資料都通過張量的形式來表示。從功能的角度上看,張量可以被簡單理解為多維陣列。其中零階張量表示標量,也就是一個數;一階張量表示向量,也就是一個數組;n階張量可以理解為一個n維陣列。

  • 張量在TensorFlow中的實現並不是直接採用陣列的形式,它只是TensorFlow中的運算結果的引用。在張量中並沒有真正儲存數字,它儲存的是如何得到這些數字的計算過程。

import tensorflow as tf
a = tf.constant([1, 2], name = 'a')
b = tf.constant([3, 4], name = 'b')
result = a + b
print(result)
# 輸出:Tensor('add:0', shape=(2,), dtype=int32
# 得到的是對結果的一個引用,是一個張量結構。主要儲存了三個屬性:名字、維度、型別

張量的使用

  1. 對中間計算結果的引用
  2. 當計算圖構造完成之後,使用張量獲得計算結果

TensorFlow執行模型——會話

  • 會話擁有並管理TensorFlow程式執行時的所有資源。所有計算完成之後需要關閉會話來幫助系統回收資源,否則就可能出現資源洩露的問題。
# 第一種
sess = tf.Session()
sess.run(...)
sess.close()
# 第二種
with tf.Session() as sess:
    sess.run(...)
  • TensorFlow不會自動生成預設的會話,而是需要手動指定。當預設的會話被指定之後可以通過tf.Tensor.eval函式來計算一個張量的取值
sess = tf.Session()
with sess.as_default():
    print(result.eval())
# 以下程式碼與上面程式碼有同樣功能
with tf.Session() as sess:
    print(sess.run(result))
    # print(result.eval(session = sess))
  • 通過設定預設會話的方式來獲取張量的取值更加方便。
sess = tf.InteractiveSession()
print(result.eval())
sess.close()

Tensorflow——變數

  • tf.Variable(tf.random_normal([2, 3], stddev=2))

  • TensorFlow支援隨機數生成函式和常數生成函式

  • tf.global_variables_initializer().run() # 初始化所有變數

  • 在TensorFlow中,變數的宣告函式tf.Variable是一個運算。這個運算的輸出結果就是一個張量。

  • Tensorflow中集合(collection):所有的變數都會被自動地加入到GraphKeys.VARIABLES這個集合中。通過tf.global.variables()函式可以拿到當前計算圖上所有變數。拿到計算圖上所有的變數有助於持久化整個計算圖的執行狀態。當構建機器學習模型時,如神經網路,可以通過變數宣告函式中的trainable引數來區分需要優化的引數和其他引數。如果宣告變數是引數trainable為True,那麼這個變數將會被加入到GraphKeys.TRAINABLE_VARIABLES集合。在Tensorflow中可以通過tf.trainable_variables函式得到所有需要優化的引數。

  • 一個變數在構建之後,他的型別不會改變,但是維度在程式執行過程中可以改變,通過設定引數validate_shape=False

w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1))
tf.assign(w1, w2, validate=False)

Tensorflow——placeholder

  • Tensorflow提供了placeholder機制用於提供輸入資料。placeholder相當於定義了一個位置,這個位置中的資料在程式執行時再指定。這樣在程式中就不需要生成大量的常量來提供輸入資料,而只需要將資料通過placeholder傳入TensorFlow計算圖。在placeholder定義時,這個位置上的資料型別是需要指定的,和其他張量一樣,placeholder的型別也是不可以改變的。placeholder中資料維度資訊可以根據提供的資料推導得出,所以不一定要給出。
w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1))

x = tf.placeholder(tf.float32, shape=(None, 2), name='x_input')

a = tf.matmul(x, w1)
y = tf.matmul(a, w2)

sess = tf.Session()
tf.global_variable_initializer().run()
sess.run(y, feed_dict={x : [[0.7, 0.9]]})

完整神經網路樣例程式

'''
訓練一個完整的神經網路
'''
from numpy.random import RandomState
import tensorflow as tf
batch_size = 8
learningRate = 0.001

w1 = tf.Variable(tf.random_normal((2, 3), stddev=1, seed=1))
w2 = tf.Variable(tf.random_normal((3, 1), stddev=1, seed=1))

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(learningRate).minimize(cross_entropy)

rdm = RandomState(1)
dataSize = 128
X = rdm.rand(dataSize, 2)
Y = [[int(x1+x2<1)] for (x1, x2) in X]

with tf.Session() as sess:
    init_op = tf.global_variables_initializer()
    sess.run(init_op)

    print(sess.run(w1))
    print(sess.run(w2))

    step = 5000
    for i in range(step):
        start = (i * batch_size) % dataSize
        end = min(start+batch_size, dataSize)

        sess.run(train_step, 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))


    print(sess.run(w1))
    print(sess.run(w2))

在這段程式中替換了原來通過常量定義的輸入x。需要提供一個feed_dict來指定x的取值,feed_dict是一個字典,在字典中需要給出每個用到的placeholder取值。