Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
實際運用中啊隨處可見的就是PC端的字數(shù)控制案例,如標題只能多少字 內(nèi)容多少字 密碼多少長度等等等等,那么這些功能它是怎么實現(xiàn)的呢,今兒這個小分享道哥就給大家分享一下怎么用js去實現(xiàn)文字的輸入控制。
已知有如下html代碼
<p class="p1"> 計算剩余字數(shù)<br> <textarea cols="70" rows="8" id="msg" onkeyup="test()"></textarea><br> <span id="msg_s"></span> </p>
要實現(xiàn)在文本域textarea中輸入長度不能超過50的字符并且剩余字數(shù)跟隨輸入的內(nèi)容不斷變化,提示還可以輸入多少個字符 (要注意的是一個中文占兩個字符 一個英文字母或者符號占一個字符)
代碼如下
<script type="text/javascript"> function test2(){ //取出文本框中文本內(nèi)容 var a=document.getElementById("msg"); var len=50-a.value.length; //與50作比較,得道剩余字數(shù) if(len>0){ //如果剩余字數(shù)大于0,則提示剩余字數(shù) document.getElementById("msg_s").innerHTML="您還剩余"+len+"個字"; }else{ //如果剩余字數(shù)小于0,即字數(shù)已超出,則只留前50個字 document.getElementById("msg_s").innerHTML="您還剩余0個字"; a.value=a.value.substr(0,50); } } </script>
運行結(jié)果如下
家好,我是站長 polarisxu。
今天要聊的內(nèi)容應該可以當做一道面試題,你可以先想想該怎么實現(xiàn)。
統(tǒng)計字數(shù)是一個很常見的需求,很多人印象最深的應該是微博早些時候限制 140 字,而且邊輸入會邊統(tǒng)計剩余字數(shù)。現(xiàn)在很多社區(qū)文章也會有字數(shù)統(tǒng)計的功能,而且可以依據(jù)字數(shù)來預估閱讀時間。比如 Go語言中文網(wǎng)就有這樣的功能。
下手之前先分析下這個需求。從我個人經(jīng)驗看,在實際面試中,針對一個面試題,你的分析過程,循序漸進的解決方案,可以很好的展示你的思考過程。正所謂分析問題、解決問題。這會給你加分的。
我們采用類似詞法分析的思路分析這個需求。
一篇文章通常包含如下元素,我們也稱之為 token:
其中普通文字通常會分為歐美和中日韓(CJK),因為 CJK 屬于表意文字,和歐美字母的文字差異很大。同時這里還涉及到編碼的問題。本文假設使用 UTF-8 編碼。
對于標點符號,中文標點和英文標點也會很不一樣。
此外還有全角和半角的問題。
根據(jù)以上分析,對于該需求作如下假定:
本文的解決方案針對以上的假定進行。
先看最簡單的。
根據(jù)以上分析,如果文章只包含普通文本且是英文,也就是說,每個字(單詞)根據(jù)空格分隔,統(tǒng)計是最簡單的。
func TotalWords(s string) int {
n := 0
inWord := false
for _, r := range s {
wasInWord := inWord
inWord = !unicode.IsSpace(r)
if inWord && !wasInWord {
n++
}
}
return n
}
還有一種更簡單的方式:
len(strings.Fields(s))
不過看 strings.Fields 的實現(xiàn),性能會不如第一種方式。
回顧上面的需求分析,會發(fā)現(xiàn)這個實現(xiàn)是有 Bug 的。比如下面的例子:
s1 := "Hello,playground"
s2 := "Hello, playground"
用上面的實現(xiàn),s1 的字數(shù)是 1,s2 的字數(shù)是 2。它們都忽略了標點符號。而且因為寫法的多樣性(不規(guī)范統(tǒng)一),導致計算字數(shù)會有誤差。所以我們需要對寫法進行規(guī)范。
其實和寫代碼要有規(guī)范一樣,文章也是有規(guī)范的。比如出版社對于一本書的排版會有明確的規(guī)定。為了讓我們的文章看起來更舒服,也應該遵循一定的規(guī)范。
這里推薦一個 GitHub 上的排版指南:《中文文案排版指北》,它的宗旨,統(tǒng)一中文文案、排版的相關用法,降低團隊成員之間的溝通成本,增強網(wǎng)站氣質(zhì)。這個規(guī)范開頭關于空格的一段話很有意思:
有研究顯示,打字的時候不喜歡在中文和英文之間加空格的人,感情路都走得很辛苦,有七成的比例會在 34 歲的時候跟自己不愛的人結(jié)婚,而其余三成的人最后只能把遺產(chǎn)留給自己的貓。畢竟愛情跟書寫都需要適時地留白。
建議大家可以看看這個指北,一些知名的網(wǎng)站就是按照這個做的。
因為 GCTT 的排版在這個規(guī)范做,但人為約束不是最好的方法,所以我開發(fā)了一個 Go 工具:https://github.com/studygolang/autocorrect,用于自動給中英文之間加入合理的空格并糾正專用名詞大小寫。
所以為了讓字數(shù)統(tǒng)計更準確,我們假定文章是按一定的規(guī)范書寫的。比如上面的例子,規(guī)范的寫法是 s2 := "Hello, playground"。不過這里標點不算作字數(shù)。
剛?cè)ノ⒉┥显嚵艘幌拢l(fā)現(xiàn)微博的字數(shù)計算方式有點詭異,竟然是 9 個字。
測試一下發(fā)現(xiàn),它直接把兩個英文字母算作一個字(兩個字節(jié)算一個字)。而漢字是正常的。大家可以想想微博是怎么實現(xiàn)的。
中文不像英文,單詞之間沒有空格分隔,因此開始的那兩種方式不適合。
如果是純中文,我們怎么計算字數(shù)呢?
在 Go 語言中,字符串使用 UTF-8 編碼,一個字符用 rune 表示。因此在標準庫中查找相關計算方法。
func RuneCountInString(s string) (n int)
這個方法能計算字符串包含的 rune(字符)數(shù),對于純中文,就是漢字數(shù)。
str := "你好世界"
fmt.Println(utf8.RuneCountInString(str))
以上代碼輸出 4。
然而,因為很多時候文章會中英文混合,因此我們先采用上面的純英文的處理方式,即:strings.Fields(),將文章用空格分隔,然后處理每一部分。
func TotalWords(s string) int {
wordCount := 0
plainWords := strings.Fields(s)
for _, word := range plainWords {
runeCount := utf8.RuneCountInString(word)
if len(word) == runeCount {
wordCount++
} else {
wordCount += runeCount
}
}
return wordCount
}
增加如下的測試用例:
func TestTotalWords(t *testing.T) {
tests := []struct {
name string
input string
want int
}{
{"en1", "hello,playground", 2},
{"en2", "hello, playground", 2},
{"cn1", "你好世界", 4},
{"encn1", "Hello你好世界", 5},
{"encn2", "Hello 你好世界", 5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := wordscount.TotalWords(tt.input); got != tt.want {
t.Errorf("TotalWords() = %v, want %v", got, tt.want)
}
})
}
}
發(fā)現(xiàn) en1 和 encn1 測試不通過,因為沒有按照上面說的規(guī)范書寫。因此我們通過程序增加必要的空格。
// AutoSpace 自動給中英文之間加上空格
func AutoSpace(str string) string {
out := ""
for _, r := range str {
out = addSpaceAtBoundary(out, r)
}
return out
}
func addSpaceAtBoundary(prefix string, nextChar rune) string {
if len(prefix) == 0 {
return string(nextChar)
}
r, size := utf8.DecodeLastRuneInString(prefix)
if isLatin(size) != isLatin(utf8.RuneLen(nextChar)) &&
isAllowSpace(nextChar) && isAllowSpace(r) {
return prefix + " " + string(nextChar)
}
return prefix + string(nextChar)
}
func isLatin(size int) bool {
return size == 1
}
func isAllowSpace(r rune) bool {
return !unicode.IsSpace(r) && !unicode.IsPunct(r)
}
這樣可以在 TotalWords 函數(shù)開頭增加 AutoSpace 進行規(guī)范化。這時結(jié)果就正常了。
以上例子標點沒計算在內(nèi),而且如果英文和中文標點混合在一起,情況又復雜了。
為了更好地實現(xiàn)開始的需求分析,重構(gòu)以上代碼,設計如下的結(jié)構(gòu):
type Counter struct {
Total int // 總字數(shù) = Words + Puncts
Words int // 只包含字符數(shù)
Puncts int // 標點數(shù)
Links int // 鏈接數(shù)
Pics int // 圖片數(shù)
CodeLines int // 代碼行數(shù)
}
同時將 TotalWords 重構(gòu)為 Counter 的 Stat 方法,同時記錄標點數(shù):
func (wc *Counter) Stat(str string) {
wc.Links = len(rxStrict.FindAllString(str, -1))
wc.Pics = len(imgReg.FindAllString(str, -1))
// 剔除 HTML
str = StripHTML(str)
str = AutoSpace(str)
// 普通的鏈接去除(非 HTML 標簽鏈接)
str = rxStrict.ReplaceAllString(str, " ")
plainWords := strings.Fields(str)
for _, plainWord := range plainWords {
words := strings.FieldsFunc(plainWord, func(r rune) bool {
if unicode.IsPunct(r) {
wc.Puncts++
return true
}
return false
})
for _, word := range words {
runeCount := utf8.RuneCountInString(word)
if len(word) == runeCount {
wc.Words++
} else {
wc.Words += runeCount
}
}
}
wc.Total = wc.Words + wc.Puncts
}
var (
rxStrict = xurls.Strict()
imgReg = regexp.MustCompile(`<img [^>]*>`)
stripHTMLReplacer = strings.NewReplacer("\n", " ", "</p>", "\n", "<br>", "\n", "<br />", "\n")
)
// StripHTML accepts a string, strips out all HTML tags and returns it.
func StripHTML(s string) string {
// Shortcut strings with no tags in them
if !strings.ContainsAny(s, "<>") {
return s
}
s = stripHTMLReplacer.Replace(s)
// Walk through the string removing all tags
b := GetBuffer()
defer PutBuffer(b)
var inTag, isSpace, wasSpace bool
for _, r := range s {
if !inTag {
isSpace = false
}
switch {
case r == '<':
inTag = true
case r == '>':
inTag = false
case unicode.IsSpace(r):
isSpace = true
fallthrough
default:
if !inTag && (!isSpace || (isSpace && !wasSpace)) {
b.WriteRune(r)
}
}
wasSpace = isSpace
}
return b.String()
}
代碼過多的細節(jié)不討論。此外,關于文章內(nèi)的代碼行數(shù)統(tǒng)計未實現(xiàn)(目前沒有想到特別好的方法,如果你有,歡迎交流)。
通過本文的分析發(fā)現(xiàn),精準統(tǒng)計字數(shù)沒那么容易,這里涉及到很多的細節(jié)。
當然,實際應用中,字數(shù)不需要那么特別精準,而且對于非正常文字(比如鏈接、代碼)怎么處理,會有不同的約定。
本文涉及到的完整代碼放在 GitHub:https://github.com/polaris1119/wordscount。
自floydhub
作者:Alfrick Opidi
機器之心編譯
參與:杜偉、張倩
我們在閱讀新聞報道等實時性文章時,需要快速歸納出文章的大意。但是,如果將一篇很長的文章歸納成一個能夠涵蓋原文中心思想的小段落,則需要我們耗費大量時間。本文介紹了自然語言處理中的兩種文本自動摘要生成方法——抽取式和抽象式文本摘要。這兩種方法通過計算文本中句子成分的權重來生成摘要,可以大大節(jié)省通讀全文以及歸納總結(jié)主要信息的時間,為讀者提供方便。
你是否曾將一篇冗長的文檔歸納為一個小的段落?你用了多長時間呢?手動歸納總結(jié)耗費時間、枯燥乏味。文本自動摘要可以克服此類難題,幫你輕松歸納出一篇文章的中心思想。
文本摘要方法能夠?qū)θ唛L文本進行簡潔準確的總結(jié),同時將重點放在傳達有用信息的章節(jié),而又不失去文章大意。
文本自動摘要旨在將冗長文檔變成縮寫版本,若手動完成則可能非常麻煩且成本高昂。
在生成需要的摘要文本之前,機器學習算法可被訓練用以理解文檔,識別傳達重要事實和信息的章節(jié)。
使用文本摘要機器學習算法生成一篇在線新聞文章的摘要。
文本自動摘要的必要性
隨著目前數(shù)字空間中數(shù)據(jù)的爆炸式增長,而大多又是非結(jié)構(gòu)化的文本數(shù)據(jù),因而需要開發(fā)文本自動摘要工具,使人們可以輕易獲知文本大意。當前,我們可以快速訪問大量信息。但是,大多數(shù)信息冗長、無關緊要,還可能無法傳達其本意。例如,如果你想從一篇在線新聞報道中搜尋一些特定信息,你也許要吃透報道內(nèi)容,花費大量時間剔除無用信息,之后才能找到自己想要了解的信息。所以,使用能夠提取有用信息并剔除無關緊要和無用數(shù)據(jù)的自動文本摘要生成器變得非常重要。文本摘要的實現(xiàn)可以增強文檔的可讀性,減少搜尋信息的時間,獲得更多適用于特定領域的信息。
文本自動摘要的主要類型
從廣義的角度看,自然語言處理(NLP)中有兩種文本摘要生成方法:抽取式和抽象式。
抽取式摘要(extraction-based summarization)
在抽取式摘要中,抽取一段文本中表示重點內(nèi)容的單詞子集,并結(jié)合起來生成摘要。我們可以將抽取式摘要看作是一支熒光筆-從源文本中抽取主要信息。
熒光筆 = 抽取式摘要
在機器學習中,抽取式摘要通常需要衡量基本句子成分的權重,并根據(jù)權重結(jié)果生成摘要。
不同類型的算法和方法均可用于衡量句子的權重,之后根據(jù)各成分之間的關聯(lián)性和相似性進行排序-并進一步將這些成分連接起來以生成摘要。
如下例所示:
抽取式摘要
如上例所示,抽取式摘要由熒光筆標黃的單詞組成,生成摘要的語法可能不準確。
抽象式摘要
在抽象式摘要中,高級深度學習方法(advanced deep learning technique)用于解釋和縮寫原始文檔,就像人類所做的一樣。將抽象式摘要想象成一支鋼筆-它能生成或許不屬于源文檔的新句子。
鋼筆 = 抽象式摘要
由于抽象式機器學習算法能夠生成表示源文本中最重要信息的新短語和句子,所以這些抽象式算法有助于克服抽取式摘要中的語法不準確問題。
如下例所示:
抽象式摘要。
盡管抽象式文本摘要的表現(xiàn)更好,但開發(fā)相關算法需要復雜的深度學習技巧和語言模型。
為了獲得合理產(chǎn)出,抽象式摘要方法必須能夠解決諸多自然語言處理問題,如自然語言生成、語義表征和推理排序(inference permutation)。
同樣地,抽取式文本摘要方法依然大受歡迎。在本文中,我們將重點介紹抽象式文本摘要方法。
如何執(zhí)行文本摘要
我們使用以下一段話展示如何執(zhí)行文本摘要抽?。?/p>
我們依照以下步驟對這段話作總結(jié),同時盡可能保留原意。
第一步:將這段話轉(zhuǎn)換成句子
首先,我們將這段話分割成相應的句子。轉(zhuǎn)換成句子的最佳方法是在句點(period)出現(xiàn)時提取一個句子。
第二步:文本處理
接下來,我們在文本處理中移除停止詞(那些沒有實際意義的常見詞,如「and」和「the」)、數(shù)字、標點符號以及句子中的其他特殊字符。
句子成分的過濾有助于移除冗余和不重要的信息,這些信息對文本意圖的表達或許沒有任何價值。
以下是文本處理結(jié)果:
第三步:分詞
切分各個句子,列出句子中的所有單詞。
以下是單詞列表:
['peter','elizabeth','took','taxi','attend','night','party','city','party','elizabeth','collapse','rush','hospital', 'diagnose','brain', 'injury', 'doctor','told','peter','stay','besides','get','well','peter', 'stayed','hospital','days','without','leaving']
第四步:評估單詞的加權出現(xiàn)頻率(occurrence frequency)
緊接著,我們計算所有單詞的加權出現(xiàn)頻率。為此,我們用每個單詞的出現(xiàn)頻率除以這段話中出現(xiàn)最多次的單詞的頻率,在這段話中出現(xiàn)最多的是 Peter,總共出現(xiàn)了三次。
下表給出了每個單詞的加權出現(xiàn)頻率。
第五步:用相應的加權頻率替代原句中的各個單詞,然后計算總和。
我們在文本處理步驟中已經(jīng)移除了停止詞和特殊字符等無關緊要的單詞,因而它們的加權頻率為零,也就沒有必要在計算時加上。
根據(jù)所有單詞的加權頻率總和,我們可以推導出:第一個句子在整段話中的權重最大。所以,第一個句子能夠?qū)@段話的意思作出最具代表性的總結(jié)。
此外,如果第一個句子與第三個句子(該句的權重在整段話中排第二)相結(jié)合,則可以作出更好的總結(jié)。
以上例子只是基本說明了如何在機器學習中執(zhí)行抽取式文本摘要?,F(xiàn)在,我們看看如何在創(chuàng)建實際摘要生成器中運用上述概念。
維基百科文章的文本摘要
讓我們動手創(chuàng)建一個可以簡化冗長 web 文章中信息的文本摘要生成器。為簡單起見,除了 Python 的 NLTK toolkit,我們不使用任何其他機器學習庫(machine learning library)。
以下是摘要生成器的代碼 blueprint:
# Creating a dictionary for the word frequency table frequency_table = _create_dictionary_table(article) # Tokenizing the sentences sentences = sent_tokenize(article) # Algorithm for scoring a sentence by its words sentence_scores = _calculate_sentence_scores(sentences, frequency_table) # Getting the threshold threshold = _calculate_average_score(sentence_scores) # Producing the summary article_summary = _get_article_summary(sentences, sentence_scores, 1.5 * threshold) print(article_summary)
依照下列步驟使用 Python 語言創(chuàng)建一個簡單的文本摘要生成器。
第一步:準備數(shù)據(jù)
在這個例子中,我們想總結(jié)一下這篇 Wikipedia 文章的信息,這篇文章只是對 20 世紀發(fā)生的主要事件進行概述。
為了獲取這篇文章的文本,我們將使用 Beautiful Soup 庫。
以下是抓取文章內(nèi)容的代碼:
import bs4 as BeautifulSoup import urllib.request # Fetching the content from the URL fetched_data = urllib.request.urlopen('https://en.wikipedia.org/wiki/20th_century') article_read = fetched_data.read() # Parsing the URL content and storing in a variable article_parsed = BeautifulSoup.BeautifulSoup(article_read,'html.parser') # Returning <p> tags paragraphs = article_parsed.find_all('p') article_content = '' # Looping through the paragraphs and adding them to the variable for p in paragraphs: article_content += p.text
在以上代碼中,我們首先導入抓取網(wǎng)頁數(shù)據(jù)所必需的庫。BeautifulSoup 庫用于解析網(wǎng)頁內(nèi)容,而 urllib library 用于連接網(wǎng)頁和檢索 HTML。
BeautifulSoup 將輸入文本轉(zhuǎn)化為 Unicode 字符,將輸出文本轉(zhuǎn)化為 UTF-8 字符,省去了從 web 上抓取文本時處理不同字符集編碼的麻煩。
我們使用 urllib.request 程序中的 urlopen 函數(shù)打開網(wǎng)頁。之后,使用 read 函數(shù)讀取所抓取的數(shù)據(jù)對象。為了解析數(shù)據(jù),我們調(diào)用 BeautifulSoup 對象,并向它傳遞兩個參數(shù),即 article_read 和 html.parser。
find_all 函數(shù)用于傳回 HTML 中出現(xiàn)的所有<p>元素。此外,.text 使我們只能選擇<p>元素中的文本。
第二步:處理數(shù)據(jù)
為盡可能確保廢棄的文本數(shù)據(jù)無噪聲,我們將執(zhí)行一些基本的文本清理(text cleaning)。為協(xié)助完成這一處理過程,我們將從 NLTK 庫中導入一個停止詞列表。
我們還將引入 PorterStemmer,這是一種將單詞還原成詞根形式的算法。例如,單詞 cleaning、cleaned 和 cleaner 都可以還原成詞根 clean。
此外,我們還將創(chuàng)建一個包含文本中每一單詞出現(xiàn)頻率的字典表。我們將依次讀取文本及相應單詞,以消除所有停止詞。
之后,我們將檢查單詞是否出現(xiàn)在 frequency_table 中。如果一個單詞之前就在字典中,則其值更新 1。否則,如果一個單詞首次被識別到,則其值設置為 1。
例如,頻率表應如下所示:
代碼如下:
from nltk.corpus import stopwords from nltk.stem import PorterStemmer def _create_dictionary_table(text_string) -> dict: # Removing stop words stop_words = set(stopwords.words("english")) words = word_tokenize(text_string) # Reducing words to their root form stem = PorterStemmer() # Creating dictionary for the word frequency table frequency_table = dict() for wd in words: wd = stem.stem(wd) if wd in stop_words: continue if wd in frequency_table: frequency_table[wd] += 1 else: frequency_table[wd] = 1 return frequency_table
第三步:將文章分割成句子
為了將 article_content 分割成一個句子集,我們將使用 NLTK 庫中的內(nèi)置方法。
from nltk.tokenize import word_tokenize, sent_tokenize sentences = sent_tokenize(article)
第四步:確定句子的加權頻率
為了評估文本中每個句子的分數(shù),我們將分析每個單詞的出現(xiàn)頻率。在這種情況下,我們將根據(jù)句子中的單詞對該句進行評分,也就是加上句子中每個重要單詞的出現(xiàn)頻率。
請看以下代碼:
def _calculate_sentence_scores(sentences, frequency_table) -> dict: # Algorithm for scoring a sentence by its words sentence_weight = dict() for sentence in sentences: sentence_wordcount = (len(word_tokenize(sentence))) sentence_wordcount_without_stop_words = 0 for word_weight in frequency_table: if word_weight in sentence.lower(): sentence_wordcount_without_stop_words += 1 if sentence[:7] in sentence_weight: sentence_weight[sentence[:7]] += frequency_table[word_weight] else: sentence_weight[sentence[:7]] = frequency_table[word_weight] sentence_weight[sentence[:7]] = sentence_weight[sentence[:7]] / sentence_wordcount_without_stop_words return sentence_weight
重要的是,為了避免長句的分數(shù)必然高于短句,我們用每個句子的分數(shù)除以該句中的單詞數(shù)。
另外,為了優(yōu)化字典內(nèi)存,我們?nèi)我馓砑?sentence[:7],這指的是每個句子的前七個字符。但在較長的文檔中,你很可能遇到具有相同首個 n_chars 的句子,這時最好使用哈希函數(shù)(hash function)或 index 函數(shù)(index function)來處理此類極端情況(edge-cases),避免沖突。
第五步:計算句子閾值
為了進一步調(diào)整適合摘要的句子類型,我們將創(chuàng)建句子的平均分。借助于這個閾值,我們可以避免選擇分數(shù)低于平均分的句子。
代碼如下:
def _calculate_average_score(sentence_weight) -> int: # Calculating the average score for the sentences sum_values = 0 for entry in sentence_weight: sum_values += sentence_weight[entry] # Getting sentence average value from source text average_score = (sum_values / len(sentence_weight)) return average_score
第六步:生成摘要
最后,我們擁有了所有必需的參數(shù),因而現(xiàn)在可以生成文章摘要了。
代碼如下:
def _get_article_summary(sentences, sentence_weight, threshold): sentence_counter = 0 article_summary = '' for sentence in sentences: if sentence[:7] in sentence_weight and sentence_weight[sentence[:7]] >= (threshold): article_summary += " " + sentence sentence_counter += 1 return article_summary
總結(jié)
下圖展示了創(chuàng)建文本摘要算法的工作流程。
以下是機器學習中簡單抽取式文本摘要生成器的完整代碼:
#importing libraries from nltk.corpus import stopwords from nltk.stem import PorterStemmer from nltk.tokenize import word_tokenize, sent_tokenize import bs4 as BeautifulSoup import urllib.request #fetching the content from the URL fetched_data = urllib.request.urlopen('https://en.wikipedia.org/wiki/20th_century') article_read = fetched_data.read() #parsing the URL content and storing in a variable article_parsed = BeautifulSoup.BeautifulSoup(article_read,'html.parser') #returning <p> tags paragraphs = article_parsed.find_all('p') article_content = '' #looping through the paragraphs and adding them to the variable for p in paragraphs: article_content += p.text def _create_dictionary_table(text_string) -> dict: #removing stop words stop_words = set(stopwords.words("english")) words = word_tokenize(text_string) #reducing words to their root form stem = PorterStemmer() #creating dictionary for the word frequency table frequency_table = dict() for wd in words: wd = stem.stem(wd) if wd in stop_words: continue if wd in frequency_table: frequency_table[wd] += 1 else: frequency_table[wd] = 1 return frequency_table def _calculate_sentence_scores(sentences, frequency_table) -> dict: #algorithm for scoring a sentence by its words sentence_weight = dict() for sentence in sentences: sentence_wordcount = (len(word_tokenize(sentence))) sentence_wordcount_without_stop_words = 0 for word_weight in frequency_table: if word_weight in sentence.lower(): sentence_wordcount_without_stop_words += 1 if sentence[:7] in sentence_weight: sentence_weight[sentence[:7]] += frequency_table[word_weight] else: sentence_weight[sentence[:7]] = frequency_table[word_weight] sentence_weight[sentence[:7]] = sentence_weight[sentence[:7]] / sentence_wordcount_without_stop_words return sentence_weight def _calculate_average_score(sentence_weight) -> int: #calculating the average score for the sentences sum_values = 0 for entry in sentence_weight: sum_values += sentence_weight[entry] #getting sentence average value from source text average_score = (sum_values / len(sentence_weight)) return average_score def _get_article_summary(sentences, sentence_weight, threshold): sentence_counter = 0 article_summary = '' for sentence in sentences: if sentence[:7] in sentence_weight and sentence_weight[sentence[:7]] >= (threshold): article_summary += " " + sentence sentence_counter += 1 return article_summary def _run_article_summary(article): #creating a dictionary for the word frequency table frequency_table = _create_dictionary_table(article) #tokenizing the sentences sentences = sent_tokenize(article) #algorithm for scoring a sentence by its words sentence_scores = _calculate_sentence_scores(sentences, frequency_table) #getting the threshold threshold = _calculate_average_score(sentence_scores) #producing the summary article_summary = _get_article_summary(sentences, sentence_scores, 1.5 * threshold) return article_summary if __name__ == '__main__': summary_results = _run_article_summary(article_content) print(summary_results)
點擊原文中的以下按鈕在 FloydHub Notebook 上運行代碼:
在這個例子中,我們所采用的閾值是平均分的 1.5 倍。這個超參數(shù)值(hyperparameter value)在幾次試驗后為我們生成了良好的結(jié)果。當然,你可以根據(jù)自身的偏好對數(shù)值進行微調(diào),并改進摘要生成效果。
下圖是 Wikipedia 文章的生成摘要。
使用文本摘要算法生成的 Wikipedia 文章摘要。
如你所見,運行代碼可以對冗長的 Wikipedia 文章進行總結(jié),并簡要概述 20 世紀發(fā)生的主要事件。
盡管如此,我們還可以改進摘要生成器,使之更好地生成長篇幅文本的簡潔、精確摘要。
更多內(nèi)容
當然,本文只是簡要介紹了機器學習中使用文本摘要算法所能實現(xiàn)的功能。
若想了解更多有關該主題,特別是抽象式文本摘要的知識,下面一些有用的資源可以為你提供幫助:
有沒有可能將兩種方法(抽象式和抽取式文本自動摘要)相結(jié)合?這是指針生成網(wǎng)絡(pointer generator network)的主要原理,該網(wǎng)絡通過結(jié)合抽?。ㄖ赶颍┖统橄螅ㄉ桑┤〉昧俗罴研Ч?/p>
圖源:"Taming Recurrent Neural Networks for Better Summarization"
《WikiHow: A Large Scale Text Summarization Dataset》一文提出了一個新的大規(guī)模文本自動摘要數(shù)據(jù)集 WikiHow,該數(shù)據(jù)集包含提取自 WikiHow 在線知識庫的 230000 多篇文章。目前可用的大多數(shù)數(shù)據(jù)集的規(guī)模不足以訓練序列到序列模型,它們也許只能提供有限的摘要,并且更適合執(zhí)行抽取式摘要。但是,WikiHow 數(shù)據(jù)集規(guī)模大,質(zhì)量高,能夠在抽象式文本摘要中獲得最優(yōu)結(jié)果。
《Pretraining-Based Natural Language Generation for Text Summarization》一文提出了一個基于序列到序列范式的獨特二階段模型。該模型同時在編碼器和解碼器側(cè)利用 BERT,并在學習過程中注重強化目標。當該模型在一些基準數(shù)據(jù)集上進行評估時,結(jié)果顯示,該方法在文本自動摘要中表現(xiàn)更好,尤其相較于其他傳統(tǒng)系統(tǒng)而言。
原文鏈接:https://blog.floydhub.com/gentle-introduction-to-text-summarization-in-machine-learning/
本文為機器之心編譯,轉(zhuǎn)載請聯(lián)系本公眾號獲得授權。
?------------------------------------------------
加入機器之心(全職記者 / 實習生):hr@jiqizhixin.com
投稿或?qū)で髨蟮溃篶ontent@jiqizhixin.com
廣告 & 商務合作:bd@jiqizhixin.com
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。