建立網(wǎng)站如何推廣東莞優(yōu)化疫情防控措施
文章目錄
- 0 簡介
- 1 項(xiàng)目架構(gòu)
- 2 項(xiàng)目的主要過程
- 2.1 數(shù)據(jù)清洗、預(yù)處理
- 2.2 分桶
- 2.3 訓(xùn)練
- 3 項(xiàng)目的整體結(jié)構(gòu)
- 4 重要的API
- 4.1 LSTM cells部分:
- 4.2 損失函數(shù):
- 4.3 搭建seq2seq框架:
- 4.4 測試部分:
- 4.5 評價NLP測試效果:
- 4.6 梯度截斷,防止梯度爆炸
- 4.7 模型保存
- 5 重點(diǎn)和難點(diǎn)
- 5.1 函數(shù)
- 5.2 變量
- 6 相關(guān)參數(shù)
- 7 桶機(jī)制
- 7.1 處理數(shù)據(jù)集
- 7.2 詞向量處理seq2seq
- 7.3 處理問答及答案權(quán)重
- 7.4 訓(xùn)練&保存模型
- 7.5 載入模型&測試
- 8 最后
0 簡介
🔥 優(yōu)質(zhì)競賽項(xiàng)目系列,今天要分享的是
基于深度學(xué)習(xí)的中文對話問答機(jī)器人
該項(xiàng)目較為新穎,適合作為競賽課題方向,學(xué)長非常推薦!
🧿 更多資料, 項(xiàng)目分享:
https://gitee.com/dancheng-senior/postgraduate
1 項(xiàng)目架構(gòu)
整個項(xiàng)目分為 數(shù)據(jù)清洗 和 建立模型兩個部分。
(1)主要定義了seq2seq這樣一個模型。
首先是一個構(gòu)造函數(shù),在構(gòu)造函數(shù)中定義了這個模型的參數(shù)。
以及構(gòu)成seq2seq的基本單元的LSTM單元是怎么構(gòu)建的。
(2)接著在把這個LSTM間單元構(gòu)建好之后,加入模型的損失函數(shù)。
我們這邊用的損失函數(shù)叫sampled_softmax_loss,這個實(shí)際上就是我們的采樣損失。做softmax的時候,我們是從這個6000多維里邊找512個出來做采樣。
損失函數(shù)做訓(xùn)練的時候需要,測試的時候不需要。訓(xùn)練的時候,y值是one_hot向量
(3)然后再把你定義好的整個的w[512*6000]、b[6000多維],還有我們的這個cell本身,以及我們的這個損失函數(shù)一同代到我們這個seq2seq模型里邊。然后呢,這樣的話就構(gòu)成了我們這樣一個seq2seq模型。
函數(shù)是tf.contrib.legacy_seq2seq.embedding_attention_seq2seq()
(4)最后再將我們傳入的實(shí)參,也就是三個序列,經(jīng)過這個桶的篩選。然后放到這個模型去訓(xùn)練啊,那么這個模型就會被訓(xùn)練好。到后面,我們可以把我們這個模型保存在model里面去。模型參數(shù)195M。做桶的目的就是節(jié)約計算資源。
2 項(xiàng)目的主要過程
前提是一問一答,情景對話,不是多輪對話(比較難,但是熱門領(lǐng)域)
整個框架第一步:做語料
先拿到一個文件,命名為.conv(只要不命名那幾個特殊的,word等)。輸入目錄是db,輸出目錄是bucket_dbs,不存在則新建目錄。
測試的時候,先在控制臺輸入一句話,然后將這句話通過正反向字典Ids化,然后去桶里面找對應(yīng)的回答的每一個字,然后將輸出通過反向字典轉(zhuǎn)化為漢字。
2.1 數(shù)據(jù)清洗、預(yù)處理
讀取整個語料庫,去掉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維的向量組成的。后面的隱藏層的神經(jīng)元的個數(shù)是512,也就是維度。
句子長度超過桶長,就截斷或直接丟棄。
四個桶是在read_bucket_dbs()讀取的方法中創(chuàng)建的,讀桶文件的時候,實(shí)例化四個桶對象。
2.3 訓(xùn)練
先讀取json字典,加上pad等四個標(biāo)記。
lstm有兩層,attention在解碼器的第二層,因?yàn)榈诙硬攀莑stm的輸出,用兩層提取到的特征越好。
num_sampled=512, 分批softmax的樣本量(
訓(xùn)練和測試差不多,測試只前向傳播,不反向更新
3 項(xiàng)目的整體結(jié)構(gòu)
s2s.py:相當(dāng)于main函數(shù),讓代碼運(yùn)行起來
里面有train()、test()、test_bleu()和create_model()四個方法,還有FLAGS成員變量,
相當(dāng)于靜態(tài)成員變量 public static final string
decode_conv.py和data_utils.py:是數(shù)據(jù)處理
s2s_model.py:
里面放的是模型
里面有init()、step()、get_batch_data()和get_batch()四個方法。構(gòu)造方法傳入構(gòu)造方法的參數(shù),搭建S2SModel框架,然后sampled_loss()和seq2seq_f()兩個方法
data_utils.py:
讀取數(shù)據(jù)庫中的文件,并且構(gòu)造正反向字典。把語料分成四個桶,目的是節(jié)約計算資源。先轉(zhuǎn)換為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ù)字、字母、標(biāo)點(diǎn)符號、漢字的字典,加上生僻字,以及PAD、EOS、GO、UNK 共6865維度,輸入的時候會進(jìn)行詞嵌入word
embedding成512維,輸出時,再轉(zhuǎn)化為6865維。
model:
文件夾下裝的是訓(xùn)練好的模型。
也就是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),這里加載模型比較耗時,時間復(fù)雜度最高
dgk_shooter_min.conv:
是語料,形如: E
M 畹/華/吾/侄/
M 你/接/到/這/封/信/的/時/候/
decode_conv.py: 對語料數(shù)據(jù)進(jìn)行預(yù)處理
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
構(gòu)建雙層lstm網(wǎng)絡(luò),只是一個雙層的lstm,不是雙層的seq2seq
4.2 損失函數(shù):
?
tf.nn.sampled_softmax_loss( weights=local_w_t,
b labels=labels, #真實(shí)序列值,每次一個
inputs=loiases=local_b,
cal_inputs, #預(yù)測出來的值,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 30decoder_inputs, # tensor of decoder seq 30tmp_cell, #自定義的cell,可以是GRU/LSTM, 設(shè)置multilayer等num_encoder_symbols=source_vocab_size,# 編碼階段字典的維度6865num_decoder_symbols=target_vocab_size, # 解碼階段字典的維度 6865embedding_size=size, # embedding 維度,512num_heads=20, #選20個也可以,精確度會高點(diǎn),num_heads就是attention機(jī)制,選一個就是一個head去連,5個就是5個頭去連output_projection=output_projection,# 輸出層。不設(shè)定的話輸出維數(shù)可能很大(取決于詞表大小),設(shè)定的話投影到一個低維向量feed_previous=do_decode,# 是否執(zhí)行的EOS,是否允許輸入中間cdtype=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,可以評估測試結(jié)果,NITK是個框架
?
from nltk.translate.bleu_score import sentence_bleu
score = sentence_bleu(
references,#y值
list(ret),#y^
weights=(1.0,)#權(quán)重為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)
通過權(quán)重梯度的總和的比率來截取多個張量的值。t_list是梯度張量, clip_norm是截取的比率,這個函數(shù)返回截取過的梯度張量和一個所有張量的全局范數(shù)
4.7 模型保存
?
tf.train.Saver(tf.global_variables(), write_version=tf.train.SaverDef.V2)
5 重點(diǎn)和難點(diǎn)
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 相關(guān)參數(shù)
?
model = s2s_model.S2SModel(data_utils.dim, # 6865,編碼器輸入的語料長度data_utils.dim, # 6865,解碼器輸出的語料長度buckets, # buckets就是那四個桶,data_utils.buckets,直接在data_utils寫的一個變量,就能直接被點(diǎn)出來FLAGS.size, # 隱層神經(jīng)元的個數(shù)512FLAGS.dropout, # 隱層dropout率,dropout不是lstm中的,lstm的幾個門里面不需要dropout,沒有那么復(fù)雜。是隱層的dropoutFLAGS.num_layers, # lstm的層數(shù),這里寫的是2FLAGS.max_gradient_norm, # 5,截斷梯度,防止梯度爆炸FLAGS.batch_size, # 64,等下要重新賦值,預(yù)測就是1,訓(xùn)練就是64FLAGS.learning_rate, # 0.003FLAGS.num_samples, # 512,用作負(fù)采樣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,/*每次往里面放多少組對話對,這個是比較靈活的。如果找一句話之間的相關(guān)性,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 桶機(jī)制
7.1 處理數(shù)據(jù)集
語料庫長度桶結(jié)構(gòu)
(5, 10): 5問題長度,10回答長度
每個桶中對話數(shù)量,一問一答為一次完整對話
Analysis
(1) 設(shè)定4個桶結(jié)構(gòu),即將問答分成4個部分,每個同種存放對應(yīng)的問答數(shù)據(jù)集[87, 69, 36,
8]四個桶中分別有87組對話,69組對話,36組對話,8組對話;
(2) 訓(xùn)練詞數(shù)據(jù)集符合桶長度則輸入對應(yīng)值,不符合桶長度,則為空;
(3) 對話數(shù)量占比:[0.435, 0.78, 0.96, 1.0];
7.2 詞向量處理seq2seq
獲取問答及答案權(quán)重
參數(shù):
- data: 詞向量列表,如[[[4,4],[5,6,8]]]
- bucket_id: 桶編號,值取自桶對話占比
步驟:
- 問題和答案的數(shù)據(jù)量:桶的話數(shù)buckets = [(5, 10), (10, 15), (20, 25), (40, 50)]
- 生成問題和答案的存儲器
- 從問答數(shù)據(jù)集中隨機(jī)選取問答
- 問題末尾添加PAD_ID并反向排序
- 答案添加GO_ID和PAD_ID
- 問題,答案,權(quán)重批量數(shù)據(jù)
- 批量問題
- 批量答案
- 答案權(quán)重即Attention機(jī)制
- 若答案為PAD則權(quán)重設(shè)置為0,因?yàn)槭翘砑拥腎D,其他的設(shè)置為1
Analysis
-
(1) 對問題和答案的向量重新整理,符合桶尺寸則保持對話尺寸,若不符合桶設(shè)定尺寸,則進(jìn)行填充處理,
問題使用PAD_ID填充,答案使用GO_ID和PAD_ID填充; -
(2) 對問題和答案向量填充整理后,使用Attention機(jī)制,對答案進(jìn)行權(quán)重分配,答案中的PAD_ID權(quán)重為0,其他對應(yīng)的為1;
-
(3) get_batch()處理詞向量;返回問題、答案、答案權(quán)重數(shù)據(jù);
返回結(jié)果如上結(jié)果:encoder_inputs, decoder_inputs, answer_weights.
7.3 處理問答及答案權(quán)重
?
參數(shù):session: tensorflow 會話.encoder_inputs: 問題向量列表decoder_inputs: 回答向量列表answer_weights: 答案權(quán)重列表bucket_id: 桶編號which bucket of the model to use.forward_only: 前向或反向運(yùn)算標(biāo)志位
返回:一個由梯度范數(shù)組成的三重范數(shù)(如果不使用反向傳播,則為無)。平均困惑度和輸出
Analysis
-
(1) 根據(jù)輸入的問答向量列表,分配語料桶,處理問答向量列表,并生成新的輸入字典(dict), input_feed = {};
-
(2) 輸出字典(dict), ouput_feed = {},根據(jù)是否使用反向傳播獲得參數(shù),使用反向傳播,
output_feed存儲更新的梯度范數(shù),損失,不使用反向傳播,則只存儲損失; -
(3) 最終的輸出為分兩種情況,使用反向傳播,返回梯度范數(shù),損失,如反向傳播不使用反向傳播,
返回?fù)p失和輸出的向量(用于加載模型,測試效果),如前向傳播;
7.4 訓(xùn)練&保存模型
步驟:
-
檢查是否有已存在的訓(xùn)練模型
-
有模型則獲取模型輪數(shù),接著訓(xùn)練
-
沒有模型則從開始訓(xùn)練
-
一直訓(xùn)練,每過一段時間保存一次模型
-
如果模型沒有得到提升,減小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) 該聊天機(jī)器人使用bucket桶結(jié)構(gòu),即指定問答數(shù)據(jù)的長度,匹配符合的桶,在桶中進(jìn)行存取數(shù)據(jù);
(2) 該seq2seq模型使用Tensorflow時,未能建立獨(dú)立標(biāo)識的圖結(jié)構(gòu),在進(jìn)行后臺封裝過程中出現(xiàn)圖為空的現(xiàn)象;
?
從main函數(shù)進(jìn)入test()方法。先去內(nèi)存中加載訓(xùn)練好的模型model,這部分最耗時,改batch_size為1,傳入相關(guān)的參數(shù)。開始輸入一個句子,并將它讀進(jìn)來,讀進(jìn)來之后,按照桶將句子分,按照模型輸出,然后去查字典。接著在循環(huán)中輸入上句話,找對應(yīng)的桶。然后拿到的下句話的每個字,找概率最大的那個字的index的id輸出。get_batch_data(),獲取data [('天氣\n', '')],也就是問答對,但是現(xiàn)在只有問,沒有答get_batch()獲取encoder_inputs=1*10,decoder_inputs=1*20 decoder_weights=1*20step()獲取預(yù)測值output_logits,
8 最后
🧿 更多資料, 項(xiàng)目分享:
https://gitee.com/dancheng-senior/postgraduate