关键词:
从RNN到BERT
一年前的这个时候,我逃课了一个星期,从澳洲飞去上海观看电竞比赛,也顺便在上海的一个公司联系了面试。当时,面试官问我对RNN的了解程度,我回答“没有了解”。但我把这个问题带回了学校,从此接触了RNN,以及它的加强版-LSTM。
时隔一年,LSTM好像已经可以退出历史舞台。BERT站在了舞台中间,它可以更快且更好的解决NLP问题。我打算以边学习边分享的方式,用BERT(GTP-2)过一遍常见的NLP问题。这一篇博客是文本分类的baseline system。
BERT
如果你熟悉transformer,相信理解bert对你来说没有任何难度。bert就是encoder的堆叠。
如果你不熟悉transformer,这篇文章是我见过的最棒的transformer图解,可以帮助你理解:http://jalammar.github.io/illustrated-transformer/
当然这个作者也做出了很棒的bert图解,链接在此:http://jalammar.github.io/illustrated-bert/
BERT做文本分类
bert是encoder的堆叠。当我们向bert输入一句话,它会对这句话里的每一个词(严格说是token,有时也被称为word piece)进行并列处理,并为每个词输出对应的向量。我们给输入文本的句首添加一个[CLS] token(CLS为classification的缩写),然后我们只考虑这个CLS token的对应输出,用它来做classifier的输入,最终输出具体的分类。
使用Huggingface
Huggingface可以帮助我们轻易的完成文本分类任务。
通过它,我们可以轻松的读取预训练语言模型,以及使用它自带的文本分类bert模型-BertForSequenceClassification。
正式开始解决问题
数据介绍
数据来自Kaggle的competition:Real or Not? NLP with Disaster Tweets 链接:https://www.kaggle.com/c/nlp-getting-started
这是推特的数据集,数据的格式如下:
id | location | keyword | text | target |
1 | 圣地亚哥 | 大火 | 圣地亚哥国家公园出现严重森林大火 | 1 |
2 | 硅谷 | 沙滩 | 今天在硅谷的沙滩晒太阳真开心 | 0 |
我们需要做的,就是根据推文的location、keyword 以及 text 来判断这篇推文是否和灾难有关。
它的现实意义在于,如果我们能够根据推文来第一时间发现灾难,有关部门就可以快速做出反应,将灾难的损失降低到最小。就像前段时间温岭油罐车爆炸,群众第一时间就把信息、视频上传到了微博,消防部门可以通过微博获取信息。
探索式资料分析(EDA)与数据清理
在拿到数据后,我们需要进行探索式资料分析。由于这不是本篇博客最重要的部分,这里我只给出大体轮廓和结论。在我的kaggle notebook上有详细的代码及plot。https://www.kaggle.com/jianweitang/nlp-with-disaster-tweets-eda
我们保留keyword这一列,摒弃location这一列。
有标签的训练数据有7613条,无标签的测试数据有3263条
Training Set Shape: (7613, 5) Test Set Shape: (3263, 4)
对于location这一列,它具有较多的缺失值,并且有非常多的unique values,暂且认为很难将他与灾难直接联系到一起,我们直接把location这一列摒弃。
Number of unique values in keyword = 222 (Training) - 222 (Test) Number of unique values in location = 3342 (Training) - 1603 (Test)
而对于keyword这一列,它的缺失值很少,unique values有222个。同时它与label之间有可见的相关性,有些词只在灾难推文中出现,有些词只在非灾难推文中出现。如下图:
标签的分布是均匀的,这意味着我们可以直接把它拿来训练模型
文本清洁
- 去除特殊符号
- 把缩写及网络用语展开,例如把 he‘s 展开为 he is, lmao 展开为 laughing my ass off
- 把hashtags和usernames展开
- 纠正错误拼写
推文错误标记
在数据中我们发现了重复的text被标记成了不同的标签,大概有十几个样本。这些样本可能是有争议,也可能是单纯的标记错误,在这里我们直接删掉这些样本。
BERT预处理
import random import torch from torch.utils.data import TensorDataset, DataLoader, random_split from transformers import BertTokenizer from transformers import BertForSequenceClassification, AdamW from transformers import get_linear_schedule_with_warmup seed = 42 random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic = True device = torch.device(‘cuda‘)
我们先读取预训练的 bert-base-uncased 模型,用来进行分词,以及词向量转化
# Get text values and labels text_values = train[‘final_text‘].values labels = train[‘target‘].values # Load the pretrained Tokenizer tokenizer = BertTokenizer.from_pretrained(‘bert-base-uncased‘, do_lower_case=True)
来用这个tokenizer切分数据里的第一条推文试试看
print(‘Original Text : ‘, text_values[1]) print(‘Tokenized Text: ‘, tokenizer.tokenize(text_values[1])) print(‘Token IDs : ‘, tokenizer.convert_tokens_to_ids(tokenizer.tokenize(text_values[1])))
输出:
Original Text : Forest fire near La Ronge Sask. Canada Tokenized Text: [‘forest‘, ‘fire‘, ‘near‘, ‘la‘, ‘ron‘, ‘##ge‘, ‘sas‘, ‘##k‘, ‘.‘, ‘canada‘] Token IDs : [3224, 2543, 2379, 2474, 6902, 3351, 21871, 2243, 1012, 2710]
除了分词以外,我们需要给它添加[CLS]和[SEP],以及[PAD],其中CLS在句首,SEP在句尾,PAD为统一句子长度的padding。这里看看tokenizer会给他们分别怎样的index。
text = ‘[CLS]‘ print(‘Original Text : ‘, text) print(‘Tokenized Text: ‘, tokenizer.tokenize(text)) print(‘Token IDs : ‘, tokenizer.convert_tokens_to_ids(tokenizer.tokenize(text))) print(‘ ‘) text = ‘[SEP]‘ print(‘Original Text : ‘, text) print(‘Tokenized Text: ‘, tokenizer.tokenize(text)) print(‘Token IDs : ‘, tokenizer.convert_tokens_to_ids(tokenizer.tokenize(text))) print(‘ ‘) text = ‘[PAD]‘ print(‘Original Text : ‘, text) print(‘Tokenized Text: ‘, tokenizer.tokenize(text)) print(‘Token IDs : ‘, tokenizer.convert_tokens_to_ids(tokenizer.tokenize(text)))
输出:
Original Text : [CLS] Tokenized Text: [‘[CLS]‘] Token IDs : [101] Original Text : [SEP] Tokenized Text: [‘[SEP]‘] Token IDs : [102] Original Text : [PAD] Tokenized Text: [‘[PAD]‘] Token IDs : [0]
实际使用中,我们用tokenizer.encode()这个function来直接把文本转化为token_id 并添加special tokens。
我们定义一个encode_fn把数据集的整个文本都转化为tokens。
# Function to get token ids for a list of texts def encode_fn(text_list): all_input_ids = [] for text in text_values: input_ids = tokenizer.encode( text, add_special_tokens = True, # 添加special tokens, 也就是CLS和SEP max_length = 160, # 设定最大文本长度 pad_to_max_length = True, # pad到最大的长度 return_tensors = ‘pt‘ # 返回的类型为pytorch tensor ) all_input_ids.append(input_ids) all_input_ids = torch.cat(all_input_ids, dim=0) return all_input_ids
all_input_ids = encode_fn(text_values)
labels = torch.tensor(labels)
接下来,我们把数据分为训练集与验证集,并构建dataloader。
epochs = 4 batch_size = 32 # Split data into train and validation dataset = TensorDataset(all_input_ids, labels) train_size = int(0.90 * len(dataset)) val_size = len(dataset) - train_size train_dataset, val_dataset = random_split(dataset, [train_size, val_size]) # Create train and validation dataloaders train_dataloader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True) val_dataloader = DataLoader(val_dataset, batch_size = batch_size, shuffle = False)
加载与训练的bert模型, 并定义optimizer与learning rate scheduler
# Load the pretrained BERT model model = BertForSequenceClassification.from_pretrained(‘bert-base-uncased‘, num_labels=2, output_attentions=False, output_hidden_states=False) model.cuda() # create optimizer and learning rate schedule optimizer = AdamW(model.parameters(), lr=2e-5) total_steps = len(train_dataloader) * epochs scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=total_steps)
定义一个计算accuracy的方法,方便在训练的时候print出精确度的变化
from sklearn.metrics import f1_score, accuracy_score def flat_accuracy(preds, labels): """A function for calculating accuracy scores""" pred_flat = np.argmax(preds, axis=1).flatten() labels_flat = labels.flatten() return accuracy_score(labels_flat, pred_flat)
BERT的训练与验证
for epoch in range(epochs): model.train() total_loss, total_val_loss = 0, 0 total_eval_accuracy = 0 for step, batch in enumerate(train_dataloader): model.zero_grad() loss, logits = model(batch[0].to(device), token_type_ids=None, attention_mask=(batch[0]>0).to(device), labels=batch[1].to(device)) total_loss += loss.item() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() scheduler.step() model.eval() for i, batch in enumerate(val_dataloader): with torch.no_grad(): loss, logits = model(batch[0].to(device), token_type_ids=None, attention_mask=(batch[0]>0).to(device), labels=batch[1].to(device)) total_val_loss += loss.item() logits = logits.detach().cpu().numpy() label_ids = batch[1].to(‘cpu‘).numpy() total_eval_accuracy += flat_accuracy(logits, label_ids) avg_train_loss = total_loss / len(train_dataloader) avg_val_loss = total_val_loss / len(val_dataloader) avg_val_accuracy = total_eval_accuracy / len(val_dataloader) print(f‘Train loss : avg_train_loss‘) print(f‘Validation loss: avg_val_loss‘) print(f‘Accuracy: avg_val_accuracy:.2f‘) print(‘ ‘)
输出:
Train loss : 0.441781023875452 Validation loss: 0.34831519580135745 Accuracy: 0.86 Train loss : 0.3275374324204257 Validation loss: 0.3286557973672946 Accuracy: 0.88 Train loss : 0.2503694619696874 Validation loss: 0.355623895690466 Accuracy: 0.86 Train loss : 0.19663514375973207 Validation loss: 0.3806843503067891 Accuracy: 0.86
这里比较特别的一点是,即使只有4个epochs,validation loss也是一直在增大的。我看了下其他人使用pytorch和huggingface的训练部分,也存在这个问题,反而使用tensorflow和TFhub的稍微好一点。我猜测这里的原因是过拟合。
模型预测
这里与训练类似,把测试集构建为dataloade,然后将预测结果输出到csv文件。到这里整个流程就结束了。
# Create the test data loader text_values = df_test[‘final_text‘].values all_input_ids = encode_fn(text_values) pred_data = TensorDataset(all_input_ids) pred_dataloader = DataLoader(pred_data, batch_size=batch_size, shuffle=False)
model.eval() preds = [] for i, (batch,) in enumerate(pred_dataloader): with torch.no_grad(): outputs = model(batch.to(device), token_type_ids=None, attention_mask=(batch>0).to(device)) logits = outputs[0] logits = logits.detach().cpu().numpy() preds.append(logits) final_preds = np.concatenate(preds, axis=0) final_preds = np.argmax(final_preds, axis=1)
# Create submission file submission = pd.DataFrame() submission[‘id‘] = df_test[‘id‘] submission[‘target‘] = final_preds submission.to_csv(‘submission.csv‘, index=False)
小结
我把预测结果上传后,score是0.83,在kaggle上排名100多。考虑到排名靠前的60位使用的不是NLP方法,他们找到了正确答案并直接上传得到了100%的正确率,我对这个简单模型的结果还是挺满意的。
也希望我对这个学习过程的分享,能够帮助到一同学习NLP的人。
用于命名实体识别的 PyTorch Huggingface BERT-NLP
】用于命名实体识别的PyTorchHuggingfaceBERT-NLP【英文标题】:PyTorchHuggingfaceBERT-NLPforNamedEntityRecognition【发布时间】:2019-07-1907:13:05【问题描述】:我已经为MADE1.0数据集使用GoogleBERTbyHuggingFace的PyTorch实现已经有一段时间了。直到上次... 查看详情
从头开始微调/训练 HuggingFace 模型的正确方法 (PyTorch)
】从头开始微调/训练HuggingFace模型的正确方法(PyTorch)【英文标题】:CorrectWaytoFine-Tune/TrainHuggingFace\'sModelfromscratch(PyTorch)【发布时间】:2020-12-0805:57:18【问题描述】:例如,我想使用现有配置从头开始训练BERT模型。以下代码是正... 查看详情
Huggingface 在情绪分析任务中给出 pytorch 索引错误
】Huggingface在情绪分析任务中给出pytorch索引错误【英文标题】:Huggingfacegivingpytorchindexerroronsentimentanalysistask【发布时间】:2021-12-1604:17:11【问题描述】:我正在尝试对服务器上包含数百万条推文的数据集进行情绪分析。我正在调... 查看详情
huggingface pytorch-transformers:如何用某些值初始化嵌入?
】huggingfacepytorch-transformers:如何用某些值初始化嵌入?【英文标题】:huggingfacepytorch-transformers:howtoinitializeembeddingswithcertainvalues?【发布时间】:2019-12-2603:21:42【问题描述】:我正在微调来自huggingface的bert模型。有没有办法手动... 查看详情
pytorch实现mlp(基于pytorch实现)
文章目录前言一、导入相关库二、加载Cora数据集三、定义MLP网络3.1定义MLP层3.1.1定义参数WWW和bb 查看详情
使用 huggingface pytorch-transformers GPT-2 进行分类任务
】使用huggingfacepytorch-transformersGPT-2进行分类任务【英文标题】:usinghuggingface\'spytorch-transformersGPT-2forclassifcationtasks【发布时间】:2019-12-0610:55:08【问题描述】:我想使用GPT-2来制作文本分类器模型。通过GPT-2提取特征后,我不确定... 查看详情
使用 HuggingFace 库在 Pytorch 中训练 n% 的最后一层 BERT(训练 12 个中的最后 5 个 BERTLAYER。)
】使用HuggingFace库在Pytorch中训练n%的最后一层BERT(训练12个中的最后5个BERTLAYER。)【英文标题】:Trainn%lastlayersofBERTinPytorchusingHuggingFaceLibrary(trainLast5BERTLAYERoutof12.)【发布时间】:2021-03-0901:39:44【问题描述】:Bert的架构类似于encod... 查看详情
基于pytorch使用实现cnn如何使用pytorch构建cnn卷积神经网络(代码片段)
基于pytorch使用实现CNN如何使用pytorch构建CNN卷积神经网络本文是一个基于pytorch使用CNN在生物信息学上进行位点预测的例子基于pytorch实现CNN,基于CNN进行位点预测,将CNN代码进行封装,可以非常简单的使用代码,基... 查看详情
pytorch实现基于charrnn的文本分类与生成(代码片段)
Pytorch实现基于CharRNN的文本分类与生成标签:deep-learningpytorchnlp1简介本篇主要介绍使用pytorch实现基于CharRNN来进行文本分类与内容生成所需要的相关知识,并最终给出完整的实现代码。2相关API的说明pytorch框架中每种网络... 查看详情
pytorch实战用pytorch实现基于神经网络的图像风格迁移(代码片段)
用PyTorch实现基于神经网络的图像风格迁移1.风格迁移原理介绍2.FastNeuralStyle网络结构3.用PyTorch实现风格迁移3.1首先看看如何使用预训练的VGG。3.2接下来要实现风格迁移网络参考资料风格迁移,又称为风格转换。只需要给定原... 查看详情
基于pytorch使用实现cnn如何使用pytorch构建cnn卷积神经网络(代码片段)
基于pytorch使用实现CNN如何使用pytorch构建CNN卷积神经网络本文是一个基于pytorch使用CNN在生物信息学上进行位点预测的例子基于pytorch实现CNN,基于CNN进行位点预测,将CNN代码进行封装,可以非常简单的使用代码,基... 查看详情
如何将拥抱脸模型用于nlp音频分类和计算机视觉
在将HuggingFace用于NLP、音频分类或计算机视觉时,用户需要知道HuggingFace为每种项目类型提供了什么那些花时间研究音频分类 项目、NLP和/或计算机视觉等模型和框架的人可能想知道如何将HuggingFace用于其中一些模型。HuggingFac... 查看详情
基于pytorch实现图片去模糊降噪,超详细,有代码,数据,可直接运行。
教大家一下,图片去模糊降噪的基本方法和完整的实验流程。目录简介:环境配置 参数设置数据显示 查看详情
基于pytorch平台实现对mnist数据集的分类分析(前馈神经网络softmax)基础版(代码片段)
基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)基础版文章目录基于pytorch平台实现对MNIST数据集的分类分析(前馈神经网络、softmax)基础版前言一、基于“前馈神经网络”模型,分类分析... 查看详情
基于深度学习的语义分割初探fcn以及pytorch代码实现(代码片段)
基于深度学习的语义分割初探FCN以及pytorch代码实现FCN论文论文地址:https://arxiv.org/abs/1411.4038FCN是基于深度学习方法的第一篇关于语义分割的开山之作,虽然这篇文章的分割结果现在看起来并不是目前最好的,但其意... 查看详情
pytorch深度学习实践入门01(代码片段)
文章目录基于PyTorch的两层神经网络一、基于numpy的两层神经网络实现:二、基于PyTorch的两层神经网络实现:三、使用nn库实现两层神经网络四、自定义nnModules实现两层神经网络总结基于PyTorch的两层神经网络提示:在... 查看详情
pytorch实现gat(基于pytorch实现)(代码片段)
...台:Windows10语言环境:python3.7编译器:PyCharmPyTorch版本:1.11.0PyG版本:2.1.0💥项目专栏:【图神经网络代码实战目录】本文我们将使用PyTorch来简易实现一个GAT(图注意力网络),不使用PyG库&... 查看详情
将 AllenNLP 解释与 HuggingFace 模型一起使用
】将AllenNLP解释与HuggingFace模型一起使用【英文标题】:UsingAllenNLPInterpretwithaHuggingFacemodel【发布时间】:2021-07-1602:17:15【问题描述】:我想将AllenNLPInterpret(代码+演示)与使用HuggingFace(electra基鉴别器)训练的PyTorch分类模型结合... 查看详情