TRANSFER LEARNING FOR COMPUTER VISION TUTORIAL
 发布时间 
 阅读量: 
 阅读量 
在这个教程中,你将学习图像分类的迁移学习。你可以在[这里]看到更多关于迁移学习的知识。(https://cs231n.github.io/transfer-learning/)
在实践中,很少有人从头开始训练整个卷积网络(使用随机初始化),因为拥有足够大小的数据集的情况相对较少。相反,通常是在非常大的数据集上预先训练卷积网络(例如ImageNet,它包含120万张图像,包含1000个类别),然后将卷积网络用作感兴趣任务的初始化或固定特征提取器。
这两种主要的迁移学习场景如下:
    	一、优化卷积神经网络:我们使用预先训练的网络来初始化网络,而不是随机初始化,就像在imagenet 1000数据集上训练的网络一样。其余的训练看起来和往常一样。
    	二、ConvNet作为固定的特征提取器:在这里,我们将冻结除最终完全连接层之外的所有网络的权值。最后一个完全连通的层被一个新的具有随机权值的层替换,只有这一层被训练。
    
    
            from __future__ import print_function, division
    
    import torch
    import torch.nn as nn
    import torch.optim as optim
    from torch.optim import lr_scheduler
    import numpy as np
    import torchvision
    from torchvision import datasets, models, transforms
    import matplotlib.pyplot as plt
    import time
    import os
    import copy
    from torch.utils.data import DataLoader
    
    plt.ion()  # 使matplotlib的显示模式转换为交互(interactive)模式
    
    
    

        加载数据
我们将使用torchvision 和 torch.utils.packages 来加载数据。
我们今天要解决的问题是训练一个模型来分类蚂蚁和蜜蜂。我们有大约120张蚂蚁和蜜蜂的训练图像。每个类有75个验证图像。通常,如果从零开始训练,这是一个非常小的数据集。由于我们使用迁移学习,我们应该能够很好地进行归纳。
数据可以在这里下载
    data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    "val": transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
    }
    
    data_dir = 'D:\python_Project\pytorch\Image_and_Video\Transfer_Learning\data/hymenoptera_data'
    image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x])
                  for x in ['train', 'val']}
    # num_workers应该是多线程,我这里用不了,所以改为0,实际应该是4
    dataloaders = {x: DataLoader(image_datasets[x], batch_size=1, shuffle=True, num_workers=0)
               for x in ['train', 'val']}
    
    dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
    class_names = image_datasets['train'].classes
    
    device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
    
    
    

        可视化一些图片
    def imshow(inp, title=None):
    """展示 张量"""
    # img的格式是(C,H,W)  而plt.imshow()接受的格式是(H, W, C),所以需要转换维度
    inp = inp.numpy().transpose(1, 2, 0)
    mean = np.array([0.485, 0.456, 0.406])  # mean 是均值,std 是方差
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean  # 将像素转换成原来的值,与transforms.Normalize相对应
    inp = np.clip(inp, 0, 1)  # 截取函数,所有比 0 小的变为0,比1 大的变为1
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(20)
    plt.show()
    
    
    # 获取一批训练数据
    inputs, classes = next(iter(dataloaders['train']))
    
    # 给每个图像之间制造网格,以便间隔开
    out = torchvision.utils.make_grid(inputs)
    
    #
    imshow(out, title=[class_names[x] for x in classes])
    
    
    

        
训练模型
    ok,我们写一个通用函数来训练模型,我们将会阐明:
    		一、计划好学习率
    		二、保存最好的模型
    
    
            def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    since = time.time()
    # 保存权重,使用深拷贝
    best_model_wts = copy.deepcopy(model.state_dict())
    best_acc = 0.0
    
    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)
    
        # 每一个阶段都有一个训练集和验证集
        for phase in ['train', 'val']:   # val 是验证集的意思
            if phase == 'train':
                model.train()   # 设置模型为训练模式
            else:
                model.eval()   # 设置模型为评估模式
    
            running_loss = 0.0
            running_correct = 0
    
            # 迭代数据
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)
    
                # 将梯度置0
                optimizer.zero_grad()
    
                # 前向传播
                # 只在训练的时候追踪
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)  # 寻找每行的最大值, 返回每行的最大值  以及 最大值的索引
                    loss = criterion(outputs, labels)
    
                    # 只有训练的时候,才需要backward + optimize
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()
    
                # 统计
                running_loss += loss.item() * inputs.size(0)
                running_correct += torch.sum(preds == labels.data)
            if phase == 'train':
                scheduler.step()
    
            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_correct.double() / dataset_sizes[phase]
    
            print('{} Loss: {:.4f} Acc: {:.4f}'.format(
                phase, epoch_loss, epoch_acc))
    
            # 拷贝最好的模型
            if phase == 'val' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())
    
        print()
    
    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_acc))
    
    # 加载最好的模型权重
    model.load_state_dict(best_model_wts)
    return model
    
    
    

        可视化模型预测
    def visualize_model(model, num_images=6):
    was_training = model.training
    model.eval()
    images_so_far = 0
    fig = plt.figure()
    
    with torch.no_grad():
        for i, (inputs, labels) in enumerate(dataloaders['val']):
            inputs = inputs.to(device)
            labels = labels.to(device)
    
            outputs = model(inputs)
            _, preds = torch.max(outputs, 1)
    
            for j in range(inputs.size()[0]):
                images_so_far += 1
                ax = plt.subplot(num_images//2, 2, images_so_far)
                ax.axis('off')
                ax.set_title('predicted: {}'.format(class_names[preds[j]]))
                imshow(inputs.cpu().data[j])
    
                if images_so_far == num_images:
                    model.train(mode=was_training)
                    return
        model.train(mode=was_training)
    
    
    

        微调网络
加载一个预先训练的网络,重置最后的全连接层。
    model_ft = models.resnet18(pretrained=True)
    # 得到 fc 的输入特征的数量
    num_ftrs = model_ft.fc.in_features
    # 设置输出样本为2
    # 设置全连接层 nn.Linear(num_ftrs, len(class_names)).
    model_ft.fc = nn.Linear(num_ftrs, 2)
    
    model_ft = model_ft.to(device)
    
    criterion = nn.CrossEntropyLoss()
    
    # 让所有参数都得到训练
    optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)
    
    # 每7个epoch,lr衰减0.1倍
    exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
    
    
    
    
    

        训练以及评估
    model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler,
                       num_epochs=25)
    visualize_model(model_ft)
    
    
        ok,这里说我遇到的一个问题
在跑的过程当中,我的网络出现了这个问题
我蒙蔽了,debug了一下发现是loss.backward() 出了问题。百度了一下,发现可能是配置的问题,但是我懒得弄,怕把配置又弄乱了,等到了学校,用学校的机子可以一试hhh。
需要的看这个
卷积网络作为固定特征提取器
这里,我们需要冻结除最后一层外的所有网络。我们需要设置requires_grad == False来冻结参数,这样梯度就不会在backward()中计算。
    model_conv = torchvision.models.resnet18(pretrained=True)
    for param in model_conv.parameters():
    param.requires_grad = False
    
    # Parameters of newly constructed modules have requires_grad=True by default
    num_ftrs = model_conv.fc.in_features
    model_conv.fc = nn.Linear(num_ftrs, 2)
    
    model_conv = model_conv.to(device)
    
    criterion = nn.CrossEntropyLoss()
    
    # Observe that only parameters of final layer are being optimized as
    # opposed to before.
    optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)
    
    # Decay LR by a factor of 0.1 every 7 epochs
    exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)
    model_conv = train_model(model_conv, criterion, optimizer_conv,
                         exp_lr_scheduler, num_epochs=25)
    
    visualize_model(model_conv)
    
    plt.ioff()  # 最后关闭交互
    plt.show()
    
    
    

        
在考虑要不要把原来的cuda删了重下,这样搞了很久跑不出效果的感觉太难受了!
全部评论 (0)
 还没有任何评论哟~ 
