【一起深度学习——kaggle叶子分类】

news2024/12/29 2:07:02

kaggle 叶子分类

  • 目的:将叶子进行分类。
  • 实现步骤:
    • 1、数据处理:
    • 2、加载数据
    • 3、 定义残差块
    • 4、定义Resnet模型。
    • 5、定义训练以及评估函数:
    • 6、开始训练:
    • 7、输出结果:

目的:将叶子进行分类。

实现步骤:

1、数据处理:

对数据进行处理。由于数据图片(测试集,训练集,验证集全都放在了images中,应将其进行分开)
处理步骤:
1、 读取csv文件,解析其中的地址信息。
2、 创建 训练集 和 测试集 的文件夹,用于存放对应的图片数据。
3、 遍历 训练数据 和 测试数据 中的图片地址,从images复制到对应文件夹中

定义数据处理函数:

#定义读取数据的函数
def read_images():
    # 读取 csv文件。
    train_df = pd.read_csv("train.csv")
    test_df = pd.read_csv("test.csv")

    # 创建 保存训练集和测试集的文件夹
    os.makedirs(name="train_images",exist_ok=True)  #训练集
    os.makedirs(name="val_images",exist_ok=True)    #验证集
    os.makedirs(name="test_images",exist_ok=True)   #测试集

    #设置验证集的占比
    val_ration = 0.2

    #获取训练集的样本数量。
    num_samples = len(train_df)
    #验证集的样本数量。
    num_val_samples = int(num_samples * val_ration)
    # 取出验证集的所有索引
    val_indices = random.sample(range(num_samples),num_val_samples)
    # print(val_indices)
    #复制images中的数据到 训练集中
    for index,row in train_df.iterrows():
        # print(row[1])  #打印看一下数据格式
        # print(index)
        # print(row['image'])
        image_path = row['image']
        label = row['label']
        # 若下标在验证集的索引中:
        if index in val_indices:
            target_dir = os.path.join("val_images",label)
        else:
            target_dir = os.path.join("train_images", label)
        os.makedirs(target_dir,exist_ok=True)
        shutil.copy(image_path,target_dir)

    #复制images中的数据到 训练集中
    for row in train_df.iterrows():
        #print(row[1])  #打印看一下数据格式
        # print(row[1]['image'])
        image_path = row[1]['image']
        label = row[1]['label']
        target_dir = os.path.join("train_images",label)
        os.makedirs(target_dir,exist_ok=True)
        shutil.copy(image_path,target_dir)
    #复制images中的数据到 测试集 中
    for row in test_df.iterrows():
        #print(row[1])  #打印看一下数据格式
        # print(row[1]['image'])
        image_path = row[1]['image']
        target_dir = os.path.join("test_images/test_data")
        os.makedirs(target_dir,exist_ok=True)
        shutil.copy(image_path,target_dir)
# read_images()

对于读取测试集时,
target_dir = os.path.join(“test_images/test_data”) 这里的路径我为啥要设置多一个/test_data呢,因为如果不设置这个文件夹的话,后续使用Dataloader.datasets.ImageFolder 就读取不了,但是我现在不知道如何直接加载一个文件夹中图片,所以暂时只能这样。

2、加载数据

tran = torchvision.transforms.Compose([
        torchvision.transforms.ToTensor(),
        # torchvision.transforms.Resize((96,96))
   ]
)

#读取数据
train_data = torchvision.datasets.ImageFolder(root="train_images",transform=torchvision.transforms.ToTensor())
val_data = torchvision.datasets.ImageFolder(root="val_images",transform=torchvision.transforms.ToTensor())
test_data = torchvision.datasets.ImageFolder(root="test_images",transform=torchvision.transforms.ToTensor())

batch_sz = 256
#加载数据
train_loader = DataLoader(train_data,batch_sz,shuffle=True,drop_last=True)
val_loader = DataLoader(val_data,batch_sz,shuffle=False,drop_last=False)
test_loader = DataLoader(test_data,batch_sz,shuffle=False,drop_last=False)
# for (images,labels) in train_loader:
#     print(images.shape)
# 从上边这段代码可知 images维度torch.Size([128, 3, 224, 224])  (样本数量,通道,高度,宽度)

3、 定义残差块

class ResidualBlock(nn.Module):
    def __init__(self,in_channels,out_channels,strides=1):
        super(ResidualBlock,self).__init__()
        # 设置卷积核为3,padding =1能够保持大小不变
        self.conv1 = nn.Conv2d(in_channels,out_channels,kernel_size=3,stride=strides,padding=1)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(out_channels,out_channels,kernel_size=3,stride=1,padding=1)
        self.bn2 = nn.BatchNorm2d(out_channels)

        #如果
        self.downsample = None
        # 因为步幅不为 1 的话 就会导致形状大小发生变化。
        if strides != 1 or in_channels != out_channels:
            self.downsample = nn.Sequential(
                nn.Conv2d(in_channels,out_channels,kernel_size=1,stride=strides),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self,X):
        Y = self.relu(self.bn1(self.conv1(X)))
        Y = self.bn2(self.conv2(Y))
        if self.downsample is not  None:
            X = self.downsample(X)
        Y += X
        return self.relu(Y)

4、定义Resnet模型。

原理图:
在这里插入图片描述

定义Resnet18模型。
第一层:7x7 的卷积层,64 个输出通道,步幅为 2。
最大池化层:3x3 的池化核,步幅为 2,用于下采样。
4 个阶段(layers),每个阶段包含若干个残差块(ResidualBlock)。
第一个阶段:2 个残差块。 每个残差块包括两个卷积。
第二个阶段:2 个残差块。
第三个阶段:2 个残差块。
第四个阶段:2 个残差块。
全局平均池化层:对特征图进行全局平均池化,将每个通道的特征图变成一个值。
全连接层:将全局平均池化层的输出连接到输出类别数量的全连接层,用于分类

class Resnet18(nn.Module):
    def __init__(self):
        super(Resnet18, self).__init__()
        # H2 = (224- 7 + 3 * 2) / 2 +1 =112
        # [128,3,224,224] => [128,64,112,112]
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        # H2 = (112 - 3 + 2*1 )/2 +1 = 56
        # [128,64,112,112] => [128,64,56,56]
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        # 添加残缺块 1,每个残缺块呢,当block2为2时,会产生2个残缺块,执行四次卷积。
        # H2 = (56 - 3+2*1)/1 +1 = 56
        # 第一个残缺块:[128,64,56,56] => [128,64,56,56]
        #             [128,64,56,56] => [128,64,56,56]
        # 第二个残缺块:[128,64,56,56] => [128,64,56,56]
        #             [128,64,56,56] => [128,64,56,56]
        self.layer1 = self.make_layer(64, 64, 2)
        # 添加残缺块 2
        # 第一个残缺块:H2 = (56 - 3 +2*1)/2+1 = 28
        #            [128,64,56,56] => [128,128,28,28]
        #            H2 = (28 - 3 + 2*1)/1 +1 =28
        #            [128,128,56,56] => [128,128,28,28]
        #     X :    [128,64,56,56] => [128,128,28,28]  只是将X的通道数和形状变为与 Y一致,可相加。
        #第二个残缺块:[128,128,28,28] => [128,128,28,28]
        #            [128,128,28,28] => [128,128,28,28]
        self.layer2 = self.make_layer(64, 128, 2, stride=2)
        # 添加残缺块 3
        # 第一个残缺块:H2 = (28-3 + 2*1)/2 +1 = 14
        #               [128,128,28,28] => [128,256,14,14]
        #               [128,256,14,14] => [128,256,14,14]
        #       x:      [128,128,28,28] => [128,256,14,14]
        # 第二个残缺块:H2 = (14-3 +2*1) /1 +1 = 14
        #                [128,256,14,14] => [128,256,14,14]
        #                 [128,256,14,14] => [128,256,14,14]
        self.layer3 = self.make_layer(128, 256, 2, stride=2)
        # 添加残缺块 4
        # 第一个残缺块:H2 = (14 - 3 +2*1)/2 +1 = 7
        #               [128,256,14,14] =>[128,512,7,7]
        #               [128,512,7,7] => [128,512,7,7]
        #       X :     [[128,256,14,14]] => [128,512,7,7]
        # 第二个残缺块:H2 = (7-3+2*1)/1+1 =7
        #               [128,512,7,7] => [128,512,7,7]
        #               [128,512,7,7] => [128,512,7,7]
        self.layer4 = self.make_layer(256, 512, 2, stride=2)
        # 池化层,采用自适应池化(指定特征图的高度,宽度)
        # [128,512,7,7] => [128,512,1,1]
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        # 全连接层
        #  [128,512] => [128,176]
        self.fc = nn.Linear(512, 176)

    def make_layer(self, in_channels, out_channels, blocks, stride=1):
        layers = []
        layers.append(ResidualBlock(in_channels, out_channels, stride))
        for _ in range(1, blocks):
            # 这里的stride 默认为1
            layers.append(ResidualBlock(out_channels, out_channels))
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)
        x = self.avgpool(x)
        #   [128,512,1,1] => [128,512*1*1]
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

5、定义训练以及评估函数:

device = torch.device('cuda')
def train(model, train_loader, val_loader, criterion, optimizer, num_epochs):
    for epoch in range(num_epochs):
        model.train()  # 设置模型为训练模式
        train_loss = 0.0
        correct = 0
        total = 0

        # 训练模型
        for x, y in train_loader:
            x, y = x.to(device), y.to(device)

            optimizer.zero_grad()  # 梯度清零
            outputs = model(x)  # 前向传播
            loss = criterion(outputs, y)  # 计算损失
            loss.backward()  # 反向传播
            optimizer.step()  # 更新参数

            train_loss += loss.item() * x.size(0)
            _,predicted = outputs.max(1)   #这里_ 表示占位符,outputs.max(1)返回的是最大值,和最大值的索引
            total += y.size(0)
            correct += predicted.eq(y).sum().item()

        # 计算训练集上的平均损失和准确率
        train_loss = train_loss / len(train_loader.dataset)
        train_acc = 100. * correct / total

        # 在验证集上评估模型
        val_loss, val_acc = evaluate(model, val_loader, criterion)

        # 打印训练信息
        print(
            f'Epoch [{epoch + 1}/{num_epochs}], Train Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%, Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.2f}%')


def evaluate(model, val_loader, criterion):
    model.eval()  # 设置模型为评估模式
    val_loss = 0.0
    correct = 0
    total = 0

    with torch.no_grad():  # 禁止梯度计算
        for x, y in val_loader:
            x, y = x.to(device), y.to(device)
            outputs = model(x)
            loss = criterion(outputs, y)

            val_loss += loss.item() * x.size(0)
            _, predicted = outputs.max(1)
            total += y.size(0)
            correct += predicted.eq(y).sum().item()

    # 计算验证集上的平均损失和准确率
    val_loss = val_loss / len(val_loader.dataset)
    val_acc = 100. * correct / total
    return val_loss, val_acc


6、开始训练:

model = Resnet18().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
num_epochs = 10

train(model,train_loader,val_loader,criterion=criterion,optimizer=optimizer,num_epochs=num_epochs)

7、输出结果:

好家伙,直接过拟合了,应该对数据进一步处理,避免过拟合。
Epoch [1/10], Train Loss: 4.0659, Train Acc: 10.37%, Val Loss: 7.2078, Val Acc: 9.92%
Epoch [2/10], Train Loss: 2.5477, Train Acc: 32.15%, Val Loss: 22.1796, Val Acc: 3.00%
Epoch [3/10], Train Loss: 1.8311, Train Acc: 47.40%, Val Loss: 10.9325, Val Acc: 6.52%
Epoch [4/10], Train Loss: 1.3895, Train Acc: 58.54%, Val Loss: 2.5158, Val Acc: 33.61%
Epoch [5/10], Train Loss: 1.0679, Train Acc: 67.53%, Val Loss: 13.1189, Val Acc: 8.95%
Epoch [6/10], Train Loss: 0.8598, Train Acc: 73.25%, Val Loss: 5.0033, Val Acc: 20.12%
Epoch [7/10], Train Loss: 0.7152, Train Acc: 77.59%, Val Loss: 1.3642, Val Acc: 59.86%
Epoch [8/10], Train Loss: 0.5771, Train Acc: 81.43%, Val Loss: 0.6216, Val Acc: 79.32%
Epoch [9/10], Train Loss: 0.5036, Train Acc: 83.70%, Val Loss: 3.3536, Val Acc: 34.62%
Epoch [10/10], Train Loss: 0.4170, Train Acc: 86.52%, Val Loss: 8.0067, Val Acc: 21.36%

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1647258.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

观测与预测差值自动变化系统噪声Q的自适应UKF(AUKF_Q)MATLAB编写

简述 基于三维模型的UKF,设计一段时间的输入状态误差较大,此时通过对比预测的状态值与观测值的残差,在相应的情况下自适应扩大系统方差Q,构成自适应无迹卡尔曼滤波(AUKF),与传统的UKF相比&…

【Qt】按钮类控件

文章目录 1 :peach:Push Button:peach:2 :peach:Radio Buttion:peach:3 :peach:Check Box:peach:4 :peach:Tool Button:peach: 1 🍑Push Button🍑 使⽤ QPushButton 表⽰⼀个按钮,这也是当前我们最熟悉的⼀个控件了,QPushButton …

Ubuntu添加非root用户到Docker用户组

前言 首先平常公司的Linux生产环境为了防止误操作导致灾难性问题,一般都不会给我们开发开放root管理员的账号权限。所以平常在Ubuntu的普通用户登录的时候,要操作Dcoker一般都需要带上sudo来提升命令执行权限。为了解决这一问题,我们只需要将…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-13-按键实验

前言: 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Axure中继器介绍以及案例分享

中继器是 Axure 中一个比较高阶的应用,它可以让我们在纯静态网页中模拟出类似带有后台数据交互的增删改查的效果。 一、中继器的基本使用方法: 整体流程分为三个步骤 ☆创建中继器 我们先在 Axured画布中拖入一个中继器元件 双击中继器后的效果 打开之…

java高并发实战<1>

我们一个请求--->tomcat--->db 我们只需要把我们的应用部署在tomcat中, 就可以了 这就是你单体的感念,单机结构你只用一个服务器就完成了你项目的部署单点问题一旦这台机器挂了,用户就没有办法用你这个服务,单机能力有限 随着你用户量增长的过程…

04.添加自定义监控项

添加自定义监控项 监控项就是监控每一个指标 1.命令行,手动取值 [rootyunlong66 ~]# iostat |awk $1 ~/sda/ sda 5.89 36.10 122.71 557910 1896585 [rootyunlong66 ~]# iostat |awk $1 ~/sda/{print $2} 5.892.修改zabbix-age…

OpenNJet下载安装及入门实战教程

一、什么是OpenNJet OpenNJet是一款开放原子开源基金会孵化及运营的开源项目。OpenNJet采用C语言实现。是一款高性能、轻量级的WEB应用及代理软件。    OpenNJet 应用引擎是高性能、轻量级的WEB应用与代理软件。作为云原生服务网格的数据平面,NJet具备动态配置加载…

【Git】回滚旧提交版本且不影响最新提交版本

【Git】回滚旧提交版本且不影响最新提交版本 一、场景假设 远程仓库origin中有一个分支main,有4次提交记录:v1、v2、v3、v4。 二、需求 需要回滚旧提交版本,但不影响已有的所有提交版本(即不影响最新提交版本)&…

k8s保持pod健康

存活探针 Kubemetes 可以通过存活探针 (liveness probe) 检查容器是否还在运行。可以为 pod 中的每个容器单独指定存活探针。如果探测失败,Kubemetes 将定期执行探针并重新启动容器。 Kubemetes 有以下三种探测容器的机制: HTTP GET 探针对容器的 IP 地…

深入探索归并排序算法:分而治之的排序艺术

在计算机科学领域,排序算法是一项基础且重要的技术,归并排序作为一种经典的分治算法,以其稳定性和高效性而闻名。本文将带您深入探索归并排序算法的原理、实现方法以及应用场景,揭示这一排序艺术背后的精髓。 **归并排序算法简介…

【管理篇】管理三步曲:管理规划(一)

目录标题 管理到底都要做哪些事呢如何开始带团队? 职能:如何界定团队是干什么的?目标:如何为团队设定合理的目标规划资源:需要申请哪些资源(1)你是否了解资源的丰富性?(2…

判断dll/lib是32/64位、查看lib是导入库/静态库的方法 、查看dll包含的符合、lib包含的函数

一、判断dll/lib是32/64位 原文链接:https://www.cnblogs.com/bandaoyu/p/16752602.html 1. 简便方法: 直接用记事本或者notepad(或txt文本)打开exe文件(dll文件),会有很多乱码,不要头疼,接下…

优雅处理返回信息状态码:Result对象在Spring Boot中的应用

前言 在开发过程中,处理返回的信息状态码是一个重要的问题,尤其是在大型项目中。为了统一处理这些状态码,我在Spring Boot中创建了一个名为Result的Java对象,用于封装返回的信息和状态码。在本文中,我将分享如何实现这…

网络安全的重要性及人才需求

安全现在是大趋势,说是铁饭碗也不为过,就业前景好,方向多比传统计算机行业就业舒服点。但是大厂依然是985,211的天下,是双非能进大厂的,只是凤毛麟角。前提是你的能力可以让公司忽略你的学历。 以2023年为…

Leetcode—622. 设计循环队列【中等】

2024每日刷题(128) Leetcode—622. 设计循环队列 实现代码 class MyCircularQueue { public:MyCircularQueue(int k): q(k) {qSize k;}bool enQueue(int value) {if(isFull()) {return false;}q[rear] value;rear (rear 1) % qSize;deflag false;…

精准读取CSV/Excel数据 - 灵活指定行列范围的 Python 解决方案

文章目录 源代码项目简介导入相关库__file_exists 装饰器函数的签名和注释主要功能的实现运行演示读取 Excel 文件 源代码 https://github.com/ma0513207162/PyPrecip。pyprecip\reading\read_api.py 路径下。 项目简介 PyPrecip 是一个专注于气候数据处理的 Python 库&#xf…

【STM32嵌入式系统设计与开发】——18DAC(DAC输出应用)

这里写目录标题 STM32资料包: 百度网盘下载链接:链接:https://pan.baidu.com/s/1mWx9Asaipk-2z9HY17wYXQ?pwd8888 提取码:8888 一、任务描述二、任务实施1、工程文件夹创建2、函数编辑(1)主函数编辑&#…

Python3中Richdem包遇到问题

Python3中Richdem包遇到问题 文章目录 Python3中Richdem包遇到问题问题一报错解决 问题二报错解决 参考 问题一 报错 RichDEM 是一套数字高程模型 (DEM) 水文分析工具,这次打算用richdem进行地形分析,尝试在conda里面安装richde…

UDP如何端口映射?

UDP端口映射是一种网络技术,通过它可以实现在异地组网的情况下,不暴露在公网上,通过私有通道传输数据,并对数据进行安全加密,以保障数据的安全性。这项技术在如今日益复杂和危险的网络环境中显得尤为重要。 UDP&#x…