文本多标签分类模型开发记录

文本分类是NLP应用领域中最常见也最重要的任务类型,也是机器学习领域的经典应用场景之一。

本文通过笔者工作中的一个真实案例,讨论通过机器学习实现文本多标签分类的过程以及一些优化经验。

对于文中涉及到的一些机器学习术语,如不理解其含义,可自行百度,或参考机器学习基础

案例介绍

案例:试题知识点预测

描述:中小学课程试题对应一个或多个知识点,现在想要通过机器学习训练出一个模型,可以通过试题文本,预测出试题知识点。

这个案例可以抽象成一个文本的多标签分类任务,其中的知识点就属于标签。

机器学习库:scikit-learn

收集数据

机器学习任务的基础是要有数据,所以首先要收集数据。收集数据的方式多种多样,需要视情况而定。

在本案例中,开发程序对接数据服务或数据接口,抽取数据并将其保存起来。这里不进行详细描述。

探索数据

在本案例中,探索数据时,根据实际情况制定了以下几个统计指标。

以高中化学试题数据为例。

1、试题总量

统计试题的总量,观察试题量情况。试题总量大小直接影响样本总量大小,试题是原始数据,需要经过处理,才能成为样本。

在这里插入图片描述

2、标签类型占比

在本案例中,标签类型分为三种:NODE,KNOWLEDGE_POINT,TESTING_POINT。

其中NODE类型的标签在业务上是要排除的,所以在处理数据集的时候,也要排除掉NODE类型的标签的数据,观察类型占比,可以大致看出NODE类型的标签对整个数据集的影响。

在这里插入图片描述

在实际开发不同课程的知识点预测模型时,个别课程的可用样本量远小于样本总量,通过分析,发现标签中NODE类型占比过高,导致被排除的样本太多。这个指标在分析中发挥了很大的作用。

3、标签样本量统计

统计标签的样本量,可以观察到每个标签的样本量,帮助我们分析标签的使用情况。

在这里插入图片描述
在这里插入图片描述

这两个图是标签样本量统计图的滑块在不同位置的情况,可以看到样本量在最大位置左右和中位数位置左右的差距,这显示样本数据集是极度不平衡的,大部分标签的样本量非常少。

所以在实际开发模型的时候,我制定了热门标签策略,这个策略选择样本量在某个阈值之上的标签,并使用这些标签的样本组成样本数据集。在这个策略中,热门标签的数量也要考虑进去,因为模型可预测的标签取决于输入数据集中的标签数量,在本案例中,输入数据集中的标签数量就是热门标签数量,如果热门标签数量太少,则模型可预测的标签数量就少,模型可能无法达到应用要求。

4、标签样本量质量分布

统计每个标签的样本量,也就是每个标签被使用的次数,然后统计标签样本量分布,有助于观察标签的使用情况。

在这里插入图片描述

从上图可以看到,样本量在(100, 150]这个范围的标签有101个,而样本量少于150的标签累积有416个。

从这个指标中,我们可以观察标签样本量的质量分布,也可以辅助确定热门标签样本量阈值。

其他的还可以统计:

  • 样本总量 - (在本案例中)不同于试题总量,样本是试题经过处理后,可用于训练模型的数据,样本总量一般会小于试题总量。

  • 样本标签数量质量分布 - 可以观察样本的标签个数的整体分布情况。

  • 样本文本长度质量分布 - 可以观察样本的文本长度的整体分布情况。

  • 词频分布 - 对文本进行切词后,统计词频,观察词频的分布,可以为特征工程提供参考。

可以多制定一些指标,更全面的了解数据集,通过观察指标,可以得到一些好的启发,这对优化模型很有用。

选择算法

本案例使用scikit-learn实现多标签分类任务,经过筛选,最终保留以下几个算法:

算法 简介
LinearSVC 线性支持向量分类,一种常用的SVM,可以更好地适应大量样本。
SGDClassifier 随机梯度下降分类器,使用随机梯度下降法训练模型的线性分类器,默认拟合一个线性SVM。
MultinomialNB 多项式朴素贝叶斯,实现了多项式分布数据的朴素贝叶斯算法。
ComplementNB 补码朴素贝叶斯,是标准多项式朴素贝叶斯算法的一种自适应算法,特别适用于不平衡数据。
KNeighborsClassifier K近邻分类器,实现了K近邻选举算法。

这里不对算法进行详细介绍,因为这脱离了本文的主题。后面如果有机会,再单独介绍。

为了方便对算法进行选型,我开发了模型选择模块,以每个算法为核心,分别构建了pipeline,并预置一些超参选项,用于网格搜索,然后将数据馈送进选型模块,生成模型评估报告。评估报告不绝对可靠,只能作为参考,因为一个模型表现不好,有可能是没有调优好。

通过分析评估报告,并结合实践和需求,最终选择了SGDClassifier。

准备样本

本环节的主要任务是按照数据规范和业务需求,将不符合要求的数据从数据集中剔除,同时将数据转换成下游任务可以处理的格式。

在本案例中,数据格式如下:

  • 知识点数据以树形结构存在
  • 试题的知识点标签以列表形式存在

业务需求如下:

  • 可预测的知识点数量尽可能多
  • 预测准确率尽可能高

将数据格式和业务需求考虑在内,梳理出的清洗转换目标主要包括:

  • 文本处理策略。对试题文本进行切词处理,根据策略筛选出有效的文本作为候选样本。
  • 标签处理策略。移除样本中的关联知识点,只保留最细化知识点。
  • 兼顾业务需求。通过设定样本量阈值,移除标签样本量小于样本量阈值的标签样本,调整标签数量和准确率。

文本处理策略

文本处理的目的主要包括两部分:

  • 将文本处理成模型可用的格式
  • 保证样本集的文本尽可能规范

将文本处理成模型可用的格式

特征提取模块使用空格分词,这对于中文不友好,所以需要对中文做一些预处理,这时就需要用到切词工具,将中文句子切分成单词,然后用空格连接起来,形成特征提取模块可以处理的格式。

本案例使用的切词工具是jieba。切词后,将词通过空格连接起来,此时中文文本就变成了空格分词的文本,和英文文本格式保持了一致。

在开发中,尝试了jieba的两种切词模式:精确模式和全模式。

精确模式会保留原始的文本顺序,按照切词顺序拼接,就可以恢复原来的文本。

全模式会尽可能的找出文本中的词或短语,部分字符会重复利用,增加了词的数量,但是丢失了原始的文本顺序。

示例如下:

# 精确模式
seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("/ ".join(seg_list))
# 我/ 来到/ 北京/ 清华大学

# 全模式
seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("/ ".join(seg_list))
# 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学

在本案例的优化实践中,发现精确模式+N-Gram的组合,可以使模型达到一个比较好的效果。

保证样本集的文本尽可能规范

在原始数据中,一般会存在异常的文本,这些文本如果作为样本训练模型,可能会降低模型的学习效果,所以需要根据实际情况定制一些清洗策略,保证样本集尽可能规范。

在本案例中,丢弃了文本切词数量小于2的文本。

标签处理策略

标签处理的目的主要是保证样本集的标签尽可能规范。

在本案例中,标签就是知识点。

知识点以树形结构存在,并分为三种类型,根据需求,只需要其中两种类型的知识点。

在分析原始数据时,发现以下问题:(部分问题是在模型优化分析中发现的)

  1. 一些较早的试题存在三种类型的知识点
  2. 少量试题的标签列表中会出现重复的知识点
  3. 部分课程的知识点数量庞大,但实际应用的知识点数量占比较小
  4. 知识点关联的试题量非常不均衡
  5. 部分课程中,人工标注知识点的标准不统一
  6. 部分课程知识点之间存在同义情况,不同试题使用了同义的不同知识点

这些问题中,一些问题可以使用程序处理,一些问题需要人工处理。

由于人工处理成本高,在本案例中,只处理了可以用程序处理的问题。

兼顾业务需求

在尽可能规范了文本和标签之后,还需要考虑到业务需求。

机器学习任务对于样本量有一定的要求,如果数据量太小,就不适合应用机器学习。

对于分类任务,类别越多,提高模型预测准确率的难度越大,并且在实际应用中,大部分样本集是不均衡的,这进一步增加了提高模型预测准确率的难度。

在本案例中,标签样本量非常不均衡,在制作样本集的时候,必须丢弃不满足样本量的标签数据,但是这样就会减少模型可预测的标签数量,于是就需要对模型预测准确度和可预测标签数量做一个平衡。

在本案例中,通过控制样本量最小阈值,来控制样本集中标签数量。

训练模型

当样本集准备好之后,就可以准备训练模型了。

在本案例中,训练模型环节主要分为三步:准备输入数据,构建模型pipeline,评估模型。

核心代码如下:

from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.feature_extraction.text import HashingVectorizer, TfidfTransformer
from sklearn.multioutput import MultiOutputClassifier
from sklearn.linear_model import SGDClassifier
from sklearn.pipeline import Pipeline
from .utils import mutillable_train_test_split
from .accuracy import mean_top_k_allin, mean_top_k_cross

......

# 拆分训练集和测试集
X_train, X_test, y_train, y_test = mutillable_train_test_split(
    texts, labels, test_fraction=0.2)

# 标签向量化
mlb = MultiLabelBinarizer()
y_train = mlb.fit_transform(y_train)
y_test = mlb.transform(y_test)

# 初始化分类器pipeline
text_clf = Pipeline([
    ('vect', HashingVectorizer(ngram_range=(1, 3), n_features=(2 ** 16))),
    ('tfidf', TfidfTransformer()),
    ('clf', MultiOutputClassifier(
        estimator=SGDClassifier(loss='modified_huber', early_stopping=True),
        n_jobs=clf_n_jobs)
    )
])

# 训练
text_clf.fit(X_train, y_train)

# 评估
pred_proba_test = text_clf.predict_proba(X_test)  # 概率预测
# 评估准确率
cross_acc_test_top1 = mean_top_k_cross(y_test, pred_proba_test, labels=labels, k=1)
cross_acc_test_top2 = mean_top_k_cross(y_test, pred_proba_test, labels=labels, k=2)
cross_acc_test_top3 = mean_top_k_cross(y_test, pred_proba_test, labels=labels, k=3)
# NOTE: 对于allin指标,太小的k不合理,因为真实标签数量可能大于k。
allin_acc_test_top5 = mean_top_k_allin(y_test, pred_proba_test, labels=labels, k=5)
allin_acc_test_top10 = mean_top_k_allin(y_test, pred_proba_test, labels=labels)
logger.info('=' * 50)
logger.info(f'Test CrossAcc Top1: {
      
      cross_acc_test_top1}')
logger.info(f'Test CrossAcc Top2: {
      
      cross_acc_test_top2}')
logger.info(f'Test CrossAcc Top3: {
      
      cross_acc_test_top3}')
logger.info(f'Test AllinAcc Top5: {
      
      allin_acc_test_top5}')
logger.info(f'Test AllinAcc Top10: {
      
      allin_acc_test_top10}')
logger.info('=' * 50)

准备输入数据

对应代码:

# 拆分训练集和测试集
X_train, X_test, y_train, y_test = mutillable_train_test_split(
    texts, labels, test_fraction=0.2)

# 标签向量化
mlb = MultiLabelBinarizer()
y_train = mlb.fit_transform(y_train)
y_test = mlb.transform(y_test)

拆分数据集部分

使用了自定义的数据集拆分方法,这个方法是为了提高可预测知识点数量而开发的。

核心思想:不考虑标签可能存在的潜在关系,以多分类的思想拆分数据。

实现逻辑:根据标签进行分组,每个分组内抽样指定比例的测试集,合并这些数据,生成测试集,则剩下的数据集作为训练集。训练集和测试集分别按照文本分组合并标签,恢复文本对应多标签的格式。

弊端:破坏了原始的文本标签对应关系。

标签向量化部分

使用sklearn模块将标签转换为模型可识别的形式。

构建模型pipeline

对应代码:

# 初始化分类器pipeline
text_clf = Pipeline([
    ('vect', HashingVectorizer(ngram_range=(1, 3), n_features=(2 ** 16))),
    ('tfidf', TfidfTransformer()),
    ('clf', MultiOutputClassifier(
        estimator=SGDClassifier(loss='modified_huber', early_stopping=True),
        n_jobs=clf_n_jobs)
    )
])

# 训练
text_clf.fit(X_train, y_train)

pipeline前两步是进行特征提取,最后一步是算法模型。整个pipeline构成一个端到端的模型。

特征提取

文本特征提取使用词袋模型的N-Gram模型。

文本特征提取,主要是统计token出现次数,然后将token出现的次数作为特征,一个token就是一个词,实际上就是词频统计。

在sklearn中,有两个类可以实现文本的词频特征提取,分别是:CountVectorizerHashingVectorizer

这两个类都可以转化一个文本文档集合到一个token统计矩阵,这个矩阵是一个稀疏矩阵。

不同的是,CountVectorizer会将特征序列保存进一个词汇表,而HashingVectorizer使用hash函数将特征序列映射到一个稀疏矩阵。

CountVectorizer保存了一个词汇表,所以我们可以获取到这个词汇表,并对原始特征进行分析。

HashingVectorizer使用hash函数将特征序列映射到一个稀疏矩阵,这样会丢失原始特征,但是很节约内存。

当特征量非常大时,HashingVectorizer是很好的选择,HashingVectorizer中可以设置特征空间大小,为了避免hash碰撞,一般会设置一个比较大的数值,默认是2**20。

在实践中发现,HashingVectorizer的特征空间的大小也直接影响到模型的大小,所以这个指标也可以用来降低模型大小,当然前提是模型评估指标不会出现严重的下降。

上面的步骤统计了词频,但是这样会有一个问题,更长的文档比更短的文档,将有更高的平均统计值,即使两个文档可能在讨论同一个话题。

为了避免这些潜在的不一致问题,就要用到大名鼎鼎的tf-idf算法了,sklearn中也提供了相关的类:TfidfTransformer,该类可以将词频向量转化成tf-idf向量。

tf-idf虽然很强力,但是也不是在任何算法中都能使用,比如在朴素贝叶斯算法中,就不推荐使用tf-idf,尽管在某些实践中,使用tf-idf的朴素贝叶斯模型表现也很好。

在本案例的实践探索中,不使用tf-idf的朴素贝叶斯模型要比使用tf-idf的朴素贝叶斯模型表现好很多。

下面是sklearn官网中,介绍多项式朴素贝叶斯时的一段文本:

MultinomialNB implements the naive Bayes algorithm for multinomially distributed data, and is one of the two classic naive Bayes variants used in text classification (where the data are typically represented as word vector counts, although tf-idf vectors are also known to work well in practice).

算法模型

在本案例中,算法模型的选型考虑了概率输出和预测准确率两方面,经过实践评估确定。

评估模型

对应代码:

# 评估
pred_proba_test = text_clf.predict_proba(X_test)  # 概率预测
# 评估准确率
cross_acc_test_top1 = mean_top_k_cross(y_test, pred_proba_test, labels=labels, k=1)
cross_acc_test_top2 = mean_top_k_cross(y_test, pred_proba_test, labels=labels, k=2)
cross_acc_test_top3 = mean_top_k_cross(y_test, pred_proba_test, labels=labels, k=3)
# NOTE: 对于allin指标,太小的k不合理,因为真实标签数量可能大于k。
allin_acc_test_top5 = mean_top_k_allin(y_test, pred_proba_test, labels=labels, k=5)
allin_acc_test_top10 = mean_top_k_allin(y_test, pred_proba_test, labels=labels)
logger.info('=' * 50)
logger.info(f'Test CrossAcc Top1: {
      
      cross_acc_test_top1}')
logger.info(f'Test CrossAcc Top2: {
      
      cross_acc_test_top2}')
logger.info(f'Test CrossAcc Top3: {
      
      cross_acc_test_top3}')
logger.info(f'Test AllinAcc Top5: {
      
      allin_acc_test_top5}')
logger.info(f'Test AllinAcc Top10: {
      
      allin_acc_test_top10}')
logger.info('=' * 50)

在本案例中,模型的预测结果是一个列表,使用传统分类的准确率指标无法评估部分预测准确的情况,所以自定义了一些多标签准确率指标,主要围绕TopK的预测准确率情况开发指标。

TopK策略:从预测结果中取出所有概率非0的标签,按照概率从大到小排列,取TopK。

这里应用了两种指标:

  • mean_top_k_cross:TopK标签列表和真实标签列表有交集,记为1,反之为0,统计均值。
  • mean_top_k_allin:TopK标签列表包含真实标签列表,记为1,反之为0,统计均值。

模型调优

主要是对pipeline进行调优,包括各阶段模块选择和超参选择。

利用sklearn的网格搜索功能,可以对pipeline的超参进行调优,其内部对超参进行排列组合,每组超参都会进行一次训练,记录评估结果,最后选出效果最好的超参组合。

更复杂的pipeline超参调优可以参考这个思路开发。

模型应用

存储、加载模型:

from joblib import dump, load

my_model = './model.joblib'
dump(text_clf, my_model)  # 存储模型
model = load(my_model)  # 加载模型

应用分析

应用分析是基于采集的应用数据,设计一些合理的指标,观测模型在真实应用中的表现。

应用分析指标结合上面的模型可以为模型优化方向和策略提供一些参考。

在本案例中,应用分析数据来源主要包括两部分:

  • 用户数据
  • 日志数据

用户数据

此数据采集自用户使用模型时的记录数据,数据以试题为单位,主要包含以下下属性:

属性 说明
id 试题id
courseid 课程id
predicted 模型预测的标签
confirmed 用户标注的标签
adopted 模型预测命中的标签数量
unexpected 模型预测未命中的标签数量
confirmdate 用户打标的时间

根据这些数据,可以设计出多种指标,目前为止,围绕adoptedunexpected两个属性,设计了如下指标:

指标 说明(以每日每个课程分组统计) 用途
日平均命中率 平均命中率。 观测模型应用过程中的命中率的总体平均表现。
日全部命中率 预测结果全部命中的试题数量占全部试题数量的比例。 观测模型应用过程中的命中全部标签的表现。
日部分命中率 预测结果至少命中一个的试题数量占全部试题数据的比例。 观测模型应用过程中的至少命中一个标签的表现。
日平均标注数量 试题标注标签数量的平均值。 观测用户标注标签数量的平均情况。
日平均命中数量 预测结果命中数量的平均值。 观测模型命中标签数量的平均情况。
日平均未命中数量 预测结果未命中数量的平均值。 观测模型未命中标签数量的平均情况。

根据上述属性,还可以计算命中位置相关指标。由于在本案例中,预测结果是以概率进行排序的TopK结果,命中标签的位置越靠近Top1,说明预测效果越好。所以命中位置相关指标对模型的观测有一定的参考意义,不过这个指标设计相对复杂,目前没有进行统计。

在这里描述本案例的指标设计,目的是说明在实际应用中,可以根据具体情况和需求设计不同的指标,这些指标并没有统一的规则限制,只要对模型优化有意义即可。

日志数据

根据具体应用场景,设计一些日志分析指标,用来分析服务应用情况。

本案例(知识点预测服务)在应用中属于试题标签预测服务的一个子服务,试题标签预测服务目前还有一个子服务是课程预测服务(一个多分类模型,这里不做介绍)。

应用日志使用ELK进行分析。

整个试题标签预测服务的日志分析,目前设计了四个指标:

  • 接口访问组成日统计
  • 接口访问量占比
  • 知识点接口访问趋势周统计
  • 知识点接口访问量统计

(由于日志数据接入ELK时间不长,下图只有从接入时间开始的日志数据)

在这里插入图片描述

以本案例(知识点预测服务)为目标的分析指标有:知识点接口访问趋势周统计知识点接口访问量统计

这里简单说明一下指标作用。

通过知识点接口访问趋势周统计,可以观测近期知识点预测服务各课程的接口访问趋势,如果趋势发生异常变化,可以提示我们深入分析异常原因。

通过知识点接口访问量统计,可以观测近期知识点预测服务各课程的接口访问量,对于访问量大的接口,可以重点优化其关联的模型。

模型优化

模型优化主要包括两部分:

  • 特征工程优化:优化数据集,更好的抽象数据,更准确更规范的数据。
  • 模型优化:模型超参优化,替换模型。

假设上面开发的模型版本为v1.0,下面以模型版本更新说明本案例目前为止做过的优化。

v1.1

在这个版本,主要优化了pipeline的超参。

# v1.0
text_clf = Pipeline([
    ('vect', HashingVectorizer(ngram_range=(1, 3), n_features=(2 ** 16))),
    ('tfidf', TfidfTransformer()),
    ('clf', MultiOutputClassifier(
        estimator=SGDClassifier(loss='modified_huber', early_stopping=True),
        n_jobs=clf_n_jobs)
    )
])

# v1.1
text_clf = Pipeline([
    ('vect', HashingVectorizer(  # 调整超参 token_pattern=r'(?u)\b\w+\b'
        token_pattern=r'(?u)\b\w+\b', ngram_range=(1, 3), n_features=(2 ** 16))),
    ('tfidf', TfidfTransformer()),
    ('clf', MultiOutputClassifier(
        estimator=SGDClassifier(loss='modified_huber', early_stopping=True),
        n_jobs=clf_n_jobs)
    )
])

主要是更改了HashingVectorizer的token_pattern的默认值。

token_pattern参数主要用来控制token的模式,默认值会忽略字符长度小于2的token,例如标点符号和单字符英文字母。

指定token_pattern=r'(?u)\b\w+\b' ,就不会忽略字符长度小于于2的token。

在本案例中,token主要是中文词和符号,字符长度小于2的token可能会成为有效的特征。

优化之后,模型评估指标有较明显的提升,应用分析指标也有小幅提升。

v1.2

在这个版本,主要优化了特征工程。

优化项主要包括:

  • 重构文本处理逻辑
  • 重构标签处理逻辑
  • 更新数据集拆分方法

重构文本处理逻辑

使用paddlehub词法分析lac替代jieba切词。

目的是增加语义信息,这有利于丰富文本特征。

jieba也有词法分析,并且引入了paddle模式。

考虑到paddle拥有更先进的NLP技术,决定使用paddlehub词法分析。

paddlehub lac示例:

import paddlehub as hub

lac = hub.Module(name="lac")
test_text = ["今天是个好日子", "天气预报说今天要下雨", "下一班地铁马上就要到了"]

results = lac.cut(text=test_text, use_gpu=False, batch_size=1, return_tag=True)

for result in results:
    print(result['word'])
    print(result['tag'])

# ['今天', '是', '个', '好日子']
# ['TIME', 'v', 'q', 'n']
# ['天气预报', '说', '今天', '要', '下雨']
# ['n', 'v', 'TIME', 'v', 'v']
# ['下', '一班', '地铁', '马上', '就要', '到', '了']
# ['f', 'm', 'n', 'd', 'v', 'v', 'xc']

在本案例中,将试题文本转换成切词和语义结合的形式,并使用空格连接切词,如下:

test_text = ["今天是个好日子", "天气预报说今天要下雨", "下一班地铁马上就要到了"]

results = lac.cut(text=test_text, use_gpu=False, batch_size=1, return_tag=True)

for result in results:
    wt = []
    for w, t in zip(result['word'], result['tag']):
        wt.append(w + '/' + t)
    print(' '.join(wt))

# 今天/TIME 是/v 个/q 好日子/n
# 天气预报/n 说/v 今天/TIME 要/v 下雨/v
# 下/f 一班/m 地铁/n 马上/d 就要/v 到/v 了/xc

另外更新了数据清洗策略,进一步规范文本数据。

重构标签处理逻辑

标签处理逻辑主要进行了两个方面的重构:

  • 调整样本量阈值策略
  • 更新标签清洗逻辑

移除样本量限制

v1.0为了兼顾部分课程的的准确率和标签数量,使用了样本量阈值策略,不同课程的样本量阈值不同,实质上是准确率优先。

v1.2调整了样本量阈值策略,全部课程统一使用50作为最小样本量阈值,50是为了保证有效的样本量,这个版本的优化方向是标签数量优先。

更新标签清洗逻辑

v1.0为了提高标签数量,将单个标签作为一个单位进行样本量筛选,而不是将标签组合作为一个单位进行样本量筛选。

v1.2的核心思想是将标签组合作为一个单位进行样本量筛选。其中为了提高标签组合的数量、标签数量和总样本量,主要使用了标签组合派生策略。

这里对标签组合派生策略进行简单的介绍:

  1. 统计单标签样本量
  2. 对原始标签组合进行排序,按照标签样本量从小到大,标签id从小到大排列
  3. 统计标签组合样本量
  4. 对于不满足样本量阈值的标签组合进行子集派生,依次移除组合列表的第一个标签,生成N个子集
  5. 统计派生后的数据的样本量,保留满足样本量阈值的最大标签子集
  6. 合并所有满足样本量阈值的数据

更新数据集拆分方法

v1.0为了配合标签清洗逻辑,以及提高Top10准确率,使用了自定义拆分方法,忽略标签之间的潜在联系,将多标签分类按照多分类的方法进行拆分再重组,这样做实质上破坏了文本与标签的原始对应关系。

v1.2考虑优先保持文本与标签的原始分布,使用了sklearn的数据集拆分方法。

指标分析

v1.2相对于v1.1,评估指标提升明显。

这里以小学数学为例。

模型评估指标有较明显的提升,准确率提升明显,标签数量提升明显。

在这里插入图片描述

应用分析指标变化不大,在标签数量明显增加的情况下,应用指标没有下降,算是一种隐性提升了。

在这里插入图片描述

未来优化方向

主要还是围绕特征工程,模型算法两方面进行优化。

模型算法方面计划使用深度学习,可以尝试当下主流的NLP技术,比如BERT。

特征工程方面需要根据模型算法进行设计,一个优秀的特征工程,可以极大的提升模型表现。

猜你喜欢

转载自blog.csdn.net/xwd127429/article/details/123788939