简介:面向自然语言处理与专利信息挖掘领域研究者,这份文档系统阐述了基于预训练模型的多标签专利分类方法。内容围绕IPC小类级别的细粒度分类难题,详细介绍了如何构建可扩展的大规模专利数据集,并对BERT、RoBERTa、RBT3三种预训练模型进行微调,通过Sigmoid激活函数与BCEWithLogitsLoss损失函数完成多标签分类任务。实验结果显示准确率达91.2%,Micro-F1值达71.7%,验证了预训练模型在该任务上的有效性。资源为单篇docx格式文档,共1个文件,压缩包大小约413KB,便于直接阅读与打印。已有279人学习该资料,适合需要借鉴专利自动分类方案、了解预训练模型微调流程的读者参考。
1. 预训练模型做多标签专利分类:为什么把 6 万个标签砍到 30 个,精度反而涨了
这份文档是完整的多标签专利分类实验,核心结论值得先说:在 CNPatents-Large 全量数据集上,BERT-wwm-ext 的准确率只有 0.659、Micro-F1 只有 0.597,但做了高频标签筛选、把分类标签从 654 个压缩到 30 个之后,同一模型在同一数据集上准确率冲到 0.862、Micro-F1 涨到 0.717。很多人第一次看到这个数字会觉得矛盾——标签少了,分类任务不是变“简单”了吗?实际上这正好戳中了多标签专利分类最核心的痛点:数据不平衡。这篇博客会完整拆解文档里的数据构建、模型选型、训练参数和踩坑点,适合正在做中文文本多标签分类、或者准备用预训练模型处理专利/法律/技术文档的工程师。
2. 数据构建:IPC 分类体系和「前 4 位」标签的取舍逻辑
2.1 为什么用 IPC 前 4 位而不是完整分类号
专利分类标准和普通文本分类最大的区别在于:类别体系是层级化的,而且每个专利可以同时属于多个类别。文档里提到的 IPC 分类体系一共包含 8 个部、131 个大类、646 个小类、7 523 个大组和 68 899 个小组,加起来 76 422 个组别。如果直接用完整分类号做标签,类别数接近 7 万,任何一个深度学习模型都学不动,而且绝大多数类别下的样本极少,训练必然稀疏。
文档的做法是取 IPC 分类号的前 4 位。举个例子,一个专利的 IPC 分类号是 G06F 17/30,前 4 位是“G06F”,对应“电数字数据处理”这个小类;如果分类号是 H04L 29/06,前 4 位是“H04L”,对应“数字信息传输”这个小类。取前 4 位之后,类别粒度停留在“小类”级别,既不会因为粒度太粗(比如只取部级 G / H)导致分类失去区分度,也不会因为粒度太细(取到小组 68 899 个)导致无法训练。
| IPC 层级 | 分类号示例 | 粒度说明 |
|---|---|---|
| 部 | G | 物理 |
| 大类 | G06 | 计算;推算;计数 |
| 小类 | G06F | 电数字数据处理 |
| 大组 | G06F 17/00 | 特定功能的数据处理设备或方法 |
| 小组 | G06F 17/30 | 信息检索 |
从表里能看出,前 4 位对应的是“小类”,文档里两个数据集的标签数量分别是 654 和 638,就是该粒度下的类别总数。这里要注意一个细节:标签数量不是固定的 646,因为 IPC 分类体系在更新,而且实际采集中有些小类没有出现样本,所以最终标签数会略有浮动。
2.2 数据集划分与 CNPatents-Large / CNPatents-Small 的定位
数据来自 Google Patent 2018 至 2021 年的中文专利文档,每篇保留标题、摘要和分类号,按 8:2 切分训练集和测试集。这里有两个数据集,定位不一样:
CNPatents-Large 是全部 1 033 917 篇文档,训练集 827 134、测试集 206 783。它解决的是“大规模语料下模型能不能学住”的问题。
CNPatents-Small 是部分文档 398 527 篇,训练集 318 822、测试集 79 705。它解决的是“数据量减少后模型表现会怎么变、数据规模敏感性如何”的问题。
实际复现时有个容易被忽略的点:文档说“随机采集”,但随机采集后必须做去重。专利数据库里同一个专利的公开文本和授权文本可能重复出现,有些专利族也会共享摘要。如果不去重,训练集和测试集之间可能出现重叠,准确率虚高,而且错误样本对模型的影响会被放大。常见做法是按专利公开号去重,保留最早公开的那条。
2.3 高频标签筛选:为什么只留前 30 个标签
文档里最关键的预处理步骤就是高频标签筛选。两个数据集存在一个通病:部分标签下的训练样本少于 100 篇。这对预训练模型的微调是致命的,因为样本太少时模型只能“背住”那几十篇文本,根本学不到该类别下的语义共性。
文档的处理是统计每个标签下的专利文本数,过滤后只保留排名前 30 的标签。两个数据集过滤后的情况分别是:
| 数据集 | 过滤前专利量 | 过滤后专利量 | 过滤前标签数 | 过滤后标签数 |
|---|---|---|---|---|
| CNPatents-Large | 1 033 917 | 685 133 | 654 | 30 |
| CNPatents-Small | 398 527 | 314 424 | 638 | 30 |
注意一个细节:CNPatents-Large 从 103 万篇筛到 68 万篇,筛掉了约三分之一的样本;CNPatents-Small 从 39.8 万筛到 31.4 万,只筛了约五分之一。这说明大规模数据集里的长尾更严重——标签多但样本不均衡,排名靠前的 30 个标签虽然数量占比更大,但大量样本集中在少数标签上,其他标签下样本寥寥。这一步做完之后,数据集的类别分布依然不均衡(G06F 有 43 183 篇,而 G02B 只有 6 189 篇),但这个不均衡已经是可以接受的范围了。
从实验效果看,高频标签筛选后,CNPatents-Large 的准确率从 0.659 提到 0.862,Micro-F1 从 0.597 提到 0.717,准确率提升约 20 个点,相当于“类别平衡处理”直接贡献了一个大版本的提升。这给很多做文本分类的人一个提醒:与其花大量时间调模型结构,不如先看数据分布,把尾部类别的样本问题解决掉。
3. 模型选型:BERT-wwm-ext、RoBERTa-wwm-ext 与 RBT3 的差异和选择
3.1 全词遮盖(Whole Word Masking)对中文预训练模型的特殊意义
2018 年 Google 发布的 BERT-base-Chinese 有一个结构性问题:中文按字切分,预训练阶段做 Masked LM 时随机遮盖的是单个字。比如“专利”这个词,如果只遮盖“专”,模型看到的上下文是“[MASK] 利”,它可以通过“利”字推测出“专利”,但没有真正学习到“专利”这个整体概念的语义。文档里引用的哈工大讯飞联合实验室的 BERT-wwm-ext 采用了全词遮盖策略:一个词组的部分字被选中遮盖时,词组内所有字一起被遮盖。
| 遮盖方式 | 处理结果 |
|---|---|
| 原始文本 | 使用语言模型来预测下一个词的概率 |
| 分词文本 | 使用 语言 模型 来 预测 下 一个 词 的 概率 |
| 原始遮盖 | 使 用 语 言 [MASK] 型 来 [MASK] 测 下 一 个 词 的 pro[MASK]##lity |
| 全词遮盖 | 使 用 语 言 [MASK] [MASK] 来 [MASK] [MASK] 下 一 个 词 的 [MASK] [MASK] [MASK] |
对专利文本来说,全词遮盖的收益比普通文本更大。专利摘要里有大量专业术语,比如“半导体”、“神经网络”、“无线通信”这类复合词,如果按字遮盖,模型永远无法学到这些术语的完整语义边界。文档选择 BERT-wwm-ext 而不是原始 BERT-base-Chinese,这个决策是合理的。
3.2 RoBERTa 和 RBT3 分别改了什么
RoBERTa-wwm-ext 是哈工大讯飞实验室按照 RoBERTa 训练方式重新训练的 BERT 模型。RoBERTa 相对于 BERT 的改动可以归纳为四点:更大的 Batch Size 和更长的训练步数、去掉 Next Sentence Prediction 任务、使用更长的序列训练、动态 Masking 机制。从文档给的参数表能看到,RoBERTa-wwm-ext 的训练数据是 54 亿词,优化器用 AdamW,词汇表 21 128。
BERT-wwm-ext 和 RoBERTa-wwm-ext 的参数差异集中在训练细节,而不是模型结构——两者都是 12 层 Transformer、768 维隐藏层、12 个注意力头,参数量一样。但实际分类效果有一点点差别,尤其在 Micro-F1 上:在高频标签筛选后的数据集上,RoBERTa-wwm-ext(0.717)和 BERT-wwm-ext(0.717)几乎持平,RBT3 明显掉队(0.707)。
RBT3 的定位比较特殊:它是由 RoBERTa-wwm-ext 的 3 层初始化,然后继续训练了 100 万步得到的小模型。12 层变 3 层,计算量大幅下降,但表达能力也明显受限。文档实验里 RBT3 在 CNPatents-Large(30)上的 Micro-F1 只有 0.707,在 CNPatents-Small(30)上是 0.669,全面弱于两个 12 层模型。这说明在专利分类这种文本结构复杂、术语密集的任务上,深层模型带来的收益是实实在在的,单纯为了推理速度选 3 层小模型会付出不小的精度代价。
3.3 选型结论:三个模型怎么选
从文档的多个实验结果可以归纳出一个原则:数据量小的时候,模型之间的差距会更大。CNPatents-Small(30)上 BERT-wwm-ext 准确率 0.912、RoBERTa-wwm-ext 0.912、RBT3 0.910,差距不大;但 Micro-F1 上 BERT-wwm-ext 是 0.693、RoBERTa-wwm-ext 0.696、RBT3 只有 0.669,差了近 3 个点。在大数据集上,BERT 和 RoBERTa 的差距不明显,说明对中文专利分类这个任务,全词遮盖带来的收益已经覆盖了 BERT 和 RoBERTa 之间的训练策略差异。
实际项目中我一般这样选:如果推理资源充足、追求最高效果,直接用 RoBERTa-wwm-ext,它在多数任务上不会比 BERT-wwm-ext 差;如果在意推理延迟,可以对比测试 RBT3 在剪枝或蒸馏后的表现,但从这份文档的直接实验看,RBT3 的精度下降比较明显,不太适合直接上线。
4. 多标签分类任务建模:Sigmoid 激活和 BCEWithLogitsLoss 的组合
4.1 从 Softmax 到 Sigmoid:多标签和多分类的本质区别
单标签分类任务在输出层用 Softmax,是所有类别概率的归一化,各类别概率之和等于 1。但专利分类不是“这个专利只能属于一个类别”,而是一个专利可以同时拥有多个 IPC 分类号。比如一项关于“基于神经网络的图像识别方法”的专利,可能同时被赋予 G06N(基于特定计算模型的计算机系统)、G06T(图像数据处理)和 G06K(数据识别)多个分类号。
这时如果用 Softmax,等于强制模型在多个正确类别之间做排他性选择,模型会在 G06N 和 G06T 之间纠结,最终只能押一个概率高的,另一个就被抑制了。Sigmoid 处理每个类别是独立的,输出向量的每一维单独压缩到 (0,1) 区间,代表样本属于该类别的概率,所有维度加起来不等于 1,所以模型可以同时给多个类别高概率。
激活函数之外,损失函数也要跟着换。单标签分类用 CrossEntropyLoss,多标签分类要换成二分类交叉熵的叠加。文档用的 BCEWithLogitsLoss 是 BCELoss 和 Sigmoid 的组合封装,它不会先算 Sigmoid 再算 BCELoss,而是在一个函数内完成数值计算,避免了中间步骤的数值精度损失。对训练稳定性的提升是实打实的——单独使用 Sigmoid+BCELoss 时,当 Sigmoid 输出接近 0 或 1 时 log 函数会出现梯度消失;BCEWithLogitsLoss 内部做了 logits 层面的合并计算,数值稳定性好得多。
4.2 训练参数详解:为什么 MAX_LEN 设 200、Batch 16、学习率 1e-5
文档里给出的实验参数组合如下:
| 参数 | 值 |
|---|---|
| MAX_LEN | 200 |
| TRAIN_BATCH_SIZE | 16 |
| VALID_BATCH_SIZE | 16 |
| EPOCHS | 3 |
| LEARNING_RATE | 1e-5 |
每组实验跑多次取最优参数,这个参数组合是调出来的。逐个解释:
MAX_LEN=200:专利文本组合是标题+摘要,摘要一般 200 到 400 字左右,截断到 200 可以保留摘要前半部分的核心信息。跨出这个场景来看,如果做权利要求书的分类,MAX_LEN 大概率要加,但训练显存和时间成本会成倍增加。
TRAIN_BATCH_SIZE=16:这是一个微调预训练模型的标准小 Batch。Batch 太小梯度噪声大、训练不稳;Batch 太大显存不够且收敛变慢。16 在 BERT-base 上是安全和效率的平衡点。
LEARNING_RATE=1e-5:BERT 微调通常使用 2e-5 到 5e-5 的学习率区间,这里取 1e-5 属于偏保守的选择。预训练模型的特征抽取器已经学到了通用语义表示,学习率设太高会破坏预训练学到的特征;设低一些可以让分类层尽量去适配任务,而不至于动摇了底层的语义表示能力。
EPOCHS=3:BERT 类模型微调一般 2 到 4 个 epoch 就会收敛。训练数据 80 万篇时跑满 3 个 epoch 需要的时间已经不小,继续增加 epoch 容易过拟合。
4.3 训练流程:伪代码级别的操作逻辑
这不是一份代码工程文档,但可以按文档的实验流程给出训练阶段的标准操作顺序:
# 以 HuggingFace Transformers 为例,对应文档第 4 章的实验流程 from transformers import BertTokenizer, BertForSequenceClassification from torch.nn import BCEWithLogitsLoss from torch.utils.data import DataLoader # 标签数量由高频标签筛选后的数据集决定,这里是 30 tokenizer = BertTokenizer.from_pretrained("hfl/chinese-bert-wwm-ext") model = BertForSequenceClassification.from_pretrained( "hfl/chinese-bert-wwm-ext", num_labels=30, problem_type="multi_label_classification" ) # 多标签任务必须用带 logits 的 BCE 损失 criterion = BCEWithLogitsLoss() # 关键参数:输入文本为 标题 + 摘要,MAX_LEN 截断到 200 sample = tokenizer( text_title, text_abstract, max_length=200, padding="max_length", truncation=True, return_tensors="pt" ) # 标签是 30 维的 0/1 向量,一个专利可同时命中多个分类号 labels = torch.tensor([1.0, 0.0, 1.0, ...], dtype=torch.float32) # 前向计算时开 model.train(),反向更新后叠加梯度裁剪 outputs = model(**sample, labels=labels) loss = outputs.loss loss.backward()# 两个容易翻车的细节: # 1. 多标签的 labels 必须用 float,不能用 long # CrossEntropyLoss 用 long,BCEWithLogitsLoss 用 float # 2. 预测阶段用 torch.sigmoid 拿到所有类别的独立概率, # 然后按阈值(通常 0.5)判断哪些类别命中代码里有几个细节容易踩坑。第一,labels必须转成float32而不能保持int64,因为 BCEWithLogitsLoss 期望连续值标签,这和单标签分类的 CrossEntropyLoss 完全不同。第二,problem_type="multi_label_classification"会触发模型内部的损失函数选择,如果不用这个参数,BertForSequenceClassification默认走的是单标签的 CrossEntropyLoss,训练出来的模型在推理时行为完全不对。第三,预测时不能用model.predict直接拿类别,而是要对logits做torch.sigmoid,然后按阈值判断每个类别是否命中,文档实验里默认阈值是 0.5。
4.4 评价指标:为什么准确率虚高、必须看 Micro-F1
文档用了两个指标:准确率(Accuracy)和 Micro-F1。准确率的定义是预测正确的样本数占总样本数的比例。但多标签场景里准确率有歧义——是预测完全正确(所有标签都命中)算正确,还是任一标签命中就算正确?文档实验的实现方式应该按样本级判断:预测的标签集合和真实标签集合完全一致才算正确。这种标准下准确率非常严格,这也是为什么文档在 CNPatents-Large 上的准确率只有 0.659——一个专利有多个标签,模型只要漏掉一个或者多猜一个,整条样本就算错。
Micro-F1 的逻辑不一样。它会先聚合所有样本的 True Positive、False Positive、False Negative,算出全局 Precision 和 Recall,再算 F1。这种计算方式对低频类别更敏感,不容易被高频类别带偏。文档实验里最好的一组是 BERT-wwm-ext 在 CNPatents-Large(30)上的 Micro-F1=0.717,意味着模型在 30 个类别上的综合表现还有约 28% 的提升空间,主要问题集中在召回率上——专利文本的多标签覆盖面太宽,模型很难把所有正确类别都召回。
5. 避坑指南:预训练模型微调做专利分类的 4 个典型问题
5.1 数据不平衡导致 Micro-F1 大幅偏低
现象:直接用全量数据集训练,模型准确率尚可,但 Micro-F1 只有 0.597,远低于高频标签筛选后的 0.717。
原因:全量数据集的 654 个标签里,大量标签下训练样本少于 100 篇。模型对这些尾部类别基本没有学习能力,测试时碰到这些类别的样本几乎全部判错,Precision 和 Recall 被严重拉低。
解决:文档的做法是统计每个标签下的样本数,过滤出排名前 30 的标签。实际操作时不必机械照搬 30 这个数字,可以画一个标签-样本数分布图,找到明显拐点再定阈值。但有一个原则必须坚持:训练样本少于某个下限(比如 100)的标签直接剔除,否则模型会在这些稀疏类上浪费参数,同时把全局指标拖下水。
5.2 序列截断导致关键分类信息丢失
现象:只保留摘要前 200 个 token,部分专利的分类结果明显不合理——比如一篇和“图像识别”相关的专利被分到了“数据处理”而不是“图像处理”。
原因:专利摘要后半段经常包含“本发明可应用于……”这类应用场景描述,这些信息对分类号判断很重要。MAX_LEN=200 截断了后半段,模型看不到应用场景,只能靠前半段的技术特征做判断。
解决:这个问题的解决方式不完全靠调大 MAX_LEN,因为显存和时间成本会成倍增加。更常见的是调整文本组合或截断策略:优先保留“标题+摘要开头 100 字+摘要结尾 100 字”;或者先对摘要做关键词抽取,抽出的关键词拼在文本前面,再整体送到模型里。文档中用的是标题+摘要直接截断,复现时如果效果不够可以按上述方式微调。
5.3 分类号预处理失误:小数点、空格和去重
现象:数据清洗后标签数量异常,出现了 654 个这样和 IPC 646 个小类对不上的数字;训练过程中部分样本的标签为空或格式错误。
原因:IPC 分类号的原始格式有多种,比如“G06F 17/30”“G06F17.30”“G06F17/30”。如果只按字符截取前 4 位,带空格的和不带空格的都能得到 G06F,但有些数据源里分类号中间用点分隔,截取时可能得到 G06F 或 G06 之类的残缺值。
解决:统一先做规范化处理,把所有分隔符转成标准格式,再提取前 4 位。另外每篇专利可能有多条分类号记录,必须先按文档去重,确保一篇专利只对应一组唯一标签,再做频率统计和标签筛选。
5.4 预测阶段阈值设置不当导致准确率和召回率失衡
现象:使用 0.5 作为 Sigmoid 输出阈值,预测标签数经常比真实标签数少——大部分样本只预测出 1 到 2 个标签,但真实标签平均是 2 到 3 个。
原因:多标签分类中正负样本极度不均衡,负类样本数量远多于正类。Sigmoid 输出的概率中,真正的正类概率经常落在 0.4 到 0.6 之间,0.5 阈值会把这些本应命中的类别判成负类。
解决:不要在训练前就把阈值定死。训练完成后在验证集上遍历阈值 0.3 到 0.7、步长 0.05,以 Micro-F1 最大化为目标选最优阈值。这种做法在文档中没有直接写,但实际场景里是标准操作,推荐直接做。
6. 把准确率从 65% 拉到 91%:高频标签筛选的边界在哪
高频标签筛选是文档所有实验中影响最显著的一步,但这个操作有明确的边界,不能无脑套用。文档里 CNPatents-Large 从 654 个标签筛到 30 个,准确率提升 0.2,Micro-F1 提升 0.12;但如果把筛选范围从 30 改成 10,准确率可能继续涨,可模型的可用性就下降了——只剩 10 个类别的分类器很难覆盖真实的专利审查场景。所以筛选的本质不是“做得越狠越好”,而是“在类别覆盖率和单类样本量之间取一个平衡点”。
判断这个平衡点的常用方法是画学习曲线:选定一个候选标签数 k,用每个标签下的样本量的中位数来衡量训练信号的充足程度。如果样本量中位数低于 200,说明这个 k 取值偏大;如果最低标签的样本数也超过 500,说明还可以再放宽一点。另外还要结合业务场景判断:如果这个分类器只用于粗筛和技术趋势分析,30 个高频类别完全够用;如果要做审查辅助推送,那么小类级别的完整覆盖是刚需,高频标签筛选只能作为预实验的快速验证手段,不能作为最终方案。
筛选之后还有一个验证环节值得做:对比筛选前后模型在保留类别上的单类 Precision 和 Recall。文档中强调的是全局准确率和 Micro-F1,但实际部署中应该把每个类别的 Precision/Recall 单独拉出来看。如果某个高频类别的 Recall 特别低,说明这个类别的文本特征内聚度不够,可能需要单独补充数据或用层次分类结构来处理。文档后记也提到了两个后续方向:扩大数据集规模、细化分类号粒度到大组或小组级别——这两点本质上是同一个思路,通过提升每个类别的样本覆盖来让模型学到更细的语义边界。
ROBERTA 模型的调参经验可以复用:学习率从 1e-5 开始试,Batch 从 16 开始,如果显存有余量先加 Batch 而不是加 MAX_LEN,因为 Batch 增大带来的稳定性收益通常比序列变长更明显。从那以后我每次做多标签文本分类,都强制先跑一遍标签频率统计,把尾部标签的分布图打出来再决定模型方案;标签没梳理清楚之前,调什么模型结构都像在碰运气。希望这些拆解对你的复现和改造有帮助。
本文还有配套的精品资源,点击获取