使用Fasttext进行简单的文本分类

使用Fasttext进行简单的文本分类指南

只使用有标签的数据集、Fasttext库和10分钟创建文本分类模型

介绍

自然语言处理在商业用例中的应用速度呈指数级增长。对于许多情况而言,文本数据的分类通常是一个耗时的手动过程,因此文本分类是可以改变业务的最简单的人工智能自动化之一。

文本分类涉及开发一个能够将标签分配给输入文本的人工智能。这些人工智能模型可以作为有监督学习问题进行训练,根据先前看到的具有相应标签的示例来调整权重。

为了在Python中进行文本分类,我们可以使用fasttext。Fasttext是一个开源且轻量级的Python库,可以快速简单地创建文本分类模型。

我将在这个演示中使用的数据集可以在这里找到。这个数据集包含与冠状病毒有关的推文和相关情感。有五种可能的类别:非常消极、消极、中立、积极和非常积极。我们将使用fasttext来构建一个使用这个数据集进行推文分类的人工智能模型。

需要注意的一点是,每个标签之间存在一个灰色区域:也就是说,积极与非常积极之间的“界限”在哪里?或者“消极”与“非常消极”之间的界限在哪里?肯定有一些推文位于这两个类别的中间,而关于标签最终落在界线的哪一边,标准可能是主观的。因此,很难达到100%的验证准确度。

如果您想自己尝试这段代码,可以在这里找到kaggle笔记本。

处理

以下是准备数据以适应fasttext模型的一些关键预处理函数。请注意,这些步骤不会以任何方式清理或修改原始文本,我将仅专注于为输入到机器学习模型的数据设置正确格式。

  1. process_data子选择感兴趣的列(我们只需要文本和相关标签)。对于标签,我们在前面添加字符串“__label__”,因为fasttext期望标签具有这个前缀。
  2. split_data将数据分成训练集、验证集和测试集。
  3. save_data_as_txt创建fasttext所需的txt文件。fasttext期望包含文本和相关标签的txt文件,这就是该函数创建的内容。
def process_data(df_train: pd.DataFrame, df_test: pd.DataFrame) -> Tuple[pd.DataFrame, pd.DataFrame]:    df_train = df_train[["Sentiment", "OriginalTweet"]]    df_test = df_test[["Sentiment", "OriginalTweet"]]    df_train["Sentiment"] = df_train["Sentiment"].apply(lambda x: "__label__" + "_".join(a for a in x.split()))    df_test["Sentiment"] = df_test["Sentiment"].apply(lambda x: "__label__" + "_".join(a for a in x.split()))    return df_train, df_testdef split_data(df_train: pd.DataFrame, df_test: pd.DataFrame, train_fraction: float) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:    split_point = round(df_train.shape[0] * train_fraction)    df_train, df_val = df_train.iloc[:split_point], df_train.iloc[split_point:]    return df_train, df_val, df_testdef save_data_as_txt(df_train: pd.DataFrame, df_validation: pd.DataFrame, df_test: pd.DataFrame) -> None:    df_train.to_csv(train_filepath,index=False,sep=" ",header=None,quoting=csv.QUOTE_NONE,quotechar="",escapechar=" ",)    df_validation.to_csv(validation_filepath,index=False,sep=" ",header=None,quoting=csv.QUOTE_NONE,quotechar="",escapechar=" ",)    df_test.to_csv(test_filepath,index=False,sep=" ",header=None,quoting=csv.QUOTE_NONE,quotechar="",escapechar=" ",)

现在,只需设置一些路径并调用上述函数,我们就可以为fasttext创建一个模型所需的所有数据了!

df_train = pd.read_csv("/kaggle/input/covid-19-nlp-text-classification/Corona_NLP_train.csv", encoding="latin1")df_test = pd.read_csv("/kaggle/input/covid-19-nlp-text-classification/Corona_NLP_test.csv", encoding="latin1")train_filepath = "df_train.txt"validation_filepath = "df_val.txt"test_filepath = "df_test.txt"df_train, df_test = process_data(df_train=df_train, df_test=df_test)df_train, df_validation, df_test = split_data(df_train=df_train, df_test=df_test, train_fraction=0.9)save_data_as_txt(df_train=df_train, df_validation=df_validation, df_test=df_test)

拟合模型

实际上,所有的辛勤工作已经完成。要拟合模型,只需调用该方法

model = fasttext.train_supervised(input=str(train_filepath))

,然后就大功告成了!我们有了一个训练好的模型。

虽然我们的模型已经训练好了,但我们不知道我们训练好的模型的表现如何。让我们写一个函数来返回所有标签类别的平均准确度:

def obtain_accuracies(model):    train_results = model.test(path=train_filepath)    validation_results = model.test(path=validation_filepath)    test_results = model.test(path=test_filepath)    return train_results, validation_results, test_results

使用以下代码调用此函数:

train_results, validation_results, test_results = obtain_accuracies(model=model)print(train_results, validation_results, test_results)

将显示基线训练准确度为71.0%,验证准确度为54.1%,测试准确度为48.4%。对于一个基线模型来说,这个结果还不错!

另外,如介绍中所提到的,如果AI将一个“极端负面”的文本分类为“负面”,那么在计算准确度时就算作错误。因此,模型产生的许多错误是“相邻错误”,这意味着实际标签是相邻标签之一。

超参数调优

到目前为止,我们有一个基线模型表现不错。我们如何更进一步改进呢?幸运的是,可以传递一些超参数到fasttext.train_supervised方法中,我们可以尝试调整它们,看看模型的性能是否会提高。

这就是我们的验证集派上用场的地方 – 我们将尝试几组不同的超参数,并在验证集上评估生成的模型。在验证集上准确度最高的一组参数将会是我们用于最终模型的参数。

我们应该尝试哪些超参数组合呢?虽然我们可以尝试手动设置超参数并尝试自己改进(或使用超参数调优库,如Optuna),但我懒得那么做,更喜欢使用随机搜索。

def create_training_params(baseline: bool = False) -> Dict[str, Any]:    if baseline:        return {}    epoch = random.randint(2, 120)    wordNgrams = random.randint(1, 6)    lr = np.random.uniform(0.02, 0.5)    dim = random.randint(50, 200)    minn = random.randint(0, 5)    maxn = random.randint(0, 5)    minCount = random.randint(1, 5)    ws = random.randint(2, 10)    lrUpdateRate = random.randint(50, 200)    bucket = random.randint(200000, 20000000)    return {        "epoch": epoch,        "wordNgrams": wordNgrams,        "lr": lr,        "dim": dim,        "minn": minn,        "maxn": maxn,        "minCount": minCount,        "lrUpdateRate": lrUpdateRate,        "ws": ws,        "bucket": bucket,        "thread": 12,    }

此函数将返回一组随机选择的参数。

我们可以通过以下代码尝试单组随机参数:

model = fasttext.train_supervised(input=str(train_filepath), **create_training_params())

让我们添加几行额外代码,创建一个更完整的超参数调优流程。下面,我们声明搜索的迭代次数,并将当前最佳准确度初始化为基线模型的准确度,当前最佳参数初始化为空字典(即默认参数)。

然后,我们运行一个循环,每次迭代都会随机生成参数,并训练和评估模型。如果新的准确度大于之前的记录,我们就更新最佳准确度,并保存最佳参数以供将来参考。

iterations = 10best_accuracy, best_params = validation_results[1], {}for it in range(iterations):    params = create_training_params()    model = fasttext.train_supervised(input=str(train_filepath), **params)    train_results, validation_results, test_results = obtain_accuracies(model=model)    if validation_results[1] > best_accuracy:        best_accuracy = validation_results[1]        best_params = params    print(f"Best accuracy so far: {best_accuracy}")print(f"Best params: {best_params}")

通过这样做,我们能够获得最终准确率为56.1%!

结论

在使用此数据集挖掘其他笔记本时,我可以看到朴素贝叶斯方法的准确率为70%,或者使用BERT方法的准确率为88%。显然,这比我们使用fasttext获得的结果要好得多。然而,在这些实现中,对数据集应用了大量的文本预处理,而这是我们没有做过的。

如果文本数据不干净,fasttext人工智能可能会查看垃圾数据,试图找到标记文本的模式。改进性能的进一步步骤是对数据集应用预处理方法。此外,原始数据框架中还有更多的数据列可用作模型输入(我们只使用了文本和标签列)。我会把这些下一步留给你,因为本文的目标是提供fasttext库的通用概述。

我们学到了:

  • 什么是fasttext库
  • 如何对数据集进行预处理,以便在fasttext中使用
  • 如何在数据上拟合一个基线模型
  • 如何调整超参数以改进基线结果

感谢您阅读到最后!我希望您会觉得这篇文章有帮助,并祝您在文本分类任务中好运。

链接:

Linkedin: https://www.linkedin.com/in/sergei-issaev/

Github: https://github.com/sergeiissaev

Kaggle: https://www.kaggle.com/sergei416

VoAGI: https://medium.com/@sergei740

Twitter: https://twitter.com/realSergAI

了解更多关于Vooban的信息: https://vooban.com/en