亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Python 結(jié)巴分詞實(shí)現(xiàn)關(guān)鍵詞抽取分析

 更新時(shí)間:2017年10月21日 11:05:44   作者:南宮伊楓  
這篇文章主要介紹了Python 結(jié)巴分詞實(shí)現(xiàn)關(guān)鍵詞抽取分析,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

1 簡(jiǎn)介

關(guān)鍵詞抽取就是從文本里面把跟這篇文檔意義最相關(guān)的一些詞抽取出來(lái)。這個(gè)可以追溯到文獻(xiàn)檢索初期,當(dāng)時(shí)還不支持全文搜索的時(shí)候,關(guān)鍵詞就可以作為搜索這篇論文的詞語(yǔ)。因此,目前依然可以在論文中看到關(guān)鍵詞這一項(xiàng)。

除了這些,關(guān)鍵詞還可以在文本聚類、分類、自動(dòng)摘要等領(lǐng)域中有著重要的作用。比如在聚類時(shí)將關(guān)鍵詞相似的幾篇文檔看成一個(gè)團(tuán)簇,可以大大提高聚類算法的收斂速度;從某天所有的新聞中提取出這些新聞的關(guān)鍵詞,就可以大致了解那天發(fā)生了什么事情;或者將某段時(shí)間內(nèi)幾個(gè)人的微博拼成一篇長(zhǎng)文本,然后抽取關(guān)鍵詞就可以知道他們主要在討論什么話題。

總之,關(guān)鍵詞就是最能夠反映出文本主題或者意思的詞語(yǔ)。但是網(wǎng)絡(luò)上寫文章的人不會(huì)像寫論文那樣告訴你本文的關(guān)鍵詞是什么,這個(gè)時(shí)候就需要利用計(jì)算機(jī)自動(dòng)抽取出關(guān)鍵詞,算法的好壞直接決定了后續(xù)步驟的效果。
關(guān)鍵詞抽取從方法來(lái)說(shuō)大致有兩種:

  1. 第一種是關(guān)鍵詞分配,就是有一個(gè)給定的關(guān)鍵詞庫(kù),然后新來(lái)一篇文檔,從詞庫(kù)里面找出幾個(gè)詞語(yǔ)作為這篇文檔的關(guān)鍵詞;
  2. 第二種是關(guān)鍵詞抽取,就是新來(lái)一篇文檔,從文檔中抽取一些詞語(yǔ)作為這篇文檔的關(guān)鍵詞;

目前大多數(shù)領(lǐng)域無(wú)關(guān)的關(guān)鍵詞抽取算法(領(lǐng)域無(wú)關(guān)算法的意思就是無(wú)論什么主題或者領(lǐng)域的文本都可以抽取關(guān)鍵詞的算法)和它對(duì)應(yīng)的庫(kù)都是基于后者的。從邏輯上說(shuō),后者比前著在實(shí)際使用中更有意義。

從算法的角度來(lái)看,關(guān)鍵詞抽取算法主要有兩類:

  1. 有監(jiān)督學(xué)習(xí)算法,將關(guān)鍵詞抽取過(guò)程視為二分類問(wèn)題,先抽取出候選詞,然后對(duì)于每個(gè)候選詞劃定標(biāo)簽,要么是關(guān)鍵詞,要么不是關(guān)鍵詞,然后訓(xùn)練關(guān)鍵詞抽取分類器。當(dāng)新來(lái)一篇文檔時(shí),抽取出所有的候選詞,然后利用訓(xùn)練好的關(guān)鍵詞抽取分類器,對(duì)各個(gè)候選詞進(jìn)行分類,最終將標(biāo)簽為關(guān)鍵詞的候選詞作為關(guān)鍵詞;
  2. 無(wú)監(jiān)督學(xué)習(xí)算法,先抽取出候選詞,然后對(duì)各個(gè)候選詞進(jìn)行打分,然后輸出topK個(gè)分值最高的候選詞作為關(guān)鍵詞。根據(jù)打分的策略不同,有不同的算法,例如TF-IDF,TextRank等算法;

jieba分詞系統(tǒng)中實(shí)現(xiàn)了兩種關(guān)鍵詞抽取算法,分別是基于TF-IDF關(guān)鍵詞抽取算法和基于TextRank關(guān)鍵詞抽取算法,兩類算法均是無(wú)監(jiān)督學(xué)習(xí)的算法,下面將會(huì)通過(guò)實(shí)例講解介紹如何使用jieba分詞的關(guān)鍵詞抽取接口以及通過(guò)源碼講解其實(shí)現(xiàn)的原理。

2 示例

下面將會(huì)依次介紹利用jieba分詞系統(tǒng)中的TF-IDF及TextRank接口抽取關(guān)鍵詞的過(guò)程。

2.1 基于TF-IDF算法進(jìn)行關(guān)鍵詞抽取

基于TF-IDF算法進(jìn)行關(guān)鍵詞抽取的示例代碼如下所示,

from jieba import analyse
# 引入TF-IDF關(guān)鍵詞抽取接口
tfidf = analyse.extract_tags

# 原始文本
text = "線程是程序執(zhí)行時(shí)的最小單位,它是進(jìn)程的一個(gè)執(zhí)行流,\
    是CPU調(diào)度和分派的基本單位,一個(gè)進(jìn)程可以由很多個(gè)線程組成,\
    線程間共享進(jìn)程的所有資源,每個(gè)線程有自己的堆棧和局部變量。\
    線程由CPU獨(dú)立調(diào)度執(zhí)行,在多CPU環(huán)境下就允許多個(gè)線程同時(shí)運(yùn)行。\
    同樣多線程也可以實(shí)現(xiàn)并發(fā)操作,每個(gè)請(qǐng)求分配一個(gè)線程來(lái)處理。"

# 基于TF-IDF算法進(jìn)行關(guān)鍵詞抽取
keywords = tfidf(text)
print "keywords by tfidf:"
# 輸出抽取出的關(guān)鍵詞
for keyword in keywords:
  print keyword + "/",

控制臺(tái)輸出,

keywords by tfidf:
線程/ CPU/ 進(jìn)程/ 調(diào)度/ 多線程/ 程序執(zhí)行/ 每個(gè)/ 執(zhí)行/ 堆棧/ 局部變量/ 單位/ 并發(fā)/ 分派/ 一個(gè)/ 共享/ 請(qǐng)求/ 最小/ 可以/ 允許/ 分配/

2.2 基于TextRank算法進(jìn)行關(guān)鍵詞抽取

基于TextRank算法進(jìn)行關(guān)鍵詞抽取的示例代碼如下所示,

from jieba import analyse
# 引入TextRank關(guān)鍵詞抽取接口
textrank = analyse.textrank

# 原始文本
text = "線程是程序執(zhí)行時(shí)的最小單位,它是進(jìn)程的一個(gè)執(zhí)行流,\
    是CPU調(diào)度和分派的基本單位,一個(gè)進(jìn)程可以由很多個(gè)線程組成,\
    線程間共享進(jìn)程的所有資源,每個(gè)線程有自己的堆棧和局部變量。\
    線程由CPU獨(dú)立調(diào)度執(zhí)行,在多CPU環(huán)境下就允許多個(gè)線程同時(shí)運(yùn)行。\
    同樣多線程也可以實(shí)現(xiàn)并發(fā)操作,每個(gè)請(qǐng)求分配一個(gè)線程來(lái)處理。"

print "\nkeywords by textrank:"
# 基于TextRank算法進(jìn)行關(guān)鍵詞抽取
keywords = textrank(text)
# 輸出抽取出的關(guān)鍵詞
for keyword in keywords:
  print keyword + "/",

控制臺(tái)輸出,
keywords by textrank:
線程/ 進(jìn)程/ 調(diào)度/ 單位/ 操作/ 請(qǐng)求/ 分配/ 允許/ 基本/ 共享/ 并發(fā)/ 堆棧/ 獨(dú)立/ 執(zhí)行/ 分派/ 組成/ 資源/ 實(shí)現(xiàn)/ 運(yùn)行/ 處理/

3 理論分析

下面將會(huì)依次分析TF-IDF算法及TextRank算法的原理。

3.1 TF-IDF算法分析

在信息檢索理論中,TF-IDF是Term Frequency - Inverse Document Frequency的簡(jiǎn)寫。TF-IDF是一種數(shù)值統(tǒng)計(jì),用于反映一個(gè)詞對(duì)于語(yǔ)料中某篇文檔的重要性。在信息檢索和文本挖掘領(lǐng)域,它經(jīng)常用于因子加權(quán)。

TF-IDF的主要思想就是:如果某個(gè)詞在一篇文檔中出現(xiàn)的頻率高,也即TF高;并且在語(yǔ)料庫(kù)中其他文檔中很少出現(xiàn),即DF的低,也即IDF高,則認(rèn)為這個(gè)詞具有很好的類別區(qū)分能力。

TF-IDF在實(shí)際中主要是將二者相乘,也即TF * IDF,TF為詞頻(Term Frequency),表示詞t在文檔d中出現(xiàn)的頻率;IDF為反文檔頻率(Inverse Document Frequency),表示語(yǔ)料庫(kù)中包含詞t的文檔的數(shù)目的倒數(shù)。

TF公式:

TF計(jì)算公式為,

式中,

count(t)表示文檔di中包含詞t的個(gè)數(shù);
count(di)表示文檔di的詞的總數(shù);

IDF公式:

IDF計(jì)算公式為,
式中,

num(corpus)表示語(yǔ)料庫(kù)corpus中文檔的總數(shù);
num(t)表示語(yǔ)料庫(kù)corpus中包含t的文檔的數(shù)目;

應(yīng)用到關(guān)鍵詞抽?。?br />

1. 預(yù)處理,首先進(jìn)行分詞和詞性標(biāo)注,將滿足指定詞性的詞作為候選詞;
2. 分別計(jì)算每個(gè)詞的TF-IDF值;
3. 根據(jù)每個(gè)詞的TF-IDF值降序排列,并輸出指定個(gè)數(shù)的詞匯作為可能的關(guān)鍵詞;

3.2 TextRank算法分析

類似于PageRank的思想,將文本中的語(yǔ)法單元視作圖中的節(jié)點(diǎn),如果兩個(gè)語(yǔ)法單元存在一定語(yǔ)法關(guān)系(例如共現(xiàn)),則這兩個(gè)語(yǔ)法單元在圖中就會(huì)有一條邊相互連接,通過(guò)一定的迭代次數(shù),最終不同的節(jié)點(diǎn)會(huì)有不同的權(quán)重,權(quán)重高的語(yǔ)法單元可以作為關(guān)鍵詞。

節(jié)點(diǎn)的權(quán)重不僅依賴于它的入度結(jié)點(diǎn),還依賴于這些入度結(jié)點(diǎn)的權(quán)重,入度結(jié)點(diǎn)越多,入度結(jié)點(diǎn)的權(quán)重越大,說(shuō)明這個(gè)結(jié)點(diǎn)的權(quán)重越高;

TextRank迭代計(jì)算公式為,

WS(Vi)=(1−d)+d∗∑Vj∈In(Vi)wji∑Vk∈Out(Vj)wjk∗WS(Vj)                       

節(jié)點(diǎn)i的權(quán)重取決于節(jié)點(diǎn)i的鄰居節(jié)點(diǎn)中i-j這條邊的權(quán)重 / j的所有出度的邊的權(quán)重 * 節(jié)點(diǎn)j的權(quán)重,將這些鄰居節(jié)點(diǎn)計(jì)算的權(quán)重相加,再乘上一定的阻尼系數(shù),就是節(jié)點(diǎn)i的權(quán)重;

阻尼系數(shù) d 一般取0.85;

算法通用流程:

1. 標(biāo)識(shí)文本單元,并將其作為頂點(diǎn)加入到圖中;
2. 標(biāo)識(shí)文本單元之間的關(guān)系,使用這些關(guān)系作為圖中頂點(diǎn)之間的邊,邊可以是有向或者無(wú)向,加權(quán)或者無(wú)權(quán);
3. 基于上述公式,迭代直至收斂;
4. 按照頂點(diǎn)的分?jǐn)?shù)降序排列;

  1. 本模型使用co-occurrence關(guān)系,如果兩個(gè)頂點(diǎn)相應(yīng)的語(yǔ)義單元共同出現(xiàn)在一個(gè)窗口中(窗口大小從2-10不等),那么就連接這兩個(gè)頂點(diǎn);
  2. 添加頂點(diǎn)到圖中時(shí),需要考慮語(yǔ)法過(guò)濾,例如只保留特定詞性(如形容詞和名詞)的詞;

應(yīng)用到關(guān)鍵短語(yǔ)抽?。?br />

1. 預(yù)處理,首先進(jìn)行分詞和詞性標(biāo)注,將單個(gè)word作為結(jié)點(diǎn)添加到圖中;
2. 設(shè)置語(yǔ)法過(guò)濾器,將通過(guò)語(yǔ)法過(guò)濾器的詞匯添加到圖中;出現(xiàn)在一個(gè)窗口中的詞匯之間相互形成一條邊;
3. 基于上述公式,迭代直至收斂;一般迭代20-30次,迭代閾值設(shè)置為0.0001;
4. 根據(jù)頂點(diǎn)的分?jǐn)?shù)降序排列,并輸出指定個(gè)數(shù)的詞匯作為可能的關(guān)鍵詞;
5. 后處理,如果兩個(gè)詞匯在文本中前后連接,那么就將這兩個(gè)詞匯連接在一起,作為關(guān)鍵短語(yǔ);

4 源碼分析

jieba分詞的關(guān)鍵詞抽取功能,是在jieba/analyse目錄下實(shí)現(xiàn)的。

其中,__init__.py主要用于封裝jieba分詞的關(guān)鍵詞抽取接口;

tfidf.py實(shí)現(xiàn)了基于TF-IDF算法抽取關(guān)鍵詞;

textrank.py實(shí)現(xiàn)了基于TextRank算法抽取關(guān)鍵詞;

4.1 TF-IDF算法抽取關(guān)鍵詞源碼分析

基于TF-IDF算法抽取關(guān)鍵詞的主調(diào)函數(shù)是TFIDF.extract_tags函數(shù),主要是在jieba/analyse/tfidf.py中實(shí)現(xiàn)。

其中TFIDF是為TF-IDF算法抽取關(guān)鍵詞所定義的類。類在初始化時(shí),默認(rèn)加載了分詞函數(shù)tokenizer = jieba.dt、詞性標(biāo)注函數(shù)postokenizer = jieba.posseg.dt、停用詞stop_words = self.STOP_WORDS.copy()、idf詞典idf_loader = IDFLoader(idf_path or DEFAULT_IDF)等,并獲取idf詞典及idf中值(如果某個(gè)詞沒(méi)有出現(xiàn)在idf詞典中,則將idf中值作為這個(gè)詞的idf值)。

def __init__(self, idf_path=None):
  # 加載
  self.tokenizer = jieba.dt
  self.postokenizer = jieba.posseg.dt
  self.stop_words = self.STOP_WORDS.copy()
  self.idf_loader = IDFLoader(idf_path or DEFAULT_IDF)
  self.idf_freq, self.median_idf = self.idf_loader.get_idf()

然后開(kāi)始通過(guò)TF-IDF算法進(jìn)行關(guān)鍵詞抽取。

首先根據(jù)是否傳入了詞性限制集合,來(lái)決定是調(diào)用詞性標(biāo)注接口還是調(diào)用分詞接口。例如,詞性限制集合為["ns", "n", "vn", "v", "nr"],表示只能從詞性為地名、名詞、動(dòng)名詞、動(dòng)詞、人名這些詞性的詞中抽取關(guān)鍵詞。

1) 如果傳入了詞性限制集合,首先調(diào)用詞性標(biāo)注接口,對(duì)輸入句子進(jìn)行詞性標(biāo)注,得到分詞及對(duì)應(yīng)的詞性;依次遍歷分詞結(jié)果,如果該詞的詞性不在詞性限制集合中,則跳過(guò);如果詞的長(zhǎng)度小于2,或者詞為停用詞,則跳過(guò);最后將滿足條件的詞添加到詞頻詞典中,出現(xiàn)的次數(shù)加1;然后遍歷詞頻詞典,根據(jù)idf詞典得到每個(gè)詞的idf值,并除以詞頻詞典中的次數(shù)總和,得到每個(gè)詞的tf * idf值;如果設(shè)置了權(quán)重標(biāo)志位,則根據(jù)tf-idf值對(duì)詞頻詞典中的詞進(jìn)行降序排序,然后輸出topK個(gè)詞作為關(guān)鍵詞;

2) 如果沒(méi)有傳入詞性限制集合,首先調(diào)用分詞接口,對(duì)輸入句子進(jìn)行分詞,得到分詞;依次遍歷分詞結(jié)果,如果詞的長(zhǎng)度小于2,或者詞為停用詞,則跳過(guò);最后將滿足條件的詞添加到詞頻詞典中,出現(xiàn)的次數(shù)加1;然后遍歷詞頻詞典,根據(jù)idf詞典得到每個(gè)詞的idf值,并除以詞頻詞典中的次數(shù)總和,得到每個(gè)詞的tf * idf值;如果設(shè)置了權(quán)重標(biāo)志位,則根據(jù)tf-idf值對(duì)詞頻詞典中的詞進(jìn)行降序排序,然后輸出topK個(gè)詞作為關(guān)鍵詞;

def extract_tags(self, sentence, topK=20, withWeight=False, allowPOS=(), withFlag=False):
  # 傳入了詞性限制集合
  if allowPOS:
    allowPOS = frozenset(allowPOS)
    # 調(diào)用詞性標(biāo)注接口
    words = self.postokenizer.cut(sentence)
  # 沒(méi)有傳入詞性限制集合
  else:
    # 調(diào)用分詞接口
    words = self.tokenizer.cut(sentence)
  freq = {}
  for w in words:
    if allowPOS:
      if w.flag not in allowPOS:
        continue
      elif not withFlag:
        w = w.word
    wc = w.word if allowPOS and withFlag else w
    # 判斷詞的長(zhǎng)度是否小于2,或者詞是否為停用詞
    if len(wc.strip()) < 2 or wc.lower() in self.stop_words:
      continue
    # 將其添加到詞頻詞典中,次數(shù)加1
    freq[w] = freq.get(w, 0.0) + 1.0
  # 統(tǒng)計(jì)詞頻詞典中的總次數(shù)
  total = sum(freq.values())
  for k in freq:
    kw = k.word if allowPOS and withFlag else k
    # 計(jì)算每個(gè)詞的tf-idf值
    freq[k] *= self.idf_freq.get(kw, self.median_idf) / total
  
  # 根據(jù)tf-idf值進(jìn)行排序
  if withWeight:
    tags = sorted(freq.items(), key=itemgetter(1), reverse=True)
  else:
    tags = sorted(freq, key=freq.__getitem__, reverse=True)
  # 輸出topK個(gè)詞作為關(guān)鍵詞
  if topK:
    return tags[:topK]
  else:
    return tags

4.2 TextRank算法抽取關(guān)鍵詞源碼分析

基于TextRank算法抽取關(guān)鍵詞的主調(diào)函數(shù)是TextRank.textrank函數(shù),主要是在jieba/analyse/textrank.py中實(shí)現(xiàn)。

其中,TextRank是為TextRank算法抽取關(guān)鍵詞所定義的類。類在初始化時(shí),默認(rèn)加載了分詞函數(shù)和詞性標(biāo)注函數(shù)tokenizer = postokenizer = jieba.posseg.dt、停用詞表stop_words = self.STOP_WORDS.copy()、詞性過(guò)濾集合pos_filt = frozenset(('ns', 'n', 'vn', 'v')),窗口span = 5,(("ns", "n", "vn", "v"))表示詞性為地名、名詞、動(dòng)名詞、動(dòng)詞。

首先定義一個(gè)無(wú)向有權(quán)圖,然后對(duì)句子進(jìn)行分詞;依次遍歷分詞結(jié)果,如果某個(gè)詞i滿足過(guò)濾條件(詞性在詞性過(guò)濾集合中,并且詞的長(zhǎng)度大于等于2,并且詞不是停用詞),然后將這個(gè)詞之后窗口范圍內(nèi)的詞j(這些詞也需要滿足過(guò)濾條件),將它們兩兩(詞i和詞j)作為key,出現(xiàn)的次數(shù)作為value,添加到共現(xiàn)詞典中;

然后,依次遍歷共現(xiàn)詞典,將詞典中的每個(gè)元素,key = (詞i,詞j),value = 詞i和詞j出現(xiàn)的次數(shù),其中詞i,詞j作為一條邊起始點(diǎn)和終止點(diǎn),共現(xiàn)的次數(shù)作為邊的權(quán)重,添加到之前定義的無(wú)向有權(quán)圖中。

然后對(duì)這個(gè)無(wú)向有權(quán)圖進(jìn)行迭代運(yùn)算textrank算法,最終經(jīng)過(guò)若干次迭代后,算法收斂,每個(gè)詞都對(duì)應(yīng)一個(gè)指標(biāo)值;

如果設(shè)置了權(quán)重標(biāo)志位,則根據(jù)指標(biāo)值值對(duì)無(wú)向有權(quán)圖中的詞進(jìn)行降序排序,最后輸出topK個(gè)詞作為關(guān)鍵詞;

def textrank(self, sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'), withFlag=False):

  self.pos_filt = frozenset(allowPOS)
  # 定義無(wú)向有權(quán)圖
  g = UndirectWeightedGraph()
  # 定義共現(xiàn)詞典
  cm = defaultdict(int)
  # 分詞
  words = tuple(self.tokenizer.cut(sentence))
  # 依次遍歷每個(gè)詞
  for i, wp in enumerate(words):
    # 詞i 滿足過(guò)濾條件
    if self.pairfilter(wp):
      # 依次遍歷詞i 之后窗口范圍內(nèi)的詞
      for j in xrange(i + 1, i + self.span):
        # 詞j 不能超出整個(gè)句子
        if j >= len(words):
          break
        # 詞j不滿足過(guò)濾條件,則跳過(guò)
        if not self.pairfilter(words[j]):
          continue
        # 將詞i和詞j作為key,出現(xiàn)的次數(shù)作為value,添加到共現(xiàn)詞典中
        if allowPOS and withFlag:
          cm[(wp, words[j])] += 1
        else:
          cm[(wp.word, words[j].word)] += 1
  # 依次遍歷共現(xiàn)詞典的每個(gè)元素,將詞i,詞j作為一條邊起始點(diǎn)和終止點(diǎn),共現(xiàn)的次數(shù)作為邊的權(quán)重
  for terms, w in cm.items():
    g.addEdge(terms[0], terms[1], w)
  
  # 運(yùn)行textrank算法
  nodes_rank = g.rank()
  
  # 根據(jù)指標(biāo)值進(jìn)行排序
  if withWeight:
    tags = sorted(nodes_rank.items(), key=itemgetter(1), reverse=True)
  else:
    tags = sorted(nodes_rank, key=nodes_rank.__getitem__, reverse=True)

  # 輸出topK個(gè)詞作為關(guān)鍵詞
  if topK:
    return tags[:topK]
  else:
    return tags

其中,無(wú)向有權(quán)圖的的定義及實(shí)現(xiàn)是在UndirectWeightedGraph類中實(shí)現(xiàn)的。根據(jù)UndirectWeightedGraph類的初始化函數(shù)__init__,我們可以發(fā)現(xiàn),所謂的無(wú)向有權(quán)圖就是一個(gè)詞典,詞典的key是后續(xù)要添加的詞,詞典的value,則是一個(gè)由(起始點(diǎn),終止點(diǎn),邊的權(quán)重)構(gòu)成的三元組所組成的列表,表示以這個(gè)詞作為起始點(diǎn)的所有的邊。

無(wú)向有權(quán)圖添加邊的操作是在addEdge函數(shù)中完成的,因?yàn)槭菬o(wú)向圖,所以我們需要依次將start作為起始點(diǎn),end作為終止點(diǎn),然后再將start作為終止點(diǎn),end作為起始點(diǎn),這兩條邊的權(quán)重是相同的。

def addEdge(self, start, end, weight):
  # use a tuple (start, end, weight) instead of a Edge object
  self.graph[start].append((start, end, weight))
  self.graph[end].append((end, start, weight))

執(zhí)行textrank算法迭代是在rank函數(shù)中完成的。

首先對(duì)每個(gè)結(jié)點(diǎn)賦予相同的權(quán)重,以及計(jì)算出該結(jié)點(diǎn)的所有出度的次數(shù)之和;

然后迭代若干次,以確保得到穩(wěn)定的結(jié)果;

在每一次迭代中,依次遍歷每個(gè)結(jié)點(diǎn);對(duì)于結(jié)點(diǎn)n,首先根據(jù)無(wú)向有權(quán)圖得到結(jié)點(diǎn)n的所有入度結(jié)點(diǎn)(對(duì)于無(wú)向有權(quán)圖,入度結(jié)點(diǎn)與出度結(jié)點(diǎn)是相同的,都是與結(jié)點(diǎn)n相連的結(jié)點(diǎn)),在前面我們已經(jīng)計(jì)算出這個(gè)入度結(jié)點(diǎn)的所有出度的次數(shù),而它對(duì)于結(jié)點(diǎn)n的權(quán)值的貢獻(xiàn)等于它本身的權(quán)值 乘以 它與結(jié)點(diǎn)n的共現(xiàn)次數(shù) / 這個(gè)結(jié)點(diǎn)的所有出度的次數(shù) ,將各個(gè)入度結(jié)點(diǎn)得到的權(quán)值相加,再乘以一定的阻尼系數(shù),即可得到結(jié)點(diǎn)n的權(quán)值;

迭代完成后,對(duì)權(quán)值進(jìn)行歸一化,并返回各個(gè)結(jié)點(diǎn)及其對(duì)應(yīng)的權(quán)值。

def rank(self):
  ws = defaultdict(float)
  outSum = defaultdict(float)

  wsdef = 1.0 / (len(self.graph) or 1.0)
  # 初始化各個(gè)結(jié)點(diǎn)的權(quán)值
  # 統(tǒng)計(jì)各個(gè)結(jié)點(diǎn)的出度的次數(shù)之和
  for n, out in self.graph.items():
    ws[n] = wsdef
    outSum[n] = sum((e[2] for e in out), 0.0)

  # this line for build stable iteration
  sorted_keys = sorted(self.graph.keys())
  # 遍歷若干次
  for x in xrange(10): # 10 iters
    # 遍歷各個(gè)結(jié)點(diǎn)
    for n in sorted_keys:
      s = 0
      # 遍歷結(jié)點(diǎn)的入度結(jié)點(diǎn)
      for e in self.graph[n]:
        # 將這些入度結(jié)點(diǎn)貢獻(xiàn)后的權(quán)值相加
        # 貢獻(xiàn)率 = 入度結(jié)點(diǎn)與結(jié)點(diǎn)n的共現(xiàn)次數(shù) / 入度結(jié)點(diǎn)的所有出度的次數(shù)
        s += e[2] / outSum[e[1]] * ws[e[1]]
      # 更新結(jié)點(diǎn)n的權(quán)值
      ws[n] = (1 - self.d) + self.d * s

  (min_rank, max_rank) = (sys.float_info[0], sys.float_info[3])

  # 獲取權(quán)值的最大值和最小值
  for w in itervalues(ws):
    if w < min_rank:
      min_rank = w
    if w > max_rank:
      max_rank = w

  # 對(duì)權(quán)值進(jìn)行歸一化
  for n, w in ws.items():
    # to unify the weights, don't *100.
    ws[n] = (w - min_rank / 10.0) / (max_rank - min_rank / 10.0)

  return ws

4.3 使用自定義停用詞集合

jieba分詞中基于TF-IDF算法抽取關(guān)鍵詞以及基于TextRank算法抽取關(guān)鍵詞均需要利用停用詞對(duì)候選詞進(jìn)行過(guò)濾。實(shí)現(xiàn)TF-IDF算法抽取關(guān)鍵詞的類TFIDF和實(shí)現(xiàn)TextRank算法抽取關(guān)鍵詞的類TextRank都是類KeywordExtractor的子類。而在類KeywordExtractor,實(shí)現(xiàn)了一個(gè)方法,可以根據(jù)用戶指定的路徑,加載用戶提供的停用詞集合。
類KeywordExtractor是在jieba/analyse/tfidf.py中實(shí)現(xiàn)。

類KeywordExtractor首先提供了一個(gè)默認(rèn)的名為STOP_WORDS的停用詞集合。

然后,類KeywordExtractor實(shí)現(xiàn)了一個(gè)方法set_stop_words,可以根據(jù)用戶指定的路徑,加載用戶提供的停用詞集合。
可以將extra_dict/stop_words.txt拷貝出來(lái),并在文件末尾兩行分別加入“一個(gè)”和“每個(gè)”這兩個(gè)詞,作為用戶提供的停用詞文件,使用用戶提供的停用詞集合進(jìn)行關(guān)鍵詞抽取的實(shí)例代碼如下,

from jieba import analyse
# 引入TF-IDF關(guān)鍵詞抽取接口
tfidf = analyse.extract_tags
# 使用自定義停用詞集合
analyse.set_stop_words("stop_words.txt")

# 原始文本
text = "線程是程序執(zhí)行時(shí)的最小單位,它是進(jìn)程的一個(gè)執(zhí)行流,\
    是CPU調(diào)度和分派的基本單位,一個(gè)進(jìn)程可以由很多個(gè)線程組成,\
    線程間共享進(jìn)程的所有資源,每個(gè)線程有自己的堆棧和局部變量。\
    線程由CPU獨(dú)立調(diào)度執(zhí)行,在多CPU環(huán)境下就允許多個(gè)線程同時(shí)運(yùn)行。\
    同樣多線程也可以實(shí)現(xiàn)并發(fā)操作,每個(gè)請(qǐng)求分配一個(gè)線程來(lái)處理。"

# 基于TF-IDF算法進(jìn)行關(guān)鍵詞抽取
keywords = tfidf(text)
print "keywords by tfidf:"
# 輸出抽取出的關(guān)鍵詞
for keyword in keywords:
  print keyword + "/",

關(guān)鍵詞結(jié)果為,
keywords by tfidf:
線程/ CPU/ 進(jìn)程/ 調(diào)度/ 多線程/ 程序執(zhí)行/ 執(zhí)行/ 堆棧/ 局部變量/ 單位/ 并發(fā)/ 分派/ 共享/ 請(qǐng)求/ 最小/ 可以/ 允許/ 分配/ 多個(gè)/ 運(yùn)行/

對(duì)比章節(jié)2.1中的關(guān)鍵詞抽取結(jié)果,可以發(fā)現(xiàn)“一個(gè)”和“每個(gè)”這兩個(gè)詞沒(méi)有抽取出來(lái)。

keywords by tfidf:
線程/ CPU/ 進(jìn)程/ 調(diào)度/ 多線程/ 程序執(zhí)行/ 每個(gè)/ 執(zhí)行/ 堆棧/ 局部變量/ 單位/ 并發(fā)/ 分派/ 一個(gè)/ 共享/ 請(qǐng)求/ 最小/ 可以/ 允許/ 分配/

實(shí)現(xiàn)原理 ,這里仍然以基于TF-IDF算法抽取關(guān)鍵詞為例。

前面已經(jīng)介紹了,jieba/analyse/__init__.py主要用于封裝jieba分詞的關(guān)鍵詞抽取接口,在__init__.py首先將類TFIDF實(shí)例化為對(duì)象default_tfidf,而類TFIDF在初始化時(shí)會(huì)設(shè)置停用詞表,我們知道類TFIDF是類KeywordExtractor的子類,而類KeywordExtractor中提供了一個(gè)名為STOP_WORDS的停用詞集合,因此類TFIDF在初始化時(shí)先將類KeywordExtractor中的STOP_WORDS拷貝過(guò)來(lái),作為自己的停用詞集合stop_words。

# 實(shí)例化TFIDF類
default_tfidf = TFIDF()
# 實(shí)例化TextRank類
default_textrank = TextRank()

extract_tags = tfidf = default_tfidf.extract_tags
set_idf_path = default_tfidf.set_idf_path
textrank = default_textrank.extract_tags

# 用戶設(shè)置停用詞集合接口
def set_stop_words(stop_words_path):
  # 更新對(duì)象default_tfidf中的停用詞集合
  default_tfidf.set_stop_words(stop_words_path)
  # 更新對(duì)象default_textrank中的停用詞集合
  default_textrank.set_stop_words(stop_words_path)

如果用戶需要使用自己提供的停用詞集合,則需要調(diào)用analyse.set_stop_words(stop_words_path)這個(gè)函數(shù),set_stop_words函數(shù)是在類KeywordExtractor實(shí)現(xiàn)的。set_stop_words函數(shù)執(zhí)行時(shí),會(huì)更新對(duì)象default_tfidf中的停用詞集合stop_words,當(dāng)set_stop_words函數(shù)執(zhí)行完畢時(shí),stop_words也就是更新后的停用詞集合。我們可以做個(gè)實(shí)驗(yàn),驗(yàn)證在調(diào)用analyse.set_stop_words(stop_words_path)函數(shù)前后,停用詞集合是否發(fā)生改變。

from jieba import analyse
import copy

# 將STOP_WORDS集合深度拷貝出來(lái)
stopwords0 = copy.deepcopy(analyse.default_tfidf.STOP_WORDS)
# 設(shè)置用戶自定停用詞集合之前,將停用詞集合深度拷貝出來(lái) 
stopwords1 = copy.deepcopy(analyse.default_tfidf.stop_words)

print stopwords0 == stopwords1
print stopwords1 - stopwords0

# 設(shè)置用戶自定停用詞集合
analyse.set_stop_words("stop_words.txt")
# 設(shè)置用戶自定停用詞集合之后,將停用詞集合深度拷貝出來(lái)
stopwords2 = copy.deepcopy(analyse.default_tfidf.stop_words)

print stopwords1 == stopwords2
print stopwords2 - stopwords1

結(jié)果如下所示,

True
set([])
False
set([u'\u6bcf\u4e2a', u'\u8207', u'\u4e86', u'\u4e00\u500b', u'\u800c', u'\u4ed6\u5011', u'\u6216', u'\u7684', u'\u4e00\u4e2a', u'\u662f', u'\u5c31', u'\u4f60\u5011', u'\u5979\u5011', u'\u6c92\u6709', u'\u57fa\u672c', u'\u59b3\u5011', u'\u53ca', u'\u548c', u'\u8457', u'\u6211\u5011', u'\u662f\u5426', u'\u90fd'])

說(shuō)明:

  • 沒(méi)有加載用戶提供的停用詞集合之前,停用詞集合就是類KeywordExtractor中的STOP_WORDS拷貝過(guò)來(lái)的;
  • 加載用戶提供的停用詞集合之后,停用詞集合在原有的基礎(chǔ)上進(jìn)行了擴(kuò);

證明了我們的想法。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評(píng)論