0基础深度学习项目3:基于pytorch实现天气识别


🍺要求:

  1. 本地读取并加载数据。
  2. 测试集accuracy到达93%

🍻拔高:

  1. 测试集accuracy到达95%
  2. 调用模型识别一张本地图片

一、创建环境

● 语言环境:Python3.8
● 编译器:jupyter notebook
● 深度学习环境:Pytorch
数据:🔗百度网盘(提取码:hqij )

PS:本次我使用的环境是谷歌的🔗colab,有免费的gpu可以用,需要先注册一个账号,但是国内网站好像无法访问,缺点是上传文件比较慢,不过这次的数据集比较小,几分钟就可以了。具体的使用方法可以百度一下,也是比较好上手的。

如果文中有函数不懂可以翻一下前面2篇,尤其是卷积层和池化层的手动计算,在项目2中有详细说明。
0基础深度学习项目1:基于Pytorch实现mnist手写数字识别
0基础深度学习项目2:基于Pytorch实现CIFAR10彩色图片识别

二、前期准备

2.1 设置GPU

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

import os,PIL,pathlib,random

# 有GPU的切GPU,没GPU的用CPU,大家都有美好的未来
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

结果:
在这里插入图片描述

2.2 导入数据

# 导入数据
data_dir = '/content/drive/MyDrive/weather_photos'  # 这个地址可以挂载云盘之后直接右键复制地址
# 将字符串类型的文件夹路径转换为pathlib.Path对象
data_dir = pathlib.Path(data_dir)

data_paths = list(data_dir.glob('*')) # 以列表的形式将该路径下所有的子文件的文件路径存储在data_paths 中
classeNames = [str(path).split("/")[-1] for path in data_paths] # 通过split切割文件所属的类别名称
print(classeNames) # 输出类别名称,分别为云、雨、晴、日出

本次使用的数据集是一些天气图片的合集,共有4个文件夹,分别为云、雨、晴、日出。
在这里插入图片描述

2.3 数据可视化

# 随机选取24张图片进行展示
import matplotlib.pyplot as plt
from PIL import Image

# 指定图像文件夹路径
image_folder = '/content/drive/MyDrive/weather_photos/cloudy/'
# 获取文件夹中的所有图像文件
image_files = [f for f in os.listdir(image_folder) if f.endswith(('.jpg','.png','.jpeg'))]

# 创建matplotlib图像
fig, axs = plt.subplots(3,8, figsize=(16, 4)) # 3行8列

# 遍历图像文件并显示
for ax, img_file in zip(axs.flat,image_files):  # axes换成axs就好了
  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()

结果:
在这里插入图片描述

2.4 处理图像信息

# 处理一下图片
total_datadir = '/content/drive/MyDrive/weather_photos'

# transforms是pytorch中的图像预处理包。一般用compose把多个步骤整合到一起
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) # ImageFolder用于创建一个数据集对象,total_data包含所有图像数据的数据集对象,可用于训练神经网络模型
print(total_data)

结果:
在这里插入图片描述

2.5 划分数据集

# 划分数据集
train_size = int(0.8 * len(total_data)) # 训练集数据大小为总体数据长度的80%
test_size = len(total_data) - train_size # 测试集数据大小,为总体数据减去训练集数据
# 将总体数据total_data按照指定的大小比例随机划分训练集和测试集
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
train_dataset,test_dataset

结果:
在这里插入图片描述

2.6 加载数据集

# 加载数据集
batch_size = 32
train_dl = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True,num_workers=1) # num_workers为数据加载的子进程数量,如果是cpu运行的话要改成num_workers=0
test_dl = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=True,num_workers=1)

# 查看数据
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

结果:
在这里插入图片描述

三、构建简单的CNN网络

卷积神经网络CNN一般由__特征提取网络__和__分类网络__构成,特征提取网络用于图片的特征提取,分类网络用于将图片进行分类。

该案例的网络结构为:
在这里插入图片描述

上面的网络数据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)

import torch.nn.functional as F

class Network_bn(nn.Module):
  def __init__(self):
    super(Network_bn,self).__init__()
    """
    nn.Conv2d()函数:
    第一个参数(in_channels):输入图像的通道数
    第二个参数(out_channels):输出图像的通道数
    第三个参数(kernel_size):卷积核的大小
    第四个参数(stride):卷积核的步长,默认为1
    第五个参数(padding):卷积核的填充,默认为0
	
	nn.MaxPool2d()函数:
    第一个参数(kernel_size):最大的窗口大小
    第二个参数(stride):窗口的步幅,默认为kernel_size
    第三个参数(padding):卷积核的填充,默认为0
    第四个参数(dilation):控制窗口中元素步幅的参数
    """
    self.conv1 = nn.Conv2d(in_channels=3, out_channels=12, kernel_size=5, stride=1, padding=0)
    self.bn1 = nn.BatchNorm2d(12)  
    self.conv2 = nn.Conv2d(in_channels=12, out_channels=12, kernel_size=5, stride=1, padding=0)
    self.bn2 = nn.BatchNorm2d(12)
    self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)
    self.conv3 = nn.Conv2d(in_channels=12, out_channels=24, kernel_size=5, stride=1, padding=0)
    self.bn3 = nn.BatchNorm2d(24)
    self.conv4 = nn.Conv2d(in_channels=24, out_channels=24, kernel_size=5, stride=1, padding=0)
    self.bn4 = nn.BatchNorm2d(24)
    self.pool2 = nn.MaxPool2d(kernel_size=2, stride=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 = self.pool2(x)
    x = x.view(-1,24*50*50)
    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)
print(model)

结果:
在这里插入图片描述

四、训练模型

4.1 设置超参数

# 设置超参数
loss_fn = nn.CrossEntropyLoss() # 创建损失函数
learn_rate = 1e-4 # 学习率
opt = torch.optim.SGD(model.parameters(), lr=learn_rate) # 创建优化器

4.2 编写训练函数

# 编写训练函数
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) # 将输入和标签数据发送到GPU

    # 计算预测误差
    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_loss, train_acc

4.3 编写测试函数

# 编写测试函数
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) # 将输入和标签数据发送到GPU

      # 计算预测误差
      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_loss, test_acc

4.4 开始训练

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

epoch_range = range(epochs)

plt.figure(figsize=(12, 3)) # 设置画布大小

plt.subplot(1, 2, 1) # 一行两列,画第一张图
plt.plot(epoch_range, train_acc, label='Train Acc') # 绘制训练集准确率曲线
plt.plot(epoch_range, test_acc, label='Test Acc') # 绘制测试集准确率曲线
plt.legend(loc='lower right') # 设置图例位置

plt.title('Training and Validation Acc')  # (这一行是后面加的,执行时忘写了,所以左图少个标题)

plt.subplot(1, 2, 2) # 一行两列,画第二张图
plt.plot(epoch_range, train_loss, label='Train Loss') # 绘制训练集损失曲线
plt.plot(epoch_range, test_loss, label='Test Loss') # 绘制测试集损失曲线
plt.legend(loc='upper right') # 设置图例位置

plt.title('Training and Validation Loss')

plt.tight_layout() # 自动调整子图布局
plt.show() # 显示

结果:
在这里插入图片描述

五、总结

可能提高模型预测精度的办法:

  1. 调整训练轮数epoch,适当提高epoch可以增加预测准确度;
  2. 调整超参数,例如修改学习率的大小;
  3. 修改优化器,例如本例中的SGD优化器可以换成Adam优化器再进行训练。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值