当前位置:   article > 正文

Bert学习笔记--使用篇(pytorch)(附源码)(入门级)_pycharm bert

pycharm bert

一、将虚拟环境加载至pycharm

1、进入pycharm,新建项目

2、输入项目信息

二、使用示例

  1. import torch
  2. def test():
  3. from transformers import BertTokenizer, BertModel, BertForMaskedLM
  4. from transformers import BertModel, BertTokenizer
  5. BERT_PATH = './model/chinese_wwm_L-12_H-768_A-12/publish/'
  6. tokenizer = BertTokenizer.from_pretrained(BERT_PATH)
  7. print(tokenizer.tokenize('I have a good time, thank you.'))
  8. bert = BertModel.from_pretrained(BERT_PATH)
  9. print('load bert model over')
  10. if __name__ == '__main__':
  11. test()

文件结构

结果

可以看到bert模型成功使用,且使用了掩码,将“than”掩盖为“##”

三、初步使用

1、预处理数据

现在我们基本熟悉了 BERT 的基本使用,接下来为其准备输入数据。一般情况下,在训练模型前,都需要对手上的数据进行预处理,以满足模型需要。

为了更容易理解得到的输出tokenization,我们以一个简短的文本为例。

  1. from transformers import BertTokenizer
  2. BERT_PATH = './model/chinese_wwm_L-12_H-768_A-12/publish/'
  3. tokenizer = BertTokenizer.from_pretrained(BERT_PATH)
  4. # tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
  5. example_text = 'I will watch Memento tonight'
  6. bert_input = tokenizer(example_text, padding='max_length',
  7. max_length=15,
  8. truncation=True,
  9. return_tensors="pt")
  10. # ------- bert_input ------
  11. print(bert_input['input_ids'])
  12. print(bert_input['token_type_ids'])
  13. print(bert_input['attention_mask'])

下面是对上面BertTokenizer参数的解释:

  • padding:将每个sequence填充到指定的最大长度。
  • max_length: 每个sequence的最大长度。本示例中我们使用 15,但对于本文实际数据集,我们将使用 512,这是 BERT 允许的sequence 的最大长度。
  • truncation:如果为True,则每个序列中超过最大长度的标记将被截断。
  • return_tensors:将返回的张量类型。由于我们使用的是 Pytorch,所以我们使用pt;如果你使用 Tensorflow,那么你需要使用tf

从上面的变量中看到的输出bert_input,是用于稍后的 BERT 模型。但是这些输出是什么意思?

1. 第一行是 input_ids,它是每个 token 的 id 表示。实际上可以将这些输入 id 解码为实际的token,如下所示:

  1. example_text = tokenizer.decode(bert_input.input_ids[0])
  2. print(example_text)

由上述结果所示,BertTokenizer负责输入文本的所有必要转换,为 BERT 模型的输入做好准备。它会自动添加 [CLS]、[SEP] 和 [PAD] token。由于我们指定最大长度为 15,所以最后有五个 [PAD] token。

2. 第二行是 token_type_ids,它是一个 binary mask,用于标识 token 属于哪个 sequence。如果我们只有一个 sequence,那么所有的 token 类型 id 都将为 0。对于文本分类任务,token_type_ids是 BERT 模型的可选输入参数。

3. 第三行是 attention_mask,它是一个 binary mask,用于标识 token 是真实 word 还是只是由填充得到。如果 token 包含 [CLS]、[SEP] 或任何真实单词,则 mask 将为 1。如果 token 只是 [PAD] 填充,则 mask 将为 0。

2、数据集类

现在我们知道从BertTokenizer中获得什么样的输出,接下来为新闻数据集构建一个Dataset类,该类将作为一个类来将新闻数据转换成模型需要的数据格式。

  1. import torch
  2. import numpy as np
  3. from transformers import BertTokenizer
  4. BERT_PATH = './model/chinese_wwm_L-12_H-768_A-12/publish'
  5. tokenizer = BertTokenizer.from_pretrained(BERT_PATH)
  6. labels = {'business':0,
  7. 'entertainment':1,
  8. 'sport':2,
  9. 'tech':3,
  10. 'politics':4
  11. }
  12. class Dataset(torch.utils.data.Dataset):
  13. def __init__(self, df):
  14. self.labels = [labels[label] for label in df['category']]
  15. self.texts = [tokenizer(text,
  16. padding='max_length',
  17. max_length = 512,
  18. truncation=True,
  19. return_tensors="pt")
  20. for text in df['text']]
  21. def classes(self):
  22. return self.labels
  23. def __len__(self):
  24. return len(self.labels)
  25. def get_batch_labels(self, idx):
  26. # Fetch a batch of labels
  27. return np.array(self.labels[idx])
  28. def get_batch_texts(self, idx):
  29. # Fetch a batch of inputs
  30. return self.texts[idx]
  31. def __getitem__(self, idx):
  32. batch_texts = self.get_batch_texts(idx)
  33. batch_y = self.get_batch_labels(idx)
  34. return batch_texts, batch_y

在上面实现的代码中,我们定义了一个名为 labels的变量,它是一个字典,将DataFrame中的 category 映射到 labels的 id 表示。注意,上面的__init__函数中,还调用了BertTokenizer将输入文本转换为 BERT 期望的向量格式。

定义Dataset类后,将数据框拆分为训练集、验证集和测试集,比例为 80:10:10

  1. np.random.seed(112)
  2. df = pd.read_csv('./data/train.csv')
  3. df_train, df_val, df_test = np.split(df.sample(frac=1, random_state=42),
  4. [int(.8 * len(df)), int(.9 * len(df))])
  5. print(len(df_train), len(df_val), len(df_test))

3、构建模型

至此,我们已经成功构建了一个 Dataset 类来生成模型输入数据。现在使用具有 12 层 Transformer 编码器的预训练 BERT 基础模型构建实际模型。

  1. from torch import nn
  2. from transformers import BertModel
  3. class BertClassifier(nn.Module):
  4. def __init__(self, dropout=0.5):
  5. super(BertClassifier, self).__init__()
  6. BERT_PATH = './model/chinese_wwm_L-12_H-768_A-12/publish'
  7. self.bert = BertModel.from_pretrained(BERT_PATH)
  8. self.dropout = nn.Dropout(dropout)
  9. self.linear = nn.Linear(768, 5)
  10. self.relu = nn.ReLU()
  11. def forward(self, input_id, mask):
  12. _, pooled_output = self.bert(input_ids= input_id, attention_mask=mask,return_dict=False)
  13. dropout_output = self.dropout(pooled_output)
  14. linear_output = self.linear(dropout_output)
  15. final_layer = self.relu(linear_output)
  16. return final_layer

从上面的代码可以看出,BERT 模型输出了两个变量:

  • 在上面的代码中命名的第一个变量_包含sequence中所有 token 的 Embedding 向量层。
  • 命名的第二个变量pooled_output包含 [CLS] token 的 Embedding 向量。对于文本分类任务,使用这个 Embedding 作为分类器的输入就足够了。

然后将pooled_output变量传递到具有 ReLU 激活函数的线性层。在线性层中输出一个维度大小为 5 的向量,每个向量对应于标签类别。

4、训练模型

接下来是训练模型。使用标准的 PyTorch 训练循环来训练模型。

  1. from torch.optim import Adam
  2. from tqdm import tqdm
  3. def train(model, train_data, val_data, learning_rate, epochs):
  4. # 通过Dataset类获取训练和验证集
  5. train, val = Dataset(train_data), Dataset(val_data)
  6. # DataLoader根据batch_size获取数据,训练时选择打乱样本
  7. train_dataloader = torch.utils.data.DataLoader(train, batch_size=2, shuffle=True)
  8. val_dataloader = torch.utils.data.DataLoader(val, batch_size=2)
  9. # 判断是否使用GPU
  10. use_cuda = torch.cuda.is_available()
  11. device = torch.device("cuda" if use_cuda else "cpu")
  12. # 定义损失函数和优化器
  13. criterion = nn.CrossEntropyLoss()
  14. optimizer = Adam(model.parameters(), lr=learning_rate)
  15. if use_cuda:
  16. model = model.cuda()
  17. criterion = criterion.cuda()
  18. # 开始进入训练循环
  19. for epoch_num in range(epochs):
  20. # 定义两个变量,用于存储训练集的准确率和损失
  21. total_acc_train = 0
  22. total_loss_train = 0
  23. # 进度条函数tqdm
  24. for train_input, train_label in tqdm(train_dataloader):
  25. train_label = train_label.to(device)
  26. mask = train_input['attention_mask'].to(device)
  27. input_id = train_input['input_ids'].squeeze(1).to(device)
  28. # 通过模型得到输出
  29. output = model(input_id, mask)
  30. # 计算损失
  31. batch_loss = criterion(output, train_label)
  32. total_loss_train += batch_loss.item()
  33. # 计算精度
  34. acc = (output.argmax(dim=1) == train_label).sum().item()
  35. total_acc_train += acc
  36. # 模型更新
  37. model.zero_grad()
  38. batch_loss.backward()
  39. optimizer.step()
  40. # ------ 验证模型 -----------
  41. # 定义两个变量,用于存储验证集的准确率和损失
  42. total_acc_val = 0
  43. total_loss_val = 0
  44. # 不需要计算梯度
  45. with torch.no_grad():
  46. # 循环获取数据集,并用训练好的模型进行验证
  47. for val_input, val_label in val_dataloader:
  48. # 如果有GPU,则使用GPU,接下来的操作同训练
  49. val_label = val_label.to(device)
  50. mask = val_input['attention_mask'].to(device)
  51. input_id = val_input['input_ids'].squeeze(1).to(device)
  52. output = model(input_id, mask)
  53. batch_loss = criterion(output, val_label)
  54. total_loss_val += batch_loss.item()
  55. acc = (output.argmax(dim=1) == val_label).sum().item()
  56. total_acc_val += acc
  57. print(
  58. f'''Epochs: {epoch_num + 1}
  59. | Train Loss: {total_loss_train / len(train_data): .3f}
  60. | Train Accuracy: {total_acc_train / len(train_data): .3f}
  61. | Val Loss: {total_loss_val / len(val_data): .3f}
  62. | Val Accuracy: {total_acc_val / len(val_data): .3f}''')

我们对模型进行了 5 个 epoch 的训练,我们使用 Adam 作为优化器,而学习率设置为1e-6。因为本案例中是处理多类分类问题,则使用分类交叉熵作为我们的损失函数。

建议使用 GPU 来训练模型,因为 BERT 基础模型包含 1.1 亿个参数。

  1. EPOCHS = 5
  2. model = BertClassifier()
  3. LR = 1e-6
  4. train(model, df_train, df_val, LR, EPOCHS)

排查了三个小时错误之后,我的模型它终于在跑了,我好欣慰。

 进度条终于进了!!!!!!!!!!!

训练结果

  1. 100%|██████████| 4000/4000 [09:37<00:00, 6.93it/s]
  2. 0%| | 0/4000 [00:00<?, ?it/s]Epochs: 1
  3. | Train Loss: 0.590
  4. | Train Accuracy: 0.518
  5. | Val Loss: 0.446
  6. | Val Accuracy: 0.686
  7. 100%|██████████| 4000/4000 [09:47<00:00, 6.81it/s]
  8. Epochs: 2
  9. | Train Loss: 0.305
  10. | Train Accuracy: 0.810
  11. | Val Loss: 0.270
  12. | Val Accuracy: 0.842
  13. 100%|██████████| 4000/4000 [09:36<00:00, 6.93it/s]
  14. 0%| | 0/4000 [00:00<?, ?it/s]Epochs: 3
  15. | Train Loss: 0.163
  16. | Train Accuracy: 0.910
  17. | Val Loss: 0.223
  18. | Val Accuracy: 0.878
  19. 100%|██████████| 4000/4000 [09:45<00:00, 6.83it/s]
  20. Epochs: 4
  21. | Train Loss: 0.092
  22. | Train Accuracy: 0.953
  23. | Val Loss: 0.208
  24. | Val Accuracy: 0.882
  25. 100%|██████████| 4000/4000 [09:52<00:00, 6.76it/s]
  26. Epochs: 5
  27. | Train Loss: 0.058
  28. | Train Accuracy: 0.970
  29. | Val Loss: 0.196
  30. | Val Accuracy: 0.898
  31. Test Accuracy: 0.932

显然,由于训练过程的随机性,每次可能不会得到与上面截图类似的损失和准确率值。如果在 5 个 epoch 之后没有得到好的结果,可以尝试将 epoch 增加到 10 个,或者调整学习率。

5、在测试数据上评估模型

现在我们已经训练了模型,我们可以使用测试数据来评估模型在未见数据上的性能。下面是评估模型在测试集上的性能的函数。

  1. def evaluate(model, test_data):
  2. test = Dataset(test_data)
  3. test_dataloader = torch.utils.data.DataLoader(test, batch_size=2)
  4. use_cuda = torch.cuda.is_available()
  5. device = torch.device("cuda" if use_cuda else "cpu")
  6. if use_cuda:
  7. model = model.cuda()
  8. total_acc_test = 0
  9. with torch.no_grad():
  10. for test_input, test_label in test_dataloader:
  11. test_label = test_label.to(device)
  12. mask = test_input['attention_mask'].to(device)
  13. input_id = test_input['input_ids'].squeeze(1).to(device)
  14. output = model(input_id, mask)
  15. acc = (output.argmax(dim=1) == test_label).sum().item()
  16. total_acc_test += acc
  17. print(f'Test Accuracy: {total_acc_test / len(test_data): .3f}')

精确度为0.932,已然不低

四、下一步目标

1、保存训练后的模型,方便下次直接调用

2、学习如何加快模型训练速度

3、直接调用模型

4、保存分类后的结果

五、全部代码(包含bert中文模型、数据集)

GitHub地址:

GitHub - YZXqqq/first_bert: 第一次尝试bert模型训练及测试,结果为0.9+

百度网盘下载链接:

链接: https://pan.baidu.com/s/1BddRqHZAP6yWwLSxr7CgOg?pwd=rxff 提取码: rxff 复制这段内容后打开百度网盘手机App,操作更方便哦

如有失效或者需要阿里云盘链接的,可以留言告知,也可以私信我,也可以关注个人公众号【机器学习之NLP爬坡之旅】后台私信【001】获取下载链接

注:压缩包有1G

参考文章:

保姆级教程,用PyTorch和BERT进行文本分类 - 知乎

关于封面

封面提示词来自智谱清言旗下的GLM-4

封面来自智谱清言旗下的AI画图 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/235663
推荐阅读
相关标签
  

闽ICP备14008679号