前文
中文分詞映企、詞性標注、命名實體識別是自然語言理解中怎抛,基礎性的工作卑吭,同時也是非常重要的工作芽淡。在很多NLP的項目中马绝,工作開始之前都要經(jīng)過這三者中的一到多項工作的處理。在深度學習中挣菲,有一種模型可以同時勝任這三種工作富稻,而且效果還很不錯--那就是biLSTM_CRF。
下面啸驯,進入正題,biLSTM_CRF模型在tensorflow中的實現(xiàn)祟峦。
運行環(huán)境
python 3.6
tensorflow 1.2
本文GITHUB 歡迎Star和Fork罚斗。
使用同樣方法,構造的中文分詞宅楞。中文分詞GITHUB
正文
1.數(shù)據(jù)預處理
2.模型構建
3.模型訓練與測試
4.模型驗證
5.總結
1.數(shù)據(jù)預處理
首先是將預測數(shù)據(jù)進行處理针姿,轉(zhuǎn)成模型能夠識別的數(shù)字。數(shù)據(jù)是以列形式存儲厌衙,截圖翻轉(zhuǎn)了一下距淫。
# 將tag轉(zhuǎn)換成數(shù)字
tag2label = {"O": 0, "B-PER": 1, "I-PER": 2, "B-LOC": 3, "I-LOC": 4, "B-ORG": 5, "I-ORG": 6}
依據(jù)字典與標簽字典,將文字與標簽分別轉(zhuǎn)成數(shù)字瓷们。第一行是文本业栅,第二行是標簽秒咐。下一步是生成batch的操作。
生成batch后碘裕,需要對batch內(nèi)句子padding到統(tǒng)一的長度反镇,并計算每句的真實長度堰汉。
2.模型構建
采用雙向LSTM對序列進行處理买乃,將輸出結果進行拼接。輸入shape[batch,seq_Length,hidden_dim]种吸,輸出shape[batch,seq_length,2*hidden_dim]你弦。
with tf.name_scope('biLSTM'):
cell_fw = tf.nn.rnn_cell.LSTMCell(pm.hidden_dim)
cell_bw = tf.nn.rnn_cell.LSTMCell(pm.hidden_dim)
outputs, outstates = tf.nn.bidirectional_dynamic_rnn(cell_fw=cell_fw, cell_bw=cell_bw,inputs=self.embedding,
sequence_length=self.seq_length, dtype=tf.float32)
outputs = tf.concat(outputs, 2)#將雙向RNN的結果進行拼接
#outputs三維張量惊豺,[batchsize,seq_length,2*hidden_dim]
我們從本文的第一幅圖中,可以看出禽作,整個biLSTM完整的輸出格式是[batch,seq_length,num_tag]尸昧。num_tag是標簽的數(shù)量,本實驗中是標簽數(shù)量是7旷偿。所以我們需要一個全連接層烹俗,將輸出格式處理一下。
with tf.name_scope('output'):
s = tf.shape(outputs)
output = tf.reshape(outputs, [-1, 2*pm.hidden_dim])
output = tf.layers.dense(output, pm.num_tags)
output = tf.contrib.layers.dropout(output, pm.keep_pro)
self.logits = tf.reshape(output, [-1, s[1], pm.num_tags])
self.logits就是需要輸入CRF層中的數(shù)據(jù)萍程。代碼的第三行幢妄,對output的變形,表示將[batch,seq_length,2hidden_dim]變成[batchseq_length,2*hidden_dim]茫负,最后處理時再變形為[batch,seq_length,num_tag]蕉鸳。
下面就是CRF層的處理:
with tf.name_scope('crf'):
log_likelihood, self.transition_params = crf_log_likelihood(inputs=self.logits, tag_indices=self.input_y, sequence_lengths=self.seq_length)
# log_likelihood是對數(shù)似然函數(shù),transition_params是轉(zhuǎn)移概率矩陣
#crf_log_likelihood{inputs:[batch_size,max_seq_length,num_tags],
#tag_indices:[batchsize,max_seq_length],
#sequence_lengths:[real_seq_length]
#transition_params: A [num_tags, num_tags] transition matrix
#log_likelihood: A scalar containing the log-likelihood of the given sequence of tag indices.
這一步忍法,是調(diào)用from tensorflow.contrib.crf import crf_log_likelihood函數(shù)潮尝,求最大似然函數(shù),以及求轉(zhuǎn)移矩陣饿序。最大似然函數(shù)前加上"-"勉失,可以用梯度下降法求最小值;
with tf.name_scope('loss'):
self.loss = tf.reduce_mean(-log_likelihood) #最大似然取負原探,使用梯度下降
轉(zhuǎn)移矩陣可以幫助維特比算法來求解最優(yōu)標注序列乱凿。
def predict(self, sess, seqs):
seq_pad, seq_length = process_seq(seqs)
logits, transition_params = sess.run([self.logits, self.transition_params], feed_dict={self.input_x: seq_pad,
self.seq_length: seq_length,
self.keep_pro: 1.0})
label_ = []
for logit, length in zip(logits, seq_length):
#logit 每個子句的輸出值,length子句的真實長度踢匣,logit[:length]的真實輸出值
# 調(diào)用維特比算法求最優(yōu)標注序列
viterbi_seq, _ = viterbi_decode(logit[:length], transition_params)
label_.append(viterbi_seq)
return label_
3.模型訓練與測試
訓練時告匠,共進行12次迭代,每迭代4次离唬,將訓練得到的結果,保存到checkpoints划鸽;loss的情況输莺,保留到tensorboard中戚哎;每100個batch,輸出此時的訓練結果與測試結果。模型的loss由最初在訓練集54.93降到2.29嫂用,在測試集上由47.45降到1.73型凳。我們看下,保存的模型在驗證集上的效果嘱函。
4.模型驗證
我從1998年的人民網(wǎng)的新聞素材中甘畅,隨機抽取了幾條語句。ORG表示組織名詞往弓,LOC表示地理名詞,PER表示人名疏唾。從驗證結果上看,模型在命名實體識別上函似,效果還可以槐脏。
5.總結
模型訓練數(shù)據(jù)從github上獲取的,在訓練時使用未訓練的字向量撇寞,不知道使用預訓練字向量是否可以提高準確率顿天。受限于電腦的性能,訓練數(shù)據(jù)僅使用50000條蔑担;提高機器性能牌废,加大訓練數(shù)據(jù)的量,準確性應該可以進一步提高啤握。寫這篇博客畔规,主要是介紹實驗過程,希望能夠給需要的人帶來幫助恨统。