天天看點

pytorch深入學習(五)

文章目錄

      • 圖像識别常用子產品
      • 圖像執行個體
      • 附錄
        • 1. ImageFolder類
        • 2. torchvision.models
        • 3. model.named_parameters()和model.parameters()
        • 4. torch.optim.lr_scheduler:調整學習率
        • 5. optimizer.step()和scheduler.step()

圖像識别常用子產品

  1. torchvision.datasets

  • 裡面是常用的資料集
  1. torchvision.models

  • 常用的卷積網絡架構, resnet等, 可以直接調用
  1. torchvision.transforms

  • 圖像增強的措施, 均值, resize啥的

下面介紹一下transforms子產品

transforms.Compose(
    [transforms.RandomCrop(45), # 随機翻轉-45到45°
     transforms.CenterCrop(224), # 從中心開始裁剪224*224大小的資料
     transforms.RandomHorizontalFlip(p=0.5), # 随機水準翻轉,選擇一個機率
     transforms.RandomVerticalFlip(p=0.5), # 随機垂直翻轉
     transforms.RandomGrayscale(p=0.025), # 機率轉換為灰階, 3通道變為R=G=B
     transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1), 
     # 參數1為亮度,參數2為對比度,參數3為飽和度,參數4為色相
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])  # 均值, 标準差, 這個一般是資料集算出的标準差和均值
     ]
)
           

圖像執行個體

  • 資料已放在網盤連結中:

    連結:https://pan.baidu.com/s/1czBQ3G7HSu5OtQIVEtEMEw 提取碼:1234

import os
import matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn
import torch.optim as optim
import torchvision
from torchvision import transforms, models, datasets
#https://pytorch.org/docs/stable/torchvision/index.html
import imageio
import time
import warnings
import random
import sys
import copy
import json
from PIL import Image


data_dir = './flower_data/' 
train_dir = data_dir + '/train'
valid_dir = data_dir + '/valid'

# 下面是用字典形式設定的變換, 比如data_transforms['train']代表對train的變換
data_transforms = {  
    'train': transforms.Compose([transforms.RandomRotation(45),#随機旋轉,-45到45度之間随機選
        transforms.CenterCrop(224),#從中心開始裁剪
        transforms.RandomHorizontalFlip(p=0.5),#随機水準翻轉 選擇一個機率機率
        transforms.RandomVerticalFlip(p=0.5),#随機垂直翻轉
        transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1),#參數1為亮度,參數2為對比度,參數3為飽和度,參數4為色相
        transforms.RandomGrayscale(p=0.025),#機率轉換成灰階率,3通道就是R=G=B
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#均值,标準差
    ]),
    'valid': transforms.Compose([transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

batch_size = 8

image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in ['train', 'valid']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True) for x in ['train', 'valid']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'valid']}
class_names = image_datasets['train'].classes


# 這是把變換後的圖檔變換回來
def im_convert(tensor):
    """ 展示資料"""

    image = tensor.to("cpu").clone().detach()
    image = image.numpy().squeeze()
    image = image.transpose(1, 2, 0) # 圖檔需要把通道放在像素的最後
    image = image * np.array((0.229, 0.224, 0.225)) + np.array((0.485, 0.456, 0.406))
    image = image.clip(0, 1)
    # numpy.clip(a, a_min, a_max, out=None)[source]
    # 其中a是一個數組,後面兩個參數分别表示最小和最大值,

    return image

# 下面是繪制資料
fig=plt.figure(figsize=(20, 12))
columns = 4
rows = 2

dataiter = iter(dataloaders['valid'])
inputs, classes = dataiter.next()

for idx in range (columns*rows):
    ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[])
    ax.set_title(cat_to_name[str(int(class_names[classes[idx]]))])
    plt.imshow(im_convert(inputs[idx]))
plt.show()



model_name = 'resnet'  #可選的比較多 ['resnet', 'alexnet', 'vgg', 'squeezenet', 'densenet', 'inception']

#是否用人家訓練好的特征來做
feature_extract = True
train_on_gpu = torch.cuda.is_available()

if not train_on_gpu:
    print('CUDA is not available.  Training on CPU ...')
else:
    print('CUDA is available!  Training on GPU ...')

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

def set_parameter_requires_grad(model, feature_extracting):
    if feature_extracting:
        for param in model.parameters():
            param.requires_grad = False # 這就是固定參數了

# 設定一個模型初始化函數, 這裡使用了torchvision.models子產品
def initialize_model(model_name, num_classes, feature_extract, use_pretrained=True):
    # 選擇合适的模型,不同模型的初始化方法稍微有點差別
    model_ft = None
    input_size = 0

    if model_name == "resnet":
        """ Resnet152
        """
        model_ft = models.resnet152(pretrained=use_pretrained) # 使用預訓練的模型
        set_parameter_requires_grad(model_ft, feature_extract) # 模型參數設定梯度為False, 
        # 也就無法梯度下降, 進而實作固定模型
        num_ftrs = model_ft.fc.in_features 
        # 進行模型層的替換, 把最後一層變為其他的線性層, 層的名字可以通過列印模型得到
        model_ft.fc = nn.Sequential(nn.Linear(num_ftrs, num_classes),
                                   nn.LogSoftmax(dim=1))
                                   
        input_size = 224

    return model_ft, input_size

model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True)

#GPU計算
model_ft = model_ft.to(device)

# for name, parameter in model_ft.named_parameters():
#     print(name, parameter.requires_grad)

# 模型儲存
filename='checkpoint.pth'  

# 是否訓練所有層
params_to_update = model_ft.parameters()

print("Params to learn:")
if feature_extract:
    params_to_update = []
    for name,param in model_ft.named_parameters():


        if param.requires_grad == True:  # 隻有最後添加的線性層需要訓練
            params_to_update.append(param) # 這是個清單
            print("\t",name)
            print(params_to_update)

else:
    for name,param in model_ft.named_parameters():
        if param.requires_grad == True:
            print("\t",name)


# 優化器設定
optimizer_ft = optim.Adam(params_to_update, lr=1e-2)
scheduler = optim.lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)#學習率每7個epoch衰減成原來的1/10
#最後一層已經LogSoftmax()了,是以不能nn.CrossEntropyLoss()來計算了,nn.CrossEntropyLoss()相當于logSoftmax()和nn.NLLLoss()整合
criterion = nn.NLLLoss()


           

附錄

1. ImageFolder類

pytorch深入學習(五)

使用此類的檔案夾比如如下命名, 我們是用1, 2等數字代替類别

2. torchvision.models

  • 這個子產品是為了友善的加載模型, 參數是是否使用預訓練的模型
import torchvision.models as models
resnet18 = models.resnet18(pretrained=True)
alexnet = models.alexnet(pretrained=True)
squeezenet = models.squeezenet1_0(pretrained=True)
vgg16 = models.vgg16(pretrained=True)
densenet = models.densenet161(pretrained=True)
inception = models.inception_v3(pretrained=True)
googlenet = models.googlenet(pretrained=True)
shufflenet = models.shufflenet_v2_x1_0(pretrained=True)
mobilenet_v2 = models.mobilenet_v2(pretrained=True)
mobilenet_v3_large = models.mobilenet_v3_large(pretrained=True)
mobilenet_v3_small = models.mobilenet_v3_small(pretrained=True)
resnext50_32x4d = models.resnext50_32x4d(pretrained=True)
wide_resnet50_2 = models.wide_resnet50_2(pretrained=True)
mnasnet = models.mnasnet1_0(pretrained=True)
           

3. model.named_parameters()和model.parameters()

  • 疊代列印model.named_parameters()将會列印每一次疊代元素的名字和param
model = DarkNet([1, 2, 8, 8, 4])
for name, param in model.named_parameters(): # 存在參數名和參數兩個值
    print(name,param.requires_grad)  
    param.requires_grad = False
           
  • 疊代列印model.parameters()将會列印每一次疊代元素的param而不會列印名字,這是它和named_parameters的差別,兩者都可以用來改變requires_grad的屬性。
for index, param in enumerate(model.parameters()):
    print(param.shape)
           

4. torch.optim.lr_scheduler:調整學習率

  • torch.optim.lr_scheduler子產品提供了一些根據epoch訓練次數來調整學習率(learning rate)的方法。一般情況下我們會設定随着epoch的增大而逐漸減國小習率進而達到更好的訓練效果。

學習率的調整應該放在optimizer更新之後

>>> scheduler = ...
>>> for epoch in range(100):
>>>     train(...)
>>>     validate(...)
>>>     scheduler.step()
           

注意這裡的scheduler.step()是在每個epoch之後的, 而不是在一個mini-batch中

5. optimizer.step()和scheduler.step()

optimizer.step()和scheduler.step()的差別

optimizer.step()

通常用在每個

mini-batch

之中,而

scheduler.step()

通常用在

epoch

裡面,但是不絕對,可以根據具體的需求來做。隻有用了

optimizer.step()

,模型才會更新,而

scheduler.step()

是對

lr

進行調整。通常我們有:

optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum = 0.9)
scheduler = lr_scheduler.StepLR(optimizer, step_size = 100, gamma = 0.1)
model = net.train(model, loss_function, optimizer, scheduler, num_epochs = 100)
           

在scheduler的step_size表示scheduler.step()每調用step_size次,對應的學習率就會按照政策調整一次。是以如果scheduler.step()是放在mini-batch裡面,那麼step_size指的是經過這麼多次疊代,學習率改變一次。

繼續閱讀