迁移学习1——基于深度学习和迁移学习的识花实践

参考博客:https://cosx.org/2017/10/transfer-learning/


下面的例子中将示范如何将一个图像识别的深度卷积网络,VGG,迁移到识别花朵类型的新任务上,在原先的任务中,VGG 只能识别花,但是迁移学习可以让模型不但能识别花,还能识别花的具体品种。

VGG 介绍

VGG 的两个版本分别是 16 层网络版和 19 层网络版。VGG 的输入数据格式是 224 * 224 * 3 的像素数据,经过一系列的卷积神经网络和池化网络处理之后,输出的是一个 4096 维的特征数据,然后再通过 3 层全连接的神经网络处理,最终由 softmax 规范化得到分类结果。VGG模型是一个. npy 文件,本质上是一个巨大的 numpy 对象,包含了 VGG16 模型中的所有参数,该文件大约有 500M,所以可见如果是从头训练这样一个模型是非常耗时的,借助于迁移学习的思想,我们可以直接在这个模型的基础上进行训练。

在接下来的迁移学习实践中,我们会采用稍微简单的一些的 VGG16,他和 VGG19 有几乎完全一样的准确度,但是运算起来更快一些。

 

VGG 的结构图如下:

搭建属于自己的识花网络

整体思路:

首先我们会将所有的图片交给 VGG16,利用 VGG16 的深度网络结构中的五轮卷积网络层和池化层,对每张图片得到一个 4096 维的特征向量,然后我们直接用这个特征向量替代原来的图片,再加若干层全连接的神经网络,对花朵数据集进行训练。因此本质上,我们是将 VGG16 作为一个图片特征提取器,然后在此基础上再进行一次普通的神经网络学习,这样就将原先的 224 * 224 * 3 维度的数据转化为了 4096 维的,而每一维度的信息量大大提高,从而大大降低了计算资源的消耗,实现了把学习物体识别中得到的知识应用到特殊的花朵分类问题上。

代码存放位置:/home/wei/MyPaperCode/recognizeFlowersBaseOnDeeplearningAndTransferLearning

#导入需要用的 python 模块
import os
import numpy as np
import tensorflow as tf

#从文件夹中导入文件
from tensorflow_vgg import vgg16
from tensorflow_vgg import utils


#加载识花数据集。接下来我们将 flower_photos 文件夹中的花朵图片都载入到进来,并且用图片所在的子文件夹作为标签值。

data_dir = 'flower_photos/'
#返回文件夹中,子文件及子文件夹 名称的列表,contents为['tulips', 'dandelion', 'LICENSE.txt', 'roses', 'sunflowers', 'daisy']
contents = os.listdir(data_dir)
#去掉子文件的名称,只保留子文件夹的名称
classes = [each for each in contents if os.path.isdir(data_dir + each)]
#classes即为['tulips', 'dandelion', 'roses', 'sunflowers', 'daisy']
#利用 VGG16 计算得到特征值

# 首先设置计算batch的值,如果运算平台的内存越大,这个值可以设置得越高
batch_size = 10
# 用codes_list来存储特征值
codes_list = []
# 用labels来存储花的类别
labels = []
# batch数组用来临时存储图片数据
batch = []

codes = None

with tf.Session() as sess:
    # 构建VGG16模型对象
    vgg = vgg16.Vgg16()  #调用vgg16文件中的Vgg16()
    input_ = tf.placeholder(tf.float32, [None, 224, 224, 3])
    with tf.name_scope("content_vgg"):
        # 载入VGG16模型
        vgg.build(input_)
    
    # 对每个不同种类的花分别用VGG16计算特征值
    for each in classes:
        print("Starting {} images".format(each))
        class_path = data_dir + each
        files = os.listdir(class_path)
        for ii, file in enumerate(files, 1):
            # 载入图片并放入batch数组中
            img = utils.load_image(os.path.join(class_path, file)) #此时224*224*3
            batch.append(img.reshape((1, 224, 224, 3))) #添加到batch数组中
            labels.append(each)
            
            # 如果图片数量到了batch_size则开始具体的运算
            if ii % batch_size == 0 or ii == len(files):
                images = np.concatenate(batch)#沿现有轴加入数组序列

                feed_dict = {input_: images}
                # 计算特征值
                codes_batch = sess.run(vgg.relu6, feed_dict=feed_dict)
                
                # 将结果放入到codes数组中
                if codes is None:
                    codes = codes_batch
                else:
                    codes = np.concatenate((codes, codes_batch))
                
                # 清空数组准备下一个batch的计算
                batch = []
                print('{} images processed'.format(ii))
                
#以上我们就可以得到一个 codes 数组,和一个 labels 数组,分别存储了所有花朵的特征值和类别


#用如下的代码将这两个数组保存到硬盘上
with open('codes', 'w') as f:
    codes.tofile(f)
    
import csv
with open('labels', 'w') as f:
    writer = csv.writer(f, delimiter='\n')
    writer.writerow(labels)

以上代码讲解:

enumerate()枚举

myfiles = ['A', 'B', 'C']
for nn in enumerate(myfiles, 1):  #1表示序号从1开始
    print(nn)

for mm, myfile in enumerate(myfiles, 5):  #5表示序号从5开始
    print(mm)
    print(myfile)

输出为:

(1, 'A')
(2, 'B')
(3, 'C')
5
A
6
B
7
C    

os.path.join()函数

语法:  os.path.join(path1[,path2[,......]])

返回值:将多个路径组合后返回

注:第一个绝对路径之前的参数将被忽略

os.path.join(class_path, file)表示的是路径,比如class_path = flower_photos/tulips, file = 111.jpg时其表示为flower_photos/tulips/111.jpg

 

print('{} images processed'.format(ii))

往字符串'{} images processed'中传入数据,ii为50时输出为 50 images processed


#准备训练集,验证集和测试集
#首先把 labels 数组中的分类标签用 One Hot Encode 的方式替换。

from sklearn.preprocessing import LabelBinarizer

lb = LabelBinarizer()
lb.fit(labels)

labels_vecs = lb.transform(labels)

labels为       ['tulips', 'tulips', 'tulips',......., 'daisy', 'daisy']

labels_vecs为

[[0 0 0 0 1]
 [0 0 0 0 1]
 [0 0 0 0 1]
 ...
 [1 0 0 0 0]
 [1 0 0 0 0]
 [1 0 0 0 0]]
# 接下来就是抽取数据,因为不同类型的花的数据数量并不是完全一样的,而且 labels 数组中的数据也还没有被打乱,所
# 以最合适的方法是使用 StratifiedShuffleSplit 方法来进行分层随机划分。假设我们使用训练集:验证集:测试集 
# = 8:1:1,那么代码如下:


from sklearn.model_selection import StratifiedShuffleSplit

ss = StratifiedShuffleSplit(n_splits=1, test_size=0.2)

train_idx, val_idx = next(ss.split(codes, labels))

half_val_len = int(len(val_idx)/2)
val_idx, test_idx = val_idx[:half_val_len], val_idx[half_val_len:]

train_x, train_y = codes[train_idx], labels_vecs[train_idx]
val_x, val_y = codes[val_idx], labels_vecs[val_idx]
test_x, test_y = codes[test_idx], labels_vecs[test_idx]

print("Train shapes (x, y):", train_x.shape, train_y.shape)#x表示特征图,y表示labels
print("Validation shapes (x, y):", val_x.shape, val_y.shape)
print("Test shapes (x, y):", test_x.shape, test_y.shape)

输出为:

('Train shapes (x, y):', (2936, 4096), (2936, 5))
('Validation shapes (x, y):', (367, 4096), (367, 5))
('Test shapes (x, y):', (367, 4096), (367, 5))

next()函数 返回迭代器的下一个项目。

# 分好了数据集之后,就可以开始对数据集进行训练了,假设我们使用一个 256 维的全连接层,一个 5 维的全连接层
#(因为我们要分类五种不同类的花朵),和一个 softmax 层。当然,这里的网络结构可以任意修改,你可以不断尝试
# 其他的结构以找到合适的结构。

# 输入数据的维度
inputs_ = tf.placeholder(tf.float32, shape=[None, codes.shape[1]])
# 标签数据的维度
labels_ = tf.placeholder(tf.int64, shape=[None, labels_vecs.shape[1]])

# 加入一个256维的全连接的层
fc = tf.contrib.layers.fully_connected(inputs_, 256)

# 加入一个5维的全连接层
logits = tf.contrib.layers.fully_connected(fc, labels_vecs.shape[1], activation_fn=None)

# 计算cross entropy值
cross_entropy = tf.nn.softmax_cross_entropy_with_logits(labels=labels_, logits=logits)

# 计算损失函数
cost = tf.reduce_mean(cross_entropy)

# 采用用得最广泛的AdamOptimizer优化器
optimizer = tf.train.AdamOptimizer().minimize(cost)

# 得到最后的预测分布
predicted = tf.nn.softmax(logits)

# 计算准确度
correct_pred = tf.equal(tf.argmax(predicted, 1), tf.argmax(labels_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
print(shape(l))#输出l的行列值
print(l.shape[0])#输出l的行数值
print(l.shape[1])#输出l的列数值
tf.contrib.layers.fully_connected(inputs, num_outputs, activation_fn=None)这个函数就是全链接成层,inputs是输入,num_outputs是下一层单元的个数,activation_fn是激活函数
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值