【图像分类】——来来来,干了这碗EfficientNet实战(Pytorch)

本文涉及的产品
简介: 【图像分类】——来来来,干了这碗EfficientNet实战(Pytorch)

目录


摘要


新建项目


导入所需要的库


设置全局参数


图像预处理


读取数据


设置模型


设置训练和验证


测试


完整代码:


摘要

EfficientNet是谷歌2019年提出的分类模型,自从提出以后这个模型,各大竞赛平台常常能看到他的身影,成了霸榜的神器。下图是EfficientNet—B0模型的网络结构。




从网络中可以看出,作者构建了MBConv,结构如下图:




k对应的卷积核的大小,经过1×1的卷积,然后channel放大4倍,再经过depthwise conv3×3的卷积,然后经过SE模块后,再经过1×1的卷积,把channel恢复到输入的大小,最后和上层的输入融合。


本文简单介绍一下EfficientNet的网络结构,主要实战为主,下面讲讲如何使用EfficientNet实现猫狗分类,由于本文使用的Loss函数是CrossEntropyLoss,所以只需更改类别的个数就可以实现多分类。


新建项目

新建一个图像分类的项目,data里面放数据集,dataset文件夹中自定义数据的读取方法,这次我不采用默认的读取方式,太简单没啥意思。然后再新建train.py和test.py




在项目的根目录新建train.py,然后在里面写训练代码。


导入所需要的库

首先检查有没有安装EfficientNet的库,如果没有安装则执行pip install efficientnet_pytorch安装EfficientNet库,安装后再导入。


import torch.optim as optim

import torch

import torch.nn as nn

import torch.nn.parallel

import torch.optim

import torch.utils.data

import torch.utils.data.distributed

import torchvision.transforms as transforms

from dataset.dataset import DogCat

from torch.autograd import Variable

from efficientnet_pytorch import EfficientNet

#pip install efficientnet_pytorch


设置全局参数

设置BatchSize、学习率和epochs,判断是否有cuda环境,如果没有设置为cpu。


# 设置全局参数

modellr = 1e-4

BATCH_SIZE = 64

EPOCHS = 20

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')



图像预处理


    在做图像与处理时,train数据集的transform和验证集的transform分开做,train的图像处理出了resize和归一化之外,还可以设置图像的增强,比如旋转、随机擦除等一系列的操作,验证集则不需要做图像增强,另外不要盲目的做增强,不合理的增强手段很可能会带来负作用,甚至出现Loss不收敛的情况。


# 数据预处理

transform = transforms.Compose([

   transforms.Resize((224, 224)),

   transforms.ToTensor(),

   transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

transform_test = transforms.Compose([

   transforms.Resize((224, 224)),

   transforms.ToTensor(),

   transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])



读取数据


数据集地址:链接:https://pan.baidu.com/s/1ZM8vDWEzgscJMnBrZfvQGw 提取码:48c3

将其下载后解压放到data文件夹中。数据的目录如下图:


20210605081359570.png

然后我们在dataset文件夹下面新建 __init__.py和dataset.py,在dataset.py文件夹写入下面的代码:


# coding:utf8

import os

from PIL import Image

from torch.utils import data

from torchvision import transforms as T

from sklearn.model_selection import train_test_split

class DogCat(data.Dataset):

   def __init__(self, root, transforms=None, train=True, test=False):

       """

       主要目标: 获取所有图片的地址,并根据训练,验证,测试划分数据

       """

       self.test = test

       self.transforms = transforms

       imgs = [os.path.join(root, img) for img in os.listdir(root)]

       if self.test:

           imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2].split('/')[-1]))

       else:

           imgs = sorted(imgs, key=lambda x: int(x.split('.')[-2]))

       if self.test:

           self.imgs = imgs

       else:

           trainval_files, val_files = train_test_split(imgs, test_size=0.3, random_state=42)

           if train:

               self.imgs = trainval_files

           else:

               self.imgs = val_files

   def __getitem__(self, index):

       """

       一次返回一张图片的数据

       """

       img_path = self.imgs[index]

       if self.test:

           label =-1

       else:

           label = 1 if 'dog' in img_path.split('/')[-1] else 0

       data = Image.open(img_path)

       data = self.transforms(data)

       return data, label

   def __len__(self):

       return len(self.imgs)

然后我们在train.py调用DogCat读取数据


dataset_train = DogCat('data/train', transforms=transform, train=True)

dataset_test = DogCat("data/train", transforms=transform_test, train=False)

# 读取数据

print(dataset_train.imgs)

# 导入数据

train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=False)


设置模型

使用CrossEntropyLoss作为loss,模型采用efficientnet-B3。更改最后一层的全连接,将类别设置为2,然后将模型放到DEVICE。优化器选用Adam。


# 实例化模型并且移动到GPU

criterion = nn.CrossEntropyLoss()

model_ft = EfficientNet.from_pretrained('efficientnet-b3')

num_ftrs = model_ft._fc.in_features

model_ft._fc = nn.Linear(num_ftrs, 2)

model_ft.to(DEVICE)

# 选择简单暴力的Adam优化器,学习率调低

optimizer = optim.Adam(model_ft.parameters(), lr=modellr)

def adjust_learning_rate(optimizer, epoch):

   """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""

   modellrnew = modellr * (0.1 ** (epoch // 50))

   print("lr:", modellrnew)

   for param_group in optimizer.param_groups:

       param_group['lr'] = modellrnew

设置训练和验证

# 定义训练过程

def train(model, device, train_loader, optimizer, epoch):

   model.train()

   sum_loss = 0

   total_num = len(train_loader.dataset)

   print(total_num, len(train_loader))

   for batch_idx, (data, target) in enumerate(train_loader):

       data, target = Variable(data).to(device), Variable(target).to(device)

       output = model(data)

       loss = criterion(output, target)

       optimizer.zero_grad()

       loss.backward()

       optimizer.step()

       print_loss = loss.data.item()

       sum_loss += print_loss

       if (batch_idx + 1) % 50 == 0:

           print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(

               epoch, (batch_idx + 1) * len(data), len(train_loader.dataset),

                      100. * (batch_idx + 1) / len(train_loader), loss.item()))

   ave_loss = sum_loss / len(train_loader)

   print('epoch:{},loss:{}'.format(epoch, ave_loss))

# 验证过程

def val(model, device, test_loader):

   model.eval()

   test_loss = 0

   correct = 0

   total_num = len(test_loader.dataset)

   print(total_num, len(test_loader))

   with torch.no_grad():

       for data, target in test_loader:

           data, target = Variable(data).to(device), Variable(target).to(device)

           output = model(data)

           loss = criterion(output, target)

           _, pred = torch.max(output.data, 1)

           correct += torch.sum(pred == target)

           print_loss = loss.data.item()

           test_loss += print_loss

       correct = correct.data.item()

       acc = correct / total_num

       avgloss = test_loss / len(test_loader)

       print('\nVal set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(

           avgloss, correct, len(test_loader.dataset), 100 * acc))

# 训练

for epoch in range(1, EPOCHS + 1):

   adjust_learning_rate(optimizer, epoch)

   train(model_ft, DEVICE, train_loader, optimizer, epoch)

   val(model_ft, DEVICE, test_loader)

torch.save(model_ft, 'model.pth')


完成上面的代码后就可以开始训练,点击run开始训练,如下图:

20210605090823874.png




由于我们使用了预训练模型,所以收敛速度很快。


测试

20210605094222832.png

我介绍两种常用的测试方式,第一种是通用的,通过自己手动加载数据集然后做预测,具体操作如下:


测试集存放的目录如下图:




第一步 定义类别,这个类别的顺序和训练时的类别顺序对应,一定不要改变顺序!!!!我们在训练时,cat类别是0,dog类别是1,所以我定义classes为(cat,dog)。


第二步 定义transforms,transforms和验证集的transforms一样即可,别做数据增强。


第三步 加载model,并将模型放在DEVICE里,


第四步 读取图片并预测图片的类别,在这里注意,读取图片用PIL库的Image。不要用cv2,transforms不支持。


import torch.utils.data.distributed

import torchvision.transforms as transforms

import torchvision.datasets as datasets

from PIL import Image

from torch.autograd import Variable

import os

classes = ('cat', 'dog')

transform_test = transforms.Compose([

        transforms.Resize((224, 224)),

       transforms.ToTensor(),

       transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model = torch.load("model.pth")

model.eval()

model.to(DEVICE)

path='data/test/'

testList=os.listdir(path)

for file in testList:

       img=Image.open(path+file)

       img=transform_test(img)

       img.unsqueeze_(0)

       img = Variable(img).to(DEVICE)

       out=model(img)

       # Predict

       _, pred = torch.max(out.data, 1)

       print('Image Name:{},predict:{}'.format(file,classes[pred.data.item()]))

运行结果:

20210605094244904.png




第二种使用我们刚才定义的dataset.py加载测试集。代码如下:


import torch.utils.data.distributed

import torchvision.transforms as transforms

from dataset.dataset import DogCat

from torch.autograd import Variable

classes = ('cat', 'dog')

transform_test = transforms.Compose([

   transforms.Resize((224, 224)),

   transforms.ToTensor(),

   transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model = torch.load("model.pth")

model.eval()

model.to(DEVICE)

dataset_test =DogCat('data/test/', transform_test,test=True)

print(len(dataset_test))

# 对应文件夹的label

for index in range(len(dataset_test)):

   item = dataset_test[index]

   img, label = item

   img.unsqueeze_(0)

   data = Variable(img).to(DEVICE)

   output = model(data)

   _, pred = torch.max(output.data, 1)

   print('Image Name:{},predict:{}'.format(dataset_test.imgs[index], classes[pred.data.item()]))

   index += 1

完整代码:

train.py


import torch.optim as optim

import torch

import torch.nn as nn

import torch.nn.parallel

import torch.optim

import torch.utils.data

import torch.utils.data.distributed

import torchvision.transforms as transforms

from dataset.dataset import DogCat

from torch.autograd import Variable

from efficientnet_pytorch import EfficientNet

#pip install efficientnet_pytorch

# 设置全局参数

modellr = 1e-4

BATCH_SIZE = 32

EPOCHS = 10

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# 数据预处理

transform = transforms.Compose([

   transforms.Resize((224, 224)),

   transforms.ToTensor(),

   transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

transform_test = transforms.Compose([

   transforms.Resize((224, 224)),

   transforms.ToTensor(),

   transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

dataset_train = DogCat('data/train', transforms=transform, train=True)

dataset_test = DogCat("data/train", transforms=transform_test, train=False)

# 读取数据

print(dataset_train.imgs)

# 导入数据

train_loader = torch.utils.data.DataLoader(dataset_train, batch_size=BATCH_SIZE, shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset_test, batch_size=BATCH_SIZE, shuffle=False)

# 实例化模型并且移动到GPU

criterion = nn.CrossEntropyLoss()

model_ft = EfficientNet.from_pretrained('efficientnet-b3')

num_ftrs = model_ft._fc.in_features

model_ft._fc = nn.Linear(num_ftrs, 2)

model_ft.to(DEVICE)

# 选择简单暴力的Adam优化器,学习率调低

optimizer = optim.Adam(model_ft.parameters(), lr=modellr)

def adjust_learning_rate(optimizer, epoch):

   """Sets the learning rate to the initial LR decayed by 10 every 30 epochs"""

   modellrnew = modellr * (0.1 ** (epoch // 50))

   print("lr:", modellrnew)

   for param_group in optimizer.param_groups:

       param_group['lr'] = modellrnew

# 定义训练过程

def train(model, device, train_loader, optimizer, epoch):

   model.train()

   sum_loss = 0

   total_num = len(train_loader.dataset)

   print(total_num, len(train_loader))

   for batch_idx, (data, target) in enumerate(train_loader):

       data, target = Variable(data).to(device), Variable(target).to(device)

       output = model(data)

       loss = criterion(output, target)

       optimizer.zero_grad()

       loss.backward()

       optimizer.step()

       print_loss = loss.data.item()

       sum_loss += print_loss

       if (batch_idx + 1) % 50 == 0:

           print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(

               epoch, (batch_idx + 1) * len(data), len(train_loader.dataset),

                      100. * (batch_idx + 1) / len(train_loader), loss.item()))

   ave_loss = sum_loss / len(train_loader)

   print('epoch:{},loss:{}'.format(epoch, ave_loss))

# 验证过程

def val(model, device, test_loader):

   model.eval()

   test_loss = 0

   correct = 0

   total_num = len(test_loader.dataset)

   print(total_num, len(test_loader))

   with torch.no_grad():

       for data, target in test_loader:

           data, target = Variable(data).to(device), Variable(target).to(device)

           output = model(data)

           loss = criterion(output, target)

           _, pred = torch.max(output.data, 1)

           correct += torch.sum(pred == target)

           print_loss = loss.data.item()

           test_loss += print_loss

       correct = correct.data.item()

       acc = correct / total_num

       avgloss = test_loss / len(test_loader)

       print('\nVal set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(

           avgloss, correct, len(test_loader.dataset), 100 * acc))

# 训练

for epoch in range(1, EPOCHS + 1):

   adjust_learning_rate(optimizer, epoch)

   train(model_ft, DEVICE, train_loader, optimizer, epoch)

   val(model_ft, DEVICE, test_loader)

torch.save(model_ft, 'model.pth')

test1.py


import torch.utils.data.distributed

import torchvision.transforms as transforms

import torchvision.datasets as datasets

from PIL import Image

from torch.autograd import Variable

import os

classes = ('cat', 'dog')

transform_test = transforms.Compose([

        transforms.Resize((224, 224)),

       transforms.ToTensor(),

       transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model = torch.load("model.pth")

model.eval()

model.to(DEVICE)

path='data/test/'

testList=os.listdir(path)

for file in testList:

       img=Image.open(path+file)

       img=transform_test(img)

       img.unsqueeze_(0)

       img = Variable(img).to(DEVICE)

       out=model(img)

       # Predict

       _, pred = torch.max(out.data, 1)

       print('Image Name:{},predict:{}'.format(file,classes[pred.data.item()]))

test2.py


import torch.utils.data.distributed

import torchvision.transforms as transforms

from dataset.dataset import DogCat

from torch.autograd import Variable

classes = ('cat', 'dog')

transform_test = transforms.Compose([

   transforms.Resize((224, 224)),

   transforms.ToTensor(),

   transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])

])

DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

model = torch.load("model.pth")

model.eval()

model.to(DEVICE)

dataset_test =DogCat('data/test/', transform_test,test=True)

print(len(dataset_test))

# 对应文件夹的label

for index in range(len(dataset_test)):

   item = dataset_test[index]

   img, label = item

   img.unsqueeze_(0)

   data = Variable(img).to(DEVICE)

   output = model(data)

   _, pred = torch.max(output.data, 1)

   print('Image Name:{},predict:{}'.format(dataset_test.imgs[index], classes[pred.data.item()]))

   index += 1


相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
2天前
|
机器学习/深度学习 算法 PyTorch
【从零开始学习深度学习】38. Pytorch实战案例:梯度下降、随机梯度下降、小批量随机梯度下降3种优化算法对比【含数据集与源码】
【从零开始学习深度学习】38. Pytorch实战案例:梯度下降、随机梯度下降、小批量随机梯度下降3种优化算法对比【含数据集与源码】
|
2天前
|
机器学习/深度学习 资源调度 PyTorch
【从零开始学习深度学习】15. Pytorch实战Kaggle比赛:房价预测案例【含数据集与源码】
【从零开始学习深度学习】15. Pytorch实战Kaggle比赛:房价预测案例【含数据集与源码】
|
2天前
|
机器学习/深度学习 自然语言处理 PyTorch
【从零开始学习深度学习】48.Pytorch_NLP实战案例:如何使用预训练的词向量模型求近义词和类比词
【从零开始学习深度学习】48.Pytorch_NLP实战案例:如何使用预训练的词向量模型求近义词和类比词
|
2天前
|
机器学习/深度学习 PyTorch 算法框架/工具
【从零开始学习深度学习】47. Pytorch图片样式迁移实战:将一张图片样式迁移至另一张图片,创作自己喜欢风格的图片【含完整源码】
【从零开始学习深度学习】47. Pytorch图片样式迁移实战:将一张图片样式迁移至另一张图片,创作自己喜欢风格的图片【含完整源码】
|
2天前
|
机器学习/深度学习 算法 PyTorch
【从零开始学习深度学习】45. Pytorch迁移学习微调方法实战:使用微调技术进行2分类图片热狗识别模型训练【含源码与数据集】
【从零开始学习深度学习】45. Pytorch迁移学习微调方法实战:使用微调技术进行2分类图片热狗识别模型训练【含源码与数据集】
|
24天前
|
机器学习/深度学习 自然语言处理 算法
【深度学习】与【PyTorch实战】
【深度学习】与【PyTorch实战】
|
1月前
|
机器学习/深度学习 自然语言处理 算法
PyTorch与NLP:自然语言处理的深度学习实战
随着人工智能技术的快速发展,自然语言处理(NLP)作为其中的重要分支,日益受到人们的关注。PyTorch作为一款强大的深度学习框架,为NLP研究者提供了强大的工具。本文将介绍如何使用PyTorch进行自然语言处理的深度学习实践,包括基础概念、模型搭建、数据处理和实际应用等方面。
|
1月前
|
机器学习/深度学习 PyTorch 测试技术
PyTorch实战:图像分类任务的实现与优化
【4月更文挑战第17天】本文介绍了使用PyTorch实现图像分类任务的步骤,包括数据集准备(如使用CIFAR-10数据集)、构建简单的CNN模型、训练与优化模型以及测试模型性能。在训练过程中,使用了交叉熵损失和SGD优化器。此外,文章还讨论了提升模型性能的策略,如调整模型结构、数据增强、正则化和利用预训练模型。通过本文,读者可掌握基础的PyTorch图像分类实践。
|
1月前
|
机器学习/深度学习 并行计算 PyTorch
【多GPU炼丹-绝对有用】PyTorch多GPU并行训练:深度解析与实战代码指南
本文介绍了PyTorch中利用多GPU进行深度学习的三种策略:数据并行、模型并行和两者结合。通过`DataParallel`实现数据拆分、模型不拆分,将数据批次在不同GPU上处理;数据不拆分、模型拆分则将模型组件分配到不同GPU,适用于复杂模型;数据和模型都拆分,适合大型模型,使用`DistributedDataParallel`结合`torch.distributed`进行分布式训练。代码示例展示了如何在实践中应用这些策略。
622 2
【多GPU炼丹-绝对有用】PyTorch多GPU并行训练:深度解析与实战代码指南
|
1月前
|
机器学习/深度学习 算法 PyTorch
【PyTorch实战演练】深入剖析MTCNN(多任务级联卷积神经网络)并使用30行代码实现人脸识别
【PyTorch实战演练】深入剖析MTCNN(多任务级联卷积神经网络)并使用30行代码实现人脸识别
203 2