首页 > 其他分享 >李哥深度学习代码复盘--分类实战(上)

李哥深度学习代码复盘--分类实战(上)

时间:2025-01-18 22:00:54浏览次数:3  
标签:512 nn -- self 李哥 64 model 224 复盘

分类实战:对图片进行分类

其中带标签的训练数据共有11类,每类280个,不带标签的训练数据共6786个,不带标签的数据需要用到半监督学习的方法。

上半是对带标签的数据进行训练验证,下半则主要介绍半监督学习及代码复盘

1.数据处理

(1)数据增广,通过对现有的数据样本进行变换,生成更多数据样本,以提高模型泛化能力,减少过拟合风险。

#训练集数据变换
train_transform = transforms.Compose(
    [
        transforms.ToPILImage(),             #将数据转换为PIL Image对象,以便对其进行操作
        transforms.RandomResizedCrop(224),   #随机放大裁剪,并调整到224*224
        transforms.RandomRotation(50),       #50度以内随机旋转
        transforms.ToTensor()                #将PIL Image对象转换为tensor,即模型接受的输入
    ]
)
#验证集数据变换   (验证和测试需要用原图,故不做变换)
val_transform = transforms.Compose(
    [
        transforms.ToTensor()
    ]
)

(2)数据集类,类中初始化、获取项目和长度三个函数与回归实战大体相似,但因为分类图片存储在文件夹中,不能使用csv模块读写,故另外定义一个读文件函数。

HW = 224
class food_Dataset(Dataset):
    def __init__(self, path, mode="train"):
        self.mode = mode
        self.X, self.Y = self.read_file(path)   #调用读文件函数
        self.Y = torch.LongTensor(self.Y)       #标签转为长整型
        if mode == "train":
            self.transform = train_transform    #数据增广,允许图片进行变换
        else:
            self.transform = val_transform

    def read_file(self, path):
        for i in tqdm(range(11)):             # 标签数据被分成11个文件夹,tqdm用于显示进度
            file_dir = path + "/%02d" % i     # %02d,保留两位整数
            file_list = os.listdir(file_dir)  # 列出文件夹下所有的文件名字

            xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)  #某类的所有图片
            yi = np.zeros(len(file_list), dtype=np.uint8)    #某类图片对应的标签

            for j, img_name in enumerate(file_list):
                img_path = os.path.join(file_dir, img_name)  
                #合并目录路径和图片名字得到图片路径
                img = Image.open(img_path)  # 读取图片
                img = img.resize((HW, HW))  # 调整图片大小  512*512 --> 224*224
                xi[j, ...] = img    #存入图像
                yi[j] = i    #存入标签

            if i == 0:         #如果是第一次则直接赋值,否则拼接以合并所有图像与标签
                X = xi
                Y = yi
            else:
                X = np.concatenate((X, xi), axis=0)   #axis=0代表新内容放到下一行
                Y = np.concatenate((Y, yi), axis=0)
        print("读到了%d个数据" % len(Y))    #显示一共读到了多少数据
        return X, Y
    #读文件函数接收文件路径参数,返回所有读取并调整大小后的图像数据及其对应的标签

    def __getitem__(self, item):
        return self.transform(self.X[item]), self.Y[item]
        
    def __len__(self):
        return len(self.X)
#实例化
train_set = food_Dataset(train_path, "train")    
val_set = food_Dataset(val_path, "val")

2.定义模型(三种方法)

(1)自定义。

class myModel(nn.Module):
    def __init__(self, num_class):       #num_class为图片类别数
        super(myModel, self).__init__()
        #3*224*224 ->512*7*7 
        #layer0到layer1为四个卷积层序列
        self.layer0 = nn.Sequential(
            nn.Conv2d(3, 64, 3, 1, 1),     #卷积层  3*224*224->64*224*224
        #(输入为3,输出为64,卷积核为3*3,步长1,填充1(保证输出与输入尺寸一致,根据卷积核大小定)
            nn.BatchNorm2d(64),            #批量归一化层(标准化处理,加速训练过程)
            nn.ReLU(),                     #激活函数
            nn.MaxPool2d(2),               #最大池化层,池化窗口2*2  64*224*224->64*112*112
        )
        self.layer1 = nn.Sequential(
            nn.Conv2d(64, 128, 3, 1, 1),   # 64*112*112->128*112*112
            nn.BatchNorm2d(128),
            nn.ReLU(),
            nn.MaxPool2d(2),               # 128*112*112->128*56*56
        )
        self.layer2 = nn.Sequential(
            nn.Conv2d(128, 256, 3, 1, 1),  # 128*56*56->256*56*56
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2),               # 256*56*56->256*28*28
        )
        self.layer3 = nn.Sequential(
            nn.Conv2d(256, 512, 3, 1, 1),  # 256*28*28->512*28*28
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2),               # 512*28*28->512*14*14
        )

        self.pool = nn.MaxPool2d(2)        #额外池化层  512*14*14->512*7*7(参数量25088)
        self.fc1 = nn.Linear(25088, 1000)  #第一个全连接层   25088-->1000
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(1000, num_class)   #第二个全连接层   1000-->num_class

    def forward(self, x):
        x = self.layer0(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.pool(x)
        x = x.view(x.size()[0], -1)    #拉直,将多维张量转化为一维向量以便进入全连接层
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x
#实例化
model = myModel(11) 

其中每个卷积层也可如下,不过前向传播会稍繁复。

        self.conv1 = nn.Conv2d(3, 64, 3, 1, 1)   
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU()
        self.pool1 = nn.MaxPool2d(2)

(2)从其他文件中调用,比如之前写过的可以直接拿来用。

from model_utils.model import initialize_model
#引用model_utils文件夹下的model.py文件中定义的initialize_model模型模块
model, _ = initialize_model("resnet18", 11, use_pretrained=True)
#调用并传入相关参数,具体看函数定义

(3)从模块里调用。

from torchvision.models import resnet18    #引入ResNet-18卷积神经网络模型
model = resnet18(pretrained=True)     #实例化,True用架构和参数,false只用架构
in_fetures = model.fc.in_features     #获取原输入特征
model.fc = nn.Linear(in_fetures, 11)  #建立新的全连接层,接受原来的输入,但调整输出为所需

3.训练和验证

与回归实战类似,区别在于用最大准确率代替了最小损失,初始化为0,若验证准确率大于最大准确率则更新模型。同时记录了每轮模型在训练集和验证集上的准确率,进行可视化。

在训练集上的准确次数计算如下:

train_acc += np.sum(np.argmax(pred.detach().cpu().numpy(), axis=1) == target.cpu().numpy())
#argmax指出最大值下标, 最大值下标与标签比较得到预测正确数量

5.配置及调用

本次实战使用了新的优化器AdamW,它与SGD有两个区别,一是SGD主要计算某点的梯度,而AdamW需要综合该点与该点以前的梯度;二是,可以自动调整学习率,更不容易出现梯度爆炸。

总体而言AdamW能够提供比SGD更稳定的训练过程。

另外也使用了新的损失计算函数--交叉熵,用于处理本次实战面对的分类问题。

所得结果如下:

标签:512,nn,--,self,李哥,64,model,224,复盘
From: https://blog.csdn.net/m0_69136216/article/details/145213829

相关文章

  • exgcd(扩展欧几里得算法)
    当我们要求解ax+by=c时,注意到x和y应该是一个解集,c是a的x倍加上b的y倍的和,假设gcd(a,b)==d,那么,c也应该是d的整数倍,即d|c.那么根据这,我们可以想到在思考ax+by=c的解集前,我们可以先思考ax+by=d的解集,注意到等式右边缩小了c/d倍,假设原解集为x1,y1,现解集为x2,y2,那么将x2,y2扩大c/......
  • 极坐标与直角坐标之间变换的原理和应用示例
            极坐标变换的原理是将平面上的点从直角坐标系转换为极坐标系,或者从极坐标系转换为直角坐标系。以下是关于极坐标变换原理的详细解释:一、极坐标系的基本概念        在极坐标系中,一个点的位置由两个参数确定:径向距离(ρ)和极角(θ)。        (1)......
  • 深入HDFS——DataNode启动源码
    引入上一篇我们看完了NameNode的启动源码,对于NameNode我们已经很熟悉了,今天我们接着来看看它的“得力干将”——DataNode。首先,自然还是从元数据管理篇提到的DataNode类(org.apache.hadoop.hdfs.server.datanode.DataNode)开始。不过在深入启动源码前,我们先看看它的源码注释:D......
  • 数据结构与算法之栈: LeetCode 71. 简化路径 (Ts版)
    简化路径https://leetcode.cn/problems/simplify-path/description/描述给你一个字符串path,表示指向某一文件或目录的Unix风格绝对路径(以‘/’开头),请你将其转化为更加简洁的规范路径在Unix风格的文件系统中规则如下一个点‘.’表示当前目录本身此外,两个......
  • 智能驾驶域控接口介绍
    针对域控制器硬件接口需支撑L2+功能实现的要求,以下是对所需硬件接口类型及其应满足的要求的详细分析:一、域控制器硬件接口类型域控制器作为智能驾驶系统的核心部件,其硬件接口类型多样,以满足不同传感器、执行器及内部通信的需求。以下是对所提及接口类型的解释:FPDLINK/GMSL:......
  • SolidState通关手册---靶机练习
    SolidState靶机训练声明B站UP主泷羽sec笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。✍......
  • 【数据库】MySQL数据库SQL语句汇总
    目录1.SQL通用语法2.SQL分类2.1.DDL2.2.DML2.3.DQL2.4.DCL3.DDL3.1.数据库操作3.1.1.查询3.1.2.创建3.1.3.删除3.1.4.使用3.2.表操作3.2.1.查询3.2.2.创建3.2.3.数据类型3.2.3.1.数值类型3.2.3.2.字符串类型3.2.3.3.日期时间类型3.2.4.修改3.2.4.1.添加......
  • 关于我的博客建站经历
    我是一名前端开发工程师,从大四的时候开始自学前端,荒废了三年时光,在大四的时候才算真正走进“编程”这扇大门。也是从那个时候开始学着搭建自己的个人博客,用来记录自己的学习笔记,但是却坚持不下来。而且发现一个奇怪的现象,对于搭建站点的过程我很感兴趣,内容输出却坚持不下......
  • SVG To Font 创建自己的字体图标库
    关于字体图标字体图标是一种特殊的字体,它可以像文字一样,通过CSS来控制它的大小和颜色。SVGToFontSVG虽然也能在网站中直接使用,但是它如果要修改大小或者颜色的话,就需要更改SVG的源码,特别不方便!网上有许多SVG转Font的方式,这里介绍一种js库svgtofont,用脚本......
  • C语言-预处理命令
    1、预处理命令是以# 开头的指令        用于在编译前对源代码进行一些处理2、与#号相关的代码    1、#include                用于在源代码中引入其他文件。可以引入标准库的头文件,也可以引入自定义的头              ......