1. 程式人生 > >NLP11-基於Gensim的文字相似性挖掘[LsiModel]

NLP11-基於Gensim的文字相似性挖掘[LsiModel]

摘要:通過對資料的抓取,基於jieba分詞,去掉停用詞,運用Gensim建立詞典,生成BOW語料,執行tfidf模型計算詞權重,採用LsiModel進行降維,最後運用Gensim提供的MatrixSimilarity類來計算兩文件的相似性【基於餘弦的距離的計算】。

1. 爬取資料

確定一個內容主題為健康資訊類, python的requests包可以對所給的資訊URL進行請求並抓取,可採用beautifulsoup來解釋,共21754篇;儲存成一行一個文件, 用分號把題目與內容分開;如下: 
這裡寫圖片描述

2. 分詞與去掉停用詞處理

3. 建立語料詞典

執行gensim的建立詞典類,共建立差不多20W左右的詞彙列表,2W多文件數,518W多詞。 
=================dictinary info============= 
詞數: 199252 
處理的文件數(num_docs): 21754 
沒有去重詞條總數(num_pos): 5188031 
================= dictinary =============

4. 生成語料詞袋錶示

把所有文件轉換成BOW形式,也即是文字轉成了向量的初步;陣列(詞id,詞頻),每行對應一個文件,對於整個語料來看其實這是一個大的稀疏矩陣。M*N,M表示文件數,N表示詞數; 
這裡寫圖片描述

5. 計算tfidf向量

主要是把重點資訊的權重增加,是屬於特徵選擇的範疇;詞頻大的表示這個詞對於這篇文章的重要性不一定大,如果這個詞對於每篇文件都多,這個詞在某篇文件就可以顯得不太重要了,對全部文件進行一個綜合計算,這樣,就應用到了tfidf模型。 
這裡寫圖片描述

6. LSA模型

面對著高維度,有199252維,建立LSA模型,把整個大的矩陣對映到100~300之間的維度,這個有相關學術研究的結果的。這裡選擇150維,如把所有語料都降維到150維,把稀疏的高階矩陣變成一個計算起來會比較輕鬆的小矩陣,也把一些沒有用的燥音給過濾掉了,這個模型可以被後來的語料查詢與分類所呼叫。 
這裡寫圖片描述

7. 文字相似應用

文字相似可以應用到文字檢索,推薦系統等場景,計算相似性的演算法比較多,一般三個思想,思想一,直接對文字進行比較,例如,判斷是否相等,判斷文件最小修改次數轉換等等;思想二,把文字轉接成區域性敏感的hash編碼,通過計算編碼的差異來判斷文字的相似性,例如simhash; 思想三,採用文字的主題模型來分析,主題相同或相似的詞、文件也是很相似的;對於計算的方法方面,各種vec距離等等都可以用;gensim求相似的用了餘弦來求解的,實現方法是先把兩向量換算成單位向量,然後作點乘。任選取第39條文件作為測試: 
這裡寫圖片描述
文字內容url:http://news.familydoctor.com.cn/a/201708/2238329.html

 
頁面顯示內容為,主要是講述了長壽,笑,心情相關等內容. 
這裡寫圖片描述

Gensim對原語料中每行作點乘,這裡只把結果大於0.6拿出來,如下顯示<文件id,相似關係值>,注意,這個文件標號為從0開始的。 
(38, 1.0), (8247, 0.82197654), (9157, 0.79860479), (6145, 0.76660019), (21559, 0.74808526), (21028, 0.71339124), (8473, 0.70313895), (2871, 0.69301218), (3876, 0.68953127), (8908, 0.68686718), (9589, 0.67818797), (4171, 0.67647427), (9164, 0.67124206), (21179, 0.66530234), (9967, 0.66434395), (9160, 0.66402805), (9171, 0.66402805), (6905, 0.66163081), (21023, 0.65950018), (3872, 0.65947652), (4163, 0.65850592), (3718, 0.65803713), (4957, 0.65741718), (9057, 0.65515375), (15104, 0.65506059), (9196, 0.64842445), (638, 0.64582705), (2042, 0.64499903), (16153, 0.644602), (6889, 0.64316607), (9607, 0.64258647), (9804, 0.64086813), (9989, 0.64086813), (20105, 0.63806242), (1171, 0.63073081), (4071, 0.62697035), (11939, 0.62295383), (368, 0.62168962), (9453, 0.61991268), (4066, 0.61936527), (21723, 0.61891901), (14141, 0.61883008), (4150, 0.6184234), (11813, 0.61639971), (9174, 0.61517316), (362, 0.61501044), (9190, 0.61155277), (4997, 0.61000597), (19149, 0.60973948), (19058, 0.6090942), (19944, 0.6090942), (20792, 0.6090942), (21325, 0.6090942), (370, 0.60723531), (9613, 0.60543227), (19320, 0.60264295), (7638, 0.60229903), (8010, 0.60081267) 
講述笑與長壽的關係,解釋為什麼可以長壽.笑可以幫助減輕壓力,促進血液迴圈,促進疾病康復等,推薦的相關文件基本也是跟這個主題相關。

8. 結果分析

相似文件01:(8247, 0.82197654)、(6145, 0.76660019),這兩篇是同一篇文章:這裡寫圖片描述 
相似文件02(9157, 0.79860479):笑起來時,大腦發生了什麼變化 
相似文件03(21559, 0.74808526):冥想15分鐘 身體能發生12個變化 
相似文件04(21028, 0.71339124):醫學家解釋人體六個怪現象 [可能這裡面涉及到笑] 
相似文件05(8473, 0.70313895), 5個小鍛鍊讓你越來越年輕[主要抗衰老的養生] 
相似文件06(2871, 0.69301218), 八個簡單方法緩解心理壓力 
相似文件07(3876, 0.68953127),有趣的人活得長 
相似文件08(8908, 0.68686718),三個特徵確實和長壽有關 
相似文件09(9589, 0.67818797), 有1種病竟然更長壽 
相似文件10(4171, 0.67647427), 鍛鍊能治拖延症;幫助人們調節心情,增加完成目標的動力 
相似文件11(9164, 0.67124206), 哪種性格的人更長壽 
相似文件12(21179, 0.66530234):愛做六件事的人不會長壽

9. 程式碼

實現的程式碼:

# -*- coding:utf-8 -*-
import string

import jieba
import jieba.analyse
from bs4 import BeautifulSoup
from gensim import corpora, models, similarities

# 判斷是否是數字
def isXiaoShu(word):
    rs = False
    a = re.search(r'^\d*\.?\d*$', word)
    if a:
        if a.group(0) == '':
            pass
        else:
            rs = True
    else:
        pass
    return rs

# 分詞
def cutPhase(inFile, outFile):
    # 如果沒有自己定義的詞典,這行不要
    jieba.load_userdict("dict_all.txt")
    # 載入停用詞
    stoplist = {}.fromkeys([line.strip() for line in open('stopwords.txt', 'r', encoding='utf-8')])
    f1 = open(inFile, 'r', encoding='utf-8')
    f2 = open(outFile, 'a', encoding='utf-8')
    line = f1.readline()
    count = 0
    while line:
        b = BeautifulSoup(line, "lxml")
        line = b.text
        # line.replace('\u3000', '').replace('\t', '').replace(' ', '')
        # 分詞
        segs = jieba.cut(line, cut_all=False)
        # 過濾停用詞
        segs = [word for word in list(segs)
                if word.lstrip() is not None
                and word.lstrip() not in stoplist
                and word.lstrip() not in string.punctuation
                and not isXiaoShu(word.lstrip())
                ]
        # 每個詞用空格隔開        
        f2.write(" ".join(segs))
        f2.write('\n')
        line = f1.readline()
        count += 1
        if count % 100 == 0:
            print(count)
    f1.close()
    f2.close()


class MyNews(object):
    def __init__(self, dict, in_file):
        self.dict = dict
        self.in_file = in_file

    def __iter__(self):
        for line in open(self.in_file, encoding='utf-8'):
            yield self.dict.doc2bow(line.split())


if __name__ == '__main__':
    is_train = True
    # 進行訓練計算模型
    if is_train:
        # 分詞
        cutPhase(inFile=u'a\資訊文章資料.txt', outFile=u"a\資訊文章資料.cut")
        # 建立詞典
        dict = corpora.Dictionary(line.lower().split() for line in open(u'a\資訊文章資料_cut.txt', encoding='utf-8'))
        dict.save('a\資訊文章資料.dic')

        # 載入詞典:建立詞袋語料
        # if is_load:
        #     dict = corpora.Dictionary.load(u'a/資訊文章資料.dic')
        print('=================dictinary info=============')
        print('詞數:', len(dict.keys()))
        print('處理的文件數(num_docs):', dict.num_docs)
        print('沒有去重詞條總數(num_pos):', dict.num_pos)
        print('=================dictinary=============')
        bows = MyNews(dict, in_file=u'a/資訊文章資料.cut')
        # 儲存詞代資訊
        corpora.MmCorpus.serialize('a/資訊文章資料.mm', bows)

        # 計算iftdf
        tfidf = models.TfidfModel(dictionary=dict)
        corpus_tfidf = tfidf[bows]
        tfidf.save(u'a/資訊文章資料.tfidf')

        # 計算lsi模型並儲存
        lsi = models.LsiModel(corpus_tfidf, id2word=dict, num_topics=150)
        lsi.save(u'a/資訊文章資料.lsi')
        # 計算所有語料
        corpus_lsi = lsi[corpus_tfidf]

        # 生成相似矩陣
        print('載入bows')
        bows = corpora.MmCorpus(u'a/資訊文章資料.mm')
        print('載入tfidf模型')
        tfidf = models.TfidfModel.load(u'a/資訊文章資料.tfidf')
        print('載入LSI模型')
        lsi = models.LsiModel.load(u'a/資訊文章資料.lsi')
        print('儲存相似矩陣')
        mSimilar = similarities.MatrixSimilarity(lsi[tfidf[bows]])
        mSimilar.save(u'a/資訊文章資料.mSimilar')
    # 應用模型,相關的查詢
    else:
        print('載入詞典')
        dict = corpora.Dictionary.load(u'a/資訊文章資料.dic')
        print('載入tfidf模型')
        tfidf = models.TfidfModel.load(u'a/資訊文章資料.tfidf')
        print('載入LSI模型')
        lsi = models.LsiModel.load(u'a/資訊文章資料.lsi')
        mSimilar = similarities.MatrixSimilarity.load(u'a/資訊文章資料.mSimilar')

        # 任先一句分好詞的文件
        doc = """  長壽 祕方 疫力 ... 補品 笑一笑 增 壽命 心情  """
        # 把測試語料轉成詞袋向量
        vec_bow = dict.doc2bow(doc.split())
        # 求tfidf值
        vec_tfidf = tfidf[vec_bow]
        # 轉成lsi向量
        vec_lsi = lsi[vec_tfidf]
        # 求解相似性文件
        sims = mSimilar[vec_lsi]
        print('排序後的結果:')
        sims = sorted(enumerate(sims), key=lambda item: -item[1])
        print(sims)

這是一個文字相似性挖掘粗略過程,請大家多多指教。