迁移学习和finetune的区别及迁移学习代码实现
转载:https://blog.csdn.net/gentelyang/article/details/77512565
一:区别
1:迁移学习是将已经学习到的知识应用到其他领域,比如通用的语音模型迁移到某个人的语音模型上。
迁移学习就是将一个问题上训练好的模型通过简单的调整使其适用于一个新的问题。
例如利用ImageNet数据集上训练好的Inception-V3模型来解决一个新的图像分类问题,可以保留训练好的Inception-v3模型中所有卷积层的参数,只是替换最后一层全连接层,在最后这一层全连接层之前的网络层称为瓶颈层。而将新的图像通过训练好的卷积神经网络直到瓶颈层的过程可以看成是对图像进行特征提取的过程,瓶颈层输出再通过一个单层的全连接层神经网络可以很好的区分类别,所以有理由相信将瓶颈层的输出的节点向量可以被称为任何图像的更加精简且表达能力更强的特征向量。所以可以直接利用这个训练好的神经网络对图像进行特征提取,然后再将提取得到特征向量作为输入来训练一个新的单层全连接网络来处理分类问题。
但是在数据量足够的情况下,迁移学习的效果不如完全重新训练,但是迁移学习所需要的训练时间和训练样本要远远小于训练完整的模型。
比如把已经训练好的模型的某一层的输出拿出来,然后用一个svm、LR等分类,更好的去利用从某一层输出的特征(也叫知识),这也还是迁移学习的思想,如下前三个是transfer learning经常用到的方法。最后一个是finetune的思想。
把Alexnet里卷积层最后一层输出的特征拿出来,然后直接用SVM分类。这是Transfer Learning,因为你用到了Alexnet中已经学到了的“知识”。把Vggnet卷积层最后的输出拿出来,用贝叶斯分类器分类。思想基本同上。甚至你可以把Alexnet、Vggnet的输出拿出来进行组合,自己设计一个分类器分类。这个过程中你不仅用了Alexnet的“知识”,也用了Vggnet的“知识”。https://github.com/Gogul09/flower-recognition(此方法实现代码)最后,你也可以直接使用fine-tune这种方法,在Alexnet的基础上,重新加上全连接层,再去训练网络。
2:finetune(微调):例子:在Alexnet的基础上,我们重新加上一个层再去训练网络,比如再加入一个全连接层,那就是先
固定前面的层,让新加的fc层的loss值降低到一个很低的值,再调低学习率,放开所有层一块去训练这样可以收敛到一个不错的效果。
3:所以我个人认为迁移学习直接将现有的或者从现有的模型中提取出来的有用的东西应用的另一个领域,不在进行训练之前的网络部分,只需要训练我们添加部分网络的部分,将迁移过来的模型的某一层的输出作为我们新增加网络部分的输入。
而finetune就是微调,思想是:利用原有模型的参数信息,作为我们要训练的新的模型的初始化参数,这个新的模型可以和原来一样也可以增添几个层(进行适当的调整)。
4:传统的机器学习框架下,学习的任务是在给定充分训练数据集的基础上学习一个分类模型;然后利用这个学习到的模型来对测试文档进行分类和预测。然而,我们看到机器学习算法在当前web挖掘应用领域存在一个关键问题:一些新出现的领域中的大量训练数据非常难得到。web领域中大量新的数据不断涌现,从传统的新闻,网页,到图片,再到博客,播客等。传统的机器学习需要对每个领域都标定大量训练数据,这将会耗费大量的人力物力,而没有大量的标注数据,会使得很多与学习相关研究与应用无法开展,其次传统的机器学习假设训练数据与测试数据服从相同的数据分布。然而在很多情况下,这种相同分布不满足,通常可能发生的情况如训练数据过期。如果我们有大量的,在不同分布下的训练数据,完全丢弃这些数据也是非常浪费的,如何利用这些数据就是迁移学习主要解决的问题。
迁移学习可以从现有的数据中迁移知识,用来帮助将来的学习。
迁移学习的目标是将从一个环境中学到的知识用来帮助新环境中的学习任务,因此迁移学习不会想传统机器学习那样作同分布假设。
例子:一个会下象棋的人可以更容易的学会下围棋。
迁移学习目前分为一下三个部分:同构空间下基于实例的迁移学习;同构空间下基于特征的迁移学习;异构空间下的迁移学习。
基于实例的迁移学习有更强的知识迁移能力,基于特征的迁移学习具有更广规范的知识迁移能力;异构空间的迁移具有广泛的学习与扩展能力。
迁移学习即一种学习对另一种学习的影响,它广泛的存在于知识技能态度和行为的规范的学习中,任何一种学习都将受先验知识的影响,只要有学习就有迁移,迁移是学习的继续和巩固,优势提高和深化学习的条件,学习与迁移不可分割。
二:迁移学习实例
为了能够快速地训练好自己的花朵图片分类器,我们可以使用别人已经训练好的模型参数,在此基础之上训练我们的模型。这个便属于迁移学习。本文提供训练数据集和代码下载。
原理:卷积神经网络模型总体上可以分为两部分,前面的卷积层和后面的全连接层。卷积层的作用是图片特征的提取,全连接层作用是特征的分类。我们的思路便是在inception-v3网络模型上,修改全连接层,保留卷积层。卷积层的参数使用的是别人已经训练好的,全连接层的参数需要我们初始化并使用我们自己的数据来训练和学习。
上面inception-v3模型图红色箭头前面部分是卷积层,后面是全连接层。我们需要修改修改全连接层,同时把模型的最终输出改为5。
由于这里使用了tensorflow框架,所以,我们需要获取上图红色箭头所在位置的张量BOTTLENECK_TENSOR_NAME(最后一个卷积层激活函数的输出值,个数为2048)以及模型最开始的输入数据的张量JPEG_DATA_TENSOR_NAME。获取这两个张量的作用是,图片训练数据通过JPEG_DATA_TENSOR_NAME张量输入模型,通过BOTTLENECK_TENSOR_NAME张量获取通过卷积层之后的图片特征。
BOTTLENECK_TENSOR_SIZE = 2048
BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0'
JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0'
通过下面的代码加载模型,同时获取上面所述的两个张量。
最后便是定义交叉熵损失函数。模型使用反向传播训练,而训练的参数并不是模型的所有参数,仅仅是全连接层的参数,卷积层的参数是不变的。
定义交叉熵损失函数。
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=ground_truth_input)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(cross_entropy_mean)
那么接下来的是如何给我们的模型输入数据了,这里提供了几个操作数据的函数。由于训练数据集比较小,
先把所有的图片通过JPEG_DATA_TENSOR_NAME张量输入模型,然后获取BOTTLENECK_TENSOR_NAME张量的值并保存到硬盘中。
在模型训练的时候,从硬盘中读取所保存的BOTTLENECK_TENSOR_NAME张量的值作为全连接层的输入数据。因为一张图片可能会被使用多次。
运行代码在到时候再去看我的pycharm中的trasform这个项目。
这个代码实现部分参考的是https://blog.csdn.net/liangyihuai/article/details/79219457这个博客的内容
完整代码
# coding=utf8
import glob
import os.path
import random
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
BOTTLENECK_TENSOR_SIZE = 2048#最后一个卷积层激活函数输出值,个数是2048个1×1的
BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0'#张量获取通过卷积层之后的图片特征
JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0'#模型开始的输入数据的张量,张量输入
MODEL_DIR = './inception_dec_2015'#inception模型的闻之
MODEL_FILE= 'tensorflow_inception_graph.pb'#模型文件的准确位置
CACHE_DIR = './bottleneck'#最后一个卷积层输出的每一类的特征,这个要输入到fc中然后进行分类用的。
INPUT_DATA = './flower_photos'
VALIDATION_PERCENTAGE = 10#
TEST_PERCENTAGE = 10#验证集测试集都占10%
LEARNING_RATE = 0.01
STEPS = 4000
BATCH = 100
def create_image_lists(testing_percentage, validation_percentage):
result = {}
sub_dirs = [x[0] for x in os.walk(INPUT_DATA)]#os.walk()方法的作用是在目录树中游走输出在目录中的文件名,向上或者向下。
is_root_dir = True
for sub_dir in sub_dirs:
if is_root_dir:
is_root_dir = False
continue
extensions = ['jpg', 'jpeg', 'JPG', 'JPEG']
file_list = []
dir_name = os.path.basename(sub_dir)
for extension in extensions:
file_glob = os.path.join(INPUT_DATA, dir_name, '*.' + extension)
file_list.extend(glob.glob(file_glob))
if not file_list: continue
label_name = dir_name.lower()
#初始化
training_images = []
testing_images = []
validation_images = []
for file_name in file_list:
base_name = os.path.basename(file_name)#这个只是取回去文件名,去掉其路径
#basename的作用是去掉目录的路径,只返回文件名,而dirname用于 去掉文件名,只返回目录所在的路径。os.split()的作用是返回路径名和文件名的元组
# 随机划分数据
chance = np.random.randint(100)
if chance < validation_percentage:
validation_images.append(base_name)
elif chance < (testing_percentage + validation_percentage):
testing_images.append(base_name)
else:
training_images.append(base_name)
result[label_name] = {
'dir': dir_name,
'training': training_images,
'testing': testing_images,
'validation': validation_images,
}
return result
def get_image_path(image_lists, image_dir, label_name, index, category):
label_lists = image_lists[label_name]
category_list = label_lists[category]
mod_index = index % len(category_list)
base_name = category_list[mod_index]
sub_dir = label_lists['dir']
full_path = os.path.join(image_dir, sub_dir, base_name)
return full_path
def get_bottleneck_path(image_lists, label_name, index, category):
return get_image_path(image_lists, CACHE_DIR, label_name, index, category) + '.txt'
def run_bottleneck_on_image(sess, image_data, image_data_tensor, bottleneck_tensor):
bottleneck_values = sess.run(bottleneck_tensor, {image_data_tensor: image_data})
bottleneck_values = np.squeeze(bottleneck_values)
return bottleneck_values
def get_or_create_bottleneck(sess, image_lists, label_name, index, category, jpeg_data_tensor, bottleneck_tensor):
label_lists = image_lists[label_name]
sub_dir = label_lists['dir']
sub_dir_path = os.path.join(CACHE_DIR, sub_dir)
if not os.path.exists(sub_dir_path): os.makedirs(sub_dir_path)
bottleneck_path = get_bottleneck_path(image_lists, label_name, index, category)
if not os.path.exists(bottleneck_path):
image_path = get_image_path(image_lists, INPUT_DATA, label_name, index, category)
image_data = gfile.FastGFile(image_path, 'rb').read()
bottleneck_values = run_bottleneck_on_image(sess, image_data, jpeg_data_tensor, bottleneck_tensor)
bottleneck_string = ','.join(str(x) for x in bottleneck_values)
with open(bottleneck_path, 'w') as bottleneck_file:
bottleneck_file.write(bottleneck_string)
else:
with open(bottleneck_path, 'r') as bottleneck_file:
bottleneck_string = bottleneck_file.read()
bottleneck_values = [float(x) for x in bottleneck_string.split(',')]
return bottleneck_values
def get_random_cached_bottlenecks(sess, n_classes, image_lists, how_many, category, jpeg_data_tensor, bottleneck_tensor):
bottlenecks = []
ground_truths = []
for _ in range(how_many):
label_index = random.randrange(n_classes)
label_name = list(image_lists.keys())[label_index]
image_index = random.randrange(65536)
bottleneck = get_or_create_bottleneck(
sess, image_lists, label_name, image_index, category, jpeg_data_tensor, bottleneck_tensor)
ground_truth = np.zeros(n_classes, dtype=np.float32)
ground_truth[label_index] = 1.0
bottlenecks.append(bottleneck)
ground_truths.append(ground_truth)
return bottlenecks, ground_truths
def get_test_bottlenecks(sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor):
bottlenecks = []
ground_truths = []
label_name_list = list(image_lists.keys())
for label_index, label_name in enumerate(label_name_list):
category = 'testing'
for index, unused_base_name in enumerate(image_lists[label_name][category]):
bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, index, category,jpeg_data_tensor, bottleneck_tensor)
ground_truth = np.zeros(n_classes, dtype=np.float32)
ground_truth[label_index] = 1.0
bottlenecks.append(bottleneck)
ground_truths.append(ground_truth)
return bottlenecks, ground_truths
def main():
image_lists = create_image_lists(TEST_PERCENTAGE, VALIDATION_PERCENTAGE)
n_classes = len(image_lists.keys())
# 读取已经训练好的Inception-v3的模型
with gfile.FastGFile(os.path.join(MODEL_DIR, MODEL_FILE), 'rb') as f:
graph_def = tf.GraphDef()
graph_def.ParseFromString(f.read())
bottleneck_tensor, jpeg_data_tensor = tf.import_graph_def(
graph_def, return_elements=[BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME])
# 定义新的神经网络输入
bottleneck_input = tf.placeholder(tf.float32, [None, BOTTLENECK_TENSOR_SIZE], name='BottleneckInputPlaceholder')
ground_truth_input = tf.placeholder(tf.float32, [None, n_classes], name='GroundTruthInput')
# 定义一个权链接层
with tf.name_scope('final_training_ops'):
weights = tf.Variable(tf.truncated_normal([BOTTLENECK_TENSOR_SIZE, n_classes], stddev=0.001))
biases = tf.Variable(tf.zeros([n_classes]))
logits = tf.matmul(bottleneck_input, weights) + biases
final_tensor = tf.nn.softmax(logits)
# 定义交叉商损失函数
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(logits=logits, labels=ground_truth_input)
cross_entropy_mean = tf.reduce_mean(cross_entropy)
train_step = tf.train.GradientDescentOptimizer(LEARNING_RATE).minimize(cross_entropy_mean)
# 计算准确率
with tf.name_scope('evaluation'):
correct_prediction = tf.equal(tf.argmax(final_tensor, 1), tf.argmax(ground_truth_input, 1))
#tf.argmax()的用法,tf.argmax(final_tensor,1)返回的是final_tensor中,值最大的一个值,1在这里是指的返回一个值
#final_tensor返回的是概率的大小,返回的是概率的最大值作为最后的值
#tf.equal()的用法是比较tf.argmax(final_tensor, 1)和 tf.argmax(ground_truth_input, 1)对应位置的值是否相等,
#相等的时候返回true,否则返回false,然后统计true多占的比列就是最后的准确率。
evaluation_step = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
#tf.cast()的作用是改变数据类型的,改变correct_prediction的数据类型为float32
#tf.reduce_mean()的用法,在tensor的某一维度上,计算元素的平均值,由于输出的维度比原tensor降低了,所以也叫做降为。
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
# 训练过程
for i in range(STEPS):
train_bottlenecks, train_ground_truth = get_random_cached_bottlenecks(
sess, n_classes, image_lists, BATCH, 'training', jpeg_data_tensor, bottleneck_tensor)
sess.run(train_step,
feed_dict={bottleneck_input: train_bottlenecks, ground_truth_input: train_ground_truth})
#这里是train_bottlenecks,从磁盘读入的张量值作为输入向量,来训练全链接层,
if i % 100 == 0 or i + 1 == STEPS:
validation_bottlenecks, validation_ground_truth = get_random_cached_bottlenecks(
sess, n_classes, image_lists, BATCH, 'validation', jpeg_data_tensor, bottleneck_tensor)
validation_accuracy = sess.run(evaluation_step, feed_dict={
bottleneck_input: validation_bottlenecks, ground_truth_input: validation_ground_truth})
print('Step %d: Validation accuracy on random sampled %d examples = %.1f%%' %
(i, BATCH, validation_accuracy * 100))
# 在最后的测试数据上测试正确率
test_bottlenecks, test_ground_truth = get_test_bottlenecks(
sess, image_lists, n_classes, jpeg_data_tensor, bottleneck_tensor)
test_accuracy = sess.run(evaluation_step, feed_dict={
bottleneck_input: test_bottlenecks, ground_truth_input: test_ground_truth})
print('Final test accuracy = %.1f%%' % (test_accuracy * 100))
if __name__ == '__main__':#防止导入的包中的内容,也就是import 后面的内容也被运行
main()
#python是脚本语言,不像编译语言一样,先将程序编译成二进制再运行 ,而是动态的逐行解释运行,也就是从脚本的第一行开始运行,没有统一的入口。
#一个python源码除了可以直接运行外,还可以最为模块,也就是库导入,不管是导入还是运行,最顶层的代码都会被运行,python用缩进来区分代码层次,而
#实际上在导入的时候,有一部分代码我们是不希望被运行的。
#if __name__ =='main'就相当于程序的入口,python本身并没有规定这莫写,这只是一种编程习惯,由于模块之间相互引用,不同模块可能有这样的定义
#,而入口程序只能有一个,到底那一额入口程序被选中,这就取决于 __name__的值。
#__name__可以清晰的反映一个模块在包中的层次,其实,所谓模块的在包中的层次。__name__是内置变量,用于表示当前模块的名字,同时还能反映一个
#包的结构,如果模块直接运行的,则代码被运行,如果模块被导入的,则代码不能运行。
迁移学习和finetune的区别及迁移学习代码实现相关推荐
- 迁移学习笔记3: TCA, Finetune, 与Triplet Network(元学习)
主要想讲的内容有: TCA, Finetune, Triplet Network 迁移学习与元学习有哪几类方法 想讲的目标(但不一定完全能写完, 下一次笔记补充): 分别属于什么方法, 处于什么位置, ...
- 迁移学习与微调的区别
一.迁移学习: 1.从字面意义上理解是知识转移的学习方法,指一种学习方法:类比机器学习.深度学习等等概念: 2.把已训练好的模型参数迁移到新的模型来帮助新模型训练 二.微调: 1.从字面意义上理解是小 ...
- 迁移学习花式Finetune方法大汇总
如果觉得我的算法分享对你有帮助,欢迎关注我的微信公众号"圆圆的算法笔记",更多算法笔记和世间万物的学习记录- 迁移学习广泛地应用于NLP.CV等各种领域,通过在源域数据上学习知识, ...
- 净迁移人口预测程序python_高质量深度学习模型, 一键模型预测,迁移学习很简单...
飞桨(PaddlePaddle)核心框架Paddle Fluid v1.5已经发布,而作为其关键工具,用来迁移学习的PaddleHub也进行了全面更新,正式发布了1.0版本. 全新的PaddleHub ...
- 【迁移学习】隐私保护下的迁移算法
作者:李新春 -------- 计算机软件新技术国家重点实验室 伪文艺程序员 既可提刀立码,行遍天下 又可调参炼丹,卧于隆中 本文概要 本文介绍一种特殊场景下的迁移算法:隐私保护下的迁移算法.首先,本 ...
- 元学习、迁移学习、对比学习、自监督学习与少样本学习的关系解读
文章目录 前言 一.对比自监督学习与FSL 1.对比学习与自监督学习 2.自监督学习与FSL 二.元学习与FSL 1.元学习是什么 2.元学习与FSL 三.迁移学习与FSL 1.迁移学习 2.迁移学习 ...
- 【深度学习前沿应用】图像风格迁移
[深度学习前沿应用]图像风格迁移 活动地址:[CSDN21天学习挑战赛](https://marketing.csdn.net/p/bdabfb52c5d56532133df2adc1a728fd) ...
- 迁移学习:数据不足时如何深度学习
转自:http://www.sohu.com/a/125849954_470008 使用深度学习技术解决问题的过程中,最常见的障碍在于训练模型过程中所需的海量数据.需要如此多的数据,原因在于机器在学习 ...
- 人工智能、机器学习、深度学习和神经网络的区别
人工智能 背景:人工智能最初可以追溯至1956年,当时多名计算机科学家在达特茅斯会议上共同提出了人工智能的概念.在随后几十年中,人工智能一方面被认为是人类文明未来的发展方向,另一方面也被认为是难以企及 ...
最新文章
- 使用javascript模拟常见数据结构(二)
- mysql开启慢查询日志
- 数字孪生等前沿技术,将如何解码未来交通?
- 全球最大AI商业展会开幕,这家老牌巨头担当中国唯一代表
- 《Swift开发实战》——第16章,第16.2节下标脚本用法
- linux raid和mdadm,linux的raid和mdadm
- 文献学习(part78-A)--A Survey of Clustering Algorithms for Big Data: T axonomy Empirical Analysis
- Mac平台上的几款串口工具
- Unity 2017 Game Optimization 读书笔记 Dynamic Graphics (4)
- nginx 一个请求发给多台机器_配置Nginx实现负载均衡
- php阅读器开发,微信小程序阅读器的简单实例开发
- 八皇后问题的非递归解法
- 真不好意思,让你贱笑了
- MATLAB的最速下降法实现
- 分布式日志收集(ELK)
- 2022面试必刷461道大厂架构面试真题汇总+面经+简历模板
- Android 利用canvas画各种图形(点、直线、弧、圆、椭圆、文字、矩形、多边形、曲线、圆角矩形)
- tiled素材_瓦片地图 Tiled Map
- K-SVD: An Algorithm for Designing Overcomplete Dictionaries for Sparse Representation
- 英语学习单词篇(14)