深度学习实验十三 卷积神经网络(4)——使用预练习resnet18实现CIFAR-10分 ...

打印 上一主题 下一主题

主题 808|帖子 808|积分 2424

目次
一、数据加载
二、数据集类构建
三、模型构建
四、模型练习
五、模型评价及猜测
附完整可运行代码:
实验大要步调:

注:
在自己电脑的CPU跑代码×
毗连长途服务器跑代码√
本次实验由于数据量巨大,我的笔记本上还没有GPU,跑代码不停跑不出来,不停停在如下这个位置不动:

于是我想到了毗连长途服务器来跑代码。
 如果你的电脑上有GPU,那么可以参考学长的博客跑:学长本次实验的博客
我这里使用的是AutoDL算力云,租个3080的服务器,跑个七八分钟就能出来,而且租一小时才1块多!安利!
我在这里也附上怎样使用长途服务器的方法
进入AutoDL官网后,点击算力市场:

任意租个就可以


基础镜像这边选个Pytorch的某个版本就行
接下来就跟着这个视频做吧:Pycharm毗连长途GPU服务器跑深度学习_哔哩哔哩_bilibili
有几点需要留意的地方:
1.pycharm必须是专业版,社区版没有毗连长途服务器的功能。pycharm专业版参考这个链接:​​​​​​​Pycharm专业版​​​​​​​
2.毗连成功之后,可能会遇到一些安装包安装不上的标题,比如我在import torch的这里不停报错,体现安装错误,多安装几次就好了,安装的时间尽量不要连hbu的校园网,不是很稳定,容易下载出错。
3.在自己笔记本就能运行,也没有报错,为什么毗连个长途服务器会出现size不匹配的错误?还有numpy也会出错?


第一个标题,这个错误是因为模型中的输入和权重在不同的装备上进行计算,导致了范例不匹配。我的模型使用了GPU(torch.cuda.FloatTensor),而输入数据却是CPU上的张量(torch.FloatTensor)。PyTorch要求输入和模型的权重在雷同的装备上,才能进行有用的计算。以是,需要时时候刻确保输入数据和模型在同一个装备上。
于是要多处添加如下的代码:

第二个标题,出现在 nndl_3.py 文件中的 self.metric.update(logits, y) 这一行,具体是因为实验将一个在 GPU 上的 tensor 转换为 NumPy 数组。由于 NumPy 不支持在 GPU 上的 tensor,以是必须先将其移动到 CPU 上。代码改成如下:

一、数据加载

本实验的数据集使用计算机视觉领域的经典数据集:CIFAR-10数据集,数据集下载位置:数据集下载

代码如下:
  1. def load_cifar10_batch(folder_path, batch_id=1, mode='train'):
  2.     if mode == 'test':
  3.         file_path = os.path.join(folder_path, 'test_batch')
  4.     else:
  5.         file_path = os.path.join(folder_path, 'data_batch_' + str(batch_id))
  6.     # 加载数据集文件
  7.     with open(file_path, 'rb') as batch_file:
  8.         batch = pickle.load(batch_file, encoding='latin1')
  9.     imgs = batch['data'].reshape((len(batch['data']), 3, 32, 32)) / 255.
  10.     labels = batch['labels']
  11.     return np.array(imgs, dtype='float32'), np.array(labels)
  12. imgs_batch, labels_batch = load_cifar10_batch(folder_path=r'cifar-10-batches-py',
  13.                                               batch_id=1, mode='train')
  14. # 打印一下每个batch中X和y的维度
  15. print("batch of imgs shape: ", imgs_batch.shape, "batch of labels shape: ", labels_batch.shape)
  16. print(torch.__version__)
  17. print(torch.cuda.is_available())
  18. # ==========可视化观察其中的一张样本图像和对应的标签=================================
  19. import matplotlib.pyplot as plt
  20. image, label = imgs_batch[1], labels_batch[1]
  21. print("The label in the picture is {}".format(label))
  22. plt.figure(figsize=(2, 2))
  23. plt.imshow(image.transpose(1, 2, 0))
  24. plt.show()
复制代码
运行结果:
 

二、数据集类构建

代码如下:
  1. # =====构建Dataset类======================================================
  2. import torch
  3. from torch.utils.data import Dataset, DataLoader
  4. import torchvision.transforms as transforms
  5. class CIFAR10Dataset(Dataset):
  6.     def __init__(self, folder_path=r'cifar-10-batches-py', mode='train', device=None):
  7.         if mode == 'train':
  8.             self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, batch_id=1, mode='train')
  9.             for i in range(2, 5):
  10.                 imgs_batch, labels_batch = load_cifar10_batch(folder_path=folder_path, batch_id=i, mode='train')
  11.                 self.imgs, self.labels = np.concatenate([self.imgs, imgs_batch]), np.concatenate(
  12.                     [self.labels, labels_batch])
  13.         elif mode == 'dev':
  14.             self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, batch_id=5, mode='dev')
  15.         elif mode == 'test':
  16.             self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, mode='test')
  17.         self.transforms = transforms.Compose([transforms.Resize(32), transforms.ToTensor(),
  18.                                               transforms.Normalize(mean=[0.4914, 0.4822, 0.4465],
  19.                                                                    std=[0.2023, 0.1994, 0.2010])])
  20.         self.device = device  # 保存设备信息
  21.     def __getitem__(self, idx):
  22.         img, label = self.imgs[idx], self.labels[idx]
  23.         img, label = torch.tensor(img).float(), torch.tensor(label).long()  # 转为Tensor
  24.         if self.device:
  25.             img, label = img.to(self.device), label.to(self.device)  # 将数据传输到目标设备
  26.         return img, label
  27.     def __len__(self):
  28.         return len(self.imgs)
  29. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  30. print(device)
  31. # 创建数据集时传递设备信息
  32. train_dataset = CIFAR10Dataset(folder_path=r'cifar-10-batches-py', mode='train', device=device)
  33. dev_dataset = CIFAR10Dataset(folder_path=r'cifar-10-batches-py', mode='dev', device=device)
  34. test_dataset = CIFAR10Dataset(folder_path=r'cifar-10-batches-py', mode='test', device=device)
复制代码
三、模型构建

使用PyTorch API中的Resnet18进行图像分类实验。
代码如下:
  1. # =======模型构建========================================
  2. from torchvision.models import resnet18
  3. time1 = time.time()
  4. resnet18_model = resnet18(pretrained=True)
复制代码
四、模型练习

代码如下:
  1. # =======模型训练================================================
  2. import torch.nn.functional as F
  3. import torch.optim as opt
  4. from nndl_3 import RunnerV3, Accuracy
  5. # 学习率大小
  6. lr = 0.001
  7. # 批次大小
  8. batch_size = 64
  9. # 加载数据
  10. train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
  11. dev_loader = DataLoader(dev_dataset, batch_size=batch_size)
  12. test_loader = DataLoader(test_dataset, batch_size=batch_size)
  13. # 定义网络
  14. model = resnet18_model.to(device)
  15. # 定义优化器,这里使用Adam优化器以及l2正则化策略,相关内容在7.3.3.2和7.6.2中会进行详细介绍
  16. optimizer = opt.Adam(lr=lr, params=model.parameters(), weight_decay=0.005)
  17. # 定义损失函数
  18. loss_fn = F.cross_entropy
  19. loss_fn = loss_fn
  20. # 定义评价指标
  21. metric = Accuracy(is_logist=True)
  22. # 在训练和评估时,将数据传输到设备上
  23. for data, labels in train_loader:
  24.     data, labels = data.to(device), labels.to(device)  # 将数据和标签都发送到相同的设备
  25.     logits = model(data)  # 进行前向传播
  26.     loss = loss_fn(logits, labels)
  27.     optimizer.zero_grad()
  28.     loss.backward()
  29.     optimizer.step()
  30. # 实例化RunnerV3
  31. runner = RunnerV3(model, optimizer, loss_fn, metric)
  32. # 启动训练
  33. log_steps = 3000
  34. eval_steps = 3000
  35. runner.train(train_loader, dev_loader, num_epochs=30, log_steps=log_steps,
  36.              eval_steps=eval_steps, save_path="best_model.pdparams")
  37. from nndl_3 import plot
  38. plot(runner, fig_name='cnn-loss4.pdf')
复制代码
运行结果:

五、模型评价及猜测

代码如下:
  1. # ========模型评价============================================
  2. # 加载最优模型
  3. runner.load_model('best_model.pdparams')
  4. # 模型评价
  5. score, loss = runner.evaluate(test_loader)
  6. print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
  7. # ==========模型预测================================================
  8. import matplotlib.pyplot as plt
  9. # 获取测试集中的一个batch的数据
  10. X, label = next(iter(test_loader))
  11. X = X.to(device)
  12. logits = runner.predict(X, dim=1)
  13. # 多分类,使用softmax计算预测概率
  14. pred = F.softmax(logits)
  15. # print(pred)
  16. # 获取概率最大的类别
  17. pred_class = torch.argmax(pred[2][0]).cpu().numpy()
  18. label = label[2].item()
  19. # 输出真实类别与预测类别
  20. print("The true category is {} and the predicted category is {}".format(label, pred_class))
  21. # 可视化图片
  22. plt.figure(figsize=(2, 2))
  23. imgs, labels = load_cifar10_batch(folder_path=r'cifar-10-batches-py', mode='test')
  24. plt.imshow(imgs[2].transpose(1, 2, 0))
  25. plt.savefig('cnn-test-vis.pdf')
  26. time2 = time.time()
  27. print('使用预训练模型的训练时间:{}'.format(time2-time1))
复制代码
 运行结果:

运行时间:

以上是使用预练习的结果,对比不适用预练习,只需要修改这一行代码:
  1. resnet18_model = resnet18(pretrained=True)
复制代码
把True改为False即可。
不使用预练习的结果如下:



对比可以看出:使用预练习运行时间变短了,正确率变高了,那么这是为什么呢?
首先来说,什么是预练习?什么是迁移学习呢?
   预练习:      
    “预练习”的做法一般是将大量低成本网络的练习数据放在一起,颠末某种预训方法去学习其中的共性,然后将其中的共性“移植”到特定任务的模型中,再使用相关特定领域的少量标注数据进行“微调”,这样的话,模型只需要从“共性”出发,去学习该特定任务的“特别”部分即可。
        因为预练习模型已经在大规模数据集上学到了一般性的特征表现(如边缘、纹理、颜色等),以是参数已经初始化为较优值,只需对少部分参数进行调解,模型无需从零开始学习,因而练习过程更快。
    迁移学习:
  迁移学习主要头脑:
  

  迁移学习的焦点头脑是:在一个任务上练习得到的模型包含的知识可以部分或全部地转移到另一个任务上。这通常涉及以下两个主要步调:
  ·源任务学习: 在源任务上练习模型,这个任务通常有大量的数据可用。
  ·知识迁移: 将从源任务学到的知识(如网络参数、特征表现等)应用到目的任务上。
迁移学习通过重复利用已有的知识,不但进步了学习效率和模型性能,还低落了对标注数据的需求和整体练习成本。它在小数据集、跨领域应用和办理复杂标题方面展现了显著的上风。
平凡练习、预练习、迁移学习的对比:
  

   实验过程中,使用的优化器不是SGD了,而是换成了Adam,那么Adam优化器和SGD优化器有什么区别呢?
   Adam是一种常用的自适应优化算法,它结合了动量和自适应学习率机制。Adam是基于梯度降落法的,它通过调解每个参数的学习率来加速练习并避免一些常见标题(如梯度消失或爆炸)。
Adam优化器和SGD的对比:
  

Adam优化器主动调解每个参数的学习率,尤其得当大规模的深度学习任务。以是本次实验选择了Adam优化器。
  本次实验中还用到了权重衰减,用来防止过拟合,那么它的原理是什么呢?
   权重衰减是一种正则化方法,旨在防止模型的过拟合。它通过在损失函数中添加一个项,使得模型的参数保持较小的值,从而避免模型过分依赖某些特征,导致过拟合。
  权重衰减的公式:
  对于一个损失函数J(θ),在应用权重衰减后,损失函数变为:
  

λ 是权重衰减的超参数(通常称为正则化强度),决定了对权重的惩罚水平。
  
是模型权重的L2范数,即权重的平方和。
L2正则化和权重衰减本质上是雷同的,区别在于在实现时是否将其明白作为正则化项添加到损失函数中。通常,现代优化器(如Adam、SGD)会通过直接修改损失函数来实现这一点,从而形成权重衰减。
  
附完整可运行代码:

主程序:
  1. import osimport pickleimport timeimport numpy as npimport torchdef load_cifar10_batch(folder_path, batch_id=1, mode='train'):
  2.     if mode == 'test':
  3.         file_path = os.path.join(folder_path, 'test_batch')
  4.     else:
  5.         file_path = os.path.join(folder_path, 'data_batch_' + str(batch_id))
  6.     # 加载数据集文件
  7.     with open(file_path, 'rb') as batch_file:
  8.         batch = pickle.load(batch_file, encoding='latin1')
  9.     imgs = batch['data'].reshape((len(batch['data']), 3, 32, 32)) / 255.
  10.     labels = batch['labels']
  11.     return np.array(imgs, dtype='float32'), np.array(labels)
  12. imgs_batch, labels_batch = load_cifar10_batch(folder_path=r'cifar-10-batches-py',
  13.                                               batch_id=1, mode='train')
  14. # 打印一下每个batch中X和y的维度
  15. print("batch of imgs shape: ", imgs_batch.shape, "batch of labels shape: ", labels_batch.shape)
  16. print(torch.__version__)
  17. print(torch.cuda.is_available())
  18. # ==========可视化观察其中的一张样本图像和对应的标签=================================
  19. import matplotlib.pyplot as plt
  20. image, label = imgs_batch[1], labels_batch[1]
  21. print("The label in the picture is {}".format(label))
  22. plt.figure(figsize=(2, 2))
  23. plt.imshow(image.transpose(1, 2, 0))
  24. plt.show()
  25. # =====构建Dataset类======================================================
  26. import torch
  27. from torch.utils.data import Dataset, DataLoader
  28. import torchvision.transforms as transforms
  29. class CIFAR10Dataset(Dataset):
  30.     def __init__(self, folder_path=r'cifar-10-batches-py', mode='train', device=None):
  31.         if mode == 'train':
  32.             self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, batch_id=1, mode='train')
  33.             for i in range(2, 5):
  34.                 imgs_batch, labels_batch = load_cifar10_batch(folder_path=folder_path, batch_id=i, mode='train')
  35.                 self.imgs, self.labels = np.concatenate([self.imgs, imgs_batch]), np.concatenate(
  36.                     [self.labels, labels_batch])
  37.         elif mode == 'dev':
  38.             self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, batch_id=5, mode='dev')
  39.         elif mode == 'test':
  40.             self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, mode='test')
  41.         self.transforms = transforms.Compose([transforms.Resize(32), transforms.ToTensor(),
  42.                                               transforms.Normalize(mean=[0.4914, 0.4822, 0.4465],
  43.                                                                    std=[0.2023, 0.1994, 0.2010])])
  44.         self.device = device  # 保存设备信息
  45.     def __getitem__(self, idx):
  46.         img, label = self.imgs[idx], self.labels[idx]
  47.         img, label = torch.tensor(img).float(), torch.tensor(label).long()  # 转为Tensor
  48.         if self.device:
  49.             img, label = img.to(self.device), label.to(self.device)  # 将数据传输到目标设备
  50.         return img, label
  51.     def __len__(self):
  52.         return len(self.imgs)
  53. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  54. print(device)
  55. # 创建数据集时传递设备信息
  56. train_dataset = CIFAR10Dataset(folder_path=r'cifar-10-batches-py', mode='train', device=device)
  57. dev_dataset = CIFAR10Dataset(folder_path=r'cifar-10-batches-py', mode='dev', device=device)
  58. test_dataset = CIFAR10Dataset(folder_path=r'cifar-10-batches-py', mode='test', device=device)
  59. # =======模型构建========================================
  60. from torchvision.models import resnet18
  61. time1 = time.time()
  62. resnet18_model = resnet18(pretrained=True)
  63. # =======模型训练================================================
  64. import torch.nn.functional as F
  65. import torch.optim as opt
  66. from nndl_3 import RunnerV3, Accuracy
  67. # 学习率大小
  68. lr = 0.001
  69. # 批次大小
  70. batch_size = 64
  71. # 加载数据
  72. train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
  73. dev_loader = DataLoader(dev_dataset, batch_size=batch_size)
  74. test_loader = DataLoader(test_dataset, batch_size=batch_size)
  75. # 定义网络
  76. model = resnet18_model.to(device)
  77. # 定义优化器,这里使用Adam优化器以及l2正则化策略,相关内容在7.3.3.2和7.6.2中会进行详细介绍
  78. optimizer = opt.Adam(lr=lr, params=model.parameters(), weight_decay=0.005)
  79. # 定义损失函数
  80. loss_fn = F.cross_entropy
  81. loss_fn = loss_fn
  82. # 定义评价指标
  83. metric = Accuracy(is_logist=True)
  84. # 在训练和评估时,将数据传输到设备上
  85. for data, labels in train_loader:
  86.     data, labels = data.to(device), labels.to(device)  # 将数据和标签都发送到相同的设备
  87.     logits = model(data)  # 进行前向传播
  88.     loss = loss_fn(logits, labels)
  89.     optimizer.zero_grad()
  90.     loss.backward()
  91.     optimizer.step()
  92. # 实例化RunnerV3
  93. runner = RunnerV3(model, optimizer, loss_fn, metric)
  94. # 启动训练
  95. log_steps = 3000
  96. eval_steps = 3000
  97. runner.train(train_loader, dev_loader, num_epochs=30, log_steps=log_steps,
  98.              eval_steps=eval_steps, save_path="best_model.pdparams")
  99. from nndl_3 import plot
  100. plot(runner, fig_name='cnn-loss4.pdf')
  101. # ========模型评价============================================
  102. # 加载最优模型
  103. runner.load_model('best_model.pdparams')
  104. # 模型评价
  105. score, loss = runner.evaluate(test_loader)
  106. print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
  107. # ==========模型预测================================================
  108. import matplotlib.pyplot as plt
  109. # 获取测试集中的一个batch的数据
  110. X, label = next(iter(test_loader))
  111. X = X.to(device)
  112. logits = runner.predict(X, dim=1)
  113. # 多分类,使用softmax计算预测概率
  114. pred = F.softmax(logits)
  115. # print(pred)
  116. # 获取概率最大的类别
  117. pred_class = torch.argmax(pred[2][0]).cpu().numpy()
  118. label = label[2].item()
  119. # 输出真实类别与预测类别
  120. print("The true category is {} and the predicted category is {}".format(label, pred_class))
  121. # 可视化图片
  122. plt.figure(figsize=(2, 2))
  123. imgs, labels = load_cifar10_batch(folder_path=r'cifar-10-batches-py', mode='test')
  124. plt.imshow(imgs[2].transpose(1, 2, 0))
  125. plt.savefig('cnn-test-vis.pdf')
  126. time2 = time.time()
  127. print('使用预训练模型的训练时间:{}'.format(time2-time1))
复制代码
nndl_3的代码:
  1. import torch
  2. from matplotlib import pyplot as plt
  3. from torch import nn
  4. class Op(object):
  5.     def __init__(self):
  6.         pass
  7.     def __call__(self, inputs):
  8.         return self.forward(inputs)
  9.     def forward(self, inputs):
  10.         raise NotImplementedError
  11.     def backward(self, inputs):
  12.         raise NotImplementedError
  13. # 实现一个两层前馈神经网络
  14. class Model_MLP_L2_V3(torch.nn.Module):
  15.     def __init__(self, input_size, hidden_size, output_size):
  16.         super(Model_MLP_L2_V3, self).__init__()
  17.         self.fc1 = torch.nn.Linear(input_size, hidden_size)
  18.         w_ = torch.normal(0, 0.01, size=(hidden_size, input_size), requires_grad=True)
  19.         self.fc1.weight = torch.nn.Parameter(w_)
  20.         self.fc1.bias = torch.nn.init.constant_(self.fc1.bias, val=1.0)
  21.         self.fc2 = torch.nn.Linear(hidden_size, output_size)
  22.         w2 = torch.normal(0, 0.01, size=(output_size, hidden_size), requires_grad=True)
  23.         self.fc2.weight = nn.Parameter(w2)
  24.         self.fc2.bias = torch.nn.init.constant_(self.fc2.bias, val=1.0)
  25.         self.act = torch.sigmoid
  26.     def forward(self, inputs):
  27.         outputs = self.fc1(inputs)
  28.         outputs = self.act(outputs)
  29.         outputs = self.fc2(outputs)
  30.         return outputs
  31. class RunnerV3(object):
  32.     def __init__(self, model, optimizer, loss_fn, metric, **kwargs):
  33.         self.model = model
  34.         self.optimizer = optimizer
  35.         self.loss_fn = loss_fn
  36.         self.metric = metric  # 只用于计算评价指标
  37.         # 记录训练过程中的评价指标变化情况
  38.         self.dev_scores = []
  39.         # 记录训练过程中的损失函数变化情况
  40.         self.train_epoch_losses = []  # 一个epoch记录一次loss
  41.         self.train_step_losses = []  # 一个step记录一次loss
  42.         self.dev_losses = []
  43.         # 记录全局最优指标
  44.         self.best_score = 0
  45.     def train(self, train_loader, dev_loader=None, **kwargs):
  46.         # 将模型切换为训练模式
  47.         self.model.train()
  48.         # 传入训练轮数,如果没有传入值则默认为0
  49.         num_epochs = kwargs.get("num_epochs", 0)
  50.         # 传入log打印频率,如果没有传入值则默认为100
  51.         log_steps = kwargs.get("log_steps", 100)
  52.         # 评价频率
  53.         eval_steps = kwargs.get("eval_steps", 0)
  54.         # 传入模型保存路径,如果没有传入值则默认为"best_model.pdparams"
  55.         save_path = kwargs.get("save_path", "best_model.pdparams")
  56.         custom_print_log = kwargs.get("custom_print_log", None)
  57.         # 训练总的步数
  58.         num_training_steps = num_epochs * len(train_loader)
  59.         if eval_steps:
  60.             if self.metric is None:
  61.                 raise RuntimeError('Error: Metric can not be None!')
  62.             if dev_loader is None:
  63.                 raise RuntimeError('Error: dev_loader can not be None!')
  64.         # 运行的step数目
  65.         global_step = 0
  66.         # 进行num_epochs轮训练
  67.         for epoch in range(num_epochs):
  68.             # 用于统计训练集的损失
  69.             total_loss = 0
  70.             for step, data in enumerate(train_loader):
  71.                 X, y = data
  72.                 # 获取模型预测
  73.                 logits = self.model(X)
  74.                 loss = self.loss_fn(logits, y.long())  # 默认求mean
  75.                 total_loss += loss
  76.                 # 训练过程中,每个step的loss进行保存
  77.                 self.train_step_losses.append((global_step, loss.item()))
  78.                 if log_steps and global_step % log_steps == 0:
  79.                     print(
  80.                         f"[Train] epoch: {epoch}/{num_epochs}, step: {global_step}/{num_training_steps}, loss: {loss.item():.5f}")
  81.                 # 梯度反向传播,计算每个参数的梯度值
  82.                 loss.backward()
  83.                 if custom_print_log:
  84.                     custom_print_log(self)
  85.                 # 小批量梯度下降进行参数更新
  86.                 self.optimizer.step()
  87.                 # 梯度归零
  88.                 self.optimizer.zero_grad()
  89.                 # 判断是否需要评价
  90.                 if eval_steps > 0 and global_step > 0 and \
  91.                         (global_step % eval_steps == 0 or global_step == (num_training_steps - 1)):
  92.                     dev_score, dev_loss = self.evaluate(dev_loader, global_step=global_step)
  93.                     print(f"[Evaluate]  dev score: {dev_score:.5f}, dev loss: {dev_loss:.5f}")
  94.                     # 将模型切换为训练模式
  95.                     self.model.train()
  96.                     # 如果当前指标为最优指标,保存该模型
  97.                     if dev_score > self.best_score:
  98.                         self.save_model(save_path)
  99.                         print(
  100.                             f"[Evaluate] best accuracy performence has been updated: {self.best_score:.5f} --> {dev_score:.5f}")
  101.                         self.best_score = dev_score
  102.                 global_step += 1
  103.             # 当前epoch 训练loss累计值
  104.             trn_loss = (total_loss / len(train_loader)).item()
  105.             # epoch粒度的训练loss保存
  106.             self.train_epoch_losses.append(trn_loss)
  107.         print("[Train] Training done!")
  108.     # 模型评估阶段,使用'torch.no_grad()'控制不计算和存储梯度
  109.     @torch.no_grad()
  110.     def evaluate(self, dev_loader, **kwargs):
  111.         assert self.metric is not None
  112.         # 将模型设置为评估模式
  113.         self.model.eval()
  114.         global_step = kwargs.get("global_step", -1)
  115.         # 用于统计训练集的损失
  116.         total_loss = 0
  117.         # 重置评价
  118.         self.metric.reset()
  119.         # 遍历验证集每个批次
  120.         for batch_id, data in enumerate(dev_loader):
  121.             X, y = data
  122.             # 计算模型输出
  123.             logits = self.model(X)
  124.             # 计算损失函数
  125.             loss = self.loss_fn(logits, y).item()
  126.             # 累积损失
  127.             total_loss += loss
  128.             # 累积评价
  129.             self.metric.update(logits, y)
  130.         dev_loss = (total_loss / len(dev_loader))
  131.         dev_score = self.metric.accumulate()
  132.         # 记录验证集loss
  133.         if global_step != -1:
  134.             self.dev_losses.append((global_step, dev_loss))
  135.             self.dev_scores.append(dev_score)
  136.         return dev_score, dev_loss
  137.     # 模型评估阶段,使用'torch.no_grad()'控制不计算和存储梯度
  138.     @torch.no_grad()
  139.     def predict(self, x, **kwargs):
  140.         # 将模型设置为评估模式
  141.         self.model.eval()
  142.         # 运行模型前向计算,得到预测值
  143.         logits = self.model(x)
  144.         return logits
  145.     def save_model(self, save_path):
  146.         torch.save(self.model.state_dict(), save_path)
  147.     def load_model(self, model_path):
  148.         model_state_dict = torch.load(model_path)
  149.         self.model.load_state_dict(model_state_dict)
  150. class Accuracy():
  151.     def __init__(self, is_logist=True):
  152.         # 用于统计正确的样本个数
  153.         self.num_correct = 0
  154.         # 用于统计样本的总数
  155.         self.num_count = 0
  156.         self.is_logist = is_logist
  157.     def update(self, outputs, labels):
  158.         if outputs.shape[1] == 1:  # 二分类
  159.             outputs = torch.squeeze(outputs, dim=-1)
  160.             if self.is_logist:
  161.                 # logist判断是否大于0
  162.                 preds = torch.tensor((outputs >= 0), dtype=torch.float32)
  163.             else:
  164.                 # 如果不是logist,判断每个概率值是否大于0.5,当大于0.5时,类别为1,否则类别为0
  165.                 preds = torch.tensor((outputs >= 0.5), dtype=torch.float32)
  166.         else:
  167.             # 多分类时,使用'torch.argmax'计算最大元素索引作为类别
  168.             preds = torch.argmax(outputs, dim=1)
  169.         # 获取本批数据中预测正确的样本个数
  170.         labels = torch.squeeze(labels, dim=-1)
  171.         batch_correct = torch.sum(torch.tensor(preds == labels, dtype=torch.float32)).cpu().numpy()
  172.         batch_count = len(labels)
  173.         # 更新num_correct 和 num_count
  174.         self.num_correct += batch_correct
  175.         self.num_count += batch_count
  176.     def accumulate(self):
  177.         # 使用累计的数据,计算总的指标
  178.         if self.num_count == 0:
  179.             return 0
  180.         return self.num_correct / self.num_count
  181.     def reset(self):
  182.         # 重置正确的数目和总数
  183.         self.num_correct = 0
  184.         self.num_count = 0
  185.     def name(self):
  186.         return "Accuracy"
  187. # 可视化
  188. def plot(runner, fig_name):
  189.     plt.figure(figsize=(10, 5))
  190.     plt.subplot(1, 2, 1)
  191.     train_items = runner.train_step_losses[::30]
  192.     train_steps = [x[0] for x in train_items]
  193.     train_losses = [x[1] for x in train_items]
  194.     plt.plot(train_steps, train_losses, color='#8E004D', label="Train loss")
  195.     if runner.dev_losses[0][0] != -1:
  196.         dev_steps = [x[0] for x in runner.dev_losses]
  197.         dev_losses = [x[1] for x in runner.dev_losses]
  198.         plt.plot(dev_steps, dev_losses, color='#E20079', linestyle='--', label="Dev loss")
  199.     # 绘制坐标轴和图例
  200.     plt.ylabel("loss", fontsize='x-large')
  201.     plt.xlabel("step", fontsize='x-large')
  202.     plt.legend(loc='upper right', fontsize='x-large')
  203.     plt.subplot(1, 2, 2)
  204.     # 绘制评价准确率变化曲线
  205.     if runner.dev_losses[0][0] != -1:
  206.         plt.plot(dev_steps, runner.dev_scores,
  207.                  color='#E20079', linestyle="--", label="Dev accuracy")
  208.     else:
  209.         plt.plot(list(range(len(runner.dev_scores))), runner.dev_scores,
  210.                  color='#E20079', linestyle="--", label="Dev accuracy")
  211.     # 绘制坐标轴和图例
  212.     plt.ylabel("score", fontsize='x-large')
  213.     plt.xlabel("step", fontsize='x-large')
  214.     plt.legend(loc='lower right', fontsize='x-large')
  215.     plt.savefig(fig_name)
  216.     plt.show()
复制代码
今天的分享就到这里,下次再见~
 


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

花瓣小跑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表