Pytorch实现天气识别

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

一:前期准备

1.设置GPU

如果设备上支持GPU就使用GPU,否则使用CPU

import torch
import torch.nn as nn
import torch vision.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 

2.导入数据

data_dir = '/content/drive/MyDrive/Colab Notebooks/第5天/data'
data_dir = pathlib.Path(data_dir)

data_path = list(data_dir.glob('*'))
class_names = [str(path).split("/")[-1] for path in data_paths]  
#classeNames = [str(path).split("\\")[1] for path in data_paths] #windows 用这行
print(class_names)
#或者使用
#files = os.listdir(data_dir)
#print(files)

代码输出

  • 第一步:使用pathlib.path()函数将字符串类型的文件夹路径转换为pathlib.path对象。
  • 第二步:使用glob()方法获取data_dir路径下的所有文件路径,并以列表形式存储在data_paths中。
  • 第三步:通过split()函数对data_paths中的每个文件路径执行分割操作,获得各个文件所属的类别名称,并存储在classeNames中。
  • 第四步: 打印classNames列表,显示每个文件所属的类别名称。
import matplotlib.pyplot as plt
from PIL import Image

#指定图像文件夹路径
image_folder = '/content/drive/MyDrive/Colab Notebooks/第5天/data/cloudy/'

#获取文件夹中的所有图像文件
image_files = [f for f in os.listdir(image_filder) 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()

total_datadir = '/content/drive/MyDrive/Colab Notebooks/第5天/data'


# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
train_transforms = transforms.Compose([
    transforms.Resize([224,224]), #将输入图片resize成统一尺寸
    transforms.ToTensor(), #将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(  #标准化处理-->转换为标准正态分布(高斯分布),使模型更容易收敛
        mean = [0.485,0.456,0.406],
        std = [0.229,0.224,0.225]) #mean和std从数据中随机抽样计算得到
])

total_data = datasets.ImageFolder(total_datadir,transform = train_transforms)
total_data

3.划分数据集

train_size = int(0.8*len(total_data))
test_size = len(total_data)-train_size
train_datasize , test_datasize = torch.utils.data.random_split(total_data,[train_size,test_size])
train_datasize,test_datasize

代码输出

  • train_size 表示训练集大小,通过将整体数据的80%转换为整数得到。
  • test_size 表示测试集大小,是总体数据长度减去训练集大小。

使用torch.utils.data.random_split()方法进行数据集划分。该方法将总体数据total_data按照指定的大小比例([train_size,test_size])随机划分为训练集和测试集,并将划分结果分别赋值给train_dataset 和 test_dataset两个变量。

train_size,test_size

代码输出

for X,y in test_dl:
  print("shape of X [N,C,H,W]:",X.shape)
  print("shape of y:",y.shape,y.dtype)
  break

torch.utils.data.DataLoader()参数详解

torch.utils.data.DataLoader是Pytorch中用于加载和管理数据的一个实用工具类。它允许你以小批次的方式迭代你的数据集,这对于训练神经网络和其他机器学习任务非常有用。DataLoader构造函数接受多个参数,下面是一些常用的参数及解释:

1. dataset(必须参数):这是你的数据集对象,通常是torch.utils.data.Dataset的子类,它包含了你的数据样本。

2:batch_size(可选参数):指定每个小批次中包含的样本数,默认值为1.

3:shuffle(可选参数):如果设置为True,则在每个epoch开始时对数据进行洗牌,以随机打乱样本的顺序。这对于训练数据的随机性很重要,以避免模型学习到数据的顺序性。默认值为False。

4:num_workers(可选参数):用于数据加载的子进程数量。通常,将其设置为大于0的值可以加快数据加载速度,特别是当数据集很大时。默认值为0,表示在主进程中加载数据。

5:pin_memory(可选参数):如果设置为True,则数据加载到GPU时会将数据存储在CUDA的锁业内存中,这可以加速数据传输到GPU.默认值为False。

6:drop_last(可选参数):如果设置为True,则在最后一个小批次可能包含样本数小于batch_size时,丢弃该小批次。这在某些情况下很有用,以确保所有小批次具有相同大小,默认值为False。

7:time_out(可选参数):如果设置为正整数,它定义了每个子进程在等待数据加载器传递数据时的超时时间(以秒为单位)。这可以用于避免子进程卡住的情况。默认值为 0,表示没有超时限制。 

8:worker_init_in(可选参数):一个可选的函数,用于初始化每个子进程的状态。这对于设置每个子进程的随机种子或其他初始化操作很有用。

二:构建简单的CNN网络

在卷积层和全连接层之间,我们可以使用之前的torch.flatten()也可以使用下面的x.view()亦或是torch.nn.Flatten()。torch.nn.Flatten()与TensorFlow中的Flatten()层类似,前两者则仅仅是一种数据集拉伸操作(将二维数据拉伸为一维), torch.flatten()方法不会改变x本身,而是返回一个新的张量。而x.view()方法则是直接在原有数据上进行操作。

网络结构图:

上面的网络数据shape变化过程为:

3, 224, 224(输入数据)
-> 12, 220, 220(经过卷积层1)
-> 12, 216, 216(经过卷积层2)-> 12, 108, 108(经过池化层1)
-> 24, 104, 104(经过卷积层3)

-> 24, 100, 100(经过卷积层4)-> 24, 50, 50(经过池化层2)
-> 60000 -> num_classes(4)

计算公式为:
OutputSize = (\frac{InputSize-KernelSize+2*padding}{Stride})+1

import torch.nn.functional as F

class Network_bn(nn.Module):
  def __init__(self):
    super(Network_bn,self).__init__()
    """
    nn.Conv2d()函数:
    第一个参数(in_channels)是输入的channel数量
    第二个参数(out_channels)是输出的channel数量
    第三个参数(kerne_size)是卷积核大小
    第四个参数(stride)是步长,默认为1
    第五个参数(padding)是填充大小,默认为0
    """
    self.conv1 = nn.Conv2d(3,12,5,1,0)
    self.bn1 = nn.BatchNorm2d(12)
    self.conv2 = nn.Conv2d(12,12,5,1,0)
    self.bn2 = nn.BatchNorm2d(12)
    self.pool1 = nn.MaxPool2d(2,2)
    self.conv3 = nn.Conv2d(12,24,5,1,0)
    self.bn3 = nn.BatchNorm2d(24)
    self.conv4 = nn.Conv2d(24,24,5,1,0)
    self.bn4 = nn.BatchNorm2d(24)
    self.pool2 = nn.MaxPool2d(2,2)
    self.fc1 = nn.Linear(24*50*50,len(class_names))

  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 = self.pool2(x)
    x= x.view(-1,24*50*50)
    #x= torch.flatten(x,1)
    x = self.fc1(x)
    return x
  
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

model = Network_bn().to(device)
model

三:训练模型

1.设置超参数

loss_fn = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

2.编写训练函数

def train(dataloader,model,loss_fn,optimizer):
  size = len(dataloader.dataset) #训练集的大小,一共900张图
  num_batches = len(dataloader) #批次数目,29(900/32)
  
  train_loss,train_acc =0,0 #初始化训练损失和正确率

  for X,y in dataloader:#获取图片及其标签
    X,y = X.to(device),y.to(devie)

    #计算预测误差
    pred = model(X)        #网络输出
    loss = loss_fn(pred,y) #计算网络输出和真实值之间的差距

    #反向传播
    optimizer.zero_grad() #梯度清零
    loss.backward()       #反向传播
    optimizer.step()      #更新参数

    #记录acc与loss
    train_loss += loss.item()
    train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()

  train_loss /= num_batches
  train_acc /= size
  return train_acc,train_loss

3.编写测试函数

测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器。

def test(dataloader,model,loss_fn):
  size = len(dataloader.dataset) #测试集的大小,一共225张图片
  num_batches = len(dataloader) #批次数目 8(225/32=7.03,向上取整)
  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) #计算网络输出和真实值之间的差距

      #记录acc与loss
      test_loss += loss.item()
      test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()

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

4.正式训练

1.model.train()

model.train()的作用是启用 Batch Normalization 和 Dropout。如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train()model.train()是保证BN层能够用到每一批数据的均值和方差。对于Dropoutmodel.train()是随机取一部分网络连接来训练更新参数。

2.model.eval()

model.eval()的作用是不启用 Batch Normalization 和 Dropout。如果模型中有BN层(Batch Normalization)和Dropout,在测试时添加model.eval()model.eval()是保证BN层能够用全部训练数据的均值和方差,即测试过程中要保证BN层的均值和方差不变。对于Dropoutmodel.eval()是利用到了所有网络连接,即不进行随机舍弃神经元。训练完train样本后,生成的模型model要用来测试样本。在model(test)之前,需要加上model.eval(),否则的话,有输入数据,即使不训练,它也会改变权值。这是model中含有BN层和Dropout所带来的的性质。

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,optimizer)
  
  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')

四:结果可视化

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              #分辨率

epoch_range = range(epochs)

plt.figure(figsize=(12,3))
plt.subplot(1,2,1)
plt.plot(epoch_range,train_acc,label='Training Accuracy')
plt.plot(epoch_range,test_acc,label='Test Accuracy')
plt.legend(loc = 'lower right')
plt.title('Training and Validation Accuracy')

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

五:总结

测试集中的loss值仍有较大波动,可能需要对学习率进行调整

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值