报错如下
Traceback (most recent call last):
File "C:\Users\17122\Desktop\segnet\train.py", line 56, in <module>
train(epoch)
File "C:\Users\17122\Desktop\segnet\train.py", line 41, in train
for batch_idx, data in enumerate(data123, 0):
File "C:\Users\17122\anaconda3\lib\site-packages\torch\utils\data\dataset.py", line 53, in __getitem__
raise NotImplementedError
NotImplementedError
ERROR conda.cli.main_run:execute(47): `conda run python C:\Users\17122\Desktop\segnet\train.py` failed. (See above for error)
data123这个变量对应的类继承父类Dataset的时候没有实现相应的__getitem__接口
之前使用Keras
做字符验证码识别的时候,得到的经验就是针对这种比较简单的字符验证码,无需过于复杂的模型,几层CNN就够了。
import torch
from torch import nn
from torch import optim
import os
class NeuralNetWork(nn.Module):
def __init__(self, channel, num_classes):
"""
:param channel: 输入图片的channel
:param num_classes: 分类数量
"""
super(NeuralNetWork, self).__init__()
self.convin = nn.Sequential(
nn.Conv2d(channel, 64, kernel_size=(3, 3), padding=1, bias=False),
nn.ReLU(),
nn.Conv2d(64, 64, kernel_size=(3, 3), padding=1, bias=False),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Dropout(0.25)
)
self.convall = nn.Sequential(
nn.Conv2d(64, 64, kernel_size=(3, 3), padding=1, bias=False),
nn.ReLU(),
nn.Conv2d(64, 64, kernel_size=(3, 3), padding=1, bias=False),
nn.ReLU(),
nn.MaxPool2d(2, 2),
nn.Dropout(0.25)
)
# 承接卷积层和fc层
self.fc1 = nn.Sequential(
nn.Linear(64*5*11, 1024), # 这个输入值需要计算,根据输入图像的尺寸决定(本次输入图像尺寸为40*90)
nn.ReLU(),
nn.Dropout(0.5)
)
self.dense1 = nn.Sequential(
nn.Linear(1024, 512),
nn.ReLU(),
nn.Linear(512, num_classes),
# nn.LogSoftmax()
)
self.dense2 = nn.Sequential(
nn.Linear(1024, 512),
nn.ReLU(),
nn.Linear(512, num_classes),
# nn.LogSoftmax()
)
self.dense3 = nn.Sequential(
nn.Linear(1024, 512),
nn.ReLU(),
nn.Linear(512, num_classes),
# nn.LogSoftmax()
)
def forward(self, n_input):
# 进行卷积、激活和池化操作
feature = self.convin(n_input)
feature = self.convall(feature)
feature = self.convall(feature)
# 对特征层(Tensor类型)进行维度变换,变成两维
feature = feature.view(n_input.size(0), -1) # size(0)是批次大小
# 进行全连接操作
feature = self.fc1(feature)
out_put1 = self.dense1(feature)
out_put2 = self.dense2(feature)
out_put3 = self.dense3(feature)
# 每个样本有三个输出值
return [out_put1, out_put2, out_put3]
关于模型代码,有以下几点说明:
使用几层卷积、卷积核的数量、池化操作和dropout等并不是固定的,这要根据你的训练情况逐步调整;
全连接层的地方的输入值是需要计算的,是由输入到全连接层的输出通道数量x你的图片经过你的卷积和池化层后得到的尺寸,比如这里输出通道数量为64,原始输入图片尺寸为40x90
,经过padding=1的卷积层尺寸不变,经过三次(2, 2)的池化层,变为5x11
。
40x90 --> 20x45 --> 10x22 --> 5x11
并且在全连接层之前要把feature转换为(batch, )形状的二维tensor。
如何控制每个样本有3个输出值,这里是我遇到的难题,因为之前学习都是每个样本一个类型。
这里经过咨询有经验的同事得知,实际上就是利用相同的线性层计算得到三个值,同时返回。
不过需要注意的是,即使这三个输出值是经过了相同的线性层,就像这里的
nn.Sequential(
nn.Linear(1024, 512),
nn.ReLU(),
nn.Linear(512, num_classes),
)
但是一定是三个独立定义的层(层名称无所谓),如果均使用同一个层,那么输出的这三个值永远都是一样的(亲身踩坑)
解决方法:
该问题是由于GPU内存不足导致的,可以采取以下方法解决:
1.减小batch size
可以通过减小batch size来减小显存占用,具体方法是在训练代码中将batch size减小为一个较小的值,如16或32等。代码如下:
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16)
2.释放显存
可以通过PyTorch提供的torch.cuda.empty_cache()函数来释放显存,代码如下:
import torch
if hasattr(torch.cuda, 'empty_cache'):
torch.cuda.empty_cache()
3.降低模型复杂度
在不影响模型精度的情况下,可以考虑降低模型的复杂度,比如减少模型中的参数数量,或者通过使用预训练的模型来减少训练所需的时间和内存占用。
4.使用分布式训练
如果有多个GPU可以使用,可以考虑使用分布式训练来减小单个GPU内存的压力。具体方法是使用PyTorch提供的torch.nn.parallel.DistributedDataParallel来实现分布式训练,代码如下:
import torch
import torch.nn as nn
import torch.nn.parallel
import torch.backends.cudnn as cudnn
import torch.distributed as dist
import torch.optim
import torch.utils.data
import torch.utils.data.distributed
# 初始化进程组
dist.init_process_group(backend='nccl', world_size=4, init_method='...')
# 使用 DistributedSampler 作为数据加载器,保证每个进程都可以访问到不同的数据切片
train_sampler = torch.utils.data.distributed.DistributedSampler(train_dataset)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=16, sampler=train_sampler)
# 将模型复制到多个GPU上
model = nn.parallel.DistributedDataParallel(model)
以上是常见的解决方法,可以根据自己的实际情况进行选择和尝试。