自然语言学习12-聊天机器人

自动问答(Question Answering,QA)

自动问答主要研究的内容和关键科学问题如下:

  1. 问句理解:给定用户问题,自动问答首先需要理解用户所提问题。用户问句的语义理解包含词法分析、句法分析、语义分析等多项关键技术,需要从文本的多个维度理解其中包含的语义内容。

  2. 文本信息抽取:自动问答系统需要在已有语料库、知识库或问答库中匹配相关的信息,并抽取出相应的答案。

  3. 知识推理:自动问答中,由于语料库、知识库和问答库本身的覆盖度有限,并不是所有问题都能直接找到答案。这就需要在已有的知识体系中,通过知识推理的手段获取这些隐含的答案。

pip install chatterbot

error:

 

1. 手动设置语料,体验基于规则的聊天机器人回答。

from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer
Chinese_bot = ChatBot("Training demo") #创建一个新的实例
Chinese_bot.set_trainer(ListTrainer)
Chinese_bot.train([
    '亲,在吗?',
    '亲,在呢',
    '这件衣服的号码大小标准吗?',
    '亲,标准呢,请放心下单吧。',
    '有红色的吗?',
    '有呢,目前有白红蓝3种色调。',
])

测试

# 测试一下
question = '亲,在吗'
print(question)
response = Chinese_bot.get_response(question)
print(response)
print("\n")
question = '有红色的吗?'
print(question)
response = Chinese_bot.get_response(question)
print(response)

error

 

1)首先载入语料

lines = open("QQ.txt","r",encoding='gbk').readlines()
sec = [ line.strip() for line in lines]

2)训练模型

from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer
Chinese_bot = ChatBot("Training")
Chinese_bot.set_trainer(ListTrainer)
Chinese_bot.train(sec)

如果训练过程很慢,可以在第一步中加入如下代码,即只取前1000条进行训练:

sec = sec[0:1000]

(3)最后,对训练好的模型进行测试

基于 深度学习的Seq2Seq 模型制作中文聊天机器人

RNN结构:

  • One To One 结构,仅仅只是简单的给一个输入得到一个输出,此处并未体现序列的特征,例如图像分类场景。
  • One To Many 结构,给一个输入得到一系列输出,这种结构可用于生产图片描述的场景。
  • Many To One 结构,给一系列输入得到一个输出,这种结构可用于文本情感分析,对一些列的文本输入进行分类,看是消极还是积极情感。
  • Many To Many 结构,给一系列输入得到一系列输出,这种结构可用于翻译或聊天对话场景,将输入的文本转换成另外一系列文本。
  • 同步 Many To Many 结构,它是经典的 RNN 结构,前一输入的状态会带到下一个状态中,而且每个输入都会对应一个输出,我们最熟悉的应用场景是字符预测,同样也可以用于视频分类,对视频的帧打标签。

在 Many To Many 的两种模型中,第四和第五种是有差异的,经典 RNN 结构的输入和输出序列必须要等长,它的应用场景也比较有限。而第四种,输入和输出序列可以不等长,这种模型便是 Seq2Seq 模型,即 Sequence to Sequence。它实现了从一个序列到另外一个序列的转换,比如 Google 曾用 Seq2Seq 模型加 Attention 模型实现了翻译功能,类似的还可以实现聊天机器人对话模型。经典的 RNN 模型固定了输入序列和输出序列的大小,而 Seq2Seq 模型则突破了该限制。

Seq2Seq 属于 Encoder-Decoder 结构,这里看看常见的 Encoder-Decoder 结构。基本思想就是利用两个 RNN,一个 RNN 作为 Encoder,另一个 RNN 作为 Decoder。Encoder 负责将输入序列压缩成指定长度的向量,这个向量就可以看成是这个序列的语义,这个过程称为编码,如下图,获取语义向量最简单的方式就是直接将最后一个输入的隐状态作为语义向量。也可以对最后一个隐含状态做一个变换得到语义向量,还可以将输入序列的所有隐含状态做一个变换得到语义变量。

通过 Keras 实现一个 LSTM_Seq2Seq 自动问答机器人

1. 语料准备。

语料使用 Tab 键 \t 把问题和答案区分,每一对为一行。其中,语料为爬虫爬取的工程机械网站的问答。

2. 模型构建和训练。

第一步,引入需要的包:

from keras.models import Model
from keras.layers import Input, LSTM, Dense
import numpy as np
import pandas as pd

第二步,定义模型超参数、迭代次数、语料路径:

#Batch size 的大小
batch_size = 32  
# 迭代次数epochs
epochs = 100
# 编码空间的维度Latent dimensionality 
latent_dim = 256  
# 要训练的样本数
num_samples = 5000 
#设置语料的路径
data_path = 'E://nlp//12//files.txt'

第三步,把语料向量化:

#把数据向量话
input_texts = []
target_texts = []
input_characters = set()
target_characters = set()

with open(data_path, 'r', encoding='utf-8') as f:
    lines = f.read().split('\n')
for line in lines[: min(num_samples, len(lines) - 1)]:
    #print(line)
    input_text, target_text = line.split('\t')
    # We use "tab" as the "start sequence" character
    # for the targets, and "\n" as "end sequence" character.
    target_text = target_text[0:100]
    target_text = '\t' + target_text + '\n'
    input_texts.append(input_text)
    target_texts.append(target_text)

    for char in input_text:
        if char not in input_characters:
            input_characters.add(char)
    for char in target_text:
        if char not in target_characters:
            target_characters.add(char)

input_characters = sorted(list(input_characters))
target_characters = sorted(list(target_characters))
num_encoder_tokens = len(input_characters)
num_decoder_tokens = len(target_characters)
max_encoder_seq_length = max([len(txt) for txt in input_texts])
max_decoder_seq_length = max([len(txt) for txt in target_texts])

print('Number of samples:', len(input_texts))
print('Number of unique input tokens:', num_encoder_tokens)
print('Number of unique output tokens:', num_decoder_tokens)
print('Max sequence length for inputs:', max_encoder_seq_length)
print('Max sequence length for outputs:', max_decoder_seq_length)

input_token_index = dict(
    [(char, i) for i, char in enumerate(input_characters)])
target_token_index = dict(
    [(char, i) for i, char in enumerate(target_characters)])

encoder_input_data = np.zeros(
    (len(input_texts), max_encoder_seq_length, num_encoder_tokens),dtype='float32')
decoder_input_data = np.zeros(
    (len(input_texts), max_decoder_seq_length, num_decoder_tokens),dtype='float32')
decoder_target_data = np.zeros(
    (len(input_texts), max_decoder_seq_length, num_decoder_tokens),dtype='float32')

for i, (input_text, target_text) in enumerate(zip(input_texts, target_texts)):
    for t, char in enumerate(input_text):
        encoder_input_data[i, t, input_token_index[char]] = 1.
    for t, char in enumerate(target_text):
        # decoder_target_data is ahead of decoder_input_data by one timestep
        decoder_input_data[i, t, target_token_index[char]] = 1.
        if t > 0:
            # decoder_target_data will be ahead by one timestep
            # and will not include the start character.
            decoder_target_data[i, t - 1, target_token_index[char]] = 1.

第四步,LSTM_Seq2Seq 模型定义、训练和保存:

encoder_inputs = Input(shape=(None, num_encoder_tokens))
encoder = LSTM(latent_dim, return_state=True)
encoder_outputs, state_h, state_c = encoder(encoder_inputs)
# 输出 `encoder_outputs` 
encoder_states = [state_h, state_c]

# 状态 `encoder_states` 
decoder_inputs = Input(shape=(None, num_decoder_tokens))
decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)
decoder_outputs, _, _ = decoder_lstm(decoder_inputs,
                       initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)

# 定义模型
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

# 训练
model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
model.fit([encoder_input_data, decoder_input_data], decoder_target_data,
          batch_size=batch_size,
          epochs=epochs,
          validation_split=0.2)
# 保存模型
model.save('s2s.h5')

第五步,Seq2Seq 的 Encoder 操作:

encoder_model = Model(encoder_inputs, encoder_states)

decoder_state_input_h = Input(shape=(latent_dim,))
decoder_state_input_c = Input(shape=(latent_dim,))
decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
decoder_outputs, state_h, state_c = decoder_lstm(
    decoder_inputs, initial_state=decoder_states_inputs)
decoder_states = [state_h, state_c]
decoder_outputs = decoder_dense(decoder_outputs)
decoder_model = Model(
    [decoder_inputs] + decoder_states_inputs,
    [decoder_outputs] + decoder_states)

第六步,把索引和分词转成序列:

reverse_input_char_index = dict(
    (i, char) for char, i in input_token_index.items())
reverse_target_char_index = dict(
    (i, char) for char, i in target_token_index.items())

第七步,定义预测函数,先使用预模型预测,然后编码成汉字结果:

def decode_sequence(input_seq):
    # Encode the input as state vectors.
    states_value = encoder_model.predict(input_seq)
    #print(states_value)

    # Generate empty target sequence of length 1.
    target_seq = np.zeros((1, 1, num_decoder_tokens))
    # Populate the first character of target sequence with the start character.
    target_seq[0, 0, target_token_index['\t']] = 1.

    # Sampling loop for a batch of sequences
    # (to simplify, here we assume a batch of size 1).
    stop_condition = False
    decoded_sentence = ''
    while not stop_condition:
        output_tokens, h, c = decoder_model.predict(
            [target_seq] + states_value)

        # Sample a token
        sampled_token_index = np.argmax(output_tokens[0, -1, :])
        sampled_char = reverse_target_char_index[sampled_token_index]
        decoded_sentence += sampled_char
        if (sampled_char == '\n' or
           len(decoded_sentence) > max_decoder_seq_length):
            stop_condition = True

        # Update the target sequence (of length 1).
        target_seq = np.zeros((1, 1, num_decoder_tokens))
        target_seq[0, 0, sampled_token_index] = 1.
        # 更新状态
        states_value = [h, c]
    return decoded_sentence

3. 模型预测。

定义一个预测函数:

def predict_ans(question):
        inseq = np.zeros((len(question), max_encoder_seq_length, num_encoder_tokens),dtype='float16')
        decoded_sentence = decode_sequence(inseq)
        return decoded_sentence

 

print('Decoded sentence:', predict_ans("挖机履带掉了怎么装上去"))

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章