基于 BERT 与语义角色标注的细粒度中文仇恨言论检测
代码详见:https://github.com/xiaozhou-alt/Hate_Identification-BERT
文章目录
- 一、项目介绍
- 二、数据集介绍
- 三、项目实现
- 1.项目目录
- 2.数据预处理
- 3.HanLP 介绍(面向生产环境的前沿多语种自然语言处理技术✨)
- 4.关键模型算法分析
- 1)BERT 预训练模型
- 2)HanLP—语义角色标注(SRL)
- 3)HanLP—依存句法分析(DEP)
- 5.评估指标
- 四、结果展示
一、项目介绍
本项目是一个基于 BERT 和 HanLP 中的一系列自然语言处理方法,旨在开发一个基于深度学习的仇恨言论检测系统,能够识别文本中的仇恨言论目标群体以及言论核心论点,系统采用BERT模型以及HanLP库中的语义角色标注(SRL)和依存句法分析(DEP),通过语义分析和模式识别技术实现高效检测。
二、数据集介绍
本次项目的数据来自:
阿里云:AI大模型赛【打榜赛】CCL25-Eval 任务10:细粒度中文仇恨识别评测
以下是天池比赛任务的 相关 GitHub 仓库 中的 README 数据集介绍:
片段级中文仇恨言论四元组抽取旨在构建结构化的仇恨言论四元组(评论对象、论点、目标群体、是否仇恨,四种元素的具体说明如下:
- 评论对象(Target): 帖子的评述对象,如一个人或一个群体。当实例无具体目标时设为NULL,例如,保持安全!
- 论点(Argument): 包含对评论目标关键论点的信息片段。
- 目标群体(Targeted Group): 指包含仇恨信息的评论对象-论点对涉及的目标群体。标注的目标群体包括 “ 地域(Region)”、“ 种族(Racism)”、“ 性别(Sexism)”、“ LGBTQ ”、“ 其他(others)”共5类,需要注意的是对于一个评论对象的论点描述,目标群体可能包括不止一个。例如,女性男性 | 都怕爱滋 | Sexism, others | hate 的四元组中,既有对于性别的歧视言论,又有对于艾滋病人的歧视言论。
- 是否仇恨(Hateful): 评论对象-论点对是否构成了对某些群体的仇恨言论。
对于非仇恨文本以及不包含特定群体的一般攻击性言论,同样需要对目标群体和观点进行抽取,并设为 non-hate。由于样本中可能有多个评论对象,因此可以包含多个四元组。
本次评测使用的中文仇恨言论四元组抽取数据集收集了贴吧、知乎等国内社交媒体平台的用户评论数据,为每条样本提供了高质量的二元分类标签,并对句子中的评论对象、论点和目标群体进行片段级标注。该数据集总计 8000 条中文数据,其中仇恨言论为 4935 ,非仇恨言论为 3065 条。每条语句均包含一个或多个中文仇恨言论四元组,共计 9405 个,其中仇恨四元组 5949 个,非仇恨四元组 3456 个。
任务的输入为社交媒体文本,输出为仇恨四元组,顺序依次为Target、Argument、Targeted Group、Hateful。每个四元组中各元素之间用" | "分割,并利用[END]结尾;如果一条样本中包含多个四元组,不同四元组之间利用[SEP]分割。
以下是两个实例:
输入:你可真是头蠢驴,这都做不好。
输出:你 | 蠢驴 | non-hate | non-hate [END]
输入:老黑我是真的讨厌,媚黑的还倒贴。
输出:老黑 | 讨厌 | Racism | hate [SEP] 媚黑的 | 倒贴 | Racism | hate [END]
以下是数据集评论去除部分停用词之后的词云图(网上恶意还是太大了⊙﹏⊙):
三、项目实现
1.项目目录
Hate_Identification-BERT/
├── data/ # 数据集目录
│ ├── train.json # 训练数据
│ ├── test.json # 测试数据
│ └── output/ # 数据分析输出
├── images/ # 项目相关图片
├── model/ # 预训练模型
│ ├── bert-base-chinese/ # BERT中文模型
│ └── bilstm/ # BiLSTM模型权重
├── result/ # 实验结果
├── src/
│ ├── bert_ablation/ # BERT消融实验
│ │ ├── no_srl.py # 无SRL版本
│ │ ├── single_layer.py # 单层判断版本
│ │ └── single_task.py # 单任务版本
│ ├── bert_model.py # BERT主模型
│ ├── bilstm_crf.py # BiLSTM-CRF模型
│ └── keyword_config.py # 关键词配置
2.数据预处理
考虑到后续对模型进行评估的数据需要,我们将数据按照 8:2 的比例进行数据集划分,得到的数据条目输出如下所示:
import json
from sklearn.model_selection import train_test_split# 加载原始数据
with open('data.json', 'r', encoding='utf-8') as f:data = json.load(f)# 划分索引保持原始格式
train_idx, test_idx = train_test_split(range(len(data)),test_size=0.2,random_state=42,stratify=[1 if 'hate' in d['output'] else 0 for d in data]
)# 按索引划分数据
train_data = [data[i] for i in train_idx]
test_data = [data[i] for i in test_idx]# 统计数据量
print(f"训练集数据量: {len(train_data)}条")
print(f"测试集数据量: {len(test_data)}条")# 保存划分后的数据(保持原始JSON格式)
with open('train.json', 'w', encoding='utf-8') as f:json.dump(train_data, f, ensure_ascii=False, indent=2)with open('test.json', 'w', encoding='utf-8') as f:json.dump(test_data, f, ensure_ascii=False, indent=2)
训练集数据量: 3200条
测试集数据量: 800条
对数据进行初步的分析,包括:
- 总样本数
- 仇恨四元组数量
- 非仇恨四元组数量
- 每个样本的平均四元组数量
- 目标群体分布统计
def analyze_data(data):stats = {'total_samples': len(data),'hate_quads': 0,'non_hate_quads': 0,'avg_quads_per_sample': 0}target_groups = []hate_status = []for sample in data:quads = parse_quadruples(sample['output'])stats['avg_quads_per_sample'] += len(quads)for quad in quads:target_groups.append(quad['target_group'])if quad['hateful'] == 'hate':stats['hate_quads'] += 1else:stats['non_hate_quads'] += 1stats['avg_quads_per_sample'] /= stats['total_samples']stats['target_group_dist'] = dict(Counter(target_groups))return stats
将统计结果保存为 Excel 文件:
- basic_stats.xlsx :基础统计数据
- target_group_dist.xlsx :目标群体分布数据
3.HanLP 介绍(面向生产环境的前沿多语种自然语言处理技术✨)
HanLP 支持在线使用(只是如果没有申请 API 的话,仅支持每分钟两次调用):HanLP | 在线演示
HanLP 提供了中文分词,词性标注,命名实体识别等多种 NLP 技术
4.关键模型算法分析
1)BERT 预训练模型
实验中使用了 BERT 预训练模型进行评论 content 的总体处理,bert-base-chinese预训练模型下载地址,from_pretrained 方法会自动下载或加载本地缓存的模型权重。
model_name = "/kaggle/input/bert/transformers/default/1/bert-base-chinese"
tokenizer = BertTokenizer.from_pretrained(model_name)
model = BertModel.from_pretrained(model_name).to(device)
- 模型架构:以 BERT-base-chinese 预训练模型作为编码器,用于提取文本的深层语义表征。在此基础上,设计两个分类头:其一为目标群体分类头,通过线性变换层结合 Softmax 激活函数,输出 29 类目标群体的概率分布;其二为仇恨属性分类头,利用线性变换层搭配 Sigmoid 激活函数,输出文本是否包含仇恨属性的二元概率。BERT 通过共享编码器参数,实现跨任务的语义信息交互与共享,增强对中文复杂语义的理解和捕捉能力。
- 多任务训练策略:在训练过程中,针对不同任务采用适配的损失计算方式。目标群体分类任务采用交叉熵损失函数,用于度量预测类别分布与真实标签之间的差异;仇恨属性分类任务则采用二分类交叉熵损失函数,衡量二元分类预测结果与真实类别的误差。考虑到四元组数据存在长度可变的特点,为缓解训练过程中的梯度稀疏问题,采用梯度累积策略,每 4 步更新一次模型参数。
- 变长序列处理:为适应输入文本的长度差异,设计自定义数据整理函数。该函数对输入序列进行动态填充,使同一批次的文本序列具有相同长度,并生成对应的批次级注意力掩码。在模型计算过程中,注意力掩码能够有效屏蔽填充标记的影响,确保模型仅对真实文本内容进行语义编码和特征学习,避免无效信息干扰模型训练与推理。
更改后的 BERT 模型架构图如下所示:
2)HanLP—语义角色标注(SRL)
SRL 和 DEP 的整体处理流程如下所示:
SRL 驱动的目标识别:使用 HanLP 对输入文本进行语义角色标注,提取谓词“ARG0”(施事者)作为候选目标。若未找到 ARG0,则通过依存分析提取主语(“nsubj”关系)作为替代目标。例如,在“基佬应该感谢自己没被吊死在树上”中,SRL 识别出谓词 “感谢”作为论点,施事者 “基佬”作为评论对象。
srl_result = HanLP(text, tasks='srl').get('srl')
for predicate in srl_result:if isinstance(predicate, dict) and 'arguments' in predicate:for role in predicate['arguments']:if role.get('type') == 'ARG0':target = role.get('word')
3)HanLP—依存句法分析(DEP)
依存分析识别词语间的语法关系,具体的语义角色关系和依存句法分析名称,可以详见这篇文章:
NLP-文本处理:依存句法分析(主谓、动宾、动补…)【基于“分词后得到的词语列表A”+“A进行词性标注后得到的词性列表B”来进行依存句法分析】【使用成熟的第三方工具包】
dep_result = HanLP(text, tasks='dep').get('dep')
for word in dep_result:if word.get('deprel') == 'nsubj':target = word.get('lemma')
代码中我使用dep提取句子中的主谓语,与我的关键词进行对比,检测符合的目标群体类别和是否仇恨。
5.评估指标
以下为项目官方 GitHub 中 README 文件中的评估指标方法:
评价指标为提交结果和标准答案的硬匹配和软匹配分别的F1分数,以及两种方式的F1分数的平均分 计算方式与机器学习库sklearn一致。具体的计算公式如下:
F 1 − s c o r e {F1-score} F1−score:
F 1 = 2 × P × R P + R {F1}=2\times\frac{P\times R}{P+R} F1=2×P+RP×R
硬匹配: 当且仅当预测四元组的每一个元素都与答案中对应元素完全一致才判断为正确抽取的四元组。
软匹配: 当且仅当预测四元组的 Targeted Group , Hateful 两个元素和标准答案中相对应的两个元素完全一致,并且预测四元组的 Target ,Argument 两个元素和标准答案中相对应的两个元素的字符串匹配程度超过50% 才判断为正确抽取的四元组。(计算方式为Python 标准库 difflib 模块中的 SequenceMatcher 函数一致)。具体计算如下:
l e n p r e d len_{pred} lenpred:预测四元组长度
l e n g o l d len_{gold} lengold:标准答案长度
M:预测四元组和标准答案之间的最长公共子序列长度
S i m i l a r i t y = M × 2 l e n p r e d + l e n g o l d {Similarity}=\frac{M\times 2}{len_{pred}+len_{gold}} Similarity=lenpred+lengoldM×2
说明:在软匹配指标计算过程中最长公共子序列对文本的顺序有要求,只有字符正确并目字符顺序正确才会被计算为最长公共子序列。
四、结果展示
项目中选取了一下 基准模型 作为对比:
- Rule-Matching:基于人工定义的歧视性词汇库与规则匹配,通过正则表达式识别仇恨关键词(如“黑鬼”、“基佬”),并映射到目标群体标签。
- BiLSTM-CRF:经典序列标注模型,采用双向长短期记忆网络结合条件随机场,对四元组元素进行联合抽取,输入特征为词嵌入与位置编码。
- BERT-single:基于 BERT 的单任务模型,未设置多任务学习框架。
不同模型的硬匹配、软匹配和 F1 分数如图所示,本文中使用的 BERT-multi 方法相较于另外三种方法,三个关键性指标有显著的提升。
为量化各核心模块的贡献,设计 消融实验 移除关键组件,结果如下表所示:
模型 | 硬匹配 | 软匹配 | F1-score |
---|---|---|---|
BERT-multi | 0.1681 | 0.3687 | 0.2684 |
无 SRL | 0.0951 | 0.2464 | 0.1708 |
无双层判断 | 0.1206 | 0.2863 | 0.2035 |
单任务模型 | 0.1336 | 0.3176 | 0.2256 |
- 移除 SRL 模块(无 SRL):目标提取仅依赖关键词匹配,F1-score相比完整模型下降9.76%,表明语义角色标注对复杂句式的评论对象定位至关重要,如“支持把那些人都赶出去”中,SRL 正确识别“赶出去”的受事者 “那些人”,而关键词匹配易漏标。
- 单层仇恨判断(无双层):仅保留第一层关键词匹配,F1-score相比完整模型下降6.49%,说明第二层多维度特征对隐喻仇恨的识别不可或缺,例如“都是西方紫苯的功劳啊”无直接仇恨词,但通过识别特殊表情,正确识别隐含的仇恨情感。
- 单任务模型(单任务):移除仇恨属性分类头,F1-score下降4.28%,表明仇恨属性与目标群体的跨任务学习存在互补效应,BERT 的共享编码器有效捕捉了两者的语义关联。
如果你喜欢我的文章,不妨给小周一个免费的点赞和关注吧!