当前位置:   article > 正文

用pytorch进行BERT文本分类_pytorch bert文本分类

pytorch bert文本分类

BERT 是一个强大的语言模型,至少有两个原因:

  1. 它使用从 BooksCorpus (有 8 亿字)和 Wikipedia(有 25 亿字)中提取的未标记数据进行预训练。
  2. 顾名思义,它是通过利用编码器堆栈的双向特性进行预训练的。这意味着 BERT 不仅从左到右,而且从右到左从单词序列中学习信息。

BERT 模型需要一系列 tokens (words) 作为输入。在每个token序列中,BERT 期望输入有两个特殊标记:

  • [CLS] :这是每个sequence的第一个token,代表分类token。
  • [SEP] :这是让BERT知道哪个token属于哪个序列的token。这一特殊表征法主要用于下一个句子预测任务或问答任务。如果我们只有一个sequence,那么这个token将被附加到序列的末尾。

就像Transformer的普通编码器一样,BERT 将一系列单词作为输入,这些单词不断向上流动。每一层都应用自我注意,并将其结果通过前馈网络传递,然后将其传递给下一个编码器。

 

BERT 输出

每个位置输出一个大小为 hidden_ size的向量(BERT Base 中为 768)。对于我们在上面看到的句子分类示例,我们只关注第一个位置的输出(将特殊的 [CLS] token 传递到该位置)。

该向量现在可以用作我们选择的分类器的输入。该论文仅使用单层神经网络作为分类器就取得了很好的效果。

使用 BERT 进行文本分类

本文的主题是用 BERT 对文本进行分类。

在这篇文章中,我们将使用kaggle上的BBC 新闻分类数据集。

数据集已经是 CSV 格式,它有 2126 个不同的文本,每个文本都标记在 5 个类别中的一个之下:

sport(体育),business(商业),politics(政治),tech(科技),entertainment(娱乐)。


模型下载 https://huggingface.co/bert-base-cased/tree/main

数据集下载 bbc-news https://huggingface.co/datasets/SetFit/bbc-news/tree/main

 有4个400多MB的文件,pytorch的模型对应的是436MB的那个文件。

需要安装transforms库

pip install transforms 

全部的流程代码:

  1. # 全部流程代码
  2. import numpy as np
  3. import torch
  4. from transformers import BertTokenizer
  5. tokenizer = BertTokenizer.from_pretrained('bert-base-cased')
  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
  35. # 数据集准备
  36. # 拆分训练集、验证集和测试集 8:1:1
  37. import pandas as pd
  38. bbc_text_df = pd.read_csv('./bbc-news/bbc-text.csv')
  39. # bbc_text_df.head()
  40. df = pd.DataFrame(bbc_text_df)
  41. np.random.seed(112)
  42. df_train, df_val, df_test = np.split(df.sample(frac=1, random_state=42), [int(.8*len(df)), int(.9*len(df))])
  43. print(len(df_train),len(df_val), len(df_test))
  44. # 1780 222 223
  45. # 构建模型
  46. from torch import nn
  47. from transformers import BertModel
  48. class BertClassifier(nn.Module):
  49. def __init__(self, dropout=0.5):
  50. super(BertClassifier, self).__init__()
  51. self.bert = BertModel.from_pretrained('bert-base-cased')
  52. self.dropout = nn.Dropout(dropout)
  53. self.linear = nn.Linear(768, 5)
  54. self.relu = nn.ReLU()
  55. def forward(self, input_id, mask):
  56. _, pooled_output = self.bert(input_ids= input_id, attention_mask=mask,return_dict=False)
  57. dropout_output = self.dropout(pooled_output)
  58. linear_output = self.linear(dropout_output)
  59. final_layer = self.relu(linear_output)
  60. return final_layer
  61. #从上面的代码可以看出,BERT Classifier 模型输出了两个变量:
  62. #1. 在上面的代码中命名的第一个变量_包含sequence中所有 token 的 Embedding 向量层。
  63. #2. 命名的第二个变量pooled_output包含 [CLS] token 的 Embedding 向量。对于文本分类任务,使用这个 Embedding 作为分类器的输入就足够了。
  64. # 然后将pooled_output变量传递到具有ReLU激活函数的线性层。在线性层中输出一个维度大小为 5 的向量,每个向量对应于标签类别(运动、商业、政治、 娱乐和科技)。
  65. from torch.optim import Adam
  66. from tqdm import tqdm
  67. def train(model, train_data, val_data, learning_rate, epochs):
  68. # 通过Dataset类获取训练和验证集
  69. train, val = Dataset(train_data), Dataset(val_data)
  70. # DataLoader根据batch_size获取数据,训练时选择打乱样本
  71. train_dataloader = torch.utils.data.DataLoader(train, batch_size=2, shuffle=True)
  72. val_dataloader = torch.utils.data.DataLoader(val, batch_size=2)
  73. # 判断是否使用GPU
  74. use_cuda = torch.cuda.is_available()
  75. device = torch.device("cuda" if use_cuda else "cpu")
  76. # 定义损失函数和优化器
  77. criterion = nn.CrossEntropyLoss()
  78. optimizer = Adam(model.parameters(), lr=learning_rate)
  79. if use_cuda:
  80. model = model.cuda()
  81. criterion = criterion.cuda()
  82. # 开始进入训练循环
  83. for epoch_num in range(epochs):
  84. # 定义两个变量,用于存储训练集的准确率和损失
  85. total_acc_train = 0
  86. total_loss_train = 0
  87. # 进度条函数tqdm
  88. for train_input, train_label in tqdm(train_dataloader):
  89. train_label = train_label.to(device)
  90. mask = train_input['attention_mask'].to(device)
  91. input_id = train_input['input_ids'].squeeze(1).to(device)
  92. # 通过模型得到输出
  93. output = model(input_id, mask)
  94. # 计算损失
  95. batch_loss = criterion(output, train_label)
  96. total_loss_train += batch_loss.item()
  97. # 计算精度
  98. acc = (output.argmax(dim=1) == train_label).sum().item()
  99. total_acc_train += acc
  100. # 模型更新
  101. model.zero_grad()
  102. batch_loss.backward()
  103. optimizer.step()
  104. # ------ 验证模型 -----------
  105. # 定义两个变量,用于存储验证集的准确率和损失
  106. total_acc_val = 0
  107. total_loss_val = 0
  108. # 不需要计算梯度
  109. with torch.no_grad():
  110. # 循环获取数据集,并用训练好的模型进行验证
  111. for val_input, val_label in val_dataloader:
  112. # 如果有GPU,则使用GPU,接下来的操作同训练
  113. val_label = val_label.to(device)
  114. mask = val_input['attention_mask'].to(device)
  115. input_id = val_input['input_ids'].squeeze(1).to(device)
  116. output = model(input_id, mask)
  117. batch_loss = criterion(output, val_label)
  118. total_loss_val += batch_loss.item()
  119. acc = (output.argmax(dim=1) == val_label).sum().item()
  120. total_acc_val += acc
  121. print(
  122. f'''Epochs: {epoch_num + 1}
  123. | Train Loss: {total_loss_train / len(train_data): .3f}
  124. | Train Accuracy: {total_acc_train / len(train_data): .3f}
  125. | Val Loss: {total_loss_val / len(val_data): .3f}
  126. | Val Accuracy: {total_acc_val / len(val_data): .3f}''')
  127. #我们对模型进行了 5 个 epoch 的训练,我们使用 Adam 作为优化器,而学习率设置为1e-6。
  128. #因为本案例中是处理多类分类问题,则使用分类交叉熵作为我们的损失函数。
  129. EPOCHS = 5
  130. model = BertClassifier()
  131. LR = 1e-6
  132. train(model, df_train, df_val, LR, EPOCHS)
  133. # 测试模型
  134. def evaluate(model, test_data):
  135. test = Dataset(test_data)
  136. test_dataloader = torch.utils.data.DataLoader(test, batch_size=2)
  137. use_cuda = torch.cuda.is_available()
  138. device = torch.device("cuda" if use_cuda else "cpu")
  139. if use_cuda:
  140. model = model.cuda()
  141. total_acc_test = 0
  142. with torch.no_grad():
  143. for test_input, test_label in test_dataloader:
  144. test_label = test_label.to(device)
  145. mask = test_input['attention_mask'].to(device)
  146. input_id = test_input['input_ids'].squeeze(1).to(device)
  147. output = model(input_id, mask)
  148. acc = (output.argmax(dim=1) == test_label).sum().item()
  149. total_acc_test += acc
  150. print(f'Test Accuracy: {total_acc_test / len(test_data): .3f}')
  151. # 用测试数据集进行测试
  152. evaluate(model, df_test)

 

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

在训练的这一步会非常耗时间,用GPU加速了,也需要大概39分钟.

因为BERT模型本身就是一个比较大的模型,参数非常多。

最后一步测试的时候,测试的准确率还是比较高的。达到 99.6%

 

模型的保存。这个在原文里面是没有提到的。

我们花了很多时间训练的模型如果不保存一下,下次还要重新训练岂不是费时费力?

  1. # 保存模型
  2. torch.save(model.state_dict(),"bertMy.pth")
  3. # load 加载模型
  4. model = BertClassifier()
  5. model.load_state_dict(torch.load("bertMy.pth"))

 可以用下面的代码查看model里面的模型。

  1. for param_tensor in model.state_dict():
  2. print(param_tensor, "\t", model.state_dict()[param_tensor].size())

 也可以将保存的模型文件 bertMy.pth上传到netron网站进行模型可视化。Netronhttps://netron.app/

 

原文位于这个地址,但是原文中的代码缺了读csv那一段 :

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

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

闽ICP备14008679号