深度学习--week_3-新手小白入门之路

- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/rnFa-IeY93EpjVu0yzzjkw) 中的学习记录博客**
- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)**

前言

本文利用pytorch实现CNN卷积神经网络识别天气,是对上两篇博客的知识点使用,如若基础有缺请返回上两篇博客查看详细内容。本文主要聚焦于搭建卷积神经网络,如何提升预测的性能以及预测准确率,希望本文能对你有所帮助,如有不足,请多多包涵。接下来进入正文。

一.前置准备

1.环境搭建

本文所用环境如下:

编译环境 pytho==3.8

编译器 Jupyter Lab

torch== 2.1.0+cu118

torchvision == 0.16.0+cu118 

可用如下代码进行gpu检验,如果硬件不支持,可使用cpu:

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets

import os,PIL,pathlib,random

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

device

输出为 device(type='cuda')

2.数据集准备

(一).数据集介绍

数据集为天气识别数据集,可由网上自由下载,这里不提供下载途径了,数据集介绍,数据集中一共有1126张图片,分为cloudy,rain,shine,sunrise四个类别,我们的目标就是通过自主划分训练集和验证集,使得网络在验证集上实现精准分类。

(二).数据读取

将数据集的根文件夹(此时跟文件夹名为data_3)放于代码同目录下,利用如下代码进行数据查看 :

1.获取图像的所在的各个文件夹名: 首先data_dir确定根目录文件夹,利用pathlib.Path转化为WindowPath类,用于将'./data_3/'重新定义为可操作的路径对象,然后利用对应的.glob方法选取文件下的所有文件路径,用list存储,然后利用列表生成式,对data_paths表格的每个元素进行划分,提取出/后面的部分,即将data_3/rain中的rain提取出来,存储在classeNames中,作为将要预测的目标别。

data_dir='./data_3/' #根目录
data_dir=pathlib.Path(data_dir) #获取根目录路径
data_paths=list(data_dir.glob('*')) #list获取路径下对应的所有文件路径
 # 结果如[Path('data_3/class1'), Path('data_3/class2'), ...]
classeNames=[str(path).split("\\")[1] for path in data_paths] #利用/进行划分,/前是一段,/后是一段,取第二段

classeNames #获取文件夹名

 

对文件夹中的其中一类图片进行可视化查看,代码: image_folder为选取的文件夹路径,image_file通过列表生成式选取全部图片,其中os.listdir(image_folder)作用是获取该路径下的所有文件,利用if条件选取以".jpg",".png",".jpeg"三个后缀的文件,fig,axes创建子图,for循环中,axes.flat为将子图数组展平为一维数组,方便位置选取,遍历每个子图和文件中的图像,os.path.join()将文件路径(./data_3/cloudy/)和图像文件名(sunny_001.jpg)拼接起来组成完整的图片路径(./data_3/cloudy/sunny_001.jpg),然后利用Image.open()函数打开图像文件,用ax.imshow显示图像,ax.axis('off')表示关闭坐标轴。

import matplotlib.pyplot as plt
from PIL import Image
#指定文件路径
image_folder='./data_3/cloudy/'
#获取所有的图像文件
image_files=[f for f in os.listdir(image_folder) if f.endswith((".jpg",".png",".jpeg"))]
#创建matplotlib图像
fig,axes=plt.subplots(3,8,figsize=(16,6))

for ax,img_file in zip(axes.flat,image_files):
    img_path=os.path.join(image_folder,img_file)
    img=Image.open(img_path)
    ax.imshow(img)
    ax.axis('off')

#显示图像
plt.tight_layout()
plt.show()

 

 二 . 数据处理

1.标准化

由于数据是图片形式,我们要进行模型训练要转为数据形式,接下来我们要进行图片与数据的转换。代码如下: 首先确定对应数据集根目录data_3,然后利用transforms.Compose方法组合多个图像变换操作,将图像尺寸调整为224,224(默认尺寸),transform转为tensor张量,transforms.Normalize()方法是对张量格式的图像进行标准化处理,均值和方差为该组取值,数值的来源是通过对计算ImageNet数据集中所有训练图像的RGB通道均值和标准差得出的。接着利用datasets.ImageFolder函数扫描data_3下的所有子文件夹,采用刚刚创建的train_trainforms形式的格式对图像进行标准化处理

total_datadir='./data_3/' #文件目录
train_transforms=transforms.Compose([
    transforms.Resize([224,224]), #输入图像调整大小为224*224
    transforms.ToTensor(), #转换为pytorch张量,归一化到[0,1]之间
    transforms.Normalize( #均值,标准差,标准正态分布
        mean=[0.485,0.456,0.406], #数据是从数据集中随机抽样得到的
        std=[0.229,0.224,0.225]
    )
])
total_data=datasets.ImageFolder(total_datadir,transform=train_transforms)
total_data

2.数据集划分

由于没有确定的数据集划分要求,这里指定百分之八十的数据为训练集,剩余为测试集,划分代码:其中torch.utils.data.random_split()代码用于数据集划分,total_data为划分的数据集,[train_size,test_size]为划分比例。

#划分数据集
train_size=int(0.8*len(total_data))
test_size=len(total_data)-train_size
print('测试集:{},训练集:{}'.format(train_size,test_size))
#划分数据集大小
train_dataset,test_dataset=torch.utils.data.random_split(total_data,[train_size,test_size])
train_dataset,test_dataset

 对数据形状进行查看:

print('每个长度: ',train_size,test_size)
print(len(train_dataset[0]))
imgs,label=train_dataset[0]
print(imgs.shape) #3,224,224
print(label)

批次划分:代码:(之前都有详细介绍,不懂可参考前两篇博客)

batch_size=32
train_dl=torch.utils.data.DataLoader(train_dataset,
                                    batch_size=batch_size,
                                    shuffle=True)
test_dl=torch.utils.data.DataLoader(test_dataset,
                                   batch_size=batch_size,
                                   shuffle=True)

 划分查看:

for X,y in test_dl:
    print('形状[批次,通道数,高度,宽度]',X.shape)
    print('形状y,',y.dtype)
    break

 三.模型搭建

1.模型代码:

这里的模型代码就不做详细解释了,有不懂的可查阅前两篇。

#构建CNN
import torch
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        #输入(B,3,224,224)->(B,12,222,222)
        self.conv1=nn.Conv2d(in_channels=3,out_channels=12,kernel_size=3,stride=1,padding=0)
        self.bn1=nn.BatchNorm2d(12) #归一化操作
        #(B,12,222,222)->(B,24,220,220)
        self.conv2=nn.Conv2d(in_channels=12,out_channels=24,kernel_size=3,stride=1,padding=0)
        self.bn2=nn.BatchNorm2d(24) #归一化
        #(B,24,220,220)->(B,24,220,220)
        self.pool1=nn.MaxPool2d(2,2) #卷积核,步长
        #第三层卷积层
        #(B,24,110,110)->(B,24,110,104)
        # self.conv3=nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=0)
        # self.bn3=nn.BatchNorm2d(64) #归一化
        
        #(B,24,110,110)->(B,24,108,108)
        self.conv4=nn.Conv2d(in_channels=24,out_channels=24,kernel_size=3,stride=1,padding=0)
        self.bn4=nn.BatchNorm2d(24) #对24特征图归一化
        #(B,24,108,108)->(B,24,106,106)
        self.conv5=nn.Conv2d(in_channels=24,out_channels=24,kernel_size=3,stride=1,padding=0)
        self.bn5=nn.BatchNorm2d(24)
        #(B,24,106,106)->(B,24,53,53)
        self.pool2=nn.MaxPool2d(2,2) 

        self.fc1=nn.Linear(24*53*53,len(classeNames))
        
    def forward(self,x):
        x=F.relu(self.bn1(self.conv1(x)))
        x=F.relu(self.bn2(self.conv2(x)))
        x=self.pool1(x)
        # x=F.relu(self.bn3(self.conv3(x)))
        x=F.relu(self.bn4(self.conv4(x)))
        x=F.relu(self.bn5(self.conv5(x)))
        x=self.pool2(x)
        x=x.view(-1,24*53*53) #类似于全连接层
        x=self.fc1(x)

        return x

model=Model().to(device)
model

 

#训练模型
loss_fn=nn.CrossEntropyLoss() #创建损失函数
learn_rate=1e-4
opt=torch.optim.SGD(model.parameters(),lr=learn_rate)

四. 模型训练

1.训练代码:

这里只给出代码,不作细致描述,有需要的可见前面两篇博客:

def train(dataloader,model,loss_fn,optimizer):
    size=len(dataloader.dataset)
    num_batches=len(dataloader) #批次数目
    train_loss,train_acc=0,0 #
    for x,y in dataloader:
        x,y=x.to(device),y.to(device)
        pred=model(x)
        loss=loss_fn(pred,y)
        #反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step() #每一步更新

        train_acc+=(pred.argmax(1)==y).type(torch.float).sum().item()
        train_loss+=loss.item()

    train_acc/=size
    train_loss/=num_batches

    return train_acc,train_loss

2.测试代码:

这里只给出代码,不作细致描述,有需要的可见前面两篇博客:

def test(dataloader,model,loss_fn):
    size=len(dataloader.dataset)
    num_batches=len(dataloader)
    test_loss,test_acc=0,0

    with torch.no_grad():
        for imgs,target in dataloader:
            imgs,target=imgs.to(device),target.to(device)

            target_pred=model(imgs)
            loss=loss_fn(target_pred,target)

            test_loss+=loss.item()
            test_acc+=(target_pred.argmax(1)==target).type(torch.float).sum().item()

    test_acc/=size
    test_loss/=num_batches
    return test_acc,test_loss

3.训练代码

这里只给出代码,不作细致描述,有需要的可见前面两篇博客:

epochs=20
train_loss=[]
train_acc=[]
test_loss=[]
test_acc=[]
for epoch in range(epochs):
    model.train()
    epoch_train_acc,epoch_train_loss=train(train_dl,model,loss_fn,opt)
    model.eval()
    epoch_test_acc,epoch_test_loss=test(test_dl,model,loss_fn)

    train_acc.append(epoch_train_acc)
    train_loss.append(epoch_train_loss)
    test_acc.append(epoch_test_acc)
    test_loss.append(epoch_test_loss)
    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
    print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
print('Done')
    

4.模型调参过程:

1.第一轮:

#构建CNN
import torch
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        #输入(B,3,224,224)->(B,12,222,222)
        self.conv1=nn.Conv2d(in_channels=3,out_channels=12,kernel_size=5,stride=1,padding=0)
        self.bn1=nn.BatchNorm2d(12) #归一化操作
        #(B,12,222,222)->(B,24,220,220)
        self.conv2=nn.Conv2d(in_channels=12,out_channels=12,kernel_size=5,stride=1,padding=0)
        self.bn2=nn.BatchNorm2d(12) #归一化
        #(B,24,220,220)->(B,24,220,220)
        self.pool1=nn.MaxPool2d(2,2) #卷积核,步长
        #第三层卷积层
        #(B,24,110,110)->(B,24,110,104)
        # self.conv3=nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=0)
        # self.bn3=nn.BatchNorm2d(64) #归一化
        
        #(B,24,110,110)->(B,24,108,108)
        self.conv4=nn.Conv2d(in_channels=12,out_channels=24,kernel_size=5,stride=1,padding=0)
        self.bn4=nn.BatchNorm2d(24) #对24特征图归一化
        #(B,24,108,108)->(B,24,106,106)
        self.conv5=nn.Conv2d(in_channels=24,out_channels=24,kernel_size=5,stride=1,padding=0)
        self.bn5=nn.BatchNorm2d(24)
        #(B,24,106,106)->(B,24,53,53)
        self.pool2=nn.MaxPool2d(2,2) 

        self.fc1=nn.Linear(24*50*50,len(classeNames))
        
    def forward(self,x):
        x=F.relu(self.bn1(self.conv1(x)))
        x=F.relu(self.bn2(self.conv2(x)))
        x=self.pool1(x)
        # x=F.relu(self.bn3(self.conv3(x)))
        x=F.relu(self.bn4(self.conv4(x)))
        x=F.relu(self.bn5(self.conv5(x)))
        x=self.pool2(x)
        x=x.view(-1,24*50*50) #类似于全连接层
        x=self.fc1(x)

        return x

model=Model().to(device)
model

 网络结构下图所示:

 结果:

第二轮:

模型参数依照下面:

Model( 
(conv1): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1)) 
(bn1): BatchNorm2d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(conv2): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1)) 
(bn2): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv4): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1)) 
(bn4): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(conv5): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1)) 
(bn5): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (fc1): Linear(in_features=80000, out_features=4, bias=True) ) 

第三轮:

模型参数:


Model( 
(conv1): Conv2d(3, 12, kernel_size=(3, 3), stride=(1, 1)) 
(bn1): BatchNorm2d(12, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(conv2): Conv2d(12, 24, kernel_size=(3, 3), stride=(1, 1)) 
(bn2): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv4): Conv2d(24, 24, kernel_size=(3, 3), stride=(1, 1)) 
(bn4): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 
(conv5): Conv2d(24, 24, kernel_size=(3, 3), stride=(1, 1)) 
(bn5): BatchNorm2d(24, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) 
(fc1): Linear(in_features=67416, out_features=4, bias=True) )

 第四轮:

 

 5.结果可视化:

代码:有疑问可看上两篇

import matplotlib.pyplot as plt
#隐藏警告
import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率

from datetime import datetime
current_time = datetime.now() # 获取当前时间

epochs_range = range(epochs)

plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')
plt.xlabel(current_time) # 打卡请带上时间戳,否则代码截图无效

plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

总结

比较耗时的调参过程,通过调参过程继续深化对CNN卷积网络的理解。

以上便是该次实践的全部内容,全部代码已经呈现,有需要的一段一段复制运行即可,如有问题,可在评论区指出(也可私聊),博主会欣然改正,有疑问也可私信博主,相信你能看到这里,应该会有点收获,如果给你带来了收获,那至少我会感到很荣幸,博主这么久的心血也没有白费,码字不易,给个三连,球球了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值