文本分类是一种自然语言处理(NLP)任务,旨在将文本数据分配到预定义的类别或标签中。在文本分类任务中,算法接收输入的文本数据,并根据其内容或语义特征将其分配到一个或多个类别中。
文本分类在许多现实世界的应用中都有广泛的应用,包括但不限于以下几个方面:
- 情感分析:将文本数据分类为积极、消极或中性等情感类别,用于分析用户评论、社交媒体帖子等的情感倾向。
- 垃圾邮件过滤:将电子邮件分类为垃圾邮件或非垃圾邮件,以过滤出用户不需要的信息。
- 新闻分类:将新闻文章分类到不同的主题类别,如政治、体育、科技等,以便用户更好地浏览和检索新闻内容。
https://fasttext.cc/docs/en/supervised-tutorial.html
1. 处理训练预料
在使用 FastText 训练文本分类模型时,输入数据需要满足以下要求:
- 数据格式:输入数据应该是一行一篇文档的格式
- 标签格式:每篇文档需要有一个或多个标签,标签应该用
__label__
前缀表示,紧接着是标签的名称,多个标签之间用空格分隔。例如:__label__sports __label__football
- 数据处理:在输入数据之前,需要进行一些文本预处理,例如:分词、去除停用词、小写转换等
- 编码格式:确保文本数据的编码格式是 UTF-8 ,以避免出现编码问题导致的错误
- 数据量:FastText 对于大规模数据集的处理效果较好,建议使用大规模数据集进行训练
- 数据均衡:确保各个类别的样本数量相对均衡,以避免模型训练过程中的偏差
__label__标签1 __label__标签2 跟队 记者 活塞 裁掉 特雷肖 瑟曼 签下 特拉 德翁 霍林斯
通过满足上述要求,可以有效地利用 FastText 进行文本分类任务的训练。我们学习使用的数据集包含 14 个新闻类别,分为训练集(9000)、验证集(1400)、测试集(1400),具体每个标签数据分布如下:
# 测试集 {'房产': 100, '星座': 100, '教育': 100, '社会': 100, '家居': 100, '科技': 100, '时尚': 100, '娱乐': 100, '体育': 100, '时政': 100, '游戏': 100, '财经': 100, '彩票': 100, '股票': 100} # 训练集 {'科技': 1813, '股票': 1733, '体育': 1408, '娱乐': 977, '时政': 696, '社会': 525, '教育': 458, '财经': 396, '家居': 343, '游戏': 219, '房产': 209, '时尚': 139, '彩票': 65, '星座': 19} # 验证集 {'体育': 100, '科技': 100, '社会': 100, '娱乐': 100, '股票': 100, '房产': 100, '教育': 100, '时政': 100, '财经': 100, '星座': 100, '游戏': 100, '家居': 100, '彩票': 100, '时尚': 100}
# 读取停用词 stopwords = {word.strip() for word in open('data/stopwords.txt', encoding='utf-8')} def clean_text(text): # 去除非中文字符 text = re.sub(r'[^\u4e00-\u9fa5]', ' ', text) # 文本分词 text = jieba.cut(text) # 去除停用词 clean_text = [] for word in text: word = word.strip() if len(word) <= 1: continue if word not in stopwords: clean_text.append(word) return clean_text # 数据处理 def preprocess(): for fname in glob.glob('data/news/*.txt'): data = pd.read_csv(fname, sep='\t') news_list = [] for news, target in data.to_numpy().tolist(): news = clean_text(news) if len(news) == 0: continue news = ' '.join(news) news_list.append('__label__' + str(target) + ' ' + news + '\n') # 存储数据 fname = os.path.basename(fname) open('temp/' + fname, 'w', encoding='utf-8').writelines(news_list)
2. 训练分类模型
2.1 简单使用
# 训练推理 def model_train(): # 模型训练 model = fasttext.train_supervised(input='temp/train.txt') # 模型评估 (测试集数量, P@1, R@1) print(model.test('temp/test.txt', threshold=0.0)) # 模型推理 print(model.predict(' '.join(clean_text('新浪体育讯 北京时间4月16日消息,火箭今天迎来常规赛的收官战。')))) # 模型存储 model.save_model('temp/train.bin')
2.2 评估指标
P@K 和 R@K 是什么意思呢?
P@1 表示在模型只预测 1 个标签的情况下,预测正确的样本数量占预测标签数量的比重。
R@1 表示在模型只预测 1 个标签的情况下,预测正确的样本数量占总样本数量的比重。
P@2 表示在模型只预测 2 个标签的情况下,预测正确的样本数量占预测标签数量的比重。
R@2 表示在模型只预测 2 个标签的情况下,预测正确的样本数量占总样本数量的比重。
P@K 表示在模型只预测 K 个标签的情况下,预测正确的样本数量占预测标签数量的比重。
R@K 表示在模型只预测 K 个标签的情况下,预测正确的样本数量占总样本数量的比重。
例如,我们有 10 个样本,模型对每个样本预测 2 个标签:
- A(正确) B(错误)
- C(错误) B(正确)
- A(正确) D(错误)
- D(错误) B(错误)
- D(错误) A(错误)
- A(错误) C(正确)
- C(错误) D(错误)
- A(错误) C(错误)
- D(正确) B(错误)
- B(错误) D(错误)
总样本数量 10,总预测标签数量 20,预测正确的标签数量 5,则 P@2 = 5/20,R@2 = 5/10
# 评估指标 def model_train_method(): model = fasttext.load_model('temp/train.bin') result = model.test('temp/test.txt', k=3, threshold=0.0) print(result) labels, inputs = [], [] for line in open('temp/test.txt', encoding='utf-8'): line = line.split() labels.append(line[0]) inputs.append(' '.join(line[1:])) preds = model.predict(inputs, k=3)[0] print(len(preds)) right = 0 for label, pred in zip(labels, preds): if label in pred: right += 1 print(right/(result[0] * 3), right/result[0])
2.3 训练参数
input
:输入文件路径,包含用于训练的文本数据lr
:学习率 (learning rate),控制模型参数更新的步长。默认值为 0.1dim
:词向量的维度 (dimensionality),即每个词语被表示为多少维的向量。默认值为 100epoch
:训练轮数 (number of epochs),即模型迭代训练的次数。默认值为 5minCount
:词频阈值,低于该词频的词语将被忽略。默认值为 1wordNgrams
:词袋模型中考虑的最大 n-gram 的长度。默认值为 1thread
:线程数,用于并行处理。默认值为 12pretrainedVectors
:预训练的词向量文件路径,可用于初始化词向量。默认值为空
# 训练参数 def model_train_param(): # 模型训练 model = fasttext.train_supervised(input='temp/train.txt', epoch=20, lr=9e-1, dim=300, loss='softmax', minCount=5, wordNgrams=1, pretrainedVectors='data/model/cc.zh.300.vec') # 模型评估 (测试集数量, P@1, R@1) print(model.test('temp/test.txt', threshold=0.0))
3. 自动参数搜索
Doc:https://fasttext.cc/docs/en/autotune.html
FastText 在训练模型时,可以通过以下参数来实现超参数的搜索:
autotuneValidationFile
:指定用于调优的验证集文件路径,评估模型在不同参数下的性能表现autotuneMetric
:用于评估模型性能的指标,默认 f1 scoreautotuneDuration
:模型调优的时间限制,当超过这个时间后,将停止搜索并返回最佳的参数autotuneModelSize
:模型调优的模型大小限制
这些参数主要用于在 FastText 中进行模型调优的过程中,帮助用户自动地找到最佳的模型参数设置,以达到最佳的性能。Autotune 会根据验证数据和指定的性能指标,在一定的时间和模型大小限制下,尝试不同的参数组合,并返回性能最优的模型参数设置
# 自动调参 def model_autotune(): # 训练模型,并自动调参 model = fasttext.train_supervised(input='temp/train.txt', autotuneValidationFile='temp/valid.txt', autotuneDuration=30, autotuneMetric='f1', verbose=2) # 评估模型 print('eval:', model.test('temp/test.txt', k=1, threshold=0.0)) # 存储模型 model.save_model('temp/autotune.bin')
4. 多标签分类
https://fasttext.cc/docs/en/supervised-tutorial.html#multi-label-classification
# 多标签分类 def model_train_multi_label(): # 标签:组织关系裁员 财经交易出售收购 # 数据:计算机行业大变革?甲骨文中国区裁员,IBM收购红帽公司 text = '计算机行业 变革 甲骨文 中国区 裁员 收购 红帽 公司' model = fasttext.train_supervised(input='data/multi-label.txt', epoch=100, lr=5e-1, loss='ova') print(model.test('data/multi-label.txt')) print(model.predict(text, k=2, threshold=0.0))
下面是一个简单的例子,以帮助你理解多标签分类和 ova 方法:
假设有一个电影分类任务,我们想为电影分配以下标签:
- Romance(浪漫)
- Action(动作)
- Comedy(喜剧)
我们有以下几部电影及其标签:
- 《泰坦尼克号》 – Romance
- 《速度与激情》 – Action
- 《哈利·波特》 – Fantasy
- 《当哈利遇到萨莉》 – Romance, Comedy
- 《勇敢的心》 – Action, Drama
- 《超人》 – Action, Fantasy
使用 ova 方法,我们将每个标签视为一个独立的二分类任务。
- 对于 ‘Romance’ 标签:
- 正类:《泰坦尼克号》, 《当哈利遇到萨莉》
- 负类:《速度与激情》, 《哈利·波特》, 《勇敢的心》, 《超人》
- 对于 ‘Action’ 标签:
- 正类:《速度与激情》, 《勇敢的心》, 《超人》
- 负类:《泰坦尼克号》, 《哈利·波特》, 《当哈利遇到萨莉》
- 对于 ‘Comedy’ 标签:
- 正类:《当哈利遇到萨莉》
- 负类:《泰坦尼克号》, 《速度与激情》, 《哈利·波特》, 《勇敢的心》, 《超人》
我们使用上面的数据训练 3 个二分类模型。
5. 模型量化压缩
Doc:https://fasttext.cc/docs/en/python-module.html#compress-model-files-with-quantization
# 模型压缩 def model_quantization(): # 加载模型 model = fasttext.load_model('temp/autotune.bin') # 压缩模型 model.quantize(input='temp/train.txt', retrain=True) # 评估模型 print('eval:', model.test('temp/test.txt', k=1, threshold=0.0))