文本分类(Word Classification)的任务简要教程
省流版本
有监督多分类文本分类问题总结
1. 问题定义
- 文本分类是NLP中的基础任务,核心目标是将输入文本准确映射到有限互斥的类别集合。
- 有监督多分类:模型通过带标签的数据集学习映射函数
,常见于垃圾邮件、情感分析、主题分类等场景。
2. 主要挑战与注意事项
- 需要大规模标注数据(通常1k-2w条,视任务而定)。
- 模型扩展性有限(新增类别通常需要重新训练)。
- 难以处理未知类别(可引入“background”类别)。
- 其它分类问题的常见挑战:类别不平衡等
3. 主流建模与流程
推荐流程
简化流程:
数据分析&任务难度评估 → 降噪 → 模型方法选择
非微调的流程:
数据分析 → 分词与降噪 → 特征表示 → 模型选择与训练 → 评估与调优
模型方法选择上:
- 如果任务难度较大,直接上Transformer微调
- 否则可以先从简单的ML模型开始试
- 特殊任务(比如无空格单短字符串),可以考虑使用Char-level CNN
具体流程:
数据分析与Tokenization
特征表示(Embeddings)
- 统计型:OneHot、BoW、TF-IDF(适合简单场景)。
- 静态词向量:Word2Vec、FastText(适用于自有大规模语料数据集希望快速训练一个特化的embeddings,对一词多义(词汇依赖于上下文语境) 表现不佳)。
- 上下文相关:
- RNN-based: ELMo
- Transformer-based: BERT、GPT(当前SOTA,适合捕捉复杂语义)
模型选择
- 传统ML:
- 理解数据的分布性质(线性可分性,特征可分性,聚集性,概率独立性):LDA,KNN,NB,DT
- 寻找最优模型:SVM,LR,RF,XGBoost;注意SVM在大样本量的情况下一般超级超级慢
- 深度学习:
- CNN:适合局部特征、短文本。
- RNN:处理顺序和中等长度依赖。
- Transformer:捕捉长距离依赖,适合复杂语义(如BERT、GPT)。
- 传统ML:
特殊情况评估:思考是否需要处理未见词问题(新单词,连写,拼写错误)
4. 特别提示与经验
- 特征挖掘极为关键,直接影响分类效果上限。需重视数据分析,理解文本分布情况
- 分词与降噪需结合任务场景,确保信息损失可接受。
文本分类问题简介
文本分类问题(Text Classification)是NLP的基础问题,具有相当广泛的应用场景,本文尝试对文本分类问题的研究情况与主流技术方法(截至文章更新)进行简要介绍。
应用场景
文本分类的应用场景相当多((,下面大概列几个比较经典的场景
- 垃圾邮件分类
- 情感分析
- 文档自动分组(主题分类)
- 信息识别/过滤
任务建模
有监督多分类问题
最基础的一个任务建模是建模成有监督的多分类问题:
形式化
任务目标:假定所有可行文本构成的输入空间为
- 输入:一个文本文档(字符串)d
- 输出:该文档所属类别c,其中
- 机器学习过程:机器学习模型基于预标签的数据集 $ DS = { (d_1,c_i), (d_2, c_j), … ( d_n, c_k )
f $
讨论
有监督多分类问题里面最简单的就是二分类问题,虽然简单其实在text classification里也是相当常用的(诈骗邮件分类就是一个典型的多分类问题)
建模成有监督多分类问题的Pros and Cons:
- Pros:
- 任务研究比较充分,模型很多且在大量场景中表现出色
- Cons:
- 需要一个较大规模的数据集(取决于实际任务难度,一个快速验证的场景基本上需要1k条数据,而像邮件分类这种任务基本上需要1w-2w条数据(包含训练+测试)),很可能需要人工label
- 模型不一定具备可扩展性(部分模型,新增类型需要重新训练)
- 模型不一定能处理未知场景类别(可以考虑引入“background”类别应对类似情况)
- 经典的classification要遇到的挑战(类不平衡,etc…)
无监督聚类问题
TODO 先占坑
多标签问题
TODO 先占坑
基于有监督多分类问题建模的研究工作
Overview
由于输入文本是字符串形式的,因此研究关键在于两个环节:
- 寻找一种合适的方法,将字符串映射成为特征向量(充分保留分类所需的语义信息(distinguishable))
- 使用模型学习特征向量至分类模型的函数
其中第一步的特征挖掘十分重要,将极大程度上决定分类效果的上限
基本上主流框架遵循下面三个基础步骤:
- Tokenization: 对输入文本进行分词/分符,最终构成一个token向量
一般情况下Token,可能是一个完整的短语(英语较少,中文很多),词汇,词根词缀,乃至基本的单char字符 - Embeddings: 对于token向量,将其映射为一个特征向量x,作为模型输入
映射得到的特征向量常见的有三个结构:
- (FeatureSize,):一个包含features个特征的一维向量,常见于Count, TF-IDF等基础方法
- (TokenNums,FeatureSize):这种结构常见于基于DL模型的方法,其中TokenNums一般具有上限,但是否强padding到这一上限取决于网络结构,具体会在模型选择部分 介绍
- Model: 学习 一个函数以映射特征向量
到类别
文本特征处理
文本特征的性质
与其它数据(文本、时间序列)不同,文本数据具备独特的性质。在分析任务模型时,可以从如下角度定性/定量分析给定任务文本特征(尤其是在该特征上的可分性)&尝试明确任务需/简化任务难度:
非定长性:
- 输入文本往往都是非定长的
- 输入文本集中在一定范围内(可以通过数据分析查看文本长度分布情况)
- 输入文本可能存在极端长度(使用特殊处理方法,如截断处理)
语义整体性:
- 整体性主要体现在下面几个方面:
- 【多数有义性】文本中的绝大部分token都对整体语义信息有一定程度贡献,多个较小的语义贡献被移除也会导致整体语言的语义丧失(举例:”There is an apple under
the tree”,如果移除所有虚词变成”There apple tree”,会导致显著的语义模糊) - 【长距离关联性】在自然语言中,一个/一组token的语义信息可能与较远距离外的另一个/组token具备强关联性
- 【多数有义性】文本中的绝大部分token都对整体语义信息有一定程度贡献,多个较小的语义贡献被移除也会导致整体语言的语义丧失(举例:”There is an apple under
- 思考:到底有多少的token会对我的distinguishing带来贡献?
- 有些时候数据集可以简单地依据少数几个/组token便完成classification,这个时候用一些简单地XGBoost/RF之类的模型都可以取得很好的结果,而且相较于重型模型还具备比较强的鲁棒性(不易过拟合);在特征提取阶段也可以大刀阔斧地移除无用噪声词
- 反之,如果token都对distinguishability有一定贡献,则需要进一步评估决策边界是否是复杂非线性地,并进一步使用复杂模型(比如MLP)学习非 线性特征
- 思考:对于当前的任务场景,长距离关联性对于distinguishability是否是显著的?
- 如果不显著,可以使用强局部性的一些模型,可能会更加轻量(如CNN);
- 中等程度局部性的模型推荐RNN-based的模型
- 若需要捕捉长距离的语义信息以进行分类决策,现有的SOTA基本都是Transformer-based + Attention的模型。
- 整体性主要体现在下面几个方面:
语序信息:在自然语言中,语序/语言结构会显著改变整体的语义信息;建模时请思考:
- 对于当前的任务场景,语序/语言结构信息对于distinguishability是否是显著的?
- 如果不显著,多大程度(粒度)上的顺序打乱会对distinguishability造成影响?
- 对于可以word完全乱序的case,直接BoW模型都是可以的,上重型模型几乎不会有显著提升
- 对于低显著性的模型,可以考虑使用一个简化模型,或者在一些建模的过程中不引入层级信息
- 如果是显著的,BoW之类的简化模型可能并不合适。考虑进阶模型(Transformer-based / RNN-based )考虑额外添加[SEP]和[CLS]等特殊token?
Tokenization
数据的基本降噪
基本的数据降噪过程主要包含如下选项,选项的选用需要结合任务场景进行分析,并结合实验验证效果:
大小写统一;一般情况下大小写没有提供太多的语义信息,因此可以全部转小写。不过在特殊场景(比如spam email classification)中可能还是能提供一定的distinguishable信息的
文本清洗:去除无效的文本字符等,主要可选去除如下信息
- 结构化存储符号(比如HTML标签)
- 标点符号
- 特殊字符:非ASCII字符等
- 非英文词汇:对于英文文本,英文词汇占据了主要的语言语义,因此可能可以去除。不过这一步需要慎重考虑非英文词汇部分文本的distinguishable程度
- 停用词:停用词如“the”、“is”、“at”等对分类任务通常无实际意义,在简单的分类任务在也往往会被去除。
- 数字
- 其它无关于任务的内容
词形还原(Lemmatization):主要针对屈折语,一般分为下面两类:
- 词干提取:将单词还原为词根(如“running”→“run”)
- 词形还原:将词语还原为其原型(如“better”→“good”)
- 词形还原主要旨在降低模型的学习难度,trade off是会轻微的损失语义信息
去除低频词:尤其对于Count/TF-IDF等比较简单的有效,主要是用来降低特征数量的
拼写纠正:实际情况用的不多,但是可能能够提升真实场景下的效果
这一部分的具体实现可以参考链接
。注意上述任何过程几乎都会造成信息损失,因此一定要基于任务场景评估该移除过程是否会损失用于distinguishing的信息
(major-minor原则,轻微的、难以学习的distinguishing信息丢失是可以移除的,但是对于重要的分类依据,可能信息需要保留或使用特殊处理方式(可参考特殊分词补充)
性能考量:基本上都极快可以忽略不计,除了用NTLK库做Lemmatization真的是超级超级慢
分词
分词基本上可以细化到三个粒度:Word-level、SubWord-level、Char-level
可参考资料:What is an LLM tokenizer或其中文翻译版本
各种分词器的代码实现可以参考链接
性能考量:绝大多数的tokenization复杂度的都是接近于O(CharLen)的(不过常数有区别,基于词典的tokenization一般会慢一些)
Word-Level
对于英文等基于空格分隔的文字系统,这个基本上是最简单的分词方法(直接按照空格进行分词)。而对于中文等无空格分割文字系统,这一部分稍显困难,可以使用词汇表方法(比如在中文分词领域最常用的jieba分词)进行词汇分词。
- Pros: 简单,完整保留词汇结构便于模型学习
- Cons:
- 对于相当多的文字系统可能会生成大量的同义token(如run,running)等,提高了模型的学习难度,可能需要词形还原。
- 存在未见词问题等(具体讨论参见后续章节)
- 使用场景:较低任务难度、强自然语言语义信息
SubWord-level
Subword分词介于Char-level和Word-level之间,基本思想是提取字符高频对并进行合并
(主流);或对词汇基于词典进行语义结构拆分(较少);主流方法比较容易实现机器自动化学习,只需要大量的有效文本,无需人工构建词典表,因此使用较多。
常见的SubWord分词方法:BPE、Unigram、WordPiece等,具体技术原理参考字词级Tokenizer实现;
分词示例(一个可能的基于subword的分词结果):
1 | running -> (run, n, ing); |
- Pros:
- 有效平衡词表大小与泛化能力,无需词形还原等
- 相较于word-level能够学习更细粒度的语言组信息(比如词根),对未见词问题处理较好
- Cons:
- 相较于Char-level可能损失/改变部分语义信息(分词不当的情况下)
- 需要数据集训练分词器,存在训练开销(目前已有很多数据集/训练好的分词器表,但需要评估数据集的分词能力是否适配于当前任务场景,避免分词不当)
- 使用场景: 通用程度最高、成熟分词器可用/可训练
Char-level
Char-level的分词方法十分简单粗暴,往往直接使用OneHotEncoding(或LabelEncoding)方法
- Pros:
- 简单,能最大程度的完整保留全部的语义文本信息,不会因为基于自然语言语义处理的方法产生信息损失
- 能够处理未见词问题(不过模型可能对于未见词/拼写错误的表现并不鲁棒)
- Cons: 模型学习难度较大
- 生成序列长度极长(对于英文文本,基本上是Word-level的6倍以上),需要模型捕捉长举例语义信息
- 单字符几乎无实际含义,需要模型结合前后字符(编码出的数字/向量)才能恢复基本语义,需要模型能够有效捕捉这个局部语义极值(ps: CNN-based 的捕捉能力其实就已经足够基于字符捕捉词汇语义了)
- 使用场景:较低任务难度、强局部性、无空格分割(如短字符串分类)
特殊的,针对表意文字系统(如中文),由于单字包含了一定的语义信息,使用char-level的“字”切分也能保留相当程度的语义信息以供后续学习
OneHotEncoding得到的矩阵基本上是可接受的(字母系统基本上只有<100个字母),不过对于中文这类的表意文字系统会比较麻烦(常用字也接近5000左右),只能说需要基于实际模型和机器情况综合评估
特殊分词补充
可以考虑对上述分词结果进行补充以降低模型的学习难度(提高文本可理解能力),比如:
- 对于简单的embeddings方法模型,可能会丢失复杂特殊字符串的信息/模型无法直接基于原始信息学习复杂规律(比如email)。可以使用regex提取特殊特征进行编码,降低模型的学习负担
- Transformer-based等方法也会在句子中嵌入特殊token(比如[CLS]、[SEP])token,用于辅助模型聚合语义信息/判断分句等
Embeddings
Embeddings模块的目标是:将token序列映射成特征向量x作为模型输入。主流的方法可以分成三类,下面解释分类与经典方法
- Statistic Based:最Naive的方法
- OneHotEncoding(token词表不能太大,常用于Char-level encoding)
- BoW模型:CountFrequency, TF-IDF
- StaticWordEmbeddings(NN): 基于大规模语料训练一个NN,最终实现一个 <token,vector>
映射表,token对应向量与context无关(context的理解基于大规模语料实现)- Word2Vec
- GloVe
- FastText
- ContextualizedEmbeddings(NN): 基于大规模预料训练一个NN,该NN能够依据context动态生成token vector
- RNN-like Based: ELMo, ULMFiT
- [SOTA] Transformer Based: BERT, GPT, …
具体技术原理与实现可参考[TODO](TODO:The Implementation of Embeddings)
上面的方法里,除了BoW,生成的Embeddings结构基本都是(TokenNums,FeatureSize),具体是否定长取决于网络结构:
- 许多DL模型由于网络结构(主要指早期的简单CNN模型,会受制于网络末端的全连接层参数)并不支持变长的特征向量x,因此无论是训练还是inference阶段都必须将输入特征x
完整的padding到TokenNums长度 - RNN-like和或者使用类似于Global Pooling/Attention Pooling的模型(Transformer,CNN)的支持变长的TokenNums长度
tokenSequence定长讨论:
- 很显然如果token定长将很难处理变长文本情况(将每个输入都padding到上万的token是很不现实的)。
- 但对于许多文本分类任务,tokenNums可能并不大(或者绝大多数token都小于某个长度(major
case)),因此会设置一个适中的maxTokenNums,然后padding到这个长度是可接受的(常见于比较简单的任务)【需要分析任务的string长度特点】
对于爆token的情况:基本上采取截断处理;可以考虑使用summary/外置query库
文本处理的常见问题
这里主要讨论一些文本处理的常见问题(挑战性的cases);
需要注意的是这些挑战性的case在许多分类下游任务里是高度corner的:
- 不一定需要费工夫去处理
- 处理了可能也(几乎)不影响distinguishability
未见词问题
未见词(Out of Vocabulary)指的是输入词汇不存在于词表中的情况,常见的case是:
- 船新单词(一般基本上通过词猜不出语义,猜得出的参考连写,猜不出的参考拼写错误)
- 连写(空格丢失等导致无法分词,如lookat)
- 拼写错误(拼写错误矫正可参考[TODO](TODO The Implementation of Spelling-Correction Techniques)
未见词问题的最优处理方式遵循如下两点:
- P-A: 尽可能依据词汇组成,推测其含义(对于拼写错误,则是依据错误的拼写推测实际的正确拼写)
- P-B: 无法推测含义的保证不报错,或专门标注其语义为”UNKNOWN”
对于三种不同的分词方法:
Word-Level的分词方法无法直接解决未见词
- 常见的处理方法基本只考虑了P-B(忽略,或者映射成[UNKNOWN] token)这种方法会相当程度丢失语义信息
- P-A部分
- 尝试找一个相似的已有词汇可以解决拼写错误的case
- 全新单词/连写等可以勉强尝试拆分,但会受制于word-level不会学习词缀信息的问题
ps: Word-level对未见词的处理能力实际上是有限的,如果未见词情况比较严重都转战SubWord-Level或者Char-Level了
SubWord-Level
P-A部分
- 船新单词和连写能够比较好的infer语义
- 拼写错误很可能直接变成UNKNOWN
word(如果Tokenizer不包含单字母)或者子词(如果包含单字母/错拼可拆分);可以尝试找一个相似的已有词汇可以解决拼写错误的case(类似于拼写错误纠正器)
ps: 许多模型具备一定的语境填空能力可以补全相应信息
需要特殊机制处理P-B(比如对于未知字符直接映射到UNKNOWN)
Char-Level
- P-A部分
- 对于船新单词和连写的处理很OK
- 但是对于拼写错误,只能仰仗模型是否能对局部特征扰动鲁棒了;或者引入一个预检查的机制(不过很可能有不小的性能开销)
- P-B部分:只需要处理未知字符即可(一般都是选择直接忽略);模型会努力infer任何有效的字符组的含义
- P-A部分
模型选择
一般可以分成:传统ML方法,DL方法两类
一般而言ML方法比较快,适合初步验证&解决较为简单的文本分类任务;
DL中,CNN和RNN方法控制好模型大小也跑的相对较快(注意别捏出太大的FC层了)
目前达到SOTA的都是Transformer-based的,不过计算成本较高
下面是具体模型讨论
传统有监督分类学习模型
- Linear-Model: LDA, LR
- Distance-Model: KNN
- Bayes-Model: NaiveBayes
- Tree-Model: DecisionTree,Random Forest,GBDT(如XGBoost)
- Non-linear-Model:SVM,QDA
具体模型原理可以参考TODO
也不是每个模型都要试一遍(一般来讲同类的选个比较优秀的就行),一个可供参考的ML效果验证选项:
- 理解数据的分布性质(线性可分性,特征可分性,聚集性,概率独立性):LDA,KNN,NB,DT
- 寻找最优模型:SVM,LR,RF,XGBoost;注意SVM在大样本量的情况下一般超级超级慢
深度学习方法
- CNN-based: 主要原理是对映射得到的embeddings做1d-Conv进行特征提取+FC分类头。可以利用图像学习中的advanced的方法(比如pooling
,残差,attention
等)进一步提升效果。可参考TODO - RNN-based: 循环神经网络递归处理序列信息,最终使用最后一个时刻的隐状态或池化(如max/mean pooling)后的隐状态作为文本的整体表示,接入全连接分类头进行分类。可以结合多层堆叠、双向RNN(Bi-RNN)、注意力机制(Attention)、残差连接等高级方法进一步提升对长距离依赖与语义特征的建模能力。TODO
- Transformer-based: 主要原理是基于自注意力(Self-Attention)机制的Transformer结构,在编码时直接建模序列中任意两个位置的依赖关系,通常取[CLS](或全局池化)token的输出作为文本表示,并接入全连接分类头。一般会使用预训练模型做下游分类任务。可以用多头注意力,层归一化,残差连接等技术提升效果TODO
训练有监督多分类问题模型的推荐流程
这部分都是私货(
在本章的Overview中已经提到了特征挖掘的重要性。实际上,对于较为简单的分类任务&正常的数据集,即便是在第二步选择一个非常简单的模型(LR, RF)也能取得相当好的效果(F1>80%),而且简单地分类任务任务其实占majority ,因为语义信息的可区分性其实是相当明显的。
理论上建议模型/方法的选择从简到难,也节省运算资源: D。下面列一个可供参考的流程
- 数据集分析&Tokenization方法确定:
- 数据集分析&Embeddings方法确定:
- 选择合适的Embeddings方法(参考Embeddings)
- 对映射得到的特征向量集合X进行数据分析;查看其分布规律&可分性
- 模型选择:
- 传统ML模型:使用传统的ML模型对
在实际研究中流程不一定是顺序执行的,每一步做到一定程度就可以move on到下一步了(毕竟做到后面也有可能想出前述步骤的改进方法);一般而言遵循三个阶段:
- 快速分析:使用基础的tokenization,embeddings和model对任务难度与数据分布规律进行快速分析(推荐使用KNN进行聚集程度分析,Naive Bayes进行概率分布分析,
LR进行线性可分验证,RF进行特征可分性验证,MLP尝试进行非线性可分的验证) - 预处理选优:基于上一阶段分析结果,选择一个较好的基础模型以寻找最好的预处理(Tokenization,
Embeddings)方法集;(一般而言这一步在1-3个ML模型上验证就ok,同时结合人工对数据分布规律的分析) - 模型选优:基于最合适的预处理方法,寻找最佳ML/DL模型
不过由于近年来Transformer-based的方法嘎嘎乱啥,而且基本上transformer-based
的方法已经把特征工程和模型两个模块都打包好了(毕竟embeddings模块也是要学的),有时候直接进行微调这种也是可行的,在这种情况下基本上只需要遵循下面三个步骤:
- 快速分析:同上一部分,依旧需要对任务难度等具备大致理解
- 预处理选优:一般情况下Transformer-based的主流框架已经包含分词tokenization
器了,预处理阶段的工作主要是分析任务场景,尝试减少数据噪声(避免模型过拟合到一些意想不到的特征乃至测试集泄露) - 模型选优:查看模型训练的主数据集,分析主数据集与当前任务数据集的相似程度,并尝试apply多个模型进行实验(取决于有多少卡)
- ps: 想上大一点的模型,比如直接上GPT尝试叠精度没准也是可以的XD,不过考虑在工程应用中的场景(比如模型可能是离线部署在移动设备上的),因此模型是需要在速度和精度两个方面做好权衡的
经典场景
垃圾邮件分类问题
可以使用这个经典数据集(原来是2006年就已经在广泛研究了吗O.o
https://plg.uwaterloo.ca/~gvcormac/treccorpus06/about.html
(包含中文/英文诈骗邮件分类;full的数据量是:12910 ham, 24912 spam)
这个数据集比较简单,CF/TFIDF + LR/NB/XGBoost 应该都能达到99%+的APRF(Acc,Precision,Recall, F1-Score)【所以也不是啥任务都得用BERT不是么XD】
推荐综述
TODO 推荐一些综述在这里?
其它参考资料
TODO 如有放置参考资料