【实践】从零开始一个NLP的Word2Vec智能问答

一、实验目的及任务

1.1 实验目的

  • 了解NLP的基本知识
  • 了解gensim库的使用
  • 学习Word2Vec算法实战
  • 学习文本预处理等相关内容

二、实验环境

  • Ubuntu1604 系统;
  • 用户账号:stu/stu123;管理员:root/root123

三、实验步骤

3.1 认识NLP

你使用过苹果公司的人工智能语音助手Siri吗?有没有好奇过Siri是如何理解你说的(大部分的)话?Siri的工作过程就是自然语言处理在实践中应用的一个鲜活案例。

自然语言处理(NLP)是一种专业分析人类语言的人工智能。(简称为NLP),它的工作原理是这样的:

  • 接收自然语言,这种语言是通过人类的自然使用演变而来的,我们每天都用它来交流
  • 转译自然语言,通常是通过基于概率的算法
  • 分析自然语言并输出结果

3.2 NLP 技术解析 – 词向量

从上我们大概知道的整体的运作流程,但还是相当匪夷所思。计算机怎么能读懂我们说的话呢?

毕竟我们对着电脑喊,它也不知道我在干什么。

计算机的世界里只有01

那么问题来了,如何转换成计算机所熟知的内容。就需要用到我们数学中的大杀器:词向量

如果数学知识了解不多可按需复习,不需要了解全部的知识才来学习。

例如我们有两句话,并假设转换成词向量:

【我想要吃苹果】

举例:[ 10, 20, 30, 40, 50, 60 ] 设向量为a

【我想要苹果吃】

举例:[ 10, 20, 30, 50, 60, 40 ] 设向量为b

那么从上面ab两个向量我们就可以觉得这两句话是一个意思。

3.3 NLP 技术解析 – 分词

我们理解计算机用什么方式能够理解我们说的话,现在又遇到了一个问题:

如何把我们重要的文字转换成词向量这是迫在眉睫的需要解决的问题,我们采取分词的方式进行解决。

例如:【我想要吃苹果!】

对于如上这一句话我们可以分成:【 我 / 想要 / 吃 / 苹果 / ! 】

在上述内容当中,就分成了【我】,【想要】,【吃】,【苹果】,【!】等这几个词,很容易观察到其实有些内容我们是并不需要等。例如感叹号,那么我们就对她进行删除,诸如此类的还有很多的地嗯啊,。...等等的停用词我们讲他去除,这样也减少我们向量运算的维度和影响。

在这里我们进行了两个内容:

  • 分词
  • 去停用词

我们也可以考虑加入低频词,出现频率低的词进行删除

3.4 Word2Vec

Word2vec 论文由 Google 的研究团队发布于 2013 年,它的发布,很大程度上改变了 NLP 技术的发展,不仅如此,在使用神经网络来解决各个领域的问题时,谈必离不开 Embedding,而 Embedding 究竟是什么?了解 Word2vec 的同学都知道,它其实是 Word2vec 的另一个名字,或广义的 Word2vec,是一种使用稠密向量来表示特征的表示学习方法

例如在搜索与推荐领域,Word2vec 的思想可以用来对 Item、Query 和 User 等关键特征编码,产生 Item、Query 和 User 的稠密向量,有了这些向量后,就可以进一步将它们用于召回和排序场景中。可以毫不夸张的说,它是智能化搜索和推荐系统的基础,或者说 Word2vec 的出现,推进了搜索与推荐领域智能化的发展。

例如:

隔壁 张阿姨 觉得 刘德华 很 帅

隔壁 张阿姨 觉得 我 很 帅
复制代码

例如过后两者的稠密向量

刘德华[0.9, 0.3]
我   [0.89999, 0.3]
复制代码

那么 = 刘德华。说明两个词是相近的。

Word2Vec的处理有两种模型:CBOW 模型Skip-gram 模型这里不再展开。同学们可以参阅其他论文进行学习。

3.5 Anaconda3 安装

  1. 下载Anaconda;
$ cd  ~/Downloads     # 进入下载目录
$ wget 去官网下载哦哈哈哈哈 #下载资源
$ sudo sh ./Anaconda3-2019.07-Linux-x86_64.sh 
复制代码

按回车继续;

1565252128060.png

然后按住回车阅读注册信息,然后输入yes

1565252231269.png
查看文件即将安装的位置,按回车,即可安装;

1565252287299.png

输入yes,初始化conda;

1565252753843.png

至此Anaconda安装完成

安装完成后关闭终端,重新打开即可。

3.6 Gensim 安装

Gensim是一款开源的第三方Python工具包,用于从原始的非结构化的文本中,无监督地学习到文本隐层的主题向量表达。
它支持包括TF-IDF,LSA,LDA,和word2vec在内的多种主题模型算法,
支持流式训练,并提供了诸如相似度计算,信息检索等一些常用任务的API接口等。

他的安装也是非常简单。

pip install gensim
复制代码

如果其中使用提示numpy某些属性或者函数不可用,需升级numpy版本。

pip install numpy=1.19.2

3.7 Jieba 分词安装

jieba分词是用于中文分词非常好用的一款工具包,安装也非常简单

pip install jieba
复制代码

四、实验步骤

我们从某市的市民服务中心获取服务问答情况,分析题目而获取最类似的问答内容。

image-20210601152155460.png

数据内容如上,因问题或者回答不可控因素,数据分隔符采用++$++

问答数据集:

$ cd  ~/Downloads     # 进入下载目录
$ wget -c http://res.aihyzh.com/基于政务领域的智能问答/data.txt #下载资源
复制代码

停用词数据集:

$ cd  ~/Downloads     # 进入下载目录
$ wget -c http://res.aihyzh.com/基于政务领域的智能问答/stop_words.txt #下载资源
复制代码

我们在终端输入

jupyter notebook
复制代码

打开我们的编辑器,新建一个Python3文件,进行我们以下的实验内容

4.1 加载数据内容

首先我们先加载相关包

import pandas as pd
import jieba
from gensim.models import word2vec
复制代码

image-20210601153847906.png

没有任何的报错提示,即正确。如有其他错误可结合搜索引擎进行解决。

我们通过文本加载数据并用++$++作为分隔符:

q = [] #问题列表
a = [] #答案列表
# 读取数据文件的内容
with open('/home/stu/Downloads/data.txt') as f:
  	# 以行模式读取内容放到text中,
    text = f.read().splitlines()
    for line in text:
      	# 分隔符每行的数据,返回qa列表
        qa = line.split("++$++")
        try:
          	# 第一列为问题,第二列为答案
            q.append(qa[0])
            a.append(qa[1])
        except:
          	# 如果遇到了错误的,打印出来。进行格式调整
            print("error", qa, index)        

qa_table = pd.DataFrame({ 'q':q, 'a': a })    
qa_table
复制代码

image-20210601154031976.png

我们可以打印qa_table查看我们的数据内容是否成功加载

4.2 创建停用词表

停用词文件在上面也已经下载了,

内容格式是一行一个内容,那么我们直接将数据进行加载。

image-20210601155445209.png

# 停用词表
stop_words = []
with open('/home/stu/Downloads/stop_words.txt') as f:
    stop_words = f.read().splitlines()

# stop_words
复制代码

4.3 创建分词函数

我们通过jieba分词,并通过停用表进行过滤一些多余的数据

def get_words_by_jieba(line, stop_words):
  	# 通过jieba.cut进行分词
    words = list(jieba.cut(line))
    # 将分词数据进行循环,去除停用词
    for w in words:
        print("w", w, w in stop_words)
        if w in stop_words or w.strip() == "" :
            words.remove(w)
    return words
# 测试一下
get_words_by_jieba("古北路拓宽工 程施工噪音扰民严重超标。", stop_words)
复制代码

image-20210601160242640.png

4.4 获取所有分词

在这一步我们将所有的问题进行循环,并且将题目进行分词,分词后的数据放到字典中。构建一个数据格式:{"噪音": [1, 3, 10]}

这样说明噪音这个词出现在第1,3,10问题,那么判断这几个题目哪个相似度比较高一点。

qa_index_dist = {} # 所有的分词对应哪个编号,Key 词汇, Value是索引
qa_all_words = [] # 所有的分词

# 迭代qa_table所有数据
for row in qa_table.iterrows():
  	# 第一个是 索引,第二个第一列也就是q,第三个是第二列也就是a
    qa_index = row[0]
    # 分词并去停用词
    question_cut = get_words_by_jieba(row[1]["q"], stop_words)
    # 加入到分词组中
    qa_all_words.append(question_cut)
    for word in question_cut:
        if word in qa_index_dist:
            # 如果单词在列表中,更新索引
            qa_index_dist[word].add(qa_index)
        else:
            qa_index_dist[word] = set()
            qa_index_dist[word].add(qa_index)
            # 如果单词不再列表中,加入
复制代码

image-20210601161632149.png

两个内容的具体数据格式 ⬇️⬇️⬇️

image-20210601161757362.png

4.5 训练模型

训练模型非常简单,我们刚刚通过gensim包加载了Word2Vec。

# 训练模型
model = word2vec.Word2Vec(qa_all_words, min_count=1)
# 第一个参数是句子,可以是列表,也可以是通过word2vec.LineSentence加载的数据等
# min_count 是最小词频过滤
复制代码

更多参数可参考: https://radimrehurek.com/gensim/models/word2vec.html

4.6 定义最相似问题函数

首先我们从这个函数的作用进行分析,用户输入问题,我们获取最相似的几个问题进行返回。那么它会分成三步:

  • 将输入问题进行分词并过滤停用词
  • 根据词汇获取相关索引编号
  • 根据索引编号找到问题,匹配问题库中问题与用户输入的相似度
# 第一个参数是输入问题
# 第二个停用词列表
# 第三个参数原题库表
# 第四个参数是模型
def get_similarity_qa(input_question, stop_words_list, qa_table, model):
  	# 对输入的问题进行分词
    input_words = get_words_by_jieba(input_question, stop_words_list)
    # 循环输入问题的分词,判断一下这个分词我们是否有,如果没有我们就删除
    # 模型匹配不能有不存在的分词,会导致出错
    for w in input_words:
        if w not in qa_index_dist.keys():
            input_words.remove(w)
            
    # 根据剩余的分词获取这些分词对应的题库索引,
    # 通过set的update操作可以将所有相关的索引都放到一个set中,并且去除重复的
    input_words_index = set()
    for words in input_words:
        input_words_index.update(qa_index_dist[words])
    
    # 定义相关问题列表
    siml_list = []
    # 循环所有相关索引的题目
    for index in input_words_index:
      	# 「题库中的题目」与「输入问题的分词」进行相似度匹配
        # 加入到问题列表中
        # simi 相似度
        # words 问题原本名称
        simi = model.wv.n_similarity(qa_all_words[index], input_words)
        words = qa_table["q"][index]
        siml_list.append([simi, words])
        
    # 返回根据simi正向排序的内容
    return pd.DataFrame(siml_list,columns=['simi','words']).sort_values(["simi"], ascending=False)
复制代码

效果还是不错看起来比较好的。

image-20210601163958500.png

看完点个赞吧~ ?

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享