【Pytorch基础教程22】肺部感染识别任务(模型微调实战)

本文涉及的产品
函数计算FC,每月15万CU 3个月
简介: 一、任务介绍数据集来源:https:/​/​www.​kaggle.​com/​paultimothymooney/​chest-​xray-​pneumonia/​download

一、任务介绍

数据集来源:https://www.kaggle.com/paultimothymooney/chest-xray-pneumonia/download

image.png

肺部感染识别任务,加载已经在imageNet上训练过的预训练模型ResNet,冻结中低层的参数(权重),将最后两层替换,微调参数,实现肺部感染的图片分类任务。说白了迁移学习,模型微调就是把拖拉机改装下零件,变成我们需要的变形金刚。


迁移学习(Transfer learning) 就是把已经训练好的模型参数迁移到新的模型来帮助新模型训练。


来回顾下ResNet网络:ResNet是为了解决梯度消失(由于在梯度计算的过程中是用的反向传播,所以需要利用链式法则来进行梯度计算,是一个累乘的过程。若每一个地方梯度都是小于1的,累乘后梯度会趋于0)的问题。

image.png


(1)residual block要求输入和输出的tensor维度相同。

(2)有的跳连接在上图汇总是虚线的,表示不一定做跳连接(因为维度不匹配的原因,无法跳跃后相加),所以需要做单独处理——如不做跳连接,或者在跳连接中做一个池化层,注意池化不改变通道数(上面栗子的正路是做一个卷积,起到/2效果)。

image.png

(3)构造网络的超参数和input、output的size需要计算好。为了检验网络是否正确,可以先对net简单测试(输入rand的tensor代入),如注释其他层,看前面层的结果和预期的tensor大小是否吻合,即【增量式开发】。

(4)卷积层中做的事,res是层间做的事。


代码如下,ResidualBlock和Net两个类变了,其余和之前没变。

class ResidualBlock(nn.Module):
    def __init__(self, channels):
        super(ResidualBlock, self).__init__()
        self.channels = channels
        self.conv1 = nn.Conv2d(channels,
                               channels,
                               kernel_size = 3,
                               padding = 1)
        self.conv2 = nn.Conv2d(channels,
                               channels,
                               kernel_size = 3,
                               padding = 1)
    def forward(self, x):
        y = F.relu(self.conv1(x))
        y = self.conv2(y)
        # x+y后再relu激活
        return F.relu(x + y)
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size = 5)
        self.conv2 = nn.Conv2d(16, 32, kernel_size = 5)
        self.mp = nn.MaxPool2d(2)
        self.rblock1 = ResidualBlock(16)
        self.rblock2 = ResidualBlock(32)
        self.fc = nn.Linear(512, 10)
    def forward(self, x):
        in_size = x.size(0)
        x = self.mp(F.relu(self.conv1(x)))
        x = self.rblock1(x)
        x = self.mp(F.relu(self.conv2(x)))
        x = self.rblock2(x)
        x = x.view(in_size, -1)
        x = self.fc(x)
        return x

二、步骤概览

image.png

其中注意迁移学习的部分,我们直接拿torchvision.models.resnet50模型微调,首先冻结预训练模型中的所有参数,然后替换掉最后两层的网络(替换2层池化层,还有fc层改为dropout,正则,线性,激活等部分),最后返回模型。


三、具体流程

实验环境:NVIDIA GeForce RTX 3090,jupyter notebook。


3.1 加载库和观察数据

# 1 加入必要的库
import torch
import torch.nn as nn
import numpy as np
import torch.optim as optim
from torch.optim import lr_scheduler
from torchvision import datasets, transforms, utils, models
import time
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
from torch.utils.tensorboard.writer import SummaryWriter
import os
import torchvision
import copy

如果安装库较慢的童鞋可以加上清华源等。


另外如果需要在ubuntu上解压数据集压缩包,注意命令:

1、.tar 用 tar –xvf 解压

2、.gz 用 gzip -d或者gunzip 解压

3、.tar.gz和.tgz 用 tar –xzf 解压

4、.bz2 用 bzip2 -d或者用bunzip2 解压

5、.tar.bz2用tar –xjf 解压

6、.Z 用 uncompress 解压

7、.tar.Z 用tar –xZf 解压

8、.rar 用 unrar e解压

9、.zip 用 unzip 解压


了解我们数据集很重要,设置batch_size = 8,每次读取8张图片:


加载库,定义方法,显示图片。

定义超参数:如batch_size、DEVICE等。

图片转换:如transform,下面对train和val的字典形式进行操作,如:

随机裁剪成300×300,

随机地水平方向旋转或拉伸RandomHorizontalFlip(),

随机中间裁剪CenterCrop,

并且使用官方的正则化方法transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])。

验证集val的操作可以和train一样。

操作数据集:

文件直接拖到terminal也能直接知道文件路径。

加载train和val可以以字典形式,我们用列表推导式“同时”读取,最后以字典形式保存(ps:列表推导式比for循环快,这里还1句代替2句)。

为数据集创建一个迭代器,读取数据,也是字典形式,x对应加载数据集的x。一般需要设置Shuffle和batch_size。

获取标签的类别名称(这里是感染和正常的情况),直接用image_datasets['train'].classes即可。

最后一步:读取图片,注意用python的迭代器iter把dataloader['train']变为一个可迭代对象,然后外层用next逐个读取,赋值给变量数据datas和标签targets。

# 1 加载库
import torch
import torch.nn as nn
from torchvision import datasets, transforms
import os
from torch.utils.data import DataLoader
from torchvision.utils import make_grid
import matplotlib.pyplot as plt
import numpy as np
# 2 定义一个方法:显示图片 
def image_show(inp, title=None):
    plt.figure(figsize=(14, 3)) 
    inp = inp.numpy().transpose((1,2,0))
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    inp = std * inp + mean
    inp = np.clip(inp, 0, 1)
    plt.imshow(inp)
    if title is not None:
        plt.title(title)
    plt.pause(0.001)
    plt.show()
def main():
    # 3 定义超参数
    BATCH_SIZE = 8 # 每批处理的数据数量
    DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    # 4 图片转换
    data_transforms = {
        'train':
            transforms.Compose([
                transforms.Resize(300),
                transforms.RandomResizedCrop(300),
                transforms.RandomHorizontalFlip(),
                transforms.CenterCrop(256),
                transforms.ToTensor(),
                transforms.Normalize([0.485, 0.456, 0.406],
                                     [0.229, 0.224, 0.225])
            ]),
        'val':
            transforms.Compose([
                transforms.Resize(300),
                transforms.CenterCrop(256),
                transforms.ToTensor(),
                transforms.Normalize([0.485, 0.456, 0.406],
                                     [0.229, 0.224, 0.225])
            ])
    }
    # 5 操作数据集
    # 5.1 数据集路径
    data_path = "chest_xray"
    # 5.2 加载数据集train 和 val
    image_datasets = { x : datasets.ImageFolder(os.path.join(data_path, x),
                                                data_transforms[x]) for x in ['train', 'val']}
    # 5.3 为数据集创建一个迭代器,读取数据
    datalaoders = {x : DataLoader(image_datasets[x], shuffle=True,
                                  batch_size=BATCH_SIZE) for x in ['train', 'val']}
    # 5.3 训练集和验证集的大小(图片的数量)
    data_sizes = {x : len(image_datasets[x]) for x in ['train', 'val']}
    # 5.4 获取标签的类别名称:  NORMAL 正常 --- PNEUMONIA 感染
    target_names = image_datasets['train'].classes
    # 6 显示一个batch_size的图片(8张图片)
    # 6.1 读取8张图片
    datas, targets = next(iter(datalaoders['train']))
    # 6.2 将若干张图片拼成一幅图像
    out = make_grid(datas, nrow=4, padding=10)
    # 6.3 显示图片
    image_show(out, title=[target_names[x] for x in targets])
if __name__ == '__main__':
    main()

为了好看哈哈,可以将8张图片拼起来(用torchvision.utils的make_grid,还能设置图片之间的间隔padding)

最后显示图片,直接用pytorch官网的image_show了。



3.2 迁移学习,微调模型

模型微调:迁移学习的应用场景,如在目标数据集上训练目标模型,将从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的。


获取pytorch中的预训练模型,冻结预训练模型中所有参数;

然后模型微调:如替换ResNet最后的2层网络,返回一个新模型

首先冻结预训练模型中的所有参数,然后替换掉最后两层的网络(替换2层池化层,还有fc层改为dropout,正则,线性,激活等部分),最后返回模型。


其中新的池化层,是两个nn.AdaptiveAvgPool2d(size)拼接起来的(最后一个参数为1,表示是纵向维度的拼接)。

接着的原来的fc层被替换,这里一坨层用的上节提到的nn.Sequential组合:

dropout一般写在正则化后,取参数0.5,丢掉一些神经元;

线性层处理后,加激活层和正则化。

# 8 更改池化层
class AdaptiveConcatPool2d(nn.Module):
    def __init__(self, size=None):
        super().__init__()
        size = size or (1, 1) # 池化层的卷积核大小,默认值为(1,1)
        self.pool_one = nn.AdaptiveAvgPool2d(size) # 池化层1
        self.pool_two = nn.AdaptiveAvgPool2d(size) # 池化层2
    def forward(self, x):
        return torch.cat([self.pool_one(x), self.pool_two(x), 1]) # 连接两个池化层
# 7 迁移学习:拿到一个成熟的模型,进行模型微调
def get_model():
    model_pre = models.resnet50(pretrained=True) # 获取预训练模型
    # 冻结预训练模型中所有的参数
    for param in model_pre.parameters():
        param.requires_grad = False
    # 微调模型:替换ResNet最后的两层网络,返回一个新的模型
    model_pre.avgpool = AdaptiveConcatPool2d() # 池化层替换
    model_pre.fc = nn.Sequential(
        nn.Flatten(), # 所有维度拉平
        nn.BatchNorm1d(4096), # 256 x 6 x 6   ——> 4096
        nn.Dropout(0.5),  # 丢掉一些神经元
        nn.Linear(4096, 512),  # 线性层的处理
        nn.ReLU(), # 激活层
        nn.BatchNorm1d(512), # 正则化处理
        nn.Linear(512,2),
        nn.LogSoftmax(dim=1), # 损失函数
    )
    return model_pre

3.3 定义tensorboard writer的函数

这里比较特殊,为了保存成日志:

from torch.utils.tensorboard.writer import SummaryWriter
# 定义函数,获取Tensorboard的writer
def tb_writer():
    timestr = time.strftime("%Y%m%d_%H%M%S")
    writer = SummaryWriter('logdir/' + timestr)
    return writer

这里的writer是为了tensorboard的可视化使用,一般的用法如下:

writer = SummaryWriter("../logs")  #定义logs文件位置
writer.add_scalar("test_acc", total_accuracy, total_test_step) #添加名称,数据,step
writer.close()  #关闭
#######################
# 在命令行:
tensorboard --logdir=logs

3.4 训练结果

训练了20min左右,结果很神奇的,先是accuracy慢慢升到0.88后,又降低到0.75了:

               Epoch |        Training Loss |            Test Loss |             Accuracy |
Test Loss : 0.7482, Accuracy : 0.7500
                   0 |  0.37546360227437947 |   0.7481886744499207 |                 0.75 |
Test Loss : 0.4785, Accuracy : 0.8125
                   1 |  0.26250585539975774 |   0.4785425364971161 |                 0.81 |
Test Loss : 0.6143, Accuracy : 0.8125
                   2 |  0.24647439648354016 |    0.614277184009552 |                 0.81 |
Test Loss : 0.4550, Accuracy : 0.8750
                   3 |  0.22980742459834175 |  0.45500850677490234 |                 0.88 |
Test Loss : 0.4762, Accuracy : 0.8750
                   4 |  0.22832605980867202 |  0.47616851329803467 |                 0.88 |
Test Loss : 0.5090, Accuracy : 0.8125
                   5 |  0.22127203448984092 |   0.5090200304985046 |                 0.81 |
Test Loss : 0.5329, Accuracy : 0.8125
                   6 |   0.2200799149982977 |   0.5329417586326599 |                 0.81 |
Test Loss : 0.7185, Accuracy : 0.8125
                   7 |  0.20401323301827207 |   0.7185251116752625 |                 0.81 |
Test Loss : 0.6448, Accuracy : 0.7500
                   8 |  0.21905418684930994 |   0.6448410749435425 |                 0.75 |
Test Loss : 0.6633, Accuracy : 0.7500
                   9 |   0.2127879018557976 |   0.6633487343788147 |                 0.75 |
Training complete in 20.00m 12.74s
相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
1月前
|
存储 物联网 PyTorch
基于PyTorch的大语言模型微调指南:Torchtune完整教程与代码示例
**Torchtune**是由PyTorch团队开发的一个专门用于LLM微调的库。它旨在简化LLM的微调流程,提供了一系列高级API和预置的最佳实践
157 59
基于PyTorch的大语言模型微调指南:Torchtune完整教程与代码示例
|
2月前
|
算法 PyTorch 算法框架/工具
Pytorch学习笔记(九):Pytorch模型的FLOPs、模型参数量等信息输出(torchstat、thop、ptflops、torchsummary)
本文介绍了如何使用torchstat、thop、ptflops和torchsummary等工具来计算Pytorch模型的FLOPs、模型参数量等信息。
327 2
|
14天前
|
机器学习/深度学习 人工智能 PyTorch
Transformer模型变长序列优化:解析PyTorch上的FlashAttention2与xFormers
本文探讨了Transformer模型中变长输入序列的优化策略,旨在解决深度学习中常见的计算效率问题。文章首先介绍了批处理变长输入的技术挑战,特别是填充方法导致的资源浪费。随后,提出了多种优化技术,包括动态填充、PyTorch NestedTensors、FlashAttention2和XFormers的memory_efficient_attention。这些技术通过减少冗余计算、优化内存管理和改进计算模式,显著提升了模型的性能。实验结果显示,使用FlashAttention2和无填充策略的组合可以将步骤时间减少至323毫秒,相比未优化版本提升了约2.5倍。
33 3
Transformer模型变长序列优化:解析PyTorch上的FlashAttention2与xFormers
|
29天前
|
并行计算 监控 搜索推荐
使用 PyTorch-BigGraph 构建和部署大规模图嵌入的完整教程
当处理大规模图数据时,复杂性难以避免。PyTorch-BigGraph (PBG) 是一款专为此设计的工具,能够高效处理数十亿节点和边的图数据。PBG通过多GPU或节点无缝扩展,利用高效的分区技术,生成准确的嵌入表示,适用于社交网络、推荐系统和知识图谱等领域。本文详细介绍PBG的设置、训练和优化方法,涵盖环境配置、数据准备、模型训练、性能优化和实际应用案例,帮助读者高效处理大规模图数据。
48 5
|
1月前
|
存储 PyTorch 算法框架/工具
利用PyTorch的三元组损失Hard Triplet Loss进行嵌入模型微调
本文介绍了如何使用 PyTorch 和三元组边缘损失(Triplet Margin Loss)微调嵌入模型,详细讲解了实现细节和代码示例。
40 4
|
2月前
|
机器学习/深度学习 自然语言处理 监控
利用 PyTorch Lightning 搭建一个文本分类模型
利用 PyTorch Lightning 搭建一个文本分类模型
63 8
利用 PyTorch Lightning 搭建一个文本分类模型
|
2月前
|
机器学习/深度学习 自然语言处理 数据建模
三种Transformer模型中的注意力机制介绍及Pytorch实现:从自注意力到因果自注意力
本文深入探讨了Transformer模型中的三种关键注意力机制:自注意力、交叉注意力和因果自注意力,这些机制是GPT-4、Llama等大型语言模型的核心。文章不仅讲解了理论概念,还通过Python和PyTorch从零开始实现这些机制,帮助读者深入理解其内部工作原理。自注意力机制通过整合上下文信息增强了输入嵌入,多头注意力则通过多个并行的注意力头捕捉不同类型的依赖关系。交叉注意力则允许模型在两个不同输入序列间传递信息,适用于机器翻译和图像描述等任务。因果自注意力确保模型在生成文本时仅考虑先前的上下文,适用于解码器风格的模型。通过本文的详细解析和代码实现,读者可以全面掌握这些机制的应用潜力。
111 3
三种Transformer模型中的注意力机制介绍及Pytorch实现:从自注意力到因果自注意力
|
3月前
|
机器学习/深度学习 PyTorch 调度
在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型
在深度学习中,学习率作为关键超参数对模型收敛速度和性能至关重要。传统方法采用统一学习率,但研究表明为不同层设置差异化学习率能显著提升性能。本文探讨了这一策略的理论基础及PyTorch实现方法,包括模型定义、参数分组、优化器配置及训练流程。通过示例展示了如何为ResNet18设置不同层的学习率,并介绍了渐进式解冻和层适应学习率等高级技巧,帮助研究者更好地优化模型训练。
191 4
在Pytorch中为不同层设置不同学习率来提升性能,优化深度学习模型
|
3月前
|
机器学习/深度学习 监控 PyTorch
PyTorch 模型调试与故障排除指南
在深度学习领域,PyTorch 成为开发和训练神经网络的主要框架之一。本文为 PyTorch 开发者提供全面的调试指南,涵盖从基础概念到高级技术的内容。目标读者包括初学者、中级开发者和高级工程师。本文探讨常见问题及解决方案,帮助读者理解 PyTorch 的核心概念、掌握调试策略、识别性能瓶颈,并通过实际案例获得实践经验。无论是在构建简单神经网络还是复杂模型,本文都将提供宝贵的洞察和实用技巧,帮助开发者更高效地开发和优化 PyTorch 模型。
50 3
PyTorch 模型调试与故障排除指南
|
2月前
|
存储 并行计算 PyTorch
探索PyTorch:模型的定义和保存方法
探索PyTorch:模型的定义和保存方法

热门文章

最新文章