『关键词挖掘』结合 LDA + Word2Vec + TextRank 实现关键词的挖掘

利用 Python,结合 LDA + Word2Vec + Pagerank 实现关键词的挖掘。先用 LDA 方法初步选择出主题及其词分布,接着将每个主题下的词表示为词向量,用相似性表示词与词之间的权重,最后用 TextRank 方法对于主题下的关键词进行二次过滤。

文章导航

  • 实现思路
  • 先前准备
  • 牛刀小试
  • 计算TR值
  • 完整代码
  • 效果图


实现思路

文献:融合主题词嵌入和网络结构分析的主题关键词提取方法

主要方法词向量(Word2Vec) + 主题模型(LDA) + 关键词网络分析

这篇文献提供了一种 关键词提取 的思路:

  • 首先利用 LDA 对于数据集的主题进行初步提取,生成 主题 - 词 (m×n) 矩阵;

  • 接着,用 Word2Vec 训练数据集,得到词向量模型;

  • For each t in Topic (m):

    • For each w in KeyWords (n):

      • 利用生成的词向量模型,用余弦法计算该主题下词与词的相似度,作为两点之间的权重;

      • 设置阈值,过滤掉权重较低的词关系,其余的两两词之间连成一条边;

      • 利用 PageRank 方法进行迭代,最后输出 PR 值最高的 TopN 个词作为该主题下的关键词。
        『关键词挖掘』结合 LDA + Word2Vec + TextRank 实现关键词的挖掘_第1张图片

总结:先用 LDA 方法初步选择出主题及其词分布,接着将每个主题下的词表示为词向量,用相似性表示词与词之间的权重,最后用 PageRank 方法对于主题下的关键词进行二次过滤。


先前准备

在开始关键词提取之前,我希望您已经准备好了以下条件:

  • 已经安装好了 numpy 包:pip install numpy
  • 已经用 LDA 得到了 主题-词 分布
  • 已经训练好了 Word2Vec 模型

如果有疑问,请参考我之前的文章:【用Word2Vec训练中文词向量】 【用Python实现主题模型LDA】

如果一切准备就绪,那就开始吧!!!


牛刀小试

在这一步,主要实现了 参数定义 + 文件读取 + 加载模型 的步骤。

首先是定义函数,主要传入三个参数:

  • simvalue 给定一个最小的相似性值,若词与词之间的相似性大于该值,则构建词与词之间的边
  • alpha 计算 TextRank 时所用,一般为 0.85
  • iter_num 迭代次数
    def __init__(self, simvalue, alpha, iter_num):
        self.word_list = []  # 记录主题-词模型
        self.edge_dict = {}  # 记录节点的边连接字典
        self.simvalue = simvalue  # 满足该最小相似性值,词与词之间可构成边
        self.alpha = alpha
        self.iter_num = iter_num  # 迭代次数

接下来是读取 主题-词 文档,保存在" topicword.txt "。每一行为一个主题,每个主题下有30个词,保存在 word_list 中。我一共分了5个主题。
在这里插入图片描述

    # 读取文档,返回一个总的主题-词列表
    def readFile(self):
        with open("topicword.txt", "r", encoding='utf-8') as tw:
            for line in tw:
                self.word_list.append(line.strip().split(" "))
        # print(self.word_list)
        return self.word_list

在这一步的最后,是要加载之前训练好的 Word2Vec 模型。

# 加载Word2Vec模型
    def loadModel(self):
    	self.path = "word2vec.model"  # Word2Vec模型路径
        self.model = word2vec.Word2Vec.load(self.path)
        print("模型加载完成")
        return self.model

计算TR值

  • (1) 首先根据词语之间相似性,构建每个词的相邻词,过滤掉相似性较小的词,返回边的集合
    『关键词挖掘』结合 LDA + Word2Vec + TextRank 实现关键词的挖掘_第2张图片
    这个图可以帮助理解。对于每一个主题,判断每一个词在不在定义的词典内。若不在词典内,说明还未进行边的连接。对于这个 word ,遍历该主题内的每一个词,计算该词与其余词的向量相似性。若该相似性大于事先定义的 simvalue ,则将其加入 tmp_set。假定 word1 最后返回的 tmp_set 为 tmp_set = { Word0, Word2 },表明 word1 与 word0 和 word2 建立了边连接。最后将它保存到字典中,形式为 { Word1 : Word0, Word 2 }
    def calTR(self):

        # 首先根据词语之间相似性,构建每个词的相邻词,过滤掉相似性较小的词,返回边的集合
        word_list_len = len(self.word_list)  # 主题个数
        term_num = 30  # 每个主题下词的个数
        names = globals()
        for list, t in zip(self.word_list, range(word_list_len)):  # list表示每一个主题-词,t为主题序数,即第几个主题
            names['self.edge_dict_' + str(t)] = {}  # 为每一个主题建立一个词典,如第一个主题的词典名称为 self.edge_dict_0
            for index, word in enumerate(list):  # 枚举可以同时遍历索引和值
                if word not in names.get('self.edge_dict_' + str(t)).keys():  # 表明该词还未进行边的连接
                    tmp_set = set()  # set()函数创建一个无序不重复元素集
                    for i in range(term_num):
                        if i == index:
                            continue  # 若为该单词,则跳出本次循环
                        word0 = list[i]
                        similarity = self.model.wv.similarity(word, word0)  # 计算当前词与剩余词语的相似性
                        if similarity > self.simvalue:
                            tmp_set.add(word0)
                    #  返回一个词的相邻节点
                    names.get('self.edge_dict_' + str(t))[word] = tmp_set
                    

以第0个主题为例,返回的形式为 self.edge_dict_0 = {'Word0' : { 'Word1, Word 5'}, 'Word1' : {'Word0, Word 2'}......}

  • (2) 接下来是构建矩阵和归一化。通过这张图就可以很好理解,刚开始用0初始化矩阵,接着对于每一个词,例如 word1,它的相关词为 word0word2,则分别计算它们两两相似度,将相似度更新到矩阵中。最后再对矩阵进行归一化。
    『关键词挖掘』结合 LDA + Word2Vec + TextRank 实现关键词的挖掘_第3张图片
			# 根据边的相连关系,构建矩阵
            # 用0来初始化矩阵
            names['self.matrix_' + str(t)] = np.zeros([len(set(list)), len(set(list))])
            self.word_index = {}  # 记录词的index
            self.index_dict = {}  # 记录节点index对应的词
            for i, v in enumerate(set(list)):
                self.word_index[v] = i  # 用词来找到它的索引
                self.index_dict[i] = v  # 用索引来找到它的词
            for key in names.get('self.edge_dict_' + str(t)).keys():  # 对于字典里的每一个节点
                for w in names.get('self.edge_dict_' + str(t))[key]:  # 对于每一个节点的相邻点
                    # 相邻节点的边权重变为向量相似度
                    names.get('self.matrix_' + str(t))[self.word_index[key]][self.word_index[w]] = self.model.wv.similarity(key, w)
                    names.get('self.matrix_' + str(t))[self.word_index[w]][self.word_index[key]] = self.model.wv.similarity(w, key)
            # print(t)
            # print(names.get('self.matrix_' + str(t)))

        # 归一化
            for j in range(names.get('self.matrix_' + str(t)).shape[1]):  # 对第j列元素
                sum = 0
                for i in range(names.get('self.matrix_' + str(t)).shape[0]):  # 对第i行元素
                    sum += names.get('self.matrix_' + str(t))[i][j]
                for i in range(names.get('self.matrix_' + str(t)).shape[0]):
                    names.get('self.matrix_' + str(t))[i][j] /= sum
            # print(t)
            # print(names.get('self.matrix_' + str(t)))
  • (3) 接下来进行的是TR值的计算以及输出。TR值计算时运用了公式,输出时对一个主题下每个词的TR值进行降序排列。
			# 根据textrank公式计算权重
            self.TR = np.ones([len(set(list)), 1])
            for i in range(self.iter_num):
                self.TR = (1 - self.alpha) + self.alpha * np.dot(names.get('self.matrix_' + str(t)), self.TR)
            print("主题#%d:" % t)
            #print(self.TR)

            # 输出词和相应的权重
            word_pr = {}
            for i in range(len(self.TR)):
                word_pr[self.index_dict[i]] = self.TR[i][0]
            # sorted() 函数对所有可迭代的对象进行排序操作,返回的是一个新的 list,key指用来进行比较的元素,reverse为排序规则  ,True降序 ,False升序(默认)
            res = sorted(word_pr.items(), key=lambda x: x[1], reverse=True)
            # print(res)
            list = []
            for n in range(len(res)):
                list.append(res[n][0])
            print(list)

完整代码

import gensim.models as word2vec
import numpy as np
from gensim.models.word2vec import LineSentence


class TextRank(object):

    def __init__(self, simvalue, alpha, iter_num):
        self.word_list = []  # 记录主题-词模型
        self.edge_dict = {}  # 记录节点的边连接字典
        self.simvalue = simvalue  # 满足该最小相似性值,词与词之间可构成边
        self.alpha = alpha
        self.iter_num = iter_num  # 迭代次数

    # 读取文档,返回一个总的主题-词列表
    def readFile(self):
        with open("topicword.txt", "r", encoding='utf-8') as tw:
            for line in tw:
                self.word_list.append(line.strip().split(" "))
        # print(self.word_list)
        return self.word_list

    # 加载Word2Vec模型
    def loadModel(self):
        self.path = "word2vec_hotel.model"  # Word2Vec模型路径
        self.model = word2vec.Word2Vec.load(self.path)
        print("模型加载完成")
        return self.model

    # 计算TR值
    def calTR(self):

        # 首先根据词语之间相似性,构建每个词的相邻词,过滤掉相似性较小的词,返回边的集合
        word_list_len = len(self.word_list)  # 主题个数
        term_num = 30  # 每个主题下词的个数
        names = globals()
        for list, t in zip(self.word_list, range(word_list_len)):  # list表示每一个主题-词,t为主题序数,即第几个主题
            names['self.edge_dict_' + str(t)] = {}  # 为每一个主题建立一个词典,如第一个主题的词典名称为 self.edge_dict_0
            for index, word in enumerate(list):  # 枚举可以同时遍历索引和值
                if word not in names.get('self.edge_dict_' + str(t)).keys():  # 表明该词还未进行边的连接
                    tmp_set = set()  # set()函数创建一个无序不重复元素集
                    for i in range(term_num):
                        if i == index:
                            continue  # 若为该单词,则跳出本次循环
                        word0 = list[i]
                        similarity = self.model.wv.similarity(word, word0)  # 计算当前词与剩余词语的相似性
                        if similarity > self.simvalue:
                            tmp_set.add(word0)
                    #  返回一个词的相邻节点
                    names.get('self.edge_dict_' + str(t))[word] = tmp_set
            # print(names.get('self.edge_dict_' + str(t)))

        # 根据边的相连关系,构建矩阵
        # for list, t in zip(self.word_list, range(word_list_len)):
            # 用0来初始化矩阵
            names['self.matrix_' + str(t)] = np.zeros([len(set(list)), len(set(list))])
            self.word_index = {}  # 记录词的index
            self.index_dict = {}  # 记录节点index对应的词
            for i, v in enumerate(set(list)):
                self.word_index[v] = i  # 用词来找到它的索引
                self.index_dict[i] = v  # 用索引来找到它的词
            for key in names.get('self.edge_dict_' + str(t)).keys():  # 对于字典里的每一个节点
                for w in names.get('self.edge_dict_' + str(t))[key]:  # 对于每一个节点的相邻点
                    # 相邻节点的边权重变为向量相似度
                    names.get('self.matrix_' + str(t))[self.word_index[key]][self.word_index[w]] = self.model.wv.similarity(key, w)
                    names.get('self.matrix_' + str(t))[self.word_index[w]][self.word_index[key]] = self.model.wv.similarity(w, key)
            # print(names.get('self.matrix_' + str(t)))

        # 归一化
            for j in range(names.get('self.matrix_' + str(t)).shape[1]):  # 对第j列元素
                sum = 0
                for i in range(names.get('self.matrix_' + str(t)).shape[0]):  # 对第i行元素
                    sum += names.get('self.matrix_' + str(t))[i][j]
                for i in range(names.get('self.matrix_' + str(t)).shape[0]):
                    names.get('self.matrix_' + str(t))[i][j] /= sum


        # 根据textrank公式计算权重
        # for list, t in zip(self.word_list, range(word_list_len)):
            self.TR = np.ones([len(set(list)), 1])
            for i in range(self.iter_num):
                self.TR = (1 - self.alpha) + self.alpha * np.dot(names.get('self.matrix_' + str(t)), self.TR)
            print("主题#%d:" % t)
            #print(self.TR)

            # 输出词和相应的权重
            word_pr = {}
            for i in range(len(self.TR)):
                word_pr[self.index_dict[i]] = self.TR[i][0]
            # sorted() 函数对所有可迭代的对象进行排序操作,返回的是一个新的 list,key指用来进行比较的元素,reverse为排序规则  ,True降序 ,False升序(默认)
            res = sorted(word_pr.items(), key=lambda x: x[1], reverse=True)
            # print(res)
            list = []
            for n in range(len(res)):
                list.append(res[n][0])
            print(list)


if __name__ == '__main__':
    tr = TextRank(0.45, 0.85, 800)
    tr.readFile()
    tr.loadModel()
    tr.calTR()


效果图

LDA获取的 主题-词 文档
在这里插入图片描述
经过TextRank计算的 主题-词
『关键词挖掘』结合 LDA + Word2Vec + TextRank 实现关键词的挖掘_第4张图片
就此完结 @^_^@

你可能感兴趣的:(摘要抽取)