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