文章節(jié)選自《自然語言處理技術(shù)入門與實(shí)戰(zhàn)》
在自然語言處理中,另外一個重要的應(yīng)用領(lǐng)域,就是文本的自動撰寫。關(guān)鍵詞、關(guān)鍵短語、自動摘要提取都屬于這個領(lǐng)域中的一種應(yīng)用。不過這些應(yīng)用,都是由多到少的生成。這里我們介紹其另外一種應(yīng)用:由少到多的生成,包括句子的復(fù)寫,由關(guān)鍵詞、主題生成文章或者段落等。
基于關(guān)鍵詞的文本自動生成模型
本章第一節(jié)就介紹基于關(guān)鍵詞生成一段文本的一些處理技術(shù)。其主要是應(yīng)用關(guān)鍵詞提取、同義詞識別等技術(shù)來實(shí)現(xiàn)的。下面就對實(shí)現(xiàn)過程進(jìn)行說明和介紹。
場景
在進(jìn)行搜索引擎廣告投放的時候,我們需要給廣告撰寫一句話描述。一般情況下模型的輸入就是一些關(guān)鍵詞。比如我們要投放的廣告為鮮花廣告,假設(shè)廣告的關(guān)鍵詞為:“鮮花”、“便宜”。對于這個輸入我們希望產(chǎn)生一定數(shù)量的候選一句話廣告描述。
對于這種場景,也可能輸入的是一句話,比如之前人工撰寫了一個例子:“這個周末,小白鮮花只要99元,并且還包郵哦,還包郵哦!”。需要根據(jù)這句話復(fù)寫出一定數(shù)量在表達(dá)上不同,但是意思相近的語句。這里我們就介紹一種基于關(guān)鍵詞的文本(一句話)自動生成模型。
原理
模型處理流程如圖1所示。
圖1
首先根據(jù)輸入的數(shù)據(jù)類型不同,進(jìn)行不同的處理。如果輸入的是關(guān)鍵詞,則在語料庫中選擇和輸入關(guān)鍵詞相同的語句。如果輸入的是一個句子,那么就在語料庫中選擇和輸入語句相似度大于指定閾值的句子。
對于語料庫的中句子的關(guān)鍵詞提取的算法,則使用之前章節(jié)介紹的方法進(jìn)行。對于具體的算法選擇可以根據(jù)自己的語料庫的形式自由選擇。
圖2
語句相似度計(jì)算,這里按照圖2左邊虛線框中的流程進(jìn)行計(jì)算:
首先對待計(jì)算的兩個語句進(jìn)行分詞處理,對于分詞后的語句判斷其是否滿足模板變換,如果滿足則直接將語句放入候選集,并且設(shè)置相似度為0。如果不滿足則進(jìn)入到c)步進(jìn)行計(jì)算。
判斷兩個語句是否滿足模板變換的流程圖,如圖2中右邊虛線框所標(biāo)記的流程所示:(1)首先判斷分詞后,兩個句子的詞是不是完全一樣,而只是位置不同,如果是則滿足模板變換的條件。(2)如果詞不完全相同,就看看對不同的詞之間是否可以進(jìn)行同義詞變換,如果能夠進(jìn)行同義詞變換,并且變換后的語句兩個句子去公共詞的集合,該集合若為某一句話的全部詞集合,則也滿足模板變換條件。(3)如果上述兩個步驟都不滿足,則兩個句子之間不滿足模板變換。
對兩個句子剩余的詞分別兩兩計(jì)算其詞距離。假如兩個句子分別剩余的詞為,句1:“鮮花”、“多少錢”、“包郵”。句2:“鮮花”、“便宜”、“免運(yùn)費(fèi)”。那么其距離矩陣如下表所示:
得到相似矩陣以后,就把兩個句子中相似的詞替換為一個,假設(shè)我們這里用“包郵”替換掉“免運(yùn)費(fèi)”。那么兩個句子的詞向量就變?yōu)椋壕?:<鮮花、多少錢、包郵>,句2:<鮮花、便宜、包郵>。
對于兩個句子分別構(gòu)建bi-gram統(tǒng)計(jì)向量,則有:(1)句1:< begin,鮮花>、<鮮花,多少錢>、<多少錢,包郵>、<包郵,end>。(2)句2:< begin,鮮花>、<鮮花,便宜>、<便宜,包郵>、<包郵,end>。
這兩個句子的相似度由如下公式計(jì)算:
所以上面的例子的相似度為:1.0-2.0*2/8=0.5。
完成候選語句的提取之后,就要根據(jù)候選語句的數(shù)量來判斷后續(xù)操作了。如果篩選的候選語句大于等于要求的數(shù)量,則按照句子相似度由低到高選取指定數(shù)量的句子。否則要進(jìn)行句子的復(fù)寫。這里采用同義詞替換和根據(jù)指定模板進(jìn)行改寫的方案。
實(shí)現(xiàn)
實(shí)現(xiàn)候選語句計(jì)算的代碼如下:
Map
if (type == 0) {//輸入為關(guān)鍵詞
result = getKeyWordsSentence(keyWordsList);
}else {
result = getWordSimSentence(sentence);
}
//得到候選集數(shù)量大于等于要求的數(shù)量則對結(jié)果進(jìn)行裁剪
if (result.size() >= number) {
result = sub(result, number);
}else {
//得到候選集數(shù)量小于要求的數(shù)量則對結(jié)果進(jìn)行添加
result = add(result, number);
}
首先根據(jù)輸入的內(nèi)容形式選擇不同的生成模式進(jìn)行語句生成。這一步的關(guān)鍵在于對語料庫的處理,對于相似關(guān)鍵詞和相似語句的篩選。對于關(guān)鍵詞的篩選,我們采用布隆算法進(jìn)行,當(dāng)然也可以采用索引查找的方式進(jìn)行。對于候選語句,我們首先用關(guān)鍵詞對于語料庫進(jìn)行一個初步的篩選。確定可能性比較大的語句作為后續(xù)計(jì)算的語句。
對于得到的候選結(jié)果,我們都以map的形式保存,其中key為后續(xù)語句,value為其與目標(biāo)的相似度。之后按照相似度從低到高進(jìn)行篩選。至于map的排序我們在之前的章節(jié)已經(jīng)介紹了,這里就不再重復(fù)了。
實(shí)現(xiàn)語句相似篩選計(jì)算的代碼如下。
for (String sen : sentenceList) {
//對待識別語句進(jìn)行分詞處理
List
List
//首先判斷兩個語句是不是滿足目標(biāo)變換
boolean isPatternSim = isPatternSimSentence(wordsList1, wordsList2);
if (!isPatternSim) {//不滿足目標(biāo)變換
//首先計(jì)算兩個語句的bi-gram相似度
double tmp = getBigramSim(wordsList1, wordsList2);
//這里的篩選條件是相似度小于閾值,因?yàn)閎i-gram的相似度越小,代表兩者越相似
if (threshold > tmp) {
result.put(sen,tmp);
}
}else {
result.put(sen,0.0);
}
}
首先對待識別的兩個語句進(jìn)行分詞,并對分詞后的結(jié)果進(jìn)行模板轉(zhuǎn)換的識別,如果滿足模板轉(zhuǎn)換的條件,則將語句作為候選語句,并且賦值一個最小的概率。如果不滿足則計(jì)算兩者的bi-gram的相似度。再根據(jù)閾值進(jìn)行篩選。
這里使用的bi-gram是有改進(jìn)的,而常規(guī)的bi-gram是不需要做比例計(jì)算的。這里進(jìn)行這個計(jì)算是為了避免不同長度的字符的影響。對于相似度的度量也可以根據(jù)自己的實(shí)際情況選擇合適的度量方式進(jìn)行。
拓展
本節(jié)處理的場景是:由文本到文本的生成。這個場景一般主要涉及:文本摘要、句子壓縮、文本復(fù)寫、句子融合等文本處理技術(shù)。其中本節(jié)涉及文本摘要和句子復(fù)寫兩個方面的技術(shù)。文本摘要如前所述主要涉及:關(guān)鍵詞提取、短語提取、句子提取等。句子復(fù)寫則根據(jù)實(shí)現(xiàn)手段的不同,大致可以分為如下幾種。
基于同義詞的改寫方法。這也是本節(jié)使用的方式,這種方法是詞匯級別的,能夠在很大程度上保證替換后的文本與原文語義一致。缺點(diǎn)就是會造成句子的通順度有所降低,當(dāng)然可以結(jié)合隱馬爾科夫模型對于句子搭配進(jìn)行校正提升整體效果。
基于模板的改寫方法。這也是本節(jié)使用的方式。該方法的基本思想是,從大量收集的語料中統(tǒng)計(jì)歸納出固定的模板,系統(tǒng)根據(jù)輸入句子與模板的匹配情況,決定如何生成不同的表達(dá)形式。假設(shè)存在如下的模板。
rzv n, a a ——> a a, rzv n
那么對于(輸入):這/rzv, 鮮花/n, 真/a, 便宜/a就可以轉(zhuǎn)換為(輸出):真/a, 便宜/a, 這/rzv, 鮮花/n該方法的特點(diǎn)是易于實(shí)現(xiàn),而且處理速度快,但問題是模板的通用性難以把握,如果模板設(shè)計(jì)得過于死板,則難以處理復(fù)雜的句子結(jié)構(gòu),而且,能夠處理的語言現(xiàn)象將受到一定的約束。如果模板設(shè)計(jì)得過于靈活,往往產(chǎn)生錯誤的匹配。
基于統(tǒng)計(jì)模型和語義分析生成模型的改寫方法。這類方法就是根據(jù)語料庫中的數(shù)據(jù)進(jìn)行統(tǒng)計(jì),獲得大量的轉(zhuǎn)換概率分布,然后對于輸入的語料根據(jù)已知的先驗(yàn)知識進(jìn)行替換。這類方法的句子是在分析結(jié)果的基礎(chǔ)上進(jìn)行生成的,從某種意義上說,生成是在分析的指導(dǎo)下實(shí)現(xiàn)的,因此,改寫生成的句子有可能具有良好的句子結(jié)構(gòu)。但是其所依賴的語料庫是非常大的,這樣就需要人工標(biāo)注很多數(shù)據(jù)。對于這些問題,新的深度學(xué)習(xí)技術(shù)可以解決部分的問題。同時結(jié)合知識圖譜的深度學(xué)習(xí),能夠更好地利用人的知識,最大限度地減少對訓(xùn)練樣本的數(shù)據(jù)需求。
RNN模型實(shí)現(xiàn)文本自動生成
6.1.2節(jié)介紹了基于短文本輸入獲得長文本的一些處理技術(shù)。這里主要使用的是RNN網(wǎng)絡(luò),利用其對序列數(shù)據(jù)處理能力,來實(shí)現(xiàn)文本序列數(shù)據(jù)的自動填充。下面就對其實(shí)現(xiàn)細(xì)節(jié)做一個說明和介紹。
場景
在廣告投放的過程中,我們可能會遇到這種場景:由一句話生成一段描述文本,文本長度在200~300字之間。輸入也可能是一些主題的關(guān)鍵詞。
這個時候我們就需要一種根據(jù)少量文本輸入產(chǎn)生大量文本的算法了。這里介紹一種算法:RNN算法。在5.3節(jié)我們已經(jīng)介紹了這個算法,用該算法實(shí)現(xiàn)由拼音到漢字的轉(zhuǎn)換。其實(shí)這兩個場景的模式是一樣的,都是由給定的文本信息,生成另外一些文本信息。區(qū)別是前者是生成當(dāng)前元素對應(yīng)的漢字,而這里是生成當(dāng)前元素對應(yīng)的下一個漢字。
原理
同5.3節(jié)一樣,我們這里使用的還是Simple RNN模型。所以整個計(jì)算流程圖如圖3所示。
圖3
在特征選擇的過程中,我們需要更多地考慮上下兩段之間的銜接關(guān)系、一段文字的長度、段落中感情變化、措辭變換等描述符提取。這樣能更好地實(shí)現(xiàn)文章自然的轉(zhuǎn)承啟合。
在生成的文章中,對于形容詞、副詞的使用可以給予更高的比重,因?yàn)樾稳菰~、副詞一般不會影響文章的結(jié)構(gòu),以及意思的表達(dá),但同時又能增加文章的吸引力。比如最好的,最漂亮的,最便宜的等,基本都是百搭的詞,對于不同的名詞或者動名詞都可以進(jìn)行組合,同時讀者看到以后,一般都有欲望去深度了解。
對于和主題相關(guān)的詞,可以在多處使用,如果能替換為和主題相關(guān)的詞都盡量替換為和主題相關(guān)的詞。因?yàn)檫@個不僅能提升文章的連貫性,還能增加主題的曝光率。
上面這些是對于廣告場景提出的一些經(jīng)驗(yàn)之談,其他場景的模式或許不太適用,不過可以根據(jù)自己的場景確定具體的優(yōu)化策略。
具體的計(jì)算流程和5.3節(jié)基本一致,在這里就不再贅述了。
代碼
實(shí)現(xiàn)特征訓(xùn)練計(jì)算的代碼如下:
public double train(List
alreadyTrain = true;
double minError = Double.MAX_VALUE;
for (int i = 0; i < totalTrain; i++) {
//定義更新數(shù)組
double[][] weightLayer0_update = new double[weightLayer0.length][weightLayer0[0].length];
double[][] weightLayer1_update = new double[weightLayer1.length][weightLayer1[0].length];
double[][] weightLayerh_update = new double[weightLayerh.length][weightLayerh[0].length];
List
List
double[] hiddenLayerInitial = new double[hiddenLayers];
//對于初始的隱含層變量賦值為0
Arrays.fill(hiddenLayerInitial, 0.0);
hiddenLayerInput.add(hiddenLayerInitial);
double overallError = 0.0;
//前向網(wǎng)絡(luò)計(jì)算預(yù)測誤差
overallError = propagateNetWork(x, y, hiddenLayerInput,
outputLayerDelta, overallError);
if (overallError < minError) {
minError = overallError;
}else {
continue;
}
first2HiddenLayer = Arrays.copyOf(hiddenLayerInput.get(hiddenLayerInput.size()-1), hiddenLayerInput.get(hiddenLayerInput.size()-1).length);
double[] hidden2InputDelta = new double[weightLayerh_update.length];
//后向網(wǎng)絡(luò)調(diào)整權(quán)值矩陣
hidden2InputDelta = backwardNetWork(x, hiddenLayerInput,
outputLayerDelta, hidden2InputDelta,weightLayer0_update, weightLayer1_update, weightLayerh_update);
weightLayer0 = matrixAdd(weightLayer0, matrixPlus(weightLayer0_update, alpha));
weightLayer1 = matrixAdd(weightLayer1, matrixPlus(weightLayer1_update, alpha));
weightLayerh = matrixAdd(weightLayerh, matrixPlus(weightLayerh_update, alpha));
}
return -1.0;
}
首先對待調(diào)整的變量進(jìn)行初始化,在完成這一步之后,就開始運(yùn)用前向網(wǎng)絡(luò)對輸入值進(jìn)行預(yù)測,完成預(yù)測之后,則運(yùn)用后向網(wǎng)絡(luò)根據(jù)預(yù)測誤差對各個權(quán)值向量進(jìn)行調(diào)整。
這個過程需要注意的是,每次權(quán)值的更新不是全量更新,而是根據(jù)學(xué)習(xí)速率alpha來進(jìn)行更新的。其他的步驟基本和之前描述的一樣。
實(shí)現(xiàn)預(yù)測計(jì)算的代碼如圖下:
public double[] predict(double[] x) {
if (!alreadyTrain) {
new IllegalAccessError("model has not been trained, so can not to be predicted!!!");
}
double[] x2FirstLayer = matrixDot(x, weightLayer0);
double[] firstLayer2Hidden = matrixDot(first2HiddenLayer, weightLayerh);
if (x2FirstLayer.length != firstLayer2Hidden.length) {
new IllegalArgumentException("the x2FirstLayer length is not equal with firstLayer2Hidden length!");
}
for (int i = 0; i < x2FirstLayer.length; i++) {
firstLayer2Hidden[i] += x2FirstLayer[i];
}
firstLayer2Hidden = sigmoid(firstLayer2Hidden);
double[] hiddenLayer2Out = matrixDot(firstLayer2Hidden, weightLayer1);
hiddenLayer2Out = sigmoid(hiddenLayer2Out);
return hiddenLayer2Out;
}
在預(yù)測之前首先要確定模型是不是已經(jīng)訓(xùn)練完成,如果沒有訓(xùn)練完成,則要先進(jìn)行訓(xùn)練得到預(yù)測模型。當(dāng)然這是一個跟具體業(yè)務(wù)邏輯有關(guān)的校驗(yàn),這主要是針對預(yù)測和訓(xùn)練分開的情況,如果訓(xùn)練和預(yù)測是在一個流程內(nèi)的,則也可以不用校驗(yàn)。
在得到訓(xùn)練模型之后,就根據(jù)前向網(wǎng)絡(luò)的流程逐步計(jì)算,最終得到預(yù)測值。因?yàn)槲覀冞@里是一個分類問題,所以最終是選擇具有最大概率的字作為最終的輸出。
拓展
文本的生成,按照輸入方式不同,可以分為如下幾種:
文本到文本的生成。即輸入的是文本,輸出的也是文本。
圖像到文本。即輸入的是圖像,輸出的是文本。
數(shù)據(jù)到文本。即輸入的是數(shù)據(jù),輸出的是文本。
其他。即輸入的形式為非上面三者,但是輸出的也是文本。因?yàn)檫@類的輸入比較難歸納,所以就歸為其他了。
其中第2、第3種最近發(fā)展得非???,特別是隨著深度學(xué)習(xí)、知識圖譜等前沿技術(shù)的發(fā)展?;趫D像生成文本描述的試驗(yàn)成果在不斷被刷新?;贕AN(對抗神經(jīng)網(wǎng)絡(luò))的圖像文本生成技術(shù)已經(jīng)實(shí)現(xiàn)了非常大的圖譜,不僅能夠根據(jù)圖片生成非常好的描述,還能根據(jù)文本輸入生成對應(yīng)的圖片。
由數(shù)據(jù)生成文本,目前主要應(yīng)用在新聞撰寫領(lǐng)域。中文和英文的都有很大的進(jìn)展,英文的以美聯(lián)社為代表,中文的則以騰訊公司為代表。當(dāng)然這兩家都不是純粹地以數(shù)據(jù)為輸入,而是綜合了上面4種情況的新聞撰寫。
從技術(shù)上來說,現(xiàn)在主流的實(shí)現(xiàn)方式有兩種:一種是基于符號的,以知識圖譜為代表,這類方法更多地使用人的先驗(yàn)知識,對于文本的處理更多地包含語義的成分。另一種是基于統(tǒng)計(jì)(聯(lián)結(jié))的,即根據(jù)大量文本學(xué)習(xí)出不同文本之間的組合規(guī)律,進(jìn)而根據(jù)輸入推測出可能的組合方式作為輸出。隨著深度學(xué)習(xí)和知識圖譜的結(jié)合,這兩者有明顯的融合現(xiàn)象,這應(yīng)該是實(shí)現(xiàn)未來技術(shù)突破的一個重要節(jié)點(diǎn)。
-
自然語言
+關(guān)注
關(guān)注
1文章
292瀏覽量
13838
原文標(biāo)題:如何使用 RNN 模型實(shí)現(xiàn)文本自動生成 | 贈書
文章出處:【微信號:AI_Thinker,微信公眾號:人工智能頭條】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
評論