基于词向量的faq问答(附代码)

该文详细介绍了如何使用jieba分词和停用词表对JSON格式的FAQ数据进行预处理,然后利用Word2Vec训练词向量模型。通过计算相似度,找出与新问题最相关的答案。文章还涉及增量训练,以适应新词的出现。
摘要由CSDN通过智能技术生成

基于词向量的 f a q faq faq问答

step 1

读取将 J S O N JSON JSON文件中的问答卷,将其中的 q u e s t i o n question question a n s w e r answer answer分离sentences_list记录 q u e s t i o n question question

with open('train.json', 'r', encoding='utf-8') as f:
    for line in f:
        # 表示将字符串line解析为JSON格式的对象
        json_obj = json.loads(line)
        question[cnt] = json_obj
        sentences_list.append(question[cnt]['question'])
        cnt = cnt + 1

with open('train.json', 'r', encoding='utf-8') as f:r的形式,以'utf-8'的格式打开'train.json'文件。

sentences_list是一个列表,里面存储了所有的question

step 2

加载停用词表

def stopwordslist():
    #strip()方法只会去除字符串开头和结尾的空白字符,返回一个list
    stopwords = [line.strip() for line in open('stopwords.txt', 'r', encoding='utf-8').readlines()]
    return stopwords

'stopwords.txt'里面的停用词加载出来,并存储到列表stopwords

step 3

使用 j i e b a jieba jieba分词和停用词表进行分词

    # 加载停用词表
    stopwords = stopwordslist()
    # print(f"stopwords===========>{stopwords}")
    sentences_cut = []
    # 结巴分词
    for ele in sentences_list:
        cuts = jieba.cut(ele, cut_all=False)
        new_cuts = []
        for cut in cuts:
            if cut not in stopwords:
                new_cuts.append(cut)
        sentences_cut.append(new_cuts)

使用jieba分词,把sentences_list里面的每一个question分词,并且过滤掉停用词,并把过滤之后的词放到 sentences_cut中。

例如:

J S O N JSON JSON文件中question是这样的::

"question": "我是在工地开车把左手小手指粉碎骨折,但老板为了报保险给我用别人的名字那我还可以和老板要误工费吗"

经过 j i e b a jieba jieba分词和过滤停用词之后得到

['工地', '开车', '左手', '小手指', '粉碎', '骨折', '老板', '报', '名字', '老板', '误工费']

step 4

把列表sentences_cut写入到文件'data1.txt'

with open('data1.txt', 'w', encoding='utf-8') as f:
    for ele in sentences_cut:
        # print(ele)
        ele = ele + list('\n')
        elestr = ' '.join(ele)
        f.write(elestr)
step 5

训练模型

可以用 B r o w n C o r p u s BrownCorpus BrownCorpus, T e x t 8 C o r p u s Text8Corpus Text8Corpus l i n e S e n t e n c e lineSentence lineSentence来构建sentences

    # 可以用BrownCorpus,Text8Corpus或lineSentence来构建sentences,一般大语料使用这个
    # word2vec.LineSentence()函数返回的可迭代对象赋值给变量sentences,sentences是一个二维list
    sentences = list(word2vec.LineSentence('data1.txt'))
    # sentences = list(word2vec.Text8Corpus('data.txt'))
    ##训练方式1
    model = Word2Vec(sentences, vector_size=300,min_count=1, window=5, sg=1, workers=multiprocessing.cpu_count())
    # 训练方式2
    # 加载一个空模型
    model2 = Word2Vec(min_count=1)
    # 加载词表
    model2.build_vocab(sentences)
    # 训练
    model2.train(sentences, total_examples=model2.corpus_count, epochs=10)

其中第一种方式中Word2Vec各参数的意义为:

在这里插入图片描述

step 6

模型的保存与加载

模型保存可以有很多种格式,根据格式的不同可以分为2种,一种是保存为.model的文件,一种是非.model文件的保存。我常用的保存格式是.model和.vector直接上代码和结果:

    #方式一:
    model.save('word2vec.model')
    #方式二:
    model.wv.save_word2vec_format('word2vec.vector')
    model.wv.save_word2vec_format('word2vec.bin')

这两种方式的加载在获取词向量的时候应该是差别不大,区别就是.model可以继续训练, . v e c t o r .vector .vector的文件不能继续训练。加载速度也可以见,前者比后者快很多。前者时间为0.0020秒后者0.03秒,相差十多倍。 . v e c t o r .vector .vector . b i n .bin .bin文件直接可以用 . t x t .txt .txt打开可视,而 . m o d e l .model .model并不可视,它们的内存占用要少一些,加载的时间要多一点。

在这里插入图片描述

结果如下:

在这里插入图片描述

step 7

检验结果

	ask = '被车撞,对方负全责,我该找车主还是保险公司来买单?'
    new_cuts_first = []
    cuts = jieba.cut(ask, cut_all=False)
    for cut in cuts:
        if cut not in stopwords:
            new_cuts_first.append(cut)
    print(new_cuts_first)

ask相当于用户提出的问题,按照上面的方式进行分词,new_cuts_first就是分词之后的结果。

#similar_max 记录最大相似度
    similar_max = 0
    #flag 记录所在位置
    flag = -1
    length = len(sentences_list)
    for i in range(length):
        sen = sentences_list[i]
        cuts = jieba.cut(sen, cut_all=False)
        new_cuts_sceond = []
        for cut in cuts:
            if cut not in stopwords:
                new_cuts_sceond.append(cut)
        if len(new_cuts_first) != 0 and len(new_cuts_sceond) != 0:
           

利用相同的方法对sentences_list里面的米每一个问题都分词。并记录到new_cuts_sceond当中。

 if len(new_cuts_first) != 0 and len(new_cuts_sceond) != 0:
            # 计算相似度
            similar = model.wv.n_similarity(new_cuts_first, new_cuts_sceond)
            # print(similar)
            if similar >= similar_max:
                similar_max = similar
                flag = i
    print(question[flag]['answer'])
    print(flag)

计算相似度那里,model.wv.n_similarity(new_cuts_first, new_cuts_sceond)具体来说,n_similarity函数会接受两个参数:一个是词语列表,另一个是另一个词语列表。它会计算并返回这两个词语列表中的各个词语的平均余弦相似度(cosine similarity)

得到的最终结果为:

在这里插入图片描述

第一行的输出为 a s k ask ask j i e b a jieba jieba分词的结果new_cuts_first

第二行的输出为 a s k ask ask的回答。

第三行的输出为在原 J S O N JSON JSON文件中,所在位置。(从0开始,也就是原文件中第11998行)。

在这里插入图片描述

总的代码
import multiprocessing
import jieba
from gensim.models import Word2Vec, word2vec
import json

cnt = 0
question = {}
#将问题添加到sentences_list里面
sentences_list = []
with open('train.json', 'r', encoding='utf-8') as f:
    for line in f:
        # 表示将字符串line解析为JSON格式的对象
        json_obj = json.loads(line)
        question[cnt] = json_obj
        sentences_list.append(question[cnt]['question'])
        cnt = cnt + 1
#停用词
def stopwordslist():
    #strip()方法只会去除字符串开头和结尾的空白字符,返回一个list
    stopwords = [line.strip() for line in open('stopwords.txt', 'r', encoding='utf-8').readlines()]
    return stopwords

if __name__ == '__main__':
    # 加载停用词表
    stopwords = stopwordslist()
    # print(f"stopwords===========>{stopwords}")
    sentences_cut = []
    # 结巴分词
    for ele in sentences_list:
        #cut_all=False分词采用精确模式,cut_all=False=True分词采用全模式
        cuts = jieba.cut(ele, cut_all=False)
        new_cuts = []
        for cut in cuts:
            if cut not in stopwords:
                new_cuts.append(cut)
        sentences_cut.append(new_cuts)
    # print(f"sentences_cut===========>{sentences_cut}")

    # 分词后的文本保存在data.txt中
    with open('data1.txt', 'w', encoding='utf-8') as f:
        for ele in sentences_cut:
            ele = ele + list('\n')
            elestr = ' '.join(ele)
            f.write(elestr)

    # 可以用BrownCorpus,Text8Corpus或lineSentence来构建sentences,一般大语料使用这个
    # word2vec.LineSentence()函数返回的可迭代对象赋值给变量sentences,sentences是一个二维list
    sentences = list(word2vec.LineSentence('data1.txt'))
    # sentences = list(word2vec.Text8Corpus('data.txt'))

    ## 训练方式1
    model = Word2Vec(sentences, vector_size=300,min_count=1, window=5, sg=1, workers=multiprocessing.cpu_count())
    # # 训练方式2
    # # 加载一个空模型
    # model2 = Word2Vec(min_count=1)
    # # 加载词表
    # model2.build_vocab(sentences)
    # # 训练
    # model2.train(sentences, total_examples=model2.corpus_count, epochs=10)
    # print(f"model2==========>{model2}")

    ## 保存
    #方式一:
    model.save('word3vec.model')
    #方式二:
    model.wv.save_word2vec_format('word3vec.vector')
    model.wv.save_word2vec_format('word3vec.bin')


    # 分词
    ask = '我被车撞到,对方负全责,保险公司垫付交强险X万元费用,但是还差医院X万多,差的钱该找车主还是保险公司来买单?'
    new_cuts_first = []
    cuts = jieba.cut(ask, cut_all=False)
    for cut in cuts:
        if cut not in stopwords:
            new_cuts_first.append(cut)
    print(new_cuts_first)
    #similar_max 记录最大相似度
    similar_max = 0
    #flag 记录所在位置
    flag = -1
    length = len(sentences_list)
    for i in range(length):
        sen = sentences_list[i]
        cuts = jieba.cut(sen, cut_all=False)
        new_cuts_sceond = []
        for cut in cuts:
            if cut not in stopwords:
                new_cuts_sceond.append(cut)
        if len(new_cuts_first) != 0 and len(new_cuts_sceond) != 0:
            # 计算相似度
            similar = model.wv.n_similarity(new_cuts_first, new_cuts_sceond)
            # print(similar)
            if similar >= similar_max:
                similar_max = similar
                flag = i
    print(question[flag]['answer'])
    print(flag)

至此一个完整的 f a q faq faq问答 d e m o demo demo已经完成。

但是考虑到存在这样的场景,模型训练以后,会有新的语料,也就存在新词,这个时候新词用 w o r d 2 v e c word2vec word2vec就得不到词向量,会报 o v o ovo ovo(貌似这样的,反正就是 o u t v a c b u a r y out vacbuary outvacbuary)的错误。那么就需要重新训练模型, g e n s i m gensim gensim就提供了一个很好的机制,就是增量训练,新词不用和旧词全部一起再重新训练。

增量训练
    model = Word2Vec.load('word2vec.model')
    print(model)
    new_sentence = [
        '我喜欢吃苹果',
        '大话西游手游很好玩',
        '人工智能包含机器视觉和自然语言处理'
    ]
    stopwords = stopwordslist()
    sentences_cut = []
    #结巴分词
    for ele in new_sentence:
        cuts = jieba.cut(ele,cut_all=False)
        new_cuts = []
        for cut in cuts:
            if cut not in  stopwords:
                new_cuts.append(cut)
        sentences_cut.append(new_cuts)
    #增量训练word2vec
    model.build_vocab(sentences_cut,update=True) #注意update = True 这个参数很重要
    model.train(sentences_cut,total_examples=model.corpus_count,epochs=10)
    print(model)

new_sentence就相当于新的语料。

model.build_vocab(sentences_cut,update=True)
#新增词汇训练模型,得到新的模型。旧模型Word2Vec(vocab=122, size=256, alpha=0.025),新模型Word2Vec(vocab=149, size=256, alpha=0.025)。
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值