Python中如何使用LSTM训练QA问答系统?
我想用 word2vex 训练词向量用于后续 lstm 模型的训练,那么训练词向量用的语料可以和训练 lstm 用的一样吗?
Python中如何使用LSTM训练QA问答系统?
7 回复
可以。
要训练一个基于LSTM的QA问答系统,核心是构建一个序列到序列(Seq2Seq)的模型,用编码器-解码器结构来处理问题和答案。这里我给你一个使用Keras实现的基础版本,它虽然简单,但包含了核心流程。
主要思路是:编码器把问题句子编码成一个固定维度的上下文向量,解码器再用这个向量来逐个生成答案单词。
import numpy as np
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, LSTM, Dense, Embedding, Masking
from tensorflow.keras.utils import to_categorical
# 1. 准备示例数据(实际中你需要大量QA对)
questions = ['What is AI?', 'How does it work?']
answers = ['Artificial Intelligence.', 'It learns from data.']
# 2. 文本预处理和分词
tokenizer = Tokenizer(filters='', lower=True)
tokenizer.fit_on_texts(questions + answers)
# 创建词汇表
vocab_size = len(tokenizer.word_index) + 1
print(f"词汇表大小: {vocab_size}")
# 3. 准备编码器输入(问题)
encoder_input_seqs = tokenizer.texts_to_sequences(questions)
encoder_input_data = pad_sequences(encoder_input_seqs, padding='post')
# 4. 准备解码器输入和输出(答案)
# 解码器输入:在答案开头添加起始标记,例如 '<start>'
# 解码器输出:在答案结尾添加结束标记,例如 '<end>'
# 这里简化处理,实际需要更严谨的标记
decoder_input_seqs = tokenizer.texts_to_sequences(answers)
decoder_input_data = pad_sequences(decoder_input_seqs, padding='post')
# 解码器目标数据:偏移一步,作为要预测的下一个词
decoder_target_seqs = [seq[1:] for seq in decoder_input_seqs]
decoder_target_data = pad_sequences(decoder_target_seqs, padding='post')
decoder_target_data = to_categorical(decoder_target_data, num_classes=vocab_size)
# 5. 定义模型参数
embedding_dim = 256
latent_dim = 512
# 6. 构建编码器
encoder_inputs = Input(shape=(None,))
enc_emb = Embedding(vocab_size, embedding_dim, mask_zero=True)(encoder_inputs)
enc_lstm = LSTM(latent_dim, return_state=True)
encoder_outputs, state_h, state_c = enc_lstm(enc_emb)
encoder_states = [state_h, state_c]
# 7. 构建解码器
decoder_inputs = Input(shape=(None,))
dec_emb = Embedding(vocab_size, embedding_dim, mask_zero=True)(decoder_inputs)
dec_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)
decoder_outputs, _, _ = dec_lstm(dec_emb, initial_state=encoder_states)
decoder_dense = Dense(vocab_size, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)
# 8. 定义训练模型
model = Model([encoder_inputs, decoder_inputs], decoder_outputs)
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()
# 9. 训练模型(这里epochs设得很小,仅为演示)
model.fit([encoder_input_data, decoder_input_data],
decoder_target_data,
batch_size=2,
epochs=10,
validation_split=0.2)
# 10. 构建推理模型(用于预测)
# 编码器推理模型
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 = dec_lstm(dec_emb, 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)
# 11. 定义解码函数(预测过程)
def decode_sequence(input_seq):
# 编码输入序列,获取初始状态
states_value = encoder_model.predict(input_seq, verbose=0)
# 生成一个长度为1的空目标序列,并填入起始标记(这里假设起始标记索引为1)
target_seq = np.zeros((1, 1))
target_seq[0, 0] = 1 # 起始标记索引
stop_condition = False
decoded_sentence = []
while not stop_condition:
output_tokens, h, c = decoder_model.predict([target_seq] + states_value, verbose=0)
# 采样一个词(这里简单取argmax,实际可用采样方法)
sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_word = tokenizer.index_word.get(sampled_token_index, '')
if sampled_word == '' or len(decoded_sentence) > 20: # 简单停止条件
stop_condition = True
else:
decoded_sentence.append(sampled_word)
# 更新目标序列和状态
target_seq = np.zeros((1, 1))
target_seq[0, 0] = sampled_token_index
states_value = [h, c]
return ' '.join(decoded_sentence)
# 12. 测试预测
test_question = 'What is AI?'
test_seq = tokenizer.texts_to_sequences([test_question])
test_input = pad_sequences(test_seq, maxlen=encoder_input_data.shape[1], padding='post')
predicted_answer = decode_sequence(test_input)
print(f"问题: {test_question}")
print(f"预测答案: {predicted_answer}")
这个代码展示了从数据准备、模型构建、训练到推理的完整流程。有几个关键点:
- 数据准备:需要将文本转换为数字序列,并处理好变长序列(用
pad_sequences)。 - 模型结构:编码器LSTM生成上下文向量,解码器LSTM用这个向量初始化并生成答案。
- 训练:解码器的输入是完整的答案序列,目标是偏移一步的序列,这样模型学习预测下一个词。
- 推理:需要一个单独的推理流程,用编码器状态初始化解码器,然后循环生成答案词。
要让它真正可用,你需要:1)大量高质量的QA训练数据;2)更完善的文本预处理(如专用起始/结束标记);3)更大的模型和更长时间的训练;4)加入注意力机制(Attention)来提升长序列表现;5)使用预训练词向量(如GloVe)。
简单说,先用这个框架跑通,再逐步加入注意力机制和更好的数据。
那请问用于训练词向量的语料在语料文件大小,每篇文章的长度等方面有什么需要注意的吗
直接加一个 embedding_lookup 就好了 不 word2vec 也可以
越大越好。可以像楼下说的,用 embedding 层来做,数据量大的时候差别不大。
embedding_lookup 的实现原理和 word2vec 貌似不一样吧?
用 embedding 层的话是先将训练集的句子中单词转换成一个个 id,相当于 ont-hot 编码,并且初始化一个词向量矩阵,再输入 embedding 层的吧。

