Pytorch学习笔记(深度之眼)(3)之网络模型

21 篇文章 2 订阅
17 篇文章 1 订阅

网络模型

步骤:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述构建子模块——lenet.py
拼接子模块——forward函数中实现

nn.Module

在这里插入图片描述在这里插入图片描述在这里插入图片描述

====================================================================================================

相关函数:

1.nn.Conv2d

nn.Conv2d(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True))
参数:
in_channel: 输入数据的通道数,例RGB图片通道数为3;
out_channel: 输出数据的通道数,这个根据模型调整;
kennel_size: 卷积核大小,可以是int,tuple;kennel_size=2,意味着卷积大小(2,2), kennel_size=(2,3),意味着卷积大小(2,3)即非正方形卷积。
stride:步长,默认为1,与kennel_size类似,stride=2,意味着步长上下左右扫描皆为2, stride=(2,3),左右扫描步长为2,上下为3;
padding:零填充

2.nn.Linear()

PyTorch的nn.Linear()是用于设置网络中的全连接层的,需要注意的是全连接层的输入与输出都是二维张量,一般形状为[batch_size, size],不同于卷积层要求输入输出是四维张量。其用法与形参说明如下:
在这里插入图片描述in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。
out_features指的是输出的二维张量的大小,即输出的二维张量的形状为[batch_size,output_size],当然,它也代表了该全连接层的神经元个数。
从输入输出的张量的shape角度来理解,相当于一个输入为[batch_size, in_features]的张量变换成了[batch_size, out_features]的输出张量。

3.Image.open(x).convert(‘RGB‘)

如果不使用.convert(‘RGB’)进行转换的话,读出来的图像是RGBA四通道的,A通道为透明通道,该对深度学习模型训练来说暂时用不到,因此使用convert(‘RGB’)进行通道转换。

4.getitem(self,key):

这个方法返回与指定键想关联的值。对序列来说,键应该是0~n-1的整数,其中n为序列的长度。对映射来说,键可以是任何类型。

5.os.listdir(path)

os.listdir(path)的用途是得到路径path下的所有文件,返回list列表形式。

====================================================================================================

code

自定义网络:

# -*- coding: utf-8 -*-
"""
# @file name  : lenet.py
# @author     : yts3221@126.com
# @date       : 2020-12-21 10:08:00
# @brief      : lenet模型定义
"""
import torch.nn as nn
import torch.nn.functional as F


class LeNet(nn.Module):
    def __init__(self, classes):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, classes)

    def forward(self, x):
        out = F.relu(self.conv1(x))
        out = F.max_pool2d(out, 2)
        out = F.relu(self.conv2(out))
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = F.relu(self.fc1(out))
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return out

    def initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.xavier_normal_(m.weight.data)
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                nn.init.normal_(m.weight.data, 0, 0.1)
                m.bias.data.zero_()


class LeNet2(nn.Module):
    def __init__(self, classes):
        super(LeNet2, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, classes)
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        x = self.classifier(x)
        return x

分类模型训练:

# -*- coding: utf-8 -*-
"""
# @file name  : 2_train_lenet.py
# @author     : tingsongyu
# @date       : 2019-09-07 10:08:00
# @brief      : 分类模型训练
"""

import os

os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
import random
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
import torchvision.transforms as transforms
import torch.optim as optim
from matplotlib import pyplot as plt
from lenet import LeNet
from dataset import CATDataset

def set_seed(seed=1):
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)


set_seed()  # 设置随机种子
rmb_label = {"cat": 0, "dog": 1}

# 参数设置
MAX_EPOCH = 12
BATCH_SIZE = 35
LR = 0.0005
log_interval = 10
val_interval = 1

# ============================ step 1/5 数据 ============================

# 设置路径
# 对于一个分类问题,这里data_dir目录下一般包括两个文件夹:train和valid,
# 每个文件件下面包含N个子文件夹,N是你的分类类别数,
# 每个子文件夹里存放的就是这个类别的图像
split_dir = os.path.join("G:\\", "hello", "data", "cat_dog_split")
train_dir = os.path.join(split_dir, "train")
valid_dir = os.path.join(split_dir, "valid")

# 设置均值和方差?????????????
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]

# 数据预处理
train_transform = transforms.Compose([
    transforms.Resize((32, 32)),                  # 缩放
    transforms.RandomCrop(32, padding=4),         # 裁剪
    transforms.ToTensor(),                        # 将图像转成张量数据
    # 该函数作用对象需要是一个Tensor,所以在上一步通过transforms.ToTensor()生成Tensor
    transforms.Normalize(norm_mean, norm_std),
])

valid_transform = transforms.Compose([
    transforms.Resize((32, 32)),                  # 缩放
    transforms.ToTensor(),                        # 裁剪
    transforms.Normalize(norm_mean, norm_std),    # 将图像转成张量数据
])

# 构建MyDataset实例
# Dataset必须是用户自己构建,构建时传入两个参数,
# data_dir:数据路径 即从哪读数据?
# transform:数据预处理
# 按住ctrl后用鼠标点击函数可以直接跳转到函数具体实现的位置
train_data = CATDataset(data_dir=train_dir, transform=train_transform)
valid_data = CATDataset(data_dir=valid_dir, transform=valid_transform)

# 构建DataLoder
# 数据迭代器,传入数据,数据大小等相关设置
# shuffle=True 表示每一个epoch中的样本都是乱序
train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
valid_loader = DataLoader(dataset=valid_data, batch_size=BATCH_SIZE)

# ============================ step 2/5 模型 ============================
# 初始化卷积神经网络LeNet
net = LeNet(classes=2)
net.initialize_weights()

# ============================ step 3/5 损失函数 ============================
# 在PyTorch中采用torch.nn模块来定义网络的所有层,
# 比如卷积、降采样、损失层等等,这里采用交叉熵函数
# 分类任务通常采用交叉熵损失
criterion = nn.CrossEntropyLoss()                                                   # 选择损失函数

# ============================ step 4/5 优化器 ============================
# 优化器使用随机梯度下降
optimizer = optim.SGD(net.parameters(), lr=LR, momentum=0.9)

# 定义学习率的变化策略,这里采用的是torch.optim.lr_scheduler模块的StepLR类,
# 表示每隔step_size个epoch就将学习率降为原来的gamma倍。
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)     # 设置学习率下降策略

# ============================ step 5/5 训练 ============================
train_curve = list()
valid_curve = list()

# 以epoch为周期,在每个epoch中会有多个iteration的训练,在每一个iteration中训练模型
for epoch in range(MAX_EPOCH):

    loss_mean = 0.
    correct = 0.
    total = 0.

# 从DataLoader迭代器中不停地去获取一个Batchsize大小的数据
    net.train()
    for i, data in enumerate(train_loader):         # 在每一个iteration中训练模型

        # forward
        inputs, labels = data                       # 每一次读取一个batchsize大小的数据
        outputs = net(inputs)                       # 输入到模型中进行前向传播

        # backward
        # 反向传播获取梯度
        optimizer.zero_grad()                       # 将网络中的梯度清零
        # 将输出的outputs和原来导入的labels作为loss函数的输入就可以得到损失
        loss = criterion(outputs, labels)
        # 计算得到loss后就要回传损失
        # 要注意的是这是在训练的时候才会有的操作,测试时候只有forward过程。
        loss.backward()

        # update weights
        # 更新权重
        # 回传损失过程中会计算梯度,
        # 然后需要根据这些梯度更新参数,
        # optimizer.step()就是用来更新参数的
        optimizer.step()

        # 统计分类情况
        # 统计分类准确率
        # 得到输出后(网络的全连接层的输出)
        # 还希望能到到模型预测该样本属于哪个类别的信息,这里采用torch.max
        # torch.max()的第一个输入是tensor格式,
        # 所以用outputs.data而不是outputs作为输入
        # 第二个参数1是代表dim的意思,也就是取每一行的最大值,
        # 其实就是我们常见的取概率最大的那个index
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).squeeze().sum().numpy()

        # 打印训练信息
        loss_mean += loss.item()
        train_curve.append(loss.item())
        if (i+1) % log_interval == 0:
            loss_mean = loss_mean / log_interval
            print("Training:Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                epoch, MAX_EPOCH, i+1, len(train_loader), loss_mean, correct / total))
            loss_mean = 0.

    scheduler.step()  # 更新学习率

    # validate the model
    # 在每一个epoch中会进行验证集的测试,通过验证集来观察模型是否过拟合
    if (epoch+1) % val_interval == 0:

        correct_val = 0.
        total_val = 0.
        loss_val = 0.
        net.eval()
        with torch.no_grad():
            for j, data in enumerate(valid_loader):
                inputs, labels = data
                outputs = net(inputs)
                loss = criterion(outputs, labels)

                _, predicted = torch.max(outputs.data, 1)
                total_val += labels.size(0)
                correct_val += (predicted == labels).squeeze().sum().numpy()

                loss_val += loss.item()

            valid_curve.append(loss_val/valid_loader.__len__())
            print("Valid:\t Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format(
                epoch, MAX_EPOCH, j+1, len(valid_loader), loss_val, correct_val / total_val))


train_x = range(len(train_curve))
train_y = train_curve

train_iters = len(train_loader)
valid_x = np.arange(1, len(valid_curve)+1) * train_iters*val_interval  # 由于valid中记录的是epochloss,需要对记录点进行转换到iterations
valid_y = valid_curve

plt.plot(train_x, train_y, label='Train')
plt.plot(valid_x, valid_y, label='Valid')

plt.legend(loc='upper right')
plt.ylabel('loss value')
plt.xlabel('Iteration')
plt.show()

# ============================ inference ============================

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
test_dir = os.path.join(BASE_DIR, "test_data")

test_data = CATDataset(data_dir=test_dir, transform=valid_transform)
valid_loader = DataLoader(dataset=test_data, batch_size=1)

for i, data in enumerate(valid_loader):
    # forward
    inputs, labels = data
    outputs = net(inputs)
    _, predicted = torch.max(outputs.data, 1)

    rmb = 1 if predicted.numpy()[0] == 0 else 100
    print("模型获得{}元".format(rmb))
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值