介绍
自然语言处理(NLP)是一种将非结构化文本处理成有意义的知识的人工智能技术。NLP解决了分类、主题建模、文本生成、问答、推荐等业务问题。虽然TF/IDF矢量化或其他高级词嵌入(如GLOVE和Word2Vec)在此类NLP业务问题上表现出了良好的性能,但这些模型存在局限性就是使用一个向量对词进行编码而不考虑上下文的不同含义。因此,当试图解决理解用户意图所需的问题时,这些模型可能不能很好地执行。一个例子是,当用户与自动聊天机器人交互时,它试图理解用户查询的意图并准确地提供响应。
对于这种情况,NLP中的另一个例子是从下面两个句子中解码上下文意义。
- A thieve robbed a bank.
- He went to river bank.
从以上两种表述中,人们很容易就能看出“bank”有两种不同的含义;然而,机器不能区分,因为上面提到的词嵌入使用相同的标记“bank”,而不管他们的上下文意义。为了克服这一挑战,谷歌从Transformers (BERT)模型开发了最先进的双向编码器表示。
BERT是什么?
BERT是在8亿单词的图书语料库和2500万单词的英语维基百科上训练的预训练模型。在BERT中,“bank”将有两个不同的含义,因为它们的上下文差异。在保持NLP任务的高性能的同时并不会降低模型构建的训练时间。并且可以从BERT中提取新的语言特征用于模型预测。与RNN、LSTM、CNN等深度学习模型相比,BERT的发展速度要快得多。作为高层次的理解,BERT有两种不同的架构变体:BERT base和BERT large。第一个变型有12个Transformers 块,12个注意头,1.1亿参数,后一个变型有24个Transformers ,16个注意头,3.4亿参数。它在使用过程中完成了两个NLP的任务:遮蔽语言建模和下一句预测。
数据集
从此处(https://datahack.analyticsvidhya.com/contest/janatahack-independence-day-2020-ml-hackathon/#ProblemStatement)获取数据集,该数据集可用于研究论文的主题建模的多标签分类对比。对比的目的是从大型的科学文章在线存档中尽可能地容易找到相关的文章。我选择此数据集的原因是,尽管有许多关于二进制分类的Twitter情绪讨论BERT和Pytorch的文章,但很少找到有关处理多类问题的。并且有很多共享代码可能无法正常工作。
查看如下的代码我建议具备python,NLP,深度学习和Pytorch框架的基础知识。必须使用Google帐户才能使用Google Colab帐户。
处理数据的方法
在传统的NLP机器学习问题中,我们倾向于清除不需要的文本,例如删除停用词,标点符号,删除符号和数字等。但是,在BERT中,不需要执行此类预处理任务,因为BERT使用了这些 单词的顺序和位置,以了解用户输入的意图。
ML / DL工程师应该从不同方面探索数据集,以真正了解他们手中的数据类型,这是一个好习惯。NLP的典型功能是单词计数,动词计数,形容词计数,数字计数,标点符号计数,双字母组计数,三字组计数等。为简便起见,我已展示了如何对单词计数列进行计数,其中单个标题中使用的总单词数将被计算在内。您可能还需要处理类似于TITLE的Abstract列,以及ABSTRACT和TITLE的组合。
下面的命令创建“ WORD_COUNT”列。
df_raw['WORD_COUNT'] = df_raw['TITLE'].apply(lambda x: len(x.split())
这将生成“ WORD_COUNT”的分布图,即标题的长度。
如您所见,文章标题的大部分以10个单词为中心,这是预期的结果,因为TITLE应该简短,简洁且有意义。
由于我将仅使用“ TITLE”和“ target_list”,因此我创建了一个名为df2的新数据框。df2.head()命令显示训练数据集中的前五个记录。如您所见,两个目标标签被标记到最后的记录,这就是为什么这种问题称为多标签分类问题的原因。
df2=df_raw[['TITLE', 'target_list']].copy() df2.head()
同时,设置将用于模型训练的参数。由于我更喜欢使用2*base数字,因此最大长度设置为16,这涵盖了大部分“ TITLE”长度。训练和有效批处理大小设置为32。epoch为4,因为它很容易在几个epoch上过度拟合。我从lr=0.00001开始学习。您可以随意尝试不同的值以提高准确性。
#Sectionsofconfig#DefiningsomekeyvariablesthatwillbeusedlateroninthetrainingMAX_LEN=16TRAIN_BATCH_SIZE=32VALID_BATCH_SIZE=32EPOCHS=4LEARNING_RATE=1e-05tokenizer=BertTokenizer.from_pretrained('bert-base-uncased')
让我们创建一个称为“ CustomDataset”的通用类。Class从我们的原始输入特征生成张量,并且Pytorch张量可以接受class的输出。它期望具有上面定义的“ TITLE”,“ target_list”,max_len,并使用BERT toknizer.encode_plus函数将输入设置为数字矢量格式,然后转换为张量格式返回。
classCustomDataset(Dataset): def__init__(self, dataframe, tokenizer, max_len): self.tokenizer=tokenizerself.data=dataframeself.title=dataframe['TITLE'] self.targets=self.data.target_listself.max_len=max_lendef__len__(self): returnlen(self.title) def__getitem__(self, index): title=str(self.title[index]) title=" ".join(title.split()) inputs=self.tokenizer.encode_plus( title, None, add_special_tokens=True, max_length=self.max_len, padding='max_length', return_token_type_ids=True, truncation=True ) ids=inputs['input_ids'] mask=inputs['attention_mask'] token_type_ids=inputs["token_type_ids"] return { 'ids': torch.tensor(ids, dtype=torch.long), 'mask': torch.tensor(mask, dtype=torch.long), 'token_type_ids': torch.tensor(token_type_ids, dtype=torch.long), 'targets': torch.tensor(self.targets[index], dtype=torch.float) }
数据集的80%用于模型训练,而20%用于验证。测试数据集完全用于测试目的。
train_size=0.8train_dataset=df2.sample(frac=train_size,random_state=200) valid_dataset=df2.drop(train_dataset.index).reset_index(drop=True) train_dataset=train_dataset.reset_index(drop=True) print("FULL Dataset: {}".format(df2.shape)) print("TRAIN Dataset: {}".format(train_dataset.shape)) print("TEST Dataset: {}".format(valid_dataset.shape)) training_set=CustomDataset(train_dataset, tokenizer, MAX_LEN) validation_set=CustomDataset(valid_dataset, tokenizer, MAX_LEN)
我们已经讨论了将张量准备为输入特征的大部分基础工作。现在,构建BERT模型很容易。由于来自模型的冗长输出,我已简化为仅显示模型。我已使用dropout 0.3来随机减少特征,以最大程度地减少第2层的过拟合。第3层采用了768维特征,这些特征是从使用BERT的第2层输出的。它返回6个特征,这是对目标列表的最终预测。
classBERTClass(torch.nn.Module): def__init__(self): super(BERTClass, self).__init__() self.l1=transformers.BertModel.from_pretrained('bert-base-uncased') self.l2=torch.nn.Dropout(0.3) self.l3=torch.nn.Linear(768, 6) defforward(self, ids, mask, token_type_ids): _, output_1=self.l1(ids, attention_mask=mask, token_type_ids=token_type_ids) output_2=self.l2(output_1) output=self.l3(output_2) returnoutputmodel=BERTClass() model.to(device)
BCE损失函数用于找出模型预测值与实际目标值之间的误差。使用Adam优化器。损失功能请参见下文。
defloss_fn(outputs, targets): returntorch.nn.BCEWithLogitsLoss()(outputs, targets) optimizer=torch.optim.Adam(params=model.parameters(), lr=LEARNING_RATE)