之前在自然語言處理技術(shù)系列的第一篇NER實(shí)戰(zhàn)的結(jié)語中介紹過:序列標(biāo)注(分詞攘滩,NER)祠挫,文本分類(情感分析)汉嗽,句子關(guān)系判斷(語意相似判斷)歼秽,句子生成(機(jī)器翻譯)是NLP領(lǐng)域的四大任務(wù)腌零,之后我又陸續(xù)簡單介紹了情感分析實(shí)戰(zhàn),和Seq2Seq生成對聯(lián)梯找。今天我們來到這個(gè)系列的終章篇——語義相似判斷。語義相似判斷就是判斷兩個(gè)句子是否具有相同的語義益涧,其應(yīng)用場景多用于問答系統(tǒng):
- 判斷兩個(gè)問句是否具有相同的語義锈锤。
- 判斷問題和檢索出的答案是否匹配。
當(dāng)然也可以用于其他場景比如判斷兩幅圖片是否是一樣——人臉識別闲询,所以從廣義上來說久免,就是語義相識判斷就是判斷兩個(gè)東西是否具有某種相似度的任務(wù)。
語義相似判斷任務(wù)簡介
語義相似可以轉(zhuǎn)化為一個(gè)分類問題扭弧。給模型輸入兩個(gè)句子,然后希望模型判斷出兩個(gè)句子語義是否相似阎姥。具體輸入輸出細(xì)節(jié)如下:
輸入:
- 1.為何我無法申請開通花唄信用卡收款
- 2.支付寶開通信用卡花唄收款不符合條件怎么回事
輸出:1
如果輸出0表示不相似,輸出1表示相似鸽捻。
語義相似判斷算法簡介
語義相似還是NLP中的老問題呼巴,如何將句子映射到到向量空間中同時(shí)保持語義泽腮,然后我們就可以通過各種距離去衡量句子的相似程度。
- 上古時(shí)期的方式是通過bag of words衣赶,tf-idf這種詞袋模型去映射句子诊赊。
- 之后出現(xiàn)了word2vector技術(shù),我們就可以將一句話中每個(gè)詞的的詞向量求平均來表示句子向量府瞄,后面又出現(xiàn)了借用word2vector的思路實(shí)現(xiàn)的sen2vector技術(shù)碧磅。
- 最近比較火的當(dāng)然就是使用深度學(xué)習(xí)技術(shù)將句子映射到向量空間。
這里我簡要的介紹一下我在實(shí)戰(zhàn)中使用的深度學(xué)習(xí)模型Siamese Network架構(gòu)遵馆。
Siamese在英語中是“孿生”鲸郊、“連體”的意思,而Siamese Network货邓,如下圖所示就是兩個(gè)共享參數(shù)的神經(jīng)網(wǎng)絡(luò)秆撮,其中神經(jīng)網(wǎng)絡(luò)部分可以任意構(gòu)建,使用CNN逻恐,RNN之類的都可以像吻。通過同一網(wǎng)絡(luò)將兩個(gè)輸入映射到向量空間,然后在去計(jì)算它們的相似性复隆,相似性度量可以通過: - 歐式距離拨匆,
- 余弦距離,
-
或者給數(shù)據(jù)打上標(biāo)簽挽拂,通過神經(jīng)網(wǎng)絡(luò)去判斷相似性等惭每。
本次實(shí)戰(zhàn)就是采用的Siamese-BiLSTM,意思就是用雙向的LSTM做共享參數(shù)的network部分亏栈。
螞蟻金服大賽實(shí)戰(zhàn)
讀入數(shù)據(jù)
數(shù)據(jù)格式如下台腥,每條數(shù)據(jù)都是兩個(gè)句子和一個(gè)標(biāo)簽。
執(zhí)行下方代碼讀入數(shù)據(jù)绒北。
import pandas as pd
train_data = pd.read_csv("./huabei/train.csv",encoding="utf-8",header=None,sep="\t")
val_data = pd.read_csv("./huabei/val.csv",encoding="utf-8",header=None,sep="\t")
train_data[1] = train_data[1].apply(lambda x : [char for char in x])
train_data[2] = train_data[2].apply(lambda x : [char for char in x])
train_data
生成字典
執(zhí)行下方代碼黎侈,為后續(xù)的文本轉(zhuǎn)ID構(gòu)建一個(gè)字典。
from itertools import chain
train_data_1 = list(chain.from_iterable(train_data[1]))
train_data_2 = list(chain.from_iterable(train_data[2]))
all_words = set(train_data_1 + train_data_2)
print(len(all_words))
vocab = { j:i+1 for i, j in enumerate(all_words)}
vocab["unk"] = 0
數(shù)據(jù)預(yù)處理
做一些簡單的文本轉(zhuǎn)ID闷游,然后padding峻汉,之后方便喂給模型。
from keras.preprocessing.sequence import pad_sequences
import numpy as np
train_data[1] = train_data[1].apply(lambda x:[vocab.get(i,0) for i in x])
train_data[2] = train_data[2].apply(lambda x:[vocab.get(i,0) for i in x])
Sens_1 = pad_sequences(train_data[1],maxlen=100)
Sens_2 = pad_sequences(train_data[2],maxlen=100)
labels = np.array(train_data[3])
labels = labels.reshape(*labels.shape,1)
構(gòu)建模型
下面定義了一個(gè)構(gòu)建模型的function脐往。
def SiameseBiLSTM(vocab,max_length):
K.clear_session()
embedding = Embedding(input_dim = len(vocab),output_dim = 200, input_length=max_length)
bilstm = Bidirectional(LSTM(128))
sequence_input1 = Input(shape=(max_length,))
embedded_sequences_1 = embedding(sequence_input1)
x1 = bilstm(embedded_sequences_1)
sequence_input2 = Input(shape=(max_length,))
embedded_sequences_2 = embedding(sequence_input2)
x2 = bilstm(embedded_sequences_2)
merged = concatenate([x1, x2])
merged = BatchNormalization()(merged)
merged = Dropout(0.5)(merged)
merged = Dense(100, activation="relu")(merged)
merged = BatchNormalization()(merged)
merged = Dropout(0.5)(merged)
preds = Dense(1, activation='sigmoid')(merged)
model = Model(inputs=[sequence_input1,sequence_input2], outputs=preds)
model.compile(loss='binary_crossentropy', optimizer='adam')
model.summary()
return model
model = SiameseBiLSTM(vocab,100)
執(zhí)行上方代碼休吠,從Keras的模型架構(gòu)可視化輸出可以清楚的看到embedding_1和bidirectional_1這輛層會被兩個(gè)輸入共享。
其架構(gòu)示意圖如下业簿,兩個(gè)輸入通過BiLSTM編碼成兩個(gè)向量之后瘤礁,直接將他們拼接一下,喂給下游的全連結(jié)層去做相識度判斷梅尤。
訓(xùn)練模型
model.fit([Sens_1,Sens_2],labels,batch_size=32,epochs=5,validation_split=0.2)
構(gòu)建完模型后柜思,將要判斷相似度的句子岩调,和標(biāo)簽喂給模型,定義好赡盘,batch_size,和訓(xùn)練輪數(shù)epoch誊辉,就讓它跑起來,其模型訓(xùn)練過程如下圖亡脑。
模型預(yù)測
model.predict([sen_pre_1,sen_pre_2])
通過上述代碼就可以進(jìn)行預(yù)測了,sen_pre_1和sen_pre_2是經(jīng)過數(shù)據(jù)預(yù)處理邀跃,padding后的100維向量霉咨,模型會輸出一個(gè)(0,1)之間的值拍屑,你可以定義一個(gè)閾值將這個(gè)值轉(zhuǎn)換為[0,1]標(biāo)簽途戒。
結(jié)語
至此,四大任務(wù)的自然語言處理(NLP)技術(shù)實(shí)戰(zhàn)全部完結(jié)僵驰。任務(wù)看起來都很有趣喷斋,keras用起來也簡單易上手,baseline的構(gòu)建也不是很難蒜茴。但是想要做好星爪,還有大量工作要做。
這四個(gè)任務(wù)粉私,還有很多可以優(yōu)化的地方顽腾,比如
- 在輸入部分,我很少預(yù)訓(xùn)練詞向量(除了情感分析實(shí)戰(zhàn))诺核,對于模型的輸入我也是以字為單位抄肖,要知道中文以詞為單位時(shí)其表達(dá)才準(zhǔn)確;
- 在模型上的使用窖杀,我基本上全用的LSTM或者GRU漓摩,沒有加任何attention,也沒使用更復(fù)雜的網(wǎng)絡(luò)入客;(需要扎實(shí)的領(lǐng)域知識和腦洞)
- 模型訓(xùn)練部分管毙,超參數(shù)的調(diào)優(yōu)和模型的evaluation我基本上沒做。(極其耗時(shí)耗力)
四個(gè)任務(wù)的flow都不是特別完美痊项。筆者希望通過這個(gè)系列讓大家感受到深度學(xué)習(xí)做NLP的樂趣锅风。若想成為大神,從好奇鞍泉,喜愛開始皱埠,然后苦讀論文,多思考咖驮,勤做實(shí)驗(yàn)边器,多創(chuàng)新训枢,這個(gè)過程就很枯燥了,堅(jiān)持下去忘巧,才能有所建樹恒界。(后會有期)
參考:
https://github.com/amansrivastava17/lstm-siamese-text-similarity
http://www.reibang.com/p/92d7f6eaacf5