1.项目背景:
主题为汽车大师问答摘要与推理。要求使用汽车大师提供的11万条 技师与用户的多轮对话与诊断建议报告 数据建立模型,模型需基于对话文本、用户问题、车型与车系,输出包含摘要与推断的报告文本,综合考验模型的归纳总结与推断能力。
汽车大师是一款通过在线咨询问答为车主解决用车问题的APP,致力于做车主身边靠谱的用车顾问,车主用语音、文字或图片发布汽车问题,系统为其匹配专业技师提供及时有效的咨询服务。由于平台用户基数众多,问题重合度较高,大部分问题在平台上曾得到过解答。重复回答和持续时间长的多轮问询不仅会花去汽修技师大量时间,也使用户获取解决方案的时间变长,对双方来说都存在资源浪费的情况。
为了节省更多人工时间,提高用户获取回答和解决方案的效率,汽车大师希望通过利用机器学习对平台积累的大量历史问答数据进行模型训练,基于汽车大师平台提供的历史多轮问答文本,输出完整的建议报告和回答,让用户在线通过人工智能语义识别即时获得全套解决方案。
2.数据预处理
train_df = pd.read_csv(train_data_path)
test_df = pd.read_csv(test_data_path)
print('train data size {},test data size {}'.format(len(train_df), len(test_df)))
print(train_df.iloc[:1])
train data size 82943,test data size 20000
QID Brand Model Question \
0 Q1 奔驰 奔驰GL级 方向机重,助力泵,方向机都换了还是一样
Dialogue Report
0 技师说:[语音]|车主说:新的都换了|车主说:助力泵,方向机|技师说:[语音]|车主说:换了... 随时联系
-
2.1空值填充
train_df.dropna(subset=['Question', 'Dialogue', 'Report'], how='any', inplace=True)
test_df.dropna(subset=['Question', 'Dialogue'], how='any', inplace=True)
-
2.2多线程批量处理数据
from multi_proc_utils import parallelize
train_df = parallelize(train_df, sentences_proc)
test_df = parallelize(test_df, sentences_proc)
-
清除无用词
def clean_sentence(sentence):
'''
特殊符号去除
:param sentence: 待处理的字符串
:return: 过滤特殊字符后的字符串
'''
if isinstance(sentence, str):
return re.sub(
# r'[\s+\-\!\/\[\]\{\}_,.$%^*(+\"\')]+|[::+——()?【】“”!,。?、~@#¥%……&*()]+|车主说|技师说|语音|图片|你好|您好',
r'[\s+\-\/\[\]\{\}_$%^*(+\"\')]+|[+——()【】“”~@#¥%……&*()]+|你好|您好',
' ', sentence)
else:
return ' '
-
过滤停用词
def filter_words(sentence):
'''
过滤停用词
:param seg_list: 切好词的列表 [word1 ,word2 .......]
:return: 过滤后的停用词
'''
words = sentence.split(' ')
# 去掉多余空字符
words = [word for word in words if word]
# 去掉停用词 包括一下标点符号也会去掉
words = [word for word in words if word not in stop_words]
return words
-
2.3合并训练测试集合
将训练数据中['Question', 'Dialogue', 'Report']列和测试数据中['Question', 'Dialogue']合并,并保存
train_df['merged'] = train_df[['Question', 'Dialogue', 'Report']].apply(lambda x: ' '.join(x), axis=1)
test_df['merged'] = test_df[['Question', 'Dialogue']].apply(lambda x: ' '.join(x), axis=1)
print(train_df['merged'].shape)
print(test_df['merged'].shape)
merged_df = pd.concat([train_df[['merged']], test_df[['merged']]], axis=0)
merged_df.to_csv(merger_seg_path, index=None, header=False)
3.生成词向量
使用gensim工具生成初始词向量
from gensim.models.word2vec import LineSentence, Word2Vec
wv_model = Word2Vec(LineSentence(merger_seg_path),
size=embedding_dim,
sg=1,
workers=8,
iter=wv_train_epochs,
window=5,
min_count=5)
分离数据和标签:['Question', 'Dialogue']当做数据,['Report']当做标签
train_df['X'] = train_df[['Question', 'Dialogue']].apply(lambda x: ' '.join(x), axis=1)
test_df['X'] = test_df[['Question', 'Dialogue']].apply(lambda x: ' '.join(x), axis=1)
填充开始结束符号,未知词填充 oov, 长度填充
def pad_proc(sentence, max_len, vocab):
'''
# 填充字段
< start > < end > < pad > < unk > max_lens
'''
# 0.按空格统计切分出词
words = sentence.strip().split(' ')
# 1. 截取规定长度的词数
words = words[:max_len]
# 2. 填充< unk > ,判断是否在vocab中, 不在填充 < unk >
sentence = [word if word in vocab else '<UNK>' for word in words]
# 3. 填充< start > < end >
sentence = ['<START>'] + sentence + ['<STOP>']
# 4. 判断长度,填充 < pad >
sentence = sentence + ['<PAD>'] * (max_len - len(words))
return ' '.join(sentence)
# 使用GenSim训练得出的vocab
vocab = wv_model.wv.vocab
# 获取适当的最大长度
train_x_max_len = get_max_len(train_df['X'])
test_X_max_len = get_max_len(test_df['X'])
X_max_len = max(train_x_max_len, test_X_max_len)
train_df['X'] = train_df['X'].apply(lambda x: pad_proc(x, X_max_len, vocab))
# 获取适当的最大长度
test_df['X'] = test_df['X'].apply(lambda x: pad_proc(x, X_max_len, vocab))
# 获取适当的最大长度
train_y_max_len = get_max_len(train_df['Report'])
train_df['Y'] = train_df['Report'].apply(lambda x: pad_proc(x, train_y_max_len, vocab))
保存pad oov处理后的,数据和标签
train_df['X'].to_csv(train_x_pad_path, index=None, header=False)
train_df['Y'].to_csv(train_y_pad_path, index=None, header=False)
test_df['X'].to_csv(test_x_pad_path, index=None, header=False)
再次训练词向量,因为新加的符号不在词表 和 词向量矩阵
wv_model.build_vocab(LineSentence(train_x_pad_path), update=True)
wv_model.train(LineSentence(train_x_pad_path), epochs=1, total_examples=wv_model.corpus_count)
print('1/3')
wv_model.build_vocab(LineSentence(train_y_pad_path), update=True)
wv_model.train(LineSentence(train_y_pad_path), epochs=1, total_examples=wv_model.corpus_count)
print('2/3')
wv_model.build_vocab(LineSentence(test_x_pad_path), update=True)
wv_model.train(LineSentence(test_x_pad_path), epochs=1, total_examples&