合集:AI案例-NLP-传媒业
赛题:中文问题相似度挑战赛
主办方:科大讯飞xDatawhale
主页:http://challenge.xfyun.cn/topic/info?type=chinese-question-similarity&ch=dw-sq-1
AI问题:语义相似度识别
数据集:约5千条问题对和标签。若两个问题是相同的问题,标签为1,否则为0。
数据集价值:构建一个重复问题识别算法。
解决方案:使用gensim构建词向量模型
一、赛题描述
背景
问答系统中包括三个主要的部分:问题理解,信息检索和答案抽取。而问题理解是问答系统的第一部分也是非常关键的一部分。问题理解有非常广泛的应用,如重复评论识别、相似问题识别等。
重复问题检测是一个常见的文本挖掘任务,在很多实际问答社区都有相应的应用。重复问题检测可以方便进行问题的答案聚合,以及问题答案推荐,自动QA等。由于中文词语的多样性和灵活性,本赛题需要选手构建一个重复问题识别算法。
任务
本次赛题希望参赛选手对两个问题完成相似度打分。
训练集:约5千条问题对和标签。若两个问题是相同的问题,标签为1;否则为0。
测试集:约5千条问题对,需要选手预测标签。
二、数据集描述
数据说明
训练集给定问题对和标签,使用\t进行分隔。测试集给定问题对,使用\t进行分隔。
E.g.:世界上什么东西最恐怖 世界上最恐怖的东西是什么? 1
解析:“世界上什么东西最恐怖”与”世界上最恐怖的东西是什么“问题相同,故是重复问题相似度高,标签为1。否则标签为0。
数据样例:train.csv
"有哪些女明星被潜规则啦 哪些女明星被潜规则了 1"
"怎么支付宝绑定银行卡? 银行卡怎么绑定支付宝 1"
"请问这部电视剧叫什么名字 请问谁知道这部电视剧叫什么名字 1"
"泰囧完整版下载 エウテルペ完整版下载 0"
"在沧州市区哪家卖的盐焗鸡好吃? 沧州饭店哪家便宜又好吃又实惠 0"
"男生说女生很像他自己什么意思 女生和男生说我想咬你是什么意思 0"
"影音先锋下载怎么那么慢? 影音先锋怎么下载 0"
"胆怯反义词是什么 胆怯的反义词是什么啊 1"
"“必须”和“必需”有什么区别? 必须和必需的区别是什么? 1"
"怎么在手机上找回支付宝支付密码 手机怎么找回支付宝的支付密码 1"
数据集版权许可协议
BY-NC-SA 4.0
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans
三、解决方案样例
工作原理介绍
中文词向量是通过训练(如 Word2Vec、BERT、FastText 等模型)将词语映射到高维空间中的稠密向量(例如 300 维)。每个向量隐含了词语的 语义 和 语法 特征,例如:
- 语义:
"猫"
和"狗"
的向量方向接近(同属动物)。 - 语法:
"跑步"
和"游泳"
的向量方向接近(同属动词)。
中文词向量相似度是 将词语语义映射到数学空间的量化表达,核心在于通过向量计算捕捉词语间的语义关联。实际应用中需结合具体任务选择模型、优化分词,并理解其局限性(如多义词、领域偏差)。Word2Vec 是 Google 于 2013 年提出的浅层神经网络词嵌入模型,旨在将文本中的单词映射为稠密向量(词向量),通过向量空间距离捕捉语义和语法关系。
词向量的意义
- 语义相似性:向量空间中距离相近的词具有相似含义(如
国王 - 男
≈女王 - 女
)。 - 上下文关联:同一上下文中出现的词向量方向相近(如
咖啡 - 因特网
≈茶 - 微信
)。 - 数学运算:向量可进行加减运算推断关系(如
巴黎 - 法国
+北京 - 中国
≈东京 - 日本
)。
运行环境
python | 3.12.3 |
---|---|
sklearn-compat | 0.1.3 |
gensim | 4.3.3 |
python-Levenshtein | 0.27.1 |
lightgbm | 3.3.0 |
python-Levenshtein
包的功能是:专注于高效计算字符串之间的 Levenshtein 距离(莱文斯坦编辑距离) 和相似度,底层用 C 语言优化,速度极快。莱文斯坦编辑距离定义为:将一个字符串转换成另一个字符串所需的最少单字符编辑操作次数。适用拼写纠错、模糊搜索、DNA序列比对等需要快速计算字符串差异的场景。
distance
包的功能是:提供多种字符串/序列距离算法的通用库,包括但不限于 Levenshtein 距离。
导入系统库
import pandas as pd
import distance
import Levenshtein
from tqdm import tqdm
import numpy as np
import pandas as pd
import os
import jieba
from gensim.models import word2vec
import lightgbm as lgb
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score
from scipy.spatial.distance import cosine, cityblock, canberra, euclidean, minkowski, braycurtis, \
correlation, chebyshev, jensenshannon, mahalanobis, seuclidean, sqeuclidean
处理流程
- 数据加载:读取训练集(train.csv)和测试集(test.csv)
- 数据合并:将训练集和测试集合并进行统一特征工程处理
- 特征工程:构建丰富的文本相似度特征
- 模型训练:使用LightGBM进行5折交叉验证
- 预测输出:生成测试集的预测结果
1、加载数据集
读取训练集(train.csv)和测试集(test.csv)
train = pd.read_csv('data/train.csv', sep='\t', header=None)
train.columns = ['q1','q2','label']
test = pd.read_csv('data/test.csv', sep='\t', header=None)
test.columns = ['q1','q2']
test['label'] = 1
sample_submit = pd.read_csv('data/sample_submit.csv')
sample_submit['q1'] = test['q1']
sample_submit['q2'] = test['q2']
2、数据合并
将训练集和测试集合并进行统一特征工程处理。
3、特征工程
构建丰富的文本相似度特征。
基础特征
- 文本长度特征:每个问题的字符长度(q1_len, q2_len)
- 长度差异特征:长度差、绝对差、比例(q1q2_len_diff, q1q2_len_diff_abs, q1q2_rate)
- 特殊符号特征:是否以问号结尾(q1_end_special)
共现字特征
- 共现字符数:两个问题共有的不同字符数量(comm_q1q2char_nums)
- 字符位置匹配:前8个字符在另一个问题中的匹配位置(q1_pos_1到q1_pos_8)
距离特征:使用多种文本距离算法计算相似度:
- Jaccard距离
- Sorensen距离
- Levenshtein编辑距离
- Levenshtein比率
词向量特征
- 分词处理:使用jieba进行中文分词
- Word2Vec训练:基于所有问题文本训练词向量模型
- 向量相似度计算:
- 计算12种不同的向量距离(余弦、欧式、曼哈顿等)
- 对每个问题的词向量取平均得到句向量
- 句向量存储:为每个问题存储100维的归一化句向量
# 共现字位置
def char_match_pos(q1, q2, pos_i):
q1 = list(q1)
q2 = list(q2)
if pos_i < len(q1):
q2_len = min(len(q2), 25) # q2_len只匹配前25个字
for pos_j in range(q2_len):
if q1[pos_i] == q2[pos_j]:
q_pos = pos_j + 1 # 如果匹配上了,记录匹配的位置
break
elif pos_j == q2_len - 1:
q_pos = 0 # 如果没有匹配上,赋值为0
else:
q_pos = -1 # 如果后续长度不存在,赋值为-1
return q_pos
for pos_i in range(8):
data['q1_pos_' + str(pos_i + 1)] = data.apply(
lambda row: char_match_pos(row['q1'], row['q2'], pos_i), axis=1).astype(np.int8)
# 词向量的相似度特征
data['vec_cosine'] = data.progress_apply(lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 1),
axis=1)
data['vec_canberra'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 2), axis=1)
data['vec_cityblock'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 3), axis=1)
data['vec_euclidean'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 4), axis=1)
data['vec_braycurtis'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 5), axis=1)
data['vec_minkowski'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 6), axis=1)
data['vec_correlation'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 7), axis=1)
data['vec_chebyshev'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 8), axis=1)
data['vec_jensenshannon'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 9), axis=1)
data['vec_mahalanobis'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 10), axis=1)
data['vec_seuclidean'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 11), axis=1)
data['vec_sqeuclidean'] = data.progress_apply(
lambda index: get_w2v(index['q1_words_list'], index['q2_words_list'], 12), axis=1)
4、模型训练
使用LightGBM进行5折交叉验证。
- 模型选择:LightGBM梯度提升决策树
- 参数设置:
- 控制模型复杂度的参数:num_leaves=5, max_depth=6
- 正则化参数:lambda_l1=1, lambda_l2=0.001
- 采样参数:feature_fraction=0.9, bagging_fraction=0.95
- 学习率:0.1
- 训练方式:5折交叉验证
- 评估指标:二分类对数损失(binary_logloss)
no_feas = ['q1','q2','label','q1_words_list','q2_words_list']
features = [col for col in data.columns if col not in no_feas]
train, test = data[:train_size], data[train_size:]
len(features)
X = train[features] # 训练集输入
y = train['label'] # 训练集标签
X_test = test[features] # 测试集输入
n_fold = 5
folds = KFold(n_splits=n_fold, shuffle=True, random_state=1314)
params = {
'boosting_type': 'gbdt', # 决定集成学习的方式,gbdt: 梯度提升决策树
'objective': 'binary', # 定义优化目标(损失函数),binary: 二分类任务,使用对数损失函数 (Log Loss)
'num_leaves': 5, # 每棵树的叶子节点数量,叶子节点越少,模型越简单(偏向欠拟合);叶子节点越多,模型越复杂(容易过拟合)
'max_depth': 6, # 单棵树的最大深度,树越深,模型越复杂(容易过拟合);树越浅,模型越简单(偏向欠拟合
'min_data_in_leaf': 450, # 确保每个叶子节点至少包含指定数量的样本
'learning_rate': 0.1, # 学习率,控制每一步迭代的权重更新幅度
'feature_fraction': 0.9, # 每次迭代仅使用部分特征进行训练
'bagging_fraction': 0.95, # 每次迭代仅使用部分样本进行训练
'bagging_freq': 5, # 每隔多少棵树应用一次 Bagging(特征/样本采样)
'lambda_l1': 1, # •L1 正则化 (lambda_l1): 产生稀疏权重,适合特征选择
'lambda_l2': 0.001, # •L2 正则化 (lambda_l2): 防止权重过大,提升泛化性
'min_gain_to_split': 0.2, # 分裂节点所需的最小增益阈值
}
oof = np.zeros(len(X))
prediction = np.zeros(len(X_test))
for fold_n, (train_index, valid_index) in enumerate(folds.split(X)):
X_train, X_valid = X[features].iloc[train_index], X[features].iloc[valid_index]
y_train, y_valid = y[train_index], y[valid_index]
# model = lgb.LGBMRegressor(**params, n_estimators=50000, n_jobs=-1)
model = lgb.LGBMRegressor(**params, n_estimators=50000, n_jobs=-1, verbose=50, early_stopping_rounds=200) # 移到这里
# 在较新版本的 LightGBM 中,verbose 参数已经被移动到构造函数 (LGBMRegressor 初始化时) 而不是 fit 方法中。
model.fit(X_train, y_train,
eval_set=[(X_train, y_train), (X_valid, y_valid)],
eval_metric='binary_logloss')
# verbose=50,
# early_stopping_rounds=200)
y_pred_valid = model.predict(X_valid)
y_pred = model.predict(X_test, num_iteration=model.best_iteration_)
oof[valid_index] = y_pred_valid.reshape(-1,)
prediction += y_pred
prediction /= n_fold
5、预测输出
生成测试集的预测结果。
# from sklearn.metrics import accuracy_score
y_pred = (oof > 0.5)
score = accuracy_score(y_pred ,train['label'].values)
score
sub_pred = (prediction > 0.5).astype(int)
sample_submit['label'] = sub_pred
sample_submit.to_csv('lgb_submit.csv',index=None)
sample_submit['label'].value_counts()
运行结果
输出:预测准确率为0.84。
输出文件:lgb_submit.csv。数据样例如下。其中label为1代表相似、0代表不相似。
label | q1 | q2 |
---|---|---|
1 | 玩梦幻西游能赚钱吗 | 梦幻西游2不花钱能玩吗 |
1 | 夏天去什么地方旅游好 | 夏季去什么地方旅游最好(国内的地方) |
0 | 为什么梦幻西游网站打不开 | 为什么下载梦幻西游游戏补丁网页打不开 |
0 | 这对双胞胎像不 | 像这样的可爱卡通图片要男的谢了 |
1 | 免费网络游戏都有哪些? | 有哪些免费的网络游戏 |
1 | 什么牌子的沐浴露比较香 | 什么牌子沐浴露比较润 |
0 | 您好,这边是查询不到的,麻烦您联系银行核实一下的,辛苦您了 | 如果您无法通过网银自助查询,建议您联系银行为您核实查询一下。 |
1 | 英雄联盟的游戏名 | 仿英雄联盟的游戏 |
1 | 您好,手机端操作的呢,还是电脑上操作的呢 | 亲,您是手机端操作的还是电脑端操作的呢? |
0 | 房地产咨询公司面试 | 求知名房地产咨询公司 |
1 | 挂学籍是什么意思 | 学籍是什么意思啊 |
。。。 |
源码开源协议
GPL-v3