【深度学习】图像识别模型与训练策略

一、预处理与数据增强模块

transforms

transforms通常指torchvision.transforms

1.调整尺寸

transforms.Resize():将输入图像的尺寸调整为指定的大小

2.裁剪

transforms.CenterCrop():从图像中心进行裁剪,剪裁出指定大小的区域

transforms.RandomCrop():随机从图像中裁剪出指定大小的区域,常用于数据增强

3.翻转

transforms.RandomHorizontalFlip():以一定概率对图像进行水平翻转,这是一种简单有效的数据增强方式,能增加数据集的多样性

transforms.RandomVerticalFlip():以一定概率对图像进行垂直翻转

4.数据归一化

transforms.Normalize():对图像数据进行归一化处理,使其符合特定的均值和标准差分布,这有助于模型的训练收敛。

通常对于RGB图像,常见的归一化参数如下:

复制代码
normalize_transform = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

5.类型转换

transforms.ToTensor():将PIL图像或numpy.ndarray 转换为torch.Tensor,并将像素值从[0,255](如果是8位图像)归一化到[0.0,1.0]

6.组合变换

transforms.Compose():可以将多个变换操作组合在一起,按顺序依次对图像进行处理。比如先调整大小,再随机裁剪,然后转换为张量,最后归一化

复制代码
transform = transforms.Compose([
    transforms.Resize(256),
    transforms.RandomCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

二、模型选择

推导式

1.列表推导式

expression for item in iterable if condition\],其中exporession是对item进行处理后生成的结果,iterable是可迭代对象,if condition是可选的过滤条件 2.集合推导式 {expression for item in iterable if condition},与列表推导式类似,但使用花括号{},生成的是集合,集合中的元素是唯一的 3.字典推导式 {key_expression:value_expression for item in iterable if condition},用于创建字典,冒号:分隔键表达式和值表达式 ### ImageFolder ImageFolder要求数据集的目录结构遵循特定的格式,即每个类别对应一个子文件夹,子文件夹的名称即为该类别的标签,子文件夹中存放属于该类别的图像文件 ![](https://i-blog.csdnimg.cn/direct/7ee3ad6e2a4643bca9753badb5280182.png) 以下是一个使用ImageFolder加载图像数据集的基本示例: ```python import torchvision from torchvision import transforms # 定义图像预处理操作 transform = transforms.Compose([ transforms.Resize((224, 224)), # 调整图像大小为 224x224 transforms.ToTensor(), # 将图像转换为张量 transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 归一化处理 ]) # 使用 ImageFolder 加载数据集 dataset = torchvision.datasets.ImageFolder(root='data', transform=transform) # 打印数据集的类别信息 print(dataset.classes) # 类别名称列表 print(dataset.class_to_idx) # 类别名称到索引的映射 # 访问数据集中的样本 image, label = dataset[0] # 获取第一个样本及其标签 print(image.shape) # 打印图像张量的形状 print(label) # 打印标签的索引 ``` 参数说明: ImageFolder类的构造函数定义如下: ```python torchvision.datasets.ImageFolder(root, transform=None, target_transform=None, loader=, is_valid_file=None) ``` root:数据集的根目录,即包含各个类别子文件夹的目录路径 transform:对加载的图像进行预处理的操作,通常使用torchvision.transforms中的变换组合 **`target_transform`**:对图像对应的标签进行预处理的操作,可选参数。 **`loader`** :用于加载图像文件的函数,默认为 `default_loader`,它可以处理常见的图像文件格式(如 JPEG、PNG 等)。 **`is_valid_file`** :一个可选的函数,用于过滤哪些文件应该被加载,只有返回 `True` 的文件才会被包含在数据集中。 在实际训练模型时,通常会使用torch.utils.data.DataLoader来批量加载数据 ```python from torch.utils.data import DataLoader # 创建数据加载器 dataloader = DataLoader(dataset, batch_size=32, shuffle=True) # 遍历数据加载器 for images, labels in dataloader: print(images.shape) # 打印批量图像的形状 print(labels) # 打印批量标签 break # 仅打印一个批次的数据,可根据需要修改 ``` ## 三、迁移学习方法解读 迁移学习(Transfer Learning)是一种机器学习技术,旨在将从一个或多个源任务中学习到的知识应用到不同但相关的目标任务中,从而提升目标任务的学习效果和效率。 原理: 传统的机器学习和深度学习方法通常是针对特定任务进行数据收集、模型训练,要求每个任务都有大量的标注数据。但在实际应用中,很多任务可能缺乏足够的数据。迁移学习的核心思想是利用在大数据集上已经训练好的模型所学到的通用特征表示,将这些知识迁移到目标任务中,因为不同任务的数据往往在底层特征(如边缘、纹理等)上具有一定的相似性。 常见方法: ①基于特征的迁移学习 * **预训练模型特征提取**:使用在大规模数据集(如 ImageNet)上预训练好的深度神经网络(如 ResNet、VGG 等)作为特征提取器。具体做法是去掉模型的最后几层(通常是全连接层),保留前面的卷积层,将目标任务的数据输入到预训练模型中,提取中间层的特征,然后将这些特征输入到一个新的简单分类器(如全连接层)中进行训练。 * **示例代码(使用 PyTorch)**: * ```python import torch import torchvision.models as models import torch.nn as nn # 加载预训练的 ResNet18 模型 resnet = models.resnet18(pretrained=True) # 去掉最后一层全连接层 feature_extractor = nn.Sequential(*list(resnet.children())[:-1]) # 冻结特征提取器的参数 for param in feature_extractor.parameters(): param.requires_grad = False # 定义新的分类器 num_classes = 10 classifier = nn.Sequential( nn.Flatten(), nn.Linear(512, num_classes) ) # 组合特征提取器和分类器 model = nn.Sequential( feature_extractor, classifier ) ``` 注: 1)`*` 是 Python 中的解包运算符。它将列表中的元素解包,作为独立的参数传递给函数。在这里,`nn.Sequential` 函数期望接收多个子模块作为参数,而不是一个包含子模块的列表,所以使用 `*` 解包列表,将列表中的每个子模块作为独立的参数传递给 `nn.Sequential`。 `2)nn.Sequential` 是 PyTorch 中的一个容器类,它可以按顺序将多个子模块组合成一个新的模型。通过 `nn.Sequential`,将去掉最后一层的 `ResNet` 子模块按顺序组合成一个新的特征提取器 `feature_extractor`。 ②基于模型的迁移学习 * **微调(Fine - Tuning)**:在预训练模型的基础上,对模型的部分或全部参数进行微调。通常先使用预训练模型的参数初始化模型,然后在目标任务的数据集上继续训练。可以选择只微调模型的最后几层(如全连接层),也可以微调整个模型。微调时学习率通常设置得较小,以避免过度拟合。 * **示例代码(接上上面的代码)**: ```python import torch.optim as optim import torch.nn as nn # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 假设已经有了训练数据和标签 train_loader for epoch in range(10): running_loss = 0.0 for i, data in enumerate(train_loader, 0): inputs, labels = data optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() print(f'Epoch {epoch + 1}, Loss: {running_loss / len(train_loader)}') ``` ## 四、修改全连接层 ①加载预训练模型并修改全连接层 ```python # 加载预训练的 ResNet18 模型 model = models.resnet18(pretrained=True) # 获取原始全连接层的输入特征数量 num_ftrs = model.fc.in_features # 修改全连接层 # 假设目标任务有 10 个类别 num_classes = 10 model.fc = nn.Linear(num_ftrs, num_classes) ``` `model.fc` 表示 ResNet18 模型的全连接层。首先通过 `model.fc.in_features` 获取原始全连接层的 输入特征数量,然后使用 `nn.Linear` 重新定义全连接层,将输出维度设置为目标任务的类别数。 ②冻结部分层的参数 如果希望只训练新的全连接层,而保持预训练模型的其他层参数不变,可以冻结这些层的参数。 ```python # 冻结除全连接层外的所有参数 for param in model.parameters(): param.requires_grad = False for param in model.fc.parameters(): param.requires_grad = True ``` ## 项目实践 ### 1.导包 ```python import os import matplotlib.pyplot as plt %matplotlib inline import numpy as np import torch from torch import nn import torch.optim as optim import torchvision #pip install torchvision from torchvision import transforms, models, datasets #https://pytorch.org/docs/stable/torchvision/index.html import imageio import time import warnings warnings.filterwarnings("ignore") import random import sys import copy import json from PIL import Image ``` ### 2.制作数据源 data_transforms中指定了所有图像预处理操作 ImageFolder假设所有的文件按文件夹保存好,每个文件夹下面存储同一类别的图片,文件夹的名字为分类的名字 ```python data_transforms = { "train": transforms.Compose([ transforms.Resize([96,96]), transforms.RandomRotation(45), transforms.CenterCrop(64), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomVerticalFlip(p=0.5), transforms.ToTensor(), transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]) #均值,标准差 ]), "valid": transforms.Compose([ transforms.Resize([64,64]), transforms.ToTensor(), transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225]) ]) } ``` `transforms.Compose` 是一个用于组合多个数据预处理操作的函数,它会按照列表中的顺序依次对输入数据进行处理。 * `transforms.Resize([96,96])`:将输入图像的大小调整为 96x96 像素。这一步可以确保所有输入图像具有相同的尺寸,方便后续处理。 * `transforms.RandomRotation(45)`:随机将图像旋转 -45 到 45 度之间的任意角度。这是一种数据增强技术,可以增加训练数据的多样性,提高模型的泛化能力。 * `transforms.CenterCrop(64)`:从图像的中心裁剪出一个 64x64 像素的区域。这可以去除图像的边缘信息,聚焦于图像的中心部分。 * `transforms.RandomHorizontalFlip(p=0.5)`:以 0.5 的概率对图像进行水平翻转。这也是一种数据增强技术,可以增加训练数据的多样性。 * `transforms.RandomVerticalFlip(p=0.5)`:以 0.5 的概率对图像进行垂直翻转。同样是为了增加训练数据的多样性。 * `transforms.ToTensor()`:将图像数据转换为 PyTorch 的张量(Tensor)格式。同时,将图像的像素值从 \[0, 255\] 范围缩放到 \[0, 1\] 范围。 * `transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])`:对图像进行归一化处理。这里使用的均值和标准差是在 ImageNet 数据集上预计算得到的。归一化可以加速模型的训练过程,提高模型的稳定性。 ```python data_dir = "./flower_data/" train_dir = data_dir + "/train" valid_dir = data_dir + "/valid" ``` ```python batch_size = 128 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 ``` ```python # 读取标签对应的实际名字 with open("cat_to_name.json","r") as f: cat_to_name = json.load(f) ``` ### 3.加载models中提供的模型,并直接使用训练好的权重当做初始化参数 ```python model_name = "resnet" # 使用训练好的特征 feature_extract = True ``` 选择GPU/CPU ```python 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") ``` * `torch.device` 是 PyTorch 中用于表示计算设备的类,它可以指定张量和模型应该在哪个设备上进行计算。 * `"cuda:0"` 表示第一个可用的 CUDA 设备(即第一块 GPU),`"cpu"` 表示使用 CPU 进行计算。 ```python # 判断模型参数是否需要更新 def set_parameter_requires_grad(model,feature_extracting): if feature_extracting: for param in model.parameters(): param.requires_grad = False ``` ```python # 修改模型输出层 def initialize_model(model_name,num_classes,feature_extract,use_pretrained=True): model_ft = models.resnet18(pretrained=use_pretrained) set_parameter_requires_grad(model_ft,feature_extract) num_ftrs = model_ft.fc.in_features model_ft.fc = nn.Linear(num_ftrs,num_classes) input_size=64 return model_ft,input_size ``` ```python model_ft,input_size = initialize_model(model_name,102,feature_extract,use_pretrained=True) # 设置CPU还是GPU用于计算 model_ft = model_ft.to(device) # 模型保存 filename = "best.pt" # 是否训练所有层 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) else: for name,param in model_ft.named_parameters(): if param.requires_grad == True: print("\t",name) ``` ![](https://i-blog.csdnimg.cn/direct/11c7f855741444879e9c1786f0959295.png) * `feature_extract` 是一个布尔值,用于决定是否只对模型的部分层进行微调。 * `use_pretrained=True` 表示使用预训练的模型权重。当 `use_pretrained` 为 `True` 时,代码会尝试从互联网上下载预训练的模型权重文件。 ### 4.优化器设置 ```python # 优化器设置 optimizer_ft = optim.Adam(params_to_update,lr=1e-2) scheduler = optim.lr_scheduler.StepLR(optimizer_ft,step_size=10,gamma=0.01) criterion = nn.CrossEntropyLoss() ``` 第二行代码定义了一个学习率调度器,用于在训练过程中动态调整学习率。 * **详细解释**: * `optim.lr_scheduler.StepLR` 是 PyTorch 中实现步长学习率调度策略的类。该策略会在每经过一定的训练轮数(`step_size`)后,将学习率乘以一个固定的衰减因子(`gamma`)。 * `optimizer_ft` 是前面定义的 Adam 优化器,学习率调度器会根据这个优化器来调整学习率。 * `step_size=10` 表示每经过 10 个训练轮次,学习率会进行一次调整。 * `gamma=0.01` 是学习率的衰减因子,每次调整时,学习率会乘以 0.01。例如,如果初始学习率为 0.01,经过 10 个轮次后,学习率会变为 0.01 \* 0.01 = 0.0001。 ### 5.定义训练模块 ```python def train_model(model,dataloaders,criterion,optimizer,num_epochs=25,filename="best.pt"): since = time.time() best_acc = 0 model.to(device) val_acc_history = [] train_acc_history = [] train_losses = [] valid_losses = [] # 学习率 LRs = [optimizer.param_groups[0]["lr"]] best_model_wts = copy.deepcopy(model.state_dict()) for epoch in range(num_epochs): print("Epoch {}/{}".format(epoch,num_epochs-1)) print("-"*10) # 训练和验证 for phase in ["train","valid"]: if phase == "train": model.train() else: model.eval() running_loss = 0.0 running_corrects = 0 for inputs,labels in dataloaders[phase]: inputs = inputs.to(device) labels = labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs,labels) _,preds = torch.max(outputs,1) # 训练阶段更新权重 if phase == "train": loss.backward() optimizer.step() running_loss += loss.item()*inputs.size(0) running_corrects += torch.sum(preds==labels.data) epoch_loss = running_loss / len(dataloaders[phase].dataset) epoch_acc = running_corrects.double()/len(dataloaders[phase].dataset) time_elapsed = time.time() - since print("Time elapsed {:.0f}m {:.0f}s".format(time_elapsed//60,time_elapsed%60)) print("{} Loss:{:.4f} Acc:{:.4f}".format(phase,epoch_loss,epoch_acc)) # 得到最好的那次模型 if phase == "valid" and epoch_acc > best_acc: best_acc = epoch_acc best_model_wts = copy.deepcopy(model.state_dict()) state = { "state_dict":model.state_dict(), "best_acc":best_acc, "optimizer":optimizer.state_dict() } torch.save(state,filename) if phase=="valid": val_acc_history.append(epoch_acc) valid_losses.append(epoch_loss) if phase=="train": train_acc_history.append(epoch_acc) train_losses.append(epoch_loss) print("Optimizer learning rate: {:.7f}".format(optimizer.param_groups[0]["lr"])) LRs.append(optimizer.param_groups[0]["lr"]) print() scheduler.step() #学习率衰减 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,val_acc_history,train_acc_history,valid_losses,train_losses,LRs ``` `datasets.ImageFolder` 的作用 `torchvision.datasets.ImageFolder` 是 PyTorch 提供的一个方便的数据集类,用于从文件夹结构中加载图像数据。它的工作方式如下: * **文件夹结构要求**:数据目录下的每个子文件夹代表一个类别,子文件夹中的所有图像都属于该类别。例如,数据目录结构可能如下: ![](https://i-blog.csdnimg.cn/direct/bbaf5dcdd61c469d82499061c8dfe0ed.png) * **标签生成** :`ImageFolder` 会根据子文件夹的名称自动为每个图像分配一个类别标签,标签是从 0 开始的整数索引。例如,`class1` 的标签为 0,`class2` 的标签为 1,依此类推。 综上所述,虽然代码中没有显式地指定标签,但 `datasets.ImageFolder` 会根据文件夹结构自动为图像分配标签,`torch.utils.data.DataLoader` 会将数据和标签一起批量返回。 ### 6.开始训练模型 ```python model_ft,val_acc_history,train_acc_history,valid_losses,train_losses,LRs = train_model(model_ft,dataloaders,criterion,optimizer_ft,num_epochs=20) ``` 训练结果如下: ```python Epoch 0/19 ---------- Time elapsed 0m 57s train Loss:2.3975 Acc:0.4710 Time elapsed 1m 3s valid Loss:3.4273 Acc:0.3215 Optimizer learning rate: 0.0100000 Epoch 1/19 ---------- Time elapsed 1m 59s train Loss:2.2989 Acc:0.4902 Time elapsed 2m 6s valid Loss:3.4469 Acc:0.3509 Optimizer learning rate: 0.0100000 Epoch 2/19 ---------- Time elapsed 2m 60s train Loss:2.2143 Acc:0.5176 Time elapsed 3m 7s valid Loss:3.9898 Acc:0.3227 Optimizer learning rate: 0.0100000 Epoch 3/19 ---------- Time elapsed 4m 3s train Loss:2.2419 Acc:0.5266 Time elapsed 4m 9s valid Loss:4.2700 Acc:0.2983 Optimizer learning rate: 0.0100000 Epoch 4/19 ---------- Time elapsed 5m 3s train Loss:2.2196 Acc:0.5325 Time elapsed 5m 9s valid Loss:4.2233 Acc:0.3142 Optimizer learning rate: 0.0100000 Epoch 5/19 ---------- Time elapsed 6m 2s train Loss:2.1590 Acc:0.5453 Time elapsed 6m 9s valid Loss:4.2198 Acc:0.3313 Optimizer learning rate: 0.0100000 Epoch 6/19 ---------- Time elapsed 7m 3s train Loss:2.3123 Acc:0.5432 Time elapsed 7m 10s valid Loss:4.1263 Acc:0.3325 Optimizer learning rate: 0.0100000 Epoch 7/19 ---------- Time elapsed 8m 5s train Loss:2.1808 Acc:0.5476 Time elapsed 8m 11s valid Loss:4.1894 Acc:0.3276 Optimizer learning rate: 0.0100000 Epoch 8/19 ---------- Time elapsed 9m 7s train Loss:2.2034 Acc:0.5553 Time elapsed 9m 14s valid Loss:4.1895 Acc:0.3496 Optimizer learning rate: 0.0100000 Epoch 9/19 ---------- Time elapsed 10m 9s train Loss:2.2178 Acc:0.5592 Time elapsed 10m 15s valid Loss:4.4034 Acc:0.3166 Optimizer learning rate: 0.0100000 Epoch 10/19 ---------- Time elapsed 11m 9s train Loss:2.0092 Acc:0.5920 Time elapsed 11m 15s valid Loss:4.1064 Acc:0.3399 Optimizer learning rate: 0.0001000 Epoch 11/19 ---------- Time elapsed 12m 8s train Loss:1.8037 Acc:0.6145 Time elapsed 12m 15s valid Loss:3.9882 Acc:0.3594 Optimizer learning rate: 0.0001000 Epoch 12/19 ---------- Time elapsed 13m 8s train Loss:1.7063 Acc:0.6209 Time elapsed 13m 14s valid Loss:3.8820 Acc:0.3619 Optimizer learning rate: 0.0001000 Epoch 13/19 ---------- Time elapsed 14m 8s train Loss:1.6577 Acc:0.6302 Time elapsed 14m 14s valid Loss:3.8769 Acc:0.3631 Optimizer learning rate: 0.0001000 Epoch 14/19 ---------- Time elapsed 15m 11s train Loss:1.6208 Acc:0.6378 Time elapsed 15m 18s valid Loss:3.9119 Acc:0.3545 Optimizer learning rate: 0.0001000 Epoch 15/19 ---------- Time elapsed 16m 12s train Loss:1.5684 Acc:0.6467 Time elapsed 16m 18s valid Loss:3.8372 Acc:0.3545 Optimizer learning rate: 0.0001000 Epoch 16/19 ---------- Time elapsed 17m 11s train Loss:1.6428 Acc:0.6346 Time elapsed 17m 18s valid Loss:3.9027 Acc:0.3716 Optimizer learning rate: 0.0001000 Epoch 17/19 ---------- Time elapsed 18m 11s train Loss:1.5844 Acc:0.6427 Time elapsed 18m 17s valid Loss:3.8490 Acc:0.3729 Optimizer learning rate: 0.0001000 Epoch 18/19 ---------- Time elapsed 19m 11s train Loss:1.5653 Acc:0.6505 Time elapsed 19m 17s valid Loss:3.7844 Acc:0.3692 Optimizer learning rate: 0.0001000 Epoch 19/19 ---------- Time elapsed 20m 13s train Loss:1.5774 Acc:0.6404 Time elapsed 20m 19s valid Loss:3.7797 Acc:0.3692 Optimizer learning rate: 0.0001000 Training complete in 20m 19s Best val Acc: 0.372861 ``` ### 7.再训练所有层 ```python for param in model_ft.parameters(): param.requires_grad = True # 再继续训练所有的参数,学习率最好调小一点 optimizer = optim.Adam(model_ft.parameters(),lr=1e-3) scheduler = optim.lr_scheduler.StepLR(optimizer_ft,step_size=7,gamma=0.1) # 损失函数 criterion = nn.CrossEntropyLoss() ``` ```python # 加载之前训练好的权重参数 checkpoint = torch.load(filename) best_acc = checkpoint['best_acc'] model_ft.load_state_dict(checkpoint['state_dict']) ``` ```python model_ft, val_acc_history, train_acc_history, valid_losses, train_losses, LRs = train_model(model_ft, dataloaders, criterion, optimizer, num_epochs=10) ``` 训练数据如下: ```python Epoch 0/9 ---------- Time elapsed 1m 52s train Loss:2.6279 Acc:0.4615 Time elapsed 1m 59s valid Loss:3.2914 Acc:0.4034 Optimizer learning rate: 0.0010000 Epoch 1/9 ---------- Time elapsed 3m 51s train Loss:1.1428 Acc:0.6879 Time elapsed 3m 58s valid Loss:1.8249 Acc:0.5342 Optimizer learning rate: 0.0010000 Epoch 2/9 ---------- Time elapsed 5m 47s train Loss:0.9859 Acc:0.7213 Time elapsed 5m 53s valid Loss:1.6096 Acc:0.5758 Optimizer learning rate: 0.0010000 Epoch 3/9 ---------- Time elapsed 7m 42s train Loss:0.7332 Acc:0.7915 Time elapsed 7m 48s valid Loss:1.7855 Acc:0.5758 Optimizer learning rate: 0.0010000 Epoch 4/9 ---------- Time elapsed 9m 39s train Loss:0.7189 Acc:0.7999 Time elapsed 9m 45s valid Loss:1.5474 Acc:0.6015 Optimizer learning rate: 0.0010000 Epoch 5/9 ---------- Time elapsed 11m 36s train Loss:0.5291 Acc:0.8371 Time elapsed 11m 42s valid Loss:1.5273 Acc:0.6540 Optimizer learning rate: 0.0010000 Epoch 6/9 ---------- Time elapsed 13m 34s train Loss:0.5759 Acc:0.8385 Time elapsed 13m 40s valid Loss:1.6264 Acc:0.5978 Optimizer learning rate: 0.0010000 Epoch 7/9 ---------- Time elapsed 15m 32s train Loss:0.4117 Acc:0.8716 Time elapsed 15m 38s valid Loss:1.6492 Acc:0.6100 Optimizer learning rate: 0.0010000 Epoch 8/9 ---------- Time elapsed 17m 29s train Loss:0.3964 Acc:0.8770 Time elapsed 17m 36s valid Loss:1.5607 Acc:0.6540 Optimizer learning rate: 0.0010000 Epoch 9/9 ---------- Time elapsed 19m 24s train Loss:0.4744 Acc:0.8651 Time elapsed 19m 31s valid Loss:2.1448 Acc:0.6015 Optimizer learning rate: 0.0010000 Training complete in 19m 31s Best val Acc: 0.654034 ``` ### 8.加载训练好的模型并进行测试 ①加载训练好的模型 ```python model_ft,input_size = initialize_model(model_name,102,feature_extract,use_pretrained=True) model_ft=model_ft.to(device) # 保存文件的名字 filename="best.pt" # 加载模型 checkpoint = torch.load(filename) best_acc=checkpoint["best_acc"] model_ft.load_state_dict(checkpoint["state_dict"]) ``` ②获取一个测试数据 ```python dataiter = iter(dataloaders["valid"]) images,labels = dataiter.next() model_ft.eval() if train_on_gpu: output = model_ft(images.cuda()) else: output = model_ft(images) ``` ③得到概率最大的标签 ```python _,preds_tensor = torch.max(output,1) preds = np.squeeze(preds_tensor.numpy()) if not train_on_gpu else np.squeeze(preds_tensor.cpu().numpy()) ``` ④展示数据 ```python 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) return image ``` a) * `tensor.to("cpu")`:将输入的张量 `tensor` 从当前设备(可能是 GPU)移动到 CPU 上。因为后续操作如转换为 NumPy 数组等在 CPU 上进行更为方便。 * `clone()`:创建张量的一个副本。这是为了避免在后续操作中修改原始张量,确保数据的独立性。 * `detach()`:将张量从计算图中分离出来,切断其与计算图的连接。这样做可以防止在后续操作中对原始张量进行反向传播,节省计算资源。 b) * `image.numpy()`:将 PyTorch 张量转换为 NumPy 数组,以便使用 NumPy 的相关操作。 * `squeeze()`:去除数组中维度大小为 1 的维度。例如,如果输入的张量形状是 `(1, 3, 224, 224)`,经过 `squeeze()` 后,会将形状变为 `(3, 224, 224)`,这样更符合图像数据的常见表示形式。 c) * `transpose(1, 2, 0)`:对数组的维度进行转置操作。在 PyTorch 中,图像张量的形状通常是 `(channels, height, width)`,而在 NumPy 中,图像数组的常见表示形式是 `(height, width, channels)`。因此,通过 `transpose(1, 2, 0)` 可以将维度顺序从 `(channels, height, width)` 转换为 `(height, width, channels)`。 d) * 这一步是对图像数据进行反归一化操作。在图像预处理过程中,通常会对图像进行归一化,即减去均值并除以标准差。这里进行反归一化,将图像数据还原到原始的像素值范围。 * `np.array((0.229, 0.224, 0.225))` 是预处理时使用的标准差,`np.array((0.485, 0.456, 0.406))` 是预处理时使用的均值。通过乘以标准差并加上均值,实现反归一化。 e) * `clip(0, 1)`:将数组中的所有元素限制在 0 到 1 的范围内。由于反归一化过程可能会导致某些像素值超出 0 到 1 的范围,使用 `clip` 函数可以确保所有像素值都在有效的范围内,避免出现异常的像素值。 ```python fig=plt.figure(figsize=(20, 20)) columns =4 rows = 2 for idx in range (columns*rows): ax = fig.add_subplot(rows, columns, idx+1, xticks=[], yticks=[]) plt.imshow(im_convert(images[idx])) ax.set_title("{} ({})".format(cat_to_name[str(preds[idx])], cat_to_name[str(labels[idx].item())]), color=("green" if cat_to_name[str(preds[idx])]==cat_to_name[str(labels[idx].item())] else "red")) plt.show() ``` a) * `plt.figure(figsize=(20, 20))`:使用 `matplotlib` 库创建一个新的图形对象 `fig`,并设置图形的大小为 20x20 英寸。 * `columns = 4` 和 `rows = 2`:分别定义了子图网格的列数和行数,这里表示要创建一个 2 行 4 列的子图布局,总共可以显示 8 个子图。 b) * `fig.add_subplot(rows, columns, idx + 1)`:在图形 `fig` 中添加一个子图,`rows` 和 `columns` 分别指定子图网格的行数和列数,`idx + 1` 表示当前子图在网格中的位置(从 1 开始计数)。 * `xticks=[]` 和 `yticks=[]`:设置子图的 x 轴和 y 轴刻度为空,即不显示坐标轴刻度,使图像更加简洁。 c) * `cat_to_name`:是一个字典,用于将类别索引映射到对应的类别名称。 * `preds[idx]`:是模型对第 `idx` 张图像的预测类别索引,`labels[idx].item()` 是第 `idx` 张图像的真实类别索引。 * `cat_to_name[str(preds[idx])]` 和 `cat_to_name[str(labels[idx].item())]`:分别获取预测类别和真实类别的名称。 * `ax.set_title()`:为当前子图设置标题,标题格式为 `预测类别名称 (真实类别名称)`。 * `color=("green" if cat_to_name[str(preds[idx])] == cat_to_name[str(labels[idx].item())] else "red")`:根据预测类别和真实类别是否一致,设置标题的颜色。如果一致,标题颜色为绿色;否则,标题颜色为红色。 展示结果如下: ![](https://i-blog.csdnimg.cn/direct/98c4ed16df0f4f73a024936be21a601d.png) ![](https://i-blog.csdnimg.cn/direct/b468e7986dab497f821501bc3bb89d3b.png) *** ** * ** *** 至此结束。

相关推荐
一点.点24 分钟前
WiseAD:基于视觉-语言模型的知识增强型端到端自动驾驶——论文阅读
人工智能·语言模型·自动驾驶
fanstuck1 小时前
从知识图谱到精准决策:基于MCP的招投标货物比对溯源系统实践
人工智能·知识图谱
dqsh061 小时前
树莓派5+Ubuntu24.04 LTS串口通信 保姆级教程
人工智能·python·物联网·ubuntu·机器人
打小就很皮...2 小时前
编写大模型Prompt提示词方法
人工智能·语言模型·prompt
Aliano2172 小时前
Prompt(提示词)工程师,“跟AI聊天”
人工智能·prompt
weixin_445238123 小时前
第R8周:RNN实现阿尔兹海默病诊断(pytorch)
人工智能·pytorch·rnn
KingDol_MIni3 小时前
ResNet残差神经网络的模型结构定义(pytorch实现)
人工智能·pytorch·神经网络
新加坡内哥谈技术4 小时前
亚马逊推出新型仓储机器人 Vulcan:具备“触觉”但不会取代人类工人
人工智能
Alter12304 小时前
从一城一云到AI CITY,智慧城市进入新阶段
人工智能·智慧城市
科技小E4 小时前
国标GB28181视频平台EasyCVR安防系统部署知识:如何解决异地监控集中管理和组网问题
大数据·网络·人工智能·音视频