关键词提取算法

TF-IDF(Term Frequency - Inverse Document Frequency词频-逆文档频次)

由TF算法以及IDF算法组成。

TF算法:统计一个词在一篇文档内出现的频次,单词在文档中出现的次数越多,则其对文档的表达能力就越强。

IDF算法:统计一个单词在文档集合中的多少个文档内出现过,其基本思想是(若单词在越少的文档中出现,则其对文档的区分能力也就越强)

但是,TF仅衡量了单词出现的频次,但没有考虑到单词对文档的区分程度,而IDF强调的是单词的区分能力。

详情参考:https://blog.csdn.net/baimafujinji/article/details/51476117

 

TextRank算法

基本思想来源于Google的PageRank算法,类似于“一篇论文的价值有时会取决于被引用的次数”

PageRank算法

  • 如果一个网页被很多其他网页链接到的话说明这个网页比较重要,也就是PageRank值会相对较高
  • 如果一个PageRank值很高的网页链接到一个其他的网页,那么被链接到的网页的PageRank值会相应地因此而提高

用TextRank提取来提取关键词,用PageRank的思想来解释它:

TextRank算法

  • 如果一个单词出现在很多单词后面的话,那么说明这个单词比较重要
  • 一个TextRank值很高的单词后面跟着的一个单词,那么这个单词的TextRank值会相应地因此而提高

详细参考《【TextRank】关键词提取 算法原理 公式推导 源码分析》:https://blog.csdn.net/qq_41664845/article/details/82869596

 

LSA/LSI/LDA算法

在某些场景,基于文档本身的关键词提取还不是非常足够,有些关键词并不一定会显示地出现在文档当中,如一篇讲动物生存环境的科普文,通篇介绍狮子老虎等,但是文中并没有显示地出现动物二字。

主题模型认为在词与文档之间没有直接的联系,它们应当还有一个维度将它们串联起来,主题模型将这个维度称为主题。每个文档都应该对应着一个或多个的主题,而每个主题都会有对应的词分布,通过主题,就可以得到每个文档的词分布。

目前常用的方法有LSA(LSI)和LDA,其中LSA主要采用SVD(奇异值分解)方法进行暴力破解,LDA通过贝叶斯学派方法对待提取文本进行拟合。

 

TF-IDF算法需要基于一个现成的语料库,比如TF-IDF需要逆文档率(统计每个词在语料库中的多少个文档内有出现过)

主题模型的关键词提取算法需要通过对大规模文档的学习,来发现文档的隐含主题。

TextRank算法则可以脱离语料库,仅对单片文档进行分析就可以提取该文档的关键词。

实战提取文本关键词

主要使用jieba和gensim库。jieba库中主要使用其在analyse模块封装的TextRank算法。gensim用于从原始的非结构化的文本中,无监督地学习到文本隐藏的主题向量表达,在这里我们主要调用gensim中LSI和LDA模型的接口实现。

关键词提取算法的步骤:

    1、加载已有的文档数据集
    2、加载停用词表
    3、对文档进行分词
    4、根据停用词表。过滤干扰词
    5、根据数据集训练算法

新算法训练好后,对于新的文档进行一下步骤
    1、新文档分词
    2、根据停用词表过滤干扰词
    3、根据训练好的算法提取关键词 

TD-LDF训练:根据训练文本生成单词的对应IDF值字典,后续对于已经存在的单词直接读取即可。

LSI和LDA训练 :根据训练文本数据集生成“文档-主题分布矩阵”和“主题-单词分布矩阵”,gensim中有相应的接口方法,直接调用

# -*- coding: utf-8 -*-

import math

import jieba
import jieba.posseg as psg
from gensim import corpora, models
from jieba import analyse
import functools
# 导入functools库,主要使用cmp_to_key函数,python3中sorted函数废弃了cmp参数,


# 停用词表加载方法
def get_stopword_list():
    # 停用词表存储路径,每一行为一个词,按行读取进行加载
    # 进行编码转换确保匹配准确率
    stop_word_path = './stopword.txt'
    stopword_list = [sw.replace('\n', '') for sw in open(stop_word_path,encoding="utf-8").readlines()]
    # 按行读取停用词表,并去除掉其中的“\n”
    return stopword_list
    # 最终返回一个列表

# 分词方法,调用结巴接口。pos是判断是否采用词性标注的参数
def seg_to_list(sentence, pos):
    if not pos:
        # 不进行词性标注的分词方法
        seg_list = jieba.cut(sentence)
    else:
        # 进行词性标注的分词方法
        seg_list = psg.cut(sentence)
    # 返回分词迭代器
    return seg_list


# 去除干扰词
# 根据分词结果对干扰词进行过滤,根据pos判断是否过滤除名词外的其他词性,
# 再判断词是否在停用词表中+长度是否大于2
    
def word_filter(seg_list, pos):
    # 得到停用词表,并根据是否过滤来筛选词性
    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
        '''
        str.startswith(str, beg=0,end=len(string));
        str -- 这是要检查的字符串。
        beg -- 这是可选的参数设置匹配边界的初始索引。
        end -- 这是可选的参数设置匹配边界的结束索引
        '''
        # 过滤停用词表中的词,以及长度为<2的词
        # 该词不在停用词内 + 词的长度>=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, corpus_path='./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

'''

'''
# idf值统计方法
def train_idf(doc_list):
# TF-IDF根据”训练文本“生成对应的IDF值字典,后续需要时,直接从字典读取即可
    idf_dic = {}
    # 总文档数
    tt_count = len(doc_list)

    # 统计“训练文本集合doc_list”中每个词出现的文档数量
    for doc in doc_list:
        # 训练文本doc
        for word in set(doc):
        # set集合去重,统计谋篇训练文本中单词出现的次数
            idf_dic[word] = idf_dic.get(word, 0.0) + 1.0
            # get(word, 0.0)获取word的次数,如果None,则设置为0.0;不为空,则得到真实值
            
    # 按公式转换为idf值,分母加1进行平滑处理(拉普拉斯平滑,避免有些新词在语料库中未出现,导致分母为0的情况)
    for k, v in idf_dic.items():
        # 文档集中总文档数量tt_count/(词k出现的文档数量v+1)
        idf_dic[k] = math.log(tt_count / (1.0 + v))

    # 对于没有在字典中的词,默认其仅在一个文档出现,得到默认idf值
    default_idf = math.log(tt_count / (1.0))
    
    return idf_dic, default_idf


#  排序函数,用于topK关键词的按值排序,1、按关键词的计算分值排序;2、得分相同时,根据关键词排序
def cmp(e1, e2):
    import numpy as np
    # 得到相减的计算符号
    res = np.sign(e1[1] - e2[1])
    if res != 0:
        return res
    else:
    # res==0,也就是得分相同的情况
        a = e1[0] + e2[0]
        b = e2[0] + e1[0]
        if a > b:
            return 1
        elif a == b:
            return 0
        else:
            return -1

# TF-IDF类
'''
1、根据要处理的文本,计算每个词的tf值
2、获取前面训练后的IDF数据,直接获取每个词的IDF值
3、综合计算每个词的IDF值
'''
class TfIdf(object):
    # 四个参数分别是:训练好的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值,并得到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():
        # k为单词,v为该单词出现的频次
            tf_dic[k] = float(v) / tt_count

        return tf_dic

    # 按公式计算tf-idf
    def get_tfidf(self):
        tfidf_dic = {}
        for word in self.word_list:
            # 没有在字典中的词,默认频次为1,称《default_idf》
            idf = self.idf_dic.get(word, self.default_idf)
            tf = self.tf_dic.get(word, 0)

            tfidf = tf * idf
            tfidf_dic[word] = tfidf

        tfidf_dic.items()
        
        # 根据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()

# 主题模型
'''
LSA主要步骤:
1、BOW模型将每个文档表示为向量
2、文档词向量拼接称(词-文档矩阵(m*n))
3、词-文档矩阵进行奇异值分解(SVD)操作-->[m*r][r*r][r*n]
4、根据SVD结果,词-文档矩阵继续映射到更低维度k([m*k][k*k][k*n],0

 

你可能感兴趣的:(NLP自然语言处理,关键词提取,TextRank,TF-IDF,jiebagensim)