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