动手学PaddlePaddle(5):迁移学习

本次练习,用迁移学习思想,结合paddle框架,来实现图像的分类。

相关理论:

1. 原有模型作为一个特征提取器:

使用一个用ImageNet数据集提前训练(pre-trained)好的CNN,再除去最后一层全连接层(fully-connected layer),即除去原有的分类器部分。然后再用剩下的神经网络作为特征提取器应用在新的数据集上。我们只需要用新的训练集训练一个嫁接到这个特征提取器上的分类器即可。

2.fine-turning原有模型

这种方法要求不仅仅除去原有模型的分类器,再用新的数据集训练新的分类器,还要求微调(fine-tune)本身神经网络的参数(weights)。和方式1的区别是:方式1要求固定原有神经网络的参数,而fine-tune允许训练新的数据集时对原有神经网络的参数进行更改。为了防止过拟合,有时可以要求固定某些层的参数,只微调剩下的一些层。

本次实验总共分两个steps

  • step-1: 在step-1中,加载在imagenet数据集上训练好的Resnet50模型,作为预训练模型,并且冻结除fc层之外的参数,只训练fc层。得到step_1_model;

拿到新数据集,想要用预训练模型处理的时候,通常都会先用step-1的方法看看预训练模型在新数据上的表现怎么样,摸个底。如果表现不错,还想看看能不能进一步提升,就可以试试Fine-tune(即解锁比较少的卷积层继续训练),但是不要期待会有什么质的飞跃。如果由于新数据集与原数据集(例如ImageNet数据集)的差别太大导致表现很糟,那么一方面可以考虑从头训练模型,另一方面也可以考虑解锁比较多层的训练,亦或干脆只用预训练模型的参数作为初始值,对模型进行完整训练。

  • step-2: 在step-2中,把step_1_model作为预训练模型,并在此基础上重新训练,得到最终的模型step_2_model。

但是要注意:
事实上,step-2必须在已经进行过‘冻结特征提取器参数的训练之后再尝试训练模型,这时分类器的参数已经经过一次训练。如果从随机生成的分类器参数开始直接训练,在做参数更新迭代过程中梯度将很可能过大,而导致模型的崩溃,使模型忘记学到的所有东西。

在finetune时,batch_size设置最好不要太大,以便于加速模型收敛。学习率也适当小一些。


目录

step-1:

1. 导入库

2. 定义ResNet网络

3.训练前准备

4.开始训练

step-2:

5. 导入库

6.定义ResNet网络

7.训练前准备


step-1:

在step-1中,加载在imagenet数据集上训练好的Resnet50模型,作为预训练模型,并且冻结除fc层之外的参数,只训练fc层。得到step_1_model。

1. 导入库

实验第一步,导入需要的库,本实验中专门定义了一个 reader.py文件,用来对数据集进行读取和预处理,所以也需要把reader.py文件import进来。

import os
import shutil
import paddle as paddle
import paddle.fluid as fluid
from paddle.fluid.param_attr import ParamAttr
import reader
# 获取flowers数据
train_reader = paddle.batch(reader.train(), batch_size=16)
test_reader = paddle.batch(reader.val(), batch_size=16)

2. 定义ResNet网络

  • 本次实验使用ResNet50这个残差神经网络,所以,接下来需要定义一个残差神经网络。
  • PaddlePaddle官方已经提供了ResNet以及其他经典的网络模型。链接:https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/image_classification/models/resnet.py
  • 网络定义时,每一个层都由指定参数名字。
# 定义残差神经网络(ResNet)
def resnet50(input):def conv_bn_layer(input, num_filters, filter_size, stride=1, groups=1, act=None, name=None):conv = fluid.layers.conv2d(input=input,num_filters=num_filters,filter_size=filter_size,stride=stride,padding=(filter_size - 1) // 2,groups=groups,act=None,param_attr=ParamAttr(name=name + "_weights"),bias_attr=False,name=name + '.conv2d.output.1')if name == "conv1":bn_name = "bn_" + nameelse:bn_name = "bn" + name[3:]return fluid.layers.batch_norm(input=conv,act=act,name=bn_name + '.output.1',param_attr=ParamAttr(name=bn_name + '_scale'),bias_attr=ParamAttr(bn_name + '_offset'),moving_mean_name=bn_name + '_mean',moving_variance_name=bn_name + '_variance', )def shortcut(input, ch_out, stride, name):ch_in = input.shape[1]if ch_in != ch_out or stride != 1:return conv_bn_layer(input, ch_out, 1, stride, name=name)else:return inputdef bottleneck_block(input, num_filters, stride, name):conv0 = conv_bn_layer(input=input,num_filters=num_filters,filter_size=1,act='relu',name=name + "_branch2a")conv1 = conv_bn_layer(input=conv0,num_filters=num_filters,filter_size=3,stride=stride,act='relu',name=name + "_branch2b")conv2 = conv_bn_layer(input=conv1,num_filters=num_filters * 4,filter_size=1,act=None,name=name + "_branch2c")short = shortcut(input, num_filters * 4, stride, name=name + "_branch1")return fluid.layers.elementwise_add(x=short, y=conv2, act='relu', name=name + ".add.output.5")depth = [3, 4, 6, 3]num_filters = [64, 128, 256, 512]conv = conv_bn_layer(input=input, num_filters=64, filter_size=7, stride=2, act='relu', name="conv1")conv = fluid.layers.pool2d(input=conv, pool_size=3, pool_stride=2, pool_padding=1, pool_type='max')for block in range(len(depth)):for i in range(depth[block]):conv_name = "res" + str(block + 2) + chr(97 + i)conv = bottleneck_block(input=conv,num_filters=num_filters[block],stride=2 if i == 0 and block != 0 else 1,name=conv_name)pool = fluid.layers.pool2d(input=conv, pool_size=7, pool_type='avg', global_pooling=True)return pool

本次实验,使用的图片数据集是flowers。图片是3通道宽高都是224的彩色图,总类别是5种,每一个种类大约有六百多张。

3.训练前准备

接下来,开始做训练前的准备工作。首先,定义图片数据和标签数据的输入层:

# 定义输入层
image = fluid.layers.data(name='image', shape=[3, 224, 224], dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')

利用stop_gradient,使得pool以上的层停止梯度传递,相当于keras中的freeze。这样就可以只训练最后的fc层,但是要注意:数据集是5分类,所以size要设为5.

# 获取分类器的上一层
pool = resnet50(image)
# 停止梯度下降
pool.stop_gradient = True
# 由这里创建一个基本的主程序
base_model_program = fluid.default_main_program().clone()# 这里再重新加载网络的分类器,大小为本项目的分类大小
model = fluid.layers.fc(input=pool, size=5, act='softmax')

接下来,要做的工作有:

  • 定义损失函数;
  • 求准确率;
  • 定义优化器;
  • 设定训练场所;
  • 定义执行器,并且完成参数初始化;
# 获取损失函数和准确率函数
cost = fluid.layers.cross_entropy(input=model, label=label)
avg_cost = fluid.layers.mean(cost)
acc = fluid.layers.accuracy(input=model, label=label)# 定义优化方法
optimizer = fluid.optimizer.AdamOptimizer(learning_rate=1e-3)
opts = optimizer.minimize(avg_cost)# 定义训练场所
place = fluid.CUDAPlace(0)#用GPU训练
#place = fluid.CPUPlace() #用CPU训练
exe = fluid.Executor(place)
# 进行参数初始化
exe.run(fluid.default_startup_program())
  • 接下来加载预训练模型,使用paddle官网上训练好的ResNet50模型,这个模型存储在“./ResNet50_pretrained/”,也可以去官网上下载,链接:http://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_pretrained.tar
  • 通过if_exist函数判断网络所需的模型文件是否存在,然后再通过调用fluid.io.load_vars加载存在的模型文件。
# 官方提供的原预训练模型
src_pretrain_model_path = './ResNet50_pretrained/'# 通过这个函数判断模型文件是否存在
def if_exist(var):path = os.path.join(src_pretrain_model_path, var.name)exist = os.path.exists(path)return exist# 加载模型文件,只加载存在模型的模型文件
fluid.io.load_vars(executor=exe, dirname=src_pretrain_model_path, predicate=if_exist, main_program=base_model_program)

4.开始训练

接下来就定义一个双层循环来开始训练模型,并且还可以把训练过程中的cost值和acc值打印出来,以此来直观的感受训练效果。

# 优化内存
# optimized = fluid.transpiler.memory_optimize(input_program=fluid.default_main_program(), print_log=False)# 定义输入数据维度
feeder = fluid.DataFeeder(place=place, feed_list=[image, label])# 训练10次
for pass_id in range(10):# 进行训练for batch_id, data in enumerate(train_reader()):train_cost, train_acc = exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost, acc])# 每100个batch打印一次信息if batch_id % 100 == 0:print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %(pass_id, batch_id, train_cost[0], train_acc[0]))

训练结束之后,使用fluid.io.save_param保存训练好的参数。

到这里为止,把从imagenet数据集上训练好的的原预训练模型,结合数据集,把最后一层fc进行了训练。接下来就是使用这个已经处理过的模型正式训练了。

# 保存参数模型
save_pretrain_model_path = 'models/step-1_model/'# 删除旧的模型文件
shutil.rmtree(save_pretrain_model_path, ignore_errors=True)
# 创建保持模型文件目录
os.makedirs(save_pretrain_model_path)
# 保存参数模型
fluid.io.save_params(executor=exe, dirname=save_pretrain_model_path)

step-2:

5. 导入库

import os
import shutil
import paddle as paddle
import paddle.dataset.flowers as flowers
import paddle.fluid as fluid
from paddle.fluid.param_attr import ParamAttr
import reader# 获取flowers数据
train_reader = paddle.batch(reader.train(), batch_size=16)
test_reader = paddle.batch(reader.val(), batch_size=16)

6.定义ResNet网络

仍然需要定义一个残差神经网络,这个残差神经网络跟第一步时的基本一样的,只是把分类器(也就是fc层)也加进去了,这是一个完整的神经网络。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

# 定义残差神经网络(ResNet)
def resnet50(input, class_dim):def conv_bn_layer(input, num_filters, filter_size, stride=1, groups=1, act=None, name=None):conv = fluid.layers.conv2d(input=input,num_filters=num_filters,filter_size=filter_size,stride=stride,padding=(filter_size - 1) // 2,groups=groups,act=None,param_attr=ParamAttr(name=name + "_weights"),bias_attr=False,name=name + '.conv2d.output.1')if name == "conv1":bn_name = "bn_" + nameelse:bn_name = "bn" + name[3:]return fluid.layers.batch_norm(input=conv,act=act,name=bn_name + '.output.1',param_attr=ParamAttr(name=bn_name + '_scale'),bias_attr=ParamAttr(bn_name + '_offset'),moving_mean_name=bn_name + '_mean',moving_variance_name=bn_name + '_variance', )def shortcut(input, ch_out, stride, name):ch_in = input.shape[1]if ch_in != ch_out or stride != 1:return conv_bn_layer(input, ch_out, 1, stride, name=name)else:return inputdef bottleneck_block(input, num_filters, stride, name):conv0 = conv_bn_layer(input=input,num_filters=num_filters,filter_size=1,act='relu',name=name + "_branch2a")conv1 = conv_bn_layer(input=conv0,num_filters=num_filters,filter_size=3,stride=stride,act='relu',name=name + "_branch2b")conv2 = conv_bn_layer(input=conv1,num_filters=num_filters * 4,filter_size=1,act=None,name=name + "_branch2c")short = shortcut(input, num_filters * 4, stride, name=name + "_branch1")return fluid.layers.elementwise_add(x=short, y=conv2, act='relu', name=name + ".add.output.5")depth = [3, 4, 6, 3]num_filters = [64, 128, 256, 512]conv = conv_bn_layer(input=input, num_filters=64, filter_size=7, stride=2, act='relu', name="conv1")conv = fluid.layers.pool2d(input=conv, pool_size=3, pool_stride=2, pool_padding=1, pool_type='max')for block in range(len(depth)):for i in range(depth[block]):conv_name = "res" + str(block + 2) + chr(97 + i)conv = bottleneck_block(input=conv,num_filters=num_filters[block],stride=2 if i == 0 and block != 0 else 1,name=conv_name)pool = fluid.layers.pool2d(input=conv, pool_size=7, pool_type='avg', global_pooling=True)output = fluid.layers.fc(input=pool, size=class_dim, act='softmax')return output

7.训练前准备

接下来,开始做训练前的准备工作:

  • 定义图片数据和标签数据的输入层;
  • 定义损失函数;
  • 求准确率;
  • 定义优化器;
  • 设定训练场所;
  • 定义执行器,并且完成参数初始化;
# 定义输入层
image = fluid.layers.data(name='image', shape=[3, 224, 224], dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')# 获取分类器
model = resnet50(image, 5)# 获取损失函数和准确率函数
cost = fluid.layers.cross_entropy(input=model, label=label)
avg_cost = fluid.layers.mean(cost)
acc = fluid.layers.accuracy(input=model, label=label)# 获取训练和测试程序
test_program = fluid.default_main_program().clone(for_test=True)# 定义优化方法
optimizer = fluid.optimizer.AdamOptimizer(learning_rate=1e-3)
opts = optimizer.minimize(avg_cost)# 定义一个使用GPU的执行器
place = fluid.CUDAPlace(0)
#place = fluid.CPUPlace()
exe = fluid.Executor(place)
# 进行参数初始化
exe.run(fluid.default_startup_program())

加载经过step-1训练好的模型,作为新的预训练模型。并在此基础上进行重新训练:

# 经过step-1处理后的的预训练模型
pretrained_model_path = 'models/step-1_model/'# 加载经过处理的模型
fluid.io.load_params(executor=exe, dirname=pretrained_model_path)

接下来就定义一个双层循环来开始训练模型,并且还可以把训练过程中的cost值和acc值打印出来,以此来直观的感受训练效果。

# 定义输入数据维度
feeder = fluid.DataFeeder(place=place, feed_list=[image, label])# 训练10次
for pass_id in range(10):# 进行训练for batch_id, data in enumerate(train_reader()):train_cost, train_acc = exe.run(program=fluid.default_main_program(),feed=feeder.feed(data),fetch_list=[avg_cost, acc])# 每100个batch打印一次信息if batch_id % 100 == 0:print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %(pass_id, batch_id, train_cost[0], train_acc[0]))# 进行测试test_accs = []test_costs = []for batch_id, data in enumerate(test_reader()):test_cost, test_acc = exe.run(program=test_program,feed=feeder.feed(data),fetch_list=[avg_cost, acc])test_accs.append(test_acc[0])test_costs.append(test_cost[0])# 求测试结果的平均值test_cost = (sum(test_costs) / len(test_costs))test_acc = (sum(test_accs) / len(test_accs))print('Test:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, test_cost, test_acc))

训练结束之后,可以保存预测模型用于之后的预测使用。

# 保存预测模型
save_path = 'models/step_2_model/'# 删除旧的模型文件
shutil.rmtree(save_path, ignore_errors=True)
# 创建保持模型文件目录
os.makedirs(save_path)
# 保存预测模型
fluid.io.save_inference_model(save_path, feeded_var_names=[image.name], target_vars=[model], executor=exe)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apollo进阶课程㉓丨Apollo规划技术详解——Motion Planning with Environment

原文链接:进阶课程㉓丨Apollo规划技术详解——Motion Planning with Environment 当行为层决定要在当前环境中执行的驾驶行为时,其可以是例如巡航-车道,改变车道或右转,所选择的行为必须被转换成路径或轨迹,可由低级反…

Java对象模型-oop和klass

oop-klass模型 Hotspot 虚拟机在内部使用两组类来表示Java的对象和类。 oop(ordinary object pointer),用来描述对象实例信息。klass,用来描述 Java 类,是虚拟机内部Java类型结构的对等体 。 JVM内部定义了各种oop-klass,在JV…

Apollo进阶课程㉔丨Apollo 规划技术详解——Motion Planning Environment

原文链接:进阶课程㉔丨Apollo 规划技术详解——Motion Planning Environment 自动驾驶汽车核心技术包括环境感知、行为决策、运动规划与控制等方面。其中,行为决策系统、运动规划与控制系统作为无人驾驶汽车的“大脑”,决定了其在不同交通驾…

一步步编写操作系统 26 打开A20地址线

打开A20地址线 还记得实模式下的wrap-around吗?也就是地址回绕。咱们一起来复习一下。实模式下内存访问是采取“段基址:段内偏移地址”的形式,段基址要乘以16后再加上段内偏移地址。实模式下寄存器都是16位的,如果段基址和段内偏移地址都为1…

一步步编写操作系统 27 处理器微架构之流水线简介

了解处理器内部硬件架构,有助于理解软件运行原理,因为这两者本身相辅相成,相互依存。就像枪和狙击手,枪的操作和外形设计都是要根据人体工学,让人不仅操作容易,而且携带也要轻便,做到能随时射出…

Apollo进阶课程㉚丨Apollo ROS背景介绍

原文链接:进阶课程㉚丨Apollo ROS背景介绍 ROS是机器人学习和无人车学习最好Linux平台软件,资源丰厚。无人车的规划、控制算法通常运行在Linux系统上,各个模块通常使用ROS进行连接。 上周阿波君为大家详细介绍了「进阶课程㉙Apollo控制技术详…

一步步编写操作系统 30 cpu的分支预测简介

人在道路的分岔口时要预测哪条路能够到达目的地,面对众多选择时,计算机也一样要抉择,毕竟计算机的运行方式是以人的思路来设计的,计算机中的抉择其实就是人在抉择。 cpu中的指令是在流水线上执行。分支预测,是指当处理…

【HDU - 5492】Find a path(dp,tricks)

题干: Frog fell into a maze. This maze is a rectangle containing NN rows and MM columns. Each grid in this maze contains a number, which is called the magic value. Frog now stays at grid (1, 1), and he wants to go to grid (N, M). For each step,…

Apollo进阶课程㉜丨Apollo ROS原理—1

原文链接:进阶课程㉜丨Apollo ROS原理—1 ROS在开发过程中,基于功能把整个自动驾驶系统分成多个模块,每个模块负责自己消息的接收、处理、发布。当模块需要联调时,通过框架可以把各个模块快速的集成到一起。 上周阿波君为大家详细…

Ubuntu下安装Chrome浏览器的两个方法

一、通过直接下载安装Google Chrome浏览器deb包。 打开Ubuntu终端,以下为32位版本,使用下面的命令。 wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb 以下为64位版本,使用下面的命令。 wget https://dl.…

Apollo进阶课程㉝丨Apollo ROS原理—2

原文链接:进阶课程㉝丨Apollo ROS原理—2 在ROS系统中,从数据的发布到订阅节点之间需要进行数据的拷贝。在数据量很大的情况下,很显然这会影响数据的传输效率。所以Apollo项目对于ROS第一个改造就是通过共享内存来减少数据拷贝,以…

Java 10 常用集合继承关系图

概述 集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用。 类图如下: 1、Iterable与Iterator接口之间的区别 我看到好多网上的文章类图里面Collection 是继承Iterator接口&a…

【CodeForces - 673D】Bear and Two Paths(构造,tricks)

题干: Bearland has n cities, numbered 1 through n. Cities are connected via bidirectional roads. Each road connects two distinct cities. No two roads connect the same pair of cities. Bear Limak was once in a city a and he wanted to go to a cit…

Apoll进阶课程㉞丨Apollo ROS原理—3

原文链接:进阶课程㉞丨Apollo ROS原理—3 机器人操作系统(ROS)是一个成熟而灵活的机器人编程框架。ROS提供了所需的工具,可以轻松访问传感器数据,处理数据,并为机器人的电机和其它执行器生成适当的响应。整个ROS系统被设计为在计…

SM3密码杂凑算法原理

目录 1.概述 2、算法描述 2.1 概述 2.2 填充 2.3 迭代压缩 2.3 消息扩展 2.4 压缩函数 2.5 杂凑值 1.概述 SM3是我国采用的一种密码散列函数标准,由国家密码管理局于2010年12月17日发布。相关标准为“GM/T 0004-2012 《SM3密码杂凑算法》”。 在商用密码体…

动手学无人驾驶(1):交通标志识别

今天主要介绍无人驾驶当中深度学习技术的应用。 本文是根据博客专家AdamShan的文章整理而来,在此表示感谢。 关于深度学习的图像分类技术,网上已有很多关于深度学习的课程(如吴恩达老师的深度学习专项课程),故本文不对…

《操作系统真象还原》-阅读笔记(上)

第一章 配置bochs,进入bochs simulator后一直是黑屏,原来默认是调试模式,需要输入C(continue)来让调试继续。 第二章 主讲MBR及进入MBR前的步骤 1.实模式只能访问1MB的内存空间。 2.BIOS在ROM中。 3.开机上电后CS&a…

Apollo进阶课程㉟丨Apollo ROS原理—4

原文链接:进阶课程㉟丨Apollo ROS原理—4 ROS是一个强大而灵活的机器人编程框架,从软件构架的角度说,它是一种基于消息传递通信的分布式多进程框架。 ROS本身是基于消息机制的,可以根据功能把软件拆分成为各个模块,每…

《操作系统真象还原》-阅读笔记(中)

第七章 操作系统是由中断驱动的。 中断分为外部中断和内部中断。 外部中断分为可屏蔽中断和不可屏蔽中断,内部中断分为软中断和异常。 外部中断 来自CPU外部的中断。可屏蔽中断:通过INTR引脚进入CPU,外部设备如硬盘、网卡、打印机等发出的…

动手学无人驾驶(2):车辆检测

上一篇博客介绍了无人驾驶中深度学习在交通标志识别中的应用(动手学无人驾驶(1):交通标志识别)。 本文介绍如何使用深度学习进行车辆检测,使用到的模型是YOLO模型,关于YOLO模型的具体检测原理&a…