使用Fasttext进行简单的文本分类
使用Fasttext进行简单的文本分类指南
只使用有标签的数据集、Fasttext库和10分钟创建文本分类模型
介绍
自然语言处理在商业用例中的应用速度呈指数级增长。对于许多情况而言,文本数据的分类通常是一个耗时的手动过程,因此文本分类是可以改变业务的最简单的人工智能自动化之一。
文本分类涉及开发一个能够将标签分配给输入文本的人工智能。这些人工智能模型可以作为有监督学习问题进行训练,根据先前看到的具有相应标签的示例来调整权重。
为了在Python中进行文本分类,我们可以使用fasttext。Fasttext是一个开源且轻量级的Python库,可以快速简单地创建文本分类模型。
我将在这个演示中使用的数据集可以在这里找到。这个数据集包含与冠状病毒有关的推文和相关情感。有五种可能的类别:非常消极、消极、中立、积极和非常积极。我们将使用fasttext来构建一个使用这个数据集进行推文分类的人工智能模型。
需要注意的一点是,每个标签之间存在一个灰色区域:也就是说,积极与非常积极之间的“界限”在哪里?或者“消极”与“非常消极”之间的界限在哪里?肯定有一些推文位于这两个类别的中间,而关于标签最终落在界线的哪一边,标准可能是主观的。因此,很难达到100%的验证准确度。
如果您想自己尝试这段代码,可以在这里找到kaggle笔记本。
处理
以下是准备数据以适应fasttext模型的一些关键预处理函数。请注意,这些步骤不会以任何方式清理或修改原始文本,我将仅专注于为输入到机器学习模型的数据设置正确格式。
- process_data子选择感兴趣的列(我们只需要文本和相关标签)。对于标签,我们在前面添加字符串“__label__”,因为fasttext期望标签具有这个前缀。
- split_data将数据分成训练集、验证集和测试集。
- 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