0 簡介
?? 優(yōu)質競賽項目系列,今天要分享的是
基于深度學習的中文對話問答機器人
該項目較為新穎,適合作為競賽課題方向,學長非常推薦!
?? 更多資料, 項目分享:文章來源:http://www.zghlxwxcb.cn/news/detail-765147.html
https://gitee.com/dancheng-senior/postgraduate文章來源地址http://www.zghlxwxcb.cn/news/detail-765147.html
1 項目架構
整個項目分為 數(shù)據(jù)清洗 和 建立模型兩個部分。
(1)主要定義了seq2seq這樣一個模型。
首先是一個構造函數(shù),在構造函數(shù)中定義了這個模型的參數(shù)。
以及構成seq2seq的基本單元的LSTM單元是怎么構建的。
(2)接著在把這個LSTM間單元構建好之后,加入模型的損失函數(shù)。
我們這邊用的損失函數(shù)叫sampled_softmax_loss,這個實際上就是我們的采樣損失。做softmax的時候,我們是從這個6000多維里邊找512個出來做采樣。
損失函數(shù)做訓練的時候需要,測試的時候不需要。訓練的時候,y值是one_hot向量
(3)然后再把你定義好的整個的w[512*6000]、b[6000多維],還有我們的這個cell本身,以及我們的這個損失函數(shù)一同代到我們這個seq2seq模型里邊。然后呢,這樣的話就構成了我們這樣一個seq2seq模型。
函數(shù)是tf.contrib.legacy_seq2seq.embedding_attention_seq2seq()
(4)最后再將我們傳入的實參,也就是三個序列,經過這個桶的篩選。然后放到這個模型去訓練啊,那么這個模型就會被訓練好。到后面,我們可以把我們這個模型保存在model里面去。模型參數(shù)195M。做桶的目的就是節(jié)約計算資源。
2 項目的主要過程
前提是一問一答,情景對話,不是多輪對話(比較難,但是熱門領域)
整個框架第一步:做語料
先拿到一個文件,命名為.conv(只要不命名那幾個特殊的,word等)。輸入目錄是db,輸出目錄是bucket_dbs,不存在則新建目錄。
測試的時候,先在控制臺輸入一句話,然后將這句話通過正反向字典Ids化,然后去桶里面找對應的回答的每一個字,然后將輸出通過反向字典轉化為漢字。
2.1 數(shù)據(jù)清洗、預處理
讀取整個語料庫,去掉E、M和空格,還原成原始文本。創(chuàng)建conversion.db,conversion表,兩個字段。每取完1000組對話,插入依次數(shù)據(jù)庫,批量提交,通過cursor.commit.
2.2 分桶
從總的conversion.db中分桶,指定輸入目錄db, 輸出目錄bucket_dbs.
檢測文字有效性,循環(huán)遍歷,依次記錄問題答案,每積累到1000次,就寫入數(shù)據(jù)庫。
?
for ask, answer in tqdm(ret, total=total):
if is_valid(ask) and is_valid(answer):
for i in range(len(buckets)):
encoder_size, decoder_size = buckets[i]
if len(ask) <= encoder_size and len(answer) < decoder_size:
word_count.update(list(ask))
word_count.update(list(answer))
wait_insert.append((encoder_size, decoder_size, ask, answer))
if len(wait_insert) > 10000000:
wait_insert = _insert(wait_insert)
break
將字典維度6865未,投影到100維,也就是每個字是由100維的向量組成的。后面的隱藏層的神經元的個數(shù)是512,也就是維度。
句子長度超過桶長,就截斷或直接丟棄。
四個桶是在read_bucket_dbs()讀取的方法中創(chuàng)建的,讀桶文件的時候,實例化四個桶對象。
2.3 訓練
先讀取json字典,加上pad等四個標記。
lstm有兩層,attention在解碼器的第二層,因為第二層才是lstm的輸出,用兩層提取到的特征越好。
num_sampled=512, 分批softmax的樣本量(
訓練和測試差不多,測試只前向傳播,不反向更新
3 項目的整體結構
s2s.py:相當于main函數(shù),讓代碼運行起來
里面有train()、test()、test_bleu()和create_model()四個方法,還有FLAGS成員變量,
相當于靜態(tài)成員變量 public static final string
decode_conv.py和data_utils.py:是數(shù)據(jù)處理
s2s_model.py:
里面放的是模型
里面有init()、step()、get_batch_data()和get_batch()四個方法。構造方法傳入構造方法的參數(shù),搭建S2SModel框架,然后sampled_loss()和seq2seq_f()兩個方法
data_utils.py:
讀取數(shù)據(jù)庫中的文件,并且構造正反向字典。把語料分成四個桶,目的是節(jié)約計算資源。先轉換為db\conversation.db大的桶,再分成四個小的桶。buckets
= [ (5, 15), (10, 20), (15, 25), (20, 30)]
比如buckets[1]指的就是(10, 20),buckets[1][0]指的就是10。
bucket_id指的就是0,1,2,3
dictionary.json:
是所有數(shù)字、字母、標點符號、漢字的字典,加上生僻字,以及PAD、EOS、GO、UNK 共6865維度,輸入的時候會進行詞嵌入word
embedding成512維,輸出時,再轉化為6865維。
model:
文件夾下裝的是訓練好的模型。
也就是model3.data-00000-of-00001,這個里面裝的就是模型的參數(shù)
執(zhí)行model.saver.restore(sess, os.path.join(FLAGS.model_dir,
FLAGS.model_name))的時候,才是加載目錄本地的保存的模型參數(shù)的過程,上面建立的模型是個架子,
model = create_model(sess, True),這里加載模型比較耗時,時間復雜度最高
dgk_shooter_min.conv:
是語料,形如: E
M 畹/華/吾/侄/
M 你/接/到/這/封/信/的/時/候/
decode_conv.py: 對語料數(shù)據(jù)進行預處理
config.json:是配置文件,自動生成的
4 重要的API
4.1 LSTM cells部分:
?
cell = tf.contrib.rnn.BasicLSTMCell(size)
cell = tf.contrib.rnn.DropoutWrapper(cell, output_keep_prob=dropout)
cell = tf.contrib.rnn.MultiRNNCell([cell] * num_layers)
對上一行的cell去做Dropout的,在外面裹一層DropoutWrapper
構建雙層lstm網絡,只是一個雙層的lstm,不是雙層的seq2seq
4.2 損失函數(shù):
?
tf.nn.sampled_softmax_loss( weights=local_w_t,
b labels=labels, #真實序列值,每次一個
inputs=loiases=local_b,
cal_inputs, #預測出來的值,y^,每次一個
num_sampled=num_samples, #512
num_classes=self.target_vocab_size # 原始字典維度6865)
4.3 搭建seq2seq框架:
tf.contrib.legacy_seq2seq.embedding_attention_seq2seq(
encoder_inputs, # tensor of input seq 30
decoder_inputs, # tensor of decoder seq 30
tmp_cell, #自定義的cell,可以是GRU/LSTM, 設置multilayer等
num_encoder_symbols=source_vocab_size,# 編碼階段字典的維度6865
num_decoder_symbols=target_vocab_size, # 解碼階段字典的維度 6865
embedding_size=size, # embedding 維度,512
num_heads=20, #選20個也可以,精確度會高點,num_heads就是attention機制,選一個就是一個head去連,5個就是5個頭去連
output_projection=output_projection,# 輸出層。不設定的話輸出維數(shù)可能很大(取決于詞表大小),設定的話投影到一個低維向量
feed_previous=do_decode,# 是否執(zhí)行的EOS,是否允許輸入中間c
dtype=dtype
)
4.4 測試部分:
?
self.outputs, self.losses = tf.contrib.legacy_seq2seq.model_with_buckets(
self.encoder_inputs,
self.decoder_inputs,
targets,
self.decoder_weights,
buckets,
lambda x, y: seq2seq_f(x, y, True),
softmax_loss_function=softmax_loss_function
)
4.5 評價NLP測試效果:
在nltk包里,有個接口叫bleu,可以評估測試結果,NITK是個框架
?
from nltk.translate.bleu_score import sentence_bleu
score = sentence_bleu(
references,#y值
list(ret),#y^
weights=(1.0,)#權重為1
)
4.6 梯度截斷,防止梯度爆炸
?
clipped_gradients, norm = tf.clip_by_global_norm(gradients,max_gradient_norm)
tf.clip_by_global_norm(t_list, clip_norm, use_norm=None, name=None)
通過權重梯度的總和的比率來截取多個張量的值。t_list是梯度張量, clip_norm是截取的比率,這個函數(shù)返回截取過的梯度張量和一個所有張量的全局范數(shù)
4.7 模型保存
?
tf.train.Saver(tf.global_variables(), write_version=tf.train.SaverDef.V2)
5 重點和難點
5.1 函數(shù)
?
def get_batch_data(self, bucket_dbs, bucket_id):
def get_batch(self, bucket_dbs, bucket_id, data):
def step(self,session,encoder_inputs,decoder_inputs,decoder_weights,bucket_id):
5.2 變量
?
batch_encoder_inputs, batch_decoder_inputs, batch_weights = [], [], []
6 相關參數(shù)
?
model = s2s_model.S2SModel(
data_utils.dim, # 6865,編碼器輸入的語料長度
data_utils.dim, # 6865,解碼器輸出的語料長度
buckets, # buckets就是那四個桶,data_utils.buckets,直接在data_utils寫的一個變量,就能直接被點出來
FLAGS.size, # 隱層神經元的個數(shù)512
FLAGS.dropout, # 隱層dropout率,dropout不是lstm中的,lstm的幾個門里面不需要dropout,沒有那么復雜。是隱層的dropout
FLAGS.num_layers, # lstm的層數(shù),這里寫的是2
FLAGS.max_gradient_norm, # 5,截斷梯度,防止梯度爆炸
FLAGS.batch_size, # 64,等下要重新賦值,預測就是1,訓練就是64
FLAGS.learning_rate, # 0.003
FLAGS.num_samples, # 512,用作負采樣
forward_only, #只傳一次
dtype
)
{
"__author__": "qhduan@memect.co",
"buckets": [
[5, 15],
[10, 20],
[20, 30],
[40, 50]
],
"size": 512,
/*s2s lstm單元出來之后的,連的隱層的number unit是512*/
"depth": 4,
"dropout": 0.8,
"batch_size": 512,
/*每次往里面放多少組對話對,這個是比較靈活的。
如果找一句話之間的相關性,batch_size就是這句話里面的字有多少個,
如果要找上下文之間的對話,batch_size就是多少組對話*/
"random_state": 0,
"learning_rate": 0.0003,
/*總共循環(huán)20次*/
"epoch": 20,
"train_device": "/gpu:0",
"test_device": "/cpu:0"
}
7 桶機制
7.1 處理數(shù)據(jù)集
語料庫長度桶結構
(5, 10): 5問題長度,10回答長度
每個桶中對話數(shù)量,一問一答為一次完整對話
Analysis
(1) 設定4個桶結構,即將問答分成4個部分,每個同種存放對應的問答數(shù)據(jù)集[87, 69, 36,
8]四個桶中分別有87組對話,69組對話,36組對話,8組對話;
(2) 訓練詞數(shù)據(jù)集符合桶長度則輸入對應值,不符合桶長度,則為空;
(3) 對話數(shù)量占比:[0.435, 0.78, 0.96, 1.0];
7.2 詞向量處理seq2seq
獲取問答及答案權重
參數(shù):
- data: 詞向量列表,如[[[4,4],[5,6,8]]]
- bucket_id: 桶編號,值取自桶對話占比
步驟:
- 問題和答案的數(shù)據(jù)量:桶的話數(shù)buckets = [(5, 10), (10, 15), (20, 25), (40, 50)]
- 生成問題和答案的存儲器
- 從問答數(shù)據(jù)集中隨機選取問答
- 問題末尾添加PAD_ID并反向排序
- 答案添加GO_ID和PAD_ID
- 問題,答案,權重批量數(shù)據(jù)
- 批量問題
- 批量答案
- 答案權重即Attention機制
- 若答案為PAD則權重設置為0,因為是添加的ID,其他的設置為1
Analysis
-
(1) 對問題和答案的向量重新整理,符合桶尺寸則保持對話尺寸,若不符合桶設定尺寸,則進行填充處理,
問題使用PAD_ID填充,答案使用GO_ID和PAD_ID填充; -
(2) 對問題和答案向量填充整理后,使用Attention機制,對答案進行權重分配,答案中的PAD_ID權重為0,其他對應的為1;
-
(3) get_batch()處理詞向量;返回問題、答案、答案權重數(shù)據(jù);
返回結果如上結果:encoder_inputs, decoder_inputs, answer_weights.
7.3 處理問答及答案權重
?
參數(shù):
session: tensorflow 會話.
encoder_inputs: 問題向量列表
decoder_inputs: 回答向量列表
answer_weights: 答案權重列表
bucket_id: 桶編號which bucket of the model to use.
forward_only: 前向或反向運算標志位
返回:
一個由梯度范數(shù)組成的三重范數(shù)(如果不使用反向傳播,則為無)。
平均困惑度和輸出
Analysis
-
(1) 根據(jù)輸入的問答向量列表,分配語料桶,處理問答向量列表,并生成新的輸入字典(dict), input_feed = {};
-
(2) 輸出字典(dict), ouput_feed = {},根據(jù)是否使用反向傳播獲得參數(shù),使用反向傳播,
output_feed存儲更新的梯度范數(shù),損失,不使用反向傳播,則只存儲損失; -
(3) 最終的輸出為分兩種情況,使用反向傳播,返回梯度范數(shù),損失,如反向傳播不使用反向傳播,
返回損失和輸出的向量(用于加載模型,測試效果),如前向傳播;
7.4 訓練&保存模型
步驟:
-
檢查是否有已存在的訓練模型
-
有模型則獲取模型輪數(shù),接著訓練
-
沒有模型則從開始訓練
-
一直訓練,每過一段時間保存一次模型
-
如果模型沒有得到提升,減小learning rate
-
保存模型
-
使用測試數(shù)據(jù)評估模型
global step: 500, learning rate: 0.5, loss: 2.574068747580052 bucket id: 0, eval ppx: 14176.588030763274 bucket id: 1, eval ppx: 3650.0026667220773 bucket id: 2, eval ppx: 4458.454110999805 bucket id: 3, eval ppx: 5290.083583183104
7.5 載入模型&測試
(1) 該聊天機器人使用bucket桶結構,即指定問答數(shù)據(jù)的長度,匹配符合的桶,在桶中進行存取數(shù)據(jù);
(2) 該seq2seq模型使用Tensorflow時,未能建立獨立標識的圖結構,在進行后臺封裝過程中出現(xiàn)圖為空的現(xiàn)象;
?
從main函數(shù)進入test()方法。先去內存中加載訓練好的模型model,這部分最耗時,改batch_size為1,傳入相關的參數(shù)。
開始輸入一個句子,并將它讀進來,讀進來之后,按照桶將句子分,按照模型輸出,然后去查字典。
接著在循環(huán)中輸入上句話,找對應的桶。然后拿到的下句話的每個字,找概率最大的那個字的index的id輸出。
get_batch_data(),獲取data [('天氣\n', '')],也就是問答對,但是現(xiàn)在只有問,沒有答
get_batch()獲取encoder_inputs=1*10,decoder_inputs=1*20 decoder_weights=1*20
step()獲取預測值output_logits,
8 最后
?? 更多資料, 項目分享:
https://gitee.com/dancheng-senior/postgraduate
到了這里,關于競賽選題 題目:基于深度學習的中文對話問答機器人的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!