首页>>科技 >>内容

文本分类的流程,带你从头构建文本分类器

发布时间:2023-10-31 10:14:10编辑:温柔的背包来源:

很多朋友对文本分类的流程,带你从头构建文本分类器不是很了解,每日小编刚好整理了这方面的知识,今天就来带大家一探究竟。

文本分类的流程,带你从头构建文本分类器

文本分类文本分类是NLP 中最常见的任务之一。它可以用于广泛的应用程序或开发成程序,例如将用户反馈文本标记为某个类别,或者根据语言自动对客户文本进行分类。另外,我们平时看到的电子邮件垃圾邮件过滤器也是文本分类最熟悉的应用场景之一。

另一种常见的文本分类类型是情感分析,其目的是识别给定文本的极性。例如,像特斯拉这样的公司可能会分析如下推文,以确定人们是否喜欢其新车天窗。

假设您是一名数据科学算法工程师,需要构建一个系统来自动识别Twitter 上的人们对您公司产品所表达的情绪极性,例如“愤怒”或“高兴”。在本章中,我们将使用DistilBERT 模型(BERT 变体)来解决此任务。 PS:[V. Sanh 等人,“DistilBERT,BERT 的精炼版本:更小、更快、更便宜、更轻”,(2019 年)。]

该模型的主要优点是它实现了与BERT 相当的性能,同时占用更少的内存并使训练和推理更加高效。这使我们能够在几分钟内训练一个分类器,如果你想训练一个更大的BERT 模型,你可以简单地改变预训练模型的权重,检查点对应于加载到给定Transformer 模型中的权重集。

这也将是我们第一次接触Hugging Face 生态系统中的三个核心库:Datasets、Tokenizers 和Transformers。如下图所示,这些库将使我们能够快速从原始文本转变为可用于推断新推文的微调模型。因此,本着擎天柱(变形金刚)的精神,让我们深入研究并“改革并增强力量!”

数据集为了构建我们的推文情绪分类器,我们将使用一篇论文中的高质量数据集,该论文探讨了英语Twitter 消息中情绪的表达方式。

PS:[E. Saravia 等人,“CARER:情绪识别的情境化情感表征”,2018 年自然语言处理经验方法会议论文集(2018 年10 月至11 月):3687-3697, http://dx.doi.org/10.18653/v1/D18 -1404.]。

与大多数仅处理“积极”和“消极”极性的情感分析数据集不同,该数据集包含六种基本情绪:愤怒、厌恶、恐惧、喜悦、悲伤和惊讶。给定一条推文,我们的任务是训练一个模型,将其分类为一种情绪。

首先介绍Hugging Face 数据集我们将使用数据集从Hugging Face Hub 下载数据。我们可以使用list_datasets() 函数查看Hub 上有哪些数据集:

fromdatasetsimportlist_datasetsall_datasets=list_datasets()print(f'Thereare{len(all_datasets)}datasetscurrentlyavailableontheHub')print(f'Thefirst10are:{all_datasets[:10]}')Hub上当前有3480 个数据集前10 个是:['acronym_identification ', 'ade_corpus_v2', 'adversarial_qa','aeslc', 'afrikans_ner_corpus', 'ag_news', 'ai2_arc', 'air_dialogue','ajgt_twitter_ar', 'allegro_reviews'] 我们看到每个数据集都有一个名称,所以让我们加载使用load_dataset() 函数的情感数据集:

#检查我们本地计算机是否可以连接到数据集地址。出于考虑,你能想办法importrequestsrequests.head('https://www.dropbox.com/s/1pzkadrvffbqw6o/train.txt?dl=1')Response [301] fromdatasetsimportload_datasetemotions=load_dataset('emotion') 0%| | 0/3 [00:00?it/s]如果我们看看我们的情绪对象内部:

情感DatasetDict({ train: Dataset({ features: ['text', 'label'], num_rows: 16000 }) 验证: Dataset({ features: ['text', 'label'], num_rows: 2000 }) 测试: Dataset ({ features: ['text', 'label'], num_rows: 2000 })}) 我们看到它类似于Python字典。每个键值对应不同的数据集划分。我们可以通过字典查询来访问具体的数据集,例如下面是获取训练集:

train_ds=emotions['train']train_dsDataset({ features: ['text', 'label'], num_rows: 16000}) 它返回Dataset 类的实例。 Dataset对象是Datasets中的核心数据结构之一。我们将在本书中使用它,您将逐渐掌握它的许多功能。对于初学者来说,它的行为就像一个常规的Python 数组或列表,因此我们可以检查它的长度:

len(train_ds)16000 或通过索引访问样本:

train_ds[0]#训练集的第一个数据{'text': 'i did not Feel humiliated', 'label': 0} 这里,我们看到一条格式化为字典的数据,其中的键对应于列名:

train_ds.column_names['text', 'label']type(train_ds['label'])list 我们可以看到数据是tweet文本和情感标签,这表明该数据集是建立在Apache Arrow上的(Arrow定义了一个比原生Python 更节省内存的类型列格式)。我们可以通过访问Dataset 对象的features 属性来查看幕后使用的数据类型:

print(train_ds.features){'text': Value(dtype='string', id=None), 'label': ClassLabel(num_classes=6,names=['sadness', 'joy', 'love', ' anger', 'fear', 'surprise'],names_file=None,id=None)} 本例中,text列的数据类型是字符串,label列是一个特殊的ClassLabel对象,里面包含了相关的class 有关名称及其到整数的映射的信息。我们还可以使用切片来访问多行:

print(train_ds[:5]){'text': ['我没有感到羞辱', '仅仅因为身边有一个关心并且清醒的人,我就可以从感到如此绝望变成充满希望', '抓紧时间发帖我觉得贪婪是错误的”,“我对壁炉感到怀旧,我会知道它仍然在酒店内”,“我感到不满”],“标签”:[0,0,3,2,3]}请注意,在这种情况下,字典值现在是列表而不是单个元素。我们还可以通过名称获取完整的列:

print(train_ds['text'][:5])['我没有感到羞辱', '仅仅因为身边有一个关心并且清醒的人,我就可以从感到绝望变成充满希望', '我抓紧时间发布我感觉贪婪错了','我对壁炉感到怀旧,我会知道它仍然在财产上','我感到不满']现在我们已经了解了如何使用数据集加载和检查数据,让我们推文的内容经过了一些健全性检查。

自定义Huggingface 数据集如果我们的数据集不在Huggingface 数据集中怎么办?

我们将使用Hugging Face Hub 下载本书中大多数示例的数据集。但在许多情况下,我们经常发现自己正在处理存储在笔记本电脑或远程公司服务器上的数据。 Datasets 提供了多个加载脚本来处理本地和远程数据集。数据集加载中显示了最常见数据格式的示例。

[[dataset-loading]].如何加载各种格式的数据集[options='header']|======|数据格式|加载脚本|示例| CSV | `csv` | `load_dataset('csv' , data_files='my_file.csv')` |文字| `文本` | `load_dataset('text', data_files='my_file.txt')` | JSON | `json` | `load_dataset('json', data_files=' my_file.jsonl')`|======我们可以看到,对于每种数据格式,我们只需要把相关的加载脚本传递给load_dataset()函数即可,如下以及指定一个或多个文件的路径或URL 的data_files 参数。例如,情感数据集的源文件实际上托管在Dropbox 上,因此加载数据集的另一种方法是首先下载它的子集。例如,我们可以将训练集下载到本地:

dataset_url='https://www.dropbox.com/s/1pzkadrvffbqw6o/train.txt'#没有wget命令,可以在这里手动下载! wget{dataset_url}--2022-03-14 1113-- https://www.dropbox.com/s/1pzkadrvffbqw6o /train.txt正在连接到127.0.0.1:7890.已连接。错误:无法验证www.dropbox.com的证书,已颁发通过`/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA':无法在本地验证颁发者的权限。要不安全地连接到www.dropbox.com,请使用` --no-check-certificate'。无法建立SSL 连接。head-n1train.txt'head ' 未被识别为内部或外部命令、可操作程序或批处理文件。我们可以看到这里没有列标题,每条推文和情绪都用分号分隔。尽管如此,这与CSV 文件非常相似,因此我们可以通过使用csv 脚本并将data_files 参数指向train.txt 文件来本地加载数据集:

#hide_outputemotions_local=load_dataset('csv',data_files='data/train.txt',sep=';',names=['text','label']) 0%| | 0/1 [00:00?it/s] 这里我们还指定分隔符的类型和列的名称。更简单的方法是将data_files 参数指向URL 本身

#hide_outputdataset_url='https://www.dropbox.com/s/1pzkadrvffbqw6o/train.txt?dl=1'emotions_remote=load_dataset('csv',data_files=dataset_url,sep=';',names=['text','label' ]) 0%| | 0/1 [00:00?it/s] 它会自动为您下载并缓存数据集。正如您所看到的,load_dataset() 函数非常通用。我们建议您查看Datastes 文档以获取完整的概述

将Dataset 对象转换为DataFrame 虽然Datasets 提供了许多用于对数据进行切片和切块的基本功能,但将Dataset 对象转换为Pandas DataFrame 通常很方便,这样我们就可以访问高级API 来进行数据可视化。为了启用转换,数据集提供了set_format() 方法,允许我们更改数据集的输出格式。请注意,这不会更改基础数据格式(即箭头表),如果需要,您可以稍后切换到其他格式:

importpandasaspdemotions.set_format(type='pandas')df=emotions['train'][:]df.head()textlabel0我没有感到羞辱01我可以从感觉如此绝望到如此该死.02我抓紧时间发帖我觉得贪婪的错误33我对壁炉感到怀念.24我感到不满3 正如您在上面所看到的,列名称已被保留,前几行与我们之前的数据视图相同。但是,label 表示为整数,因此让我们使用label 函数的int2str() 方法在DataFrame 中创建一个具有相应标签名称的新列:

deflabel_int2str(row):returnemotions['train'].features['label'].int2str(row)df['label_name']=df['label'].apply(label_int2str)df.head()textlabellabel_name0i没有感到羞辱0sadness1i可以从感觉如此绝望到如此该死.0悲伤2我抓紧时间发帖我感到贪婪错误3愤怒3我对壁炉感到怀旧.2爱4我感到不满3愤怒在我们深入构建分类器之前,让我们仔细看看数据集。正如Andrej Karpathy 在他著名的博客文章“训练神经网络的秘诀”中指出的那样,成为“数据中的一员”是训练伟大模型的重要一步!

检查类分布每当您处理文本分类问题时,检查数据集中的类分布是第一步,也是最好的习惯。与平衡数据集相比,具有偏态类别分布的数据集在训练损失和评估指标方面可能需要不同的处理。

使用Pandas 和Matplotlib,我们可以快速可视化类分布,如下所示:

importmatplotlib.pyplotaspltdf['label_name'].value_counts(ascending=True).plot.barh()plt.title('FrequencyofClasses')plt.show() 这种情况下,我们可以看到数据集严重不平衡;快乐和悲伤类别出现的频率很高,而爱和惊喜的出现频率要低5 到10 倍。处理不平衡数据的方法有多种,包括:

对少数群体进行随机过采样。对多数类进行随机欠采样。从代表性不足的类别中收集更多标记数据。为了在本章中保持简单,我们将使用原始的、不平衡的类频率。如果您想了解有关这些采样技术的更多信息,我们建议您查看Imbalanced-learn 库。只需确保在创建训练/测试拆分之前不应用采样方法,否则它们之间会出现大量泄漏!

现在我们已经了解了类,让我们看看推文本身。

我们的推文有多长? Transformer 模型具有最大输入序列长度,称为最大上下文大小。对于使用DistilBERT 的预训练模型,最大上下文大小为512 个标记,相当于几个文本段落。正如我们将在下一节中看到的,令牌是一串底层文本;现在,我们将令牌视为一个单词。通过查看每条推文的单词分布,我们可以粗略估计每种情绪的推文长度:

df['WordsPerTweet']=df['text'].str.split().apply(len)#用空格分割得到雷标长度df.boxplot('WordsPerTweet',by='label_name',grid=False,showfliers=False,color='black')plt.suptitle('')plt.xlabel('')plt.show() 从图中我们可以看出,对于每种情绪,大多数推文的长度约为。最长的推文有15 个字,远低于DistilBERT 的最大长度。超过模型上下文大小的文本需要被截断,如果截断的文本包含关键信息,可能会导致性能下降;对于这个数据集,这似乎不是问题。

现在让我们弄清楚如何将这些原始文本转换为适合变形金刚的格式!在我们这样做的同时,我们还可以重置数据集的输出格式,因为我们不再需要DataFrame 格式:

情感.reset_format() 从文本到标记化Transformer 模型(例如DistilBERT)无法接受原始字符串作为输入;相反,他们假设文本已被标记化并编码为数字向量。分词器是将字符串分解为模型中使用的最基本单元的步骤。可以采用多种标记化策略,通常从语料库中学习以将单词细化为子单元。在查看DistilBERT 的分词器之前,我们先考虑两种常见情况:_字符_ 和单词分词器。

字符标记化最简单的标记化解决方案是将每个字符单独提供给模型。在Python中,str对象实际上是底层的数组,使我们能够仅用一行代码快速实现字符级标记化:

text='TokenizingtextisacoretaskofNLP.'tokenized_text=list(text)print(tokenized_text)['T', 'o', 'k', 'e', 'n', 'i', 'z', 'i' , 'n', 'g', ' ', 't', 'e', 'x', 't', ' ', 'i', 's', ' ', 'a', ' ' , 'c' , 'o', 'r', 'e', ' ', 't', 'a', 's', 'k', ' ', 'o', 'f', ' ', 'N', 'L', 'P', '.'] 这是一个好的开始,但我们还没有完成。我们的模型期望将每个字符转换为整数,这个过程有时称为数字化。一种简单的方法是使用唯一的整数对每个唯一的标记(在本例中为字符)进行编码:

token2idx={ch:idxforidx,chinenumerate(sorted(set(tokenized_text)))}print(token2idx){' ': 0, '.': 1, 'L': 2, 'N': 3, 'P': 4、“T”:5、“a”:6、“c”:7、“e”:8、“f”:9、“g”:10、“i”:11、“k”:12、 'n': 13, 'o': 14, 'r': 15, 's': 16, 't': 17, 'x': 18, 'z': 19} 这给了我们A 映射中的词汇表将每个字符转换为一个唯一的整数。我们现在可以使用token2idx 将标记化文本转换为整数列表:

input_ids=[token2idx[token]forkenintokenized_text]print(input_ids)[5, 14, 12, 8, 13, 11, 19, 11, 13, 10, 0, 17, 8, 18, 17, 0, 11, 16, 0, 6, 0, 7,14, 15, 8, 0, 17, 6, 16, 12, 0, 14, 9, 0, 3, 2, 4, 1] 现在每个标签都映射到唯一的数字标识符(因此名称为input_ids)。最后一步是将input_ids 转换为one-hot 向量的2D 张量。 One-hot 向量通常在机器学习中用于对分类数据进行编码,这些数据可以是序数数据,也可以是名义数据。例如,假设我们想要对《变形金刚》电视剧中的角色名称进行编码。一种方法是将每个名称映射到一个唯一的ID,如下所示:

categorical_df=pd.DataFrame({'Name':['Bumblebee','OptimusPrime','Megatron'],'LabelID':[0,1,2]})categorical_dfNameLabel ID0Bumblebee01Optimus Prime12Megatron2 这种方法的问题在于它它的名字在它们之间创建了一种想象的顺序,而神经网络非常擅长学习这些类型的关系。因此,我们可以

以上知识分享希望能够帮助到大家!