resnet18实现猫狗分类


先简单说一下整体流程,利用pytorch训练模型并转化为onnx格式,然后配置好dlinfer,利用cv22infer在cv22平台量化序列化模型,展开推理

1训练模型

1.1处理数据集

参考图片下载地址:

https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data

1.1.1首先继承写一个继承自dataset的类

#继承了Dataset的类
class  DogCat(data.Dataset):
     def  __init__( self , root, transform = None , train = True , test = False ):
         '''root表示用于训练的图片地址,前70%用于训练,后30%用于测试
         '''
         self .test  =  test
         self .train  =  train
         self .transform  =  transform
         imgs  =  [os.path.join(root, img)  for  img  in  os.listdir(root)]   #imgs是一个list,list中放(编号,图片地址)
 
         if  self .test: #如果是test集
             imgs  =  sorted (imgs, key = lambda  x:  int (x.split( '.' )[ - 2 ].split( '/' )[ - 1 ]))
         else :         #如果是train集
             imgs  =  sorted (imgs, key = lambda  x:  int (x.split( '.' )[ - 2 ]))
         
         imgs_num  =  len (imgs)
 
         if  self .test:
             self .imgs  =  imgs
         else :
             random.shuffle(imgs)
             if  self .train:
                 self .imgs  =  imgs[: int ( 0.7  *  imgs_num)] #self.imgs表示前百分之七十的图片
             else :
                 self .imgs  =  imgs[ int ( 0.7  *  imgs_num):] #self.imgs表示除了前百分之七十的图片
 
 
     # 作为迭代器必须有的方法,可以用[]符号读取数据
     def  __getitem__( self , index):
         img_path  =  self .imgs[index]
         if  self .test:
             label  =  int ( self .imgs[index].split( '.' )[ - 2 ].split( '/' )[ - 1 ])
         else :
             label  =  1  if  'dog'  in  img_path.split( '/' )[ - 1 else  0   # 狗的label设为1,猫的设为0
         data  =  Image. open (img_path)
         data  =  self .transform(data)
         return  data, label
 
 
     def  __len__( self ):
         return  len ( self .imgs)

1.1.2把传入的图片集转换为Tensor并且改变格式

# 对数据集训练集的处理
transform_train = transforms.Compose([
     transforms.Resize(( 256 256 )),  # 先调整图片大小至256x256
     transforms.RandomCrop(( 224 224 )),  # 再随机裁剪到224x224
     transforms.RandomHorizontalFlip(),  # 随机的图像水平翻转,通俗讲就是图像的左右对调
     transforms.ToTensor(),              #图片转换为Tensor
     transforms.Normalize(( 0.485 0.456 0.406 ), ( 0.229 0.224 0.2225 ))  # 归一化,数值是用ImageNet给出的数值
])
 
 
# 对数据集验证集的处理
transform_val = transforms.Compose([
     transforms.Resize(( 224 224 )),                         
     transforms.ToTensor(),                     
     transforms.Normalize(( 0.485 0.456 0.406 ), ( 0.229 0.224 0.225 )),
])
 
device = torch.device( 'cuda'  if  torch.cuda.is_available()  else  'cpu' )  # 若能使用cuda,则使用cuda
 
trainset = DogCat( '/mnt/ssd0/zhangwentao/train' , transform=transform_train)#这是我图片的地址
valset = DogCat( '/mnt/ssd0/zhangwentao/train' , transform=transform_val)
 
 
#继承了dataset                            dataset     一捆有多大      数据顺序      多线程输入,= 0 表示单线程
trainloader = torch.utils.data.DataLoader(trainset, batch_size= 20 , shuffle=True, num_workers= 0 )
valloader = torch.utils.data.DataLoader(valset, batch_size= 20 , shuffle=False, num_workers= 0 )

1.2修改网络模型

class  Net(nn.Module):
     '''pytorch的resnet18接口的最后一层fc层的输出维度是1000。这明显不符合猫狗大战数据集,
     因为猫狗大战数据集是二分类的,所以最后一层fc输出的维度已经是2才对。
     因此我们需要对resnet18进行最后一层的修改。
     '''
     def  __init__( self , model):
         super (Net,  self ).__init__()
         # 取掉model的后1层
         self .resnet_layer  =  nn.Sequential( * list (model.children())[: - 1 ])
         self .Linear_layer  =  nn.Linear( 512 2 )   # 加上一层参数修改好的全连接层
 
     def  forward( self , x):
         =  self .resnet_layer(x)
         =  x.view(x.size( 0 ),  - 1 )
         =  self .Linear_layer(x)
         return  x
 
'''具体参考完整代码,这里仅展示如何使用网络模型
model = Net(resnet18(pretrained=True))
model = model.to(device)#转到gpu上运行
'''

1.3开始训练

''' for epoch in range(1):
         train(epoch)训练
         val(epoch)  验证,算损失函数
'''
 
#训练epoch次
def  train(epoch):
     scheduler.step()  #按照Pytorch的定义是用来更新优化器的学习率的
     model.train()     #训练模式
     train_acc  =  0.0 #准确率
     for  batch_idx, (img, label)  in  enumerate (trainloader):
         image  =  Variable(img.cuda()) #放到gpu上
         label  =  Variable(label.cuda()) #放到ppu上
 
         optimizer.zero_grad() #意思是把梯度置零,也就是把loss关于weight的导数变成0.
         out  =  model(image)    #投喂图片
         
         loss  =  criterion(out, label) #利用交叉熵损失函数算出out和label的差别
         
         loss.backward() #反向传播
         optimizer.step()
 
         train_acc  =  get_acc(out, label) #获得准确率
         print ( "Epoch:%d [%d|%d] loss:%f acc:%f"  %  (epoch, batch_idx,  len (trainloader), loss.mean(), train_acc))
 
#验证准确率
def  val(epoch):
     print ( "\nValidation Epoch: %d"  %  epoch)
     model. eval () #进入推理模式
     total  =  0
     correct  =  0
     with torch.no_grad():
         for  batch_idx, (img, label)  in  enumerate (valloader):
             image  =  Variable(img.cuda())
             label  =  Variable(label.cuda())
             out  =  model(image)
 
             _, predicted  =  torch. max (out.data,  1 )
 
             total  + =  image.size( 0 )
             correct  + =  predicted.data.eq(label.data).cpu(). sum ()
 
     print ( "Acc: %f "  %  (( 1.0  *  correct.numpy())  /  total))

 

 

1.4完整代码

from  torchvision.models.resnet  import  resnet18
import  os
import  random
from  PIL  import  Image
import  torch.utils.data as data
import  numpy as np
import  torchvision.transforms as transforms
import  torch
import  torch.nn as nn
import  torch.optim as optim
from  torch.autograd  import  Variable
from  torch.optim.lr_scheduler  import  *
 
#继承了Dataset的类
class  DogCat(data.Dataset):
     def  __init__( self , root, transform = None , train = True , test = False ):
         '''root表示用于训练的图片地址,前70%用于训练,后30%用于测试
         '''
         self .test  =  test
         self .train  =  train
         self .transform  =  transform
         imgs  =  [os.path.join(root, img)  for  img  in  os.listdir(root)]   #imgs是一个list,list中放(编号,图片地址)
 
         if  self .test: #如果是test集
             imgs  =  sorted (imgs, key = lambda  x:  int (x.split( '.' )[ - 2 ].split( '/' )[ - 1 ]))
         else :         #如果是train集
             imgs  =  sorted (imgs, key = lambda  x:  int (x.split( '.' )[ - 2 ]))
         
         imgs_num  =  len (imgs)
 
         if  self .test:
             self .imgs  =  imgs
         else :
             random.shuffle(imgs)
             if  self .train:
                 self .imgs  =  imgs[: int ( 0.7  *  imgs_num)] #self.imgs表示前百分之七十
             else :
                 self .imgs  =  imgs[ int ( 0.7  *  imgs_num):] #self.imgs表示除了前百分之七十
 
 
     # 作为迭代器必须有的方法,可以用[]符号读取数据
     def  __getitem__( self , index):
         img_path  =  self .imgs[index]
         if  self .test:
             label  =  int ( self .imgs[index].split( '.' )[ - 2 ].split( '/' )[ - 1 ])
         else :
             label  =  1  if  'dog'  in  img_path.split( '/' )[ - 1 else  0   # 狗的label设为1,猫的设为0
         data  =  Image. open (img_path)
         data  =  self .transform(data)
         return  data, label
 
 
     def  __len__( self ):
         return  len ( self .imgs)
 
 
 
 
 
 
 
# 对数据集训练集的处理
transform_train  =  transforms.Compose([
     transforms.Resize(( 256 256 )),   # 先调整图片大小至256x256
     transforms.RandomCrop(( 224 224 )),   # 再随机裁剪到224x224
     transforms.RandomHorizontalFlip(),   # 随机的图像水平翻转,通俗讲就是图像的左右对调
     transforms.ToTensor(),               #图片转换为Tensor
     transforms.Normalize(( 0.485 0.456 0.406 ), ( 0.229 0.224 0.2225 ))   # 归一化,数值是用ImageNet给出的数值
])
 
 
# 对数据集验证集的处理
transform_val  =  transforms.Compose([
     transforms.Resize(( 224 224 )),                         
     transforms.ToTensor(),                     
     transforms.Normalize(( 0.485 0.456 0.406 ), ( 0.229 0.224 0.225 )),
])
 
device  =  torch.device( 'cuda'  if  torch.cuda.is_available()  else  'cpu' )   # 若能使用cuda,则使用cuda
 
trainset  =  DogCat( '/mnt/ssd0/zhangwentao/train' , transform = transform_train)
valset  =  DogCat( '/mnt/ssd0/zhangwentao/train' , transform = transform_val)
 
 
#继承了dataset                            dataset     一捆有多大      数据顺序      多线程输入,=0表示单线程
trainloader  =  torch.utils.data.DataLoader(trainset, batch_size = 20 , shuffle = True , num_workers = 0 )
valloader  =  torch.utils.data.DataLoader(valset, batch_size = 20 , shuffle = False , num_workers = 0 )
 
 
#acc为准确率,该函数计算准确率
def  get_acc(output, label):
     total  =  output.shape[ 0 ]
     _, pred_label  =  output. max ( 1 )
     num_correct  =  (pred_label  = =  label). sum ().item()
     return  num_correct  /  total
 
 
#训练epoch次
def  train(epoch):
     scheduler.step()  #按照Pytorch的定义是用来更新优化器的学习率的
     model.train()     #训练模式
     train_acc  =  0.0 #准确率
     for  batch_idx, (img, label)  in  enumerate (trainloader):
         image  =  Variable(img.cuda()) #放到gpu上
         label  =  Variable(label.cuda()) #放到ppu上
 
         optimizer.zero_grad() #意思是把梯度置零,也就是把loss关于weight的导数变成0.
         out  =  model(image)    #投喂图片
         
         loss  =  criterion(out, label) #利用交叉熵损失函数算出out和label的差别
         
         loss.backward() #反向传播
         optimizer.step()
 
         train_acc  =  get_acc(out, label) #获得准确率
         print ( "Epoch:%d [%d|%d] loss:%f acc:%f"  %  (epoch, batch_idx,  len (trainloader), loss.mean(), train_acc))
 
#验证准确率
def  val(epoch):
     print ( "\nValidation Epoch: %d"  %  epoch)
     model. eval () #进入推理模式
     total  =  0
     correct  =  0
     with torch.no_grad():
         for  batch_idx, (img, label)  in  enumerate (valloader):
             image  =  Variable(img.cuda())
             label  =  Variable(label.cuda())
             out  =  model(image)
 
             _, predicted  =  torch. max (out.data,  1 )
 
             total  + =  image.size( 0 )
             correct  + =  predicted.data.eq(label.data).cpu(). sum ()
 
     print ( "Acc: %f "  %  (( 1.0  *  correct.numpy())  /  total))
 
 
class  Net(nn.Module):
     ''' 继承了nn.Module便于操作
     
         pytorch的resnet18接口的最后一层fc层的输出维度是1000。这明显不符合猫狗大战数据集,
     因为猫狗大战数据集是二分类的,所以最后一层fc输出的维度已经是2才对。
     因此我们需要对resnet18进行最后一层的修改。
     '''
     def  __init__( self , model):
         super (Net,  self ).__init__()
         # 取掉model的后1层
         self .resnet_layer  =  nn.Sequential( * list (model.children())[: - 1 ])
         self .Linear_layer  =  nn.Linear( 512 2 )   # 加上一层参数修改好的全连接层
 
     def  forward( self , x):
         =  self .resnet_layer(x)
         =  x.view(x.size( 0 ),  - 1 )
         =  self .Linear_layer(x)
         return  x
 
 
if  __name__  = =  '__main__' :
     model  =  Net(resnet18(pretrained = True ))
     model  =  model.to(device) #转到gpu上运行
 
                                 #model.parameters()为当前的网络模型的参数空间,lr为学习率,
     optimizer  =  torch.optim.SGD(model.parameters(), lr = 0.001 , momentum = 0.9 , weight_decay = 5e - 4 )   # 设置训练细节
     '''torch.optim是一个实现各种优化算法的包,优化器
 
     优化器就是需要根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值的作用
     '''
 
     scheduler  =  StepLR(optimizer, step_size = 3 )
     criterion  =  nn.CrossEntropyLoss() #交叉熵损失函数
 
     for  epoch  in  range ( 1 ):
         train(epoch)
         val(epoch)
 
     #torch.save(model, 'modelcatdog.pth')  # 保存模型
 
     model.to( "cpu" ) #转换回cpu
 
     model. eval () #注意,必须进入推理模式。详细看pytorch官方文档
 
     dummy_input  =  Variable(torch.randn( 1 3 224 224 ))  #设置入口的shape
 
     #把模型转换为onnx模型,
     torch.onnx.export(model,
                        dummy_input,
                        "ans.onnx" , input_names = [ "input" ],
                         opset_version = 9 )


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM