本篇文章給大家分享的是有關(guān)Serverless中如何結(jié)合實(shí)現(xiàn)文本摘要和關(guān)鍵詞提???,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名與空間、虛擬空間、營銷軟件、網(wǎng)站建設(shè)、貴溪網(wǎng)站維護(hù)、網(wǎng)站推廣。
對文本進(jìn)行自動(dòng)摘要的提取和關(guān)鍵詞的提取,屬于自然語言處理的范疇。提取摘要的一個(gè)好處是可以讓閱讀者通過最少的信息判斷出這個(gè)文章對自己是否有意義或者價(jià)值,是否需要進(jìn)行更加詳細(xì)的閱讀;而提取關(guān)鍵詞的好處是可以讓文章與文章之間產(chǎn)生關(guān)聯(lián),同時(shí)也可以讓讀者通過關(guān)鍵詞快速定位到和該關(guān)鍵詞相關(guān)的文章內(nèi)容。
文本摘要和關(guān)鍵詞提取都可以和傳統(tǒng)的 CMS 進(jìn)行結(jié)合,通過對文章 / 新聞等發(fā)布功能進(jìn)行改造,同步提取關(guān)鍵詞和摘要,放到 HTML 頁面中作為 Description 和 Keyworks。這樣做在一定程度上有利于搜索引擎收錄,屬于 SEO 優(yōu)化的范疇。
關(guān)鍵詞提取的方法很多,但是最常見的應(yīng)該就是tf-idf
了。
通過jieba
實(shí)現(xiàn)基于tf-idf
關(guān)鍵詞提取的方法:
jieba.analyse.extract_tags(text, topK=5, withWeight=False, allowPOS=('n', 'vn', 'v'))
文本摘要的方法也有很多,如果從廣義上來劃分,包括提取式和生成式。其中提取式就是在文章中通過TextRank
等算法,找出關(guān)鍵句然后進(jìn)行拼裝,形成摘要,這種方法相對來說比較簡單,但是很難提取出真實(shí)的語義等;另一種方法是生成式,通過深度學(xué)習(xí)等方法,對文本語義進(jìn)行提取再生成摘要。
如果簡單理解,提取式方式生成的摘要,所有句子來自原文,而生成式方法則是獨(dú)立生成的。
為了簡化難度,本文將采用提取式來實(shí)現(xiàn)文本摘要功能,通過 SnowNLP 第三方庫,實(shí)現(xiàn)基于TextRank
的文本摘要功能。我們以《海底兩萬里》部分內(nèi)容作為原文,進(jìn)行摘要生成:
原文:
這些事件發(fā)生時(shí),我剛從美國內(nèi)布拉斯加州的貧瘠地區(qū)做完一項(xiàng)科考工作回來。我當(dāng)時(shí)是巴黎自然史博物館的客座教授,法國政府派我參加這次考察活動(dòng)。我在內(nèi)布拉斯加州度過了半年時(shí)間,收集了許多珍貴資料,滿載而歸,3 月底抵達(dá)紐約。我決定 5 月初動(dòng)身回法國。于是,我就抓緊這段候船逗留時(shí)間,把收集到的礦物和動(dòng)植物標(biāo)本進(jìn)行分類整理,可就在這時(shí),斯科舍號(hào)出事了。 我對當(dāng)時(shí)的街談巷議自然了如指掌,再說了,我怎能聽而不聞、無動(dòng)于衷呢?我把美國和歐洲的各種報(bào)刊讀了又讀,但未能深入了解真相。神秘莫測,百思不得其解。我左思右想,搖擺于兩個(gè)極端之間,始終形不成一種見解。其中肯定有名堂,這是不容置疑的,如果有人表示懷疑,就請他們?nèi)ッ幻箍粕崽?hào)的傷口好了。 我到紐約時(shí),這個(gè)問題正炒得沸反盈天。某些不學(xué)無術(shù)之徒提出設(shè)想,有說是浮動(dòng)的小島,也有說是不可捉摸的暗礁,不過,這些個(gè)假設(shè)通通都被推翻了。很顯然,除非這暗礁腹部裝有機(jī)器,不然的話,它怎能如此快速地轉(zhuǎn)移呢? 同樣的道理,說它是一塊浮動(dòng)的船體或是一堆大船殘片,這種假設(shè)也不能成立,理由仍然是移動(dòng)速度太快。 那么,問題只能有兩種解釋,人們各持己見,自然就分成觀點(diǎn)截然不同的兩派:一派說這是一個(gè)力大無比的怪物,另一派說這是一艘動(dòng)力極強(qiáng)的“潛水船”。 哦,最后那種假設(shè)固然可以接受,但到歐美各國調(diào)查之后,也就難以自圓其說了。有哪個(gè)普通人會(huì)擁有如此強(qiáng)大動(dòng)力的機(jī)械?這是不可能的。他在何地何時(shí)叫何人制造了這么個(gè)龐然大物,而且如何能在建造中做到風(fēng)聲不走漏呢? 看來,只有政府才有可能擁有這種破壞性的機(jī)器,在這個(gè)災(zāi)難深重的時(shí)代,人們千方百計(jì)要增強(qiáng)戰(zhàn)爭武器威力,那就有這種可能,一個(gè)國家瞞著其他國家在試制這類駭人聽聞的武器。繼夏斯勃步槍之后有水雷,水雷之后有水下撞錘,然后魔道攀升反應(yīng),事態(tài)愈演愈烈。至少,我是這樣想的。
通過 SnowNLP 提供的算法:
from snownlp import SnowNLP text = " 上面的原文內(nèi)容,此處省略 " s = SnowNLP(text) print("。".join(s.summary(5)))
輸出結(jié)果:
自然就分成觀點(diǎn)截然不同的兩派:一派說這是一個(gè)力大無比的怪物。這種假設(shè)也不能成立。我到紐約時(shí)。說它是一塊浮動(dòng)的船體或是一堆大船殘片。另一派說這是一艘動(dòng)力極強(qiáng)的“潛水船”
初步來看,效果并不是很好,接下來我們自己計(jì)算句子權(quán)重,實(shí)現(xiàn)一個(gè)簡單的摘要功能,這個(gè)就需要jieba
:
import re import jieba.analyse import jieba.posseg class TextSummary: def __init__(self, text): self.text = text def splitSentence(self): sectionNum = 0 self.sentences = [] for eveSection in self.text.split("\n"): if eveSection: sentenceNum = 0 for eveSentence in re.split("!|。|?", eveSection): if eveSentence: mark = [] if sectionNum == 0: mark.append("FIRSTSECTION") if sentenceNum == 0: mark.append("FIRSTSENTENCE") self.sentences.append({ "text": eveSentence, "pos": { "x": sectionNum, "y": sentenceNum, "mark": mark } }) sentenceNum = sentenceNum + 1 sectionNum = sectionNum + 1 self.sentences[-1]["pos"]["mark"].append("LASTSENTENCE") for i in range(0, len(self.sentences)): if self.sentences[i]["pos"]["x"] == self.sentences[-1]["pos"]["x"]: self.sentences[i]["pos"]["mark"].append("LASTSECTION") def getKeywords(self): self.keywords = jieba.analyse.extract_tags(self.text, topK=20, withWeight=False, allowPOS=('n', 'vn', 'v')) def sentenceWeight(self): # 計(jì)算句子的位置權(quán)重 for sentence in self.sentences: mark = sentence["pos"]["mark"] weightPos = 0 if "FIRSTSECTION" in mark: weightPos = weightPos + 2 if "FIRSTSENTENCE" in mark: weightPos = weightPos + 2 if "LASTSENTENCE" in mark: weightPos = weightPos + 1 if "LASTSECTION" in mark: weightPos = weightPos + 1 sentence["weightPos"] = weightPos # 計(jì)算句子的線索詞權(quán)重 index = [" 總之 ", " 總而言之 "] for sentence in self.sentences: sentence["weightCueWords"] = 0 sentence["weightKeywords"] = 0 for i in index: for sentence in self.sentences: if sentence["text"].find(i) >= 0: sentence["weightCueWords"] = 1 for keyword in self.keywords: for sentence in self.sentences: if sentence["text"].find(keyword) >= 0: sentence["weightKeywords"] = sentence["weightKeywords"] + 1 for sentence in self.sentences: sentence["weight"] = sentence["weightPos"] + 2 * sentence["weightCueWords"] + sentence["weightKeywords"] def getSummary(self, ratio=0.1): self.keywords = list() self.sentences = list() self.summary = list() # 調(diào)用方法,分別計(jì)算關(guān)鍵詞、分句,計(jì)算權(quán)重 self.getKeywords() self.splitSentence() self.sentenceWeight() # 對句子的權(quán)重值進(jìn)行排序 self.sentences = sorted(self.sentences, key=lambda k: k['weight'], reverse=True) # 根據(jù)排序結(jié)果,取排名占前 ratio% 的句子作為摘要 for i in range(len(self.sentences)): if i < ratio * len(self.sentences): sentence = self.sentences[i] self.summary.append(sentence["text"]) return self.summary
這段代碼主要是通過tf-idf
實(shí)現(xiàn)關(guān)鍵詞提取,然后通過關(guān)鍵詞提取對句子盡心權(quán)重賦予,最后獲得到整體的結(jié)果,運(yùn)行:
testSummary = TextSummary(text) print("。".join(testSummary.getSummary()))
可以得到結(jié)果:
Building prefix dict from the default dictionary ... Loading model from cache /var/folders/yb/wvy_7wm91mzd7cjg4444gvdjsglgs8/T/jieba.cache Loading model cost 0.721 seconds. Prefix dict has been built successfully. 看來,只有政府才有可能擁有這種破壞性的機(jī)器,在這個(gè)災(zāi)難深重的時(shí)代,人們千方百計(jì)要增強(qiáng)戰(zhàn)爭武器威力,那就有這種可能,一個(gè)國家瞞著其他國家在試制這類駭人聽聞的武器。于是,我就抓緊這段候船逗留時(shí)間,把收集到的礦物和動(dòng)植物標(biāo)本進(jìn)行分類整理,可就在這時(shí),斯科舍號(hào)出事了。同樣的道理,說它是一塊浮動(dòng)的船體或是一堆大船殘片,這種假設(shè)也不能成立,理由仍然是移動(dòng)速度太快
我們可以看到,整體效果要比剛才的好一些。
通過 Serverless 架構(gòu),將上面代碼進(jìn)行整理,并發(fā)布。
代碼整理結(jié)果:
import re, json import jieba.analyse import jieba.posseg class NLPAttr: def __init__(self, text): self.text = text def splitSentence(self): sectionNum = 0 self.sentences = [] for eveSection in self.text.split("\n"): if eveSection: sentenceNum = 0 for eveSentence in re.split("!|。|?", eveSection): if eveSentence: mark = [] if sectionNum == 0: mark.append("FIRSTSECTION") if sentenceNum == 0: mark.append("FIRSTSENTENCE") self.sentences.append({ "text": eveSentence, "pos": { "x": sectionNum, "y": sentenceNum, "mark": mark } }) sentenceNum = sentenceNum + 1 sectionNum = sectionNum + 1 self.sentences[-1]["pos"]["mark"].append("LASTSENTENCE") for i in range(0, len(self.sentences)): if self.sentences[i]["pos"]["x"] == self.sentences[-1]["pos"]["x"]: self.sentences[i]["pos"]["mark"].append("LASTSECTION") def getKeywords(self): self.keywords = jieba.analyse.extract_tags(self.text, topK=20, withWeight=False, allowPOS=('n', 'vn', 'v')) return self.keywords def sentenceWeight(self): # 計(jì)算句子的位置權(quán)重 for sentence in self.sentences: mark = sentence["pos"]["mark"] weightPos = 0 if "FIRSTSECTION" in mark: weightPos = weightPos + 2 if "FIRSTSENTENCE" in mark: weightPos = weightPos + 2 if "LASTSENTENCE" in mark: weightPos = weightPos + 1 if "LASTSECTION" in mark: weightPos = weightPos + 1 sentence["weightPos"] = weightPos # 計(jì)算句子的線索詞權(quán)重 index = [" 總之 ", " 總而言之 "] for sentence in self.sentences: sentence["weightCueWords"] = 0 sentence["weightKeywords"] = 0 for i in index: for sentence in self.sentences: if sentence["text"].find(i) >= 0: sentence["weightCueWords"] = 1 for keyword in self.keywords: for sentence in self.sentences: if sentence["text"].find(keyword) >= 0: sentence["weightKeywords"] = sentence["weightKeywords"] + 1 for sentence in self.sentences: sentence["weight"] = sentence["weightPos"] + 2 * sentence["weightCueWords"] + sentence["weightKeywords"] def getSummary(self, ratio=0.1): self.keywords = list() self.sentences = list() self.summary = list() # 調(diào)用方法,分別計(jì)算關(guān)鍵詞、分句,計(jì)算權(quán)重 self.getKeywords() self.splitSentence() self.sentenceWeight() # 對句子的權(quán)重值進(jìn)行排序 self.sentences = sorted(self.sentences, key=lambda k: k['weight'], reverse=True) # 根據(jù)排序結(jié)果,取排名占前 ratio% 的句子作為摘要 for i in range(len(self.sentences)): if i < ratio * len(self.sentences): sentence = self.sentences[i] self.summary.append(sentence["text"]) return self.summary def main_handler(event, context): nlp = NLPAttr(json.loads(event['body'])['text']) return { "keywords": nlp.getKeywords(), "summary": "。".join(nlp.getSummary()) }
編寫項(xiàng)目serverless.yaml
文件:
nlpDemo: component: "@serverless/tencent-scf" inputs: name: nlpDemo codeUri: ./ handler: index.main_handler runtime: Python3.6 region: ap-guangzhou description: 文本摘要 / 關(guān)鍵詞功能 memorySize: 256 timeout: 10 events: - apigw: name: nlpDemo_apigw_service parameters: protocols: - http serviceName: serverless description: 文本摘要 / 關(guān)鍵詞功能 environment: release endpoints: - path: /nlp method: ANY
由于項(xiàng)目中使用了jieba
,所以在安裝的時(shí)候推薦在 CentOS 系統(tǒng)下與對應(yīng)的 Python 版本下安裝,也可以使用我之前為了方便做的一個(gè)依賴工具:
通過sls --debug
進(jìn)行部署:
部署完成,可以通過 PostMan 進(jìn)行簡單的測試:
http://weahome.cn/article/pgdehh.html