当前位置:   article > 正文

pytorch-使用AlexNet完成模型的训练,微调_alexnet预训练模型

alexnet预训练模型

1. AlexNet的网络结构

AlexNet

AlexNet总共包含8层变换,分别为5层卷积层、2个全连接层和一个全连接输出层

  1. 第一层卷积窗口的形状为11*11
  2. 第二层卷积窗口的形状缩减为5*5
  3. 第三层到第五层卷积窗口均为3*3
  4. 第六层全连接层输入为25655,输出为4096,激活函数采用了ReLu
  5. 第七层全连接层输入为4096,输出为4096,激活函数采用了ReLu
  6. 由于原模型是使用imagenet数据集进行训练的,所以输出为1000

2. 代码实现

2.1 导入相应的包
import time
import torch
from torch import nn, optim
import torchvision
import sys
# 如果有gpu计算设备,选择gpu计算设备,否则选择在cpu上训练
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
2.2 定义模型
class AlexNet(nn.Module):
    def __init__(self):
        super(AlexNet, self).__init__()
        
        # 卷积层
        self.conv = nn.Sequential(
            # 第一层卷积
			# 如果使用mnist类的数据集,输入通道为1,如果使用的cifar等彩色图像的数据集,输入通道为3
            nn.Conv2d(3, 96, 11, 4),
            nn.ReLU(),
            nn.MaxPool2d(3, 2),
            
            # 第二层卷积,开始减小卷积窗口
            nn.Conv2d(96, 256, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(3, 2),
            
            # 第三层卷积
            nn.Conv2d(256, 384, 3, 1, 1),
            nn.ReLU(),
            
            # 第四层卷积
            nn.Conv2d(384, 384, 3, 1, 1),
            nn.ReLU(),
            
            # 第五层卷积
            nn.Conv2d(384, 256, 3, 1, 1),
            nn.ReLU(),
            nn.MaxPool2d(3, 2)
        )
        
        self.fc = nn.Sequential(
            # 第一层全连接层
            nn.Linear(256*5*5, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            
            # 第二层全连接层
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            
            # 第三层全连接层(输出层)
            nn.Linear(4096, 10)
        )
    
    def forward(self, x):
        x = self.conv(x)
        x = self.fc(x.view(x.shape[0], -1))
        return x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
alexnet = AlexNet()
alexnet
  • 1
  • 2
2.3 加载数据
def load_data(batch_size, resize=None, root='./data/CIFAR10'):
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))
    trans.append(torchvision.transforms.ToTensor())
    
    transform = torchvision.transforms.Compose(trans)
	
	# torchvision.datasets包内还有许多其他的数据集,只需要修改网络的输入通道数和输出类别数即可
    data_train = torchvision.datasets.CIFAR10(root=root, train=True, download=True, transform=transform)
    data_test = torchvision.datasets.CIFAR10(root=root, train=False, download=True, transform=transform)
    
    if sys.platform.startswith('win'):
        num_workers = 0  # 0表示不用额外的进程来加速读取数据
    else:
        num_workers = 4
    
    train_iter = torch.utils.data.DataLoader(data_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(data_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

    return train_iter, test_iter
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
# batchsize可以根据电脑的实际情况进行修改
batchsize = 128
# 由于imagenet数据集图片的形状为224*224,所以需要将输入的图片resize为224*224
train_iter, test_iter = load_data(batch_size=batchsize, resize=224)
  • 1
  • 2
  • 3
  • 4
2.4 训练
# lr, num_epochos可以根据实际情况进行修改
lr, num_epochos = 0.001, 5
# 优化算法采用Adam算法,也可以选择torch.optim包下其他的优化算法
optimizer = torch.optim.Adam(alexnet.parameters(), lr=lr)

def train(train_iter, test_iter, net, optimizer, device, num_epochs):
    # 将网络部署在gpu设备上
    net = net.to(device)
    print("training on", device)
    # 交叉熵
    loss = torch.nn.CrossEntropyLoss()
    batch_count = 0
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        for X, y in train_iter:
             # 输入的属性
            X = X.to(device)
            # 标签
            y = y.to(device)
            # 预测
            y_hat = net(X)
            # 计算损失
            l = loss(y_hat, y)
            # 梯度下降
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        # 测试集的准确率
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch {}, loss {:.4f}, train acc {:.4f}, test acc{:.4f}, time {:.2f} sec'
              .format(epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))

# 评估模型在测试集的表现
def evaluate_accuracy(data_iter, net, device=None):
    if device is None and isinstance(net, torch.nn.Module):
        # 如果没指定device就使用net的device
        device = list(net.parameters())[0].device
    acc_sum, n = 0.0, 0
    with torch.no_grad():
        for X, y in data_iter:
            if isinstance(net, torch.nn.Module):
                # 评估模式, 关闭dropout
                net.eval()  
                acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item()
                # 改回训练模式
                net.train() 
            else:
                if ('is_training' in net.__code__.co_varnames):  # 如果有is_training这个参数
                    # 将is_training设置成False
                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
                else:
                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return acc_sum / n
	
train(train_iter=train_iter, test_iter=test_iter, net=alexnet, optimizer=optimizer, 
      device=device,num_epochs=num_epochos)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

3. 图像增广

3.1 导入需要的包
%matplotlib inline
import os
import time
import torch
from torch import nn, optim
import torch.nn.functional as F
from torch.utils.data import Dataset, DataLoader
import torchvision
from IPython import display
from matplotlib import pyplot as plt
from PIL import Image
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
import sys

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
device
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
3.2 读取图片进行测试
# 读取图片
display.set_matplotlib_formats('svg')
plt.rcParams['figure.figsize'] = (3.5, 2.5)
img = Image.open('./dog.jpg')
plt.imshow(img)
  • 1
  • 2
  • 3
  • 4
  • 5
3.3 常用的图像增广的方法
3.3.1 定义展示函数
def show_images(imgs, num_rows, num_cols, scale=2):
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    for i in range(num_rows):
        for j in range(num_cols):
            axes[i][j].imshow(imgs[i * num_cols + j])
            axes[i][j].axes.get_xaxis().set_visible(False)
            axes[i][j].axes.get_yaxis().set_visible(False)
    return axes

def apply(img, aug, num_rows=2, num_cols=4, scale=1.5):
    Y = [aug(img) for _ in range(num_rows * num_cols)]
    show_images(Y, num_rows, num_cols, scale)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
3.3.2 图像的翻转和裁剪
# 一半概率的图像左右翻转
apply(img, torchvision.transforms.RandomHorizontalFlip())
# 一半概率的图像上下翻转
apply(img, torchvision.transforms.RandomVerticalFlip())
# 随机选取图像中10%-50%的区域,区域的宽和高之比为0.5-2,将该区域缩放到200像素
shape_aug = torchvision.transforms.RandomResizedCrop(200, scale=(0.1, 0.5), ratio=(0.5, 2))
apply(img, shape_aug)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.3.3 颜色变化
# 将图像的亮度随机变化为源图像的50%
apply(img, torchvision.transforms.ColorJitter(brightness=0.5))
# 将图像的色调随机变化为源图像的50%
apply(img, torchvision.transforms.ColorJitter(hue=0.5))
# 将图像的对比度随机变化为源图像的50%
apply(img, torchvision.transforms.ColorJitter(contrast=0.5))
# 同时设置修改亮度,色调,对比度
# 将图像的亮度随机变化为源图像的50%
apply(img, torchvision.transforms.ColorJitter(
   brightness=0.5, hue=0.5, contrast=0.5))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3.3.4 叠加多种图像增广的方法
augs = torchvision.transforms.Compose([
    torchvision.transforms.RandomHorizontalFlip(), 
    torchvision.transforms.ColorJitter(
        brightness=0.5, hue=0.5, contrast=0.5),
    torchvision.transforms.RandomResizedCrop(200, scale=(0.1, 0.5), ratio=(0.5, 2))
])
apply(img, augs)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.3.5 训练

在读取数据函数load_data里面添加图像增广的方法

def load_data(batch_size, resize=None, root='./data/CIFAR10'):
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))
	# 根据实际情况添加图像增广的方法
    trans.append(torchvision.transforms.RandomHorizontalFlip())
    trans.append(torchvision.transforms.ToTensor())
    
    transform = torchvision.transforms.Compose(trans)

    data_train = torchvision.datasets.CIFAR10(root=root, train=True, download=True, transform=transform)
    data_test = torchvision.datasets.CIFAR10(root=root, train=False, download=True, transform=transform)
    
    if sys.platform.startswith('win'):
        num_workers = 0  # 0表示不用额外的进程来加速读取数据
    else:
        num_workers = 4
    
    train_iter = torch.utils.data.DataLoader(data_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(data_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

    return train_iter, test_iter
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

4. 模型的微调

4.1 简介

pytorch继承好了许多预训练的模型,这些的模型的参数已经十分优秀了,对于

4.2 加载数据集

使用热狗数据集进行测试点击下载

import os
data_dir = './data'
os.listdir(os.path.join(data_dir, 'hotdog'))
  • 1
  • 2
  • 3
4.3 定义和初始化模型
4.3.1 查看原模型最后一层的结构
alexnet.classifier
  • 1
4.3.2 修改原模型最后一层的结构,输出改为2

选择其他预训练的模型基本操作都是相同的,把最后一层的输出改成实际需要的输出

alexnet.classifier = nn.Sequential(
            # 第一层全连接层
            nn.Linear(9216, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            
            # 第二层全连接层
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(0.5),
            
            # 第三层全连接层(输出层)
            nn.Linear(4096, 10)
        )
alexnet.classifier
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
4.3.3 获取模型参数
output_params = list(map(id, alexnet.classifier.parameters()))
features_params = filter(lambda p: id(p) not in output_params, alexnet.parameters())

lr = 0.01
optimizer = optim.Adam([
    {'params': features_params},
    {'params': alexnet.classifier.parameters(), 'lr': lr * 100}
], lr=lr, weight_decay=0.001)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
4.3.4 设置图像增广的方法
from torchvision import transforms
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
train_augs = transforms.Compose([
    transforms.RandomResizedCrop(size=224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    normalize
])
test_augs = transforms.Compose([
    transforms.Resize(size=256),
    transforms.CenterCrop(size=224),
    transforms.ToTensor(),
    normalize
])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
4.3.5 训练
from torch.utils.data import Dataset, DataLoader
from torchvision.datasets import ImageFolder

def train_fine_tuning(net, optimizer, batch_size=5, num_epochs=5):
    train_iter = DataLoader(ImageFolder(os.path.join(data_dir, 'hotdog/train'), transform=train_augs),
                            batch_size, shuffle=True)
    test_iter = DataLoader(ImageFolder(os.path.join(data_dir, 'hotdog/test'), transform=test_augs),
                           batch_size)
    train(train_iter, test_iter, net, optimizer, device, num_epochs)
	
train_fine_tuning(alexnet, optimizer)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/123977
推荐阅读
相关标签
  

闽ICP备14008679号