之前介紹過RNN的分類,本文介紹一下使用預訓練詞向量進行RNN+Attention的分類模型娃循。
下面來正式開始,RNN+Attention在tensorflow中的實現(xiàn)玛臂。
運行環(huán)境
python 3.6
tensorflow 1.2
本文GITHUB
正文
1.數(shù)據(jù)預處理
2.模型構(gòu)建
3.模型訓練與測試
4.模型驗證
5.總結(jié)
1 數(shù)據(jù)預處理
本實驗是使用THUCNews的一個子集進行訓練與測試,文本類別涉及10個類別:categories = ['體育', '財經(jīng)', '房產(chǎn)', '家居', '教育', '科技', '時尚', '時政', '游戲', '娛樂']烤蜕。這一步的主要作用是將標簽與文本分開,然后將文本與標簽分別轉(zhuǎn)化為模型所需要的數(shù)據(jù)格式垢揩。例如:文本:[我 愛 體育]玖绿,標簽:體育。轉(zhuǎn)換成:文本[3 4 20]叁巨,標簽[1 0 0 0 0 0 0 0 0 0]斑匪。
然后是padding,按照動態(tài)RNN的做法應該是根據(jù)不同batch里面句子的長度對不同batch進行padding锋勺,但本實驗所用素材來自新聞蚀瘸,每個句子其實就是一篇報道,特別的長庶橱,故分配指定長度贮勃,長度為250,故按照這個長度來padding苏章。最后是生成batch的過程寂嘉。
2 模型構(gòu)建
模型采用的是動態(tài)雙向LSTM,對于輸出結(jié)果,采用拼接的方式枫绅,輸入詞向量的shape是[batch_size,seq_length,embedding_dim]泉孩,輸出為[batch_size,seq_length,2*hidden_dim]。
with tf.name_scope('Cell'):
cell_fw = tf.contrib.rnn.BasicLSTMCell(pm.hidden_dim)
Cell_fw = tf.contrib.rnn.DropoutWrapper(cell_fw, self.keep_pro)
cell_bw = tf.contrib.rnn.BasicLSTMCell(pm.hidden_dim)
Cell_bw = tf.contrib.rnn.DropoutWrapper(cell_bw, self.keep_pro)
with tf.device('/cpu:0'), tf.name_scope('embedding'):
self.embedding = tf.get_variable('embedding', shape=[pm.vocab_size, pm.embedding_dim],
initializer=tf.constant_initializer(pm.pre_trianing))
self.embedding_input = tf.nn.embedding_lookup(self.embedding, self.input_x)
with tf.name_scope('biRNN'):
output, _ = tf.nn.bidirectional_dynamic_rnn(cell_fw=Cell_fw, cell_bw=Cell_bw, inputs=self.embedding_input,
sequence_length=self.seq_length, dtype=tf.float32)
output = tf.concat(output, 2) #[batch_size, seq_length, 2*hidden_dim]
接下來并淋,就是Attention層寓搬。我們設置三個變量,分別是w,b,u县耽,下標分別是小寫的w句喷,公式2是一個softmax過程镣典,公式3是線性加權(quán)求和。在實際模型中唾琼,是需要對參數(shù)進行很多變型的兄春,我們結(jié)合代碼看一下。
with tf.name_scope('attention'):
u_list = []
seq_size = output.shape[1].value
hidden_size = output.shape[2].value #[2*hidden_dim]
attention_w = tf.Variable(tf.truncated_normal([hidden_size, pm.attention_size], stddev=0.1), name='attention_w')
attention_u = tf.Variable(tf.truncated_normal([pm.attention_size, 1], stddev=0.1), name='attention_u')
attention_b = tf.Variable(tf.constant(0.1, shape=[pm.attention_size]), name='attention_b')
for t in range(seq_size):
#u_t:[batch,attention]
u_t = tf.tanh(tf.matmul(output[:, t, :], attention_w) + tf.reshape(attention_b, [1, -1]))
u = tf.matmul(u_t, attention_u)
u_list.append(u)
logit = tf.concat(u_list, axis=1)
#[batch:seq_size]
weights = tf.nn.softmax(logit, name='attention_weights')
#weight:[batch:seq_size]
out_final = tf.reduce_sum(output * tf.reshape(weights, [-1, seq_size, 1]), 1)
#out_final:[batch,hidden_size]
為了給每個詞賦予權(quán)重父叙,那么權(quán)重的shape應該是[batch,seq_length]神郊。
為了得到每個詞的權(quán)重,在程序中趾唱,對句子序列做了一個for循環(huán),一個循環(huán)蜻懦,得到的是同一個batch中不同序列同一步詞的語義甜癞。
例如:batch=64,t=1,也就是64個句子,每個句子第一個詞的語義宛乃。所以每一步取出的output形狀是[batch,hidden_size]悠咱。
那么u_t輸出為[batch,attention];
下一步征炼,u輸出得到的結(jié)果是[batch,1]析既;
u_list是將seq_length個u的結(jié)果存入列表中;
將列表按照第一維進行拼接谆奥,得到結(jié)果logit的形狀[batch,seq_length]--以上是公式一的步驟;
經(jīng)過softmax眼坏,得到weights,shape依舊為[batch,seq_length]--公式二步驟酸些;
將weigths經(jīng)過變形宰译,[bacth,seq_length,1],然后使用點乘“*”魄懂,表示對應數(shù)值相乘沿侈;然后將第一維的值進行相加,也就是seq_length這一維度市栗。這個步驟的意思:對于每一個句子缀拭,該句詞匯與對應權(quán)重相乘,然后得到的和再相加填帽,最后輸出的形狀[batch,hidden_size]蛛淋。
模型的最后,是全連+softmax層盲赊。這一步相對來說比較簡單铣鹏。
with tf.name_scope('dropout'):
self.out_drop = tf.nn.dropout(out_final, keep_prob=self.keep_pro)
with tf.name_scope('output'):
w = tf.Variable(tf.truncated_normal([hidden_size, pm.num_classes], stddev=0.1), name='w')
b = tf.Variable(tf.zeros([pm.num_classes]), name='b')
self.logits = tf.matmul(self.out_drop, w) + b
self.predict = tf.argmax(tf.nn.softmax(self.logits), 1, name='predict')
3 模型訓練與測試
訓練時,每迭代一次哀蘑,將訓練得到的結(jié)果诚卸,保存到checkpoints葵第;
loss,accuracy的情況,保留到tensorboard中合溺;
每100個batch,輸出此時的訓練結(jié)果與測試結(jié)果卒密。
for epoch in range(pm.num_epochs):
print('Epoch:', epoch+1)
num_batchs = int((len(x_train) - 1) / pm.batch_size) + 1
batch_train = batch_iter(x_train, y_train, batch_size=pm.batch_size)
for x_batch, y_batch in batch_train:
seq_len = sequence(x_batch)
feed_dict = model.feed_data(x_batch, y_batch, seq_len, pm.keep_prob)
_, global_step, _summary, train_loss, train_accuracy = session.run([model.optimizer, model.global_step, merged_summary,
model.loss, model.accuracy],feed_dict=feed_dict)
if global_step % 100 == 0:
test_loss, test_accuracy = model.evaluate(session, x_test, y_test)
print('global_step:', global_step, 'train_loss:', train_loss, 'train_accuracy:', train_accuracy,
'test_loss:', test_loss, 'test_accuracy:', test_accuracy)
if global_step % num_batchs == 0:
print('Saving Model...')
saver.save(session, save_path, global_step=global_step)
訓練與測試的情況如圖所示,訓練結(jié)果表明棠赛,模型收斂情況良好哮奇,準確度也較為理想。4 模型驗證
驗證集數(shù)據(jù)量是500*10睛约,使用最后保存的模型對驗證集進行預測鼎俘,并計算準確率。從驗證的情況來看辩涝,準確率達到了96.5%贸伐,從預測的前10項情況來看,模型較為理想怔揩。
5 總結(jié)
相對與之前的兩層LSTM模型對文本進行分類捉邢,本文采用的雙向LSTM+Attention模型。都進行了3次迭代商膊,從結(jié)果上看伏伐,兩層LSTM模型,準確率略高一點晕拆;但本文并沒有用兩層LSTM+Attention模型藐翎,所以對于這兩次的結(jié)果不好做比較,有興趣的可以下載github代碼潦匈,把代碼改成兩層的結(jié)構(gòu)阱高。但從訓練時間上來說,RNN網(wǎng)絡越深茬缩,時間耗費的越久赤惊。
加上之前,共介紹了4種文本分類的深度學習模型凰锡,基本可以滿足一般的情況未舟,這幾種模型不僅可以用來進行文本分類,對于其他分類情況掂为,稍微變動下裕膀,也是適應的,比如情感分類勇哗。
對于深度學習分類的介紹昼扛,到此為止。以后會介紹深度學習、傳統(tǒng)機器學習在中文分詞抄谐,詞性標注等應用的模型渺鹦。
參考
https://blog.csdn.net/thriving_fcl/article/details/73381217
https://zhuanlan.zhihu.com/p/46313756