編者按:Kushal Chauhan分享了他在Jatana.ai的NLP研究實習期間基于句嵌入進行無監督文本總結的經驗。
什么是文本總結?
文本總結是從一個或多個來源提取最重要的信息,生成一個針對某個或某群特定讀者或任務的簡化版本的過程。——Advances in Automatic Text Summarization, 1999, 第1頁
一般來說,人類相當擅長這一任務,因為我們具有理解文檔含義,使用自己的語言總結要點的能力。然而,由于當今世界信息過載,缺乏人力和時間解讀數據,自動文本總結技術十分關鍵。自動文本總結的價值在于:
減少閱讀時間。
簡化研究的篩選過程。
提高索引的有效性。
在問答系統中,個性化總結提供了個性化信息。
自動總結系統或半自動總結系統的應用讓商業摘要服務提高了處理文檔的吞吐量。
文本總結方法的類型
根據不同的標準,文本總結方法可以分為不同類型。
基于輸入類型
單文檔許多早期的總結系統只能處理單文檔。
多文檔支持任意數量的文檔作為輸入。
基于目的
通用模型對要總結的文本內容的領域不作任何假定,并將所有輸入作為同構文本處理。文本總結領域的大部分工作都屬于這類。
領域特定模型使用領域特定知識以形成更精確的總結。例如,總結特定領域的研究論文,生物醫學文檔,等等。
基于查詢總結僅僅包括回答關于輸入文本的自然語言提問的信息。
基于輸出類型
提取從輸入文本中選取最重要的句子,組成總結。現在大多數總結方法本質上都是提取式的。
摘要模型用自己的詞組和句子提供一份更連貫的總結,類似人類所做的總結。這類方法無疑更有吸引力,但比提取式總結要困難得多。
我的任務
我的任務是在電子郵件上應用文本總結,郵件以英語、丹麥語、法語等多種語言撰寫。大多數公開的文本總結數據集面向的是長文檔和文章。由于長文檔和文章的結構和短郵件有很大的不同,以監督方法訓練的模型可能在領域自適應方面表現很差。因此,我選擇探索無監督方法,期望得到總結的無偏預測。
現在,讓我們嘗試了解構成模型流程的多個步驟。
文本總結模型流程
我所用的文本總結方法借鑒了Aishwarya Padmakumar和Akanksha Saran的論文Unsupervised Text Summarization Using Sentence Embeddings。這一方法可以分解為以下步驟:
第一步:清洗郵件
讓我們先來看下典型的郵件看起來是什么樣的:
英文郵件樣本:
HiJane,
Thank you for keeping me updated on this issue. I'm happy to hear that the issue got resolved after all and you can now use the app in its full functionality again.
Also many thanks for your suggestions. We hope to improve this feature in the future.
In case you experience any further problems with the app, please don't hesitate to contact me again.
Best regards,
JohnDoe
CustomerSupport
1600AmphitheatreParkway
MountainView, CA
UnitedStates
挪威語郵件樣本:
Hei
Grunnet manglende dekning p? deres kort for m?nedlig trekk, blir dere n? overf?rt til ?rlig fakturering.
I morgen vil dere motta faktura for hosting og drift av nettbutikk for perioden 05.03.2018-05.03.2019.
Ta gjerne kontakt om dere har sp?rsm?l.
Med vennlig hilsen
JohnDoe - SomeCompany.no
04756 | johndoe@somecompany.no
Husk ? sjekk v?rt hjelpesenter, kanskje du finner svar der: https://support.somecompany.no/
意大利語郵件樣本:
CiaoJohn,
Grazie mille per averci contattato! Apprezziamo molto che abbiate trovato il tempo per inviarci i vostri commenti e siamo lieti che vi piaccia l'App.
Sentitevi liberi di parlare di con i vostri amici o di sostenerci lasciando una recensione nell'AppStore!
Cordiali saluti,
JaneDoe
CustomerSupport
OneInfiniteLoop
Cupertino
CA 95014
如你所見,郵件開頭的稱呼語和末尾的簽名對總結生成任務毫無貢獻。所以,有必要從郵件中移除這些應該不會影響總結的行。這簡化了輸入,使模型表現可以更佳。
由于不同郵件和不同語言的稱呼語和簽名不一樣,移除它們需要使用正則表達式匹配。如果只處理英文郵件,那么我們可以直接使用Mailgun的talon庫:
from talon.signature.bruteforce import extract_signature
cleaned_email, _ = extract_signature(email)
不過我需要處理多種語言的郵件,所以我修改了extract_signature函數,以支持英語之外的其他語言。我還順便移除了換行符。
上面三個郵件樣本經過清洗后,是這樣的:
清洗過的英語郵件:
Thank you for keeping me updated on this issue. I'm happy to hear that the issue got resolved after all and you can now use the app in its full functionality again. Also many thanks for your suggestions. We hope to improve this feature in the future. In case you experience any further problems with the app, please don't hesitate to contact me again.
清洗過的挪威語郵件:
Grunnet manglende dekning p? deres kort for m?nedlig trekk, blir dere n? overf?rt til ?rlig fakturering. I morgen vil dere motta faktura for hosting og drift av nettbutikk for perioden 05.03.2018-05.03.2019.Ta gjerne kontakt om dere har sp?rsm?l.
清洗過的意大利語郵件:
Grazie mille per averci contattato! Apprezziamo molto che abbiate trovato il tempo per inviarci i vostri commenti e siamo lieti che vi piaccia l'App. Sentitevi liberi di parlare di con i vostri amici o di sostenerci lasciando una recensione nell'AppStore.
完成這一預處理步驟之后,我們可以進一步探索總結流程剩下的部分。
第二步:檢測語言
由于要總結的郵件可能使用任何語言,我們首先需要做的就是判定郵件的語言。有很多使用機器學習技術識別文本語言的Python庫,例如polyglot、langdetect、textblob。我使用了langdetect,它支持55種不同的語言。只需一個簡單的函數調用就可以檢測語言:
from langdetect import detect
lang = detect(cleaned_email) # 如果是英語郵件,那么lang = 'en'
第三步:句子分割
識別了每封郵件的語言后,我們就可以根據不同語言的規則(標點符號)將郵件分割成句子。我們將使用NLTK:
from nltk.tokenize import sent_tokenize
sentences = sent_tokenize(email, language = lang)
第四步:Skip-Thought編碼器
我們需要找到一種方式,為郵件中的每句話生成固定長度的向量表示。該表示應當編碼句子的內在語義和含義。知名的Skip-Gram Word2Vec詞嵌入生成方法可以為模型詞匯表收錄的詞提供詞嵌入(FastText這樣更酷炫的方法能夠基于子詞信息為模型詞匯表外的單詞生成嵌入)。
有了詞嵌入,只需對每句話包含的詞嵌入進行加權求和,即可得到句嵌入。之所以要加權,是因為“and”、“to”、“the”等一些頻繁出現的單詞完全沒有或幾乎沒有提供任何關于句子的信息。而只在個別句子中出現的一些罕見詞,代表性要高很多。因此,權重的取值和詞頻逆相關。具體細節可以參考Sanjeev Arora等的論文(ICLR17/SyK00v5xx)
然而,這樣的無監督方法沒有考慮句子中單詞的順序。這可能對模型的表現造成不利影響。所以我選擇在維基百科數據上訓練一個Skip-Thought句編碼器。Skip-Thoughts模型包括兩部分:
編碼器網絡:編碼器通常是一個GRU循環神經網絡,為輸入中的每個句子Si生成固定長度的向量表示hi。將GRU單元的最終隱藏狀態(即,在它見過整個句子之后得到的隱藏狀態)傳給多個密集層,得到編碼表示hi。
解碼器網絡:解碼器網絡接受向量表示hi作為輸入,并嘗試生成兩個句子——Si-1和Si+1,分別為可能出現在輸入句子之前和之后的句子。生成前句和后句的是獨立的解碼器,均為GRU循環神經網絡。向量表示hi作為解碼器網絡GRU的初始隱藏狀態。
給定包含句子序列的數據集,解碼器的目標是逐詞生成前句和后句。訓練編碼器-解碼器網絡以最小化句子的重建損失,在此過程中,編碼器學習生成能為解碼器編碼足夠信息的向量表示,以便解碼器生成相鄰句子。這些學習到的表示滿足語義上相似的句子在向量空間中的嵌入互相接近,因此適合用于聚類。在我們的例子中,郵件中的句子作為編碼器網絡的輸入,以得到所需向量表示。獲得句嵌入的Skip-Thoughts方法的細節請參考原論文(arXiv:1506.06726)。
給定一個句子(灰點),模型嘗試預測前句(紅點)和后句(綠點)
至于實現,我使用了論文作者開源的代碼。該實現基于Theano,可以通過GitHub倉庫ryankiros/skip-thoughts獲取。這個實現很容易使用,只需幾行代碼就可以獲取一封郵件的句嵌入:
import skipthoughts
# 你首先需要下載預訓練模型
model = skipthoughts.load_model()
encoder = skipthoughts.Encoder(model)
encoded = encoder.encode(sentences)
第五步:聚類
為郵件中的每個句子生成句嵌入后,我們將這些高維向量空間中的嵌入聚類為數量預定義的一組聚類。聚類的數目將等于總結所需的句數。我為總結選擇的句數等于郵件總句數的平方根。另一種可能的方案是等于總句數的某個百分比,比如30%. 下面是聚類的代碼:
import numpy as np
from sklearn.cluster importKMeans
n_clusters = np.ceil(len(encoded)**0.5)
kmeans = KMeans(n_clusters=n_clusters)
kmeans = kmeans.fit(encoded)
第六步:總結
句嵌入的每個聚類可以看成一組語義上相似的句子,其含義可以通過其中的一個候選句子表達。我們選取向量表示最接近聚類中心的句子作為候選句子。每個聚類選出的候選句子經過排序構成郵件總結。總結中候選句子的順序取決于其所在聚類中的句子在原郵件中的位置。例如,如果某個候選句子所在聚類中的大多數句子出現在郵件開始,那么該句就被選為總結的第一句。下面幾行代碼實現了這一算法:
from sklearn.metrics import pairwise_distances_argmin_min
avg = []
for j in range(n_clusters):
idx = np.where(kmeans.labels_ == j)[0]
avg.append(np.mean(idx))
closest, _ = pairwise_distances_argmin_min(kmeans.cluster_centers_, encoded)
ordering = sorted(range(n_clusters), key=lambda k: avg[k])
summary = ' '.join([email[closest[idx]] for idx in ordering])
由于這一方法本質上是從文本中提取一些候選句子以形成總結,因此屬于提取式總結。
之前我們列出的郵件樣本,最終提取出的總結為:
英語郵件:
I'm happy to hear that the issue got resolved after all and you can now use the app in its full functionality again. Also many thanks for your suggestions. In case you experience any further problems with the app, please don't hesitate to contact me again.
挪威語郵件:
Grunnet manglende dekning p? deres kort for m?nedlig trekk, blir dere n? overf?rt til ?rlig fakturering. I morgen vil dere motta faktura for hosting og drift av nettbutikk for perioden 05.03.2018-05.03.2019.Ta gjerne kontakt om dere har sp?rsm?l.
意大利語郵件:
Apprezziamo molto che abbiate trovato il tempo per inviarci i vostri commenti e siamo lieti che vi piaccia l'App. Sentitevi liberi di parlare di con i vostri amici o di sostenerci lasciando una recensione nell'AppStore.
訓練
前面提到的Skip-Thought的代碼倉庫已經提供了針對英語的預訓練模型。其他一些語言需要自行訓練。我們使用了維基百科作為語料,從維基媒體基金會網站下載了.bz2壓縮文件,解壓縮得到.xml文件。接著解析.xml文件,去除html標記,只剩下純文本。有很多解析維基百科文件的工具,沒有一個是完美的。取決于使用的解析方法,解析可能要花大量時間。我使用的是GitHub上的attardi/wikiextractor,不算最好的,不過是免費的,而且可以在合理的時間內完成解析任務。我還對得到的純文本進行了一些簡單的預處理,比如移除換行符。這樣我就得到了大量的訓練數據,可以讓Skip-Thoughts模型慢慢訓練了。
Skip-Thoughts的訓練過程還要用到預訓練的詞向量,我使用了Facebook的FastText預訓練詞嵌入。由于這些詞嵌入也是在維基百科上訓練的,所以極少遇到詞匯表外的單詞。
實現細節
我把實現的模型的一個簡化版本放到了GitHub上(jatana-research/email-summarization)。這一簡化版只支持英語郵件,但是實現了上面提及的所有步驟,效果也很不錯。
結果
你也許已經注意到了,模型在只包含三兩句話的郵件上表現要差不少。例如,只包含3句話的郵件的總結會有2句話,而原本的3句話可能各自表達完全不同的事情,漏掉任何一句都是不可接受的。這正是為什么通常情況下在短輸入上不使用提取式方法進行總結的原因。序列到序列的監督模型更適合這一任務。不過在我們的例子中,郵件一般沒有這么短,所以提取式方法的效果驚人得好。
使用Skip-Thoughts向量的一個劣勢是模型需要花很多時間訓練。盡管2-3天的訓練之后就可以得到可接受的結果,但為了得到較好的結果,我還是訓練了大約一周。由于損失被句長歸一化了,在迭代過程中損失波動得很厲害。
我們可以看下數據集中最相似的句對,看看Skip-Thoughts模型表現得有多好:
I can assure you that our developers are already aware of the issue and are trying to solve it as soon as possible.
AND
I have already forwarded your problem report to our developers and they will now investigate this issue with the login page in further detail in order to detect the source of this problem.
--------------------------------------------------------------------I am very sorry to hear that.
AND
We sincerely apologize for the inconvenience caused.
--------------------------------------------------------------------Therefore, I would kindly ask you to tell me which operating system you are using the app on.
AND
Can you specify which device you are usingas well as the Androidor iOS version it currently has installed?
從上面的句子來看,這個模型的效果驚人地好,可以找出相似的句子,即使這些句子的長度很不一樣,使用的詞匯也大不相同。
可能的改進
本文介紹的方法效果相當不錯,但還不夠完美。通過增加模型復雜度,有很多可以改進的地方:
Quick-Thought向量,Skip-Thoughts的改進版,可能降低訓練時間,提升表現。
Skip-Thoughts編碼表示的維度為4800。由于維度詛咒,這樣的高維向量不是很適合聚類。我們也許可以使用自動編碼器或LSTM自動編碼器在聚類前降低這些向量的維度。
我們不一定要使用提取式方法。我們可以訓練一個解碼器網絡,讓它轉換聚類中心的編碼表示為自然語言句子,從而實現摘要式總結。Skip-Thoughts編碼器生成的數據可以用來訓練這一解碼器。然而,如果我們希望解碼器生成看上去合理的、語法正確的句子,那么需要非常小心地調整超參數和設計架構。
機器配置
所有這些試驗都是在一個n1-highmem-8的Google云主機上進行的:十核Intel Xeon CPU,Nvidia Tesla K80 GPU,52GB 內存。
特別感謝我的指導者Rahul Kumar全程給出的意見和建議,沒有他我不可能完成這一切。我也很感激Jatana.ai給我提供機會和資源完成這項工作。
-
編碼器
+關注
關注
45文章
3597瀏覽量
134171 -
數據集
+關注
關注
4文章
1205瀏覽量
24644
原文標題:基于句嵌入的無監督文本總結
文章出處:【微信號:jqr_AI,微信公眾號:論智】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論