如何基于spaCy实现三分类(积极/消极/中性)情感分析
三分类适配的核心改造逻辑
原代码的二分类逻辑存在两个根本问题,完全没法直接适配三分类需求:
- 标签生成逻辑用
bool(y)+not bool(y)的互斥布尔判断,把标签范围压到了二元,根本没给中性类别留独立位置,碰到原始标签为中性的样本时,会被错误归类到积极/消极类里 - 标签字典只定义了
POSITIVE、NEGATIVE两个键,spaCy的文本分类组件对输入标签的要求是:所有样本的标签字典必须包含完全一致的键集合,缺类的话直接会出现维度不匹配的报错
spaCy原生支持互斥多类别的文本分类训练,不需要额外插件,只要把标签格式按照要求调整,就能直接支持积极/消极/中性的三分类任务。
具体改造步骤
- 第一步:先明确你自己原始数据集的标签枚举规则,比如常见的编码是:
- 0 → 消极(NEGATIVE)
- 1 → 中性(NEUTRAL)
- 2 → 积极(POSITIVE)
你可以根据自己数据集的实际标签值调整对应关系,别搞混映射就行
- 第二步:替换原来的布尔cats生成逻辑,给每个原始标签匹配对应的三类别标签字典,字典里必须包含全部三个类别的键,属于的类别赋值为1.0,不属于的赋值为0.0
- 第三步:训练时开启互斥类别模式,告诉spaCy这三个类别是“一条评论只能属于其中一类”,而不是可以同时属于多个类别的多标签任务
改造后的可直接套用的代码
import random # 标签映射表:根据你自己的原始标签编码调整对应关系 # 示例对应:原始标签0=消极,1=中性,2=积极 label_map = { 0: {"NEGATIVE": 1.0, "POSITIVE": 0.0, "NEUTRAL": 0.0}, 1: {"NEGATIVE": 0.0, "POSITIVE": 0.0, "NEUTRAL": 1.0}, 2: {"NEGATIVE": 0.0, "POSITIVE": 1.0, "NEUTRAL": 0.0} } def load_data(split=0.8): # train为你自己加载的原始评论数据,格式是[(评论文本, 原始标签值), ...] train_data = train random.shuffle(train_data) texts, labels = zip(*train_data) # 替换原来的布尔cats生成逻辑 cats = [label_map[y] for y in labels] # 拆分训练集、验证集 split_idx = int(len(train_data) * split) return (texts[:split_idx], cats[:split_idx]), (texts[split_idx:], cats[split_idx:]) # 加载拆分后的数据 (train_texts, train_cats), (dev_texts, dev_cats) = load_data() # 转成spaCy要求的输入格式 train_data = list(zip(train_texts, [{'cats': cat} for cat in train_cats])) dev_data = list(zip(dev_texts, [{'cats': cat} for cat in dev_cats]))
训练配置的新手注意事项
- 构建TextCategorizer组件时,必须把
exclusive_classes参数设为True,代表这是互斥单标签分类;如果设为False,模型会按多标签逻辑训练,一条评论会被判定可同时属于多个类别,不符合三分类的需求 - 不需要手动设置输出层的类别数,spaCy会自动读取你输入的cats字典的键数量,自动匹配3类的输出维度
- 所有训练样本的cats字典必须包含完全一样的三个键,哪怕某条评论不属于某个类别,也要把对应的键值设为0.0,不然会直接触发维度报错
- 不要用纯布尔值作为标签值,尽量用浮点数0.0/1.0赋值,避免部分spaCy版本对布尔类型的解析兼容问题
预测时的逻辑调整
训练完成后,调用模型做预测时,不需要再做布尔值判断,直接取三个类别里得分最高的项作为最终分类结果即可,示例代码:
import spacy # 加载训练好的本地模型 nlp = spacy.load("你的模型存储路径") test_comment = "这家店的上菜速度一般,味道也没有特别惊艳" doc = nlp(test_comment) # 取得分最高的类别为预测结果 predicted_class = max(doc.cats, key=doc.cats.get)
内容的提问来源于stack exchange,提问作者rowann
相关产品推荐
相关产品推荐

