TF使用例子-LSTM實現(xiàn)序列標注

本文主要改寫了一下"Sequence Tagging with Tensorflow"程序痴荐。原文是基于英文的命名實體識別(named entity recognition)問題暑认,由于博主找不到相應的中文數(shù)據(jù)集(其實是沒備份數(shù)據(jù)丟了,如果有同學提供粗合,萬分感謝)。因此,本文用了msra的分詞數(shù)據(jù)。另外暑脆,由于用到了詞向量,所以用了搜狗實驗室發(fā)布的2008新聞數(shù)據(jù)狐肢,提前訓練了300維度的字向量(用的gensim包訓練word2vector添吗,另外后續(xù)可以嘗試Glove)。


Part I 序列標注

序列標注就是給定一串序列份名,對序列中的每個元素做一個標記碟联。比如我們希望識別一句話里面的人名,地名僵腺,組織機構名(命名實體識別)鲤孵。有如下的句子:

琪斯美是日本的“東方project”系列彈幕游戲及其衍生作品的登場角色之一。

為每個字做標注之后的結果就是:

琪(B-PER)斯(I-PER)美(E-PER)是(O)日(B-LOC)本(E-LOC)的(O)“(O)東(B-ORG)方(I-ORG)project(E-ORG)”(O)系(O)列(O)彈(O)幕(O)游(O)戲(O)及(O)其(O)衍(O)生(O)作(O)品(O)的(O)登(O)場(O)角(O)色(O)之(O)一(O)辰如。(O)*

這里標注采用的是BIEO普监,即Begin, Intermediate, End, Other(?我也不知道O是什么)

琪(B-PER)斯(I-PER)美(E-PER) 表示的含義就是 “琪”是人名開始,“斯”是人名中間的字,“美”是人名的末尾的字凯正。其它符號同理毙玻。

這里可以看到,實際上就是用一串符號來標注出你感興趣的部分漆际。那么對于分詞問題也是同理:

琪斯美是日本的“東方project”系列彈幕游戲及其衍生作品的登場角色之一淆珊。
琪斯美 是 日本 的 “ 東方project ” 系列 彈幕 游戲 及 其 衍生 作品 的 登場 角色 之一夺饲。
琪(B)斯(I)美 (E)是(S) 日(B)本(E) 的(S) “(S) 東(B)方(I)project(E) ”(S) 系(B)列(E) 彈(B)幕(E) 游(B)戲(E) 及(S) 其(S) 衍(B)生(E) 作(B)品(E) 的(S) 登(B)場(E) 角(B)色(E) 之(B)一(E)奸汇。(S)

當然,你可能想把“彈幕游戲”作為一個詞往声,這取決于你如何標注這個數(shù)據(jù)擂找,但是標注的時候要統(tǒng)一和規(guī)范。比如網(wǎng)上有PKU的數(shù)據(jù)標注規(guī)范(http://sighan.cs.uchicago.edu/bakeoff2005/data/pku_spec.pdf)浩销。
其它比如像詞性的標注都屬于同一類問題贯涎。


Part II 常用方法

常用方法有MEMM (Maximum Entropy Markov Model)【1】,CRF (Conditional Random Field)【2】與 LSTM+CRF【3】慢洋。
【1】【2】原理待補充塘雳。
【3】類型的模型大致如圖:


這是一個雙向的LSTM,這里的英文單詞可以類比成中文的字普筹,在輸出結果的時候再用crf對輸出結果進行調整(排除不太可能的標注順序)败明。
本文簡單的用tensorflow實現(xiàn)了雙向LSTM+CRF在中文文本分詞上標注問題結果。


Part III tensorflow實現(xiàn)簡單的序列標注

預處理

首先太防,我們需要為每個字建立一個id妻顶,另外可以設置一個閾值把出現(xiàn)次數(shù)小于該閾值的字用UNK(unknown)來統(tǒng)一表示。另外數(shù)字可以同義用NUM來代替蜒车。
然后我們把訓練集按照6:3:1的比例分成訓練集讳嘱,驗證集,測試集酿愧。并把格式整理成一列句子沥潭,一列標注。這里用的是BIEs標注方案嬉挡。

李 B
元 E
與 s

卞 B
德 I
培 E
初 s
識 s
于 s
1 B
9 I
4 I
7 I
年 E
钝鸽。 s

建模

這部分主要是翻譯了原文。
由于tensorflow是batch處理數(shù)據(jù)樣本的棘伴,所以我們需要對句子做padding寞埠,讓它們一樣長,所以我們需要先對其定義2個placeholders焊夸,一個表示句子仁连,一個表示每個句子除去padding的實際長度:

#shape = (batch size, max length of sentence in batch)
word_ids = tf.placeholder(tf.int32, shape=[None, None])
#shape = (batch size)`
sequence_lengths = tf.placeholder(tf.int32, shape=[None])

假設embeddings是我們預先訓練好的詞向量,那么我么可以這樣load詞向量。

L = tf.Variable(embeddings, dtype=tf.float32, trainable=False)
# shape = (batch, sentence, word_vector_size)
pretrained_embeddings = tf.nn.embedding_lookup(L, word_ids)

這里trainable設置成False而不是tf.constant饭冬,否則會有內存問題使鹅。(另外如果不需要訓練embedding層的話也沒必要設置成True)

原文把每個英文單詞作為一個詞,并考慮了這個詞當中的字母的特征昌抠,而我們這里直接只考慮每個字患朱,所以省略了字母特征這一塊。

一旦我們有了詞的表示之后炊苫,我們只用跑一個LSTM或者bi-LSTM裁厅,得到另一串向量(LSTM的隱藏層,或者bi-LSTM的前向后向的隱藏層的組合)侨艾。

對于序列標注問題执虹,前后字對于當前字的標注結果都會有影響,所以用雙向的LSTM是很有意義的唠梨。這次我們用每個time step的隱藏層狀態(tài)袋励,代碼如下:

word_embeddings = pretrained_embeddings
lstm_cell = tf.contrib.rnn.LSTMCell(hidden_size)

(output_fw, output_bw), _ = tf.nn.bidirectional_dynamic_rnn(lstm_cell, 
    lstm_cell, word_embeddings, sequence_length=sequence_lengths, 
    dtype=tf.float32)

context_rep = tf.concat([output_fw, output_bw], axis=-1)

解碼

這一步,我們可以用兩種方式來為每個tag打分:
方法一: 用softmax当叭,然后argmax選擇score值最大的那個tag,這種方法是基于字級別的茬故。
方法二: 用條件隨機場(Conditional Random Field, CRF)在句子層面做預測。
兩種方法的目的都是為了讓最后的序列標注結果的概率最大蚁鳖。先來計算scores:

W = tf.get_variable("W", shape=[2*self.config.hidden_size, self.config.ntags], 
                dtype=tf.float32)

b = tf.get_variable("b", shape=[self.config.ntags], dtype=tf.float32, 
                initializer=tf.zeros_initializer())

ntime_steps = tf.shape(context_rep)[1]
context_rep_flat = tf.reshape(context_rep, [-1, 2*hidden_size])
pred = tf.matmul(context_rep_flat, W) + b
scores = tf.reshape(pred, [-1, ntime_steps, ntags])

對于softmax磺芭, 實際上是使得每個字屬于某個tag的概率最大,最后一串序列的結果就是序列中每個字的標注概率相乘得到的才睹。這種結果都是局部的徘跪,也就是說某個字在標注的時候并沒有考慮前后面字的標注結果的影響。
對于linear-chain CRF: 定義了一個全局的score琅攘,考慮了標注結果之間的轉移垮庐。如下圖:



如果我們不考慮轉移情況,都選取局部最大的值坞琴,我們就會標注為PER-PER-LOC了哨查。

訓練

用CRF得到loss, 另外tf.contrib.crf.crf_log_likelihood還會返回轉移矩陣T剧辐,方便我們在做預測的時候用它:

# shape = (batch, sentence)
labels = tf.placeholder(tf.int32, shape=[None, None], name="labels")

log_likelihood, transition_params = tf.contrib.crf.crf_log_likelihood(
scores, labels, sequence_lengths)

loss = tf.reduce_mean(-log_likelihood)

用local softmax的到的loss, 這里用mask過濾掉pad上去的token帶來的loss:

losses = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=scores, labels=labels)
# shape = (batch, sentence, nclasses)
mask = tf.sequence_mask(sequence_lengths)
# apply mask
losses = tf.boolean_mask(losses, mask)

loss = tf.reduce_mean(losses)

最后定義我們的train_op:

optimizer = tf.train.AdamOptimizer(self.lr)
train_op = optimizer.minimize(self.loss)

預測

對于local softmax直接選擇每個time step最高的值就可以:

labels_pred = tf.cast(tf.argmax(self.logits, axis=-1), tf.int32)

對于CRF寒亥,傳遞一下訓練時候得到的轉移矩陣T,用viterbi的方法搜索到最優(yōu)解即可:

# shape = (sentence, nclasses)
score = ...
viterbi_sequence, viterbi_score = tf.contrib.crf.viterbi_decode(
                                score, transition_params)

結果

樓主按照上述方法對msra的分詞數(shù)據(jù)跑了60個epoch后在(驗證集和測試集)上的準確率是96%左右荧关,f1大概也在95%的樣子溉奕。以下是分出來的結果:

琪斯美是日本的“東方project”系列彈幕游戲及其衍生作品的登場角色之一。
['B', 'I', 'E', 's', 'B', 'E', 's', 's', 'B', 'E', 'B', 'I', 'I', 'I', 'I', 'I', 'E', 's', 'B', 'E', 'B', 'E', 'B', 'E', 'B', 'E', 'B', 'E', 'B', 'E', 's', 'B', 'E', 'B', 'E', 'B', 'E', 's']

附分詞實例代碼

戳這里 數(shù)據(jù)見README

附keras實現(xiàn)的簡易版本代碼

keras官方版本目前還木有實現(xiàn)crf層忍啤,但是網(wǎng)上有同學自己實現(xiàn)了加勤,戳這里
例子:

n_words = 10000
maxlen = 32
(X_train, y_train), (X_test, y_test) = load_treebank(nb_words=n_words, maxlen=maxlen)

n_samples, n_steps, n_classes = y_train.shape

model = Sequential()
model.add(Embedding(n_words, 128, input_length=maxlen, dropout=0.2))
model.addBidirectional(LSTM(64, dropout_W=0.2, dropout_U=0.2, return_sequences=True),merge_mode='concat'))
model.add(Dropout(0.2))
model.add(TimeDistributed(Dense(n_classes)))
model.add(Dropout(0.2))
crf = ChainCRF()
model.add(crf)
model.compile(loss=crf.loss, optimizer='rmsprop', metrics=['accuracy'])

local softmax的代碼如下:

model = Sequential()
# keras.layers.embeddings.Embedding(input_dim, output_dim, init='uniform', input_length=None, W_regularizer=None, activity_regularizer=None, W_constraint=None, mask_zero=False, weights=None, dropout=0.0)
model.add(Embedding(max_features, 128, dropout=0.2))
model.add(Bidirectional(LSTM(64, dropout_W=0.2, dropout_U=0.2, return_sequences=True),merge_mode='concat'))  # try using a GRU instead, for fun
model.add(TimeDistributed(Dense(num_class)))
model.add(Activation('softmax'))


# try using different optimizers and different optimizer configs
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=50,
          validation_data=(X_test, y_test))
score, acc = model.evaluate(X_test, y_test,
                            batch_size=batch_size)


相關文獻

【1】McCallum, Andrew, Dayne Freitag, and Fernando CN Pereira. "Maximum Entropy Markov Models for Information Extraction and Segmentation."Icml. Vol. 17. 2000.
【2】Lafferty, John, Andrew McCallum, and Fernando Pereira. "Conditional random fields: Probabilistic models for segmenting and labeling sequence data."Proceedings of the eighteenth international conference on machine learning, ICML. Vol. 1. 2001.
【3】Huang, Zhiheng, Wei Xu, and Kai Yu. "Bidirectional LSTM-CRF models for sequence tagging."arXiv preprint arXiv:1508.01991(2015).

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鳄梅,更是在濱河造成了極大的恐慌叠国,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戴尸,死亡現(xiàn)場離奇詭異粟焊,居然都是意外死亡,警方通過查閱死者的電腦和手機孙蒙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門项棠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人马篮,你說我怎么就攤上這事沾乘。” “怎么了浑测?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長歪玲。 經(jīng)常有香客問我迁央,道長,這世上最難降的妖魔是什么滥崩? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任岖圈,我火速辦了婚禮,結果婚禮上钙皮,老公的妹妹穿的比我還像新娘蜂科。我一直安慰自己,他們只是感情好短条,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布导匣。 她就那樣靜靜地躺著,像睡著了一般茸时。 火紅的嫁衣襯著肌膚如雪贡定。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天可都,我揣著相機與錄音缓待,去河邊找鬼。 笑死渠牲,一個胖子當著我的面吹牛旋炒,可吹牛的內容都是我干的。 我是一名探鬼主播签杈,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼瘫镇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起汇四,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤接奈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后通孽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體序宦,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年背苦,在試婚紗的時候發(fā)現(xiàn)自己被綠了互捌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡行剂,死狀恐怖秕噪,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情厚宰,我是刑警寧澤腌巾,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站铲觉,受9級特大地震影響澈蝙,放射性物質發(fā)生泄漏。R本人自食惡果不足惜撵幽,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一灯荧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盐杂,春花似錦逗载、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至测垛,卻和暖如春捏膨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背食侮。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工号涯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人锯七。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓链快,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眉尸。 傳聞我的和親對象是個殘疾皇子域蜗,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內容