带你全面剖析python自然语言处理(TF-IDF和TextRank)

共 8831字,需浏览 18分钟

 ·

2022-06-07 13:19

前面已经做了两期python自然语言处理的相关文章,跳转连接:

带你全面剖析python自然语言处理(NLP系列一)

带你全面剖析python自然语言处理(NLP技术要点)


🚩TF-IDF和TextRank

在确保文本文件可以被Python正确读取后,我们可以开始导入jieba.analyse进行分析:

from jieba.analyse import * with open('sample.txt','r') as f:   # 'r'是只读,a为覆盖,w为覆盖写入    data = f.read() # 读取sample.txt

具体实现可以通过一下两种方法:

⭐️TF-IDF

TF-IDF全称是Term Frequency - inverse document frequency,也就是说TF-IDF是在通过TF(词频)以及IDF(逆文档频率)两个指标来共同决定某个词语的重要程度。

词频(Term Frequency)是指某个词语出现的频率。如果某个词语出现的次数多,也就说明这个词语重要性可能会很高。但是在这样的计算下,很多汉字中的虚词——“的,地,得”可能在文中出现非常多次,而它们可能大概率并不是关键词。为了将它们排除在我们的关键词列表里,我们引入逆文档频率(Inverse Document Frequency)。

假设一共用10篇文档,某个词语A在10篇文章中都出现过,而另一个词语B可能只在其中3篇文章中出现,这个时候按照IDF的逻辑我们会倾向于认为词语B更关键,而词语A更有可能是在语句中扮演辅助成分,或作为虚词出现在各个文章中。那么实现如下:

# TF-IDFfor keyword, weight in extract_tags(data, topK=10, withWeight=True): # 输出10个关键词    print('%s %s' % (keyword, weight))

⭐️TextRank

TextRank首先会提取词汇,形成节点;然后依据词汇的关联,建立链接。依照连接节点的多少,给每个节点赋予一个初始的权重数值。然后就开始迭代。根据某个词所连接所有词汇的权重,重新计算该词汇的权重,然后把重新计算的权重传递下去。直到这种变化达到均衡态,权重数值不再发生改变。这与Google的网页排名算法PageRank,在思想上是一致的。

# TextRankfor keyword, weight in textrank(data, withWeight=True):    print('%s %s' % (keyword, weight))

⭐️基于 TF-IDF 算法的关键词抽取

import jieba.analyse

jieba.analyse.extract_tags(sentence, topK=20, withWeight=False, allowPOS=())

sentence:为待提取的文本。

topK:为返回几个TF/IDF权重最大的关键词,默认值为20

allowPOS:仅包括指定词性的词,默认值为空,即不筛选。

jieba.analyse.TFIDF(idf_path=None):新建?TFIDF?实例,idf_path为 IDF频率文件

import syssys.path.append('../')import jiebaimport jieba.analysefrom optparse import OptionParserUSAGE = "usage:    python extract_tags.py [file name] -k [top k]"parser = OptionParser(USAGE)parser.add_option("-k", dest="topK")opt, args = parser.parse_args() if len(args) < 1:    print(USAGE)    sys.exit(1)file_name = args[0]if opt.topK is None:    topK = 10else:    topK = int(opt.topK)content = open(file_name, 'rb').read()tags = jieba.analyse.extract_tags(content, topK=topK)print(",".join(tags))

可以在Sklearn中调用TFIDFVectorizer库实现TF-IDF算法,并且可以通过stopwords参数来设置文档中的停用词(没有具体意义的词,如助词,语气词等),使得停用词不纳入计算范围,提高算法的精确性。

使用TFIDFVectorizer库:

from sklearn.feature_extraction.text import TfidfVectorizercorpus = [    'This is the first document.',    'This document is the second document.',    'And this is the third one.',    'Is this the first document?',]vectorizer = TfidfVectorizer()tdm = vectorizer.fit_transform(corpus)space = vectorizer.vocabulary_print(space)

详细参数和函数的解释可以参考官网:https://scikit-learn.org/stable

⭐️基于 TextRank 算法的关键词抽取

1) jieba.analyse.textrank(sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')) 直接使用,接口相同,注意默认过滤词性。

2) jieba.analyse.TextRank()新建自定义 TextRank 实例

算法论文:TextRank: Bringing Order into Texts

例如:

from __future__ import unicode_literalsimport syssys.path.append("../")import jiebaimport jieba.possegimport jieba.analyseprint('='*40)print('1. 分词')print('-'*40)seg_list = jieba.cut("我来到北京清华大学", cut_all=True)print("Full Mode: " + "/ ".join(seg_list))  # 全模式seg_list = jieba.cut("我来到北京清华大学", cut_all=False)print("Default Mode: " + "/ ".join(seg_list))  # 默认模式seg_list = jieba.cut("他来到了网易杭研大厦")print(", ".join(seg_list))seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式print(", ".join(seg_list))print('='*40)print('2. 添加自定义词典/调整词典')print('-'*40)print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))#如果/放到/post/中将/出错/。print(jieba.suggest_freq(('中', '将'), True))#494print('/'.join(jieba.cut('如果放到post中将出错。', HMM=False)))#如果/放到/post/中/将/出错/。print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))#「/台/中/」/正确/应该/不会/被/切开print(jieba.suggest_freq('台中', True))#69print('/'.join(jieba.cut('「台中」正确应该不会被切开', HMM=False)))#「/台中/」/正确/应该/不会/被/切开print('='*40)print('3. 关键词提取')print('-'*40)print(' TF-IDF')print('-'*40)s = "此外,公司拟对全资子公司吉林欧亚置业有限公司增资4.3亿元,增资后,吉林欧亚置业注册资本由7000万元增加到5亿元。吉林欧亚置业主要经营范围为房地产开发及百货零售等业务。目前在建吉林欧亚城市商业综合体项目。2013年,实现营业收入0万元,实现净利润-139.13万元。"for x, w in jieba.analyse.extract_tags(s, withWeight=True):    print('%s %s' % (x, w))print('-'*40)print(' TextRank')print('-'*40)for x, w in jieba.analyse.textrank(s, withWeight=True):    print('%s %s' % (x, w))print('='*40)print('4. 词性标注')print('-'*40)words = jieba.posseg.cut("我爱北京天安门")for word, flag in words:    print('%s %s' % (word, flag))print('='*40)print('6. Tokenize: 返回词语在原文的起止位置')print('-'*40)print(' 默认模式')print('-'*40)result = jieba.tokenize('永和服装饰品有限公司')for tk in result:    print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2]))print('-'*40)print(' 搜索模式')print('-'*40)result = jieba.tokenize('永和服装饰品有限公司', mode='search')for tk in result:    print("word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2]))

运行结果:

========================================1. 分词----------------------------------------Building prefix dict from the default dictionary ...Dumping model to file cache C:\Users\Y\AppData\Local\Temp\jieba.cacheLoading model cost 2.162 seconds.Prefix dict has been built successfully.Full Mode: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学Default Mode: 我/ 来到/ 北京/ 清华大学他, 来到, 了, 网易, 杭研, 大厦小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, ,, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造========================================2. 添加自定义词典/调整词典----------------------------------------如果/放到/post/中将/出错/。494如果/放到/post/中/将/出错/。「/台/中/」/正确/应该/不会/被/切开69「/台中/」/正确/应该/不会/被/切开========================================3. 关键词提取---------------------------------------- TF-IDF----------------------------------------欧亚 0.7300142700289363吉林 0.659038184373617置业 0.4887134522112766万元 0.3392722481859574增资 0.335824019852340454.3 0.254356755380851067000 0.254356755380851062013 0.25435675538085106139.13 0.25435675538085106实现 0.19900979900382978综合体 0.19480309624702127经营范围 0.19389757253595744亿元 0.1914421623587234在建 0.17541884768425534全资 0.17180164988510638注册资本 0.1712441526百货 0.16734460041382979零售 0.1475057117057447子公司 0.14596045237787234营业 0.13920178509021275---------------------------------------- TextRank----------------------------------------吉林 1.0欧亚 0.9966893354178172置业 0.6434360313092776实现 0.5898606692859626收入 0.43677859947991454增资 0.4099900531283276子公司 0.35678295947672795城市 0.34971383667403655商业 0.34817220716026936业务 0.3092230992619838在建 0.3077929164033088营业 0.3035777049319588全资 0.303540981053475综合体 0.29580869172394825注册资本 0.29000519464085045有限公司 0.2807830798576574零售 0.27883620861218145百货 0.2781657628445476开发 0.2693488779295851经营范围 0.2642762173558316========================================4. 词性标注----------------------------------------我 r爱 v北京 ns天安门 ns========================================6. Tokenize: 返回词语在原文的起止位置---------------------------------------- 默认模式----------------------------------------word 永和     start: 0      end:2word 服装     start: 2      end:4word 饰品     start: 4      end:6word 有限公司     start: 6      end:10---------------------------------------- 搜索模式----------------------------------------word 永和     start: 0      end:2word 服装     start: 2      end:4word 饰品     start: 4      end:6word 有限     start: 6      end:8word 公司     start: 8      end:10word 有限公司     start: 6      end:10>>>

🚩TF-IDF结合余弦相似度做相似度分析

为了找出相似的文章,需要用到"余弦相似性"(cosine similiarity)

下面,举一个例子来说明什么是"余弦相似性":

为了简单起见,我们先从句子着手。

  句子A:我喜欢看电视,不喜欢看电影。
  句子B:我不喜欢看电视,也不喜欢看电影。

请问怎样才能计算上面两句话的相似程度?

基本思路是:如果这两句话的用词越相似,它们的内容就应该越相似。因此,可以从词频入手,计算它们的相似程度。

第一步,分词。
  句子A:我/喜欢/看/电视,不/喜欢/看/电影。
  句子B:我/不/喜欢/看/电视,也/不/喜欢/看/电影。

第二步,列出所有的词。
  我,喜欢,看,电视,电影,不,也。

第三步,计算词频。
  句子A:我 1,喜欢 2,看 2,电视 1,电影 1,不 1,也 0。
  句子B:我 1,喜欢 2,看 2,电视 1,电影 1,不 2,也 1。

第四步,写出词频向量。
  句子A:[1, 2, 2, 1, 1, 1, 0]
  句子B:[1, 2, 2, 1, 1, 2, 1]

到这里,问题就变成了如何计算这两个向量的相似程度。

可以把它们想象成空间中的两条线段,都是从原点([0, 0, ...])出发,指向不同的方向。两条线段之间形成一个夹角,如果夹角为 0 度,意味着方向相同、线段重合;如果夹角为 90 度,意味着形成直角,方向完全不相似;如果夹角为 180 度,意味着方向正好相反。因此,我们可以通过夹角的大小,来判断向量的相似程度。夹角越小,就代表越相似。

以二维空间为例,a和b是两个向量,计算它们的夹角θ。余弦定理用下面的公式求得:

假定a向量是[x1, y1],b向量是[x2, y2],那么可以将余弦定理改写成下面的形式:

 

 

数学家已经证明,余弦的这种计算方法对n维向量也成立。假定A和B是两个n维向量,A是 [A1, A2, ..., An] ,B是 [B1, B2, ..., Bn] ,则A与B的夹角θ的余弦等于:

使用这个公式,我们就可以得到,句子A与句子B的夹角的余弦,计算结果如下:

余弦值越接近1,就表明夹角越接近 0 度,也就是两个向量越相似,这就叫”余弦相似性”。所以,上面的句子A和句子B是很相似的,事实上它们的夹角大约为 20.3 度。

余弦相似度计算结果区间:[-1 , 1]

两个句子的相似度计算的步骤是:

(1)使用 TF-IDF 算法,找出两篇文章的关键词;

(2)每篇文章各取出若干个关键词(比如 20 个),合并成一个集合,计算每篇文章对于这个集合中的词的词频(为了避免文章长度的差异,可以使用相对词频);

(3)生成两篇文章各自的词频向量;

(4)计算两个向量的余弦相似度,值越大就表示越相似。

利用python计算余弦相似度:

def cos_sim(a,b):

"""

计算a,b向量的余弦相似度

@param a: 1*m的向量

@param b: n*m的矩阵

@return: 1*n的值,每个样本的bi与a的余弦相似度

"""

    cos_result = np.dot(a, b.T) / np.sqrt(np.sum(b ** 2, axis=1)) /         np.sqrt(np.dot(a, a.T))

    return cos_result

import numpy as np

def cosine_similarity(x,y):

    num = x.dot(y.T)

    denom = np.linalg.norm(x) * np.linalg.norm(y)

    return num / denom

输入两个np.array向量,计算余弦函数的值

cosine_similarity(np.array([0,1,2,3,4]),np.array([5,6,7,8,9]))#0.9146591207600472 cosine_similarity(np.array([1,1]),np.array([2,2]))#0.9999999999999998 cosine_similarity(np.array([0,1]),np.array([1,0]))#0.0

推荐阅读

(点击标题可跳转阅读)

python程序设计思想

【初学不要怕】教你全方位理解python函数及其使用(包括lambda函数和递归函数详解系列)

【加解密算法实现】全面剖析RSA加解密算法(附完整C/Python源码)

详解python的运行方式

强推10款Python常用的开发工具

【专家推荐】保姆级开源工具推荐,一用一个爽,非常劲爆(收藏系列)

【恭喜考研拟录取】极力推荐科研必备软件,让你科研生涯事半功倍

老铁,三连支持一下,好吗?↓↓↓


点分享

点点赞

点在看



浏览 118
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报