Tensorflow学习笔记:CNN篇(9)——Finetuning,复用ImageNet的VGGNet进行图像识别
2018-02-14 17:35
567 查看
Tensorflow学习笔记:CNN篇(9)——Finetuning,复用在ImageNet已训练好的VGGNet进行图像识别
前序
— 到目前为止,对于模型的设计和训练,读者可能已经较为熟悉,如果读者已经能够使用设计出的模型进行训练并取得较好的结果,那么,恭喜你,你对Tensorflowd程序的编写已经可以说更上了一层台阶。— 但是在实际工程或者商业使用中,模型的训练并不都是由程序设计人员独立训练,而是通过复用已有的神经网络模型,导入已训练好的权重数据,从而实现图像的分解的目的。
— VGGNet是最常用的深度学习模型,在各种图片分类和更深一步的语义识别、图像分割上都有好的表现,因此作为最常用的深度学习基础模型被大量采用。
代码示例
Step 1: npz文件的读取
对于复用的VGG模型(在imagenet进行训练的模型),首先第一步是要获得相应的权重文件和对应的分类文件,读者可以在以下地址下载相应的文件。权重文件:http://www.cs.toronto.edu/~frossard/vgg16/vgg16_weights.npz
分类文件:http://www.cs.toronto.edu/~frossard/vgg16/imagenet_classes.py
对于下载下的vgg16_weights.npz文件的说明,需要了解npz文件格式是Numpy包中自带的一种专用的二进制文件存储格式,并且Numpy提供了很多种存取其内容的文件操作函数,可通过自带的load函数对其进行载入,之后将其作为字典赋值给vgg_dict变量,而对其读取可以使用类似字典的方式进行。
import numpy as np vgg_dict = np.load('./vgg16_weights.npz') print(vgg_dict.keys()) print(vgg_dict["conv1_1_W"])
Step 2: 复用的VGGNet模型定义
对于复用模型来说,最关键的一步就是复用其中已训练好的权重参数,而通过load方式,可以将其中所包含的数据以字典的形式读出,之后根据参数的不同予以载入。1. 第一步:定义VGGNet的复用类
首先是对于类的定义,前面已经说过,如果想要复用已经训练完毕的权重参数,则需要在模型中将其作为参数输入。而类中输入参数的方式就是将参数在整个类中共享,故在类的初始化中加入一个全局列表,将所需要共享的参数加载至类中。代码如下:
def __init__(self, imgs): self.parameters = [] ##关键语句 self.imgs = imgs self.convlayers() self.fc_layers() self.probs = tf.nn.softmax(self.fc8)
init中定义的参数与自训练的类中的相同,但是多设置了一个parameter列表,其作用就是将各个层产生的数据以列表元素的方式加载至其中。
def conv(self,name, input_data, out_channel): in_channel = input_data.get_shape()[-1] with tf.variable_scope(name): kernel = tf.get_variable("weights", [3, 3, in_channel, out_channel], dtype=tf.float32) biases = tf.get_variable("biases", [out_channel], dtype=tf.float32) conv_res = tf.nn.conv2d(input_data, kernel, [1, 1, 1, 1], padding="SAME") res = tf.nn.bias_add(conv_res, biases) out = tf.nn.relu(res, name=name) self.parameters += [kernel, biases] ##关键语句 return out
同样在卷积层方法定义时,在所有参数定义后,需要一个将参数加载到对应的列表中的方法,即使用self.parameters += [kernel, biases]将定义的卷积核和偏置值输入值参数列表中,对于全连接层的定义也一样。
def fc(self,name,input_data,out_channel): shape = input_data.get_shape().as_list() if len(shape) == 4: size = shape[-1] * shape[-2] * shape[-3] else:size = shape[1] input_data_flat = tf.reshape(input_data,[-1,size]) with tf.variable_scope(name): weights = tf.get_variable(name="weights",shape=[size,out_channel],dtype=tf.float32) biases = tf.get_variable(name="biases",shape=[out_channel],dtype=tf.float32) res = tf.matmul(input_data_flat,weights) out = tf.nn.relu(tf.nn.bias_add(res,biases)) self.parameters += [weights, biases] ##关键语句 return out
而池化层和saver方法对于类的定义没有影响,因此使用之前定义即可,即:
def saver(self): return tf.train.Saver() def maxpool(self,name,input_data): out = tf.nn.max_pool(input_data,[1,2,2,1],[1,2,2,1],padding="SAME",name=name) return out
最后一个非常重要的方法就是将获取的权重参数重载入VGGNet模型中,代码如下:
def load_weights(self, weight_file, sess): weights = np.load(weight_file) keys = sorted(weights.keys()) for i, k in enumerat 4000 e(keys): sess.run(self.parameters[i].assign(weights[k])) print("-----------all done---------------")
这里首先使用np.load方法载入权重文件,之后对获得的字典值进行排序,之后使用一个enumerate方法将数据迭代出,这里迭代的结果是序号以及key值,之后执行一个赋值操作将对应的权重值赋值到参数列表中。
完整代码
保存为VGG16_model.py文件
import numpy as np
import tensorflow as tf
import global_variable
class vgg16:
def __init__(self, imgs):
self.parameters = []
self.imgs = imgs
self.convlayers()
self.fc_layers()
self.probs = tf.nn.softmax(self.fc8)
def saver(self): return tf.train.Saver() def maxpool(self,name,input_data): out = tf.nn.max_pool(input_data,[1,2,2,1],[1,2,2,1],padding="SAME",name=name) return out
def conv(self,name, input_data, out_channel):
in_channel = input_data.get_shape()[-1]
with tf.variable_scope(name):
kernel = tf.get_variable("weights", [3, 3, in_channel, out_channel], dtype=tf.float32)
biases = tf.get_variable("biases", [out_channel], dtype=tf.float32)
conv_res = tf.nn.conv2d(input_data, kernel, [1, 1, 1, 1], padding="SAME")
res = tf.nn.bias_add(conv_res, biases)
out = tf.nn.relu(res, name=name)
self.parameters += [kernel, biases]
return out
def fc(self,name,input_data,out_channel):
shape = input_data.get_shape().as_list()
if len(shape) == 4:
size = shape[-1] * shape[-2] * shape[-3]
else:size = shape[1]
input_data_flat = tf.reshape(input_data,[-1,size])
with tf.variable_scope(name):
weights = tf.get_variable(name="weights",shape=[size,out_channel],dtype=tf.float32)
biases = tf.get_variable(name="biases",shape=[out_channel],dtype=tf.float32)
res = tf.matmul(input_data_flat,weights)
out = tf.nn.relu(tf.nn.bias_add(res,biases))
self.parameters += [weights, biases]
return out
def convlayers(self):
# zero-mean input
#conv1
self.conv1_1 = self.conv("conv1re_1",self.imgs,64)
self.conv1_2 = self.conv("conv1_2",self.conv1_1,64)
self.pool1 = self.maxpool("poolre1",self.conv1_2)
#conv2
self.conv2_1 = self.conv("conv2_1",self.pool1,128)
self.conv2_2 = self.conv("convwe2_2",self.conv2_1,128)
self.pool2 = self.maxpool("pool2",self.conv2_2)
#conv3
self.conv3_1 = self.conv("conv3_1",self.pool2,256)
self.conv3_2 = self.conv("convrwe3_2",self.conv3_1,256)
self.conv3_3 = self.conv("convrew3_3",self.conv3_2,256)
self.pool3 = self.maxpool("poolre3",self.conv3_3)
#conv4
self.conv4_1 = self.conv("conv4_1",self.pool3,512)
self.conv4_2 = self.conv("convrwe4_2",self.conv4_1,512)
self.conv4_3 = self.conv("conv4rwe_3",self.conv4_2,512)
self.pool4 = self.maxpool("pool4",self.conv4_3)
#conv5
self.conv5_1 = self.conv("conv5_1",self.pool4,512)
self.conv5_2 = self.conv("convrwe5_2",self.conv5_1,512)
self.conv5_3 = self.conv("conv5_3",self.conv5_2,512)
self.pool5 = self.maxpool("poorwel5",self.conv5_3)
def fc_layers(self):
self.fc6 = self.fc("fc1", self.pool5, 4096)
self.fc7 = self.fc("fc2", self.fc6, 4096)
self.fc8 = self.fc("fc3", self.fc7, 1000)
def load_weights(self, weight_file, sess):
weights = np.load(weight_file)
keys = sorted(weights.keys())
for i, k in enumerate(keys):
sess.run(self.parameters[i].assign(weights[k]))
print("-----------all done---------------")
程序中各个层次的定义和模型中设计相同,self.imgs是输入数据,而self.fc8是结果的最终输出值。
2. 第二步:定义模型的使用和权重的载入
对于模型的使用,可以直接通过实现类中所定义的方法进行。由于无需对模型进行重新定义,因此可以通过对数据的输入而直接获得结果。test.py文件,代码如下:
import numpy as np import tensorflow as tf from scipy.misc import imread, imresize import VGG16_model as model from imagenet_classes import class_names if __name__ == '__main__': imgs = tf.placeholder(tf.float32, [None, 224, 224, 3]) vgg = model.vgg16(imgs) prob = vgg.probs sess = tf.Session() vgg.load_weights("./vgg16_weights.npz", sess) img1 = imread('001.jpg', mode='RGB') img1 = imresize(img1, (224, 224)) prob = sess.run(vgg.probs, feed_dict={vgg.imgs: [img1]})[0] preds = (np.argsort(prob)[::-1])[0:5] for p in preds: print(class_names[p], prob[p])
首先获得了VGGNet模型的初始化,在初始化过程中就将需要分辨的图载入,之后获取模型的预测值,最后是对分类结果的打印,计算结果生成一系列key值和概率的对应表,笔者取前5个最大可信的概率。
这是一张波斯猫的图片,将其输入到模型中,其最终验证结果如下:
可以看到图中的波斯猫的概率被确认为0.999703,因此可以确定VGGNet在此的复用是有效的。
Step 3: 保存复用的VGGNet模型为Tensorflow格式(可选操作)
如果对使用后的VGGNet模型进行保存,则需要获得相应的Saver类。这里读者可能已经注意到,在设计VGGNet类的时候,已经在其中定义了Saver类,因此可以在其中直接载入数据后对模型进行保存即可。import tensorflow as tf import VGG16_model as model import global_variable if __name__ == '__main__': imgs = tf.placeholder(tf.float32, [None, 224, 224, 3]) vgg = model.vgg16(imgs) prob = vgg.probs saver = vgg.saver() sess = tf.Session() vgg.load_weights("./vgg16_weights.npz", sess) saver.save(sess, global_variable.save_path)
而对于复用已经保存好的Tensorflow格式,则可以使用重新定义类之后在其中对整个模型图进行载入的方法进行,其代码如下:
import numpy as np import tensorflow as tf import global_variable import VGG16_model as model from imagenet_classes import class_names from scipy.misc import imread, imresize imgs = tf.placeholder(tf.float32, [None, 224, 224, 3]) vgg = model.vgg16(imgs) saver = vgg.saver() sess = tf.Session() saver.restore(sess, global_variable.save_path) img1 = imread('001.jpg', mode='RGB') img1 = imresize(img1, (224, 224)) prob = sess.run(vgg.probs, feed_dict={vgg.imgs: [img1]})[0] preds = (np.argsort(prob)[::-1])[0:5] for p in preds: print(class_names[p], prob[p])
具体用法与之前的用法一样,这里不再赘述。
相关文章推荐
- Tensorflow学习笔记:CNN篇(10)——Finetuning,猫狗大战,VGGNet的重新针对训练
- Tensorflow学习笔记:CNN篇(7)——Finetuning,模型的保存与恢复
- Tensorflow学习笔记:CNN篇(8)——Finetuning,模型更为细化的保存与恢复
- TensorFlow学习--VGGNet实现&图像识别
- tensorflow 学习笔记(十二)- 用别人训练好的模型来进行图像分类
- 【TensorFlow 代码笔记】 加载预先训练网络进行图像识别(label_image)
- Matlab图像处理学习笔记(九):获取叠加物体的数量并进行分割
- TensorFlow学习记录-- 5.用lstm对手写数字进行识别(待修改,差增加rnn以及lstm的知识)
- Tensorflow学习笔记--使用迁移学习做自己的图像分类器(Inception v3)
- 图像跟踪与识别-TLD学习笔记,TLD跟踪算法详解
- OpenCV学习笔记---- 挨个读取图像文件夹下的所有文件,进行样本训练
- tensorflow 学习笔记12 循环神经网络RNN LSTM结构实现MNIST手写识别
- 深度学习在图像识别中的应用--学习笔记2
- OpenCV学习笔记2:使用opencv进行图像比较
- Andrew Ng机器学习课程笔记--week11(图像识别&总结划重点)
- SSD图像识别代码学习笔记
- tensorflow 学习笔记7 普通神经网络实现mnist手写识别
- opencv学习笔记第五章 使用形态学滤波对图像进行开闭运算
- 深度学习在图像识别中的应用--学习笔记4
- 图像识别学习笔记 - 最后一部分非原创