熟悉神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的主體結(jié)構(gòu)并不足以建立性能較好的模型。建立成功的神經(jīng)網(wǎng)絡(luò)語(yǔ)言需要注重許多細(xì)節(jié)處理,如詞典的構(gòu)建、模型初始化貌夕、超參的選擇等等,均涉及很多對(duì)模型性能有較大影響的細(xì)節(jié)民镜。
1. 前言
? ? ? ?近十幾年來(lái)啡专,神經(jīng)網(wǎng)絡(luò)語(yǔ)言建模(Neural Network Language Modeling, NNLM)一直是人工智能(Artificial Intelligence, AI)領(lǐng)域中的研究熱點(diǎn)之一。除了不斷增加的學(xué)術(shù)論文制圈,也有大量的博客们童,貼文對(duì)神經(jīng)網(wǎng)絡(luò)語(yǔ)言建模的相關(guān)技術(shù)進(jìn)行介紹辱揭,包括本系列中的《神經(jīng)網(wǎng)絡(luò)語(yǔ)言建模系列之一:基礎(chǔ)模型》。但是很少有文章對(duì)神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的實(shí)現(xiàn)細(xì)節(jié)進(jìn)行系統(tǒng)地總結(jié)病附,本文以長(zhǎng)短期記憶(Long Short Term Memory, LSTM)循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Network, RNN)語(yǔ)言模型為例问窃,基于Python語(yǔ)言,采用Tensorflow框架完沪,系統(tǒng)地介紹神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的具體實(shí)現(xiàn)過(guò)程域庇。
2. 預(yù)處理
? ? ? ?語(yǔ)言模型的訓(xùn)練需要大量的文本數(shù)據(jù),幸運(yùn)的是訓(xùn)練語(yǔ)言模型的數(shù)據(jù)不需要人工標(biāo)注覆积,屬于無(wú)監(jiān)督訓(xùn)練听皿。文本數(shù)據(jù)可以采用公共數(shù)據(jù)集,也可以自行收集文本數(shù)據(jù)宽档。在公共的數(shù)據(jù)集上訓(xùn)練和測(cè)試語(yǔ)言模型尉姨,由于數(shù)據(jù)已被處理完成,即為熟語(yǔ)料吗冤,就可以省去許多預(yù)處理工作又厉。在此針對(duì)收集到的原始文本數(shù)據(jù),也被稱為生語(yǔ)料椎瘟,介紹用于訓(xùn)練語(yǔ)言模型的文本數(shù)據(jù)的預(yù)處理覆致。
? ? ? ?對(duì)于不同語(yǔ)言的文本,處理的方式會(huì)有所區(qū)別肺蔚。筆者熟悉的主要是英文和中文的文本預(yù)處理煌妈,其他語(yǔ)種文本的預(yù)處理經(jīng)驗(yàn)比較淺。因此宣羊,此處以英文和中文的生語(yǔ)料為例璧诵,簡(jiǎn)要介紹文本數(shù)據(jù)的預(yù)處理,基本步驟如下:
- 文本進(jìn)行清洗是必不可少的仇冯,尤其當(dāng)數(shù)據(jù)來(lái)源于網(wǎng)絡(luò)之宿。針對(duì)不同來(lái)源的數(shù)據(jù),清洗的方式會(huì)有所不同赞枕,但最終的目的都是為了去除文本以外的數(shù)據(jù)澈缺,如網(wǎng)址鏈接坪创、表情符號(hào)炕婶、特殊字符、HTML標(biāo)簽等等莱预;
- 字符轉(zhuǎn)換柠掂,在處理中文文本時(shí)比較常見,將全角字符轉(zhuǎn)換為半角字符依沮,將繁體中文轉(zhuǎn)換為簡(jiǎn)體涯贞,或者相反枪狂;
- 大小轉(zhuǎn)換,為了降低詞典的大小宋渔,有時(shí)會(huì)將文本中的字母統(tǒng)一轉(zhuǎn)成小寫或者大寫州疾。進(jìn)行大小寫轉(zhuǎn)換后,會(huì)丟失部分文本特征皇拣,降低模型性能严蓖,尤其對(duì)于英文這類由字母組成的語(yǔ)言,當(dāng)然也可以選擇不進(jìn)行轉(zhuǎn)化氧急;
- 句子分割颗胡,即根據(jù)文本中的標(biāo)點(diǎn)符號(hào),將大段的文本切分為句子序列吩坝。對(duì)于英文這類語(yǔ)言的文本進(jìn)行切分時(shí)毒姨,需要進(jìn)行額外的工作,包括將標(biāo)點(diǎn)與單詞分開钉寝,將部分縮寫分開弧呐。其中縮寫的分割,如
it's
分割為it 's
嵌纲,這樣可以減少單詞量泉懦,當(dāng)然也可以選擇不進(jìn)行分割。英文的句子分割可采用開源工具NLTK或者斯坦福大學(xué)的自然語(yǔ)言處理工具包CoreNLP疹瘦。對(duì)于中文崩哩,目前還沒有接觸到提供句子切分功能的開源的工具包; - 如果目標(biāo)是中文這類沒有詞邊界的語(yǔ)言文本言沐,就需要進(jìn)行分詞邓嘹。當(dāng)然,目前也有基于字符級(jí)別的語(yǔ)言模型险胰,可以不用進(jìn)行分詞汹押。但是詞作為語(yǔ)言中的重要模式,將分詞信息引入語(yǔ)言模型起便,能夠幫助模型學(xué)習(xí)到更多的語(yǔ)言模式棚贾。中文分詞的開源工具包比較多,比如Jieba榆综,HanLP等妙痹,分詞的精度也會(huì)對(duì)語(yǔ)言模型的性能產(chǎn)生影響。
? ? ? ?經(jīng)過(guò)預(yù)處理鼻疮,文本數(shù)據(jù)的格式為每行一句文本怯伊,分詞之間以空格分隔。完成文本數(shù)據(jù)的預(yù)處理工作后判沟,便可以進(jìn)行數(shù)據(jù)集劃分耿芹。在數(shù)據(jù)充足的情況下崭篡,可分為三個(gè)部分:訓(xùn)練集、驗(yàn)證集和測(cè)試集吧秕。訓(xùn)練集一般占所有數(shù)據(jù)的80%琉闪,用于語(yǔ)言模型的訓(xùn)練;驗(yàn)證集約占總數(shù)據(jù)量的10%砸彬,用于模型超參的調(diào)整塘偎;測(cè)試集由剩余的數(shù)據(jù)組成,訓(xùn)練好的模型將在該數(shù)據(jù)集上進(jìn)行性能測(cè)試拿霉。
3. 構(gòu)建詞典
? ? ? ?從訓(xùn)練集中構(gòu)建詞典是建立語(yǔ)言模型的首要步驟吟秩,而詞典的建立就是將訓(xùn)練集中的分詞(Token)加入到字典中并分配唯一的索引。此處提到的分詞(Token)包括詞(Word)绽淘、標(biāo)點(diǎn)以及文本中與詞級(jí)別相當(dāng)?shù)淖址蛘咦址馈S袝r(shí)當(dāng)數(shù)據(jù)量較大時(shí),分詞的數(shù)量會(huì)很巨大沪铭,導(dǎo)致模型的計(jì)算量較大壮池,就需要對(duì)詞典的大小進(jìn)行限制,將詞頻較低的分詞丟棄掉杀怠。從訓(xùn)練集中構(gòu)建詞典的具體實(shí)現(xiàn)代碼如下所示:
def collect_token(train_file):
"""Build up vocabulary from training dataset.
:Param train_file: the path of training file.
"""
vocab = {} # tokens and their frequency from dataset
input_file = codecs.open(train_file, 'r', 'utf-8')
for sentence in input_file:
for token in sentence.strip().split():
if token not in vocab:
vocab[token] = 0
vocab[token] += 1
input_file.close()
return vocab
? ? ? ?從訓(xùn)練數(shù)據(jù)中收集完分詞之后椰憋,需要為每個(gè)分詞分配唯一的索引。但在分配索引之前赔退,需要向詞典中加入幾個(gè)特殊標(biāo)識(shí)符橙依。當(dāng)對(duì)詞典大小進(jìn)行限制時(shí),部分分詞未被加入詞典硕旗,這類分詞就成為詞典外的詞(Out of Vocabulary窗骑, OOV)或者未登錄詞(Unknown Words),另外漆枚,驗(yàn)證集或者測(cè)試集中也會(huì)存在未登錄詞创译。通過(guò)設(shè)定特殊標(biāo)識(shí)符,如oov
或者<unk>
墙基,來(lái)表示所有的未登錄詞软族,凡是遇到未登錄詞都用該標(biāo)識(shí)符替換。未登錄詞的存在使得語(yǔ)言模型的性能下降比較顯著残制,但目前還沒有很好的解決方案立砸。未登錄詞也一致自然語(yǔ)言相關(guān)的人工智能任務(wù)的難點(diǎn)之一,目前采用字符級(jí)或類似的模型可以改善未登錄詞的影響痘拆,這部分內(nèi)容在本系列的后續(xù)內(nèi)容中會(huì)介紹仰禽。處理未登錄詞的標(biāo)識(shí)符萌庆,還需加入句子邊界的標(biāo)識(shí)符赔桌,如<s>
和</s>
,或者<bos>
和<eos>
工闺,將文本數(shù)據(jù)輸入模型時(shí)桥氏,需要給每句文本加上邊界標(biāo)識(shí)符温峭。句子邊界也是很重要的特征,能夠幫助模型識(shí)別語(yǔ)言模式字支。有時(shí)還需要對(duì)句子進(jìn)行填充凤藏,需要設(shè)置填充標(biāo)識(shí)符,如<pad>
堕伪,一般將其索引設(shè)為0揖庄,對(duì)應(yīng)得詞向量全部設(shè)定為0。將特殊標(biāo)識(shí)符加入詞典后欠雌,便可以為每個(gè)分詞分配唯一的索引蹄梢,包括特殊標(biāo)識(shí)符,索引將作為分詞在語(yǔ)言模型中的唯一標(biāo)識(shí)富俄。索引分配部分的代碼如下所示:
def assign_index(vocab, item2id, vocab_size, item_num):
"""Assign each item in vocabulary with an unique index.
:Param vocab : items and their frequency from dataset.
:Param item2id : map items to their index.
:Param vocab_szie: specify the size of target vocabulary.
:Param item_num : count the number of items.
"""
sorted_vocab = sorted(vocab.items(),
key = lambda x: x[1], reverse = True)
if vocab_size > 0:
sorted_vocab = sorted_vocab[:vocab_size]
for item, _ in sorted_vocab:
if item in item2id:
continue
item2id[item] = item_num
item_num += 1
? ? ? ?以上便是從訓(xùn)練集中構(gòu)建詞典的步驟及相關(guān)細(xì)節(jié)禁炒,基本流程就是從訓(xùn)練集中收集分詞,并設(shè)定特殊標(biāo)識(shí)符霍比,然后為分詞分配索引幕袱。
4. 生成批數(shù)據(jù)
? ? ? ?為了利用并行計(jì)算進(jìn)行加速,訓(xùn)練或者測(cè)試語(yǔ)言模型時(shí)悠瞬,數(shù)據(jù)輸入采用批處理(Batch)的方式们豌。因此,在將文本數(shù)據(jù)輸入模型之前浅妆,不僅需要根據(jù)字典將分詞序列轉(zhuǎn)換為對(duì)應(yīng)的索引序列玛痊,還需要根據(jù)指定的批處理(Batch)的大小生成批處理數(shù)據(jù)。每句文本序列的長(zhǎng)度不同狂打,而循環(huán)神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的輸入序列長(zhǎng)度需固定擂煞。目前有兩種處理方式,一種是對(duì)長(zhǎng)度不足的文本序列進(jìn)行填充趴乡,對(duì)過(guò)長(zhǎng)的序列進(jìn)行截?cái)喽允 A硪环N處理方法是將所有的句子序列看做一個(gè)很長(zhǎng)的序列,然后分割為長(zhǎng)度相同的短序列晾捏。
? ? ? ?此處通過(guò)實(shí)例來(lái)說(shuō)明這兩種批數(shù)據(jù)生成方式蒿涎,假設(shè)模型輸入序列的長(zhǎng)度設(shè)定為15,批處理的大小為2惦辛,需要對(duì)下面兩句文本序列進(jìn)行處理:
例:
當(dāng)時(shí) 我 很 傷心 劳秋, 認(rèn)為 這 輩子 算 完了 。
我 的 孩子 天資 還 不錯(cuò) , 但 學(xué)習(xí) 成績(jī) 一般 玻淑, 小動(dòng)作 較多 嗽冒, 老師 不是 特別 喜歡 , 也 不是 特別 反感 补履。
根據(jù)第一種策略的處理方式添坊,結(jié)果如下:
<s> 當(dāng)時(shí) 我 很 傷心 , 認(rèn)為 這 輩子 算 完了 箫锤。 </s> <pad> <pad>
<s> 我 的 孩子 天資 還 不錯(cuò) 贬蛙, 但 學(xué)習(xí) 成績(jī) 一般 , 小動(dòng)作 較多 谚攒,
采用第二種處理策略時(shí)阳准,上例中句子序列的處理結(jié)果為:
<s> 當(dāng)時(shí) 我 很 傷心 , 認(rèn)為 這 輩子 算 完了 馏臭。 </s> <s> 我
的 孩子 天資 還 不錯(cuò) 溺职, 但 學(xué)習(xí) 成績(jī) 一般 , 小動(dòng)作 較多 位喂, 老師
采用第二種方法時(shí)浪耘,需要注意在生成批數(shù)據(jù)時(shí),相鄰批數(shù)據(jù)中相同位置的序列應(yīng)該是連續(xù)的塑崖,即第批數(shù)據(jù)的第條序列應(yīng)該與第批數(shù)據(jù)的第條數(shù)據(jù)是連續(xù)的文本序列七冲。
? ? ? ?本文采用第二種策略生成批數(shù)據(jù),處理完句子序列的長(zhǎng)度后规婆,根據(jù)詞典將分詞轉(zhuǎn)換為對(duì)應(yīng)的索引澜躺,最終輸入模型的就是索引序列。除了模型的輸入序列抒蚜,還需要模型輸出的目標(biāo)序列掘鄙,目標(biāo)序列于輸入序列類似。目標(biāo)分詞為輸入分詞的下一個(gè)詞嗡髓,因此目標(biāo)序列即為輸入序列向前移一位操漠。具體實(shí)現(xiàn)代碼如下:
def get_batches(batch_size, seq_length, data_type):
"""Get batches from specified dataset for model.
:Param batch_size: size of each data batch.
:Param seq_length: length of each sequence in batch.
:Param data_type : target dataset, training, validation or test.
"""
index_vector = []
file_name = ('%s.txt' % data_type)
# get the target data file
data_file = os.path.join(self.data_path, file_name)
input_file = codecs.open(data_file, 'r', 'utf-8')
# get the indexes of special mark
bos_index = self.token2id.get(self.bos_mark)
eos_index = self.token2id.get(self.eos_mark)
oov_index = self.token2id.get(self.oov_word)
# convert token sequence into index one
for line in input_file:
index_vector.append(bos_index)
index_vector.extend([self.token2id.get(token, oov_index)
for token in line.strip().split()])
index_vector.append(eos_index)
index_vector = np.asarray(index_vector, dtype = np.int32)
batch_num = int(len(index_vector) / (batch_size * seq_length))
end_index = batch_num * batch_size * seq_length
input_vector = index_vector[:end_index]
output_vector = np.copy(input_vector)
output_vector[:-1] = input_vector[1:]
output_vector[-1] = input_vector[0]
input_batch = np.split(input_vector.reshape(
batch_size, -1), batch_num, 1)
output_batch = np.split(output_vector.reshape(
batch_size, -1), batch_num, 1)
for index in range(batch_num):
yield input_batch[index], output_batch[index]
input_file.close()
5. 語(yǔ)言模型
? ? ? ?神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)采用長(zhǎng)短期記憶循環(huán)神經(jīng)網(wǎng)絡(luò),模型主體部分利用Tensorflow框架實(shí)現(xiàn)饿这。首先是創(chuàng)建占位變量浊伙,包括輸入分詞序列的索引和目標(biāo)分詞序列的索引,即:
# place a holder for input vectors
input_holder = tf.placeholder(shape = [batch_size,
seq_length], name = 'input_holder', dtype = tf.int32)
# place a holder for target token index
target_hoder = tf.placeholder(shape = [batch_size,
seq_length], name = 'target_holder', dtype = tf.int32)
? ? ? ?創(chuàng)建詞向量矩陣长捧,詞向量的數(shù)量等于詞典中分詞的個(gè)數(shù)嚣鄙。建立詞向量的查詢表,每個(gè)詞通過(guò)其在字典中的索引串结,查找詞向量矩陣中對(duì)應(yīng)的行哑子,便得到該詞的向量舅列。
# create embedding lookup table for tokens
embeddings = tf.get_variable(shape = [vocab_size,
embedding_dim], name = 'embeddings', dtype = tf.float32)
input_tensor = tf.nn.embedding_lookup(embeddings, input_holder)
? ? ? ? 神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的主體部分就是神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu),本文采用的是長(zhǎng)短期記憶循環(huán)神經(jīng)網(wǎng)絡(luò)卧蜓,利用TensorFlow框架的實(shí)現(xiàn)代碼如下帐要。這部分代碼中,在實(shí)現(xiàn)長(zhǎng)短期記憶神經(jīng)網(wǎng)絡(luò)的同時(shí)烦却,還加入了Dropout機(jī)制宠叼。作為一項(xiàng)有效且簡(jiǎn)單的泛化技術(shù)先巴,Dropout幾乎成了神經(jīng)網(wǎng)絡(luò)的標(biāo)準(zhǔn)設(shè)置其爵,在涉及神經(jīng)網(wǎng)絡(luò)的應(yīng)用中經(jīng)常被采用。
def _lstm_layers(input_tensor, unit_num, layer_num, keep_prob = 0.5,
is_train = False, is_reuse = False):
"""Long-short term memory (LSTM) recurrent neural network layer.
:Param input_tensor: batch of input data, [batch_size, seq_len, embedding_dim].
:Param unit_num : the size of hidden layer.
:Param layer_num : number of hidden layers.
:Param keep_prob : keep probabilty for dropout, default is 0.5.
:Param is_train : if create graph for training, default is False.
:Param is_reuse : if reuse this graph, default is False.
"""
with tf.variable_scope('LSTM', reuse = is_reuse) as scope:
lstm_cells = []
batch_size = input_tensor.shape[0]
# create lstm cells for lstm hidden layers
for i in range(layer_num):
lstm_cell = tf.nn.rnn_cell.LSTMCell(unit_num, forget_bias = 1.0)
# apply dropout to hidden layers except the last one if training
if is_train and (keep_prob < 1) and (i < layer_num - 1):
wrapper_cell = tf.nn.rnn_cell.DropoutWrapper(lstm_cell,
output_keep_prob = keep_prob)
lstm_cells.append(wrapper_cell)
else:
lstm_cells.append(lstm_cell)
# multiple lstm hidden layers
multi_cells = tf.nn.rnn_cell.MultiRNNCell(lstm_cells, state_is_tuple = True)
# inital state for hidden layer
init_state = multi_cells.zero_state(batch_size, dtype = tf.float32)
# final output and state of hidden layer
output, final_state = tf.nn.dynamic_rnn(inputs = input_tensor,
cell = multi_cells, initial_state = init_state, dtype = tf.float32)
return init_state, final_state, output
? ? ? ?神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的輸出層為全連接結(jié)構(gòu)的網(wǎng)絡(luò)層伸蚯,輸出的節(jié)點(diǎn)數(shù)等于詞典中分詞的數(shù)量摩渺,每個(gè)節(jié)點(diǎn)的輸出為對(duì)應(yīng)分詞的條件概率,同樣通過(guò)分詞的索引進(jìn)行對(duì)應(yīng)剂邮。
# weight for output layer of language model
weight = tf.get_variable(shape = [unit_num, vocab_size],
name = 'weight', dtype = tf.float32)
# bias terms for output layer of language model
bias = tf.get_variable(shape = [vocab_size], name = 'bias', dtype = tf.float32)
# reshape output of hidden layers to [batch_size * seq_len, hidden_size]
reshape_state = tf.reshape(lstm_output, [-1, unit_num])
# the unnormalized probability
logits = tf.matmul(reshape_state, weight) + bias
? ? ? ?模型輸出層直接輸出的為非歸一化的條件概率摇幻,需要采用Softmax函數(shù)對(duì)輸出的條件概率進(jìn)行歸一化處理,得到最終的條件概率挥萌。文本序列概率評(píng)估時(shí)绰姻,通過(guò)索引選取對(duì)應(yīng)的條件概率為目標(biāo)分詞在當(dāng)前輸入下的條件概率。如果進(jìn)行文本生成引瀑,則選取條件概率最大的分詞為最終生成的分詞狂芋。
prob = tf.nn.softmax(tf.reshape(logits)
predict_result = tf.argmax(prob, axis = -1)
? ? ? ?模型中除了詞向量,還有許多權(quán)重矩陣以及偏置向量憨栽,這些都需要設(shè)定初始值帜矾,而矩陣后者向量的初始化方法有多種,可采用均勻分布或者正態(tài)分布屑柔。 其中屡萤,得到應(yīng)用廣泛的是Xavier初始化方法,采用均勻分布掸宛,其具體形式如下:
其中死陆,和分別為神經(jīng)網(wǎng)絡(luò)第和層的節(jié)點(diǎn)數(shù),可以理解為權(quán)重矩陣或者向量的輸入尺寸和輸出尺寸唧瘾。初始化策略也被認(rèn)為是重要的泛化技術(shù)翔曲,因?yàn)槌跏蓟瘏?shù)決定了模型所處的空間位置。神經(jīng)網(wǎng)絡(luò)的優(yōu)化最終得到的是局部最優(yōu)點(diǎn)劈愚,模型初始化的起點(diǎn)決定了最終收斂的局部最優(yōu)點(diǎn)的位置瞳遍。
6. 模型訓(xùn)練
? ? ? ?語(yǔ)言模型的訓(xùn)練目標(biāo)是最大化似然函數(shù),損失函數(shù)則采用交叉熵菌羽。在TensorFlow中實(shí)現(xiàn)的代碼如下:
# calculate the softmax loss of model
loss = tf.contrib.legacy_seq2seq.sequence_loss_by_example(logits = [logits, ],
targets = [tf.reshape(target_hoder, [-1])], weights = [tf.ones([batch_size * seq_length])])
cost = tf.reduce_sum(loss) / batch_size
? ? ? ?語(yǔ)言模型訓(xùn)練所采用的優(yōu)化算法是隨機(jī)梯度下降算法(Stochastic Gradient Descent, SGD)掠械,也神經(jīng)網(wǎng)絡(luò)模型訓(xùn)練的常用優(yōu)化算法。為了提升神經(jīng)網(wǎng)絡(luò)的優(yōu)化算法性能,很多研究者對(duì)隨機(jī)梯度下降算法進(jìn)行改進(jìn)猾蒂,衍生出多種優(yōu)化算法均唉,如Adagrad、RMSprop肚菠、Adadelta舔箭、Adam等,對(duì)于不同優(yōu)化算法的對(duì)比可以參考論文S. Ruder (2017)蚊逢。本文仍采用普通的隨機(jī)梯度下降算法层扶,如需要改用其他優(yōu)化算法,可參考TensorFlow中提供的對(duì)應(yīng)接口烙荷,對(duì)優(yōu)化器進(jìn)行修改镜会。
def train_op(batch_cost, grad_cutoff):
"""Training operations for training the whole model.
:Param batchcost : value of cost function on current training batch.
:Param grad_cutoff: vaule for gradients cutoff.
"""
with tf.variable_scope('Train-OP') as scope:
learn_rate = tf.Variable(0.0, trainable = False)
# optimize the model using SGD method
optimizer = tf.train.GradientDescentOptimizer(learn_rate)
train_vars = tf.trainable_variables()
grads, _ = tf.clip_by_global_norm(tf.gradients(batch_cost, train_vars), grad_cutoff)
global_step = tf.Variable(0, name = 'global_step')
train_op = optimizer.apply_gradients(grads_and_vars = zip(grads, train_vars),
global_step = global_step, name = 'apply_gradients')
return learn_rate, train_op
? ? ? ?語(yǔ)言模型的訓(xùn)練和通常機(jī)器學(xué)習(xí)模型的訓(xùn)練類似,在訓(xùn)練數(shù)據(jù)上進(jìn)行多次迭代訓(xùn)練终抽,調(diào)整參數(shù)使得模型最終收斂戳表。語(yǔ)言模型訓(xùn)練過(guò)程中,需要在驗(yàn)證集上進(jìn)行測(cè)試昼伴,以評(píng)估模型的訓(xùn)練效果匾旭,對(duì)學(xué)習(xí)率進(jìn)行調(diào)整,同時(shí)為了防止過(guò)擬合圃郊,需要引入提前終止訓(xùn)練(Early Stop)的策略价涝。提前終止訓(xùn)練是涉及神經(jīng)網(wǎng)絡(luò)建模任務(wù)中常用的泛化技巧之一,神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的訓(xùn)練也不例外描沟。提前終止訓(xùn)練的策略多種多樣飒泻,在不同的任務(wù)中,也會(huì)有所區(qū)別吏廉。神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型訓(xùn)練中泞遗,提前終止訓(xùn)練的策略可分為如下兩種:
- 隨著訓(xùn)練的進(jìn)行,不斷減小學(xué)習(xí)率席覆。減小學(xué)習(xí)率可以從訓(xùn)練開始就進(jìn)行史辙,也可以在進(jìn)行一定迭代次數(shù)之后進(jìn)行。學(xué)習(xí)率的減少方式一般采用指數(shù)衰減佩伤,如基數(shù)取0.97聊倔,指數(shù)隨訓(xùn)練步數(shù)線性或呈指數(shù)變化,當(dāng)學(xué)習(xí)率減少至一定數(shù)值時(shí)生巡,停止訓(xùn)練耙蔑;
- 另一種調(diào)整學(xué)習(xí)率的方法依賴于驗(yàn)證集,如果當(dāng)前模型在驗(yàn)證集上的表現(xiàn)比上一次迭代步差孤荣,則將模型參數(shù)恢復(fù)為上一次迭代步的狀態(tài)甸陌,同時(shí)將學(xué)習(xí)率減半须揣,重新訓(xùn)練。如果學(xué)習(xí)率減半的次數(shù)超過(guò)特定值钱豁,則停止訓(xùn)練耻卡,一般學(xué)習(xí)率減半次數(shù)設(shè)為一次即可。
7. 模型預(yù)測(cè)
? ? ? ?語(yǔ)言模型的預(yù)測(cè)牲尺,即利用訓(xùn)練好的語(yǔ)言模型生成文本或者評(píng)估已有文本的概率卵酪。對(duì)于生成文本,語(yǔ)言模型的第一個(gè)輸入為句子的起始標(biāo)識(shí)符<s>
谤碳,而后的每次輸入是上一步產(chǎn)生的分詞溃卡,從而連續(xù)不斷地生成分詞序列。當(dāng)遇到句子的結(jié)束符</s>
時(shí)塑煎,便生成了完整的文本沫换。文本概率的評(píng)估臭蚁,就是通過(guò)語(yǔ)言模型計(jì)算給定上文時(shí),產(chǎn)生當(dāng)前詞的條件概率讯赏,從而得到整個(gè)文本序列的概率垮兑。
? ? ? ?本文建立神經(jīng)網(wǎng)絡(luò)語(yǔ)言模型的完整代碼已發(fā)布在Github,感興趣的讀者可前往下載漱挎。
作者:施孫甲由 (原創(chuàng))