深度学习| 快速上手深度学习代码的阅读和改写

打印 上一主题 下一主题

主题 541|帖子 541|积分 1623

前言:本文主要是基于pytorch的解说,方便新手对深度学习代码布局有个概念。了解后,可以学会如何去阅读模子和修改模子,从而快速复现和修改现有的模子。

  
先容

深度学习的实现是依靠于神经网络的,所以我们讨论的深度学习代码也就是在讨论神经网络。
简单回顾一下神经网络训练过程,有利于我们更好的理解代码布局。
神经网络模子(model)其实就是一堆运算单元的组合,只不过神经网络是模仿了大脑神经元来组合这些运算单元;前向流传是输入(input)通过模子的一堆运算单元组合得到一个输出(output);损失其实就是计算输出(output)和标签(label)的差别得到值;反向流传是通过这个损失值更新模子里面运算单元的参数。
对于深度学习代码其实分为六个部门:模子 数据集 训练 猜测 评估 损失。


  • 模子(model):模子的布局、前向流传、保存和读取。
  • 数据集(dataset):数据集的读取、处理惩罚和保存。
  • 训练(train):训练模子,读取数据集后,设置patch、epoch,并输出每轮的损失、举行反向流传和保存模子。
  • 猜测(predict):模子猜测结果,读取数据集和模子后,前向流传得到output结果,并输出评估结果以及保存结果。
  • 评估(evaluation):对output和label做一些指标上评估计算。
  • 损失(Loss):对output和label做一些指标上损失计算,通常评估和损失的指标会比力通用。
1. 模子

以一个简单的分类模子为例子,只是说明代码布局,不思量具体利用。
  1. import torch.nn as nn
  2. # 模型通常都会继承nn.Module,复杂模型里面的子模块也是继承nn.Module
  3. class NeuralNetwork(nn.Module):
  4.         # num_class是分类的数量
  5.         # 定义了模型具有那些结构
  6.         def __init__(self,num_class):
  7.                 super().__init__()# 继承
  8.                 self.flatten=nn.Flatten()# 展平,因为要做全连接
  9.                 # 全连接层,做分类
  10.                 self.classifer=nn.Sequential(
  11.                         nn.Linear(28*28, 512),# 全连接
  12.             nn.ReLU(),# 激活
  13.             nn.Linear(512, num_class)# 分类
  14.                 )
  15.         # 前向传播
  16.         # 模型结构是如何排列
  17.         def forward(self,x):
  18.                 x = self.flatten(x)
  19.         x = self.classifer(x)
  20.         return x
复制代码
一样寻常来说模子都会比力复杂,就是好几个模块联合在一起,会有许多个如许继承nn.Module的模块,末了有一个总的(也继承了nn.Module)调用了所有的模块。
2. 数据集

网上公开的数据集,通常可以用Dataset直接获取,比方MNIST手写体数据集。
  1. from torch.utils.data import Dataset
  2. training_data = datasets.MNIST(
  3.     root="data",# 将数据保存在本地什么位置
  4.     train=True,# 是否训练数据
  5.     download=True,# 是否下载
  6.     transform=ToTensor(),# 将图像转为 tensor
  7. )
复制代码
不是很复杂的数据也可以用transformer举行数据预处理惩罚,在用dataset读取。
  1. from torchvision import transforms
  2. from torch.utils.data import Dataset
  3. data_transform = transforms.Compose([
  4.     transforms.Resize(256,256),# 图像数据大小调整
  5.     transforms.ToTensor(),# 将图像转为 tensor
  6.     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))# 归一化,前面是mean后面是std,执行(图像-mean)/ std
  7. ])
  8. data_dataset=datasets.ImageFolder(root='地址',transform=data_transform)
复制代码
对于自己定义的数据集,常常会必要重写Dataset,来更好管理数据。
这里以图像分割二分类数据为例子,读取input和label文件夹中的图像数据。
  1. from torch.utils.data import Dataset
  2. class MyDataSet(Dataset):
  3.         # 读取input和label文件夹下所有的数据
  4.     def __init__(self, _dir, label_dir):
  5.         self.input_dir =input_dir
  6.         self.label_dir = label_dir
  7.         # 获得指定目录下的所有文件
  8.         self.input_file = glob(self.input_dir + '/*')
  9.         self.label_file = glob(self.label_dir + '/*')
  10.     # 获取单个文件
  11.     def __getitem__(self, i):
  12.         img = cv2.imread(self.input_file[i], 0)
  13.         mask = cv2.imread(self.label_file[i], 0)
  14.         # 二分类,做个二值化,因为有时候放进去的图像不是二值化的
  15.         mask[np.where(mask >0)] = 1
  16.         mask[np.where(mask == 0)] = 0
  17.         return {'input': img, 'label': mask}
  18.     # 获取文件长度
  19.     def __len__(self):
  20.         return len(self.input_file)
复制代码
3. 损失

损失函数是在训练模子的时候利用的,前向流传结束后调用,计算用来参与反向流传调参。
如果是自带的损失函数可以直接调用,比方CE。
  1. criterion = nn.CrossEntropyLoss()
复制代码
如果是自定义的,必要重写。
可以发现重写的loss是继承nn.Module而且必要写前向流传,这是由于损失计算的值是要参与反向流传的。反向流传过程中,必要计算损失函数相对于每个参数的梯度。这要求损失函数在前向流传中提供了明确的计算路径,以便在反向流传中可以或许计算这些梯度。
下面给出CE和Dice的组合Loss的例子。
  1. # Dice loss
  2. class DiceCoeff(nn.Module):
  3.     def __init__(self):
  4.         super(DiceCoeff, self).__init__()
  5.     def forward(self, pred, target):
  6.         inter = torch.dot(pred, target) + 0.0001
  7.         union = torch.sum(pred ** 2) + torch.sum(target ** 2) + 0.0001
  8.         t = 2 * inter.float() / union.float()
  9.         return t
  10. class CombinedLoss(nn.Module):
  11.     def __init__(self):
  12.         super(CombinedLoss, self).__init__()
  13.         self.cross_entropy_loss = nn.CrossEntropyLoss()
  14.         self.dice_loss = DiceLoss()
  15.     def forward(self, pred, target):
  16.         target = target.type(torch.LongTensor).cuda()
  17.         pred_soft = F.softmax(pred,dim=1)# 归一化 维度为1进行归一化,one-hot编码变成预测标签
  18.         y1 = torch.mean(self.cross_entropy_loss.forward(pred, target))
  19.         y2 = torch.mean(self.dice_loss(pred_soft, target))
  20.         y=0.5*y1+0.5*y2
  21.         return y
复制代码
4. 评估

损失是用来让模子收敛的,评估是用来反应模子的效果,通常会在训练的时候验证集上评估效果,以及在测试上评估效果。
通常会有许多个评估的指标,比方DSC、HD等,说白就是一些函数输入output和label可以或许得到某些值。
损失和评估许多指标是雷同的,比方DSC和Dice的计算逻辑是雷同的,代码思路和损失函数中前向流传是基本一样的。只不过损失函数是会参与的优化的,必要继承nn.Module而且写前向流传,而评估只是函数。
损失指标许多的时候,会同一写在一个文件里面,方便管理。
5. 训练

训练,就是读取数据,然后规定好batch和epoch,举行前向流传,计算loss,然后反向流传。
  1. os.environ['CUDA_VISIBLE_DEVICES'] = '1'# 使用那个gpu进行训练
  2. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')# 没有gpu就用cpu训练
  3. # DataLoader加载数据Dataset
  4. batch_size = 64# 每次训练拿取多少个
  5. train_loader = DataLoader(training_dataset, batch_size=batch_size)
  6. model = NeuralNetwork().to(device)
  7. loss_fn = nn.CrossEntropyLoss()# 损失函数
  8. optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)# 就是采样梯度更新模型的可学习参数,使得损失减小。
  9. num_epochs=200# 所有数据训练多少轮
  10. for epoch in range(num_epochs):
  11.         model.train()
  12.         runing_loss=0.0
  13.         for i, (x, y) in enumerate(train_loader):
  14.                 # i表示第i个batch,x和y是输入和输出
  15.                 optimizer.zero_grad()# 梯度清理
  16.         x, y = x.to(device), y.to(device)# 转到数据gpu上
  17.         pred = model(x)# 前向传播预测
  18.         loss = loss_fn(pred, y)# 计算损失
  19.         loss.backward()# 反向传播
  20.         optimizer.step()# 更新参数
  21.         runing_loss+=loss
  22.         epoch_loss=running_loss/len(train_loader.dataset)# 计算该轮损失
  23.         print(f"epoch{epoch+1},train_loss{epoch_loss:.4f}")# 输出该轮的损失
  24.         if (epoch+1)%20==0:
  25.                 torch.save(model.state_dict(), f'seg_epoch{epoch + 1}_deeplab.pth')# 保存每轮模型
复制代码
9. 猜测

猜测就是读取数据,放入模子前向流传,只不过不必要计算梯度来反向流传罢了,一样寻常会带有评价指标。
  1. # 读取模型
  2. model=NeuralNetwork().to(device)
  3. model.load_state_dict(torch.load('模型路径'))
  4. # DataLoader加载数据Dataset
  5. batch_size = 64# 每次训练拿取多少个
  6. test_loader = DataLoader(testing_dataset, batch_size=batch_size)# 读取dataset数据
  7. size = len(dataloader.dataset)
  8. total_dice=0.0
  9. with torch.no_grad():# 不需要计算梯度,因为不需要反向传播
  10.         for x,y in test_loader:
  11.                 x, y = z.to(device), y.to(device)# 放到gpu设备中
  12.                 pred = model(X)# 前向传播预测
  13.                 dice=diceCoeff(pred,y)# dsc评估指标
  14.                 total_dice+=dice
  15. total_dice/=size
  16. print(f"DiceCoeff:{total_dice}")# 输出DSC值
复制代码
如何阅读和修改深度学习代码

阅读深度学习代码无非就是为了复现利用和改进模子,在知道深度学习代码组成后,其实上手思路就很明确了。
复现通常关注在于数据的输入和输出,可以从模子的dataset以及训练和猜测数据输入和输出入手。
必要修改模子布局的时候,可以先去搜模子相关的论文了解模子布局的思路,然后再从模子的前向流传开始看起,由于前向流传能反应布局的顺序,然后跟着反向流传调用布局顺序逐步看到别的的小布局。
要修改损失和评价那些也是一样的,去找到对应的位置即可。

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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

小秦哥

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

标签云

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