Kiba518

Kiba518

沈阳-架构-开发。

Fork me on GitHub

零基础学习人工智能—Python—Pytorch学习(八)

前言

本文介绍卷积神经网络的上半部分。
其实,学习还是需要老师的,因为我自己写文章的时候,就会想当然,比如下面的滑动窗口,我就会想当然的认为所有人都能理解,而实际上,我们在学习的过程中之所以卡顿的点多,就是因为学习资源中想当然的地方太多了。

概念

卷积神经网络,简称CNN, 即Convolutional Neural Network的缩写。

滤波器/卷积核(Filter/Kernels)

卷积核是一个小矩阵(通常是3x3、5x5等),它在输入图像上滑动(即移动),并与图像的局部区域进行矩阵乘法(点积)操作。结果是一个单值,这个值代表了该局部区域的某种特征。
点积就是内积,就是np.dot函数,内积是个值,就是两个矩阵对应项相乘,在相加
例如。a=[2,3] 和 b=[4,5],它们的点积是a⋅b=(2×4)+(3×5)=8+15=23
点积的意义是a⋅b=∥a∥∥b∥cosθ,意思是说,点积等于a向量的模乘以b向量的模乘以ab的夹角θ的cos的值
向量的模就是向量的长度,v=[3,4],因为勾股定理,c²=a²+b²,所以∥v∥=c=根号下a²+b²=根号下9+16=根号下25=5
例。rgb图,是3通道,卷积核会在3个通道上都进行卷积操作,最后形成一个特征图。

卷积核的尺寸

如果尺寸是 5×5,那么滑动窗口的大小也是 5×5。
image

特征图(Feature Map)

当一个卷积核(或滤波器)滑动在输入图像上时,它会在每一个位置计算卷积核与输入图像区域的点积,结果是一个标量。通过滑动整个图像,得到一组标量值,这些值构成了一个新的二维矩阵,这个矩阵就是特征图。
在CNN中,使用越多的卷积核,意味着提取的特征图越多,因此卷积核越大就可以得到的越丰富的特征。
更多的卷积核意味着更多的计算和内存消耗。因此,在选择卷积核数量时,也要考虑硬件资源的限制。

最大池化层(Max Pooling Layer)

是卷积神经网络(CNN)中常用的下采样(或降采样)技术。它用于减小特征图的尺寸,从而减少计算量,并有助于控制模型的复杂度(防止过拟合)。
最大池化操作使用一个固定大小的窗口(通常是2x2或3x3),在特征图上滑动。
在窗口覆盖的区域内,最大池化层会选择该区域的最大值作为输出。
步幅决定了池化窗口在特征图上滑动的步长。步幅为2意味着窗口每次移动2个像素。
每次池化操作生成的特征图尺寸会比输入特征图小。池化操作会减少特征图的宽度和高度,但保持深度(通道数)不变。
例,4x4 的输入特征图如下

1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

使用 2x2 的最大池化窗口和步幅为 2,池化过程如下:
池化窗口覆盖 1 2 5 6,最大值为 6
池化窗口覆盖 3 4 7 8,最大值为 8
池化窗口覆盖 9 10 13 14,最大值为 14
池化窗口覆盖 11 12 15 16,最大值为 16
得到的输出特征图为:

6 8
14 16

结合代码理解

结合下面的代码理解上面的概念。

import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
# device config
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# hyper parameters
batch_size = 4
learning_rate = 0.001
num_epochs = 0

# dataset has PILImage images of range [0, 1].# We transform them to Tensors of normalized range [-1, 1]
# transforms.ToTensor():将PIL图像或numpy数组转换为PyTorch张量,并将值范围从[0,1]变为[0,255]。
# transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)):对图像进行归一化处理,将图像的像素值调整到[-1,1]范围。
transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])


train_dataset = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform)

test_dataset = torchvision.datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform)

train_loader = torch.utils. data.DataLoader(
    dataset=train_dataset, batch_size=batch_size, shuffle=True)

test_loader = torch.utils.data.DataLoader(
    dataset=test_dataset, batch_size=batch_size, shuffle=False)


print('每份100个,被分成多了份', len(train_loader))

def imshow(img):
    img = img / 2 + 0.5  # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# get some random training images
dataiter = iter(train_loader)
images, labels = dataiter.__next__()
# show images
imshow(torchvision.utils.make_grid(images))

# nn.Conv2d 是 PyTorch 用于定义二维卷积层的类
# 三个参数分别为 in_channels、out_channels 和 kernel_size
# in_channels (输入通道数):
# 值=3 这是输入图像的通道数。对于彩色图像,通常有3个通道(对应于RGB),因此这里的值为3。如果输入的是灰度图像,通常只有1个通道。
# 对于彩色图像(RGB),输入图像有3个通道。卷积核对每个通道独立进行操作,然后将这些结果相加,得到输出特征图。
# 输出特征图的数量由卷积核的数量决定。如果你有多个卷积核,它们会捕捉输入图像中的不同特征,每个卷积核生成一个特征图
# out_channels (输出通道数):
# 值=6 这是卷积层输出的通道数,也称为卷积核的数量。这个参数决定了卷积操作后生成多少个不同的特征图。在本例中,卷积层会生成6个特征图,也就是说会有6个不同的卷积核应用于输入图像。
# kernel_size (卷积核大小):
# 值=5 这是卷积核的尺寸,表示卷积核的宽度和高度。这里使用的是 5x5 的卷积核。这意味着每个卷积核会查看输入图像的 5x5 像素区域,并通过滑动窗口方式在整个图像上进行卷积操作。
# conv1:第一个卷积层,将输入的3通道图像(RGB)通过一个5x5的卷积核,生成6个输出通道。这里,卷积层使用 6 个卷积核,每个卷积核会生成一个特征图。因此,该卷积层的输出是 6 个特征图,特征图的深度(通道数)为 6。
conv1 = nn.Conv2d(3, 6, 5)
# pool:最大池化层,将特征图的尺寸缩小一半。
# 第一个参数 (2): 池化窗口的大小。这表示池化操作将应用于一个 2x2 的窗口上。池化窗口决定了在特征图上进行池化操作的区域大小。 
# 第二个参数 (2): 池化的步幅(stride)。步幅决定了池化窗口在特征图上滑动的步长。步幅为 2 意味着池化窗口每次移动 2 个像素。
pool = nn.MaxPool2d(2, 2)
# conv2:第二个卷积层,将6个通道的输入特征图通过一个5x5的卷积核,生成16个输出通道。
conv2 = nn.Conv2d(6, 16, 5)
# torch.Size([4, 3, 32, 32])  一个批次是4个图像,一个图像3个通道,一个通道行32个点,一个通道列32个点
print(images.shape)


x = conv1(images)
# torch.Size([4, 6, 28, 28]) 一个批次是4个图像,输出了6个特征图,一个特征图行28个点,一个特征图列28个点  
# 公式是(w-f+2p)/s+1 w是图像的宽 f是卷积的宽 p是padding s是stride步幅
# 步幅(stride, s): 默认为 1,意味着卷积核在图像上每次移动一个像素。
# 填充(padding, p): 默认为 0,意味着没有额外的像素填充在图像的边界。
print(x.shape)
x = pool(x)
# torch.Size([4, 6, 14, 14]) pooling layer 定义的是2*2 所以这个正好变成14行 14列
# print(x.shape)
x = conv2(x)
# torch.Size([4, 16, 10, 10])  (w-f+2p)/s+1=(14-5+2*0)/1+1=9+1=10
print(x.shape)
x = pool(x)  
# torch.Size([4, 16, 5, 5]) pooling layer 定义的是2*2 所以这个正好变成5行 5列
print(x.shape)

传送门:
零基础学习人工智能—Python—Pytorch学习—全集


注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!



若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

https://www.cnblogs.com/kiba/p/18375380

posted @ 2024-08-23 10:30  kiba518  阅读(241)  评论(0编辑  收藏  举报