自動問答簡介
自動聊天機(jī)器人疯汁,也稱為自動問答系統(tǒng)辱士,由于所使用的場景不同棕孙,叫法也不一樣享完。自動問答(Question Answering在辆,QA)是指利用計(jì)算機(jī)自動回答用戶所提出的問題以滿足用戶知識需求的任務(wù)。不同于現(xiàn)有搜索引擎竿滨,問答系統(tǒng)是信息服務(wù)的一種高級形式队他,系統(tǒng)返回用戶的不再是基于關(guān)鍵詞匹配排序的文檔列表,而是精準(zhǔn)的自然語言答案寞蚌。近年來田巴,隨著人工智能的飛速發(fā)展,自動問答已經(jīng)成為倍受關(guān)注且發(fā)展前景廣泛的研究方向挟秤。
自動問答主要研究的內(nèi)容和關(guān)鍵科學(xué)問題如下:
問句理解:給定用戶問題壹哺,自動問答首先需要理解用戶所提問題。用戶問句的語義理解包含詞法分析艘刚、句法分析管宵、語義分析等多項(xiàng)關(guān)鍵技術(shù),需要從文本的多個(gè)維度理解其中包含的語義內(nèi)容攀甚。
文本信息抽取:自動問答系統(tǒng)需要在已有語料庫箩朴、知識庫或問答庫中匹配相關(guān)的信息,并抽取出相應(yīng)的答案秋度。
知識推理:自動問答中炸庞,由于語料庫、知識庫和問答庫本身的覆蓋度有限荚斯,并不是所有問題都能直接找到答案埠居。這就需要在已有的知識體系中,通過知識推理的手段獲取這些隱含的答案鲸拥。
縱觀自動問答研究的發(fā)展態(tài)勢和技術(shù)現(xiàn)狀拐格,以下研究方向或問題將可能成為未來整個(gè)領(lǐng)域和行業(yè)重點(diǎn)關(guān)注的方向:基于深度學(xué)習(xí)的端到端自動問答,多領(lǐng)域刑赶、多語言的自動問答捏浊,面向問答的深度推理,篇章閱讀理解撞叨、對話等金踪。
基于 Chatterbot 制作中文聊天機(jī)器人
ChatterBot 是一個(gè)構(gòu)建在 Python 上,基于一系列規(guī)則和機(jī)器學(xué)習(xí)算法完成的聊天機(jī)器人牵敷,具有結(jié)構(gòu)清晰胡岔,可擴(kuò)展性好,簡單實(shí)用的特點(diǎn)枷餐。
Chatterbot 安裝有兩種方式:
- 使用
pip install chatterbot
安裝靶瘸; - 直接在 Github Chatterbot 下載這個(gè)項(xiàng)目,通過
python setup.py install
安裝,其中 examples 文件夾中包含幾個(gè)例子怨咪,可以根據(jù)例子加深自己的理解屋剑。
安裝過程如果出現(xiàn)錯(cuò)誤,主要是需要安裝這些依賴庫:
chatterbot-corpus>=1.1,<1.2
mathparse>=0.1,<0.2
nltk>=3.2,<4.0
pymongo>=3.3,<4.0
python-dateutil>=2.6,<2.7
python-twitter>=3.0,<4.0
sqlalchemy>=1.2,<1.3
pint>=0.8.1
1. 手動設(shè)置一點(diǎn)語料诗眨,體驗(yàn)基于規(guī)則的聊天機(jī)器人回答唉匾。
from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer
Chinese_bot = ChatBot("Training demo") #創(chuàng)建一個(gè)新的實(shí)例
Chinese_bot.set_trainer(ListTrainer)
Chinese_bot.train([
'親,在嗎匠楚?',
'親巍膘,在呢',
'這件衣服的號碼大小標(biāo)準(zhǔn)嗎?',
'親芋簿,標(biāo)準(zhǔn)呢峡懈,請放心下單吧。',
'有紅色的嗎益咬?',
'有呢逮诲,目前有白紅藍(lán)3種色調(diào)。',
])
下面進(jìn)行測試:
# 測試一下
question = '親幽告,在嗎'
print(question)
response = Chinese_bot.get_response(question)
print(response)
print("\n")
question = '有紅色的嗎梅鹦?'
print(question)
response = Chinese_bot.get_response(question)
print(response)
從得到的結(jié)果可以看出,這應(yīng)該完全是基于規(guī)則的判斷:
親冗锁,在嗎
親齐唆,在呢
有紅色的嗎?
有呢冻河,目前有白紅藍(lán)3種色調(diào)箍邮。
- 訓(xùn)練自己的語料。
本次使用的語料來自 QQ 群的聊天記錄叨叙,導(dǎo)出的 QQ 聊天記錄稍微處理一下即可使用锭弊,整個(gè)過程如下。
(1)首先載入語料擂错,第二行代碼主要是想把每句話后面的換行 \n 去掉味滞。
lines = open("QQ.txt","r",encoding='gbk').readlines()
sec = [ line.strip() for line in lines]
(2)接下來就可以訓(xùn)練模型了,由于整個(gè)語料比較大钮呀,訓(xùn)練過程也比較耗時(shí)剑鞍。
from chatterbot import ChatBot
from chatterbot.trainers import ListTrainer
Chinese_bot = ChatBot("Training")
Chinese_bot.set_trainer(ListTrainer)
Chinese_bot.train(sec)
這里需要注意,如果訓(xùn)練過程很慢爽醋,可以在第一步中加入如下代碼蚁署,即只取前1000條進(jìn)行訓(xùn)練:
sec = sec[0:1000]
(3)最后,對訓(xùn)練好的模型進(jìn)行測試蚂四,可見訓(xùn)練數(shù)據(jù)是 QQ 群技術(shù)對話光戈,也看得出程序員們都很努力哪痰,整體想的都是學(xué)習(xí)。
以上只是簡單的 Chatterbot 演示久妆,如果想看更好的應(yīng)用妒御,推薦看官方文檔。
基于 Seq2Seq 制作中文聊天機(jī)器人
前面镇饺,我們講了序列數(shù)據(jù)處理模型,從 N-gram
語言模型到 RNN 及其變種送讲。這里我們講另外一個(gè)基于深度學(xué)習(xí)的 Seq2Seq 模型奸笤。
從 RNN 結(jié)構(gòu)說起,根據(jù)輸出和輸入序列不同數(shù)量 RNN 哼鬓,可以有多種不同的結(jié)構(gòu)监右,不同結(jié)構(gòu)自然就有不同的引用場合。
- One To One 結(jié)構(gòu)异希,僅僅只是簡單的給一個(gè)輸入得到一個(gè)輸出健盒,此處并未體現(xiàn)序列的特征,例如圖像分類場景称簿。
- One To Many 結(jié)構(gòu)扣癣,給一個(gè)輸入得到一系列輸出,這種結(jié)構(gòu)可用于生產(chǎn)圖片描述的場景憨降。
- Many To One 結(jié)構(gòu)父虑,給一系列輸入得到一個(gè)輸出,這種結(jié)構(gòu)可用于文本情感分析授药,對一些列的文本輸入進(jìn)行分類士嚎,看是消極還是積極情感。
- Many To Many 結(jié)構(gòu)悔叽,給一系列輸入得到一系列輸出莱衩,這種結(jié)構(gòu)可用于翻譯或聊天對話場景,將輸入的文本轉(zhuǎn)換成另外一系列文本娇澎。
- 同步 Many To Many 結(jié)構(gòu)笨蚁,它是經(jīng)典的 RNN 結(jié)構(gòu),前一輸入的狀態(tài)會帶到下一個(gè)狀態(tài)中九火,而且每個(gè)輸入都會對應(yīng)一個(gè)輸出赚窃,我們最熟悉的應(yīng)用場景是字符預(yù)測,同樣也可以用于視頻分類岔激,對視頻的幀打標(biāo)簽勒极。
在 Many To Many 的兩種模型中,第四和第五種是有差異的虑鼎,經(jīng)典 RNN 結(jié)構(gòu)的輸入和輸出序列必須要等長辱匿,它的應(yīng)用場景也比較有限键痛。而第四種,輸入和輸出序列可以不等長匾七,這種模型便是 Seq2Seq 模型絮短,即 Sequence to Sequence。它實(shí)現(xiàn)了從一個(gè)序列到另外一個(gè)序列的轉(zhuǎn)換昨忆,比如 Google 曾用 Seq2Seq 模型加 Attention 模型實(shí)現(xiàn)了翻譯功能丁频,類似的還可以實(shí)現(xiàn)聊天機(jī)器人對話模型。經(jīng)典的 RNN 模型固定了輸入序列和輸出序列的大小邑贴,而 Seq2Seq 模型則突破了該限制席里。
Seq2Seq 屬于 Encoder-Decoder
結(jié)構(gòu),這里看看常見的 Encoder-Decoder
結(jié)構(gòu)拢驾〗贝牛基本思想就是利用兩個(gè) RNN,一個(gè) RNN 作為 Encoder繁疤,另一個(gè) RNN 作為 Decoder咖为。Encoder 負(fù)責(zé)將輸入序列壓縮成指定長度的向量,這個(gè)向量就可以看成是這個(gè)序列的語義稠腊,這個(gè)過程稱為編碼躁染,如下圖,獲取語義向量最簡單的方式就是直接將最后一個(gè)輸入的隱狀態(tài)作為語義向量麻养。也可以對最后一個(gè)隱含狀態(tài)做一個(gè)變換得到語義向量褐啡,還可以將輸入序列的所有隱含狀態(tài)做一個(gè)變換得到語義變量。
具體理論知識這里不再贅述鳖昌,下面重點(diǎn)看看备畦,如何通過 Keras 實(shí)現(xiàn)一個(gè) LSTM_Seq2Seq
自動問答機(jī)器人。
1. 語料準(zhǔn)備许昨。
語料我們使用 Tab 鍵 \t
把問題和答案區(qū)分懂盐,每一對為一行。其中糕档,語料為爬蟲爬取的工程機(jī)械網(wǎng)站的問答莉恼。
2. 模型構(gòu)建和訓(xùn)練。
第一步速那,引入需要的包:
from keras.models import Model
from keras.layers import Input, LSTM, Dense
import numpy as np
import pandas as pd
第二步俐银,定義模型超參數(shù)、迭代次數(shù)端仰、語料路徑:
#Batch size 的大小
batch_size = 32
# 迭代次數(shù)epochs
epochs = 100
# 編碼空間的維度Latent dimensionality
latent_dim = 256
# 要訓(xùn)練的樣本數(shù)
num_samples = 5000
#設(shè)置語料的路徑
data_path = 'D://nlp//ch13//files.txt'
第三步捶惜,把語料向量化:
#把數(shù)據(jù)向量話
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 模型定義荔烧、訓(xùn)練和保存:
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]
# 狀態(tài) `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)
# 訓(xùn)練
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)
第六步汽久,把索引和分詞轉(zhuǎn)成序列:
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())
第七步,定義預(yù)測函數(shù)踊餐,先使用預(yù)模型預(yù)測景醇,然后編碼成漢字結(jié)果:
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.
# 更新狀態(tài)
states_value = [h, c]
return decoded_sentence
- 模型預(yù)測。
首先吝岭,定義一個(gè)預(yù)測函數(shù):
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
然后就可以預(yù)測了:
print('Decoded sentence:', predict_ans("挖機(jī)履帶掉了怎么裝上去"))
總結(jié)
本文我們首先基于 Chatterbot 制作了中文聊天機(jī)器人三痰,并用 QQ 群對話語料自己嘗試訓(xùn)練。然后通過 LSTM 和 Seq2Seq 模型窜管,根據(jù)爬取的語料酒觅,訓(xùn)練了一個(gè)自動問答的模型,通過以上兩種方式微峰,我們們對自動問答有了一個(gè)簡單的入門。
參考文獻(xiàn)及推薦閱讀: