当前位置: 首页 > AI > 文章内容页

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

时间:2025-07-23    作者:游乐小编    

本文展示了漫卷搭建首个神经网络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 zipfile\nimport os\n\ndef unzip_file(zip_path):\n    # 获取 ZIP 文件所在的目录\n    extract_to = os.path.dirname(zip_path)\n\n    # 打开 ZIP 文件\n    with zipfile.ZipFile(zip_path, \'r\') as zip_ref:\n        # 解压所有文件到目标路径\n        zip_ref.extractall(extract_to)\n\n    print(f"ZIP 文件 \'{zip_path}\' 已成功解压到 \'{extract_to}\'")\n\n# 示例使用\nzip_path = \'./data/data150555/MyImagenet.zip\'  # ZIP 文件的路径\n\nunzip_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 os\nfrom PIL import Image\n\ndef 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)\n\n# 指定根文件夹路径\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 zipfile\nimport os\n\ndef unzip_file(zip_path):\n    # 获取 ZIP 文件所在的目录\n    extract_to = os.path.dirname(zip_path)\n\n    # 打开 ZIP 文件\n    with zipfile.ZipFile(zip_path, \'r\') as zip_ref:\n        # 解压所有文件到目标路径\n        zip_ref.extractall(extract_to)\n\n    print(f"ZIP 文件 \'{zip_path}\' 已成功解压到 \'{extract_to}\'")\n\n# 示例使用\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 [ ]

登录后复制    

热门推荐

更多

热门文章

更多

首页  返回顶部

本站所有软件都由网友上传,如有侵犯您的版权,请发邮件youleyoucom@outlook.com