Pytorch ----- 卷积神经网络 CNN --基础部分(卷积层,池化层 下采样) 附代码解读+实现~~学习笔记

基础概念梳理

全连接网络:每一层串起来了,每一个输出节点都参与了下一层输入节点的权重计算。


在前一节。直接使用全连接网络将上面这个图变成一维向量。
显然损失了空间状态。
现在使用卷积神经网络保留空间状态结构。
通过卷积层之后,通道,宽,长都可能变,也可能不变。像下面这样:
在这里插入图片描述
然后使用下采样,变成了4 * 12 * 12
使用下采样,通道数不会变,但宽和高会变。
下采样目的就是为了减小数据量,方便运算。
在这里插入图片描述
然后再通过一次卷积 和一次下采样,得到 8 * 4 * 4。
然后直接展开成一维向量,之后全链接层映射到结果分类。

在这里插入图片描述

卷积的过程

其中 不断的卷积,采样的过程叫做特征提取器。
后面的变成向量,全链接映射分类那块(最后两步)。叫分类器.

卷积过程如下图:在这里插入图片描述
图片读取进来就是RGB形式。通道有三层。左上角坐标为原点。
会有一个小块的扫描器从头到尾扫描整个图像,小块中得到的计算是原来块中全部数据特征经过权重得到的,然后计算出来新的通道 和 W,H,全部扫描完再进行融合得到卷积结果。

卷积核的解释

单通道卷积 执行过程 数值实例:
这里步长设置为1,即每次仅向右或者向下移动1个单位。
在这里插入图片描述
在这里插入图片描述

全是数据为 1 5 5 卷积核 (kernel) 为 1 3 * 3
这里就是拿卷积核 3
3 的去输入的数据 5*5中扫描。
然后做对位数乘,得到的结果放到结果集的第一个位置。
这里是做数乘,不是矩阵乘。
然后再将卷积核往后移,继续扫描。

多通道卷积

三通道卷积 的情况:

每一个通道配一个卷积核,然后算出来三个卷积结果。
再对三个卷积结果进行相加,还是对位相加然后得到一个总的结果。
实际上就是 3 * 3 * 3 张量输入数据和 3 * 3 * 3的卷积核对位相乘得到 3* 3 *3的张量,然后再求和的结果。
.在这里插入图片描述
卷积输入数据的通道数必须和所配备的卷积核通道数相同,w和h可以不一样。经过卷积之后的通道数为1。

如果想最后得到的通道数为M M>1。则可以让输入数据分别经过M个卷积核,得到M个通道为1的结果,再将这M个结果进行拼接,就可以得到M通道的卷积结果。
像下面这样:

在这里插入图片描述

  • 卷积输入数据的通道数必须和所配备的卷积核通道数相同
  • 卷积核的个数和输出的通道数 数值一致。

所以显然,如果现在我们有输入 n * w * h 通过卷积得到 m * w * h 则中间需要配备的卷积核为 m * n * w * h 为四维张量。
所以我们要配备一个四维张量的权重。

代码解读

数据准备部分代码:
输入通道数为5,输出通道数位10.
w和h都是100.
卷积核大小为3 将默认成 3 * 3 也可以写成元组(x,x)或者写成长方形的 (x,y),但一般都用正方形,所以可以只写一个数。 。
batch为1。
在这里插入图片描述
下图,输入数据 参数多了一个batch,表示当前是第几个batch,后面参数依次 通道数 ,w 和 h。
在这里插入图片描述
卷积层设置:
下图 输入通道数量,输出通道数量。卷积核大小。
在这里插入图片描述

然后 下图 将输入数据放到卷子层得到输出的卷积结果:
参数解读 [1,5,100,100] 第一个batch 通道数5 。w * h 为100 *100。(输入为 100 * 100 卷积核 3 * 3所以 输出为 100-2=98 * 98)
最后哪一行输出为卷积层权重的形状:
[10,5,3,3] 输出通道为 10 输入通道为 5 ,卷积核 3 * 3。
在这里插入图片描述
因为卷积核扫描的遍历原数据,所以卷积核的大小和原来数据的大小 即 w和h是不关心的,多扫描一点而已。仅仅关心两者的通道数,跟前面说的那样,必须一样,

在前面的例子中使用了下图的数据:
在这里插入图片描述
即输入 5 * 5 卷积核 3 * 3 得到结果为 3 * 3.
现在希望 输入和卷积核的数据不变 得到的结果变成55的,可以将输入数据的外围添加0,添加一圈变成 77的数据维度(一圈嘛 上下左右都多出来一行,实际上就是横向多了两行,竖向多了两行。),这样和33卷积则可以变成 55的数据了。

这个过程叫 padding =1 即添加了一圈,添加的一般为0,也可以是别的。

小公式: 如果要得到输入维度和卷积后的输出维度一样,则可以直接拿卷积核 的 大小 整除 2。
比如 5 * 5 的输入 卷积核 3 * 3 要得到 5 * 5 则padding = 3 / 2 = 1圈即可。

添加后可以得到 下图:

在这里插入图片描述
使用代码实现上面的过程:

import torch

input = [
    3, 4, 6, 5, 7,
    2, 4, 6, 8, 2,
    1, 6, 7, 8, 4,
    9, 7, 4, 6, 2,
    3, 7, 5, 4, 1
]
# 输入数据设置
input = torch.Tensor(input).view(1, 1, 5, 5)  # batch  通道数, w 和 h
# 卷积层设置
conv_layer = torch.nn.Conv2d(1, 1, kernel_size=3, padding=1, bias=False)  # 输入通道,输出通道
# 卷积核设置
kernel = torch.Tensor([1, 2, 3, 4, 5, 6, 7, 8, 9]).view(1, 1, 3, 3)  # 输出通道数,输入通道数  w和h
# 做出来的张量给卷积层的权重   即初始化权重
conv_layer.weight.data = kernel.data
# 将输入值给卷积层去计算得到结果
out = conv_layer(input)
print(out)

得到输出:
tensor([[[[ 91., 168., 224., 215., 127.],
          [114., 211., 295., 262., 149.],
          [192., 259., 282., 214., 122.],
          [194., 251., 253., 169.,  86.],
          [ 96., 112., 110.,  68.,  31.]]]], grad_fn=<ThnnConv2DBackward0>)

设置步长的话:

conv_layer = torch.nn.Conv2d(1, 1, kernel_size=3,padding=1,stride=2, bias=False)  # 输入通道,输出通道 padding 步长  是否设置偏置量。

池化层(采样、特征提取)

下采样
用的比较多的叫 MaxPooling 也叫最大池化层。

在这里插入图片描述
最大池化层没有权重, 默认 stride = 2 即步长为2。
将原图分成 2 * 2 一组 分成4组 :
在这里插入图片描述
在每组中找最大值 平成 2* 2 的矩阵得到结果:
在这里插入图片描述
在做 MaxPooling时 通道数量不变。图像的w和h缩为原来的一半。

整个的流程:
在这里插入图片描述
初始值为 (batch,1,28,28) 即 batch样本数 ,1通道 28*28像素。

  • 经过第一层卷积层 卷积核为 (5*5 输出为10通道),则输出的w和h 变为 28-5+1 = 24 即第一层卷积输出(batch,1,24,24)
  • 经过第一层池化层 batch和通道数不变,w和h折半,所以输出(batch,1,12,12)
  • 再次经过第二层卷积层,卷积核(5*5 输入10通道,输出20通道),则输出为(batch,20,8,8)
  • 再次经过第二层池化层,w和h折半,所以输出(batch,20,4,4)
  • 最后拿去线性层进行操作 此时的输入数为 4 * 4 *20 = 320个元素,最后变成 (bitch,10)。

之前用全连接训练的图像改成卷积训练。
流程图:
在这里插入图片描述
这里 由于池化层没有权重,所以可以只用一个变量,而每一次的卷积层是有权重的,所以每次都要用新的变量。

最后一次池化层到线性层那一块,要注意形状的改变,由 (batch,20,4,4) 变成 (batch,320)。然后扔到交叉熵里面训练,看损失。所以最后一层不用relu激活,因为交叉熵里面自动激活。

完整代码(CPU版和GPU版)

CPU版本:
完整代码:

import torch
from torch import optim
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F

batch_size = 64
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)


# 模型类设计
class DiabetesDataset(torch.nn.Module):
    def __init__(self):
        super(DiabetesDataset, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)
        self.pooling = torch.nn.MaxPool2d(2)
        self.fc = torch.nn.Linear(320, 10)

    def forward(self, x):
        batch_size = x.size(0)
        x = F.relu(self.pooling(self.conv1(x)))  # 第一轮卷积池化激活
        x = F.relu(self.pooling(self.conv2(x)))  # 第二轮卷积池化激活
        x = x.view(batch_size, -1)  # 吧数据变成线性层接受的
        x = self.fc(x)  # 去线性层
        return x


model = DiabetesDataset()



# 损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)


def train(epoch):
    runing_loss = 0.0
    for i, data in enumerate(train_loader):
        x, y = data
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
    # 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000

    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch + 1, runing_loss / 10000))


def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            x, y = data
            pre_y = model(x)
            # 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,
            # 我们要拿到最大的那个概率和其对应的下标。
            j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度

            total += y.size(0)  # 统计方向0上的元素个数 即样本个数
            correct += (pre_y == y).sum().item()  # 张量之间的比较运算
    print("第%d轮测试结束,当前正确率:%d %%" % (epoch + 1, correct / total * 100))


if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test(epoch)



结论:
这是第 1轮训练,当前损失值 0.030401轮测试结束,当前正确率:96 %
这是第 2轮训练,当前损失值 0.009372轮测试结束,当前正确率:97 %
这是第 3轮训练,当前损失值 0.006903轮测试结束,当前正确率:98 %
这是第 4轮训练,当前损失值 0.005744轮测试结束,当前正确率:98 %
这是第 5轮训练,当前损失值 0.005035轮测试结束,当前正确率:98 %
这是第 6轮训练,当前损失值 0.004566轮测试结束,当前正确率:98 %
这是第 7轮训练,当前损失值 0.004177轮测试结束,当前正确率:98 %
这是第 8轮训练,当前损失值 0.003838轮测试结束,当前正确率:98 %
这是第 9轮训练,当前损失值 0.003609轮测试结束,当前正确率:98 %
这是第 10轮训练,当前损失值 0.0033510轮测试结束,当前正确率:98 %

昨天用线性模型跑这个数据,正确率最高也就97% 。
今天用刚学的卷积池化层加入,正确率可以达到98% 。

GPU版本
完整代码:

import torch
from torch import optim
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F

batch_size = 64
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
])

train_dataset = datasets.MNIST(root='../dataset/mnist/', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/', train=False, download=True, transform=transform)
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size)


# 模型类设计
class DiabetesDataset(torch.nn.Module):
    def __init__(self):
        super(DiabetesDataset, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5)
        self.pooling = torch.nn.MaxPool2d(2)
        self.fc = torch.nn.Linear(320, 10)

    def forward(self, x):
        batch_size = x.size(0)
        x = F.relu(self.pooling(self.conv1(x)))  # 第一轮卷积池化激活
        x = F.relu(self.pooling(self.conv2(x)))  # 第二轮卷积池化激活
        x = x.view(batch_size, -1)  # 吧数据变成线性层接受的
        x = self.fc(x)  # 去线性层
        return x


model = DiabetesDataset()

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)  # 将训练模型放到GPU

# 损失函数
criterion = torch.nn.CrossEntropyLoss()
# 优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)


def train(epoch):
    runing_loss = 0.0
    for i, data in enumerate(train_loader):
        x, y = data
        x, y = x.to(device), y.to(device)
        # 清零 正向传播  损失函数  反向传播 更新
        optimizer.zero_grad()
        y_pre = model(x)
        loss = criterion(y_pre, y)
        loss.backward()
        optimizer.step()
        runing_loss += loss.item()
    # 每轮训练一共训练1W个样本,这里的runing_loss是1W个样本的总损失值,要看每一个样本的平均损失值, 记得除10000

    print("这是第 %d轮训练,当前损失值 %.5f" % (epoch + 1, runing_loss / 10000))


def test(epoch):
    correct = 0
    total = 0
    with torch.no_grad():
        for data in test_loader:
            x, y = data
            x, y = x.to(device), y.to(device)
            pre_y = model(x)
            # 这里拿到的预测值 每一行都对应10个分类,这10个分类都有对应的概率,
            # 我们要拿到最大的那个概率和其对应的下标。
            j, pre_y = torch.max(pre_y.data, dim=1)  # dim = 1 列是第0个维度,行是第1个维度

            total += y.size(0)  # 统计方向0上的元素个数 即样本个数
            correct += (pre_y == y).sum().item()  # 张量之间的比较运算
    print("第%d轮测试结束,当前正确率:%d %%" % (epoch + 1, correct / total * 100))


if __name__ == '__main__':
    for epoch in range(10):
        train(epoch)
        test(epoch)


其实就多了两三行代码,先选择GPU,然后吧训练模型放GPU上,再吧数据放GPU上就ok了~。

  • 4
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深度不学习!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值