习题答案
¶作者:欧新宇(Xinyu OU)
开发平台:Paddle 2.1
运行环境:Intel Core i7-7700K CPU 4.2GHz, nVidia GeForce GTX 1080 Ti
本教案所涉及的数据集仅用于教学和交流使用,请勿用作商用。
最后更新:2021年8月19日
下面我们将在ResNet50, ResNet18, Mobilenetv2, VGG16四个模型对十二生肖数据集
进行评估,所有模型设置batch_size=64。
数据集包含样本8500个,其中训练验证集样本7840个,训练集样本7190个, 验证集样本650个, 测试集样本660个, 共计8500个。
模型名称 | 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 | 是 | ||||||||
Zodiac_Resnet18_withoutPretrained | ResNet18 | 否 | 0.01 | 50/50 | 0.60769 | 0.90769 | 0.02072 | 0.60909 | 0.26/374.98 | 11,172,042/11,191,242 |
Zodiac_Resnet50 | ResNet50 | 是 | ||||||||
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 | 是 | ||||||||
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 | 是 | ||||||||
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 |
从实验结果可以得到以下几个结论:
实验摘要: 对于模型训练的任务,需要数据预处理,将数据整理成为适合给模型训练使用的格式。蝴蝶识别数据集是一个包含有7个不同种类619个样本的数据集,不同种类的样本按照蝴蝶的类别各自放到了相应的文件夹。不同的样本具有不同的尺度,但总体都是规整的彩色图像。
实验目的:
dataset_info.json
# ##################################################################################
# # 数据清洗
# # 作者: Xinyu Ou (http://ouxinyu.cn)
# # 数据集名称:十二生肖数据集Zodiac
# # 本程序功能:
# # 对图像坏样本,进行索引,并保存到bad.txt中,扫描文件夹时,自动跳过文件夹'.DS_Store'和'.ipynb_checkpoints'
# ###################################################################################
# import os
# import cv2
# import codecs
# # 本地运行时,需要修改数据集的名称和绝对路径,注意和文件夹名称一致
# dataset_name = 'Zodiac'
# dataset_path = 'D:\\Workspace\\ExpDatasets\\'
# dataset_root_path = os.path.join(dataset_path, dataset_name)
# excluded_folder = ['.DS_Store', '.ipynb_checkpoints'] # 被排除的文件夹
# class_prefix = ['train', 'valid', 'test']
# num_bad = 0
# num_good = 0
# num_folder = 0
# # 检测坏文件列表是否存在,如果存在则先删除。
# bad_list = os.path.join(dataset_root_path, 'bad.txt')
# if os.path.exists(bad_list):
# os.remove(bad_list)
# # 执行数据清洗
# with codecs.open(bad_list, 'a', 'utf-8') as f_bad:
# for prefix in class_prefix:
# class_name_list = os.listdir(os.path.join(dataset_root_path, prefix))
# for class_name in class_name_list:
# if class_name not in excluded_folder: # 跳过排除文件夹
# images = os.listdir(os.path.join(dataset_root_path, prefix, class_name))
# for image in images:
# if image not in excluded_folder: # 跳过排除文件夹
# img_path = os.path.join(dataset_root_path, prefix, class_name, image)
# try: # 通过尝试读取并显示图像维度来判断样本是否损坏
# img = cv2.imread(img_path, 1)
# x = img.shape
# num_good += 1
# pass
# except:
# bad_file = os.path.join(prefix, class_name, image)
# f_bad.write("{}\n".format(bad_file))
# num_bad += 1
# num_folder += 1
# print('\r 当前清洗进度:{}/{}'.format(num_folder, 3*len(class_name_list)), end='')
# print('数据集清洗完成, 损坏文件{}个, 正常文件{}.'.format(num_bad, num_good))
##################################################################################
# 数据集预处理
# 作者: Xinyu Ou (http://ouxinyu.cn)
# 数据集名称:十二生肖数据集Zodiac
# 数据集简介: 数据集包含12个类别,其中训练集样本7199个, 验证集样本650个, 测试集样本660个, 共计8509个,其中包含9个损坏的文件。
# 本程序功能:
# 1. 将数据集由官方进行划分,大体上训练集、验证集、测试集的比例为: 85:7.5:7.5
# 2. 代码将生成4个列表文件:训练集列表train.txt, 验证集列表val.txt, 测试集列表test.txt, 训练验证集trainval.txt
# 3. 数据集基本信息:数据集的基本信息使用json格式进行输出,包括数据库名称、数据样本的数量、类别数以及类别标签。
###################################################################################
import os
import cv2
import json
import codecs
# 初始化参数
num_trainval = 0
num_train = 0
num_val = 0
num_test = 0
class_dim = 0
dataset_info = {
'dataset_name': '',
'num_trainval': -1,
'num_train': -1,
'num_val': -1,
'num_test': -1,
'class_dim': -1,
'label_dict': {}
}
# 本地运行时,需要修改数据集的名称和绝对路径,注意和文件夹名称一致
dataset_name = 'Zodiac'
dataset_path = 'D:\\Workspace\\ExpDatasets\\'
dataset_root_path = os.path.join(dataset_path, dataset_name)
class_prefix = ['train', 'valid', 'test']
excluded_folder = ['.DS_Store', '.ipynb_checkpoints'] # 被排除的文件或文件夹
# 定义生成文件的路径
# data_path = os.path.join(dataset_root_path,=) # 该数据的样本分别保存在train,valid和test文件夹中,因此不需要统一指定路径
trainval_list = os.path.join(dataset_root_path, 'trainval.txt')
train_list = os.path.join(dataset_root_path, 'train.txt')
val_list = os.path.join(dataset_root_path, 'val.txt')
test_list = os.path.join(dataset_root_path, 'test.txt')
dataset_info_list = os.path.join(dataset_root_path, 'dataset_info.json')
# 读取数据清洗获得的坏样本列表
bad_list = os.path.join(dataset_root_path, 'bad.txt')
with codecs.open(bad_list, 'r', 'utf-8') as f_bad:
bad_file = f_bad.read().splitlines()
# 检测数据集列表是否存在,如果存在则先删除。其中测试集列表是一次写入,因此可以通过'w'参数进行覆盖写入,而不用进行手动删除。
if os.path.exists(trainval_list):
os.remove(trainval_list)
if os.path.exists(train_list):
os.remove(train_list)
if os.path.exists(val_list):
os.remove(val_list)
if os.path.exists(test_list):
os.remove(test_list)
# 获取类别的名称,因为train,valid,test的类别是相同的,因此只需要从train中获取即可
class_name_list = os.listdir(os.path.join(dataset_root_path, 'train'))
# 分别从train,valid和test文件夹中去索引图像,并写入列表文件中
with codecs.open(trainval_list, 'a', 'utf-8') as f_trainval:
with codecs.open(train_list, 'a', 'utf-8') as f_train:
with codecs.open(val_list, 'a', 'utf-8') as f_val:
with codecs.open(test_list, 'a', 'utf-8') as f_test:
for prefix in class_prefix:
class_name_dir = os.listdir(os.path.join(dataset_root_path, prefix))
for i in range(len(class_name_list)):
class_name = class_name_list[i]
dataset_info['label_dict'][i] = class_name_list[i]
images = os.listdir(os.path.join(dataset_root_path, prefix, class_name))
for image in images:
if image not in excluded_folder and os.path.join(prefix, class_name, image) not in bad_file: # 判断文件是否是坏样本
if prefix == 'train':
f_train.write("{}\t{}\n".format(os.path.join(dataset_root_path, prefix, class_name, image), str(i)))
f_trainval.write("{}\t{}\n".format(os.path.join(dataset_root_path, prefix, class_name, image), str(i)))
num_train += 1
num_trainval += 1
elif prefix == 'valid':
f_val.write("{}\t{}\n".format(os.path.join(dataset_root_path, prefix, class_name, image), str(i)))
f_trainval.write("{}\t{}\n".format(os.path.join(dataset_root_path, prefix, class_name, image), str(i)))
num_val += 1
num_trainval += 1
elif prefix == 'test':
f_test.write("{}\t{}\n".format(os.path.join(dataset_root_path, prefix, class_name, image), str(i)))
num_test += 1
# 将数据集信息保存到json文件中供训练时使用
dataset_info['dataset_name'] = dataset_name
dataset_info['num_trainval'] = num_trainval
dataset_info['num_train'] = num_train
dataset_info['num_val'] = num_val
dataset_info['num_test'] = num_test
dataset_info['class_dim'] = len(class_name_list)
# 输出数据集信息json和统计情况
with codecs.open(dataset_info_list, 'w', encoding='utf-8') as f_dataset_info:
json.dump(dataset_info, f_dataset_info, ensure_ascii=False, indent=4, separators=(',', ':')) # 格式化字典格式的参数列表
print("图像列表已生成, 其中训练验证集样本{}个,训练集样本{}个, 验证集样本{}个, 测试集样本{}个, 共计{}个。".format(num_trainval, num_train, num_val, num_test, num_train+num_val+num_test))
图像列表已生成, 其中训练验证集样本7840个,训练集样本7190个, 验证集样本650个, 测试集样本660个, 共计8500个。
###### 展示数据集列表信息 ###################3
from pprint import pprint
with open(dataset_info_list, 'r') as f_info:
dataset_info = json.load(f_info)
pprint(dataset_info)
{'class_dim': 12, 'dataset_name': 'Zodiac', 'label_dict': {'0': 'dog', '1': 'dragon', '10': 'snake', '11': 'tiger', '2': 'goat', '3': 'horse', '4': 'monkey', '5': 'ox', '6': 'pig', '7': 'rabbit', '8': 'ratt', '9': 'rooster'}, 'num_test': 660, 'num_train': 7190, 'num_trainval': 7840, 'num_val': 650}
实验摘要: 蝴蝶种类识别是一个多分类问题,我们通过卷积神经网络来完成。这部分通过PaddlePaddle手动构造一个Alexnet卷积神经的网络来实现识别功能。本实验主要实现训练前的一些准备工作,包括:全局参数定义,数据集载入,数据预处理,可视化函数定义,日志输出函数定义。
实验目的:
#################导入依赖库##################################################
import os
import json
import codecs
import numpy as np
import time # 载入time时间库,用于计算训练时间
import paddle
import matplotlib.pyplot as plt # 载入python的第三方图像处理库
from pprint import pprint
################全局参数配置###################################################
#### 1. 训练超参数定义
train_parameters = {
'course_name': 'DeepLearning',
'project_name': 'HW06TransferLearning',
'dataset_name': 'Zodiac',
'architecture': 'Mobilenetv2',
'training_data': 'train',
'starting_time': time.strftime("%Y%m%d%H%M", time.localtime()), # 全局启动时间
'input_size': [3, 227, 227], # 输入样本的尺度
'mean_value': [0.485, 0.456, 0.406], # Imagenet均值
'std_value': [0.229, 0.224, 0.225], # Imagenet标准差
'num_trainval': -1,
'num_train': -1,
'num_val': -1,
'num_test': -1,
'class_dim': -1,
'label_dict': {},
'total_epoch': 10, # 总迭代次数, 代码调试好后考虑
'batch_size': 64, # 设置每个批次的数据大小,同时对训练提供器和测试
'log_interval': 10, # 设置训练过程中,每隔多少个batch显示一次
'eval_interval': 1, # 设置每个多少个epoch测试一次
'checkpointed': False, # 是否保存checkpoint模型
'checkpoint_train': False, # 是否接着上一次保存的参数接着训练,优先级高于预训练模型
'checkpoint_model':'Butterfly_Mobilenetv2', # 设置恢复训练时载入的模型参数
'checkpoint_time': '202102182058', # 恢复训练时所指向的指定时间戳
'pretrained': True, # 是否使用预训练的模型
'pretrained_model':'API', # 设置预训练模型, API|Butterflies_AlexNet_final
'dataset_root_path': 'D:\\Workspace\\ExpDatasets\\',
'result_root_path': 'D:\\Workspace\\ExpResults\\',
'deployment_root_path': 'D:\\Workspace\\ExpDeployments\\',
'useGPU': True, # True | Flase
'learning_strategy': { # 学习率和优化器相关参数
'optimizer_strategy': 'Momentum', # 优化器:Momentum, RMS, SGD, Adam
'learning_rate_strategy': 'CosineAnnealingDecay', # 学习率策略: 固定fixed, 分段衰减PiecewiseDecay, 余弦退火CosineAnnealingDecay, 指数ExponentialDecay, 多项式PolynomialDecay
'learning_rate': 0.001, # 固定学习率
'momentum': 0.9, # 动量
'Piecewise_boundaries': [60, 80, 90], # 分段衰减:变换边界,每当运行到epoch时调整一次
'Piecewise_values': [0.01, 0.001, 0.0001, 0.00001], # 分段衰减:步进学习率,每次调节的具体值
'Exponential_gamma': 0.9, # 指数衰减:衰减指数
'Polynomial_decay_steps': 10, # 多项式衰减:衰减周期,每个多少个epoch衰减一次
'verbose': False
},
'augmentation_strategy': {
'withAugmentation': True, # 数据扩展相关参数
'augmentation_prob': 0.5, # 设置数据增广的概率
'rotate_angle': 15, # 随机旋转的角度
'Hflip_prob': 0.5, # 随机翻转的概率
'brightness': 0.4,
'contrast': 0.4,
'saturation': 0.4,
'hue': 0.4,
},
}
#### 2. 设置简化参数名
args = train_parameters
argsAS = args['augmentation_strategy']
argsLS = train_parameters['learning_strategy']
if not args['pretrained']:
model_name = args['dataset_name'] + '_' + args['architecture'] + '_withoutPretrained'
else:
model_name = args['dataset_name'] + '_' + args['architecture']
#### 3. 定义设备工作模式 [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()
#### 4.定义各种路径:模型、训练、日志结果图
# 4.1 数据集路径
dataset_root_path = os.path.join(args['dataset_root_path'], args['dataset_name'])
json_dataset_info = os.path.join(dataset_root_path, 'dataset_info.json')
# 4.2 训练过程涉及的相关路径
result_root_path = os.path.join(args['result_root_path'], args['project_name'], model_name)
checkpoint_models_path = os.path.join(result_root_path, 'checkpoint_models') # 迭代训练模型保存路径
final_figures_path = os.path.join(result_root_path, 'final_figures') # 训练过程曲线图
final_models_path = os.path.join(result_root_path, 'final_models') # 最终用于部署和推理的模型
logs_path = os.path.join(result_root_path, 'logs') # 训练过程日志
# 4.3 checkpoint_ 路径用于定义恢复训练所用的模型保存
checkpoint_model = os.path.join(checkpoint_models_path, args['checkpoint_model'])
# 4.4 验证和测试时的相关路径(文件)
deployment_root_path = os.path.join(args['deployment_root_path'], args['course_name'], args['project_name'], model_name)
deployment_checkpoint_model = os.path.join(deployment_root_path, 'checkpoint_models', model_name + '_final')
deployment_final_model = os.path.join(deployment_root_path, 'final_models', model_name + '_final')
deployment_final_figures_path = os.path.join(deployment_root_path, 'final_figures')
deployment_logs_path = os.path.join(deployment_root_path, 'logs')
deployment_pretrained_model = os.path.join(deployment_root_path, 'pretrained_dir', args['pretrained_model'])
# 4.5 初始化结果目录
def init_result_path():
if not os.path.exists(final_models_path):
os.makedirs(final_models_path)
if not os.path.exists(final_figures_path):
os.makedirs(final_figures_path)
if not os.path.exists(logs_path):
os.makedirs(logs_path)
if not os.path.exists(checkpoint_models_path):
os.makedirs(checkpoint_models_path)
init_result_path()
#### 5. 初始化参数
def init_train_parameters():
dataset_info = json.loads(open(json_dataset_info, 'r', encoding='utf-8').read())
train_parameters['num_trainval'] = dataset_info['num_trainval']
train_parameters['num_train'] = dataset_info['num_train']
train_parameters['num_val'] = dataset_info['num_val']
train_parameters['num_test'] = dataset_info['num_test']
train_parameters['class_dim'] = dataset_info['class_dim']
train_parameters['label_dict'] = dataset_info['label_dict']
init_train_parameters()
# 输出训练参数 train_parameters
# if __name__ == '__main__':
# pprint(args)
在Paddle 2.0+ 中,我们可使用paddle.io来构造标准的数据集类,用于通过数据列表读取样本,并对样本进行预处理。全新的paddle.vision.transforms可以轻松的实现样本的多种预处理功能,而不用手动去写数据预处理的函数,这大简化了代码的复杂性。
代码最后给出了该了简单测试,输出两种不同模式的样本,进行数据预处理和不进行数据预处理。
import os
import sys
import cv2
import numpy as np
import paddle
import paddle.vision.transforms as T
from paddle.io import DataLoader
paddle.vision.set_image_backend('cv2')
input_size = (args['input_size'][1], args['input_size'][2])
# 1. 数据集的定义
class Dataset(paddle.io.Dataset):
def __init__(self, dataset_root_path, mode='test', withAugmentation=argsAS['withAugmentation']):
assert mode in ['train', 'val', 'test', 'trainval']
self.data = []
self.withAugmentation = withAugmentation
with open(os.path.join(dataset_root_path, mode+'.txt')) as f:
for line in f.readlines():
info = line.strip().split('\t')
image_path = os.path.join(dataset_root_path, 'Data', info[0].strip())
if len(info) == 2:
self.data.append([image_path, info[1].strip()])
elif len(info) == 1:
self.data.append([image_path, -1])
prob = np.random.random()
if mode in ['train', 'trainval'] and prob >= argsAS['augmentation_prob']:
self.transforms = T.Compose([
T.RandomResizedCrop(input_size),
T.RandomHorizontalFlip(argsAS['Hflip_prob']),
T.RandomRotation(argsAS['rotate_angle']),
T.ColorJitter(brightness=argsAS['brightness'], contrast=argsAS['contrast'], saturation=argsAS['saturation'], hue=argsAS['hue']),
T.ToTensor(),
T.Normalize(mean=args['mean_value'], std=args['std_value'])
])
elif mode in ['val', 'test'] or prob < argsAS['augmentation_prob']:
self.transforms = T.Compose([
T.Resize(input_size),
T.ToTensor(),
T.Normalize(mean=args['mean_value'], std=args['std_value'])
])
# 根据索引获取单个样本
def __getitem__(self, index):
image_path, label = self.data[index]
image = cv2.imread(image_path, 1) # 使用cv2进行数据读取可以强制将的图像转化为彩色模式,其中0为灰度模式,1为彩色模式
if self.withAugmentation == True:
image = self.transforms(image)
return image, np.array(label, dtype='int64')
# 获取样本总数
def __len__(self):
return len(self.data)
###############################################################
# 测试输入数据类:分别输出进行预处理和未进行预处理的数据形态和例图
if __name__ == "__main__":
import random
# 1. 载入数据
dataset_val = Dataset(dataset_root_path, mode='val')
i = random.randrange(0, len(dataset_val))
img1 = dataset_val[i][0]
print('验证数据预处理前的数据形态(进行预处理后): {}'.format(img1.shape))
dataset_test = Dataset(dataset_root_path, mode='test', withAugmentation=False)
j = random.randrange(0, len(dataset_test))
img2 = dataset_test[j][0]
print('测试数据预处理前的数据形态(未进行预处理): {}'.format(img2.shape))
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel\ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above. and should_run_async(code)
验证数据预处理前的数据形态(进行预处理后): [3, 227, 227] 测试数据预处理前的数据形态(未进行预处理): (433, 770, 3)
结合paddle.io.DataLoader工具包,可以将读入的数据进行batch划分,确定是否进行随机打乱和是否丢弃最后的冗余样本。
import os
import sys
from paddle.io import DataLoader
# 1. 从数据集库中获取数据
dataset_trainval = Dataset(dataset_root_path, mode='trainval')
dataset_train = Dataset(dataset_root_path, mode='train')
dataset_val = Dataset(dataset_root_path, mode='val')
dataset_test = Dataset(dataset_root_path, mode='test')
# 2. 创建读取器
trainval_reader = DataLoader(dataset_trainval, batch_size=args['batch_size'], shuffle=True, drop_last=True)
train_reader = DataLoader(dataset_train, batch_size=args['batch_size'], shuffle=True, drop_last=True)
val_reader = DataLoader(dataset_val, batch_size=args['batch_size'], shuffle=False, drop_last=False)
test_reader = DataLoader(dataset_test, batch_size=args['batch_size'], shuffle=False, drop_last=False)
# 测试读取器
if __name__ == "__main__":
for i, (image, label) in enumerate(val_reader()):
print('验证集batch_{}的图像形态:{}, 标签形态:{}'.format(i, image.shape, label.shape))
break
验证集batch_0的图像形态:[64, 3, 227, 227], 标签形态:[64]
定义训练过程中用到的可视化方法, 包括训练损失, 训练集批准确率, 测试集损失,测试机准确率等。 根据具体的需求,可以在训练后展示这些数据和迭代次数的关系. 值得注意的是, 训练过程中可以每个epoch绘制一个数据点,也可以每个batch绘制一个数据点,也可以每个n个batch或n个epoch绘制一个数据点。
下面的程序除了实现训练后自动可视化的函数,同时实现将可视化的图片和数据进行保存,保存的文件夹由 final_figures_path
指定。
# 绘制训练batch精度和平均loss
def draw_process(title, loss_label, accuracy_label, iters, losses, accuracies, figurename=None, isShow=False):
# 第一组坐标轴 Loss
_, ax1 = plt.subplots() # plt.subplots(figsize=(14,6))
ax1.plot(iters, losses, color='red', label=loss_label)
ax1.set_xlabel('Iters', fontsize=20)
ax1.set_ylabel(loss_label, fontsize=20)
max_loss = max(losses)
ax1.set_ylim(0, max_loss*1.2)
# 第二组坐标轴 accuracy
ax2 = ax1.twinx()
ax2.plot(iters, accuracies, color='blue', label=accuracy_label)
ax2.set_ylabel(accuracy_label, fontsize=20)
max_acc = max(accuracies)
ax2.set_ylim(0, max_acc*1.2)
plt.title(title, fontsize=24)
# 图例
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
plt.legend(handles1+handles2, labels1+labels2, loc='best')
plt.grid()
# 将绘图结果保存到final_figures目录
if figurename != None:
if not os.path.exists(final_figures_path):
os.makedirs(final_figures_path)
plt.savefig(os.path.join(final_figures_path, figurename + '.png'))
# 将绘图数据保存到final_figures目录
figure_data = np.array([iters, losses, accuracies])
np.save(os.path.join(final_figures_path, figurename + '.npy'), figure_data)
# 显示绘图结果
if isShow is True:
plt.show()
### 绘图测试 ###################################################
if __name__ == '__main__':
root_path = deployment_final_figures_path
try:
train = np.load(os.path.join(root_path, 'train.npy'))
draw_process('train', 'loss', 'accuracy', train[0], train[1], train[2], figurename=None, isShow=True)
except:
print('数据不存在,无法进行绘制')
logging是一个专业的日志输出库,可以用于在屏幕上打印运行结果(和print()函数一致),也可以实现将这些运行结果保存到指定的文件夹中,用于后续的研究。
#################################################
# 修改者: Xinyu Ou (http://ouxinyu.cn)
# 功能: 输出日志, 并保存到日志文件
# 格式: 2021-02-03 23:03:07,354 - INFO: [Messages]
# 调用方法:
# from utils.logger import logger
# logger.info('Good morning')
#################################################
import os
import sys
import logging
def init_log_config():
"""
初始化日志相关配置
:return:
"""
global logger
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if not os.path.exists(logs_path):
os.makedirs(logs_path)
log_name = os.path.join(logs_path, model_name + '.logs')
sh = logging.StreamHandler() # 打印到屏幕控制多台
fh = logging.FileHandler(log_name, mode='w', encoding='utf8') # 打印到文件
fh.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")
fh.setFormatter(formatter)
sh.setFormatter(formatter)
logger.addHandler(fh) # 输出到文件
logger.addHandler(sh) # 输出到控制台
return logger
logger = init_log_config()
# 测试
if __name__ == "__main__":
logger.info('测试一下, 模型名称: {}'.format(model_name))
C:\Users\Administrator\anaconda3\lib\site-packages\ipykernel\ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above. and should_run_async(code) 2021-08-18 20:38:27,227 - INFO: 测试一下, 模型名称: Zodiac_Mobilenetv2
实验摘要: 蝴蝶种类识别是一个多分类问题,我们通过卷积神经网络来完成。这部分通过PaddlePaddle手动构造一个Alexnet卷积神经的网络来实现识别功能,最后一层采用Softmax激活函数完成分类任务。
实验目的:
从Alexnet开始,包括VGG,GoogLeNet,Resnet等模型都是层次较深的模型,如果按照逐层的方式进行设计,代码会变得非常繁琐。因此,我们可以考虑将相同结构的模型进行汇总和合成,例如Alexnet中,卷积层+激活+池化层
就是一个完整的结构体。
如果使用的是标准的网络结构,我们可以直接从Paddle的模型库中进行下载,并启用预训练模型,载入Imagenet预训练参数。再实际应用中,预训练(迁移学习)是非常有效的提高性能和缩减训练时间的方法。在载入Paddle模型库的时候,我们不需要手动设计模型,只需要按照下面的方法来直接调用即可。
Paddle.vision.models 内置的模型库,请参考:paddle.vision
# import paddle
# from paddle.static import InputSpec
# # 设置输入样本的维度
# input_spec = InputSpec(shape=[None] + train_parameters['input_size'], dtype='float32', name='image')
# label_spec = InputSpec(shape=[None, 1], dtype='int64', name='label')
# # 载入官方标准模型,若不存在则会自动进行下载,pretrained=True|False控制是否使用Imagenet预训练参数
# network = paddle.vision.models.mobilenet_v2(num_classes=args['class_dim'], pretrained=args['pretrained'])
# model = paddle.Model(network, input_spec, label_spec)
# logger.info('模型参数信息:')
# logger.info(model.summary()) # 是否显示神经网络的具体信息
学习率策略和优化方法是两个密不可分的模块,学习率策略定义了学习率的变化方法,常见的包括固定学习率、分段学习率、余弦退火学习率、指数学习率和多项式学习率等;优化策略是如何进行学习率的学习和变化,常见的包括动量SGD、RMS、SGD和Adam等。
import sys
import os
import paddle
import paddle.optimizer as optimizer
def learning_rate_setting(verbose=argsLS['verbose']):
if argsLS['learning_rate_strategy'] == 'PiecewiseDecay':
lr = optimizer.lr.PiecewiseDecay(boundaries=argsLS['Piecewise_boundaries'], values=argsLS['Piecewise_values'], verbose=verbose)
elif argsLS['learning_rate_strategy'] == 'CosineAnnealingDecay':
step_each_epoch = args['num_train'] // (args['batch_size'] * 2)
T_max = step_each_epoch * args['total_epoch']
lr = optimizer.lr.CosineAnnealingDecay(learning_rate=argsLS['learning_rate'], T_max=T_max, verbose=verbose)
elif argsLS['learning_rate_strategy'] == 'ExponentialDecay':
lr = optimizer.lr.ExponentialDecay(learning_rate=argsLS['learning_rate'], gamma=argsLS['Exponential_gamma'], verbose=verbose)
elif argsLS['learning_rate_strategy'] == 'PolynomialDecay':
lr = optimizer.lr.PolynomialDecay(learning_rate=argsLS['learning_rate'], decay_steps=argsLS['Polynomial_decay_steps'], verbose=verbose)
else:
lr = argsLS['learning_rate']
return lr
def optimizer_setting(model, lr):
if argsLS['optimizer_strategy'] == 'Momentum':
# 阶梯型的学习率适合比较大规模的训练数据
opt = optimizer.Momentum(learning_rate=lr, momentum=argsLS['momentum'], parameters=model.parameters())
elif argsLS['optimizer_strategy'] == 'RMS':
# 阶梯型的学习率适合比较大规模的训练数据
opt = optimizer.RMSProp(learning_rate=lr, parameters=model.parameters())
elif argsLS['optimizer_strategy'] == 'SGD':
# loss下降相对较慢,但是最终效果不错,阶梯型的学习率适合比较大规模的训练数据
opt = optimizer.SGD(learning_rate=lr, parameters=model.parameters())
elif argsLS['optimizer_strategy'] == 'Adam':
# 能够比较快速的降低 loss,但是相对后期乏力
opt = optimizer.Adam(learning_rate=lr, parameters=model.parameters())
else:
print('学习率设置错误, 请重新设置。')
return opt
# 学习率输出测试
# if __name__ == '__main__':
# print('当前学习率策略为: {}'.format(argsLS['learning_rate_strategy']))
# linear = paddle.nn.Linear(10, 10)
# lr = learning_rate_setting(verbose=True)
# opt = optimizer_setting(linear, lr)
# if argsLS['learning_rate_strategy'] == 'fixed':
# print('learning = {}'.format(argsLS['learning_rate']))
# else:
# for epoch in range(20):
# for batch_id in range(10):
# x = paddle.uniform([10, 10])
# out = linear(x)
# loss = paddle.mean(out)
# loss.backward()
# opt.step()
# opt.clear_gradients()
# # lr.step() # If you update learning rate each step
# lr.step() # If you update learning rate each epoch
验证函数有两个功能,一是在训练过程中实时地对验证集进行测试(在线测试),二是在训练结束后对测试集进行测试(离线测试)。
验证函数的具体流程包括:
在定义eval()函数的时候,我们需要为其指定两个必要参数:model
是测试的模型,data_reader
是迭代的数据读取器,取值为val_reader(), test_reader(),分别对验证集和测试集。此处验证集和测试集数据的测试过程是相同的,只是所使用的数据不同;此外,可选参数verbose用于定义是否在测试的时候输出过程
# 载入项目文件夹
import sys
import numpy as np
import paddle
import paddle.nn.functional as F
from paddle.static import InputSpec
__all__ = ['eval']
def eval(model, data_reader, verbose=0):
accuracies_top1 = []
accuracies_top5 = []
losses = []
n_total = 0
for batch_id, (image, label) in enumerate(data_reader):
n_batch = len(label)
n_total = n_total + n_batch
label = paddle.unsqueeze(label, axis=1)
loss, acc = model.eval_batch([image], [label])
losses.append(loss[0])
accuracies_top1.append(acc[0][0]*n_batch)
accuracies_top5.append(acc[0][1]*n_batch)
if verbose == 1:
print('\r Batch:{}/{}, acc_top1:[{:.5f}], acc_top5:[{:.5f}]'.format(batch_id+1, len(data_reader), acc[0][0], acc[0][1]), end='')
avg_loss = np.sum(losses)/n_total # loss 记录的是当前batch的累积值
avg_acc_top1 = np.sum(accuracies_top1)/n_total # metric 是当前batch的平均值
avg_acc_top5 = np.sum(accuracies_top5)/n_total
return avg_loss, avg_acc_top1, avg_acc_top5
##############################################################
if __name__ == '__main__':
try:
# 设置输入样本的维度
input_spec = InputSpec(shape=[None] + args['input_size'], dtype='float32', name='image')
label_spec = InputSpec(shape=[None, 1], dtype='int64', name='label')
# 载入模型
network = paddle.vision.models.mobilenet_v2(num_classes=args['class_dim'])
model = paddle.Model(network, input_spec, label_spec) # 模型实例化
model.load(deployment_checkpoint_path) # 载入调优模型的参数
model.prepare(loss=paddle.nn.CrossEntropyLoss(), # 设置loss
metrics=paddle.metric.Accuracy(topk=(1,5))) # 设置评价指标
# 执行评估函数,并输出验证集样本的损失和精度
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}'.format(avg_loss, avg_acc_top1, avg_acc_top5), end='')
except:
print('数据不存在, 跳过测试')
数据不存在, 跳过测试
在Paddle 2.0+动态图模式下,动态图模式被作为默认进程,同时动态图守护进程 fluid.dygraph.guard(PLACE)
被取消。
训练部分的具体流程包括:
import os
import time
import json
import paddle
from paddle.static import InputSpec
# 初始配置变量
total_epoch = train_parameters['total_epoch']
# 初始化绘图列表
all_train_iters = []
all_train_losses = []
all_train_accs_top1 = []
all_train_accs_top5 = []
all_test_losses = []
all_test_iters = []
all_test_accs_top1 = []
all_test_accs_top5 = []
def train(model):
# 初始化临时变量
num_batch = 0
best_result = 0
best_result_id = 0
elapsed = 0
# 根据config文件设置训练数据来源
if train_parameters['training_data'] == 'trainval':
data_reader = trainval_reader
elif train_parameters['training_data'] == 'train':
data_reader = train_reader
for epoch in range(1, total_epoch+1):
for batch_id, (image, label) in enumerate(data_reader):
num_batch += 1
label = paddle.unsqueeze(label, axis=1)
loss, acc = model.train_batch([image], [label])
if num_batch % train_parameters['log_interval'] == 0: # 每10个batch显示一次日志,适合大数据集
avg_loss = loss[0][0]
acc_top1 = acc[0][0]
acc_top5 = acc[0][1]
elapsed_step = time.perf_counter() - elapsed - start
elapsed = time.perf_counter() - start
logger.info('Epoch:{}/{}, batch:{}, train_loss:[{:.5f}], acc_top1:[{:.5f}], acc_top5:[{:.5f}]({:.2f}s)'
.format(epoch, total_epoch, num_batch, loss[0][0], acc[0][0], acc[0][1], elapsed_step))
# 记录训练过程,用于可视化训练过程中的loss和accuracy
all_train_iters.append(num_batch)
all_train_losses.append(avg_loss)
all_train_accs_top1.append(acc_top1)
all_train_accs_top5.append(acc_top5)
# 每隔一定周期进行一次测试
if epoch % train_parameters['eval_interval'] == 0 or epoch == total_epoch:
# 模型校验
avg_loss, avg_acc_top1, avg_acc_top5 = eval(model, val_reader())
logger.info('[validation] Epoch:{}/{}, val_loss:[{:.5f}], val_top1:[{:.5f}], val_top5:[{:.5f}]'.format(epoch, total_epoch, avg_loss, avg_acc_top1, avg_acc_top5))
# 记录测试过程,用于可视化训练过程中的loss和accuracy
all_test_iters.append(epoch)
all_test_losses.append(avg_loss)
all_test_accs_top1.append(avg_acc_top1)
all_test_accs_top5.append(avg_acc_top5)
# 将性能最好的模型保存为final模型
if avg_acc_top1 > best_result:
best_result = avg_acc_top1
best_result_id = epoch
# finetune model 用于调优和恢复训练
model.save(os.path.join(checkpoint_models_path, model_name + '_final'))
# inference model 用于部署和预测
model.save(os.path.join(final_models_path, model_name + '_final'), training=False)
logger.info('已保存当前测试模型(epoch={})为最优模型:{}_final'.format(best_result_id, model_name))
logger.info('最优top1测试精度:{:.5f} (epoch={})'.format(best_result, best_result_id))
# 根据需要决定是否需要将每次测试的模型都进行保存(if needed),保存模型需要耗费一定的运算时间和大量的存储资源
# 建议在训练大型模型时,开启该选项,方便训练中断时能够及时恢复训练
# 训练小型模型(训练时间短)时,可以关闭该选项,以进一步提高训练速度
if train_parameters['checkpointed']:
model.save(os.path.join(checkpoint_models_path, model_name + '_' + str(epoch)))
logger.info('训练完成,最终性能accuracy={:.5f}(epoch={}), 总耗时{:.2f}s, 已将其保存为:{}_final'.format(best_result, best_result_id, time.perf_counter() - start, model_name))
#### 训练主函数 ########################################################3
if __name__ == '__main__':
# model = MyNet(num_classes=10)
# train(model)
# 将此次训练的超参数进行保存
data = json.dumps(train_parameters, indent=4, ensure_ascii=False, sort_keys=False, separators=(',', ':')) # 格式化字典格式的参数列表
logger.info(data)
# 启动训练过程
logger.info('训练参数保存完毕,使用{}模型, 训练{}数据, 训练集{}, 启动训练...'.format(train_parameters['architecture'],train_parameters['dataset_name'],train_parameters['training_data']))
logger.info('当前模型目录为:{}'.format(model_name + '_' + train_parameters['starting_time']))
# 设置输入样本的维度
input_spec = InputSpec(shape=[None] + train_parameters['input_size'], dtype='float32', name='image')
label_spec = InputSpec(shape=[None, 1], dtype='int64', name='label')
# 载入官方标准模型,若不存在则会自动进行下载,pretrained=True|False控制是否使用Imagenet预训练参数
network = paddle.vision.models.mobilenet_v2(num_classes=args['class_dim'], pretrained=args['pretrained'])
model = paddle.Model(network, input_spec, label_spec)
logger.info('模型参数信息:')
logger.info(model.summary()) # 是否显示神经网络的具体信息
if train_parameters['checkpoint_train'] == True:
model.load(checkpoint_load_model)
logger.info('载入{}中断模型和参数完毕,开始从checkpoint恢复训练'.format(train_parameters['architecture']))
logger.info('checkpoint模型:{}'.format(checkpoint_load_model))
else:
if train_parameters['pretrained'] == False:
logger.info('载入{}模型完毕,从初始状态开始训练'.format(train_parameters['architecture']))
elif train_parameters['pretrained_model'] == 'API':
logger.info('载入Imagenet-{}预训练模型完毕,开始微调训练(fine-tune)'.format(train_parameters['architecture']))
else:
model.load(project_pretrained_model)
logger.info('载入自定义预训练{}模型完毕,开始微调训练(fine-tune)'.format(train_parameters['architecture']))
logger.info('预训练模型:{}'.format(project_pretrained_model))
# 设置学习率、优化器、损失函数和评价指标
lr = learning_rate_setting()
optimizer = optimizer_setting(model, lr)
model.prepare(optimizer,
paddle.nn.CrossEntropyLoss(),
paddle.metric.Accuracy(topk=(1,5)))
# 启动训练过程
start = time.perf_counter()
train(model)
logger.info('训练完毕,结果路径{}.'.format(result_root_path))
# 输出训练过程图
logger.info('Done.')
draw_process("Training Process", 'Train Loss', 'Train Accuracy(top1)', all_train_iters, all_train_losses, all_train_accs_top1, 'train')
draw_process("Validation Results", 'Validation Loss', 'Validation Accuracy(top1)', all_test_iters, all_test_losses, all_test_accs_top1, 'val')
2021-08-18 20:38:27,387 - INFO: { "course_name":"DeepLearning", "project_name":"HW06TransferLearning", "dataset_name":"Zodiac", "architecture":"Mobilenetv2", "training_data":"train", "starting_time":"202108182038", "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, "class_dim":12, "label_dict":{ "0":"dog", "1":"dragon", "2":"goat", "3":"horse", "4":"monkey", "5":"ox", "6":"pig", "7":"rabbit", "8":"ratt", "9":"rooster", "10":"snake", "11":"tiger" }, "total_epoch":10, "batch_size":64, "log_interval":10, "eval_interval":1, "checkpointed":false, "checkpoint_train":false, "checkpoint_model":"Butterfly_Mobilenetv2", "checkpoint_time":"202102182058", "pretrained":true, "pretrained_model":"API", "dataset_root_path":"D:\\Workspace\\ExpDatasets\\", "result_root_path":"D:\\Workspace\\ExpResults\\", "deployment_root_path":"D:\\Workspace\\ExpDeployments\\", "useGPU":true, "learning_strategy":{ "optimizer_strategy":"Momentum", "learning_rate_strategy":"CosineAnnealingDecay", "learning_rate":0.001, "momentum":0.9, "Piecewise_boundaries":[ 60, 80, 90 ], "Piecewise_values":[ 0.01, 0.001, 0.0001, 1e-05 ], "Exponential_gamma":0.9, "Polynomial_decay_steps":10, "verbose":false }, "augmentation_strategy":{ "withAugmentation":true, "augmentation_prob":0.5, "rotate_angle":15, "Hflip_prob":0.5, "brightness":0.4, "contrast":0.4, "saturation":0.4, "hue":0.4 } } 2021-08-18 20:38:27,389 - INFO: 训练参数保存完毕,使用Mobilenetv2模型, 训练Zodiac数据, 训练集train, 启动训练... 2021-08-18 20:38:27,390 - INFO: 当前模型目录为:Zodiac_Mobilenetv2_202108182038 2021-08-18 20:38:27,458 - INFO: unique_endpoints {''} 2021-08-18 20:38:27,459 - INFO: File C:\Users\Administrator/.cache/paddle/hapi/weights\mobilenet_v2_x1.0.pdparams md5 checking... 2021-08-18 20:38:27,501 - INFO: Found C:\Users\Administrator/.cache/paddle/hapi/weights\mobilenet_v2_x1.0.pdparams C:\Users\Administrator\anaconda3\lib\site-packages\paddle\fluid\dygraph\layers.py:1301: UserWarning: Skip loading for classifier.1.weight. classifier.1.weight receives a shape [1280, 1000], but the expected shape is [1280, 12]. warnings.warn(("Skip loading for {}. ".format(key) + str(err))) C:\Users\Administrator\anaconda3\lib\site-packages\paddle\fluid\dygraph\layers.py:1301: UserWarning: Skip loading for classifier.1.bias. classifier.1.bias receives a shape [1000], but the expected shape is [12]. warnings.warn(("Skip loading for {}. ".format(key) + str(err))) 2021-08-18 20:38:27,643 - INFO: 模型参数信息: 2021-08-18 20:38:27,682 - INFO: {'total_params': 2273356, 'trainable_params': 2205132} 2021-08-18 20:38:27,682 - INFO: 载入Imagenet-Mobilenetv2预训练模型完毕,开始微调训练(fine-tune)
------------------------------------------------------------------------------- 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 ReLU6-56 [[1, 384, 15, 15]] [1, 384, 15, 15] 0 Conv2D-84 [[1, 384, 15, 15]] [1, 384, 15, 15] 3,456 BatchNorm2D-84 [[1, 384, 15, 15]] [1, 384, 15, 15] 1,536 ReLU6-57 [[1, 384, 15, 15]] [1, 384, 15, 15] 0 Conv2D-85 [[1, 384, 15, 15]] [1, 96, 15, 15] 36,864 BatchNorm2D-85 [[1, 96, 15, 15]] [1, 96, 15, 15] 384 InvertedResidual-28 [[1, 64, 15, 15]] [1, 96, 15, 15] 0 Conv2D-86 [[1, 96, 15, 15]] [1, 576, 15, 15] 55,296 BatchNorm2D-86 [[1, 576, 15, 15]] [1, 576, 15, 15] 2,304 ReLU6-58 [[1, 576, 15, 15]] [1, 576, 15, 15] 0 Conv2D-87 [[1, 576, 15, 15]] [1, 576, 15, 15] 5,184 BatchNorm2D-87 [[1, 576, 15, 15]] [1, 576, 15, 15] 2,304 ReLU6-59 [[1, 576, 15, 15]] [1, 576, 15, 15] 0 Conv2D-88 [[1, 576, 15, 15]] [1, 96, 15, 15] 55,296 BatchNorm2D-88 [[1, 96, 15, 15]] [1, 96, 15, 15] 384 InvertedResidual-29 [[1, 96, 15, 15]] [1, 96, 15, 15] 0 Conv2D-89 [[1, 96, 15, 15]] [1, 576, 15, 15] 55,296 BatchNorm2D-89 [[1, 576, 15, 15]] [1, 576, 15, 15] 2,304 ReLU6-60 [[1, 576, 15, 15]] [1, 576, 15, 15] 0 Conv2D-90 [[1, 576, 15, 15]] [1, 576, 15, 15] 5,184 BatchNorm2D-90 [[1, 576, 15, 15]] [1, 576, 15, 15] 2,304 ReLU6-61 [[1, 576, 15, 15]] [1, 576, 15, 15] 0 Conv2D-91 [[1, 576, 15, 15]] [1, 96, 15, 15] 55,296 BatchNorm2D-91 [[1, 96, 15, 15]] [1, 96, 15, 15] 384 InvertedResidual-30 [[1, 96, 15, 15]] [1, 96, 15, 15] 0 Conv2D-92 [[1, 96, 15, 15]] [1, 576, 15, 15] 55,296 BatchNorm2D-92 [[1, 576, 15, 15]] [1, 576, 15, 15] 2,304 ReLU6-62 [[1, 576, 15, 15]] [1, 576, 15, 15] 0 Conv2D-93 [[1, 576, 15, 15]] [1, 576, 8, 8] 5,184 BatchNorm2D-93 [[1, 576, 8, 8]] [1, 576, 8, 8] 2,304 ReLU6-63 [[1, 576, 8, 8]] [1, 576, 8, 8] 0 Conv2D-94 [[1, 576, 8, 8]] [1, 160, 8, 8] 92,160 BatchNorm2D-94 [[1, 160, 8, 8]] [1, 160, 8, 8] 640 InvertedResidual-31 [[1, 96, 15, 15]] [1, 160, 8, 8] 0 Conv2D-95 [[1, 160, 8, 8]] [1, 960, 8, 8] 153,600 BatchNorm2D-95 [[1, 960, 8, 8]] [1, 960, 8, 8] 3,840 ReLU6-64 [[1, 960, 8, 8]] [1, 960, 8, 8] 0 Conv2D-96 [[1, 960, 8, 8]] [1, 960, 8, 8] 8,640 BatchNorm2D-96 [[1, 960, 8, 8]] [1, 960, 8, 8] 3,840 ReLU6-65 [[1, 960, 8, 8]] [1, 960, 8, 8] 0 Conv2D-97 [[1, 960, 8, 8]] [1, 160, 8, 8] 153,600 BatchNorm2D-97 [[1, 160, 8, 8]] [1, 160, 8, 8] 640 InvertedResidual-32 [[1, 160, 8, 8]] [1, 160, 8, 8] 0 Conv2D-98 [[1, 160, 8, 8]] [1, 960, 8, 8] 153,600 BatchNorm2D-98 [[1, 960, 8, 8]] [1, 960, 8, 8] 3,840 ReLU6-66 [[1, 960, 8, 8]] [1, 960, 8, 8] 0 Conv2D-99 [[1, 960, 8, 8]] [1, 960, 8, 8] 8,640 BatchNorm2D-99 [[1, 960, 8, 8]] [1, 960, 8, 8] 3,840 ReLU6-67 [[1, 960, 8, 8]] [1, 960, 8, 8] 0 Conv2D-100 [[1, 960, 8, 8]] [1, 160, 8, 8] 153,600 BatchNorm2D-100 [[1, 160, 8, 8]] [1, 160, 8, 8] 640 InvertedResidual-33 [[1, 160, 8, 8]] [1, 160, 8, 8] 0 Conv2D-101 [[1, 160, 8, 8]] [1, 960, 8, 8] 153,600 BatchNorm2D-101 [[1, 960, 8, 8]] [1, 960, 8, 8] 3,840 ReLU6-68 [[1, 960, 8, 8]] [1, 960, 8, 8] 0 Conv2D-102 [[1, 960, 8, 8]] [1, 960, 8, 8] 8,640 BatchNorm2D-102 [[1, 960, 8, 8]] [1, 960, 8, 8] 3,840 ReLU6-69 [[1, 960, 8, 8]] [1, 960, 8, 8] 0 Conv2D-103 [[1, 960, 8, 8]] [1, 320, 8, 8] 307,200 BatchNorm2D-103 [[1, 320, 8, 8]] [1, 320, 8, 8] 1,280 InvertedResidual-34 [[1, 160, 8, 8]] [1, 320, 8, 8] 0 Conv2D-104 [[1, 320, 8, 8]] [1, 1280, 8, 8] 409,600 BatchNorm2D-104 [[1, 1280, 8, 8]] [1, 1280, 8, 8] 5,120 ReLU6-70 [[1, 1280, 8, 8]] [1, 1280, 8, 8] 0 AdaptiveAvgPool2D-2 [[1, 1280, 8, 8]] [1, 1280, 1, 1] 0 Dropout-2 [[1, 1280]] [1, 1280] 0 Linear-2 [[1, 1280]] [1, 12] 15,372 =============================================================================== Total params: 2,273,356 Trainable params: 2,205,132 Non-trainable params: 68,224 ------------------------------------------------------------------------------- Input size (MB): 0.59 Forward/backward pass size (MB): 165.07 Params size (MB): 8.67 Estimated Total Size (MB): 174.34 -------------------------------------------------------------------------------
C:\Users\Administrator\anaconda3\lib\site-packages\paddle\nn\layer\norm.py:640: UserWarning: When training, we now always track global mean and variance. warnings.warn( 2021-08-18 20:38:47,444 - INFO: Epoch:1/10, batch:10, train_loss:[2.35868], acc_top1:[0.18750], acc_top5:[0.65625](19.73s) 2021-08-18 20:39:08,187 - INFO: Epoch:1/10, batch:20, train_loss:[1.53091], acc_top1:[0.53125], acc_top5:[0.92188](20.74s) 2021-08-18 20:39:28,241 - INFO: Epoch:1/10, batch:30, train_loss:[1.24399], acc_top1:[0.60938], acc_top5:[0.92188](20.05s) 2021-08-18 20:39:49,964 - INFO: Epoch:1/10, batch:40, train_loss:[1.11863], acc_top1:[0.60938], acc_top5:[0.96875](21.72s) 2021-08-18 20:40:12,133 - INFO: Epoch:1/10, batch:50, train_loss:[0.84734], acc_top1:[0.76562], acc_top5:[0.95312](22.17s) 2021-08-18 20:40:34,673 - INFO: Epoch:1/10, batch:60, train_loss:[0.77916], acc_top1:[0.76562], acc_top5:[0.95312](22.54s) 2021-08-18 20:40:54,459 - INFO: Epoch:1/10, batch:70, train_loss:[0.63126], acc_top1:[0.82812], acc_top5:[1.00000](19.79s) 2021-08-18 20:41:14,705 - INFO: Epoch:1/10, batch:80, train_loss:[0.51194], acc_top1:[0.81250], acc_top5:[1.00000](20.25s) 2021-08-18 20:41:34,630 - INFO: Epoch:1/10, batch:90, train_loss:[0.54417], acc_top1:[0.84375], acc_top5:[1.00000](19.92s) 2021-08-18 20:41:52,263 - INFO: Epoch:1/10, batch:100, train_loss:[0.54743], acc_top1:[0.81250], acc_top5:[0.98438](17.63s) 2021-08-18 20:42:10,089 - INFO: Epoch:1/10, batch:110, train_loss:[0.36209], acc_top1:[0.92188], acc_top5:[1.00000](17.83s) 2021-08-18 20:42:27,441 - INFO: [validation] Epoch:1/10, val_loss:[0.00699], val_top1:[0.86615], val_top5:[0.99538] C:\Users\Administrator\anaconda3\lib\site-packages\paddle\fluid\layers\utils.py:77: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated since Python 3.3, and in 3.9 it will stop working return (isinstance(seq, collections.Sequence) and C:\Users\Administrator\anaconda3\lib\site-packages\paddle\fluid\dygraph\dygraph_to_static\convert_call_func.py:89: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray func_in_dict = func == v C:\Users\Administrator\anaconda3\lib\site-packages\paddle\fluid\layers\math_op_patch.py:317: UserWarning: C:\Users\Administrator\anaconda3\lib\site-packages\paddle\vision\models\mobilenetv2.py:99 The behavior of expression A + B has been unified with elementwise_add(X, Y, axis=-1) from Paddle 2.0. If your code works well in the older versions but crashes in this version, try to use elementwise_add(X, Y, axis=0) instead of A + B. This transitional warning will be dropped in the future. warnings.warn( 2021-08-18 20:42:28,376 - INFO: 已保存当前测试模型(epoch=1)为最优模型:Zodiac_Mobilenetv2_final 2021-08-18 20:42:28,377 - INFO: 最优top1测试精度:0.86615 (epoch=1) 2021-08-18 20:42:35,218 - INFO: Epoch:2/10, batch:120, train_loss:[0.32727], acc_top1:[0.89062], acc_top5:[1.00000](25.13s) 2021-08-18 20:42:44,745 - INFO: Epoch:2/10, batch:130, train_loss:[0.33888], acc_top1:[0.89062], acc_top5:[0.98438](9.53s) 2021-08-18 20:42:54,753 - INFO: Epoch:2/10, batch:140, train_loss:[0.24190], acc_top1:[0.93750], acc_top5:[0.98438](10.01s) 2021-08-18 20:43:04,487 - INFO: Epoch:2/10, batch:150, train_loss:[0.39876], acc_top1:[0.90625], acc_top5:[1.00000](9.73s) 2021-08-18 20:43:14,891 - INFO: Epoch:2/10, batch:160, train_loss:[0.38700], acc_top1:[0.90625], acc_top5:[0.98438](10.40s) 2021-08-18 20:43:25,636 - INFO: Epoch:2/10, batch:170, train_loss:[0.31630], acc_top1:[0.89062], acc_top5:[0.98438](10.74s) 2021-08-18 20:43:35,091 - INFO: Epoch:2/10, batch:180, train_loss:[0.36716], acc_top1:[0.87500], acc_top5:[1.00000](9.46s) 2021-08-18 20:43:44,375 - INFO: Epoch:2/10, batch:190, train_loss:[0.32833], acc_top1:[0.90625], acc_top5:[0.98438](9.28s) 2021-08-18 20:43:54,686 - INFO: Epoch:2/10, batch:200, train_loss:[0.38044], acc_top1:[0.87500], acc_top5:[0.98438](10.31s) 2021-08-18 20:44:03,645 - INFO: Epoch:2/10, batch:210, train_loss:[0.28205], acc_top1:[0.90625], acc_top5:[0.96875](8.96s) 2021-08-18 20:44:13,641 - INFO: Epoch:2/10, batch:220, train_loss:[0.17315], acc_top1:[0.96875], acc_top5:[1.00000](10.00s) 2021-08-18 20:44:27,035 - INFO: [validation] Epoch:2/10, val_loss:[0.00529], val_top1:[0.89846], val_top5:[0.99385] 2021-08-18 20:44:27,699 - INFO: 已保存当前测试模型(epoch=2)为最优模型:Zodiac_Mobilenetv2_final 2021-08-18 20:44:27,700 - INFO: 最优top1测试精度:0.89846 (epoch=2) 2021-08-18 20:44:33,548 - INFO: Epoch:3/10, batch:230, train_loss:[0.23524], acc_top1:[0.92188], acc_top5:[1.00000](19.91s) 2021-08-18 20:44:43,755 - INFO: Epoch:3/10, batch:240, train_loss:[0.13375], acc_top1:[0.96875], acc_top5:[1.00000](10.21s) 2021-08-18 20:44:53,204 - INFO: Epoch:3/10, batch:250, train_loss:[0.24205], acc_top1:[0.90625], acc_top5:[1.00000](9.45s) 2021-08-18 20:45:02,680 - INFO: Epoch:3/10, batch:260, train_loss:[0.30691], acc_top1:[0.93750], acc_top5:[1.00000](9.48s) 2021-08-18 20:45:11,859 - INFO: Epoch:3/10, batch:270, train_loss:[0.23953], acc_top1:[0.95312], acc_top5:[1.00000](9.18s) 2021-08-18 20:45:20,276 - INFO: Epoch:3/10, batch:280, train_loss:[0.26566], acc_top1:[0.90625], acc_top5:[1.00000](8.42s) 2021-08-18 20:45:29,860 - INFO: Epoch:3/10, batch:290, train_loss:[0.21597], acc_top1:[0.95312], acc_top5:[1.00000](9.58s) 2021-08-18 20:45:39,252 - INFO: Epoch:3/10, batch:300, train_loss:[0.34542], acc_top1:[0.92188], acc_top5:[0.98438](9.39s) 2021-08-18 20:45:49,498 - INFO: Epoch:3/10, batch:310, train_loss:[0.16125], acc_top1:[0.98438], acc_top5:[1.00000](10.25s) 2021-08-18 20:45:59,449 - INFO: Epoch:3/10, batch:320, train_loss:[0.18767], acc_top1:[0.95312], acc_top5:[1.00000](9.95s) 2021-08-18 20:46:09,352 - INFO: Epoch:3/10, batch:330, train_loss:[0.32362], acc_top1:[0.93750], acc_top5:[0.98438](9.90s) 2021-08-18 20:46:25,174 - INFO: [validation] Epoch:3/10, val_loss:[0.00449], val_top1:[0.90462], val_top5:[0.99846] 2021-08-18 20:46:25,848 - INFO: 已保存当前测试模型(epoch=3)为最优模型:Zodiac_Mobilenetv2_final 2021-08-18 20:46:25,849 - INFO: 最优top1测试精度:0.90462 (epoch=3) 2021-08-18 20:46:29,669 - INFO: Epoch:4/10, batch:340, train_loss:[0.15083], acc_top1:[0.95312], acc_top5:[1.00000](20.32s) 2021-08-18 20:46:39,399 - INFO: Epoch:4/10, batch:350, train_loss:[0.07786], acc_top1:[0.98438], acc_top5:[1.00000](9.73s) 2021-08-18 20:46:49,194 - INFO: Epoch:4/10, batch:360, train_loss:[0.18050], acc_top1:[0.95312], acc_top5:[1.00000](9.79s) 2021-08-18 20:46:59,333 - INFO: Epoch:4/10, batch:370, train_loss:[0.15963], acc_top1:[0.95312], acc_top5:[1.00000](10.14s) 2021-08-18 20:47:08,950 - INFO: Epoch:4/10, batch:380, train_loss:[0.13318], acc_top1:[0.96875], acc_top5:[1.00000](9.62s) 2021-08-18 20:47:18,739 - INFO: Epoch:4/10, batch:390, train_loss:[0.14684], acc_top1:[0.93750], acc_top5:[1.00000](9.79s) 2021-08-18 20:47:29,050 - INFO: Epoch:4/10, batch:400, train_loss:[0.17527], acc_top1:[0.96875], acc_top5:[0.98438](10.31s) 2021-08-18 20:47:38,745 - INFO: Epoch:4/10, batch:410, train_loss:[0.10365], acc_top1:[0.96875], acc_top5:[1.00000](9.69s) 2021-08-18 20:47:48,626 - INFO: Epoch:4/10, batch:420, train_loss:[0.20553], acc_top1:[0.93750], acc_top5:[1.00000](9.88s) 2021-08-18 20:47:56,370 - INFO: Epoch:4/10, batch:430, train_loss:[0.06721], acc_top1:[1.00000], acc_top5:[1.00000](7.74s) 2021-08-18 20:48:06,305 - INFO: Epoch:4/10, batch:440, train_loss:[0.14027], acc_top1:[0.95312], acc_top5:[1.00000](9.93s) 2021-08-18 20:48:23,898 - INFO: [validation] Epoch:4/10, val_loss:[0.00420], val_top1:[0.91846], val_top5:[1.00000] 2021-08-18 20:48:24,665 - INFO: 已保存当前测试模型(epoch=4)为最优模型:Zodiac_Mobilenetv2_final 2021-08-18 20:48:24,666 - INFO: 最优top1测试精度:0.91846 (epoch=4) 2021-08-18 20:48:26,821 - INFO: Epoch:5/10, batch:450, train_loss:[0.09366], acc_top1:[0.98438], acc_top5:[1.00000](20.52s) 2021-08-18 20:48:36,958 - INFO: Epoch:5/10, batch:460, train_loss:[0.09438], acc_top1:[0.98438], acc_top5:[1.00000](10.14s) 2021-08-18 20:48:47,249 - INFO: Epoch:5/10, batch:470, train_loss:[0.19238], acc_top1:[0.96875], acc_top5:[0.98438](10.29s) 2021-08-18 20:48:57,046 - INFO: Epoch:5/10, batch:480, train_loss:[0.09879], acc_top1:[0.96875], acc_top5:[1.00000](9.80s) 2021-08-18 20:49:07,041 - INFO: Epoch:5/10, batch:490, train_loss:[0.06695], acc_top1:[0.98438], acc_top5:[1.00000](9.99s) 2021-08-18 20:49:15,346 - INFO: Epoch:5/10, batch:500, train_loss:[0.07161], acc_top1:[0.96875], acc_top5:[1.00000](8.31s) 2021-08-18 20:49:24,664 - INFO: Epoch:5/10, batch:510, train_loss:[0.09566], acc_top1:[0.98438], acc_top5:[1.00000](9.32s) 2021-08-18 20:49:33,218 - INFO: Epoch:5/10, batch:520, train_loss:[0.06367], acc_top1:[1.00000], acc_top5:[1.00000](8.55s) 2021-08-18 20:49:41,788 - INFO: Epoch:5/10, batch:530, train_loss:[0.07820], acc_top1:[1.00000], acc_top5:[1.00000](8.57s) 2021-08-18 20:49:51,577 - INFO: Epoch:5/10, batch:540, train_loss:[0.10651], acc_top1:[0.96875], acc_top5:[1.00000](9.79s) 2021-08-18 20:50:01,029 - INFO: Epoch:5/10, batch:550, train_loss:[0.12549], acc_top1:[0.96875], acc_top5:[1.00000](9.45s) 2021-08-18 20:50:11,980 - INFO: Epoch:5/10, batch:560, train_loss:[0.07814], acc_top1:[0.98438], acc_top5:[1.00000](10.95s) 2021-08-18 20:50:21,376 - INFO: [validation] Epoch:5/10, val_loss:[0.00400], val_top1:[0.92615], val_top5:[0.99846] 2021-08-18 20:50:22,029 - INFO: 已保存当前测试模型(epoch=5)为最优模型:Zodiac_Mobilenetv2_final 2021-08-18 20:50:22,029 - INFO: 最优top1测试精度:0.92615 (epoch=5) 2021-08-18 20:50:32,246 - INFO: Epoch:6/10, batch:570, train_loss:[0.06100], acc_top1:[1.00000], acc_top5:[1.00000](20.27s) 2021-08-18 20:50:40,916 - INFO: Epoch:6/10, batch:580, train_loss:[0.07342], acc_top1:[0.98438], acc_top5:[1.00000](8.67s) 2021-08-18 20:50:49,712 - INFO: Epoch:6/10, batch:590, train_loss:[0.07284], acc_top1:[1.00000], acc_top5:[1.00000](8.80s) 2021-08-18 20:50:59,671 - INFO: Epoch:6/10, batch:600, train_loss:[0.14909], acc_top1:[0.95312], acc_top5:[0.98438](9.96s) 2021-08-18 20:51:08,865 - INFO: Epoch:6/10, batch:610, train_loss:[0.06966], acc_top1:[0.98438], acc_top5:[1.00000](9.19s) 2021-08-18 20:51:17,472 - INFO: Epoch:6/10, batch:620, train_loss:[0.06740], acc_top1:[0.98438], acc_top5:[1.00000](8.61s) 2021-08-18 20:51:26,750 - INFO: Epoch:6/10, batch:630, train_loss:[0.10529], acc_top1:[0.98438], acc_top5:[0.98438](9.28s) 2021-08-18 20:51:36,055 - INFO: Epoch:6/10, batch:640, train_loss:[0.05156], acc_top1:[1.00000], acc_top5:[1.00000](9.30s) 2021-08-18 20:51:45,744 - INFO: Epoch:6/10, batch:650, train_loss:[0.08652], acc_top1:[0.98438], acc_top5:[1.00000](9.69s) 2021-08-18 20:51:56,040 - INFO: Epoch:6/10, batch:660, train_loss:[0.04166], acc_top1:[1.00000], acc_top5:[1.00000](10.30s) 2021-08-18 20:52:07,168 - INFO: Epoch:6/10, batch:670, train_loss:[0.04415], acc_top1:[1.00000], acc_top5:[1.00000](11.13s) 2021-08-18 20:52:18,479 - INFO: [validation] Epoch:6/10, val_loss:[0.00400], val_top1:[0.91846], val_top5:[1.00000] 2021-08-18 20:52:18,480 - INFO: 最优top1测试精度:0.92615 (epoch=5) 2021-08-18 20:52:25,105 - INFO: Epoch:7/10, batch:680, train_loss:[0.03270], acc_top1:[1.00000], acc_top5:[1.00000](17.94s) 2021-08-18 20:52:34,802 - INFO: Epoch:7/10, batch:690, train_loss:[0.04794], acc_top1:[1.00000], acc_top5:[1.00000](9.70s) 2021-08-18 20:52:45,562 - INFO: Epoch:7/10, batch:700, train_loss:[0.04604], acc_top1:[1.00000], acc_top5:[1.00000](10.76s) 2021-08-18 20:52:54,632 - INFO: Epoch:7/10, batch:710, train_loss:[0.04353], acc_top1:[1.00000], acc_top5:[1.00000](9.07s) 2021-08-18 20:53:03,736 - INFO: Epoch:7/10, batch:720, train_loss:[0.05784], acc_top1:[1.00000], acc_top5:[1.00000](9.10s) 2021-08-18 20:53:12,762 - INFO: Epoch:7/10, batch:730, train_loss:[0.10201], acc_top1:[0.98438], acc_top5:[1.00000](9.03s) 2021-08-18 20:53:21,900 - INFO: Epoch:7/10, batch:740, train_loss:[0.11172], acc_top1:[0.96875], acc_top5:[1.00000](9.14s) 2021-08-18 20:53:32,386 - INFO: Epoch:7/10, batch:750, train_loss:[0.03383], acc_top1:[1.00000], acc_top5:[1.00000](10.49s) 2021-08-18 20:53:41,702 - INFO: Epoch:7/10, batch:760, train_loss:[0.05390], acc_top1:[1.00000], acc_top5:[1.00000](9.32s) 2021-08-18 20:53:52,079 - INFO: Epoch:7/10, batch:770, train_loss:[0.04625], acc_top1:[0.98438], acc_top5:[1.00000](10.38s) 2021-08-18 20:54:00,986 - INFO: Epoch:7/10, batch:780, train_loss:[0.04487], acc_top1:[1.00000], acc_top5:[1.00000](8.91s) 2021-08-18 20:54:14,524 - INFO: [validation] Epoch:7/10, val_loss:[0.00421], val_top1:[0.91692], val_top5:[1.00000] 2021-08-18 20:54:14,525 - INFO: 最优top1测试精度:0.92615 (epoch=5) 2021-08-18 20:54:20,247 - INFO: Epoch:8/10, batch:790, train_loss:[0.02338], acc_top1:[1.00000], acc_top5:[1.00000](19.26s) 2021-08-18 20:54:30,393 - INFO: Epoch:8/10, batch:800, train_loss:[0.03130], acc_top1:[1.00000], acc_top5:[1.00000](10.15s) 2021-08-18 20:54:38,556 - INFO: Epoch:8/10, batch:810, train_loss:[0.02492], acc_top1:[1.00000], acc_top5:[1.00000](8.16s) 2021-08-18 20:54:47,180 - INFO: Epoch:8/10, batch:820, train_loss:[0.02855], acc_top1:[1.00000], acc_top5:[1.00000](8.62s) 2021-08-18 20:54:59,162 - INFO: Epoch:8/10, batch:830, train_loss:[0.04445], acc_top1:[1.00000], acc_top5:[1.00000](11.98s) 2021-08-18 20:55:09,225 - INFO: Epoch:8/10, batch:840, train_loss:[0.02702], acc_top1:[1.00000], acc_top5:[1.00000](10.06s) 2021-08-18 20:55:18,196 - INFO: Epoch:8/10, batch:850, train_loss:[0.04049], acc_top1:[1.00000], acc_top5:[1.00000](8.97s) 2021-08-18 20:55:27,605 - INFO: Epoch:8/10, batch:860, train_loss:[0.02455], acc_top1:[1.00000], acc_top5:[1.00000](9.41s) 2021-08-18 20:55:37,179 - INFO: Epoch:8/10, batch:870, train_loss:[0.02132], acc_top1:[1.00000], acc_top5:[1.00000](9.57s) 2021-08-18 20:55:46,563 - INFO: Epoch:8/10, batch:880, train_loss:[0.04612], acc_top1:[0.98438], acc_top5:[1.00000](9.38s) 2021-08-18 20:55:55,424 - INFO: Epoch:8/10, batch:890, train_loss:[0.03026], acc_top1:[1.00000], acc_top5:[1.00000](8.86s) 2021-08-18 20:56:10,626 - INFO: [validation] Epoch:8/10, val_loss:[0.00418], val_top1:[0.91538], val_top5:[0.99846] 2021-08-18 20:56:10,626 - INFO: 最优top1测试精度:0.92615 (epoch=5) 2021-08-18 20:56:13,795 - INFO: Epoch:9/10, batch:900, train_loss:[0.01744], acc_top1:[1.00000], acc_top5:[1.00000](18.37s) 2021-08-18 20:56:22,685 - INFO: Epoch:9/10, batch:910, train_loss:[0.01865], acc_top1:[1.00000], acc_top5:[1.00000](8.89s) 2021-08-18 20:56:32,232 - INFO: Epoch:9/10, batch:920, train_loss:[0.01607], acc_top1:[1.00000], acc_top5:[1.00000](9.55s) 2021-08-18 20:56:40,905 - INFO: Epoch:9/10, batch:930, train_loss:[0.03191], acc_top1:[0.98438], acc_top5:[1.00000](8.67s) 2021-08-18 20:56:49,723 - INFO: Epoch:9/10, batch:940, train_loss:[0.02905], acc_top1:[1.00000], acc_top5:[1.00000](8.82s) 2021-08-18 20:56:58,554 - INFO: Epoch:9/10, batch:950, train_loss:[0.01862], acc_top1:[1.00000], acc_top5:[1.00000](8.83s) 2021-08-18 20:57:08,666 - INFO: Epoch:9/10, batch:960, train_loss:[0.04437], acc_top1:[0.98438], acc_top5:[1.00000](10.11s) 2021-08-18 20:57:19,214 - INFO: Epoch:9/10, batch:970, train_loss:[0.00877], acc_top1:[1.00000], acc_top5:[1.00000](10.55s) 2021-08-18 20:57:29,104 - INFO: Epoch:9/10, batch:980, train_loss:[0.02090], acc_top1:[1.00000], acc_top5:[1.00000](9.89s) 2021-08-18 20:57:40,243 - INFO: Epoch:9/10, batch:990, train_loss:[0.07159], acc_top1:[0.96875], acc_top5:[1.00000](11.14s) 2021-08-18 20:57:49,314 - INFO: Epoch:9/10, batch:1000, train_loss:[0.02380], acc_top1:[1.00000], acc_top5:[1.00000](9.07s) 2021-08-18 20:58:05,972 - INFO: [validation] Epoch:9/10, val_loss:[0.00425], val_top1:[0.92769], val_top5:[0.99692] 2021-08-18 20:58:06,635 - INFO: 已保存当前测试模型(epoch=9)为最优模型:Zodiac_Mobilenetv2_final 2021-08-18 20:58:06,636 - INFO: 最优top1测试精度:0.92769 (epoch=9) 2021-08-18 20:58:08,393 - INFO: Epoch:10/10, batch:1010, train_loss:[0.01303], acc_top1:[1.00000], acc_top5:[1.00000](19.08s) 2021-08-18 20:58:17,196 - INFO: Epoch:10/10, batch:1020, train_loss:[0.01460], acc_top1:[1.00000], acc_top5:[1.00000](8.80s) 2021-08-18 20:58:27,992 - INFO: Epoch:10/10, batch:1030, train_loss:[0.02274], acc_top1:[1.00000], acc_top5:[1.00000](10.80s) 2021-08-18 20:58:38,393 - INFO: Epoch:10/10, batch:1040, train_loss:[0.01915], acc_top1:[1.00000], acc_top5:[1.00000](10.40s) 2021-08-18 20:58:48,539 - INFO: Epoch:10/10, batch:1050, train_loss:[0.00959], acc_top1:[1.00000], acc_top5:[1.00000](10.15s) 2021-08-18 20:58:57,457 - INFO: Epoch:10/10, batch:1060, train_loss:[0.01160], acc_top1:[1.00000], acc_top5:[1.00000](8.92s) 2021-08-18 20:59:06,237 - INFO: Epoch:10/10, batch:1070, train_loss:[0.01434], acc_top1:[1.00000], acc_top5:[1.00000](8.78s) 2021-08-18 20:59:14,633 - INFO: Epoch:10/10, batch:1080, train_loss:[0.02018], acc_top1:[1.00000], acc_top5:[1.00000](8.40s) 2021-08-18 20:59:24,413 - INFO: Epoch:10/10, batch:1090, train_loss:[0.01969], acc_top1:[1.00000], acc_top5:[1.00000](9.78s) 2021-08-18 20:59:33,290 - INFO: Epoch:10/10, batch:1100, train_loss:[0.01351], acc_top1:[1.00000], acc_top5:[1.00000](8.88s) 2021-08-18 20:59:42,958 - INFO: Epoch:10/10, batch:1110, train_loss:[0.02126], acc_top1:[1.00000], acc_top5:[1.00000](9.67s) 2021-08-18 20:59:53,134 - INFO: Epoch:10/10, batch:1120, train_loss:[0.02153], acc_top1:[1.00000], acc_top5:[1.00000](10.18s) 2021-08-18 21:00:02,480 - INFO: [validation] Epoch:10/10, val_loss:[0.00408], val_top1:[0.92769], val_top5:[1.00000] 2021-08-18 21:00:02,481 - INFO: 最优top1测试精度:0.92769 (epoch=9) 2021-08-18 21:00:02,481 - INFO: 训练完成,最终性能accuracy=0.92769(epoch=9), 总耗时1294.77s, 已将其保存为:Zodiac_Mobilenetv2_final 2021-08-18 21:00:02,482 - INFO: 训练完毕,结果路径D:\Workspace\ExpResults\HW06TransferLearning\Zodiac_Mobilenetv2. 2021-08-18 21:00:02,482 - INFO: Done.
训练完成后,建议将 *ExpResults* 文件夹的最终文件 **copy** 到 *ExpDeployments* 用于进行部署和应用。
Paddle 2.0+的离线测试也抛弃了fluid方法。基本流程和训练是一致。
if __name__ == '__main__':
# 设置输入样本的维度
input_spec = InputSpec(shape=[None] + args['input_size'], dtype='float32', name='image')
label_spec = InputSpec(shape=[None, 1], dtype='int64', name='label')
# 载入模型
network = paddle.vision.models.mobilenet_v2(num_classes=args['class_dim'])
model = paddle.Model(network, input_spec, label_spec) # 模型实例化
model.load(deployment_checkpoint_model) # 载入调优模型的参数
model.prepare(loss=paddle.nn.CrossEntropyLoss(), # 设置loss
metrics=paddle.metric.Accuracy(topk=(1,5))) # 设置评价指标
# 执行评估函数,并输出验证集样本的损失和精度
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}'.format(avg_loss, avg_acc_top1, avg_acc_top5), end='')
开始评估... [验证集] 损失: 0.00425, top1精度:0.92769, top5精度为:0.99692 [测试集] 损失: 0.00516, top1精度:0.91212, top5精度为:0.99545]
【结果分析】
需要注意的是此处的精度与训练过程中输出的测试精度是不相同的,因为训练过程中使用的是验证集, 而这里的离线测试使用的是测试集.
下面我们将在ResNet50, ResNet18, Mobilenetv2, VGG16四个模型对十二生肖数据集
进行评估,所有模型设置batch_size=64。
数据集包含样本8500个,其中训练验证集样本7840个,训练集样本7190个, 验证集样本650个, 测试集样本660个, 共计8500个。
模型名称 | 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)
比从头训练收敛更快
,且更容易获得较好的性能
手势识别数据集
时更容易发生过拟合,训练起来更困难;相似的ResNet50在该数据集上也趋向于过拟合参数较多
,且没有类似残差结构的辅助,需要更低的学习率来进行训练参数的数量
对训练时间
有较大的影响参数数量
是固定的,主要由其卷积层、全连接层等内部结构决定值得注意的是,由于数据集比较简单,参数的初始化以及随机梯度下降算法对于训练的过程影响比较大。即每次训练的过程(甚至结果)可能会有一定的波动,但较好的超参数设置仍然有助于获得较好的性能。