做啥类型网站网站建设中怎么设置默认页

当前位置: 首页 > news >正文

做啥类型网站,网站建设中怎么设置默认页,网站建设在线推广,做网站 简单外包目录 写在开头 一、CNN的原理

  1. 概述

  2. 卷积层 内参数#xff08;卷积核本身#xff09; 外参数#xff08;填充和步幅#xff09; 输入与输出的尺寸关系

  3. 多通道问题
    多通道输入 多通道输出

  4. 池化层 平均汇聚 最大值汇聚 二、手写数字识别

  5. 任务…目录 写在开头 一、CNN的原理

  6. 概述

  7. 卷积层 内参数卷积核本身 外参数填充和步幅 输入与输出的尺寸关系 

  8. 多通道问题  多通道输入 多通道输出

  9. 池化层 平均汇聚 最大值汇聚 二、手写数字识别

  10. 任务描述和数据集加载

  11. 网络结构LeNet-5

  12. 模型训练

  13. 模型测试

  14. 直观显示预测结果 写在最后 写在开头 本文将将介绍如何使用PyTorch框架搭建卷积神经网络(CNN)模型。简易介绍卷积神经网络的原理并实现模型的搭建、数据集加载、模型训练、测试、网络的保存等。实现机器学习领域的Hello world——手写数字识别。本文的讲解参考了B站两位up主爆肝杰哥、炮哥带你学。有关Pytorch环境配置和CNN具体原理大家可以自行查阅资料本文多数图片也来自于爆肝杰哥的讲解。这里也放上两位up主的视频链接 从0开始撸代码–手把手教你搭建LeNet-5网络模型_哔哩哔哩_bilibili Python深度学习安装Anaconda、PyTorchGPU版库与PyCharm_哔哩哔哩_bilibili 本文使用的PyTorch为1.12.0版本Numpy为1.21版本相近的版本语法差异很小。有关数组的数据结构教程、神经网络的基本原理前向传播/反向传播、神经网络作为“函数模拟器”直观感受、深度神经网络的实现DNN详见本专栏的前三篇文章链接如下  【深度学习基础】NumPy数组库的使用-CSDN博客 【深度学习基础】用PyTorch从零开始搭建DNN深度神经网络_如何搭建一个深度学习神经网络dnn pytorch-CSDN博客 【深度学习基础】使用Pytorch搭建DNN深度神经网络与手写数字识别_dnn网络模型 代码-CSDN博客 基于深度神经网络DNN实现的手写数字识别将灰度图像转换后的二维数组展平到一维将一维的784个特征作为模型输入。在“展平”的过程中必然会失去一些图像的形状结构特征因此基于DNN的实现方式并不能很好的利用图像的二维结构特征而卷积神经网络CNN对于处理图像的位置信息具有一定的优势。因此卷积神经网络经常被用于图像识别/处理领域。下面我们将对CNN进行具体介绍。 一、CNN的原理

  15. 概述 在上一篇博客介绍的深度神经网络DNN中网络的每一层神经元相互直接都有链接每一层都是全连接层我们的目标就是训练这个全连接层的权重w和偏执b最终得到预测效果良好的网络结构。 DNN的全连接层对应于CNN中的卷积层而池化层汇聚其实与激活函数的作用类似。CNN中完整的卷积层的结构是卷积-激活函数-池化汇聚其中池化层也有时可以省略。一个卷积神经网络的结构如下 如上图所示CNN的优势在于可以处理多为输入数据并同样以多维数据的形式输出至下一层保留了更多的空间信息特征。而DNN却只能将多维数据展平成一维数据必然会损失一些空间特征。

  16. 卷积层 内参数卷积核本身 CNN中的卷积层和DNN中的全连接层是平级关系在DNN中我们训练的内参数是全连接层的权重w和偏置bCNN也类似CNN训练的是卷积核也就相当于包含了权重和偏置两个内部参数。下面我们首先描述什么是卷积运算。当输入数据进入卷积层后输入数据会与卷积核进行卷积运算运算方法如下图所示 输入一个多维数据上图为二维与卷积核进行运算即输入中与卷积核形状相同的部分分别与卷积核进行逐个元素相乘再相加。例如计算结果中坐上角的15是根据如下过程计算得到的 逐个元素相乘再相加即 1 * 2 2 * 0 3* 1 0 * 0 1 * 1 2 * 2 3 * 1 0 * 0 1 * 2 15  卷积核本身相当于权重再卷积运算的过程中也可以存在偏置如下 卷积核即CNN的权重和偏置本身为内参数具体里面的数字是我们通过训练得出的我们写代码的时候只要关注一些外部设定的参数即可。下面我们将介绍一些外参数。 外参数填充和步幅 填充padding 显然只要卷积核的大小1*1必然会导致图像越卷越小为了防止输入经过多个卷积层后变得过小可以在发生卷积层之前先向输入图像的外围填入固定的数据比如0这个步骤称之为填充如下图 在我们使用Pytorch搭建卷积层的时候需要在对应的接口中添加这个padding参数向上图中这种情况相当于在3*3的卷积核外围添加了“一圈”则padding 1卷积层的接口中就要这样写 nn.Conv2d(in_channels1, out_channels6, kernel_size3, paddding1) 参数in_channels和out_channels是对应于这个卷积层输入和输出的通道数参数这里我们先放一放。 步幅stride 步幅指的是使用卷积核的位置间隔即输入中参与运算的那个范围每次移动的距离。前面几个示意图中的步幅均为1即每次移动一格如果设置stride2kernel_size2则效果如下 此时需要在卷积层接口中添加参数stride2。 输入与输出的尺寸关系  综上所述结合外参数步幅、填充和内参数卷积核可以看出如下规律 卷积核越大输出越小。 步幅越大输出越小。 填充越大输出越大。 用公式表示定量关系 如果输入和卷积核均为方阵设输入尺寸为W*W输出尺寸为N*N卷积核尺寸为F*F填充的圈数为P步幅为S则有关系 这个关系大家要重点掌握也可以自己推导一下并不复杂。如果输入和卷积核不为方阵设输入尺寸是H*W输出尺寸是OH*OW卷积核尺寸为FH*FW填充为P步幅为S则输出尺寸OH*OW的计算公式是 3. 多通道问题  多通道输入 对于手写数字识别这种灰度图像可以视为仅有高*长二维的输入。然而对于彩色图像每一个像素点都相当于是RGB的三个值的组合因此对于彩色的图像输入除了高*长两个维度外还有第三个维度——通道即红、绿、蓝三个通道也可以视为3个单通道的二维图像的混合叠加。 当输入数据仅为二维时卷积层的权重往往被称作卷积核Kernel; 当输入数据为三维或更高时卷积层的权重往往被称作滤波器Filter。 对于多通道输入输入数据和滤波器的通道数必须保持一致。这样会导致输出结果降维成二维如下图 对形状进行一下抽象则输入数据C*H*W和滤波器C*FH*FW都是长方体结果是一个长方形1*OH*OW注意C,H,W是固定的顺序通道数要写在最前。 多通道输出 如果要实现多通道输出那么就需要多个滤波器让三维输入与多个滤波器进行卷积就可以实现多通道输出输出的通道数FN就是滤波器的个数FN如下图 和单通道一样卷积运算后也有偏置如果进一步追加偏置则结果如下每个通道都有一个单独的偏置。  4. 池化层 池化也叫汇聚Pooling。池化层通常位于卷积层之后有时也可以不设置池化层其作用仅仅是在一定范围内提取特征值所以并不存在要学习的内部参数。池化仅仅对图像的高H和宽W进行特征提取并不改变通道数C。 平均汇聚 一般有平均汇聚和最大值汇聚两种。平均汇聚如下 如上图池化的窗口大小为2*2对应的步幅为2因此对于上图这种情况对应的Pytorch接口如下 nn.AvgPool2d(kernel_size2, stride2)  最大值汇聚 同理如果使用最大值汇聚如下图所示 此处Pytorch函数就这么写 nn.MaxPool2d(kernel_size2, stride2)  二、手写数字识别

  17. 任务描述和数据集加载 此处和上一篇博客类似详情见 【深度学习基础】使用Pytorch搭建DNN深度神经网络与手写数字识别_dnn网络模型 代码-CSDN博客 接下来我们实现机器学习领域的Hello World——手写数字识别。使用的数据集MNIST是机器学习领域的标准数据集其中的每一个样本都是一副二维的灰度图像尺寸为28*28 输入就相当于一个单通道的图像是二维的。我们在实现的时候要将每个样本图像转换为28*28的张量作为输入此处和上一篇DNN都一致。数据集则通过包torchvision中的datasets库进行下载。这里我快速给一段代码好了详情可见上一篇博客。 import torch from torchvision import datasets, transforms# 设定下载参数 数据集转换参数,将图像转换为张量 data_transform transforms.Compose([transforms.ToTensor() ])# 加载训练数据集 train_dataset datasets.MNIST(rootD:\Jupyter\dataset\minst, # 下载路径读者请自行设置trainTrue, # 是训练集downloadTrue, # 如果该路径没有该数据集则进行下载transformdata_transform # 数据集转换参数 )# 批次加载器,在接下来的训练中进行小批次16批次的载入数据有助于提高准确度对训练集的样本进行打乱 train_dataloader torch.utils.data.DataLoader(datasettrain_dataset, batch_size16, shuffleTrue)# 加载测试数据集 test_dataset datasets.MNIST(rootD:\Jupyter\dataset\minst, # 下载路径trainFalse, # 是训练集downloadTrue, # 如果该路径没有该数据集则进行下载transformdata_transform # 数据集转换参数 )test_dataloader torch.utils.data.DataLoader(datasettest_dataset, batch_size16, shuffleTrue)

  18. 网络结构LeNet-5 本文搭建的LeNet-5起源于1998年在手写数字识别上非常成功。其结构如下 再列一个表格具体结构如下 注:输出层的激活函数目前已经被Softmax取代。 至于这些尺寸关系我举个两例子吧 以第一层C1的输入和输出为例。输入尺寸W是28*28卷积核F尺寸为5*5步幅S为1填充P为2那么输出N的28*28怎么来的呢按照公式如下    我们也可以观察到第一层的卷积核个数为6则输出的通道数也为6。   再看一下第一个池化层S2输入尺寸是28*28卷积核F大小为2*2此处的“卷积核”实际上指的是采样范围步幅S2填充P为0则输出的14*14是这么算出来的 其他的没啥好说的读者们可以自行计算这个尺寸关系。接下来我们给出完整的CNN网络代码net.py如下 import torch from torch import nn# 定义网络模型 class MyLeNet5(nn.Module):# 初始化网络def init(self):super(MyLeNet5, self).init()self.net nn.Sequential(nn.Conv2d(in_channels1, out_channels6, kernel_size5, padding2), nn.Tanh(),nn.AvgPool2d(kernel_size2, stride2),nn.Conv2d(in_channels6, out_channels16, kernel_size5), nn.Tanh(),nn.AvgPool2d(kernel_size2, stride2),nn.Conv2d(in_channels16, out_channels120, kernel_size5), nn.Tanh(),nn.Flatten(),nn.Linear(120, 84), nn.Tanh(),nn.Linear(84, 10))# 前向传播def forward(self, x):y self.net(x)return y# 以下为测试代码也可不添加 if name main: x1 torch.rand([1, 1, 28, 28])model MyLeNet5()y1 model(x1)print(x1)print(y1) 这里我还在main添加了一些测试代码正如表格中所示假设我们向网络中输入一个1*1*28*28的向量模拟批次大小为1一个单通道28*28的灰度图输入。最终y应该是由10个数字组成的张量结果如下 从输出我们可以直观的看到输入经过神经网络前向传播后的结果。另外特别注意这个网络的结构中的参数其中卷积层的搭建API有5个外参数 in_channels输入通道数 out_channels输出通道数 kernel_size: 卷积核尺寸 padding: 填充不写则默认0 stride: 步幅不写则默认1 这个LeNet-5网络结构就长这样我们一定要严格遵守否则有可能出现无论怎么训练都始终欠拟合的情况。我就曾经试过更改/添加不同的激活函数结果无论是训练还是测试准确率都在10%徘徊相当于随机瞎猜的效果因此大家一定要严格遵循这个网络结构。 

  19. 模型训练 网络搭建好之后所有的内参数即卷积核都是随机的下面我们要通过训练尽可能提高网络的预测能力。在训练前我们首先要选择损失函数这里使用交叉熵损失函数定义优化器、进行学习率调整等代码如下 import torch from torch import nn from net import MyLeNet5 from torch.optim import lr_scheduler# 判断是否有gpu device cuda if torch.cuda.is_available() else cpu# 调用net将模型数据转移到gpu model MyLeNet5().to(device)# 选择损失函数 loss_fn nn.CrossEntropyLoss() # 交叉熵损失函数自带Softmax激活函数# 定义优化器 optimizer torch.optim.SGD(model.parameters(), lr1e-3, momentum0.9)# 学习率每隔10轮次 变为原来的0.1 lr_scheduler lr_scheduler.StepLR(optimizer, step_size10, gamma0.1)然后我们可以写一个用于训练网络的函数四个参数分别是批次加载器、模型、损失函数、优化器代码如下

    定义模型训练的函数

    def train(dataloader, model, loss_fn, optimizer):loss, current, n 0.0, 0.0, 0for batch, (X, y) in enumerate(dataloader):# 前向传播X, y X.to(device), y.to(device)output model(X)cur_loss loss_fn(output, y)# 用和pred分别接收输出10个元素中的最大值和对应下标位置, pred torch.max(output, dim1)# 计算当前轮次时训练集的精确度将所有标签值与预测值即下标位置cur_acc torch.sum(y pred)/output.shape[0]# 反向传播对内部参数卷积核进行优化optimizer.zero_grad()cur_loss.backward()optimizer.step()# 计算准确率和损失这里只是为了实时显示训练集的拟合情况。也可以不写loss cur_loss.item()current cur_acc.item()n n 1print(train_loss: , str(loss/n))print(train_acc: , str(current/n)) 只要调用这个函数即可实现模型训练 train(train_dataloader, model, loss_fn, optimizer) 当然我们最好是设定一个轮次epoch我们后续会写这样一个循环每训练一个epoch就进行一次测试实时显示一定轮次后训练集和测试集的拟合情况。

  20. 模型测试 这里和模型训练类似只不过我们要观察训练好的模型在测试集的预测效果。与训练的代码相似只是没有了反向传播优化参数的过程。用于测试的函数代码如下 def test(dataloader, model, loss_fn):model.eval()loss, current, n 0.0, 0.0, 0# 该局部关闭梯度计算功能提高运算效率with torch.no_grad(): for batch, (X, y) in enumerate(dataloader):# 前向传播X, y X.to(device), y.to(device)output model(X)cur_loss lossfn(output, y), pred torch.max(output, dim1)# 计算当前轮次时训练集的精确度cur_acc torch.sum(y pred) / output.shape[0]loss cur_loss.item()current cur_acc.item()n n 1print(test_loss: , str(loss / n))print(test_acc: , str(current / n))return current/n # 返回精确度 如上代码将测试集的精确度作为返回值我们在外围调用这个函数时可以通过循环找到测试集最大的精确度。 最终我们设定一个训练轮次epochs此处epochs50每经过一个epoch的训练就进行测试实时打印观察训练集和测试集的拟合情况。当测试集的精确度是当前的最大值时我们就保存这个模型的参数到save_model/best_model.pth代码如下 import os# 开始训练 epoch 50 max_acc 0 for t in range(epoch):print(fepoch{t1}\n—————)# 训练模型train(train_dataloader, model, loss_fn, optimizer)# 测试模型a test(test_dataloader, model, loss_fn)# 保存最好的模型参数if a max_acc:folder save_modelif not os.path.exists(folder):os.mkdir(folder)max_acc aprint(current best model acc , a)torch.save(model.state_dict(), save_model/best_model.pth) print(Done!) 运行之后可以发现测试集的精度经过1个epochs就达到了90%以上最终经过50轮次的训练测试集精度达到了99%左右 模型参数也得以保存 最后给出用于训练和测试的完整代码train.py如下所示 import torch from torch import nn from net import MyLeNet5 from torch.optim import lr_scheduler from torchvision import datasets, transforms import os# 将图像转换为张量形式 data_transform transforms.Compose([transforms.ToTensor() ])# 加载训练数据集 train_dataset datasets.MNIST(rootD:\Jupyter\dataset\minst, # 下载路径trainTrue, # 是训练集downloadTrue, # 如果该路径没有该数据集则进行下载transformdata_transform # 数据集转换参数 )# 批次加载器 train_dataloader torch.utils.data.DataLoader(datasettrain_dataset, batch_size16, shuffleTrue)# 加载测试数据集 test_dataset datasets.MNIST(rootD:\Jupyter\dataset\minst, # 下载路径trainFalse, # 是训练集downloadTrue, # 如果该路径没有该数据集则进行下载transformdata_transform # 数据集转换参数 ) test_dataloader torch.utils.data.DataLoader(datasettest_dataset, batch_size16, shuffleTrue)# 判断是否有gpu device cuda if torch.cuda.is_available() else cpu# 调用net将模型数据转移到gpu model MyLeNet5().to(device)# 选择损失函数 loss_fn nn.CrossEntropyLoss() # 交叉熵损失函数自带Softmax激活函数# 定义优化器 optimizer torch.optim.SGD(model.parameters(), lr1e-3, momentum0.9)# 学习率每隔10轮次 变为原来的0.1 lr_scheduler lr_scheduler.StepLR(optimizer, step_size10, gamma0.1)# 定于训练函数 def train(dataloader, model, loss_fn, optimizer):loss, current, n 0.0, 0.0, 0for batch, (X, y) in enumerate(dataloader):# 前向传播X, y X.to(device), y.to(device)output model(X)cur_loss lossfn(output, y), pred torch.max(output, dim1)# 计算当前轮次时训练集的精确度cur_acc torch.sum(y pred)/output.shape[0]# 反向传播optimizer.zero_grad()cur_loss.backward()optimizer.step()loss cur_loss.item()current cur_acc.item()n n 1print(train_loss: , str(loss/n))print(train_acc: , str(current/n))def test(dataloader, model, loss_fn):model.eval()loss, current, n 0.0, 0.0, 0# 该局部关闭梯度计算功能提高运算效率with torch.no_grad():for batch, (X, y) in enumerate(dataloader):# 前向传播X, y X.to(device), y.to(device)output model(X)cur_loss lossfn(output, y), pred torch.max(output, dim1)# 计算当前轮次时训练集的精确度cur_acc torch.sum(y pred) / output.shape[0]loss cur_loss.item()current cur_acc.item()n n 1print(test_loss: , str(loss / n))print(test_acc: , str(current / n))return current/n # 返回精确度# 开始训练 epoch 50 max_acc 0 for t in range(epoch):print(fepoch{t1}\n—————)train(train_dataloader, model, loss_fn, optimizer)a test(test_dataloader, model, loss_fn)# 保存最好的模型参数if a max_acc:folder save_modelif not os.path.exists(folder):os.mkdir(folder)max_acc aprint(current best model acc , a)torch.save(model.state_dict(), save_model/best_model.pth) print(Done!)

  21. 直观显示预测结果 截至目前我们已经完成了手写数字识别这个任务但是我们好像对于数据集长什么样并不是很了解似乎仅仅是用torchvision中的datasets库下载了一下。因此本小节我们的目标是从数据集取出几个特定的手写数字图片并查看我们模型对其的预测效果。 首先我们还是加载数据集和之前的代码一样这里省略。然后我们加载模型 from net import MyLeNet5# 调用net将模型数据转移到gpu model MyLeNet5().to(device) model.load_state_dict(torch.load(./save_model/best_model.pth)) 我们取出测试集中的前5长图片做一个展示即可完整代码show.py如下 import torch from net import MyLeNet5 from torchvision import datasets, transforms from torchvision.transforms import ToPILImagedata_transform transforms.Compose([transforms.ToTensor() ])# 加载训练数据集 train_dataset datasets.MNIST(rootD:\Jupyter\dataset\minst, # 下载路径trainTrue, # 是训练集downloadTrue, # 如果该路径没有该数据集则进行下载transformdata_transform # 数据集转换参数 )# 加载测试数据集 test_dataset datasets.MNIST(rootD:\Jupyter\dataset\minst, # 下载路径trainFalse, # 是训练集downloadTrue, # 如果该路径没有该数据集则进行下载transformdata_transform # 数据集转换参数 )# 判断是否有gpu device cuda if torch.cuda.is_available() else cpu# 调用net将模型数据转移到gpu model MyLeNet5().to(device) model.load_state_dict(torch.load(./save_model/best_model.pth))# 获取结果 classes [0,1,2,3,4,5,6,7,8,9 ]# 把tensor转化为图片方便可视化 image ToPILImage()# 进入验证 for i in range(5):X, y test_dataset[i][0], test_dataset[i][1] # Xy对应第i张图片和标签# image是ToPILImage的实例将Pytorch张量转换为PIL图像.show()方法会打开图像查看器并显示图像image(X).show()unsqueeze 方法在指定的 dim 维度上扩展张量的维度。这里 dim0所以它会在第0维添加一个维度.例如原来的 X 形状是 (1, 28, 28)经过 unsqueeze 处理后形状变为 (1, 1, 28, 28)。这样做的目的是将单张图像扩展成批次大小为1的形式这样模型可以接收单张图像作为输入。X torch.unsqueeze(X, dim0).float().to(device)with torch.no_grad():# 前向传播获得预测结果pred由10个元素组成的张量pred model(X)print(pred)# 将预测值和标签转化为对应的数字分类结果pred中的最大值视为预测分类predicted, actual classes[torch.argmax(pred[0])], classes[y]print(fpredicted: {predicted}, actual: {actual})运行结果如下我们可以看到测试集中的前五张图片分别是72104且我们的模型都能对其进行成功预测分类。 预测结果均正确如下 写在最后 本文介绍了如何使用PyTorch框架搭建卷积神经网络模型CNN。将CNN与DNN进行了类比。CNN中的卷积层与DNN的全连接层是平级关系。我们实现了LeNet-5的模型的搭建、模型训练、测试、网络的复用、直观查看数据集的图片预测结果等实现了机器学习领域的Hello world——手写数字识别。在CNN原理中读者应当重点关注输入输出的尺寸关系并可以对照LeNet-5结构示意图写出对应Pytorch代码。至于模型训练和测试基本都是固定的代码形式。 这篇文章到这里就结束了后续我还会继续更新深度学习的相关知识另外近期我个人的研究方向涉及到图神经网络回头也会更新一些相关博客。如果读者有相关建议或疑问也欢迎评论区与我共同探讨我一定知无不言。总结不易还请读者多多点赞关注支持