• 对pytorch中的文本分类实例代码进行逐行注释


    实例代码网址:https://pytorch.org/tutorials/beginner/text_sentiment_ngrams_tutorial.html

    注意:代码适用于jupyter notebook分块运行

    第一步、导入数据集并查看

    1. import torch
    2. from torchtext.datasets import AG_NEWS # 导入数据集
    3. train_iter = iter(AG_NEWS(split="train")) # 构建训练数据集的迭代器,节约内存
    4. next(train_iter) # 打印第一个元素(即第一个数据对)查看

    第二步、设置分词工具,并构建数据集对应的字典

    1. from torchtext.data.utils import get_tokenizer # 用于对句子进行分词,返回一个列表
    2. from torchtext.vocab import build_vocab_from_iterator # 用于生成文本文件(这里是训练集)对应的字典,需要输入一个生成器对象帮助返回文本文件中的所有单词
    3. tokenizer = get_tokenizer("basic_english") # 基于英语的分词器
    4. train_iter = AG_NEWS(split="train") # 训练数据集
    5. def yield_tokens(data_iter):
    6. """对每个训练数据集中的句子进行分词并返回分词对应的列表"""
    7. for _, text in data_iter:
    8. yield tokenizer(text)
    9. vocab = build_vocab_from_iterator(yield_tokens(train_iter), specials=['']) # 建立训练数据集的词表,并添加一个特殊字符""
    10. vocab.set_default_index(vocab['']) # 给特殊字符单独设置索引
    11. vocab(['here', 'is', 'an', 'example']) # 查看这4个单词在字典中对应的索引

    第三步、准备文本和标签的索引转换

    1. # 准备文本和标签的处理器,即给文本(根据词表)和标签设置索引
    2. text_pipeline = lambda x: vocab(tokenizer(x))
    3. label_pipeline = lambda x: int(x) - 1

    第四步、生成batch

    1. from torch.utils.data import DataLoader # 数据加载类
    2. device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 根据电脑情况设置使用gpu还是cpu进行训练
    3. def collate_batch(batch):
    4. """数据加载器需要的加载batch的方式函数,即如何把一个原始的batch数据转换成一个可以训练的batch"""
    5. label_list, text_list, offsets = [], [], [0]
    6. for _label, _text in batch:
    7. label_list.append(label_pipeline(_label)) # 把标签转换成标签索引并且加入标签列表中
    8. processed_text = torch.tensor(text_pipeline(_text), dtype=torch.int64) # 先把文本转换成字典中对应的索引,再把索引换成张量,得到处理过后的句子向量
    9. text_list.append(processed_text) # 把文本(一句话)向量加入文本列表中
    10. offsets.append(processed_text.size(0)) # 把文本向量的大小(即这个向量有多少个元素,对应句子中有多少个)作为偏移值
    11. label_list = torch.tensor(label_list, dtype=torch.int64) # 把列表转换成张量
    12. offsets = torch.tensor(offsets[:-1]).cumsum(dim=0) # 把偏移值转换成张量并在第一个维度求和
    13. text_list = torch.cat(text_list) # 对文本的张量进行拼接,默认在第一个维度进行
    14. return label_list.to(device), text_list.to(device), offsets.to(device) # 把标签、文本和偏移值全部转换成适合对应设备的
    15. train_iter = AG_NEWS(split="train") # 获取原始的训练数据集
    16. # 定义训练数据集的数据加载类
    17. dataloader = DataLoader(
    18. train_iter, batch_size=8, shuffle=False, collate_fn=collate_batch
    19. )

    第五步、定义文本分类模型

    1. from torch import nn
    2. class TextClassificationModel(nn.Module):
    3. def __init__(self, vocab_size, embed_dim, num_class):
    4. super(TextClassificationModel, self).__init__() # 继承nn.Module
    5. self.embedding = nn.EmbeddingBag(vocab_size, embed_dim, sparse=False) # 设置词嵌入层,其中第一个参数是指字典的大小;第二个参数是设置每个单词转换成的向量的维度
    6. self.fc = nn.Linear(embed_dim, num_class) # 线性层,进行维度的转换(把文本向量从最初嵌入时的维度大小转换成类别的维度大小)
    7. self.init_weights() # 设置初始权重
    8. def init_weights(self):
    9. initrange = 0.5 # 设置初始权重的范围是[-0.5, 0.5]
    10. self.embedding.weight.data.uniform_(-initrange, initrange) # 初始化词嵌入层的权重
    11. self.fc.weight.data.uniform_(-initrange, initrange) # 初始化线性层的权重
    12. self.fc.bias.data.zero_() # 初始化线性层的偏置
    13. def forward(self, text, offsets):
    14. embedded = self.embedding(text, offsets) # 得到文本的嵌入表示,即向量
    15. return self.fc(embedded) # 让文本向量经过线性层,即得到文本分类的结果
    16. train_iter = AG_NEWS(split="train") # 获得原始数据集
    17. num_class = len(set([label for (label, text) in train_iter])) # 获得数据集中标签类别的个数
    18. vocab_size = len(vocab) # 获得字典的大小
    19. emsize = 64 # 设置每个单词转换成向量的维度
    20. model = TextClassificationModel(vocab_size, emsize, num_class).to(device) # 创建模型

    第六步、定义训练与验证模型的步骤

    1. import time
    2. def train(dataloader):
    3. model.train() # 模型进入训练模式
    4. total_acc, total_count = 0, 0 # 总共预测对几个标签和一共预测了多少个标签,用于计算准确率
    5. log_interval = 500 # 每次预测500个时就打印一次
    6. start_time = time.time() # 模型开始训练的时间
    7. for idx, (label, text, offsets) in enumerate(dataloader):
    8. optimizer.zero_grad() # 清除上次的梯度
    9. predicted_label = model(text, offsets) # 预测标签
    10. loss = criterion(predicted_label, label) # 计算损失
    11. loss.backward() # 反向传播
    12. torch.nn.utils.clip_grad_norm(model.parameters(), 0.1) # 计算梯度
    13. optimizer.step() # 更新模型参数
    14. total_acc += (predicted_label.argmax(1) == label).sum().item() # 指定预测的标签值第二个维度中的最大值所在的索引,与真实标签的索引进行对比是否一样(结果只有0和1),然后求和得到一个张量值,最后将其转换成标量作为这次训练的准确率
    15. total_count += label.size(0) # 这里就是8,一个batch中有8个label
    16. if idx % log_interval == 0 and idx > 0:
    17. elapsed = time.time() - start_time # 计算每500次花费的时间
    18. print(
    19. "| epoch {:3d} | {:5d}/{:5d} batches"
    20. "| accuracy {:8.3f}".format(
    21. epoch, idx, len(dataloader), total_acc / total_count
    22. )
    23. )
    24. total_acc, total_count = 0, 0 # 满500次重新计算这2个指标
    25. start_time = time.time() # 满500次重新计算运行时间
    26. def evaluate(dataloader):
    27. model.eval()
    28. total_acc, total_count = 0, 0
    29. with torch.no_grad(): # 不计算梯度,因为只用于预测
    30. for idx, (label, text, offsets) in enumerate(dataloader):
    31. predicted_label = model(text, offsets) # 预测标签
    32. loss = criterion(predicted_label, label) # 计算损失
    33. total_acc += (predicted_label.argmax(1) == label).sum().item() # 计算正确预测数量
    34. total_count += label.size(0) # 计算总的预测数量
    35. return total_acc / total_count # 计算验证时的准确率

    第七步、分割数据集,定义超参数并训练模型

    1. from torch.utils.data.dataset import random_split # 将数据集切分成不重复的部分
    2. from torchtext.data.functional import to_map_style_dataset # 将可迭代的数据集转换成映射格式的数据集
    3. # 超参数
    4. EPOCHS = 10 # 所有batch重复迭代的次数
    5. LR = 5 # 学习率
    6. BATCH_SIZE = 64 # 每个batch的大小
    7. criterion = torch.nn.CrossEntropyLoss() # 交叉熵损失函数
    8. optimizer = torch.optim.SGD(model.parameters(), lr = LR) # 采用随机梯度下降优化算法
    9. scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.1) # 对随机梯度下降采用学习率动态调整策略,gamma是学习率指数衰减的底数(还有其他调整策略如步长衰减等)
    10. total_accu = None
    11. train_iter, test_iter = AG_NEWS() # 加载训练数据集合测试数据集
    12. train_dataset = to_map_style_dataset(train_iter) # 将训练数据集转换成映射格式
    13. test_dataset = to_map_style_dataset(test_iter) # 将测试数据集转化成映射格式
    14. num_train = int(len(train_dataset) * 0.95) # 将95%的训练数据集用于训练
    15. split_train, split_valid = random_split(
    16. train_dataset, [num_train, len(train_dataset) - num_train]
    17. ) # 随机切分,95%的训练数据集用于训练,剩余5%用于验证
    18. train_dataloader = DataLoader(
    19. split_train, batch_size = BATCH_SIZE, shuffle = True, collate_fn = collate_batch
    20. ) # 训练数据集
    21. valid_dataloader = DataLoader(
    22. split_valid, batch_size=BATCH_SIZE, shuffle = True, collate_fn = collate_batch
    23. ) # 验证训练集(用于反映真实的训练效果)
    24. test_dataloader = DataLoader(
    25. test_dataset, batch_size=BATCH_SIZE, shuffle=True, collate_fn=collate_batch
    26. ) # 测试数据集(直接用于预测,不计算准确率)
    27. for epoch in range(1, EPOCHS + 1):
    28. epoch_start_time = time.time()
    29. train(train_dataloader) # 训练模型
    30. accu_val = evaluate(valid_dataloader) # 验证模型
    31. if total_accu is not None and total_accu > accu_val:
    32. scheduler.step() # 当新一次的验证准确率没有上一次验证时的学习率高,则让学习率动态衰减以提升下次训练的效果
    33. else:
    34. total_accu = accu_val
    35. print("-" * 59)
    36. print(
    37. "| end of epoch {:3d} | time: {:5.2f}s |"
    38. "| valid accuracy {:8.3f}".format(
    39. epoch, time.time() - epoch_start_time, accu_val
    40. )
    41. )
    42. print("-" * 59)

  • 相关阅读:
    超越GPT-3,DeepMind推出新宠Gato,却被质疑“换汤不换药”?
    项目沟通管理案例题
    serialVersionUID、transient关键字、Properties作为Map集合的使用、特有方法及和IO流结合的方法
    PyCharm及python解释器详细安装教程
    【附源码】计算机毕业设计JAVA疫情下智慧社区系统
    线程安全介绍
    Ansible playbook的block
    mybatis 执行流程,mybatis源码解析,推荐收藏
    第一个简单爬虫:获取页面
    ARMv8 cache的包含策略inclusive 和 exclusive之间的区别以及Cortex-A55示例详解
  • 原文地址:https://blog.csdn.net/weixin_62588253/article/details/133960920