位置:首页 > 新闻资讯 > 漫卷的第一个神经网络,带你一起搭网络

漫卷的第一个神经网络,带你一起搭网络

时间:2025-07-23  |  作者:  |  阅读:0

本文展示了漫卷搭建首个神经网络ManjuanNet的过程,采用PaddlePaddle框架,含两个基模块,通过双路径提取特征后拼接。介绍了数据集处理(含解压、检测损坏图像)、模型训练等,对比了与ResNet34的参数量和FLOPS,其参数量更少但FLOPS高约40倍,在ImageNet100上10轮验证准确率70.04%。

ManjuanNet,漫卷自己搭建的第一个神经网络

这里将展示神经网络的搭建过程和我遇到的一些问题以及一些思考;帮助大家和我一起学习搭建神经网络;

为什么要用paddlepaddle? 因为paddle可以直接在飞桨平台实践,省去了配环境的过程,直接运行起代码可以让我们快速入门,而配环境则会大量消耗我们的热情,一个急眼,也许就不想学了;同时paddle和pytorch有着相似的接口,学会paddle,pytorch定是不在话下,而且paddle有非常重要的 中文文档 !

遇到了什么问题?我自己搭的网络,参数比Resnet34少一些,但训练速度却非常慢,希望有大佬可以解答这个问题。

破案了,我的网络因为卷积计算比resnet34多很多,所以FLOPS高出约40倍。

数据集是直接引用其他飞桨用户上传的数据集,但是数据集里有一张图片坏掉了,后续添加了检测图像是否损坏的代码;

In [1]

import warningswarnings.filterwarnings('ignore')# 忽略掉警告,由于环境原因,可能会出现各种干扰视野的警告,这里我们只关注搭建神经网络本身,所有忽略警告登录后复制 ? ?In [2]

# 导包import paddleimport paddle.nn as nnimport paddle.nn.functional as Fimport paddle.optimizer as optimimport paddle.vision.transforms as Timport paddle.vision.datasets as Dfrom paddle.io import DataLoader登录后复制 ? ?

zipfile库和os库解压ImageNet100数据集

下面两个框:第一个框是解压Imagenet100数据集的代码,用于测试我的神经网络在一个比较大的数据集上的性能,我已经训练了一个10个epoch的权重,验证集上的AC率达到70.04,文件中也保存了训练日志,名称均为imagenet100-10epoch。

第二个框是逐个图像检查是否损坏的代码;

两个框我均已经注释掉,需要运行请自主取消注释,同时在读取位置更改路径

使用工具:zipfile库是python中对压缩包操作的库os库是python中进行系统操作的库

In [3]

import zipfileimport osdef unzip_file(zip_path): # 获取 ZIP 文件所在的目录 extract_to = os.path.dirname(zip_path) # 打开 ZIP 文件 with zipfile.ZipFile(zip_path, 'r') as zip_ref: # 解压所有文件到目标路径 zip_ref.extractall(extract_to) print(f”ZIP 文件 '{zip_path}' 已成功解压到 '{extract_to}'“)# 示例使用zip_path = './data/data150555/MyImagenet.zip' # ZIP 文件的路径unzip_file(zip_path)登录后复制 ? ? ? ?

'nimport zipfilenimport osnndef unzip_file(zip_path):n # 获取 ZIP 文件所在的目录n extract_to = os.path.dirname(zip_path)nn # 打开 ZIP 文件n with zipfile.ZipFile(zip_path, 'r') as zip_ref:n # 解压所有文件到目标路径n zip_ref.extractall(extract_to)nn print(f”ZIP 文件 '{zip_path}' 已成功解压到 '{extract_to}'“)nn# 示例使用nzip_path = './data/data150555/MyImagenet.zip' # ZIP 文件的路径nnunzip_file(zip_path)n'登录后复制 ? ? ? ? ? ? ? ?In [4]

import osfrom PIL import Imagedef check_and_delete_corrupted_images(root_folder): for root, dirs, files in os.walk(root_folder): for file in files: file_path = os.path.join(root, file) try: # 尝试打开图片 with Image.open(file_path) as img: img.verify() # 验证图片是否损坏 except (IOError, SyntaxError) as e: # 如果图片损坏,删除该图片 print(f”Corrupted image found: {file_path}, deleting...“) os.remove(file_path)# 指定根文件夹路径root_folder = './data/data150555/MyImagenet/train'check_and_delete_corrupted_images(root_folder)登录后复制 ? ? ? ?

'nimport osnfrom PIL import Imagenndef check_and_delete_corrupted_images(root_folder):n for root, dirs, files in os.walk(root_folder):n for file in files:n file_path = os.path.join(root, file)n try:n # 尝试打开图片n with Image.open(file_path) as img:n img.verify() # 验证图片是否损坏n except (IOError, SyntaxError) as e:n # 如果图片损坏,删除该图片n print(f”Corrupted image found: {file_path}, deleting...“)n os.remove(file_path)nn# 指定根文件夹路径nroot_folder = './data/data150555/MyImagenet/train'ncheck_and_delete_corrupted_images(root_folder)n'登录后复制 ? ? ? ? ? ? ? ?

解压脑肿瘤分类数据集

默认使用这个数据集运行,如果想运行imagenet100,取消掉上面的注释即可

注意,由于版本问题,解压时可能出现奇怪的报错,如果该解压代码报错,请手动解压

这个数据集很奇怪的损坏掉了,晚点再解决

In [5]

'''import zipfileimport osdef unzip_file(zip_path): # 获取 ZIP 文件所在的目录 extract_to = os.path.dirname(zip_path) # 打开 ZIP 文件 with zipfile.ZipFile(zip_path, 'r') as zip_ref: # 解压所有文件到目标路径 zip_ref.extractall(extract_to) print(f”ZIP 文件 '{zip_path}' 已成功解压到 '{extract_to}'“)# 示例使用zip_path_train = './data/data303405/Training.zip' # ZIP 文件的路径zip_path_test = './data/data303405/Testing.zip' # ZIP 文件的路径unzip_file(zip_path_train)unzip_file(zip_path_test)'''登录后复制 ? ? ? ?

'nimport zipfilenimport osnndef unzip_file(zip_path):n # 获取 ZIP 文件所在的目录n extract_to = os.path.dirname(zip_path)nn # 打开 ZIP 文件n with zipfile.ZipFile(zip_path, 'r') as zip_ref:n # 解压所有文件到目标路径n zip_ref.extractall(extract_to)nn print(f”ZIP 文件 '{zip_path}' 已成功解压到 '{extract_to}'“)nn# 示例使用nzip_path_train = './data/data303405/Training.zip' # ZIP 文件的路径nzip_path_test = './data/data303405/Testing.zip' # ZIP 文件的路径nunzip_file(zip_path_train)nunzip_file(zip_path_test)n'登录后复制 ? ? ? ? ? ? ? ?

定义我的网络结构

使用的工具:

paddlepaddlepaddlepaddle框架的nnnn类

在nn类中,封装了很多方法,具体的:登录后复制 ? ? ? ?

1.Conv系列卷积方法,有Conv1D、Conv2D、Conv3D三种卷积,分别是一维、二维、三维卷积,输入的参数基本相同,区别在于输入的数据格式,具体可以参考飞桨的官方开发文档:https://www.paddlepaddle.org.cn/tutorials/projectdetail/3493103这俩我们具体使用的是Conv2D,二维卷积方法

2.BatchNormBatchNorm系列批量归一化方法,和卷积的方法类似,同样有BatchNorm1D、BatchNorm2D、BatchNorm3D三种,对应一维、二维、三维的批量归一化,同样的,这里使用二维批量归一化

3.常用的激活函数: ? ? ? ? ? ?ReLUReLU:paddle.nn.ReLU(): 的ReLU激活函数,对于输入的负值,输出为0;对于输入的非负值,输出与输入相同。 ? ? ? ? ? ?SigmoidSigmoid:paddle.nn.Sigmoid(): 输入映射到(0, 1)区间内,常用于二分类问题的输出层。 ? ? ? ? ? ?TanhTanh:paddle.nn.Tanh(): 将输入映射到(-1, 1)区间内,是另一种常见的激活函数。 ? ? ? ? ? ?LeakyReLULeakyReLU:paddle.nn.LeakyReLU(): 是ReLU的变体,允许小的负梯度当输入值为负时。这有助于解决“死亡ReLU”问题,其中神经元在训练过程中可能停止学习。我们直接使用RelU进行网络搭建,也可以自行更换其他激活函数;

4.MaxPool2DMaxPool2D?池化方法:二维的最大池化,取选定范围内的最大值作为这个区域的值,同样的,池化也有三种维度的方法,同时还有很多其他池化方法;这里不仅使用了最大池化,还使用了平均池化;

5.SequentialSequential容器:用于按顺序容纳多个网络层或模块。

paddlepaddlepaddlepaddle的flattenflatten方法

使用paddle.flatten()paddle.flatten()即可直接调用,将tensor展平为一维

第一个基模块

使用刚刚提到的paddlepaddlepaddlepaddle框架中的Conv2DConv2D、BatchNorm2DBatchNorm2D和ReLUReLU方法进行基模块的搭建;

这个基模块是resnet18的基模块改的,基本结构相同,只更改了卷积核大小和步长,将原来的两个3x3卷积改成了一个5x5,一个3x3

结构可以参考resnet18的基模块结构

In [6]

# 这里使用python的class来进行基模块的定义,这个基模块会在后面搭建模型时使用class BasicBlock1(nn.Layer): expansion = 1 def __init__(self, in_channels1, out_channels, stride=1, downsample=None): # in_channels为输入大小, out_channels输出大小, stride为卷积步长, downsample为下采样 super(BasicBlock1, self).__init__()# 继承一下自己 self.conv1 = nn.Conv2D(in_channels1, out_channels, kernel_size=5, stride=stride, padding=2, bias_attr=False) # Conv2D是paddle框架中封装好的对二维数据进行卷积的方法,和torch的封装结构一致,都封装在nn类中 # 这是第一个卷积层,卷积核大小为5*5,步长为1,padding为2,不使用bias(偏置),大核无敌! self.bn1 = nn.BatchNorm2D(out_channels) # 这是第一个BatchNorm层,对刚刚卷积后的数据进行批量归一化,out_channels为输出大小 self.relu = nn.ReLU() # 这是Relu激活函数,归一化后进入Relu激活函数,进行线性向非线性的变换 self.conv2 = nn.Conv2D(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias_attr=False) self.bn2 = nn.BatchNorm2D(out_channels) self.downsample = downsample self.stride = stride # 步长,这里可以不声明,在__init__的参数表中就有,这里是为了代码规范声明的。 def forward(self, x):# 在这里定义前向传播 identity = x out = self.conv1(x)# 先定义一个值做个卷积 out = self.bn1(out)# 然后做个归一化 out = self.relu(out)# 再经过一次激活函数 out = self.conv2(out)# 接着卷积 out = self.bn2(out)# 继续归一化 if self.downsample is not None:# 在这里和进行对齐,避免无法进行残差链接 identity = self.downsample(x) out += identity# 残差链接,这里直接将上次的计算结果作为残差链接上来 out = self.relu(out) return out登录后复制 ? ?

第二个基模块

这是第二个基模块,是我一拍脑袋设计的,可能有很多冗余;结构为:先进行一次1x1的卷积,然后进行一次3x3的卷积,接着再进行一次1x1的卷积

In [7]

# 再定义一个模块,也用于搭建class BasicBlock2(nn.Layer): expansion = 1 def __init__(self, in_channels2, out_channels, stride=1, downsample=None): # in_channels为输入大小, out_channels输出大小, stride为卷积步长, downsample为下采样 super(BasicBlock2, self).__init__()# 继承一下自己 self.conv1 = nn.Conv2D(in_channels2, out_channels, kernel_size=1, stride=stride, padding=0, bias_attr=False)# 这里不用padding,因为卷积核大小是1 self.bn1 = nn.BatchNorm2D(out_channels) # 这是第一个BatchNorm层,对刚刚卷积后的数据进行批量归一化,out_channels为输出大小 self.relu = nn.ReLU() # 这是Relu激活函数,归一化后进入Relu激活函数,进行线性向非线性的变换 self.conv2 = nn.Conv2D(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias_attr=False) self.bn2 = nn.BatchNorm2D(out_channels) self.conv3 = nn.Conv2D(out_channels, out_channels, kernel_size=1, stride=1, padding=0, bias_attr=False) self.bn3 = nn.BatchNorm2D(out_channels) self.downsample = downsample self.stride = stride # 步长,这里可以不声明,在__init__的参数表中就有,这里是为了代码规范声明的。 def forward(self, x):# 在这里定义前向传播 identity = x out = self.conv1(x)# 先定义一个值做个卷积 out = self.bn1(out)# 然后做个归一化 out = self.relu(out)# 再经过一次激活函数 out = self.conv2(out)# 接着卷积 out = self.bn2(out)# 继续归一化 out = self.conv3(out)# 接着卷积 out = self.bn3(out)# 继续归一化 if self.downsample is not None:# 在这里和进行对齐,避免无法进行残差链接 identity = self.downsample(x) out += identity# 残差链接,这里直接将上次的计算结果作为残差链接上来 out = self.relu(out) return out登录后复制 ? ?

这是我整个模型的搭建

我的模型有两个卷积路径,一条路径从少通道卷成多通道,一条路径从多通道卷成少通道,这样可以获取两种不同融合的特征,最终将两个路径得到的特征图拼接起来,经过一个全连接神经网络进行预测。

具体细节:1.直接使用python原生的append方法,向列表中插入残差块;2.手写了一个_make_layer方法,用于创建基模块组,这样可以同时创建多层;

In [8]

class manjuanNet(nn.Layer): def __init__(self, block1,block2, layer_configs, num_classes=1000):# block1和block2是可更换的基模块,这里我们用的是上面声明的BasicBlock1和BasicBlock2 # layer_configs是一个列表,指定每个残差块组的块数量配置,也可以再这里加一个入参,额外设定新的表 super(manjuanNet, self).__init__() self.in_channels1 = 64 self.in_channels2 = 256 self.conv1 = nn.Conv2D(3, 64, kernel_size=7, stride=2, padding=3, bias_attr=False)# 大核卷积,卷成64通道 self.conv2 = nn.Conv2D(3,256, kernel_size=5,stride=1, padding=3, bias_attr=False)# 中核卷积,卷成256通道 self.bn1 = nn.BatchNorm2D(64) self.bn2 = nn.BatchNorm2D(256) self.relu = nn.ReLU() self.maxpool = nn.MaxPool2D(kernel_size=3, stride=1, padding=1) # 构建残差块组 self.layer1 = self._make_layer(block1, 64, layer_configs[0]) # 第一个残差块组 self.layer2 = self._make_layer(block1, 128, layer_configs[1], stride=2) # 第二个残差块组,步长为2 self.layer3 = self._make_layer(block1, 64, layer_configs[2], stride=1) # 第三个残差块组,步长为2 self.layer4 = self._make_layer(block1, 32, layer_configs[3], stride=1) # 第四个残差块组,步长为2 self.layer1s = self._make_layers(block2, 4, layer_configs[0]) # 第一个残差块组 self.layer2s = self._make_layers(block2, 8, layer_configs[1]-1, stride=2) # 第二个残差块组,步长为1 self.layer3s = self._make_layers(block2, 16, layer_configs[2]-1, stride=1) # 第三个残差块组,步长为3 self.layer4s = self._make_layers(block2, 32, layer_configs[3], stride=1) # 第四个残差块组,步长为3 self.avgpool = nn.AdaptiveAvgPool2D((1, 1)) self.fc = nn.Linear(32 * block1.expansion + 32*block2.expansion, num_classes) #self.fc = nn.Linear(512 * block1.expansion , num_classes) def _make_layer(self, block, out_channels, num_blocks, stride=1): # 构建一个残差块组,包含多个残差块 # num_blocks: 残差块的数量 downsample = None # 判断是否需要下采样 if stride != 1 or self.in_channels1 != out_channels * block.expansion: downsample = nn.Sequential( nn.Conv2D(self.in_channels1, out_channels * block.expansion, kernel_size=1, stride=stride, bias_attr=False), nn.BatchNorm2D(out_channels * block.expansion), ) layers = [] layers.append(block(self.in_channels1, out_channels, stride, downsample)) self.in_channels1 = out_channels * block.expansion # 添加剩余的残差块,这些块不需要进行下采样 for _ in range(1, num_blocks): layers.append(block(self.in_channels1, out_channels)) return nn.Sequential(*layers) def _make_layers(self, block, out_channels, num_blocks, stride=1): # 构建一个残差块组,包含多个残差块 # num_blocks: 残差块的数量 downsample = None # 判断是否需要下采样 if stride != 1 or self.in_channels2 != out_channels * block.expansion: downsample = nn.Sequential( nn.Conv2D(self.in_channels2, out_channels * block.expansion, kernel_size=1, stride=stride, bias_attr=False), nn.BatchNorm2D(out_channels * block.expansion), ) layers = [] layers.append(block(self.in_channels2, out_channels, stride, downsample)) self.in_channels2 = out_channels * block.expansion # 添加剩余的残差块,这些块不需要进行下采样 for _ in range(1, num_blocks): layers.append(block(self.in_channels2, out_channels)) return nn.Sequential(*layers) def forward(self, x): y = x x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.maxpool(x) x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) x = self.layer4(x) x = self.avgpool(x) x = paddle.flatten(x, 1)# 这里双路径的实现方式,首先创建一个x的副本,然后分别对x和y操作,最后在直接使用concat方法拼接,然后链接一个全连接层输出 y = self.conv2(y) y = self.bn2(y) y = self.relu(y) y = self.maxpool(y) y = self.layer1s(y) y = self.layer2s(y) y = self.layer3s(y) y = self.layer4s(y) y = self.avgpool(y) y = paddle.flatten(y,1)# 搭建网络其实很简单,只需要在__init__中声明层,在forward中调用即可 res = paddle.concat([x,y],axis = 1) #res = paddle.add([x,y],axis = 1) #res = x res = self.fc(res) return res登录后复制 ? ?In [9]

def ManjuanNet(num_classes = 10): return manjuanNet(BasicBlock1,BasicBlock2, [2, 2, 2, 2],num_classes=num_classes)# 在这里声明manjuanNet,列表是每层的块数量,num_classes是分类的类别数登录后复制 ? ?

这里出现了一些失误

在训练时未保存标签的映射字典,后续测试会出现特征不对齐的现象,现已更改代码,后续会再次训练;

In [10]

!pip install pyyamlimport yaml登录后复制 ? ? ? ?

Looking in indexes: https://mirror.baidu.com/pypi/simple/, https://mirrors.aliyun.com/pypi/simple/Requirement already satisfied: pyyaml in /opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages (6.0.2)登录后复制 ? ? ? ?In [11]

import osfrom PIL import Imageimport paddleimport paddle.nn as nnimport paddle.optimizer as optimimport paddle.vision.transforms as Tfrom paddle.io import DataLoader, Datasetfrom paddle.vision.models import resnet18# 自定义数据集类class CustomDataset(Dataset): def __init__(self, root_dir, transform=None): self.root_dir = root_dir self.transform = transform self.classes = sorted(os.listdir(root_dir)) self.class_to_idx = {cls_name: i for i, cls_name in enumerate(self.classes)} # 获取当前工作目录,即.ipynb文件所在的目录 current_dir = os.getcwd() # 将类标签映射保存为YAML文件,文件保存在.ipynb文件所在的目录 yaml_path = os.path.join('./class_to_idx.yaml') with open(yaml_path, 'w') as file: yaml.dump(self.class_to_idx, file, default_flow_style=False) self.images = [] self.labels = [] for cls_name in self.classes: cls_dir = os.path.join(root_dir, cls_name) for img_name in os.listdir(cls_dir): img_path = os.path.join(cls_dir, img_name) self.images.append(img_path) self.labels.append(self.class_to_idx[cls_name]) def __len__(self): return len(self.images) def __getitem__(self, idx): img_path = self.images[idx] label = self.labels[idx] image = Image.open(img_path).convert('RGB') if self.transform: image = self.transform(image) return image, label# 数据预处理和加载transform = T.Compose([ T.Resize((224, 224)), T.ToTensor(), T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),])登录后复制 ? ?

在这里可以更换路径训练

我挂载了两个数据集,一个是比较小的,只有几千张图片的脑肿瘤分类数据集,一个是有十几万张的Imagenet100默认运行使用脑肿瘤分类数据集

In [12]

# 这里是训练集和验证集的路径,按你的路径进行修改,这里的 “./”为从当前文件所处文件夹路径开始向下寻找,具体参考python的路径操作train_dataset = CustomDataset(root_dir='./data/data150555/MyImagenet/train', transform=transform)val_dataset = CustomDataset(root_dir='./data/data150555/MyImagenet/val', transform=transform)train_loader = DataLoader(train_dataset, batch_size=48, shuffle=True, drop_last=True)val_loader = DataLoader(val_dataset, batch_size=48, shuffle=False)# 模型、损失函数和优化器device = paddle.set_device('gpu' if paddle.is_compiled_with_cuda() else 'cpu')num_classes = len(train_dataset.classes) # 根据数据集的类别数调整模型输出类别数model = ManjuanNet(num_classes=num_classes)#import paddle.visionw.models as models#model = models.resnet34()model = model.to(device)criterion = nn.CrossEntropyLoss()optimizer = optim.AdamW(parameters=model.parameters(), learning_rate=0.001,epsilon=1e-7)#使用Adamw优化器,初始学习率设置为0.001,也有很多其他优化器可以使用的,详细参考我都另一个项目或自行上网搜索登录后复制 ? ? ? ?

W0109 15:39:37.189237 60986 gpu_resources.cc:119] Please NOTE: device: 0, GPU Compute Capability: 7.0, Driver API Version: 12.0, Runtime API Version: 11.8W0109 15:39:37.191516 60986 gpu_resources.cc:164] device: 0, cuDNN Version: 8.9.登录后复制 ? ? ? ?In [13]

model登录后复制 ? ? ? ?

manjuanNet( (conv1): Conv2D(3, 64, kernel_size=[7, 7], stride=[2, 2], padding=3, data_format=NCHW) (conv2): Conv2D(3, 256, kernel_size=[5, 5], padding=3, data_format=NCHW) (bn1): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) (bn2): BatchNorm2D(num_features=256, momentum=0.9, epsilon=1e-05) (relu): ReLU() (maxpool): MaxPool2D(kernel_size=3, stride=1, padding=1) (layer1): Sequential( (0): BasicBlock1( (conv1): Conv2D(64, 64, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(64, 64, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) ) (1): BasicBlock1( (conv1): Conv2D(64, 64, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(64, 64, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) ) ) (layer2): Sequential( (0): BasicBlock1( (conv1): Conv2D(64, 128, kernel_size=[5, 5], stride=[2, 2], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=128, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(128, 128, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=128, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(64, 128, kernel_size=[1, 1], stride=[2, 2], data_format=NCHW) (1): BatchNorm2D(num_features=128, momentum=0.9, epsilon=1e-05) ) ) (1): BasicBlock1( (conv1): Conv2D(128, 128, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=128, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(128, 128, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=128, momentum=0.9, epsilon=1e-05) ) ) (layer3): Sequential( (0): BasicBlock1( (conv1): Conv2D(128, 64, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(64, 64, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(128, 64, kernel_size=[1, 1], data_format=NCHW) (1): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) ) ) (1): BasicBlock1( (conv1): Conv2D(64, 64, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(64, 64, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=64, momentum=0.9, epsilon=1e-05) ) ) (layer4): Sequential( (0): BasicBlock1( (conv1): Conv2D(64, 32, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(32, 32, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(64, 32, kernel_size=[1, 1], data_format=NCHW) (1): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) ) ) (1): BasicBlock1( (conv1): Conv2D(32, 32, kernel_size=[5, 5], padding=2, data_format=NCHW) (bn1): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(32, 32, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) ) ) (layer1s): Sequential( (0): BasicBlock2( (conv1): Conv2D(256, 4, kernel_size=[1, 1], data_format=NCHW) (bn1): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(4, 4, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) (conv3): Conv2D(4, 4, kernel_size=[1, 1], data_format=NCHW) (bn3): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(256, 4, kernel_size=[1, 1], data_format=NCHW) (1): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) ) ) (1): BasicBlock2( (conv1): Conv2D(4, 4, kernel_size=[1, 1], data_format=NCHW) (bn1): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(4, 4, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) (conv3): Conv2D(4, 4, kernel_size=[1, 1], data_format=NCHW) (bn3): BatchNorm2D(num_features=4, momentum=0.9, epsilon=1e-05) ) ) (layer2s): Sequential( (0): BasicBlock2( (conv1): Conv2D(4, 8, kernel_size=[1, 1], stride=[2, 2], data_format=NCHW) (bn1): BatchNorm2D(num_features=8, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(8, 8, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=8, momentum=0.9, epsilon=1e-05) (conv3): Conv2D(8, 8, kernel_size=[1, 1], data_format=NCHW) (bn3): BatchNorm2D(num_features=8, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(4, 8, kernel_size=[1, 1], stride=[2, 2], data_format=NCHW) (1): BatchNorm2D(num_features=8, momentum=0.9, epsilon=1e-05) ) ) ) (layer3s): Sequential( (0): BasicBlock2( (conv1): Conv2D(8, 16, kernel_size=[1, 1], data_format=NCHW) (bn1): BatchNorm2D(num_features=16, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(16, 16, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=16, momentum=0.9, epsilon=1e-05) (conv3): Conv2D(16, 16, kernel_size=[1, 1], data_format=NCHW) (bn3): BatchNorm2D(num_features=16, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(8, 16, kernel_size=[1, 1], data_format=NCHW) (1): BatchNorm2D(num_features=16, momentum=0.9, epsilon=1e-05) ) ) ) (layer4s): Sequential( (0): BasicBlock2( (conv1): Conv2D(16, 32, kernel_size=[1, 1], data_format=NCHW) (bn1): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(32, 32, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (conv3): Conv2D(32, 32, kernel_size=[1, 1], data_format=NCHW) (bn3): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (downsample): Sequential( (0): Conv2D(16, 32, kernel_size=[1, 1], data_format=NCHW) (1): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) ) ) (1): BasicBlock2( (conv1): Conv2D(32, 32, kernel_size=[1, 1], data_format=NCHW) (bn1): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (relu): ReLU() (conv2): Conv2D(32, 32, kernel_size=[3, 3], padding=1, data_format=NCHW) (bn2): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) (conv3): Conv2D(32, 32, kernel_size=[1, 1], data_format=NCHW) (bn3): BatchNorm2D(num_features=32, momentum=0.9, epsilon=1e-05) ) ) (avgpool): AdaptiveAvgPool2D(output_size=(1, 1)) (fc): Linear(in_features=64, out_features=100, dtype=None))登录后复制 ? ? ? ? ? ? ? ?In [14]

from datetime import datetime# 获取当前时间current_time = datetime.now()# 格式化输出当前时间formatted_time = current_time.strftime(”%Y-%m-%d %H:%M:%S“)# 打印当前时间print(”当前时间:“, formatted_time)登录后复制 ? ? ? ?

当前时间: 2025-01-09 15:39:39登录后复制 ? ? ? ?In [?]

# 训练循环num_epochs = 20 # 初始设置为20轮,可根据需求设置for epoch in range(num_epochs): model.train() for batch_id, (data, label) in enumerate(train_loader): data, label = data.to(device), label.to(device) optimizer.clear_grad() output = model(data) loss = criterion(output, label) loss.backward() optimizer.step() if batch_id % 50 == 0: print(f'Epoch [{epoch+1}/{num_epochs}], Step [{batch_id}/{len(train_loader)}], Loss: {loss.item():.4f}') # 获取当前时间 current_time = datetime.now() # 格式化输出当前时间 formatted_time = current_time.strftime(”%Y-%m-%d %H:%M:%S“) # 打印当前时间 print(”当前时间:“, formatted_time) #break # 打开一个文件用于写入,如果文件不存在则创建 with open('training_log.txt', 'a') as f: f.write(f'Epoch [{epoch+1}/{num_epochs}], Step [{batch_id}/{len(train_loader)}], Loss: {loss.item():.4f}, Time:[{formatted_time}]n') # 验证 model.eval() correct = 0 total = 0 with paddle.no_grad(): for data, label in val_loader: data, label = data.to(device), label.to(device) output = model(data) _, predicted = paddle.topk(output, k=1) total += label.shape[0] correct += (predicted.squeeze() == label).astype('int32').sum().item() #if batch_id % 100 == 0: # break accuracy = correct / total paddle.save(model.state_dict(), 'imagenet100.pdparams')# 保存模型 print(f'Validation Accuracy: {accuracy:.10f}')登录后复制 ? ? ? ?

Epoch [1/20], Step [0/2083], Loss: 4.7851当前时间: 2025-01-09 15:39:42Epoch [1/20], Step [50/2083], Loss: 4.5001当前时间: 2025-01-09 15:40:14Epoch [1/20], Step [100/2083], Loss: 4.4377当前时间: 2025-01-09 15:40:48Epoch [1/20], Step [150/2083], Loss: 4.3690当前时间: 2025-01-09 15:41:21Epoch [1/20], Step [200/2083], Loss: 4.3088当前时间: 2025-01-09 15:41:54Epoch [1/20], Step [250/2083], Loss: 4.2381当前时间: 2025-01-09 15:42:26Epoch [1/20], Step [300/2083], Loss: 4.3055当前时间: 2025-01-09 15:42:58登录后复制 ? ? ? ?

下面两个框是对比参数量的代码

In [?]

model = ManjuanNet(num_classes=10)def count_parameters(model): total_params = 0 for param in model.parameters(): total_params += param.numel() return total_params# 计算并打印模型的参数量total_params = count_parameters(model)print(f”Total number of parameters: {total_params}“)登录后复制 ? ?In [?]

import paddleimport paddle.vision.models as models# 创建 ResNet-34 模型实例model1 = models.resnet34()# 获取模型的参数量def count_parameters(model): total_params = 0 for param in model.parameters(): total_params += param.numel() return total_params# 计算并打印模型的参数量total_params = count_parameters(model1)print(f”Total number of parameters in ResNet-34: {total_params}“)登录后复制 ? ?

在这里计算和对比FLOPS

In [?]

# 定义输入尺寸input_shape = (1, 3, 224, 224) # 假设输入图像的尺寸为224x224,通道数为3# 计算FLOPSflops = paddle.flops(model, input_shape)print(f”FLOPS: {flops}“)登录后复制 ? ?In [?]

# 定义输入尺寸input_shape = (1, 3, 224, 224) # 假设输入图像的尺寸为224x224,通道数为3# 计算FLOPSflops = paddle.flops(model1, input_shape)print(f”FLOPS: {flops}“)登录后复制 ? ?In [?]

<br/>登录后复制 ? ?

福利游戏

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多