第三章 卷积神经网络

news2024/12/26 21:25:06

目录

  • 一、CNN 基础
  • 二、CNN 进阶

卷积神经网络,Convolutinal Neural Network,CNN

在之前两章的由线性模型构成的神经网络都是全连接神经网络

一、CNN 基础

在之前全连接层处理二维图像的时候,直接将二维图像从 N × 1 × 28 × 28 N \times 1 \times 28 \times 28 N×1×28×28 直接转换成了 N × 784 N \times 784 N×784,也就是将一个二维图像每一行数据直接拼接成了一行,这种做法,直接图像上可能相邻两个点的空间信息

卷积神经网络概述:

1 个 patch

单一输入通道卷积层的计算过程:

3 个输入通道卷积层的计算过程:

N 个输入通道卷积层的计算过程:

N 个输入通道 M 个输出通道卷积层的计算过程:

卷积核的大小:

使用 Pytorch 实现卷积层的一个例子:

import torch

in_channels, out_channels = 5, 10
width, height = 100, 100
kernel_size = 3
batch_size = 1

input = torch.randn(batch_size, in_channels, width, height)  # 正态分布随机采样,需要先指定batch-size,然后再指定通道数目、宽度、高度
conv_layer = torch.nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size)
output = conv_layer(input)

print(input.shape)
print(output.shape)
print(conv_layer.weight.shape)

# 输出:
# torch.Size([1, 5, 100, 100])
# torch.Size([1, 10, 98, 98])
# torch.Size([10, 5, 3, 3])

卷积层重要概念:padding,这里 padding=1。

假设卷积核的高和宽均为 x x x,在每一次通过卷积层的时候,如果 padding=0,那么输入矩阵的高和宽在输出卷积层的时候会减少 2 × ⌊ x 2 ⌋ 2 \times \left \lfloor \frac{x}{2} \right \rfloor 2×2x 的长度。

卷积层重要概念:stride(步长),这里 stride=2。要注意,图中红色矩形往右移动或者往下移动的步长都是 2!

padding 与 stride 在代码中的设置:

conv_layer = torch.nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, padding=1, stride=2,
                             bias=False)  # bias为True会对最终结果产生一个偏置值

下采样(Subsampling)常用方法:最大池化层(Max Pooling Layer),默认 stride=2。注意最大池化层与通道数目无关,它会在每一个通道上分别进行池化作用。

Pytorch 代码简单实现:

import torch

input = [3, 4, 6, 5,
         2, 4, 6, 8,
         1, 6, 7, 8,
         9, 7, 4, 6]

input = torch.Tensor(input).view(1, 1, 4, 4)
maxpooling_layer = torch.nn.MaxPool2d(kernel_size=2)  # 默认stride=2
output = maxpooling_layer(input)
print(output)

一个简单卷积神经网络样例:

代码实现(依然使用 MNIST 数据集):

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

batch_size = 64
transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])  # 归一化并将图像从单通道转换为多通道,即28x28转换为1x28x28

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

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


class Net(torch.nn.Module):
    def __init__(self):
        super(Net, 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)  # 320 = 20 * 4 * 4

    def forward(self, x):
        batch_size = x.size(0)
        x = self.pooling(F.relu(self.conv1(x)))
        x = self.pooling(F.relu(self.conv2(x)))
        x = x.view(batch_size, -1)
        x = self.fc(x)
        return x


model = Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cup")
model.to(device)  # 将模型放到GPU上

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)  # 添加了动量


def train(epoch):  # 将每一轮训练封装成一个函数
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        inputs, target = inputs.to(device), target.to(device)  # 将数据放到GPU上
        optimizer.zero_grad()

        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if batch_idx % 300 == 299:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0


def test():
    correct = 0  # 预测正确的数目
    total = 0  # 总共的数目
    with torch.no_grad():  # with范围内的代码不计算梯度
        for data in test_loader:
            images, labels = data
            images, labels = images.to(device), labels.to(device)  # 将数据放到GPU上
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)  # 返回(最大值, 最大值下标)
            total += labels.size(0)  # label是一个矩阵
            correct += (predicted == labels).sum().item()
    print('Accuracy on test set: %d %%' % (100 * correct / total))


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

二、CNN 进阶

减少代码冗余的方法:

  • 面向过程的语言:使用函数
  • 面向对象的语言:使用类

比较复杂的 CNN 模型:GoogleNet

Inception Module(初始模型):

  • Inception Module 如此设计的目的:在神经网络中,对于卷积核的大小(超参数)很难以确定,这里就将所有的卷积核大小全部都操作一遍,最后将其结果综合起来。如果其中某一个方案比较好,其所占比重就会比较大,上图中就存在四种方案。
  • 最后在 Concatenate 的时候,四种方案的输出维度(batch-size,通道,w,h)中只有 通道 数目不同,其余都相同。

1x1 卷积层(Network in Network):

1x1 卷积层的作用:1x1 卷积的作用:改变通道数目,减少计算量。它的直接效果是信息融合,即对同一个通道上所有像素点的信息进行了融合。

使用 1x1 卷积层与不使用 1x1 卷积层的效果对比:

模型实现图:

实现代码:

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

batch_size = 64
transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])  # 归一化并将图像从单通道转换为多通道,即28x28转换为1x28x28

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

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


class InceptionA(torch.nn.Module):
    def __init__(self, in_channels):
        super(InceptionA, self).__init__()
        self.branch1x1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)

        self.branch5x5_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch5x5_2 = torch.nn.Conv2d(16, 24, kernel_size=5, padding=2)  # padding=2保证图像w、h不变

        self.branch3x3_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1)
        self.branch3x3_2 = torch.nn.Conv2d(16, 24, kernel_size=3, padding=1)
        self.branch3x3_3 = torch.nn.Conv2d(24, 24, kernel_size=3, padding=1)

        self.branch_pool = torch.nn.Conv2d(in_channels, 24, kernel_size=1)

    def forward(self, x):
        branch1x1 = self.branch1x1(x)

        branch5x5 = self.branch5x5_1(x)
        branch5x5 = self.branch5x5_2(branch5x5)

        branch3x3 = self.branch3x3_1(x)
        branch3x3 = self.branch3x3_2(branch3x3)
        branch3x3 = self.branch3x3_3(branch3x3)

        branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)  # stride=padding=1,保证图像w、h不变
        branch_pool = self.branch_pool(branch_pool)

        outpus = [branch1x1, branch5x5, branch3x3, branch_pool]

        return torch.cat(outpus, dim=1)


class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(88, 20, kernel_size=5)  # 这里的 88 = 24 + 24 + 24 + 16

        self.incep1 = InceptionA(in_channels=10)
        self.incep2 = InceptionA(in_channels=20)

        self.mp = torch.nn.MaxPool2d(2)
        self.fc = torch.nn.Linear(1408, 10)

    def forward(self, x):
        in_size = x.size(0)
        x = F.relu(self.mp(self.conv1(x)))
        x = self.incep1(x)
        x = F.relu(self.mp(self.conv2(x)))
        x = self.incep2(x)
        x = x.view(in_size, -1)
        x = self.fc(x)
        return x


model = Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cup")
model.to(device)  # 将模型放到GPU上

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)  # 添加了动量


def train(epoch):  # 将每一轮训练封装成一个函数
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        inputs, target = inputs.to(device), target.to(device)  # 将数据放到GPU上
        optimizer.zero_grad()

        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if batch_idx % 300 == 299:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0


def test():
    correct = 0  # 预测正确的数目
    total = 0  # 总共的数目
    with torch.no_grad():  # with范围内的代码不计算梯度
        for data in test_loader:
            images, labels = data
            images, labels = images.to(device), labels.to(device)  # 将数据放到GPU上
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)  # 返回(最大值, 最大值下标)
            total += labels.size(0)  # label是一个矩阵
            correct += (predicted == labels).sum().item()
    print('Accuracy on test set: %d %%' % (100 * correct / total))


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

添加 Inception Module 后计算精度从 97% 提高到了 98%。

添加更深层次的模型:Plain Nets

由上图可以知道,针对数据集 CIFAR-10 而言,在 20 层模型的训练损失要比 50 层模型训练的损失低很多。这可能是由于过拟合导致的结果,也有可能是梯度消失造成的。

之所以会发生梯度消失,是因为在反向传播的过程中,如果损失函数 L L L 对某一个变量 ω \omega ω 的梯度 ∂ L ∂ ω < 1 \frac{\partial L}{\partial \omega} < 1 ωL<1那么在进行更新权重的时候,因为是链式法则(乘法),就会导致下式 ω = ω − α ∂ L ∂ ω \omega = \omega - \alpha \frac{\partial L}{\partial \omega} ω=ωαωL 变成 ω = ω − α ⋅ 0 = ω \omega = \omega - \alpha \cdot 0 = \omega ω=ωα0=ω其效果就是梯度不更新了,求出来的损失恒定。

为了解决这个问题,提出了 Deep Residual Learning 模型(残差模型):

如图中所示,该模型能够解决梯度消失的办法就是在激活函数之前加上了最初输入模型的 x x x,后续反向传播过程中会有 ∂ L ∂ ω > 1 \frac{\partial L}{\partial \omega} > 1 ωL>1即便梯度非常小,它也会在 ≥ 1 \ge 1 1 的附近,永远不会为 0 0 0

Deep Resdual Learning 模型代码实现:

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

batch_size = 64
transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])  # 归一化并将图像从单通道转换为多通道,即28x28转换为1x28x28

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

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


class ResidualBlock(torch.nn.Module):
    def __init__(self, channels):
        super(ResidualBlock, self).__init__()
        self.channels = channels  # 该层必须要保证输入输出Tensor维度相同
        self.conv1 = torch.nn.Conv2d(channels, channels, kernel_size=3, padding=1)
        self.conv2 = torch.nn.Conv2d(channels, channels, kernel_size=3, padding=1)

    def forward(self, x):
        y = F.relu(self.conv1(x))
        y = self.conv2(y)
        return F.relu(x + y)  # 先相加,后激活


class Net(torch.nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = torch.nn.Conv2d(1, 16, kernel_size=5)
        self.conv2 = torch.nn.Conv2d(16, 32, kernel_size=5)
        self.mp = torch.nn.MaxPool2d(2)

        self.rblock1 = ResidualBlock(16)
        self.rblock2 = ResidualBlock(32)

        self.fc = torch.nn.Linear(512, 10)

    def forward(self, x):
        in_size = x.size(0)
        x = self.mp(F.relu(self.conv1(x)))
        x = self.rblock1(x)
        x = self.mp(F.relu(self.conv2(x)))
        x = self.rblock2(x)
        x = x.view(in_size, -1)
        x = self.fc(x)
        return x


model = Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cup")
model.to(device)  # 将模型放到GPU上

criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)  # 添加了动量


def train(epoch):  # 将每一轮训练封装成一个函数
    running_loss = 0.0
    for batch_idx, data in enumerate(train_loader, 0):
        inputs, target = data
        inputs, target = inputs.to(device), target.to(device)  # 将数据放到GPU上
        optimizer.zero_grad()

        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if batch_idx % 300 == 299:
            print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
            running_loss = 0.0


def test():
    correct = 0  # 预测正确的数目
    total = 0  # 总共的数目
    with torch.no_grad():  # with范围内的代码不计算梯度
        for data in test_loader:
            images, labels = data
            images, labels = images.to(device), labels.to(device)  # 将数据放到GPU上
            outputs = model(images)
            _, predicted = torch.max(outputs.data, dim=1)  # 返回(最大值, 最大值下标)
            total += labels.size(0)  # label是一个矩阵
            correct += (predicted == labels).sum().item()
    print('Accuracy on test set: %d %%' % (100 * correct / total))


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/553442.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【DolphinScheduler 安装部署】DolphinScheduler调度系统如何安装部署?

要安装DolphinScheduler&#xff08;以下简称DS&#xff09;调度系统&#xff0c;请按照以下步骤进行操作&#xff1a; 系统要求&#xff1a; 确保您的计算机满足DS的最低系统要求&#xff0c;例如操作系统版本、内存和处理器要求等。DS通常在Linux系统上运行良好。 下载DS安装…

k8s部署mongodb副本高可用集群

此版本的NFS为单点&#xff0c;仅为练习使用&#xff0c;生产环境建议使用cephfs的卷类型&#xff0c;避免单点。或者通过keepalived加Sersync的方案对NFS作容灾处理即可用于生产环境。当然&#xff0c;对于开发或测试环境&#xff0c;方便起见&#xff0c;直接使用单点的NFS加…

Oracle OCP 和MySQL OCP 考试完成后查询成绩和下载证书的步骤

我的一些学员考完Oracle OCP和MySQL OCP不知道如何查看自己的考试成绩和证书&#xff0c;姚远老师现在详细说明具体的操作步骤&#xff0c;一共8步。 关于号主&#xff0c;姚远&#xff1a; Oracle ACE&#xff08;Oracle和MySQL数据库方向&#xff09;Oracle MAA 大师华为云…

基于Java+Springmvc+vue+element实现大学生科技创新创业项目管理系统

基于JavaSpringmvcvueelement实现大学生科技创新创业项目管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源…

【校园网设计】基于ensp的跨地区的校园网组网方案

本博客是基于模拟器ensp的校园网组网方案&#xff0c;有总校区和分校区&#xff0c;主要用了vlan划分、dhcp、nat、ospf、acl、bgp等技术。首先说一下本博客的局限性&#xff1a; 总校区和分校区之间只是使用的传统的bgp建立连接&#xff0c;这样可以在运营商上看到内网的明细&…

Squid 代理服务器的应用(传统代理、透明代理、ACL控制列表、sarg日志分析、反向代理)

一、Squid代理服务器的概述 squid 作为一款服务器代理工具&#xff0c;可以缓存网页对象&#xff0c;减少重复请求&#xff0c;从而达到加快网页访问速度&#xff0c;隐藏客户机真实IP&#xff0c;更为安全。 Squid主要提供缓存加速、应用层过滤控制的功能 1、squid代理的工…

Android Studio SDK无法勾选安装的解决方案

问题描述 1、在初次安装好Android Studio后&#xff0c;会启动AS&#xff0c;出现经典的Unable to access Android SDK add-on list报错&#xff0c;点Cancel即可。网上的解决方法分为两种&#xff1a;&#xff08;1&#xff09;设置Proxy为教育网&#xff08;2&#xff09;在…

Linux 下安装SonarQube (版本sonarqube7.6)

公司的虚拟环境坏了&#xff0c;sonarqube需要重新安装。简单记录安装过程和遇到的问题。 相关环境信息 CentOS Linux release 7.6.1810 (Core)MySQL 5.7.30sonarqube7.6JDK 11.0.2 一 安装Sonar前需要知道 1为什么要代码清洁 软件的核心是代码。代码不仅指导业务行为&#…

陈丹琦团队最新力作:上下文学习在上下文“学到”了什么?

深度学习自然语言处理 原创作者 | 鸽鸽 这段时间in-context learning真的很火&#xff0c;陈丹琦组最新的两篇文章都是ICL相关&#xff0c;今天我们拜读其中一篇&#xff1a;丹琦的硕士生、纽约大学准博士生Jane Pan的ACL小短文。 大佬的学生会做出怎样的科研示范呢&#xff1f…

单位、家庭建筑物电气、电子设备防雷举措

前 言 在现实的学习、工作、生活中&#xff0c;有时会面对自然灾害、重特大事故、环境公害及人为破坏等突发事件&#xff0c;为了控制事故的发展&#xff0c;就不得不需要事先制定应急预案。那要怎么制定科学的应急预案呢﹖下面是小编为大家整理的单位、住宅建筑物、电子电气防…

Flink有状态计算的状态容错

状态容错 State Fault Tolerance 首先来说一说状态容错。Flink 支持有状态的计算&#xff0c;可以把数据流的结果一直维持在内存&#xff08;或 disk&#xff09;中&#xff0c;比如累加一个点击数&#xff0c;如果某一时刻计算程序挂掉了&#xff0c;如何保证下次重启的时候&…

VM600 IOCN 输入/输出接口卡

用于CPUM卡的VM600 IOCN输入/输出卡 CPUM卡的输入/输出(接口)卡一个主以太网连接器(8P8C (RJ45))&#xff0c;用于与VM600 MPSx软件和/或Modbus TCP和/或PROFINET通信进行通信一个辅助以太网连接器(8P8C (RJ45))&#xff0c;用于冗余Modbus TCP通信一个主要串行连接器(6P6C (R…

LightningChart JS 4.0.x Crack

Lightning-fast, interactive & responsive 2D & 3D JavaScript charts. Next Generation, World’s Fastest JS Charts Getting Started with LightningChart JS There’s multiple ways to get started with our JS charting library. You can install the package…

CMake Practice 学习笔记二---子目录、安装

让前面的Hello World更像一个工程&#xff1a; 为工程添加一个子目录src&#xff0c;用来放置工程源代码&#xff1b;添加一个子目录doc&#xff0c;用来反之这个工程的文档hello.txt&#xff1b;在工程目录添加文本文件COPYRIGHT&#xff0c;README&#xff1b;在工程目录添加…

Linux常见指令(3)

Linux常见指令 ⑶ date指令引入描述用法例子时间时间戳 cal指令描述用法例子 find指令描述用法例子补充which指令whereis指令 grep指令描述用法例子grep 对文件的操作grep 对目录的操作 补充 打包压缩 和 解压解包zip && unzip描述用法例子对文件的操作对目录的操作 ta…

苹果手机没有备份怎么恢复数据?数据恢复,轻松解决!

案例&#xff1a;苹果手机没有进行过iTunes和iCloud备份&#xff0c;手机还原后数据都被抹掉了&#xff0c;还能恢复数据吗&#xff1f; 【求问各位大神&#xff0c;我几天前把苹果手机的数据还原了&#xff0c;也没有提前进行过数据的备份。现在想起来之前微信上有一些重要的…

uniapp使用uView框架,后端传16位数以上时出现精度缺失的情况

1、使用 json-bigint&#xff0c;失败 2、使用 config 里的 getTask&#xff0c;失败 3、修改 dataType&#xff0c;成功 原因&#xff1a; 通过ajax请求回来的数据在response和preview两种状态显示的是不同的。response中的看到的数据格式其实是字符串&#xff08;ajax请求…

GPT-4 国内使用指南 保姆教程

gpt持续火爆&#xff0c;然鹅国内很多朋友还说不会使用&#xff0c;因此有必要科普一篇文章&#xff01; app.educlub.icu很多朋友因为各种限制无法开通#ChatGPT Plus&#xff0c;而申请OpenAI的GPT-4 API也要慢慢排队&#xff08;我的也还没下来&#xff09;。于是在这里我搜…

3 个令人惊艳的 ChatGPT 项目,开源了!

过去一周&#xff0c;AI 界又发生了天翻地覆的变化&#xff0c;其中&#xff0c;最广为人知的&#xff0c;应该是 OpenAI 正式上线 ChatGPT iOS 客户端&#xff0c;让所有人都可以更方便的在手机上与 ChatGPT 聊天。 此外&#xff0c;Stable Diffusion 母公司 Stability AI 也…

智能文档处理黑科技,拥抱更高效的数字世界

目录 0 写在前面1 为何要关注智慧文档&#xff1f;2 图像弯曲矫正3 手写板反光擦除4 版面元素检测5 文档篡改检测总结 0 写在前面 近期&#xff0c;中国图象图形学学会文档图像分析与识别专业委员会与上海合合信息科技有限公司联合打造了《文档图像智能分析与处理》高峰论坛。…