pytorch实现猫狗识别

pytorch猫狗识别实现

不要轻视任何一个环节

写在前面:看官方文档很重要!

数据集

制作数据集

按照下图整理数据集文件结构,cat里面只放猫图、dog里面只放狗图

image-20220909175404481

读取数据集

这里也可以通过重写from torch.utils.data import Dataset里面的Dataset类,自定义__init____getitem____len__来加载,这里我直接用的 ImageFolder

trainData = ImageFolder(trainPath,transform=trainTransform)
valData = ImageFolder(valPath,transform=valTransform)

预处理(加载数据集)

torchvsion里提供了很多专门处理图像的方法,这里我们用transforms来整理

预览数据集,发现原数据集中的图片大小不一,通过resize统一大小(至于为什么选用(500,360)纯纯随手尝试,可以直接重置为正方形的),为了让训练效果更好,这里做了一下数据增强(包括随机翻转、调整图片饱和度和随机剪裁图片大小)

trainTransform = transforms.Compose([
                    transforms.Resize(size=(500, 360)),         # 重置图象大小
                    transforms.RandomHorizontalFlip(),          # 随机水平翻转
                    # transforms.RandomCrop(size=imgSize),             # 随机裁剪大小,输入网络中的图片为 (3, 224, 224)
                    transforms.ColorJitter(brightness=0.5, contrast=0.5, hue=0.5),  # 改变图片颜色饱和度(数据增强)
                    transforms.RandomResizedCrop(size=imgSize),
                    transforms.ToTensor(),
                    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
                    # transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
                ])
valTransform = transforms.Compose([
                    transforms.Resize([imgSize,imgSize]),
                    transforms.ToTensor(),
                    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
                ])

验证集我没有加上整理 -> 原因?

其实这里的加载分成了两部分:一个是获取数据集(ImageFolder)、一个是生成迭代器(DataLoader

trainData = ImageFolder(trainPath,transform=trainTransform)
valData = ImageFolder(valPath,transform=valTransform)
trainLoad = DataLoader(trainData,batch_size=batchSize,shuffle=True)
valLoad = DataLoader(valData,batch_size=batchSize)

构建自己的网络

推荐教程:https://mofanpy.com/tutorials/machine-learning/torch/CNN

CNN

流程如下:

  1. 卷积层(Conv2d
  2. 激励函数(ReLU
  3. 池化<向下采样>(pool

重复上述过程,最后将多维的张量展平(view)得到特征图,到全连接层(Linear)中进行分类得到输出

pytorch给我们提供了非常方便的接口,只需要继承torch.nn.Module这个类,重载__init__forward这两个魔术方法就能构造自己的网络模型

卷积中的图片大小要对应哇

class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(in_channels=3,      # 此时图片为 (3, 224, 224)
                out_channels=16,
                kernel_size=3,
                stride=1,             # 步长
                padding=1,            # padding=(kernel_size-1)/2从而保持图片大小不变(在步长为 1 时)
            ),                        # 输出图片 (16, 224, 224)
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2)     # 2x2下采样,输出图片 (16, 112, 112)
        )
        self.conv2 = torch.nn.Sequential(
            torch.nn.Conv2d(in_channels=16,
                            out_channels=32,
                            kernel_size=3,
                            stride=1,
                            padding=1,
            ),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2)
        )
        self.conv3 = torch.nn.Sequential(
            torch.nn.Conv2d(in_channels=32,
                            out_channels=64,
                            kernel_size=3,
                            stride=1,
                            padding=1,
            ),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(2)
        )
        self.dense = torch.nn.Sequential(
            torch.nn.Linear(64 * 28 * 28, 128),         # 第一个参数是图片的体积(长宽高),第二个参数是输出节点
            torch.nn.ReLU(),
            torch.nn.Linear(128, 2)
        )

    # 向前传播
    def forward(self, x):
        conv1_out = self.conv1(x)
        conv2_out = self.conv2(conv1_out)
        conv3_out = self.conv3(conv2_out)
        res = conv3_out.view(conv3_out.size(0), -1)     # 展平多维图像组,(batchSize: 64 * 28 * 28)
        out = self.dense(res)
        return out

开始训练

设置超参数

参数皆可自定义

imgSize =  224
batchSize = 64  
epochSize = 18
lr = 0.001 

生成模型、优化器、计算损失函数

model = Net()	# 创建模型
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
lossFunc = torch.nn.CrossEntropyLoss()      # 必须先把求损失的库函数定义到一个自己命名的函数再调用(需要初始化!)

训练

把每个epoch都输入网络,并格式化输出loss和accuracy(不要因为懒得搞就不写,它们的输出方便排错)

for epoch in range(epochSize):
    trainLoss = 0.0
    trainAcc = 0
    print("Epoch {}/{}".format((epoch+1), epochSize))
    print("-" * 10)
    for batch, data in enumerate(trainLoad, start=1): 
        X, y = data
        X, y = Variable(X),Variable(y)
        # X, y = X.to(device), y.to(device) 这个是把网络输入gpu上运算
        out = model(X)
        _, predicted1 = torch.max(out.data, 1)
        trainCorrect = (predicted1 == y).sum()
        trainAcc += trainCorrect.item()
        loss = lossFunc(out, y)
        optimizer.zero_grad()	# 梯度下降
        loss.backward()		# 损失函数反向传播
        optimizer.step()	# 优化器更新x
        trainLoss += loss.item()
        if batch%10 == 0:
            print('Train Loss: {:.6f}, it\'s Acc:{:.6f}'.format(trainLoss / len(trainData), trainAcc / len(trainData)))
            trainLoss = 0
            trainAcc = 0

加速训练(在gpu上跑)

分成两步:

  • 网络输入gpu

    model = Net()
    model.cuda() # 把model放进去
    
  • 数据输入gpu

    for batch, data in enumerate(trainLoad, start=1):
        X, y = data
        # X, y = Variable(X),Variable(y)
        X, y = X.to(device), y.to(device) # 把张量扔进gpu
    

报错处理

RuntimeError: stack expects each tensor to be equal size

我在读取数据集的时候统一设置了大小,但是在训练过程仍然出现了这个问题,可以看到获取的信息告诉我在卷积时两者大小不匹配了

got [3, 224, 299] at entry 0 and [3, 224, 240] at entry 1

考虑是数据增强的处理没做到位,将重置图片大小放在最后

RuntimeError: CUDA out of memory

gpu没地方了,调小batch、关掉其他应用、清理torch缓存

遇到loss不收敛

发现在epoch=1时就出现了loss在附近徘徊不收敛的问题

  • 优化器用Adam
  • 学习率不合适,可以从大往小调(可以采用lr /= 5 去迭代)
posted @ 2022-09-09 21:02  比萨在哭  阅读(479)  评论(0编辑  收藏  举报