文章目錄
-
-
- 圖像識别常用子產品
- 圖像執行個體
- 附錄
-
- 1. ImageFolder類
- 2. torchvision.models
- 3. model.named_parameters()和model.parameters()
- 4. torch.optim.lr_scheduler:調整學習率
- 5. optimizer.step()和scheduler.step()
-
圖像識别常用子產品
-
torchvision.datasets
- 裡面是常用的資料集
-
torchvision.models
- 常用的卷積網絡架構, resnet等, 可以直接調用
-
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類
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI2EzX4xSZz91ZsAzNfRHLGZkRGZkRfJ3bs92YsAjMfVmepNHLth3VapHbYJmZKNjYtljVaJDbHJWQClGVF5UMR9Fd4VGdsATNfd3bkFGazxycykFaKdkYzZUbapXNXlleSdVY2pESa9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwM2NxADZ0IDOzE2Y3UWY5UjN1QjNhhTM5IjNjlTNiRzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
使用此類的檔案夾比如如下命名, 我們是用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指的是經過這麼多次疊代,學習率改變一次。