作者:欧新宇(Xinyu OU)
当前版本:Release v1.0
开发平台:Paddle 3.0.0-beta1
运行环境:Intel Core i7-7700K CPU 4.2GHz, nVidia GeForce RTX 4080
本教案所涉及的数据集仅用于教学和交流使用,请勿用作商用。
最后更新:2025年3月8日
函数式编程作为一种编程范式,将计算机运算视作数学上的函数计算过程。它采用纯函数与不可变数据结构,有效规避了程序状态管理和易变对象的使用难题。此编程范式带来了诸多显著优势:它极大地提升了代码的可读性和可维护性,使得程序流程更加清晰,易于追踪与推理;模块化编程的特性简化了单元测试流程;不可变性原则不仅为并发代码的设计带来了便利,还显著增强了代码的可重用性和可组合性;此外,模块化编程还有助于加速开发进程,减少代码冗余,使程序结构更加简洁明了。正因如此,函数式编程在现代软件开发领域日益受到青睐,成为提升代码质量和开发效率不可或缺的重要工具。本项目将使用 ResNet18, ResNet50, Mobilenetv2, VGG16 四个模型对 十二生肖数据集
,蝴蝶分类数据集
进行训练和评估。
本页面为代码的发布版,包含三个主要部分,分别是模型训练、模型离线评估和推理预测。自主学习请访问 学生版(部分关键代码) 和 教学版(包含完整代码)。在本项目中,我们同样将使用函数式编程的方式,将代码模块化,并使用文件夹结构来管理代码和输出结果。为了便于管理代码和输出结果,我们将根据骨干模型和数据集设置不同的配置文件,其命名方式为 config_数据集名_模型名.yaml
。例如,对于ResNet50模型在蝴蝶分类数据集上的配置文件命名为 config_Butterfly_resnet50.yaml
。下面给出完整代码。
数据集准备是整个人工智能项目的前提,通常在项目进行之前就需要完成。在实际工程中,数据集的准备还涉及各种类型的数据分析,也就是说通常都需要进行数据清洗、数据标注和数据列表生成等步骤。本项目所使用的 十二生肖数据集
包含样本12个类,其中训练验证集样本7840个,训练集样本7190个, 验证集样本650个, 测试集样本660个, 共计8500个;蝴蝶分类数据集
包含7个不同种类的蝴蝶,合计619个样本,我们将按照 7:1:2
的比例划分为训练集、验证集和测试集。关于数据准备和数据集的划分的相关知识,请参考 【项目003】数据准备(Data Preparation) 或 第4.2节 数据准备。
以下给出这两个数据集的下载地址和数据列表生成代码,有兴趣的同学可以阅读这个两个数据集生成列表的源代码,了解数据列表生成的细节。需要重新生成时,注意根据操作系统环境和实际的文件夹配置修改源代码中的路径。
import os # 系统库,用于获取系统信息和路径定义等 import yaml # 导入yaml库,用于读取配置文件 import json # 导入json库,用于读写配置文件 import sys # 系统库,用于获取模块路径 sys.path.append('D:/WorkSpace/DeepLearning/WebsiteV2') # 定义模块保存位置 # sys.path.append('/home/aistudio/work/teaching') # 定义模块保存位置 from utils.getSystemInfo import getSystemInfo # 导入系统信息模块 from utils.train import train # 导入训练模块 from utils.evaluate import eval # 导入评估模块 from utils.datasets import Dataset # 导入数据集模块 from utils.getLogging import init_log_config # 导入日志模块 from utils.getOptimizer import learning_rate_setting, optimizer_setting # 导入优化器模块 from utils.getVisualization import draw_process # 导入绘图模块 import paddle # 导入百度paddle库,用于深度学习框架 from paddle.io import DataLoader # 导入百度Paddle的数据加载模块 from paddle.static import InputSpec # 导入百度Paddle的输入规格模块 import warnings # 导入警告库 warnings.filterwarnings('ignore', category=UserWarning) # 调用warnings库,忽略警告信息,请谨慎使用,可能会在调试时掩盖潜在的问题,因此建议在开发和调试阶段保持警告开启。 os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" # 解决多线程报错问题 ##################################################################################### # 1. 从本地磁盘读取 YAML 配置文件,获取全局配置参数 config_root_path = '../../Data/Projects/Project013Modularization/' config_name = 'config_Zodiac_mobilenet_v2.yaml' config_path = os.path.join(config_root_path, config_name) with open(config_path, 'r', encoding='utf-8') as yaml_file: args = yaml.safe_load(yaml_file) # 2. 本地环境配置 # 2.1 初始化模型名称,用于区分和管理当前任务 args['model_name'] = args['dataset_name'] + '_' + args['architecture'] if args['pretrained'] is False: args['model_name'] = args['model_name'] + '_without_pretrained' # 2.2 定义设备工作模式 [GPU|CPU] # 定义使用CPU还是GPU,使用CPU时use_cuda = False,使用GPU时use_cuda = True def init_device(useGPU=args['useGPU']): paddle.set_device('gpu:0') if useGPU else paddle.set_device('cpu') init_device() # 2.3 初始化本地路径 # 2.3.1 数据集路径 dataset_root_path = args['dataset_root_path'] = os.path.join(args['dataset_root_path'], args['dataset_name']) # 根据数据集的名称重写数据集根路径 # 2.3.2 初始化结果路径(用于开发环节中的保存训练结果、微调和验证) result_root_path = args['result_root_path'] = os.path.join(args['result_root_path'], args['project_name']) # 根据数据集的名称重写结果根路径 args['results_path']['final_models_path'] = os.path.join(result_root_path, 'final_models') # 最终用于部署和推理的模型 args['results_path']['final_figures_path'] = os.path.join(result_root_path, 'final_figures') # 训练过程中loss和accuracy曲线图 args['results_path']['checkpoint_models_path'] = os.path.join(result_root_path, 'checkpoint_models') # 带模型状态的微调模型 args['results_path']['logs_path'] = os.path.join(result_root_path, 'logs') # 训练过程日志 # 2.3.3 部署路径(用于推理和预测) deployment_root_path = args['deployment_root_path'] = os.path.join(args['deployment_root_path'], args['project_name']) args['deployments_path']['deployment_checkpoint_model_path'] = os.path.join(deployment_root_path, 'checkpoint_models') args['deployments_path']['deployment_final_model_path'] = os.path.join(deployment_root_path, 'final_models') args['deployments_path']['deployment_final_figures_path'] = os.path.join(deployment_root_path, 'final_figures') args['deployments_path']['deployment_logs_path'] = os.path.join(deployment_root_path, 'logs') args['deployments_path']['deployment_pretrained_model_path'] = os.path.join(deployment_root_path, 'pretrained_models') # 2.3.4 检查结果路径和部署路径是否存在,若不存在,则创建 for path in {**args['results_path'], **args['deployments_path']}.values(): if not os.path.exists(path): os.makedirs(path) # 2.4 初始化数据集参数 dataset_info = json.loads(open(os.path.join(args['dataset_root_path'] , 'dataset_info.json'), 'r', encoding='utf-8').read()) fields_dataset = ['num_trainval', 'num_train', 'num_val', 'num_test', 'class_dim', 'label_dict'] args.update({field: dataset_info[field] for field in fields_dataset}) # 2.5 初始化日志配置 logger = init_log_config(args['results_path']['logs_path'], args['model_name']) # 初始化日志模块为logger对象,配置日志输出路径和模型名称 # 3. 将生成的完整配置文件(按需求)保存成部署配置文件 fields_deploy = ['dataset_root_path', 'model_name', 'dataset_name', 'deployments_path', 'num_test', 'class_dim', 'label_dict'] args_deploy = {field: args[field] for field in fields_deploy} config_depoly_path = os.path.join(config_root_path, config_name.split('.')[0] + '_deploy.yaml') with open(config_depoly_path, 'w') as f: yaml.dump(args_deploy, f, default_flow_style=False, sort_keys=False) ###################################################33 # 输出完整的训练参数 args # if __name__ == '__main__': # print(json.dumps(args, indent=4, sort_keys=False))
# 1. 设置是否使用预训练模型 if args['pretrained'] == True: logger.info('载入 Imagenet-{} 预训练模型完毕,开始微调训练(fine-tune)。'.format(args['architecture'])) elif args['pretrained'] == False: logger.info('载入 {} 模型完毕,从初始状态开始训练。'.format(args['architecture'])) # 2. 获取训练所用的数据子集的数据,并初始化数据样本的维度 # 2.1 实例化各个数据使用数据集类获取批量数据 dataset_trainval = Dataset(dataset_root_path, args=args, mode='trainval') dataset_train = Dataset(dataset_root_path, args=args, mode='train') dataset_val = Dataset(dataset_root_path, args=args, mode='val') trainval_reader = DataLoader(dataset_trainval, batch_size=args['batch_size'], shuffle=False) train_reader = DataLoader(dataset_train, batch_size=args['batch_size'], shuffle=False) val_reader = DataLoader(dataset_val, batch_size=args['batch_size'], shuffle=False) # 2.2 设置输入样本的维度 input_spec = InputSpec(shape=[None] + args['input_size'], dtype='float32', name='image') label_spec = InputSpec(shape=[None, 1], dtype='int64', name='label') ##################################################################################### # 3. 初始化训练模型 # 3.1 自动设置训练数据来源,注意使用trainval时只进行训练,不进行验证 if args['training_data'] == 'trainval': train_reader = trainval_reader isValid = False elif args['training_data'] == 'train': train_reader = train_reader isValid = True # 3.2 初始化模型 # 载入官方标准模型,若不存在则会自动进行下载,pretrained=True|False控制是否使用Imagenet预训练参数 # 使用getattr函数动态获取模型类,例如paddle.vision.models.args['architecture'],args['architecture']为模型名称,例如mobilenet_v2 model_builder = getattr(paddle.vision.models, args['architecture']) network = model_builder(num_classes=args['class_dim'], pretrained=args['pretrained']) model = paddle.Model(network, input_spec, label_spec) # 3.3 设置学习率、优化器、损失函数和评价指标 lr = learning_rate_setting(args=args) optimizer = optimizer_setting(model, lr, args['learning_strategy']) model.prepare(optimizer, paddle.nn.CrossEntropyLoss(), paddle.metric.Accuracy(topk=(1,5))) ##################################################################################### # 4. 开始模型训练 # 4.1 输出系统硬件信息 logger.info('系统基本信息:') system_info = json.dumps(getSystemInfo(), indent=4, ensure_ascii=False, sort_keys=False, separators=(',', ':')) logger.info(system_info) # 4.2 输出训练的超参数信息 data = json.dumps(args, indent=4, ensure_ascii=False, sort_keys=False, separators=(',', ':')) # 格式化字典格式的参数列表 logger.info(data) # 4.3 输出训练的超参数信息 logger.info('模型参数信息:') logger.info(model.summary()) # 是否显示神经网络的具体信息 # 4.4 启动训练过程 visualization_log = train(model, args=args, train_reader=train_reader, val_reader=val_reader, logger=logger) # 4.5 输出训练过程图 draw_process(visualization_log, figure_path=args['results_path']['final_figures_path'], model_name=args['model_name'], isValid=isValid) logger.info('Done.')
[2025-03-23 14:32:55,932] [ INFO] 3545894799.py:3 - 载入 Imagenet-mobilenet_v2 预训练模型完毕,开始微调训练(fine-tune)。
[2025-03-23 14:32:56,081] [ INFO] 3545894799.py:48 - 系统基本信息:
当前学习率策略为: Adam + CosineAnnealingDecay, 初始学习率为:0.01
[2025-03-23 14:32:57,124] [ INFO] 3545894799.py:50 - {
"操作系统":"Windows-10-10.0.26100-SP0",
"Paddle":"3.0.0-beta1",
"CPU":"13th Gen Intel(R) Core(TM) i7-13700KF",
"内存":"13.77G/31.85G (43.20%)",
"GPU":"NVIDIA GeForce RTX 4080 SUPER 5.74G/15.99G (0.36%)",
"CUDA/cuDNN":"12.3 / 9.0.0"
}
[2025-03-23 14:32:57,126] [ INFO] 3545894799.py:54 - {
"project_name":"Project013Modularization",
"dataset_name":"Zodiac",
"architecture":"mobilenet_v2",
"model_name":"Zodiac_mobilenet_v2",
"training_data":"train",
"input_size":[
3,
227,
227
],
"mean_value":[
0.485,
0.456,
0.406
],
"std_value":[
0.229,
0.224,
0.225
],
"num_trainval":7840,
"num_train":7190,
"num_val":650,
"num_test":660,
"total_epoch":50,
"batch_size":64,
"class_dim":12,
"log_interval":10,
"eval_interval":1,
"pretrained":true,
"useGPU":true,
"dataset_root_path":"D:/workspace/ExpDatasets\\Zodiac",
"result_root_path":"D:/Workspace/ExpResults\\Project013Modularization",
"deployment_root_path":"D:/Workspace/ExpDeployments\\Project013Modularization",
"augmentation":{
"augmentation_prob":1,
"rotate_angle":15,
"Vflip_prob":0,
"Hflip_prob":0.5,
"brightness":0.2,
"contrast":0.2,
"saturation":0.2,
"hue":0.1
},
"learning_strategy":{
"optimizer_strategy":"Adam",
"learning_rate_strategy":"CosineAnnealingDecay",
"learning_rate":0.01,
"momentum":0.9,
"Piecewise_boundaries":[
20,
40,
45
],
"Piecewise_values":[
0.0001,
1e-05,
1e-06,
1e-07
],
"Exponential_gamma":0.9,
"Polynomial_decay_steps":10,
"verbose":false
},
"results_path":{
"logs_path":"D:/Workspace/ExpResults\\Project013Modularization\\logs",
"final_figures_path":"D:/Workspace/ExpResults\\Project013Modularization\\final_figures",
"checkpoint_models_path":"D:/Workspace/ExpResults\\Project013Modularization\\checkpoint_models",
"final_models_path":"D:/Workspace/ExpResults\\Project013Modularization\\final_models"
},
"deployments_path":{
"deployment_logs_path":"D:/Workspace/ExpDeployments\\Project013Modularization\\logs",
"deployment_final_figures_path":"D:/Workspace/ExpDeployments\\Project013Modularization\\final_figures",
"deployment_checkpoint_model_path":"D:/Workspace/ExpDeployments\\Project013Modularization\\checkpoint_models",
"deployment_final_model_path":"D:/Workspace/ExpDeployments\\Project013Modularization\\final_models",
"deployment_pretrained_model_path":"D:/Workspace/ExpDeployments\\Project013Modularization\\pretrained_models"
},
"label_dict":{
"0":"dog",
"1":"dragon",
"2":"goat",
"3":"horse",
"4":"monkey",
"5":"ox",
"6":"pig",
"7":"rabbit",
...
}
}
[2025-03-23 14:32:57,126] [ INFO] 3545894799.py:57 - 模型参数信息:
[2025-03-23 14:32:57,139] [ INFO] 3545894799.py:58 - {'total_params': 2273356, 'trainable_params': 2239244}
Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...
-------------------------------------------------------------------------------
Layer (type) Input Shape Output Shape Param #
===============================================================================
Conv2D-53 [[1, 3, 227, 227]] [1, 32, 114, 114] 864
BatchNorm2D-53 [[1, 32, 114, 114]] [1, 32, 114, 114] 128
ReLU6-36 [[1, 32, 114, 114]] [1, 32, 114, 114] 0
Conv2D-54 [[1, 32, 114, 114]] [1, 32, 114, 114] 288
BatchNorm2D-54 [[1, 32, 114, 114]] [1, 32, 114, 114] 128
ReLU6-37 [[1, 32, 114, 114]] [1, 32, 114, 114] 0
Conv2D-55 [[1, 32, 114, 114]] [1, 16, 114, 114] 512
BatchNorm2D-55 [[1, 16, 114, 114]] [1, 16, 114, 114] 64
InvertedResidual-18 [[1, 32, 114, 114]] [1, 16, 114, 114] 0
Conv2D-56 [[1, 16, 114, 114]] [1, 96, 114, 114] 1,536
BatchNorm2D-56 [[1, 96, 114, 114]] [1, 96, 114, 114] 384
ReLU6-38 [[1, 96, 114, 114]] [1, 96, 114, 114] 0
Conv2D-57 [[1, 96, 114, 114]] [1, 96, 57, 57] 864
BatchNorm2D-57 [[1, 96, 57, 57]] [1, 96, 57, 57] 384
ReLU6-39 [[1, 96, 57, 57]] [1, 96, 57, 57] 0
Conv2D-58 [[1, 96, 57, 57]] [1, 24, 57, 57] 2,304
BatchNorm2D-58 [[1, 24, 57, 57]] [1, 24, 57, 57] 96
InvertedResidual-19 [[1, 16, 114, 114]] [1, 24, 57, 57] 0
Conv2D-59 [[1, 24, 57, 57]] [1, 144, 57, 57] 3,456
BatchNorm2D-59 [[1, 144, 57, 57]] [1, 144, 57, 57] 576
ReLU6-40 [[1, 144, 57, 57]] [1, 144, 57, 57] 0
Conv2D-60 [[1, 144, 57, 57]] [1, 144, 57, 57] 1,296
BatchNorm2D-60 [[1, 144, 57, 57]] [1, 144, 57, 57] 576
ReLU6-41 [[1, 144, 57, 57]] [1, 144, 57, 57] 0
Conv2D-61 [[1, 144, 57, 57]] [1, 24, 57, 57] 3,456
BatchNorm2D-61 [[1, 24, 57, 57]] [1, 24, 57, 57] 96
InvertedResidual-20 [[1, 24, 57, 57]] [1, 24, 57, 57] 0
Conv2D-62 [[1, 24, 57, 57]] [1, 144, 57, 57] 3,456
BatchNorm2D-62 [[1, 144, 57, 57]] [1, 144, 57, 57] 576
ReLU6-42 [[1, 144, 57, 57]] [1, 144, 57, 57] 0
Conv2D-63 [[1, 144, 57, 57]] [1, 144, 29, 29] 1,296
BatchNorm2D-63 [[1, 144, 29, 29]] [1, 144, 29, 29] 576
ReLU6-43 [[1, 144, 29, 29]] [1, 144, 29, 29] 0
Conv2D-64 [[1, 144, 29, 29]] [1, 32, 29, 29] 4,608
BatchNorm2D-64 [[1, 32, 29, 29]] [1, 32, 29, 29] 128
InvertedResidual-21 [[1, 24, 57, 57]] [1, 32, 29, 29] 0
Conv2D-65 [[1, 32, 29, 29]] [1, 192, 29, 29] 6,144
BatchNorm2D-65 [[1, 192, 29, 29]] [1, 192, 29, 29] 768
ReLU6-44 [[1, 192, 29, 29]] [1, 192, 29, 29] 0
Conv2D-66 [[1, 192, 29, 29]] [1, 192, 29, 29] 1,728
BatchNorm2D-66 [[1, 192, 29, 29]] [1, 192, 29, 29] 768
ReLU6-45 [[1, 192, 29, 29]] [1, 192, 29, 29] 0
Conv2D-67 [[1, 192, 29, 29]] [1, 32, 29, 29] 6,144
BatchNorm2D-67 [[1, 32, 29, 29]] [1, 32, 29, 29] 128
InvertedResidual-22 [[1, 32, 29, 29]] [1, 32, 29, 29] 0
Conv2D-68 [[1, 32, 29, 29]] [1, 192, 29, 29] 6,144
BatchNorm2D-68 [[1, 192, 29, 29]] [1, 192, 29, 29] 768
ReLU6-46 [[1, 192, 29, 29]] [1, 192, 29, 29] 0
Conv2D-69 [[1, 192, 29, 29]] [1, 192, 29, 29] 1,728
BatchNorm2D-69 [[1, 192, 29, 29]] [1, 192, 29, 29] 768
ReLU6-47 [[1, 192, 29, 29]] [1, 192, 29, 29] 0
Conv2D-70 [[1, 192, 29, 29]] [1, 32, 29, 29] 6,144
BatchNorm2D-70 [[1, 32, 29, 29]] [1, 32, 29, 29] 128
InvertedResidual-23 [[1, 32, 29, 29]] [1, 32, 29, 29] 0
Conv2D-71 [[1, 32, 29, 29]] [1, 192, 29, 29] 6,144
BatchNorm2D-71 [[1, 192, 29, 29]] [1, 192, 29, 29] 768
ReLU6-48 [[1, 192, 29, 29]] [1, 192, 29, 29] 0
Conv2D-72 [[1, 192, 29, 29]] [1, 192, 15, 15] 1,728
BatchNorm2D-72 [[1, 192, 15, 15]] [1, 192, 15, 15] 768
ReLU6-49 [[1, 192, 15, 15]] [1, 192, 15, 15] 0
Conv2D-73 [[1, 192, 15, 15]] [1, 64, 15, 15] 12,288
BatchNorm2D-73 [[1, 64, 15, 15]] [1, 64, 15, 15] 256
InvertedResidual-24 [[1, 32, 29, 29]] [1, 64, 15, 15] 0
Conv2D-74 [[1, 64, 15, 15]] [1, 384, 15, 15] 24,576
BatchNorm2D-74 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
ReLU6-50 [[1, 384, 15, 15]] [1, 384, 15, 15] 0
Conv2D-75 [[1, 384, 15, 15]] [1, 384, 15, 15] 3,456
BatchNorm2D-75 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
ReLU6-51 [[1, 384, 15, 15]] [1, 384, 15, 15] 0
Conv2D-76 [[1, 384, 15, 15]] [1, 64, 15, 15] 24,576
BatchNorm2D-76 [[1, 64, 15, 15]] [1, 64, 15, 15] 256
InvertedResidual-25 [[1, 64, 15, 15]] [1, 64, 15, 15] 0
Conv2D-77 [[1, 64, 15, 15]] [1, 384, 15, 15] 24,576
BatchNorm2D-77 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
ReLU6-52 [[1, 384, 15, 15]] [1, 384, 15, 15] 0
Conv2D-78 [[1, 384, 15, 15]] [1, 384, 15, 15] 3,456
BatchNorm2D-78 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
ReLU6-53 [[1, 384, 15, 15]] [1, 384, 15, 15] 0
Conv2D-79 [[1, 384, 15, 15]] [1, 64, 15, 15] 24,576
BatchNorm2D-79 [[1, 64, 15, 15]] [1, 64, 15, 15] 256
InvertedResidual-26 [[1, 64, 15, 15]] [1, 64, 15, 15] 0
Conv2D-80 [[1, 64, 15, 15]] [1, 384, 15, 15] 24,576
BatchNorm2D-80 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
ReLU6-54 [[1, 384, 15, 15]] [1, 384, 15, 15] 0
Conv2D-81 [[1, 384, 15, 15]] [1, 384, 15, 15] 3,456
BatchNorm2D-81 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
ReLU6-55 [[1, 384, 15, 15]] [1, 384, 15, 15] 0
Conv2D-82 [[1, 384, 15, 15]] [1, 64, 15, 15] 24,576
BatchNorm2D-82 [[1, 64, 15, 15]] [1, 64, 15, 15] 256
InvertedResidual-27 [[1, 64, 15, 15]] [1, 64, 15, 15] 0
Conv2D-83 [[1, 64, 15, 15]] [1, 384, 15, 15] 24,576
BatchNorm2D-83 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536
...
Estimated Total Size (MB): 174.34
-------------------------------------------------------------------------------
启动训练...
[2025-03-21 18:17:52,261] [ INFO] train.py:54 - Epoch:1/5, batch:1, train_loss:[1.46126], acc_top1:[0.32812], acc_top5:[1.00000](0.43s)
[2025-03-21 18:17:52,420] [ INFO] train.py:54 - Epoch:1/5, batch:2, train_loss:[1.21415], acc_top1:[0.54688], acc_top5:[0.98438](0.16s)
...
[2025-03-21 18:18:01,386] [ INFO] train.py:65 - [validation] Epoch:5/5, val_loss:[0.00004], val_top1:[1.00000], val_top5:[1.00000]
[2025-03-21 18:18:01,387] [ INFO] train.py:89 - 最优top1测试精度:1.00000 (epoch=1)
[2025-03-21 18:18:01,387] [ INFO] train.py:102 - 训练完成,总耗时9.55s, 已将其保存为:Butterfly_resnet18_final
训练完成,最终性能accuracy=1.00000(epoch=1), 总耗时9.55s
日志图已保存至:D:/Workspace/ExpResults\Project013Modularization\final_figures\training_log_Butterfly_resnet18.png
[2025-03-21 18:18:01,575] [ INFO] 2069346315.py:105 - Done.
import os import yaml import sys sys.path.append('D:/WorkSpace/DeepLearning/WebsiteV2') # 定义模块保存位置 # sys.path.append('/home/aistudio/work/teaching') # 定义模块保存位置 from utils.evaluate import eval from utils.datasets import Dataset import paddle from paddle.io import DataLoader from paddle.static import InputSpec import warnings warnings.filterwarnings("ignore") # 调用warnings库,忽略警告信息,请谨慎使用,可能会在调试时掩盖潜在的问题,因此建议在开发和调试阶段保持警告开启。 # 1. 从本地磁盘读取 YAML 配置文件,获取全局配置参数,并初始化本地路径和参数 config_path = '../../Data/Projects/Project013Modularization/config_Butterfly_resnet18.yaml' with open(config_path, 'r', encoding='utf-8') as yaml_file: args = yaml.safe_load(yaml_file) args['model_name'] = args['dataset_name'] + '_' + args['architecture'] if args['pretrained'] is False: args['model_name'] = args['model_name'] + '_without_pretrained' dataset_root_path = os.path.join(args['dataset_root_path'], args['dataset_name']) # 根据数据集的名称拼接当前任务数据集的根目录路径 checkpoint_models_path = os.path.join(args['results_root_path'], args['project_name'], 'checkpoint_models', args['model_name'] + '_0-01_final') # 2. 使用数据集类获取批量数据 dataset_val = Dataset(dataset_root_path, args=args, mode='val') val_reader = DataLoader(dataset_val, batch_size=args['batch_size'], shuffle=False, drop_last=False) dataset_test = Dataset(dataset_root_path, args=args, mode='test') test_reader = DataLoader(dataset_test, batch_size=args['batch_size'], shuffle=False, drop_last=False) # 3. 设置输入样本的维度 input_spec = InputSpec(shape=[None] + args['input_size'], dtype='float32', name='image') label_spec = InputSpec(shape=[None, 1], dtype='int64', name='label') # 4. 载入官方标准模型,若不存在则会自动进行下载,pretrained=True|False控制是否使用Imagenet预训练参数 # 使用getattr函数动态获取模型类,例如paddle.vision.models.args['architecture'],args['architecture']为模型名称,例如mobilenet_v2 model = getattr(paddle.vision.models, args['architecture']) network = model(num_classes=args['class_dim'], pretrained=args['pretrained']) # 5. 初始化模型并输出基本信息 model = paddle.Model(network, input_spec, label_spec) # TODO: 载入预先训练好的模型参数 model.load(checkpoint_models_path) # 载入调优模型的参数 model.prepare(loss=paddle.nn.CrossEntropyLoss(), # 设置loss metrics=paddle.metric.Accuracy(topk=(1,5))) # 设置评价指标 # 6. 执行评估函数,并输出验证集样本的损失和精度 print('开始评估...') avg_loss, avg_acc_top1, avg_acc_top5 = eval(model, val_reader, verbose=1) print('\r[验证集] 损失: {:.5f}, top1精度:{:.5f}, top5精度为:{:.5f}\n'.format(avg_loss, avg_acc_top1, avg_acc_top5), end='') avg_loss, avg_acc_top1, avg_acc_top5 = eval(model, test_reader, verbose=1) print('\r[测试集] 损失: {:.5f}, top1精度:{:.5f}, top5精度为:{:.5f}\n'.format(avg_loss, avg_acc_top1, avg_acc_top5), end='')
开始评估...
[验证集] 损失: 0.00119, top1精度:1.00000, top5精度为:1.00000
[测试集] 损失: 0.00197, top1精度:1.00000, top5精度为:1.00000
【结果分析】
需要注意的是此处的精度与训练过程中输出的测试精度是不相同的,因为训练过程中使用的是验证集, 而这里的离线测试使用的是测试集.
import os import cv2 import json import yaml import sys import paddle sys.path.append('D:/WorkSpace/DeepLearning/WebsiteV2') # 定义模块保存位置 # sys.path.append('/home/aistudio/work/teaching') # 定义模块保存位置 from utils.predict import predict import matplotlib.pyplot as plt # 载入python的第三方图像处理库 os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" # 解决多线程报错问题 # 1. 从本地磁盘读取 YAML 配置文件,获取全局配置参数,并初始化本地路径和参数 config_path = '../../Data/Projects/Project013Modularization/config_Butterfly_resnet18_deploy.yaml' with open(config_path, 'r', encoding='utf-8') as yaml_file: args = yaml.safe_load(yaml_file) # 2. 从数据集的dataset_info文件中,获取图像标签信息 json_dataset_info = os.path.join(args['dataset_root_path'], args['dataset_name'], 'dataset_info.json') with open(json_dataset_info, 'r') as f_info: dataset_info = json.load(f_info) # 3. 本地化路径,包括模型路径,待预测样本路径 model_name = args['model_name'] deployment_model_path = args['deployments_path']['deployment_final_model_path'] # 定义最终模型路径 img_path = 'D:/WorkSpace/DeepLearning/WebsiteV2/Data/Projects/Project013Modularization/Zodiac/images/dog2.jpg' # 4. 载入待预测样本及预先训练好的模型 image = cv2.imread(img_path, 1) model = paddle.jit.load(deployment_model_path) # 5. 调用predict函数进行预测,并根据预测的类别ID,从dataset_info中获取对应的标签名称 pred_id = predict(model, image) label_name_pred = dataset_info['label_dict'][str(pred_id)] # 6. 输出结果 # 4.1 输出预测结果 print(f"待测样本的预测类别为:{label_name_pred}") # 4.2 显示待预测样本 image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) plt.imshow(image_rgb) plt.show()
待测样本的预测类别为:machaon
在实际应用中,我们通常需要对一批图像进行预测,并输出预测结果。在本项目中,我们使用 predict()
函数迭代地对一批图像进行预测,并输出预测结果。待预测图像的路径存储在 img_root_path
文件夹中,预测结果以 'results.txt' 命名,并保存在根目录中。
import os import cv2 import json import yaml import sys import codecs import paddle sys.path.append('D:/WorkSpace/DeepLearning/WebsiteV2') # 定义模块保存位置 # sys.path.append('/home/aistudio/work/teaching') # 定义模块保存位置 from utils.predict import predict import matplotlib.pyplot as plt # 载入python的第三方图像处理库 os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE" # 解决多线程报错问题 # 1. 从本地磁盘读取 YAML 配置文件,获取全局配置参数,并初始化本地路径和参数 config_path = '../../Data/Projects/Project013Modularization/config06_deploy.yaml' # config_path = '/home/aistudio/work/teaching/Configs/config06_deploy.yaml' with open(config_path, 'r', encoding='utf-8') as yaml_file: args = yaml.safe_load(yaml_file) # 2. 初始化本地化路径,包括模型路径,待预测样本路径 deployment_model_path = os.path.join(args['deployments_path']['deployment_final_model_path'], args['model_name'] + '_final') # 定义最终模型路径 img_root_path = 'D:/WorkSpace/DeepLearning/WebsiteV2/Data/Projects/Project013Modularization/Butterfly/images' result_path = 'D:/WorkSpace/DeepLearning/WebsiteV2/Data/Projects/Project013Modularization/Butterfly/results_pred.txt' # img_root_path = '/home/aistudio/work/teaching/Data/Butterfly/images' # result_path = '/home/aistudio/work/ExpResults/Project013Modularization/results_pred.txt' model = paddle.jit.load(deployment_model_path) img_list = os.listdir(img_root_path) with codecs.open(result_path, 'a', 'utf-8') as f: for img_path in img_list: image = cv2.imread(os.path.join(img_root_path, img_path), 1) pred_id = predict(model, image) label_name_pred = args['label_dict'][str(pred_id)] f.write(f"{img_path} {label_name_pred}\n") print(img_path, label_name_pred)
adm010.jpg admiral
adm017.jpg admiral
adm018.jpg admiral
mch009.jpg machaon
mch016.jpg machaon
mch023.jpg monarch_open
mnc017.jpg monarch_closed
mnc024.jpg monarch_closed
mnc030.jpg monarch_closed
pea016.jpg peacock
pea024.jpg peacock
swa004.jpg black_swallowtail
swa011.jpg zebra
swa018.jpg black_swallowtail
zeb019.jpg zebra
zeb023.jpg zebra
zeb026.jpg zebra
在本项目中,我们分别使用了AlexNet, ResNet50, ResNet18, Mobilenetv2, VGG16五个模型对 十二生肖数据集(Zodiac)
和 蝴蝶分类数据集(Butterfly)
进行训练,并获取相关的输出评估指标,所有模型的批次大小均设置为 batch_size=64。
模型名称 | Baseline模型 | ImageNet预训练 | learning_rate | best_epoch | top-1 acc | top-5 acc | loss | test_top1_acc | 单batch时间/总训练时间(s) | 可训练参数/总参数 |
---|---|---|---|---|---|---|---|---|---|---|
Zodiac_Alexnet | Alexnet | 否 | 0.001 | 3/10 | 0.48769 | 0.87231 | 0.02458 | 0.48485 | 9.06/1177.53 | 11,172,042/11,191,242 |
Zodiac_Resnet18 | ResNet18 | 是 | 0.01 | 7/10 | 0.83385 | 0.97538 | 0.01093 | 0.81364 | 10.20/1389.07 | 11,172,042/11,191,242 |
Zodiac_Resnet18_withoutPretrained | ResNet18 | 否 | 0.01 | 50/50 | 0.60769 | 0.90769 | 0.02072 | 0.60909 | 12.28/7220.98 | 11,172,042/11,191,242 |
Zodiac_Resnet50 | ResNet50 | 是 | 0.01 | 8/10 | 0.94615 | 0.99846 | 0.00343 | 0.94545 | 11.67/1455.60 | 23,479,500/23,585,740 |
Zodiac_Resnet50_withoutPretrained | ResNet50 | 否 | 0.01 | 45/50 | 0.54154 | 0.89538 | 0.04756 | 0.55152 | 11.24/7868.96 | 23,479,500/23,585,740 |
Zodiac_VGG16 | VGG16 | 是 | 0.001 | 8/10 | 0.89385 | 0.98615 | 0.00581 | 0.86515 | 16.69/2243.58 | 134,309,708/134,309,708 |
Zodiac_VGG16_withoutPretrained | VGG16 | 否 | 0.001 | 45/50 | 0.55231 | 0.90000 | 0.03685 | 0.86429 | 14.75/8727.87 | 134,309,708/134,309,708 |
Zodiac_Mobilenetv2 | Mobilenetv2 | 是 | 0.001 | 9/10 | 0.92769 | 0.99692 | 0.00425 | 0.91212 | 10.18/1294.77 | 2,205,132/2,273,356 |
Zodiac_Mobilenetv2_withoutPretrained | Mobilenetv2 | 否 | 0.001 | 44/50 | 0.52615 | 0.87077 | 0.03536 | 0.51667 | 10.44/5838.82s | 2,205,132/2,273,356 |
从实验结果可以得到以下几个结论:
微调训练(finetune)
比从头训练收敛更快
,且更容易获得较好的性能
参数较多
,且没有类似残差结构的辅助,需要更低的学习率来进行训练参数的数量
对训练时间
有较大的影响参数数量
是固定的,主要由其卷积层、全连接层等内部结构决定值得注意的是,由于数据集比较简单,参数的初始化以及随机梯度下降算法对于训练的过程影响比较大。即每次训练的过程(甚至结果)可能会有一定的波动,但较好的超参数设置仍然有助于获得较好的性能。
模型名称 | Baseline | ImageNet预训练 | 学习率 | best_epoch | 训练时间(s) | 总参数量 | val_top1_acc | val_top5_acc | test_top1_acc |
---|---|---|---|---|---|---|---|---|---|
Butterfly_Alexnet_withoutPretrained | Alexnet | 否 | 0.0001 | 40/50 | 117.39 | 57,032,519 | 0.34328 | 0.85075 | 0.29457 |
Butterfly_Alexnet | Alexnet | 是 | 0.0001 | 22/50 | 156.79 | 57,032,519 | 0.97015 | 1.00000 | 0.93798 |
Butterfly_Resnet18_withoutPretrained | ResNet18 | 否 | 0.0001 | 28/50 | 98.96 | 11,189,703 | 0.49254 | 0.91045 | 0.42636 |
Butterfly_ResNet18_0.01 | ResNet18 | 是 | 0.01 | 8/50 | 94.84 | 11,189,703 | 0.25373 | 0.80597 | 0.23256 |
Butterfly_ResNet18_0.001 | ResNet18 | 是 | 0.001 | 8/50 | 96.38 | 11,189,703 | 0.68657 | 0.97015 | 0.64341 |
Butterfly_ResNet18_0.0001 | ResNet18 | 是 | 0.0001 | 9/50 | 95.31 | 11,189,703 | 0.82090 | 1.00000 | 0.78295 |
Butterfly_ResNet18_0.00001 | ResNet18 | 是 | 0.00001 | 47/50 | 112.57 | 11,189,703 | 0.88060 | 1.00000 | 0.73643 |
Butterfly_Resnet50_withoutPretrained | ResNet50 | 是 | 0.0001 | 29/50 | 130.14 | 23,575,495 | 0.43284 | 0.88060 | 0.28682 |
Butterfly_ResNet50 | ResNet50 | 是 | 0.0001 | 14/50 | 136.09 | 23,575,495 | 0.94030 | 0.98507 | 0.96124 |
Butterfly_vgg16_withoutPretrained | VGG16 | 否 | 0.0001 | 49/50 | 324.84 | 134,289,223 | 0.59701 | 0.95522 | 0.48062 |
Butterfly_vgg16 | VGG16 | 是 | 0.0001 | 34/50 | 362.43 | 134,289,223 | 0.95522 | 1.00000 | 0.96124 |
Butterfly_mobilenet_v2_withoutPretrained | Mobilenetv2 | 否 | 0.0001 | 39/50 | 101.48 | 2,266,951 | 0.49254 | 0.94030 | 0.37984 |
Butterfly_mobilenet_v2 | Mobilenetv2 | 是 | 0.0001 | 11/50 | 96.57 | 2,266,951 | 0.92537 | 0.97015 | 0.91473 |
从实验结果可以得到以下几个结论:
值得注意的是,由于数据集比较简单,参数的初始化以及随机梯度下降算法对于训练的过程影响比较大,并且以上实验都使用了相同的优化算法Adma。因此,每次训练的过程(甚至结果)可能会有一定的波动,但较好的超参数设置仍然有助于获得较好的性能。