摘要:
合集:AI案例-NLP-零售业
赛题:CCF-BDCI-2020房地产行业聊天问答匹配
主办方:中国计算机学会 & 贝壳找房
主页:https://www.datafountain.cn/competitions/474
AI问题:文本二分类/文本对关系问题,判断回复是否匹配问题。
数据集:1,4000条贝壳房地产行业客户和房产经纪人聊天问答匹配数据集
数据集价值:构建基于语义的问和答匹配模型。
解决方案:Chinese-RoBERTa-wwm-ext模型、simpletransformers开发框架
一、赛题描述
背景
贝壳找房是以技术驱动的品质居住服务平台,“有尊严的服务者、更美好的居住”,是贝壳的使命。在帮助客户实现更美好的居住过程中,客户会和服务者(房产经纪人)反复深入交流对居住的要求,这个交流发生在贝壳APP上的IM(即时通讯)中。IM交流是双方建立信任的必要环节,客户需要在这个场景下经常向服务者咨询许多问题,而服务者是否为客户提供了感受良好、解答专业的服务就很重要,贝壳平台对此非常关注。因此,需要准确找出服务者是否回答了客户的问题,并进一步判断回答得是否准确得体,随着贝壳平台规模扩大,需要AI参与这个过程。
任务
本次赛题的任务是:给定IM交流片段,片段包含一个客户问题以及随后的经纪人若干IM消息,从这些随后的经纪人消息中找出一个是对客户问题的回答。 任务要点:
1.数据来自一个IM聊天交流过程;
2.选取的客户问题之前的聊天内容不会提供;
3.提供客户问题之后的经纪人发送的内容;
4.如果在这些经纪人发送内容之间原本来穿插了其他客户消息,不会提供;
5.这些经纪人发送内容中有0条或多条对客户问题的回答,把它找出来。
参赛者需要根据训练语料,构建出泛化能力强的模型,对不在训练语料中的测试数据做识别,从测试数据中为客户问题找出对应经纪人回答。希望参赛者能构建基于语义的问和答匹配模型,模型类型不限。
难度与挑战: 1.IM聊天的随意性和碎片化,各个地方的语言习惯不同。 2.要求模型的泛化性好。在测试集上模型的度量指标。 3.要求模型的复杂度小。最终提交模型需要符合生产环境使用要求。
二、数据集内容
1、训练集
训练集包括客户问题文件train.query.tsv和经纪人回复train.reply.tsv两个文件,涉及6000段对话(有标签答案)。
客户问题文件
客户问题文件train.query.tsv数据格式如下:
字段 | 数据类型 | 说明 |
---|---|---|
问题Id | Int | |
客户问题 | String | 同一对话Id只有一个问题,已脱敏 |
数据样例:train.query.tsv
0 采荷一小是分校吧
1 毛坯吗?
2 你们的佣金费大约是多少和契税是多少。
3 靠近川沙路嘛?
4 这套房源价格还有优惠空间吗?
5 这个税多少钱
6 这套房源价格还有优惠空间么?
7 有房子可以带我看看吗?
8 光线不好,是吗
9 几期的?
10 什么情况呢?
11 这套房源价格还有优惠空间吗?
经纪人回复文件
经纪人回复文件 train.reply.tsv 数据格式如下:
字段 | 数据类型 | 说明 |
---|---|---|
对话id | Int | 对应客户问题文件中的对话id |
经纪人回复Id | Int | Id对应真实回复顺序 |
经纪人回复内容 | String | 已脱敏 |
经纪人回复标签 | Int | 1表示此回复是针对客户问题的回答,0相反 |
数据样例:train.reply.tsv
0 0 杭州市采荷第一小学钱江苑校区,杭州市钱江新城实验学校。 1
0 1 是的 0
0 2 这是5楼 0
1 0 因为公积金贷款贷的少 0
1 1 是呢 0
1 2 这套一楼带院的,您看看 0
1 3 房本都是五年外的 0
1 4 好的??,您先看下 0
2 0 您是首套还是二套呢? 0
2 1 所有费用下来654万 1
2、测试集
测试集包括客户问题文件test.query.tsv 和经纪人回复 test.reply.tsv 两个文件,涉及14,000段对话(无标签答案)。客户问题文件数据格式同训练集。经纪人回复文件数据格式与训练集相同,只是去除了 “经纪人回复标签” 列。
客户问题文件
数据样例:test.query.tsv
0 东区西区?什么时候下证?
1 小学哪个
2 看哪个?
3 面积多少,什么户型
4 什么时候能够看房呢?
5 您好,请问这个房子周边有哪些学校?
6 什么时候能够看房呢?
7 现在可以看吗
8 业主心理价位好多嘛
9 总房款多少
10 单间配套太挤
11 明天可以看房吗
经纪人回复文件
数据样例:test.reply.tsv
0 0 我在给你发套
0 1 您看下我发的这几套
0 2 这两套也是金源花园的
0 3 价钱低
0 4 便宜的房子,一般都是顶楼
1 0 您好
1 1 恩德里小学
1 2 河西一片
2 0 我加您微信发房源连接
2 1 保利百合
2 2 微信您通过一下吧
3 0 160-200左右
3 1 我先帮咱找找,咱抽时间可以看看,方便留个电话不
3、提交数据
格式如下:
Field | Type | Description |
---|---|---|
对话id | Int | |
经纪人回复Id | Int | |
经纪人回复标签 | Int | 1表示此回复是针对客户问题的回答,0相反 |
数据集使用许可协议
BY-NC-SA 4.0
数据发布方:贝壳找房平台
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans
三、解决方案样例
解决方案
本案例的任务目标是给定IM交流片段(包含客户问题+经纪人多条消息),从经纪人消息中找出哪一条是对客户问题的回答。该解决方案的应用场景是贝壳找房平台需要评估房产经纪人是否准确回答了客户问题,提升服务质量。
关键技术点:
- 文本匹配模型:使用BERT处理句子对分类任务,将问题和回复拼接后输入模型
- 交叉验证:代码结构支持K折交叉验证(虽然实际只运行了1折)
- 中文优化:使用专门针对中文优化的RoBERTa-wwm-ext模型,处理中文文本效果更好。Chinese-RoBERTa-wwm-ext 是由哈工大(HIT)和科大讯飞联合研发的中文预训练语言模型,基于 RoBERTa 架构,并针对中文文本优化,采用全词掩码(Whole Word Masking, WWM) 技术,在多项中文 NLP 任务中表现优异。
- 结果集成:支持多模型结果集成(注释部分展示了10折结果的集成)
安装开发包
库名称 | 版本号 |
---|---|
python | 3.12.3 |
pandas | 2.2.3 |
numpy | 1.26.4 |
simpletransformers | 0.70.1 |
四、工作流程
源码:CCF_BDCI_2020_房产行业聊天问答匹配.ipynb 为解决方案基线版本。
1. 数据准备阶段
数据加载
train_query = pd.read_csv('./data/train/train.query.tsv', sep='\t', header=None)
train_reply = pd.read_csv('./data/train/train.reply.tsv', sep='\t', header=None)
- 加载两个训练文件:
train.query.tsv
:客户问题(qid, text_a)train.reply.tsv
:经纪人回复(qid, rid, text_b, labels)
数据合并与处理
train = pd.merge(train_reply, train_query, on='qid', how='left')
df = train[['text_a', 'text_b', 'labels']]
df = df.sample(frac=1, random_state=1029) # 打乱数据
- 数据分为查询(query)和回复(reply)两部分。通过qid合并问题和回复数据
- 提取关键字段:客户问题(text_a)、经纪人回复(text_b)、标签(labels):是否匹配,作为训练和测试数据 df。
- 随机打乱数据顺序
2. 模型训练与评估
训练-验证集划分
train_df = df[df.index % 10 != i] # 90%训练
eval_df = df[df.index % 10 == i] # 10%验证
- 使用简单的10折交叉验证中的1折(实际只运行了i=0的情况)
模型配置
model = ClassificationModel('bert',
'./chinese-roberta-wwm-ext',
num_labels=2,
use_cuda=True,
cuda_device=0,
args=train_args)
- 使用 Chinese-RoBERTa-wwm-ext 预训练模型
- 二分类任务(是否是对问题的回答)
- GPU加速训练
训练参数
train_args = {
'reprocess_input_data': True,
'overwrite_output_dir': True,
'num_train_epochs': 3,
'fp16': False
}
- 3个训练周期
- 不使用混合精度训练(fp16=False)
训练过程
model.train_model(train_df, eval_df=eval_df)
- 在训练集上训练模型
- 在验证集上评估性能
运行过程展示:
Epoch 1 of 3: 0%
0/3 [00:00<?, ?it/s]
Epochs 1/3. Running Loss: 0.6423: 15%
356/2429 [38:29<4:02:45, 7.03s/it]
...
3. 对测试集预测
测试数据加载
# 3. 测试与预测
test_query = pd.read_csv('./data/test/test.query.tsv', sep='\t', header=None, encoding="gbk")
test_query.columns = ['qid', 'text_a']
test_reply = pd.read_csv('./data/test/test.reply.tsv', sep='\t', header=None, encoding="gbk")
test_reply.columns = ['qid', 'rid', 'text_b']
test = pd.merge(test_reply, test_query, on='qid', how='left')
df_test = test[['text_a', 'text_b']]
- 加载并合并测试集的问题和回复数据
预测与结果生成
submit_sample = pd.read_csv('./data/sample_submission.tsv', sep='\t', header=None)
submit_sample.columns =['qid', 'rid', 'label']
data = []
for i, row in df_test.iterrows():
data.append([row['text_a'], row['text_b']])
predictions, raw_outputs = model.predict(data)
submit_sample['label'] = predictions
np.save(f'prob_{i}', raw_outputs)
submit_sample.to_csv(f'sub_{i}.tsv', sep='\t', index=False, header=False)
- 对测试集进行预测
- 保存预测结果和原始输出概率
4. 结果集成与提交
集成
# 原本设计是10折交叉验证集成,但实际只运行了1折(i=0)
# p = (p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) / 10
p = p0 # 实际只使用了单折结果
最终提交
submit_sample['label'] = p.argmax(axis=1)
submit_sample.to_csv('submission.tsv', sep='\t', index=False, header=False)
- 取概率最大的类别作为预测标签
- 生成最终提交文件
本样例提交文件 submission.tsv 包含的数据示例如下: 数据包括三个字段(’qid’, ‘rid’, ‘label’)分别为(用户问题ID、回答ID、是否匹配标识)。
0 0 0
0 1 0
0 2 0
0 3 0
0 4 0
1 0 0
1 1 1
1 2 0
2 0 0
2 1 0
2 2 0
3 0 1
源码开源协议
GPL-v3