神經(jīng)網(wǎng)絡(luò)語(yǔ)言建模系列之二:細(xì)枝末節(jié)


熟悉神經(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ù)的塑崖,即第t批數(shù)據(jù)的第i條序列應(yīng)該與第t-1批數(shù)據(jù)的第i條數(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初始化方法,采用均勻分布掸宛,其具體形式如下:

W {\sim} U[-\frac{\sqrt{6}}{\sqrt{n_i + n_{i+1}}},\frac{\sqrt{6}}{\sqrt{n_i + n_{i+1}}}]

其中死陆,n_in_{i+1}分別為神經(jīng)網(wǎng)絡(luò)第ii+1層的節(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)化算法均唉,如AdagradRMSprop肚菠、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))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末系枪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子磕谅,更是在濱河造成了極大的恐慌私爷,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膊夹,死亡現(xiàn)場(chǎng)離奇詭異衬浑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)放刨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門工秩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人进统,你說(shuō)我怎么就攤上這事助币。” “怎么了螟碎?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵眉菱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我掉分,道長(zhǎng)俭缓,這世上最難降的妖魔是什么迈螟? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮尔崔,結(jié)果婚禮上答毫,老公的妹妹穿的比我還像新娘。我一直安慰自己季春,他們只是感情好洗搂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著载弄,像睡著了一般耘拇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宇攻,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天惫叛,我揣著相機(jī)與錄音,去河邊找鬼逞刷。 笑死嘉涌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夸浅。 我是一名探鬼主播仑最,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帆喇!你這毒婦竟也來(lái)了警医?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤坯钦,失蹤者是張志新(化名)和其女友劉穎预皇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婉刀,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吟温,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了路星。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溯街。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖洋丐,靈堂內(nèi)的尸體忽然破棺而出呈昔,到底是詐尸還是另有隱情,我是刑警寧澤友绝,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布堤尾,位于F島的核電站,受9級(jí)特大地震影響迁客,放射性物質(zhì)發(fā)生泄漏郭宝。R本人自食惡果不足惜辞槐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粘室。 院中可真熱鬧榄檬,春花似錦、人聲如沸衔统。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锦爵。三九已至舱殿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間险掀,已是汗流浹背沪袭。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留樟氢,地道東北人冈绊。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嗡害,于是被迫代替她去往敵國(guó)和親焚碌。 傳聞我的和親對(duì)象是個(gè)殘疾皇子畦攘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容