CNN汇报笔记1

从全连接层过渡到卷积层

假设对ImageNet数据集(平均每个图片大概是300*300大小,总共是1000类)使用MLP来训练,由于输入是300*300输出为1000类,所以可以假定第一个隐藏层的输出为10000,此时学习参数已经为10亿(要对所有的输入元素做加权和,所以共有300*300*10000个学习参数),所以可以发现单纯用mlp来处理的话效果不太好。

从一个图片中识别一个物体有两个原则
  1. Trainslation invaiance平移不变性:对于同一物体的不同位置的图像,神经网络能够给出相同的识别结果,为使神经网络具有平移不变性

  2. locality本地性:找一个物体不需要找特别远的像素,像素与周围的像素是有相关性的,与较远的像素相关性比较差

    在有了这两个原则的话就可以将全连接做的简单一点

在这里插入图片描述

卷积层

卷积就是利用了上面的平移不变性和本地性两个性质

  1. 本地性:在卷积层的一个输出只有一个k*k大小的一个窗口的图片(全连接是所有输入的元素做加权和)
  2. 平移不变性:如下图,不需要重新给一个权重,蓝色的输出与黄色的输出共用一组权重,权重之间是共享的

在有了这两个性质之后,我们卷积的可学习参数就不与输入大小和输出大小相关,只跟k即窗口相关,输出只要对一个k*k的窗口里面做加权和,每一个输出都是重用这个k*k

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXzeZnMg-1692241455103)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230728170902897.png)]

k*k称作卷积核kernel,通常会被学习成去识别一个图片中的模式


通常会学习多组不同模式得到一个多通道的输出

激活层

单层感知机又称线性模型,MLP为多层感知机,简单的单层感知机的叠加不可以称为MLP,所以要在叠加的基础上添加非线性,而非线性就是激活函数,常见的激活函数包含下面两个(对每个元素做一个激活)

在这里插入图片描述

池化层

每一次在k*k的窗口中计算均值(平均池化)或者最大值(最大池化),例如最大池化层:输入在k个范围之内偏移的话,经过最大池化后的输出都是k*k计算出的最大值

▲ 图1.3.1 最大值池化一是均值池化示意图

全连接层

总共分为两种:线性回归和softmax回归
线性回归:最后只有一个输出,回归问题
Softmax回归:最后又m个(类别数为m)输出+softmax(概率),分类问题

CNN

卷积神经网络是一个神经网络,将各种卷积层堆积起来抽取图片中的空间信息,激活层用在卷积层的每一个之后,卷积层可以看作特殊的全连接层,如果不用激活的话就是一个线性模型,利用池化层可以得到那些对于位置没那么敏感的输出(卷积的输出是一个四维,全连接的输入是二维)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7z37Xrc1-1692241455103)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230728195056708.png)]
在这里插入图片描述

第一个代码运行结果
Test set: Avg. loss: 0.1914, Accuracy: 9443/10000 (94%)
Test set: Avg. loss: 0.1255, Accuracy: 9596/10000 (96%)
Test set: Avg. loss: 0.1018, Accuracy: 9671/10000 (97%)
Test set: Avg. loss: 0.0874, Accuracy: 9723/10000 (97%)
Test set: Avg. loss: 0.0723, Accuracy: 9775/10000 (98%)
Test set: Avg. loss: 0.0695, Accuracy: 9778/10000 (98%)
Test set: Avg. loss: 0.0626, Accuracy: 9806/10000 (98%)
Test set: Avg. loss: 0.0585, Accuracy: 9804/10000 (98%)
Test set: Avg. loss: 0.0554, Accuracy: 9811/10000 (98%)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTXsA6cI-1692241455104)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230727231007742.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mCwOyuFl-1692241455104)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230727231020134.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3pnBu4a2-1692241455105==,size_50)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230727231030226.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jGceCF06-1692241455105)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230727231041290.png)]

MNIST中包含70000张手写数字图像:60000张用于训练,10000张用于测试,图像是灰度的28*28像素的并且居中,可以减少预处理和加快运行

准备数据集

首先定义超参数,epoch数量定义整个训练数据集循环的次数

n_epochs = 3 #训练循环次数
batch_size_train = 64 #批次大小
batch_size_test = 1000
learning_rate = 0.01 #学习率为 0.01,每次更新的步长大小
momentum = 0.5 #用于加速网络的收敛速度和稳定训练过程
log_interval = 10 #每隔 10 个训练批次输出一次日志信息
random_seed = 1
torch.manual_seed(random_seed)

加载数据集

train_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('./data/', train=True, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,)) # MNIST数据集的全局平均值和标准偏差,对图像数据进行标准化处理(即减均值除以标准差)
                               ])),
    batch_size=batch_size_train, shuffle=True)
test_loader = torch.utils.data.DataLoader(
    torchvision.datasets.MNIST('./data/', train=False, download=True,
                               transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,))
                               ])),
    batch_size=batch_size_test, shuffle=True)

其中以下的代码表示对图像进行标准化处理torchvision.transforms.Composetorchvision.transforms.ToTensor()将 图像转换为 PyTorch 中的张量格式

 transform=torchvision.transforms.Compose([
                                   torchvision.transforms.ToTensor(),
                                   torchvision.transforms.Normalize(
                                       (0.1307,), (0.3081,)) # MNIST数据集的全局平均值和标准偏差,对图像数据进行标准化处理(即减均值除以标准差)
                               ])

MNIST代码

pytorch数据加载

深度学习中数据不能一次性放到模型中训练,应该利用数据加载对数据进行顺序打乱,分批,预处理操作,pytorch中提供两个类Dataset/Dataloader

Dataset(数据集类):常用的是__getitem__获取索引下标和__len__获取总个数

Dataloader(数据加载器类):常用来打乱shuffle/分批数据batch_size

# 训练数据和测试数据
train_data = MNIST(root='./Data',train=True, download=True,transform=transform1)
train_loader = DataLoader(train_data,batch_size=batch_size_test, shuffle=True)
test_data = MNIST(root='./Data', train=False, download=True,transform=transform1)
test_loader = DataLoader(test_data,shuffle=False,batch_size=batch_size_test)
print(train_data[0])
预处理
transforms.Compose 图像变换通道
torchvision.transforms.ToTensor() 将Image或者numpy转换为tensor
torchvision.transforms.Normalize((0.1307,), (0.3081,)) 接受的是一个元组作为参数,而元组只有一个元素时需要在后面添加一个逗号来表示它是一个元组,而不是一个括号内的单个值。归一化处理,MNIST数据集的均值为0.1307,标准偏差为0.3081,标准化处理即减均值除以标准差

归一化加快梯度下降最优解的速度,也加快训练网络的收敛性,不进行归一化,那么特征向量中不同特征的取值相差较大,导致目标函数变扁,进行梯度下降的时候梯度下降就会偏离最小值方向,走很多弯路

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QEiRF8XU-1692241455106)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230729111522419.png)]

模型(没有使用到卷积,可以自行添加)
class Model(nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = nn.Linear(784,256)
        self.linear2 = nn.Linear(256,64)
        self.linear3 = nn.Linear(64,10) # 10个手写数字对应10个输出

    def forward(self, x):
        x = x.view(-1,784)  # 变形
        x = torch.relu(self.linear1(x))
        x = torch.relu(self.linear2(x))
        x = torch.relu(self.linear3(x))
        return x

总共三层卷积

图片是28*28,所以特征就是28*28=784,而最终的输出是0-9的10个数字,所以最终输出为10,def forward定义模型向前传播的过程

x 是一个张量,它的形状为 [batch_size, channels, height, width],-1 PyTorch 自动计算该维度的大小,以保证张量的总大小不变
加载保存模型参数
if os.path.exists('./model/model.pkl'):
    model.load_state_dict(torch.load('./model/model.pkl'))  # 加载保存模型的参数

model.load_state_dict(state_dict) 方法接受一个字典作为参数,将字典中保存的参数加载到模型中,torch.load(filepath) 方法将其加载回来,并将其赋值给模型的 state_dict 属性,通过 model.load_state_dict(state_dict) 方法将模型的参数加载到模型中

Train()
def train(epoch):
    for index, data in enumerate(train_loader):
        input, target = data  # input为输入数据,target为标签
        optimizer.zero_grad()  # 梯度清零
        y_predict = model(input)
        loss = criterion(y_predict, target)
        loss.backward()  # 反向传播求导得出模型参数梯度,通过梯度得出最小损失
        optimizer.step()  # 更新梯度
        if index%100==0:  # 每100次保存一次模型,打印损失
            torch.save(model.state_dict(), './model/model.pkl')
            torch.save(optimizer.state_dict(), './model/optimizer.pkl')
            print(loss.item())

enumerate() 函数返回的是一个枚举对象,它可以用于循环遍历可迭代对象,同时获取每个元素的索引和对应的元素值,使用 enumerate() 函数循环遍历一个可迭代对象时,每次迭代都会返回一个元组,其中第一个元素是当前元素的索引,第二个元素是当前元素的值。

Test()
def test():
    correct = 0  # 正确的个数
    total = 0  # 总数 correct/total得到识别率
    with torch.no_grad():  # 不需要计算梯度时关闭 PyTorch 中的自动求导功能
        for data in test_loader:
            input, target = data
            output = model(input)  # output输出10个预测值,其中最大的即为预测的数
            _,predict = torch.max(output.data, dim=1)  # dim=1代表计算第二个维度,第一个维度是样本第二个维度是特征返回一个元组,第一个为最大值,第二个为最大值的下标
            total += target.size(0)  # target形状是(batch_size,1),size(0)代表取出batch_size的值
            correct += (predict==target).sum().item()
    print(correct/total)
主函数:训练
if __name__=='__main__':
    for i in range(5):
         train(i)
         test()

输出:损失值10个一组+识别率,共5组

0.6117006540298462
0.6476180553436279
0.5037038922309875
0.251853883266449
0.5397297143936157
0.2518479526042938
0.2878449857234955
0.5396743416786194
0.2518465518951416
0.2878594696521759
0.7862
0.6116890907287598
0.683617353439331
0.7195902466773987
0.4677504301071167
0.46773219108581543
0.32383614778518677
0.4318012595176697
0.2878265380859375
0.4677162170410156
0.539671778678894
0.7864
0.5037019848823547
0.4317892789840698
0.35979288816452026
0.35982105135917664
0.5756522417068481
0.5756645202636719
0.5756464600563049
0.3957996964454651
0.5036997199058533
0.46776461601257324
0.7862
0.431740939617157
0.5037461519241333
0.43175026774406433
0.2518813908100128
0.5397460460662842
0.7196362018585205
0.359783411026001
0.3238505721092224
0.4317675232887268
0.4317689538002014
0.786
0.4317931532859802
0.3957788646221161
0.683612585067749
0.2878553569316864
0.21588340401649475
0.6835839748382568
0.5037187337875366
0.7195822596549988
0.5037770867347717
0.5397251844406128
0.7862
主函数:验证
    img = Image.open(r"E:\py\stitp\one.png").convert("L")
    img = transform1(img) # 预处理
    img.view(-1,784)
    result = model(img)
    a, predict = torch.max(result.data, dim=1)
    print(result)  # 只是一个概率分布,没有softmax
    print(a)
    print("结果是:",predict.item())

输入:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OTJiDteg-1692241455106)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20230729173343769.png)](可以直接使用画图工具随时更改)

输出结果:

tensor([[ 0.0000,  0.0000,  0.0000, 33.2394,  0.0000,  0.0000,  0.0000,  0.0000,
          0.0000,  0.0000]], grad_fn=<ReluBackward0>)
tensor([33.2394])
结果是: 3

总结

  1. .convert("L")以为输入图片已经是黑白的就没必要转换,但是运行的时候报错RuntimeError: a Tensor with 3 elements cannot be converted to Scalar代表输入张量形状是3,但是我们的输入应该是2,所以我们输入的图片即使是黑白的但是基础还是RGB所以要在代码后面添加.convert("L")

    img = Image.open(r"E:\py\stitp\one.png").convert("L")
    
  2. 损失太高:

    1. 调整学习率:先使用一个较大的学习率进行训练,观察损失值的变化,然后根据损失值的情况逐步调整学习率大小。
    2. 增加训练轮数:如果模型的损失值太高,可能是因为模型还没有充分收敛。因此,可以尝试增加训练轮数,让模型有更多的时间进行训练和收敛。
  3. torch.max

    torch.max() 是一个 PyTorch 函数,用于沿着指定的维度计算张量中的最大值。具体来说,给定一个张量,torch.max(input, dim=None, keepdim=False, *, out=None) -> (Tensor, Tensor) 函数返回一个元组,包含两个张量:

    • 第一个张量是沿着指定维度的最大值。如果 keepdim=False,则返回的张量的形状是 input.shape[:dim] + input.shape[dim+1:],即在去掉指定维度后,其它维度和 input 张量相同。如果 keepdim=True,则返回的张量形状与 input 张量相同,但指定维度的大小为 1。
    • 第二个张量是沿着指定维度的最大值的索引。如果 keepdim=False,则返回的张量的形状与第一个张量相同。如果 keepdim=True,则返回的张量形状与 input 张量相同,但指定维度的大小为 1。
    import torch
    
    # 创建一个 2x3 的张量
    x = torch.tensor([[1, 2, 3], [4, 5, 6]])
    
    # 沿着第二个维度计算最大值和索引
    max_values, max_indices = torch.max(x, dim=1)
    
    # 打印结果
    print("max_values:", max_values)
    print("max_indices:", max_indices)
    
    #输出结果
    #max_values: tensor([3, 6])
    #max_indices: tensor([2, 2])
    
  4. torch.no_grad()

    torch.no_grad() 是一个上下文管理器(Context Manager),它可以用于在不需要计算梯度时关闭 PyTorch 中的自动求导功能,以提高代码的执行效率

    import torch
    import torch.nn as nn
    
    # 创建模型和损失函数
    model = nn.Linear(10, 1)
    criterion = nn.MSELoss()
    
    # 前向传播
    with torch.no_grad():
        inputs = torch.randn(1, 10)
        labels = torch.randn(1, 1)
        outputs = model(inputs)
        loss = criterion(outputs, labels)
    
    # 打印损失
    

需要计算梯度时关闭 PyTorch 中的自动求导功能,以提高代码的执行效率

import torch
import torch.nn as nn

# 创建模型和损失函数
model = nn.Linear(10, 1)
criterion = nn.MSELoss()

# 前向传播
with torch.no_grad():
    inputs = torch.randn(1, 10)
    labels = torch.randn(1, 1)
    outputs = model(inputs)
    loss = criterion(outputs, labels)

# 打印损失
print(loss)

参考:【【深度学习】简单的minist手写数字识别程序讲解】 https://www.bilibili.com/video/BV1p34y1C7BK/?share_source=copy_web&vd_source=6670138018dcd687b3ba1f02e7455222

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值