深度学习4 -- 卷积神经网络(代码实现篇+付详细流程文件)

引言

  • 本文是使用pytorch对卷积神经网络(Convolutional Neural Network, CNN)的代码实现,作为之前介绍CNN原理的一个代码补充。
  • 本文代码相关介绍相对较为详细,也为自己的一个学习过程,有错误的地方欢迎指正。
  • 本人介绍CNN原理的链接:CNN原理介绍1
    CNN原理介绍2

简述CNN结构

  • 为方便理解,如下图所示(详细介绍看上方链接)

  • 结构:一个卷积神经网络由若干卷积层、Pooling层、全连接层组成
  • 流程通俗理解(卷积):输入图片通过卷积核提取特征参数Feature Maps,此为卷积层的操作;
  • 流程通俗理解(池化):得到的特征参数经过池化层进行化简减少参数量,此为池化层的操作
  • 流程通俗理解(全连接):将最终提取到的特征信息输入到全连接神经网络进行计算
  • 上面图中一张图变成了3张Feature Maps是因为Feature Maps数量跟图片通道数有关,卷积核也同样是对应的,一个通道对应一个卷积核
  • 具体名称的介绍可以见:名称介绍

完整流程:

step1 导入需要的包

import torch
import torch.nn as nn
import torch.utils.data as Data
from torch.autograd import Variable
import torchvision # pytorch的一个视觉处理工具包(需单独安装)
  • PyTorch中主要的包
    • torch.nn :包含用于构建神经网络的模块和可扩展类的子包。
    • torch.autograd :支持PyTorch中所有的可微张量运算的子包
    • torch.nn.functional :一种功能接口,包含用于构建神经网络的典型操作,如损失函数、激活函数和卷积运算
    • torch.optim :包含标准优化操作(如SGD和Adam)的子包。
    • torch.utils :工具包,包含数据集和数据加载程序等实用程序类的子包,使数据预处理更容易
    • torchvision :一个提供对流行数据集、模型架构和计算机视觉图像转换的访问的软件包
  • 这些包可能代码中并未用到;参考资料1

step2 数据预处理

首先是关于将数据转换成tensor的原因

  • Tensor的意义:Tensor之于PyTorch就好比是array之于Numpy或者DataFrame之于Pandas,都是构建了整个框架中最为底层的数据结构;
  • Tensor的区别:Tensor又与普通的数据结构不同,具有一个极为关键的特性——自动求导而且它表示一个多维矩阵,在计算方面还可以在GPU上使用以加速计算.
  • PyTorch中对于数据集的处理有三个非常重要的类:Dataset、Dataloader、Sampler,它们均是 torch.utils.data 包下的模块(类)
    • Dataloader是数据的加载类,它是对于Dataset和Sampler的进一步包装,用于实际读取数据,可以理解为它是这个工作的真正实践者。参考资料2

关于torchvision中的数据集

  • torchvision中datasets中所有封装的数据集都是torch.utils.data.Dataset的子类,它们都可以用torch.utils.data.DataLoader进行数据加载。以datasets.MNIST类为例,具体参数和用法如下所示:
CLASS torchvision.datasets.MNIST(
          root: str, 
          train: bool = True, 
          transform: Optional[Callable] = None, 
          target_transform: Optional[Callable] = None, 
          download: bool = False
)
  • root (string): 表示数据集的根目录,其中根目录存在MNIST/processed/training.pt和MNIST/processed/test.pt的子目录(其实就是对下载的文件指定位置)
  • train (bool, optional): 如果为True,则从training.pt创建数据集,否则从test.pt创建数据集
  • download (bool, optional): 如果为True,则从internet下载数据集并将其放入根目录。如果数据集已下载,则不会再次下载
  • transform (callable, optional): 接收PIL图片并返回转换后版本图片的转换函数(就是把图片或者numpy中的数组转换成tensor)
  • target_transform (callable, optional): 接收PIL接收目标并对其进行变换的转换函数
  • 具体参考:参考资料3

什么是Variable?

  • variable是tensor的封装,在神经网络中,常需要反向传播这些的,所以需要各个节点是连接在一起的,是个计算图,tensor的数据格式就好比星星之火,但是无法汇聚一起;等变成variable之后就可以慢慢燎原了。
  • 关于Variable

代码分析

  • Data.DataLoader:加载数据
    • shuffle:表示打乱数据顺序
  • torch.unsqueeze:个人理解就是改变数据shape,此处就是把训练数据本来是一维的给"竖"起来作为一条一条数据进行训练(等以后我想起来更通俗的再修改,具体函数用法看下面参考资料)

step3 定义网络结构

  • Net需要继承自nn.Module,通过super(python中的超类)完成父类的初始化,个人理解类比于python中定义类要继承Object类,这样很多基础定义就可以略去了
    • nn.Module是nn中十分重要的类,包含网络各层的定义及forward方法参考资料6
  • nn.Sequential返回的是一个序列容器用于搭建神经网络的模块,按照被传入构造器的顺序添加到nn.Sequential()容器中,比如con1中就是进行封装先进行第一层的卷积和池化,conv2同理,然后定义前向传播(注意是卷积两次) 参考资料7
    • nn.Conv2d: 在Pytorch的nn模块中,封装了nn.Conv2d()类作为二维卷积的实现,二维卷积应该是最常用的卷积方式了 参考资料8
关于网络结构中流程梳理
  • 关于卷积层nn.Conv2d中的参数介绍:in_channels 输入图片的通道数,同理out_channels为输出图片的通道数(都为自定义的,我搜集资料看到有1,3,16,32等等);kernel_size为卷积核大小,5 * 5可以直接简写,如果是3 * 5就要写成元组的形式;stride为卷积核移动的步长;padding为填充的大小,具体意义可见文章顶部原理介绍

  • nn.ReLU()为激活函数,使用ReLU激活函数有解决梯度消失的作用(具体作用看文章顶部原理中有介绍)

  • nn.MaxPool2d:maxpooling有局部不变性而且可以提取显著特征的同时降低模型的参数,从而降低模型的过拟合,具体操作看下图,除了最大值,还可以取平均值

  • nn.Linear主要是用于全连接层 参考资料8

  • x.view()就是对tensor进行reshape:参考资料9

  • 关于卷积时候图片size的变化

    • 首先输入通道为1,尺寸大小为28 * 28,即(1,28,28)
    • 经过卷积后因为自定义输出通道为16,那么尺寸为(16,28,28)
    • 经过池化层,因为卷积核是2 * 2的,所以尺寸降低为(16,14,14)
    • 继续conv2卷积,池化后就尺寸变成了(32,7,7)
    • nn.Linear具体用法(10是因为这个识别结果是0-9,为10个类别):参考资料10
  • 关于输出尺寸的计算公式

    • O = (I - K + 2P)/ S +1
    • I输入尺寸,K是卷积核大小,P是padding大小,S是步长

step4 训练模型

  • 关于优化器torch.optim.Adam,个人也还不是特别理解详细的作用,此处待补充链接
  • 交叉熵损失函数(适合分类模型): 个人之前的总结
  • optimizer.zero_grad()关于优化器清空上层的梯度
    • 有介绍是为了优化内存的 : 参考资料11
    • 有介绍是一种tips故意不清零来进行梯度优化的
    • 总之还是那句,优化器我搞懂了再补上…
    • 其实单纯论反向传播得时候梯度的更新貌似没有提及是否需要清空上层梯度:梯度下降与反向传播
  • loss.backward()反向传播上面介绍Variable的时候已经讲过,已经封装好了(看CNN原理部分反向传播求各个参数不同层级参数的偏导求得相似,结果在这里只需要调用api…)
  • torch.max 通俗讲就是返回列表中最大的数,具体用法可以见最下方html文件

完整代码

import torch
import torch.nn as nn
import torchvision 
import torch.utils.data as Data
 
torch.manual_seed(1)  # 设置随机种子, 用于复现
 
# 超参数
EPOCH = 1       # 前向后向传播迭代次数
LR = 0.001      # 学习率 learning rate 
BATCH_SIZE = 50 # 批量训练时候一次送入数据的size
DOWNLOAD_MNIST = True 
 
# 下载mnist手写数据集
# 训练集
train_data = torchvision.datasets.MNIST(  
    root = './MNIST/',                      
    train = True,                            
    transform = torchvision.transforms.ToTensor(),                                                
    download=DOWNLOAD_MNIST 
)
 
# 测试集
test_data = torchvision.datasets.MNIST(root='./MNIST/', train=False)  # train设置为False表示获取测试集
 
# 一个批训练 50个样本, 1 channel通道, 图片尺寸 28x28 size:(50, 1, 28, 28)
train_loader = Data.DataLoader(
    dataset = train_data,
    batch_size=BATCH_SIZE,
    shuffle=True
) 
#  测试数据预处理;只测试前2000个
test_x = torch.unsqueeze(test_data.data,dim=1).float()[:2000] / 255.0
# shape from (2000, 28, 28) to (2000, 1, 28, 28)
test_y = test_data.targets[:2000]
 
class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
 
        self.conv1 = nn.Sequential(
            nn.Conv2d(                      # 输入的图片 (1,28,28)
                in_channels=1,
                out_channels=16,            # 经过一个卷积层之后 (16,28,28)
                kernel_size=5,
                stride=1,                    # 如果想要 con2d 出来的图片长宽没有变化, padding=(kernel_size-1)/2 当 stride=1
                padding=2
            ),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)      # 经过池化层处理,维度为(16,14,14)
        )
 
        self.conv2 = nn.Sequential(
            nn.Conv2d(                         # 输入(16,14,14)
                in_channels=16,
                out_channels=32,
                kernel_size=5,
                stride=1,
                padding=2
            ),                                 # 输出(32,14,14)
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2)        # 输出(32,7,7)
        )
 
        self.out = nn.Linear(32*7*7,10)
 
    def forward(self, x):
        x = self.conv1(x)                     #(batch_size,16,14,14)
        x = self.conv2(x)                     # 输出(batch_size,32,7,7)
        x = x.view(x.size(0),-1)              # (batch_size,32*7*7)
        out = self.out(x)                     # (batch_size,10)
        return out
 
cnn = CNN()
optimizer = torch.optim.Adam(cnn.parameters(),lr=LR) # 定义优化器
loss_func = nn.CrossEntropyLoss() # 定义损失函数
 
for epoch in range(EPOCH):
 
    for step,(batch_x,batch_y) in enumerate(train_loader):
        pred_y = cnn(batch_x)
        loss = loss_func(pred_y,batch_y)
        optimizer.zero_grad() # 清空上一层梯度
        loss.backward() # 反向传播
        optimizer.step() # 更新优化器的学习率,一般按照epoch为单位进行更新
 
        if step % 50 == 0:
            test_output = cnn(test_x)
            pred_y = torch.max(test_output, 1)[1].numpy()  # torch.max(test_out,1)返回的是test_out中每一行最大的数)
                                                                # 返回的形式为torch.return_types.max(
                                                                #           values=tensor([0.7000, 0.9000]),
                                                                #           indices=tensor([2, 2]))
                                                                # 后面的[1]代表获取indices
            print('Epoch: ', epoch, '| train loss: %.4f' % loss.data.numpy())
 
 
# 打印前十个测试结果和真实结果进行对比
test_output = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].numpy()
print(pred_y, 'prediction number')
print(test_y[:10].numpy(), 'real number')

补充一个测试过程掌握的小tips

  • 比如使用anaconda中的jupyter测试,但是anaconda中又很多虚拟环境,怎么将jupyter切换到测试用的虚拟环境呢
    • 需要注册,具体参考: 参考资料14
    • 比如我创建CNN测试环境是名称是python36,而默认是python3也就是base环境,按照上述安装并注册后就可以切换啦

附件

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: 卷积神经网络是一种用于图像识别、计算机视觉等任务的深度学习模型。Verilog是一种硬件描述语言,常用于数字电路设计和嵌入式系统开发。要实现一个用于图像处理的8位数据的卷积神经网络,可以使用Verilog来描述网络模型和计算逻辑。 首先,需要定义卷积神经网络的结构和参数。可以使用Verilog中的模块来定义每一层的结构,例如输入层、卷积层、池化层、全连接层和输出层。每个模块包含输入和输出端口,以便数据在各层之间传递。 然后,需要定义每一层的计算逻辑。卷积层使用卷积核对输入图像进行特征提取,并通过激活函数处理输出。池化层则对卷积层的输出进行降采样操作。全连接层将池化层的输出转化为一维向量,并与权重矩阵进行矩阵乘法操作,然后通过激活函数处理输出。最后,输出层使用softmax函数对结果进行概率分布计算。 再者,需要定义网络的输入和输出数据格式。由于本例中使用8位数据进行计算,因此需要相应的数值表示和计算逻辑。 最后,使用Verilog编译器将代码转化为可执行的硬件描述文件,并使用硬件开发平台进行验证和测试。这可以涉及对测试图像进行输入,检查输出是否与预期相符。 总之,使用Verilog实现8位数据的卷积神经网络需要定义网络结构、参数和计算逻辑,并将其转化为可执行的硬件描述文件进行验证和测试。 ### 回答2: Verilog是一种硬件描述语言,可以用于实现数字电路和硬件设计。要实现8位数据的卷积神经网络CNN),首先需要使用Verilog编CNN的相关模块。以下是一个简单的实现思路: 1. 卷积层模块:通过多个卷积核对输入图像进行卷积计算。每个卷积核都有一组权重参数,可以在Verilog中使用内存单元存储这些参数。利用乘法器和累加器实现卷积运算,并使用激活函数对结果进行非线性变换。 2. 池化层模块:使用最大或平均池化方法对卷积层的输出进行下采样。可以使用选择器和比较器来实现最大值或平均值的筛选。 3. 全连接层模块:将池化层的输出连接到全连接层的神经元上。每个神经元都有一组权重参数,可以使用内存单元存储。通过乘法器和累加器对输入进行线性变换,并使用激活函数进行非线性变换。 4. softmax层模块:对全连接层的输出进行softmax运算,得到分类结果。可以使用指数函数和除法器实现softmax运算。 5. 控制模块:设计一个控制模块来协调各个层的工作流程。控制模块可以使用状态机来实现,根据不同的状态选择不同的操作。 以上是一个简单的实现思路,当然实际的CNN实现可能更加复杂,还需要考虑数据存储、数据流控制、并行计算等方面的问题。此外,还需要合适的数据集和训练参数进行训练和测试。通过这些实现,可以在硬件中实现8位数据的卷积神经网络。 ### 回答3: 卷积神经网络(Convolutional Neural Network, CNN)是一种深度学习算法,常用于图像识别和图像分类任务。在Verilog中实现8位数据的卷积神经网络可以按照以下步骤进行: 1. 设计网络结构:首先设计卷积神经网络的结构,包括输入层、卷积层、池化层和全连接层等。此处以一个简单的卷积神经网络为例,包含一个卷积层和一个全连接层。 2. 定义权重和偏置:为了实现卷积层和全连接层的运算,需要定义相应的权重和偏置,它们用于计算网络中的每个神经元的输出值。 3. 编卷积运算模块:在Verilog中编卷积运算模块,该模块用于计算输入数据与卷积核的卷积运算。通过滑动窗口的方式,逐个计算每个位置的卷积结果,并将结果存储在输出缓存中。 4. 编池化运算模块:在Verilog中编池化运算模块,该模块用于对输入特征图进行降采样操作,以减少特征图的尺寸。常见的池化操作包括最大池化和平均池化。 5. 编全连接运算模块:在Verilog中编全连接运算模块,该模块用于将经过池化层输出的特征图转化为一个一维向量,并与权重矩阵进行矩阵乘法运算。最后,添加偏置并经过激活函数,得到最终的输出。 6. 编顶层模块:在Verilog中编顶层模块,用于组织各个子模块,并实现卷积神经网络的整体功能。 以上是一个简要的描述,实际实现时需要根据具体网络结构和硬件平台的需求进行详细设计和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

矮人三等

秀儿,是你吗秀儿?

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

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

打赏作者

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

抵扣说明:

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

余额充值