學習筆記CB013: TensorFlow、TensorBoard、seq2seq

tensorflow基於圖結構深度學習框架,內部經過session實現圖和計算內核交互。html

tensorflow基本數學運算用法。python

import tensorflow as tf
sess = tf.Session()
a = tf.placeholder("float")
b = tf.placeholder("float")
c = tf.constant(6.0)
d = tf.mul(a, b)
y = tf.mul(d, c)
print sess.run(y, feed_dict={a: 3, b: 3})
A = [[1.1,2.3],[3.4,4.1]]
Y = tf.matrix_inverse(A)
print sess.run(Y)
sess.close()

主要數字運算。git

tf.add
tf.sub
tf.mul
tf.div
tf.mod
tf.abs
tf.neg
tf.sign
tf.inv
tf.square
tf.round
tf.sqrt
tf.pow
tf.exp
tf.log
tf.maximum
tf.minimum
tf.cos
tf.sin

主要矩陣運算。github

tf.diag #生成對角陣
tf.transpose
tf.matmul
tf.matrix_determinant #計算行列式的值
tf.matrix_inverse #計算矩陣的逆

tensorboard使用。tensorflow代碼,先構建圖,而後執行,對中間過程調試不方便,提供一個tensorboard工具調試。訓練時提示寫入事件文件到目錄(/tmp/tflearn_logs/11U8M4/)。執行命令打開 http://192.168.1.101:6006 看到tensorboard的界面。數組

tensorboard --logdir=/tmp/tflearn_logs/11U8M4/

Graph和Session。微信

import tensorflow as tf
with tf.Graph().as_default() as g:
    with g.name_scope("myscope") as scope: # 有了這個scope,下面的op的name都是相似myscope/Placeholder這樣的前綴
        sess = tf.Session(target='', graph = g, config=None) # target表示要鏈接的tf執行引擎
        print "graph version:", g.version # 0
        a = tf.placeholder("float")
        print a.op # 輸出整個operation信息,跟下面g.get_operations返回結果同樣
        print "graph version:", g.version # 1
        b = tf.placeholder("float")
        print "graph version:", g.version # 2
        c = tf.placeholder("float")
        print "graph version:", g.version # 3
        y1 = tf.mul(a, b) # 也能夠寫成a * b
        print "graph version:", g.version # 4
        y2 = tf.mul(y1, c) # 也能夠寫成y1 * c
        print "graph version:", g.version # 5
        operations = g.get_operations()
 
       for (i, op) in enumerate(operations):
            print "============ operation", i+1, "==========="
            print op # 一個結構,包括:name、op、attr、input等,不一樣op不同
        assert y1.graph is g
        assert sess.graph is g
        print "================ graph object address ================"
        print sess.graph
        print "================ graph define ================"
        print sess.graph_def
        print "================ sess str ================"
        print sess.sess_str
        print sess.run(y1, feed_dict={a: 3, b: 3}) # 9.0 feed_dictgraph中的元素和值的映射
        print sess.run(fetches=[b,y1], feed_dict={a: 3, b: 3}, options=None, run_metadata=None) # 傳入的feches和返回值的shape相同
        print sess.run({'ret_name':y1}, feed_dict={a: 3, b: 3}) # {'ret_name': 9.0} 傳入的feches和返回值的shape相同

        assert tf.get_default_session() is not sess
        with sess.as_default(): # 把sess做爲默認的session,那麼tf.get_default_session就是sess, 不然不是
            assert tf.get_default_session() is sess

        h = sess.partial_run_setup([y1, y2], [a, b, c]) # 分階段運行,參數指明瞭feches和feed_dict列表
        res = sess.partial_run(h, y1, feed_dict={a: 3, b: 4}) # 12 運行第一階段
        res = sess.partial_run(h, y2, feed_dict={c: res}) # 144.0 運行第二階段,其中使用了第一階段的執行結果
        print "partial_run res:", res
        sess.close()

tensorflow Session是Graph和執行者媒介,Session.run()將graph、fetches、feed_dict序列化到字節數組,調用tf_session.TF_Run(參見/usr/local/lib/python2.7/site-packages/tensorflow/python/client/session.py)。tf_session.TF_Run調用動態連接庫_pywrap_tensorflow.so實現_pywrap_tensorflow.TF_Run接口(參見/usr/local/lib/python2.7/site-packages/tensorflow/python/pywrap_tensorflow.py)。動態連接庫是tensorflow多語言python接口。_pywrap_tensorflow.so和pywrap_tensorflow.py經過SWIG工具自動生成,tensorflow核心語言c語言,經過SWIG生成各類腳本語言接口。session

10行關鍵代碼實現線性迴歸。用梯度降低求解線性迴歸問題是tensorflow最簡單入門例子(10行關鍵代碼)。app

# -*- coding: utf-8 -*-
import numpy as np
import tensorflow as tf
# 隨機生成1000個點,圍繞在y=0.1x+0.3的直線周圍
num_points = 1000
vectors_set = []
for i in xrange(num_points):
    x1 = np.random.normal(0.0, 0.55)
    y1 = x1 * 0.1 + 0.3 + np.random.normal(0.0, 0.03)
    vectors_set.append([x1, y1])
# 生成一些樣本
x_data = [v[0] for v in vectors_set]
y_data = [v[1] for v in vectors_set]
# 生成1維的W矩陣,取值是[-1,1]之間的隨機數
W = tf.Variable(tf.random_uniform([1], -1.0, 1.0), name='W')
# 生成1維的b矩陣,初始值是0
b = tf.Variable(tf.zeros([1]), name='b')
# 通過計算得出預估值y
y = W * x_data + b
# 以預估值y和實際值y_data之間的均方偏差做爲損失
loss = tf.reduce_mean(tf.square(y - y_data), name='loss')
# 採用梯度降低法來優化參數
optimizer = tf.train.GradientDescentOptimizer(0.5)
# 訓練的過程就是最小化這個偏差值
train = optimizer.minimize(loss, name='train')
sess = tf.Session()
# 輸出圖結構
#print sess.graph_def
init = tf.initialize_all_variables()
sess.run(init)
# 初始化的W和b是多少
print "W =", sess.run(W), "b =", sess.run(b), "loss =", sess.run(loss)
# 執行20次訓練
for step in xrange(20):
    sess.run(train)
    # 輸出訓練好的W和b
    print "W =", sess.run(W), "b =", sess.run(b), "loss =", sess.run(loss)
# 生成summary文件,用於tensorboard使用
writer = tf.train.SummaryWriter("./tmp", sess.graph)

一張圖展現線性迴歸工做原理。執行代碼在本地生成一個tmp目錄,產生tensorboard讀取數據,執行:框架

tensorboard --logdir=./tmp/

打開 http://localhost:6006/ GRAPHS,展開一系列關鍵節點。圖是代碼生成graph結構,graph描述整個梯度降低解決線性迴歸問題整個過程,每個節點表明代碼的一步操做。dom

詳細分析線性迴歸graph。W和b。代碼對W有三種操做 Assign、read、train。assign基於random_uniform賦值。

W = tf.Variable(tf.random_uniform([1], -1.0, 1.0), name='W')

tf.random_uniform graph。read對應:

y = W * x_data + b

train對應梯度降低訓練過程操做。

對b有三種操做:Assign、read、train。用zeros賦初始化值。
W和b經過梯度降低計算update_W和update_b,更新W和b的值。update_W和update_b基於三個輸入計算得出,學習率learning_rate、W/b當前值、梯度gradients。
最關鍵的梯度降低過程。

loss = tf.reduce_mean(tf.square(y - y_data), name='loss')

以y-y_data爲輸入,x不是x_data,是一個臨時常量 2。2(y-y_data),明顯是(y-y_data)^2導數。以2(y-y_data)爲輸入通過各類處理最終生成參數b的增量update_b。生成update_W更新W,反向追溯依賴於add_grad(基於y-y_data)和W以及y生成,詳細計算過程:http://stackoverflow.com/ques... ,一步簡單操做被tensorflow轉成不少個節點圖,細節節點不深刻分析,只是操做圖表達,沒有過重要意義。

tensorflow自帶seq2seq模型基於one-hot詞嵌入,每一個詞用一個數字代替不足表示詞與詞之間關係,word2vec多維向量作詞嵌入,可以表示出詞之間關係。基於seq2seq思想,利用多維詞向量實現模型,預期會有更高準確性。

seq2seq模型原理。參考《Sequence to Sequence Learning with Neural Networks》論文。核心思想,ABC是輸入語句,WXYZ是輸出語句,EOS是標識一句話結束,訓練單元是lstm,lstm的特色是有長短時記憶,可以根據輸入多個字肯定後面多個字,lstm知識參考 http://deeplearning.net/tutor... 模型編碼器和解碼器共用同一個lstm層,共享參數,分開 https://github.com/farizrahma... 綠色是編碼器,黃色是解碼器,橙色箭頭傳遞lstm層狀態信息(記憶信息),編碼器惟一傳給解碼器的狀態信息。
解碼器每一時序輸入是前一個時序輸出,經過不一樣時序輸入「How are you <EOL>」,模型能自動一個字一個字輸出「W I am fine <EOL>」,W是特殊標識,是編碼器最後輸出,是解碼器觸發信號。
直接把解碼器每一時序輸入強制改成"W I am fine",把這部分從訓練樣本輸入X中傳過來,Y依然是預測輸出"W I am fine <EOL>",這樣訓練出來的模型就是編碼器解碼器模型。
使用訓練模型預測,在解碼時之前一時序輸出爲輸入作預測,就能輸出"W I am fine <EOL>」。

語料準備。至少300w聊天語料用於詞向量訓練和seq2seq模型訓練,語料越豐富訓練詞向量質量越好。
切詞:

python word_segment.py ./corpus.raw ./corpus.segment

切詞文件轉成「|」分隔問答對:

cat ./corpus.segment | awk '{if(last!="")print last"|"$0;last=$0}' | sed 's/| /|/g' > ./corpus.segment.pair

訓練詞向量。用google word2vec訓練詞向量:

word2vec -train ./corpus.segment -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-5 -threads 20 -binary 1 -iter 15

corpus.raw 原始語料數據,vectors.bin 生成的詞向量二進制文件。
生成詞向量二進制加載方法 https://github.com/warmheartl...

建立模型。用tensorflow+tflearn庫來實現。

# 首先咱們爲輸入的樣本數據申請變量空間,以下。其中self.max_seq_len是指一個切好詞的句子最多包含多少個詞,self.word_vec_dim是詞向量的維度,這裏面shape指定了輸入數據是不肯定數量的樣本,每一個樣本最多包含max_seq_len*2個詞,每一個詞用word_vec_dim維浮點數表示。這裏面用2倍的max_seq_len是由於咱們訓練是輸入的X既要包含question句子又要包含answer句子
input_data = tflearn.input_data(shape=[None, self.max_seq_len*2, self.word_vec_dim], dtype=tf.float32, name = "XY")

# 而後咱們將輸入的全部樣本數據的詞序列切出前max_seq_len個,也就是question句子部分,做爲編碼器的輸入
encoder_inputs = tf.slice(input_data, [0, 0, 0], [-1, self.max_seq_len, self.word_vec_dim], name="enc_in")

# 再取出後max_seq_len-1個,也就是answer句子部分,做爲解碼器的輸入。注意,這裏只取了max_seq_len-1個,是由於還要在前面拼上一組GO標識來告訴解碼器咱們要開始解碼了,也就是下面加上go_inputs拼成最終的go_inputs
decoder_inputs_tmp = tf.slice(input_data, [0, self.max_seq_len, 0], [-1, self.max_seq_len-1, self.word_vec_dim], name="dec_in_tmp")
go_inputs = tf.ones_like(decoder_inputs_tmp)
go_inputs = tf.slice(go_inputs, [0, 0, 0], [-1, 1, self.word_vec_dim])
decoder_inputs = tf.concat(1, [go_inputs, decoder_inputs_tmp], name="dec_in")

# 以後開始編碼過程,返回的encoder_output_tensor展開成tflearn.regression迴歸能夠識別的形如(?, 1, 200)的向量;返回的states後面傳入給解碼器
(encoder_output_tensor, states) = tflearn.lstm(encoder_inputs, self.word_vec_dim, return_state=True, scope='encoder_lstm')
encoder_output_sequence = tf.pack([encoder_output_tensor], axis=1)

# 取出decoder_inputs的第一個詞,也就是GO
first_dec_input = tf.slice(decoder_inputs, [0, 0, 0], [-1, 1, self.word_vec_dim])

# 將其輸入到解碼器中,以下,解碼器的初始化狀態爲編碼器生成的states,注意:這裏的scope='decoder_lstm'是爲了下面重用同一個解碼器
decoder_output_tensor = tflearn.lstm(first_dec_input, self.word_vec_dim, initial_state=states, return_seq=False, reuse=False, scope='decoder_lstm')

# 暫時先將解碼器的第一個輸出存到decoder_output_sequence_list中供最後一塊兒輸出
decoder_output_sequence_single = tf.pack([decoder_output_tensor], axis=1)
decoder_output_sequence_list = [decoder_output_tensor]

# 接下來咱們循環max_seq_len-1次,不斷取decoder_inputs的一個個詞向量做爲下一輪解碼器輸入,並將結果添加到decoder_output_sequence_list中,這裏面的reuse=True, scope='decoder_lstm'說明和上面第一次解碼用的是同一個lstm層
for i in range(self.max_seq_len-1):
   next_dec_input = tf.slice(decoder_inputs, [0, i+1, 0], [-1, 1, self.word_vec_dim])
   decoder_output_tensor = tflearn.lstm(next_dec_input, self.word_vec_dim, return_seq=False, reuse=True, scope='decoder_lstm')
   decoder_output_sequence_single = tf.pack([decoder_output_tensor], axis=1)
   decoder_output_sequence_list.append(decoder_output_tensor)

# 下面咱們把編碼器第一個輸出和解碼器全部輸出拼接起來,做爲tflearn.regression迴歸的輸入
decoder_output_sequence = tf.pack(decoder_output_sequence_list, axis=1)
real_output_sequence = tf.concat(1, [encoder_output_sequence, decoder_output_sequence])
net = tflearn.regression(real_output_sequence, optimizer='sgd', learning_rate=0.1, loss='mean_square')
model = tflearn.DNN(net)

模型建立完成,彙總思想:

1)訓練輸入X、Y分別是編碼器解碼器輸入和預測輸出;
2)X切分兩半,前一半是編碼器輸入,後一半是解碼器輸入;
3)編碼解碼器輸出預測值用Y作迴歸訓練
4)訓練經過樣本真實值做解碼器輸入,實際預測不會有WXYZ部分,上一時序輸出將做下一時序輸入

訓練模型。實例化模型並喂數據作訓練:

model = self.model()
model.fit(trainXY, trainY, n_epoch=1000, snapshot_epoch=False, batch_size=1)
model.load('./model/model')

trainXY和trainY經過加載語料賦值。

加載詞向量存到word_vector_dict,讀取語料文件挨個詞查word_vector_dict,賦值向量給question_seq和answer_seq:

def init_seq(input_file):
    """讀取切好詞的文本文件,加載所有詞序列
    """
    file_object = open(input_file, 'r')
    vocab_dict = {}
    while True:
        question_seq = []
        answer_seq = []
        line = file_object.readline()
        if line:
            line_pair = line.split('|')
            line_question = line_pair[0]
            line_answer = line_pair[1]
            for word in line_question.decode('utf-8').split(' '):
                if word_vector_dict.has_key(word):
                    question_seq.append(word_vector_dict[word])
            for word in line_answer.decode('utf-8').split(' '):
                if word_vector_dict.has_key(word):
                    answer_seq.append(word_vector_dict[word])
        else:
            break
        question_seqs.append(question_seq)
        answer_seqs.append(answer_seq)
    file_object.close()

有question_seq和answer_seq,構造trainXY和trainY:

def generate_trainig_data(self):
        xy_data = []
        y_data = []
        for i in range(len(question_seqs)):
            question_seq = question_seqs[i]
            answer_seq = answer_seqs[i]
            if len(question_seq) < self.max_seq_len and len(answer_seq) < self.max_seq_len:
                sequence_xy = [np.zeros(self.word_vec_dim)] * (self.max_seq_len-len(question_seq)) + list(reversed(question_seq))
                sequence_y = answer_seq + [np.zeros(self.word_vec_dim)] * (self.max_seq_len-len(answer_seq))
                sequence_xy = sequence_xy + sequence_y
                sequence_y = [np.ones(self.word_vec_dim)] + sequence_y
                xy_data.append(sequence_xy)
                y_data.append(sequence_y)
        return np.array(xy_data), np.array(y_data)

構造訓練數據建立模型,訓練:

python my_seq2seq_v2.py train

最終生成./model/model模型文件。

效果預測。訓練模型,輸入一句話預測一下回答:

predict = model.predict(testXY)

只有question沒有answer,testXY沒有Y部分,用上一句輸出做爲下一句輸入:

for i in range(self.max_seq_len-1):
   # next_dec_input = tf.slice(decoder_inputs, [0, i+1, 0], [-1, 1, self.word_vec_dim])這裏改爲下面這句
   next_dec_input = decoder_output_sequence_single
   decoder_output_tensor = tflearn.lstm(next_dec_input, self.word_vec_dim, return_seq=False, reuse=True, scope='decoder_lstm')
   decoder_output_sequence_single = tf.pack([decoder_output_tensor], axis=1)
   decoder_output_sequence_list.append(decoder_output_tensor)

詞向量是多維浮點數,預測詞向量要經過餘弦類似度匹配,餘弦類似度匹配方法:

def vector2word(vector):
    max_cos = -10000
    match_word = ''
    for word in word_vector_dict:
        v = word_vector_dict[word]
        cosine = vector_cosine(vector, v)
        if cosine > max_cos:
            max_cos = cosine
            match_word = word
    return (match_word, max_cos)

其中的vector_cosine實現以下:

def vector_cosine(v1, v2):
    if len(v1) != len(v2):
        sys.exit(1)
    sqrtlen1 = vector_sqrtlen(v1)
    sqrtlen2 = vector_sqrtlen(v2)
    value = 0
    for item1, item2 in zip(v1, v2):
        value += item1 * item2
    return value / (sqrtlen1*sqrtlen2)

def vector_sqrtlen(vector):
    len = 0
    for item in vector:
        len += item * item
    len = math.sqrt(len)
    return len

預測:

python my_seq2seq_v2.py test test.data

輸出第一列是預測每一個時序產生詞,第二列是預測輸出向量和最近詞向量餘弦類似度,第三列是預測向量歐氏距離。
max_seq_len定長8,輸出序列最後會多餘一些字,根據餘弦類似度或者其餘指標設定一個閾值截斷。
所有代碼 https://github.com/warmheartl...

參考資料:
《Python 天然語言處理》
《NLTK基礎教程 用NLTK和Python庫構建機器學習應用》
http://www.shareditor.com/blo...
http://www.shareditor.com/blo...
http://www.shareditor.com/blo...

歡迎推薦上海機器學習工做機會,個人微信:qingxingfengzi

相關文章
相關標籤/搜索