第三周作业:卷积神经网络(Part 1)

深度学习计算

层和块

单个神经元:(1)接受一些输入;(2)生成相应的标量输出;(3)具有一组相关 参数(parameters)。
层:(1)接受一组输入,(2)生成相应的输出,(3)由一组可调整参数描述。
在这里插入图片描述
自定义块
每个块必须提供的基本功能:
1.将输入数据作为其正向传播函数的参数。
2.通过正向传播函数来生成输出。请注意,输出的形状可能与输入的形状不同。例如,我们上面模型中的第一个全连接的层接收任意维的输入,但是返回一个维度256的输出。
3.计算其输出关于输入的梯度,可通过其反向传播函数进行访问。通常这是自动发生的。
4.存储和访问正向传播计算所需的参数。
5.根据需要初始化模型参数。
顺序块
需要定义两个关键函数: 1. 一种将块逐个追加到列表中的函数。 2. 一种正向传播函数,用于将输入按追加块的顺序传递给块组成的“链条”。
在正向传播函数中执行代码
到目前为止,我们网络中的所有操作都对网络的激活值及网络的参数起作用。然而,有时我们可能希望合并既不是上一层的结果也不是可更新参数的项。我们称之为常数参数(constant parameters)。

参数管理

选择了架构并设置了超参数之后,我们就进入了训练阶段。此时,我们的目标是找到使损失函数最小化的参数值。经过训练后,我们将需要使用这些参数来做出未来的预测。大多数情况下,我们可以忽略声明和操作参数的具体细节,而只依靠深度学习框架来完成繁重的工作。然而,当我们离开具有标准层的层叠架构时,我们有时会陷入声明和操作参数的麻烦中。
参数管理主要介绍:
1.访问参数,用于调试、诊断和可视化。
2.参数初始化。
3.在不同模型组件间共享参数。
参数访问
目标参数
每个参数都表示为参数(parameter)类的一个实例。要对参数执行任何操作,首先我们需要访问底层的数值。
参数是复合的对象,包含值、梯度和额外信息。除了值之外,我们还可以访问每个参数的梯度。
一次性访问所有参数
当我们处理更复杂的块(例如,嵌套块)时,需要递归整个树来提取每个子块的参数。同时,这为我们提供了另一种访问网络参数的方式。
从嵌套块收集参数
定义一个生成块的函数(可以说是块工厂),然后将这些块组合到更大的块中。因为层是分层嵌套的,所以我们也可以像通过嵌套列表索引一样访问它们。
参数初始化
默认情况下,PyTorch会根据一个范围均匀地初始化权重和偏置矩阵,这个范围是根据输入和输出维度计算出的。PyTorch的nn.init模块提供了多种预置初始化方法。
内置初始化
首先调用内置的初始化器;还可以将所有参数初始化为给定的常数(比如1);还可以对某些块应用不同的初始化方法。
自定义初始化
若深度学习框架没有提供我们需要的初始化方法,我们使用以下的分布为任意权重参数ω定义初始化方法:
在这里插入图片描述
1.实现了一个my_init函数来应用到net
2.可以直接设置参数
参数绑定
目的:在多个层间共享参数。
参数绑定的层:不仅值相等,而且由相同的张量表示。因此,如果我们其中一个参数会随另一个参数的改变而改变。
梯度变化情况:当参数绑定时,由于模型参数包含梯度,因此在反向传播期间参数绑定的层的梯度会加在一起。

自定义层

不带参数的层
要构建不带参数的层,我们只需继承基础层类并实现正向传播功能。
带参数的层
我们可以使用内置函数来创建参数,这些函数提供一些基本的管理功能。比如管理访问、初始化、共享、保存和加载模型参数。这样做的好处之一是,我们不需要为每个自定义层编写自定义序列化程序。

读写文件

加载和保存张量
对于单个张量,我们可以直接调用load和save函数分别读写它们。这两个函数都要求我们提供一个名称,save要求将要保存的变量作为输入。
1.可以将存储在文件中的数据读回内存。
2.可以存储一个张量列表,然后把它们读回内存。
3.可以写入或读取从字符串映射到张量的字典。当我们要读取或写入模型中的所有权重时,这很方便。
加载和保存模型参数
深度学习框架提供了内置函数来保存和加载整个网络。需要注意一点,这将保存模型的参数而不是保存整个模型。

卷积神经网络

从全连接层到卷积

全连接层
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结
在这里插入图片描述

图像卷积

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现
互相关运算
在这里插入图片描述
注:X:输入 K:核矩阵 h、w: 行数列数
Y(输出):输入的高-核的高度+1,输入的宽度-核的宽度+1
for for遍历所有输出元素

验证上述二维互相关运算的输出
在这里插入图片描述
实现二维卷积层
在这里插入图片描述
卷积层的简单应用:检测图像中不同颜色的边缘
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
卷积核K只能检测垂直边缘
在这里插入图片描述
学习由X生成Y的卷积核
在这里插入图片描述
注:输入输出均为1
Reshape增加两个维度:通道数,批量大小数,均为1
L:loss
访问Weight.data操作:inplace操作?

所学的卷积核的权重张量
在这里插入图片描述

填充和步幅

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
代码实现
填充(在所有侧边填充1个像素)
在这里插入图片描述
注:定义一个函数
在维度前面加入通道数、批量大小数
调用conv2d函数
四维拿掉前两维得到矩阵的输出

填充不同的高度和宽度
在这里插入图片描述
注:Padding(填充)行数2 列数1
输出输入还一样8
8*

将高度和宽度步幅设置为2
在这里插入图片描述
注:stride(步幅)=2
输出4
4 减半*

复杂例子

在这里插入图片描述
QA
1.超参数重要程度:核大小最重要,其次填充,步幅;
2.卷积核3*3居多;
3.多个输入和输出通道,重要超参数:通道数。

多输入多输出通道

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
变化:卷积核多了Co(output)。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
代码实现
实现多通入通道互相关运算
在这里插入图片描述
注:X、K都是3d,zip起来,for拿出对应输入通道的小矩阵,做互相关运算,然后求和。

验证互相关运算的输出
在这里插入图片描述
计算多通道的输出的互相关函数
在这里插入图片描述
在这里插入图片描述
1*1卷积
在这里插入图片描述

池化层

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述
代码实现
实现池化层的正向传播
在这里插入图片描述
验证二维最大汇聚层的输出
在这里插入图片描述
验证平均汇聚层
在这里插入图片描述
填充和步幅
在这里插入图片描述
深度学习框架中的步幅与池化窗口的大小相同
在这里插入图片描述
填充和步幅可以手动设定
在这里插入图片描述
可以设定一个任意大小的矩形池化窗口,并分别设定填充和步幅的高度和宽度
在这里插入图片描述
池化层在每个输入通道上单独运算
在这里插入图片描述在这里插入图片描述

LeNet

在这里插入图片描述在这里插入图片描述在这里插入图片描述
代码实现
LeNet(LeNet-5)由两个部分组成: * 卷积编码器:由两个卷积层组成; * 全连接层密集块:由三个全连接层组成。
在这里插入图片描述
注:Reshape() 批量数不变 通道数变成1 28×28
卷积层nn.Conv2d(输入1,输出6,kernel5×5,填充2) 加入nn.Sigmoid()激活函数
均值池化层 nn.AvgPool2d(kernel2×2 步幅2)
卷积层 nn.Conv2d(输入6,输出16,kernel5×5) 加入nn.Sigmoid()激活函数
均值池化层 nn.AvgPool2d(kernel2×2 步幅2) nn.Flatten():第一位批量保持,后面拉成一样的维度
nn.Linear(16×5×5,120) nn.Sigmoid()
nn.Linear(120,84) nn.Sigmoid()
nn.Linear(84,10)

检查模型
在这**里**插入图片描述
注:第一组卷积+池化+激活
卷积: 1×28×28->6×28×28 通道数变为6,高宽不变
Sigmoid: 6×28×28
平均池化:6×28×28->6×14×14 通道数不变,高宽变了
第二组卷积+池化+激活
卷积: 6×14×14->16×10×10 通道数变为16,高宽变为10×10
Sigmoid: 16×10×10
平均池化:16×10×10->16×5×5 通道数不变,高宽变了
Flatten:1×400 拉直变成MLP
Liner+Sigmoid:1×120
Liner+Sigmoid:1×84
Liner:1×10

LeNet在Fashion-MNIST数据集上的表现
在这里插入图片描述
对 evaluate_accuracy函数进行轻微的修改
在这里插入图片描述
训练和评估LeNet-5模型
在这里插入图片描述

猫狗大战

1.导入基本库

import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torchvision
from torchvision import models,transforms,datasets
import torch.nn.functional as F
from PIL import Image
import torch.optim as optim
import json, random
import os

# 判断是否存在GPU设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('Using gpu: %s ' % torch.cuda.is_available())

2.下载解压数据集,并设置路径

# 这个是训练集,猫狗各取了2000个
! wget https://gaopursuit.oss-cn-beijing.aliyuncs.com/2021/files/train.zip
! unzip train.zip
# 这个是测试集
! wget https://gaopursuit.oss-cn-beijing.aliyuncs.com/202007/dogs_cats_test.zip
! unzip dogs_cats_test.zip
# 训练图片和测试图片的路径
train_path = './train/'
test_path = './test/'

3.创建数据集

def get_data(file_path):
    file_lst = os.listdir(file_path) #获得所有文件名称 xxxx.jpg
    data_lst = []
    for i in range(len(file_lst)):
        clas = file_lst[i][:3] #cat和dog在文件名的开头
        img_path = os.path.join(file_path,file_lst[i])#将文件名与路径合并得到完整路径,以备读取
        if clas == 'cat':
            data_lst.append((img_path, 0))
        else:
            data_lst.append((img_path, 1))
    return data_lst
class catdog_set(torch.utils.data.Dataset):
    def __init__(self, path, transform):
        super(catdog_set).__init__()
        self.data_lst = get_data(path)#调用刚才的函数获得数据列表
        self.trans = torchvision.transforms.Compose(transform)
    def __len__(self):
        return len(self.data_lst)
    def __getitem__(self,index):
        (img,cls) = self.data_lst[index]
        image = self.trans(Image.open(img))
        label = torch.tensor(cls,dtype=torch.float32)
        return image,label
# 将输入图像缩放为 128*128,每一个 batch 中图像数量为128
# 训练时,每一个 epoch 随机打乱图像的顺序,以实现样本多样化
train_loader = torch.utils.data.DataLoader(
    catdog_set(train_path, [transforms.Resize((128,128)),transforms.ToTensor()]), 
    batch_size=128, shuffle=True)

4.定义网络

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.conv3 = nn.Conv2d(16, 32, 4)
        self.conv4 = nn.Conv2d(32, 32, 4)
        self.conv5 = nn.Conv2d(32, 32, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(32, 16)
        self.fc2 = nn.Linear(16, 2)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = self.pool(F.relu(self.conv3(x)))
        x = self.pool(F.relu(self.conv4(x)))
        x = F.relu(self.conv5(x))
        x = x.view(-1, 32)
        x = F.relu(self.fc1(x))
        x = F.softmax(self.fc2(x), dim=1)
        return x

5.网络训练

# 网络放到GPU上
net = Net().to(device)
#定义损失函数
criterion = nn.CrossEntropyLoss()
#定义优化器
optimizer = optim.Adam(net.parameters(), lr=0.001)
#开始训练
for epoch in range(30):  # 重复多轮训练
    for i, (inputs, labels) in enumerate(train_loader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 优化器梯度归零
        optimizer.zero_grad()
        # 正向传播 + 反向传播 + 优化 
        outputs = net(inputs)
        loss = criterion(outputs, labels.long())
        loss.backward()
        optimizer.step() 
    print('Epoch: %d loss: %.6f' %(epoch + 1, loss.item()))
print('Finished Training')

Epoch: 1 loss: 0.693109
Epoch: 2 loss: 0.695695
Epoch: 3 loss: 0.684921
Epoch: 4 loss: 0.619205
Epoch: 5 loss: 0.715462
Epoch: 6 loss: 0.603034
Epoch: 7 loss: 0.647012
Epoch: 8 loss: 0.544246
Epoch: 9 loss: 0.627370
Epoch: 10 loss: 0.639523
Epoch: 11 loss: 0.601679
Epoch: 12 loss: 0.570969
Epoch: 13 loss: 0.635081
Epoch: 14 loss: 0.645430
Epoch: 15 loss: 0.631359
Epoch: 16 loss: 0.564152
Epoch: 17 loss: 0.545618
Epoch: 18 loss: 0.651914
Epoch: 19 loss: 0.537452
Epoch: 20 loss: 0.613498
Epoch: 21 loss: 0.507586
Epoch: 22 loss: 0.577178
Epoch: 23 loss: 0.445549
Epoch: 24 loss: 0.589915
Epoch: 25 loss: 0.533884
Epoch: 26 loss: 0.475426
Epoch: 27 loss: 0.447956
Epoch: 28 loss: 0.452273
Epoch: 29 loss: 0.442422
Epoch: 30 loss: 0.464536
Finished Training

6.测试并输出结果

resfile = open('res.csv', 'w')
for i in range(0,2000): 
    img_PIL = Image.open('./test/'+str(i)+'.jpg')
    img_tensor = transforms.Compose([transforms.Resize((128,128)),transforms.ToTensor()])(img_PIL)
    img_tensor = img_tensor.reshape(-1, img_tensor.shape[0], img_tensor.shape[1], img_tensor.shape[2])
    img_tensor = img_tensor.to(device)
    out = net(img_tensor).cpu().detach().numpy()
    if out[0, 0] < out[0, 1]:
        resfile.write(str(i)+','+str(1)+'\n')
    else:
        resfile.write(str(i)+','+str(0)+'\n')
resfile.close()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值