1. 程式人生 > >NLP----關鍵詞提取演算法(TextRank,TF/IDF)

NLP----關鍵詞提取演算法(TextRank,TF/IDF)

參考書目:python自然語言處理實戰——核心技術與演算法

TF/IDF

基本思想:TF是計算一個詞在一篇文件中出現的頻率,IDF是一個詞在多少篇文件中出現過,顯然TF越高證明這個詞在這篇文章中的代表性就越強,而INF越低則證明這個詞在具有越強的區分能力。因此中和這兩個數,就能較好地算出文件的關鍵詞。

關鍵公式

tf*idf(i,j)=tf_{ij}*idf_i=\frac{n_{ij}}{\sum _kn_{kj}}*log(\frac{|D|}{1+|D_i|})

|D_i|是文件中出現詞i的文件數量,|D|是文件數

附上書上抄來的程式碼

import jieba
import jieba.posseg as psg
import math
import functools

# 停用詞表載入方法


def get_stopword_list():
    # 停用詞表儲存路徑,每一行為一個詞,按行讀取進行載入
    # 進行編碼轉換確保匹配準確率
    stop_word_path = './data/stopword.txt'
    stopword_list = [sw.replace('\n', '')
                     for sw in open(stop_word_path,encoding = 'utf-8').readlines()]
    return stopword_list

# 分詞方法,呼叫結巴介面


def seg_to_list(sentence, pos=False):
    if not pos:
        # 不進行詞性標註的分詞方法
        seg_list = jieba.cut(sentence)
    else:
        # 進行詞性標註的分詞方法
        seg_list = psg.cut(sentence)
    return seg_list

# 去幹擾詞


def word_filter(seg_list, pos=False):
    stopword_list = get_stopword_list()
    filter_list = []
    # 根據POS引數選擇是否詞性過濾
    # 不進行詞性過濾,則將詞性都標記為n,表示全部保留
    for seg in seg_list:
        if not pos:
            word = seg
            flag = 'n'
        else:
            word = seg.word
            flag = seg.flag
        if not flag.startswith('n'):
            continue
        # 過濾高停用詞表中的詞,以及長度<2的詞
        if not word in stopword_list and len(word) > 1:
            filter_list.append(word)

    return filter_list

# 資料載入,pos為是否詞性標註的引數,corpus_path為資料集路徑


def load_data(pos=False, corpus_path='./data/corpus.txt'):
    # 呼叫上面方法對資料集進行處理,處理後的每條資料僅保留非干擾詞
    doc_list = []
    for line in open(corpus_path, 'r',encoding = 'utf-8'):
        content = line.strip()
        seg_list = seg_to_list(content, pos)
        filter_list = word_filter(seg_list, pos)
        doc_list.append(filter_list)
    return doc_list


def train_idf(doc_list):
    idf_dic = {}
    #總文件數
    tt_count = len(doc_list)
    
    #每個詞出現的文件數
    for doc in doc_list:
        for word in set(doc):
            idf_dic[word] = idf_dic.get(word,0.0)+1.0

    #按公示轉換為idf值,分母加一進行平滑處理
    for k,v in idf_dic.items():
        idf_dic[k]=math.log(tt_count/(1.0+v))
    
    #對於沒有在字典中的詞,預設其僅在一個文件中出現,得到預設idf值
    default_idf = math.log(tt_count/1.0)
    return idf_dic,default_idf

def cmp(e1,e2):
    import numpy as np
    res = np.sign(e1[1]-e2[1])
    if res != 0:
        return res
    else:
        a = e1[0]+e2[0]
        b = e2[0]+e1[0]
        if a>b:
            return 1
        elif a == b:
            return 0
        else:
            return -1

class TfIdf(object):
    #統計tf值
    def get_tf_dic(self):
        tf_dic = {}
        for word in self.word_list:
            tf_dic[word] = tf_dic.get(word,0.0)+1.0
        
        tt_count = len(self.word_list)
        for k,v in tf_dic.items():
            tf_dic[k] = float(v)/tt_count

        return tf_dic
    
    #四個引數分別是:訓練好的idf字典,預設idf值,處理後的待提取文字,關鍵詞數量
    def __init__(self,idf_dic,default_idf,word_list,keyword_num):
        self.word_list = word_list
        self.idf_dic,self.default_idf = idf_dic,default_idf
        self.tf_dic  = self.get_tf_dic()
        self.keyword_num = keyword_num

    #按公式計算tf_idf
    def get_tfidf(self):
        tfidf_dic = {}
        for word in self.word_list:
            idf = self.idf_dic.get(word,self.default_idf)
            tf = self.tf_dic.get(word,0)

            tfidf  = tf*idf
            tfidf_dic[word] = tfidf

            #根據tf_idf排序,取排名前keyword_num的詞作為關鍵詞
        for k ,v in sorted(tfidf_dic.items(),key=functools.cmp_to_key(cmp),reverse=True)[:self.keyword_num]:
            print(k+"/",end='')
        print()

def tfidf_extract(word_list, pos=False, keyword_num=10):
    doc_list = load_data(pos)
    idf_dic, default_idf = train_idf(doc_list)
    tfidf_model = TfIdf(idf_dic, default_idf, word_list, keyword_num)
    tfidf_model.get_tfidf()

if __name__ == '__main__':
    text = '6月19日,《2012年度“中國愛心城市”公益活動新聞釋出會》在京舉行。' + \
           '中華社會救助基金會理事長許嘉璐到會講話。基金會高階顧問朱發忠,全國老齡' + \
           '辦副主任朱勇,民政部社會救助司助理巡視員周萍,中華社會救助基金會副理事長耿志遠,' + \
           '重慶市民政局巡視員譚明政。晉江市人大常委會主任陳健倩,以及10餘個省、市、自治區民政局' + \
           '領導及四十多家媒體參加了釋出會。中華社會救助基金會祕書長時正新介紹本年度“中國愛心城' + \
           '市”公益活動將以“愛心城市宣傳、孤老關愛救助專案及第二屆中國愛心城市大會”為主要內容,重慶市' + \
           '、呼和浩特市、長沙市、太原市、蚌埠市、南昌市、汕頭市、滄州市、晉江市及遵化市將會積極參加' + \
           '這一公益活動。中國雅虎副總編張銀生和鳳凰網城市頻道總監趙耀分別以各自媒體優勢介紹了活動' + \
           '的宣傳方案。會上,中華社會救助基金會與“第二屆中國愛心城市大會”承辦方晉江市簽約,許嘉璐理' + \
           '事長接受晉江市參與“百萬孤老關愛行動”向國家重點扶貧地區捐贈的價值400萬元的款物。晉江市人大' + \
           '常委會主任陳健倩介紹了大會的籌備情況。'

    pos = True
    seg_list = seg_to_list(text, pos)
    filter_list = word_filter(seg_list, pos)

    print('TF-IDF模型結果:')
    tfidf_extract(filter_list)

TextRank

基本思路:每個詞將自己的分數平均投給附近的詞,迭代至收斂或指定次數即可,初始分可以打1

附上程式碼

def get_stopword_list():
    path = './data/stop_words.utf8'
    stopword_list = [sw.replace('\n','') for sw in open(path,'r',encoding='utf8').readlines()]
    return stopword_list

def seg2list(text):
    import jieba
    return jieba.cut(text)

def word_filter(seg_list):
    stopword_list = get_stopword_list()
    filter_list = []
    for w in seg_list:
        if not w in stopword_list and len(w)>1:
            filter_list.append(w)
    return filter_list


str = '6月19日,《2012年度“中國愛心城市”公益活動新聞釋出會》在京舉行。' + \
           '中華社會救助基金會理事長許嘉璐到會講話。基金會高階顧問朱發忠,全國老齡' + \
           '辦副主任朱勇,民政部社會救助司助理巡視員周萍,中華社會救助基金會副理事長耿志遠,' + \
           '重慶市民政局巡視員譚明政。晉江市人大常委會主任陳健倩,以及10餘個省、市、自治區民政局' + \
           '領導及四十多家媒體參加了釋出會。中華社會救助基金會祕書長時正新介紹本年度“中國愛心城' + \
           '市”公益活動將以“愛心城市宣傳、孤老關愛救助專案及第二屆中國愛心城市大會”為主要內容,重慶市' + \
           '、呼和浩特市、長沙市、太原市、蚌埠市、南昌市、汕頭市、滄州市、晉江市及遵化市將會積極參加' + \
           '這一公益活動。中國雅虎副總編張銀生和鳳凰網城市頻道總監趙耀分別以各自媒體優勢介紹了活動' + \
           '的宣傳方案。會上,中華社會救助基金會與“第二屆中國愛心城市大會”承辦方晉江市簽約,許嘉璐理' + \
           '事長接受晉江市參與“百萬孤老關愛行動”向國家重點扶貧地區捐贈的價值400萬元的款物。晉江市人大' + \
           '常委會主任陳健倩介紹了大會的籌備情況。'
win={}
seg_list = seg2list(str)
filter_list = word_filter(seg_list)
#構建投分表,根據視窗
for i in range(len(filter_list)):
    if filter_list[i] not in win.keys():
        win[filter_list[i]]=set()
    if i-5 < 0:
        lindex = 0
    else:
        lindex = i-5
    for j in filter_list[lindex:i+5]:
        win[filter_list[i]].add(j)

# 投票
time = 0
score = {w:1.0 for w in filter_list}
while(time<50):
    for k,v in win.items():
        s = score[k]/len(v)
        score[k] = 0
        for i in v:
            score[i]+=s
    time+=1

l = sorted(score.items(), key=lambda score:score[1],reverse=True)
print(l)