基于TensorFlow学习VGG-net

论文地址

VGG net 用来实现图片识别。固定了网络的其他参数,通过增加卷积层来增加网络深度,所有层都采用小的3*3的卷积核

架构:

训练输入:为固定尺寸,224224 RGB图像。
预处理:像素减去训练集的RGB均值
卷积核:3
3,步长为1,padding。
空间池化:一系列卷积后,用最大池化,2*2,步长为2.
全连接层:提取特征后,接三个全连接层,前两个4096通道,第三个1000通道,最后softmax层,输出概率。
每个隐藏层都使用ReLu.

表格中每列代表不同网络,只是深度不同。
卷积通道数量很小,第一层仅64通道,经过最大池化通道数翻倍,最后达到512通道。
表格2展示每种模型参数数量。因为参数量主要集中在全连接层,尽管网络加深,权重也没大幅增加。

分类框架

训练:
采用mini-batch梯度下降法(小批量梯度下降),batch size=256

采用动量优化算法(模拟惯性,更新时候在一定程度上保留之前更新方向,同时利用当前batch的梯度微调最终更新方向,增加稳定性,拜托局部最优),momentum=0.9;

采用L2正则化,惩罚系数0.00005,;doupout比率为0.5

初始学习率为0.001,后期衰减为原来的0.01,再0.1,再1

迭代次数370K(74epochs)(当一个完整的数据集通过了神经网络一次并且返回了一次,这个过程称为一个 epoch);

数据增强采:用随机裁剪,水平翻转,RGB颜色变化;

设置图片大小的方法:定义S代表经过各向同性缩放的训练图像最小边(?)
第一种方法,针对单尺寸图像训练,S=256或384,把输入图片随机裁剪224*225大小图片,原则上S可取任意不小于224的值。
第二种方法是多尺度训练。每张图从【S min,S max】中随机选取S进行尺寸缩放,由于目标尺寸不定,这种方法有效。

测试:(全卷积,不裁剪)
对于已经训练好的卷积网络的一张输入图片,用下面的方法分类:
first,图像的最小边被各向同性的缩放到预定尺寸Q;
second,将原来的全连接层改为卷积层,在未被裁剪的的全图像上运用卷积网络,输出是一个与输入尺寸相关的分类得分图,输出通道与类别数相同。
last,对分类得分图进行空间平均化,得到固定尺寸的分类得分向量。
数据增强:水平翻转图片,最终取原始图片和翻转图片的softmax分类概率平均值作为最终得分。

最近常用tensorflow函数总结:

x = tf.placeholder(tf.float32,shape=[1,224,224,3])
占位,BATCH_SIZE为1,图片大小为224*224像素,3通道彩图。
sess.run(求分类评估值的节点,feed_dict{x: })


np.load(“名.npy”).item()
np.save(“名.npy”,某数组)
将数组按照二进制的形式进行读入,写出。扩展名为:npy。
其中,item()表示遍历每个元素。


tf.shape(a)返回a的维度

tf.split(切谁,怎么切,哪个维度)
tf.concat(整合谁,哪个维度)

更多:https://tensorflow.google.cn/
使用vgg16实现图片识别:
图1
展示1
图2
展示2
图3
展示3

代码解析:

vgg16.py:还原网络和参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/python
#coding:utf-8

import inspect #检查运行信息的模块
import os
import numpy as np
import tensorflow as tf
import time
import matplotlib.pyplot as plt

VGG_MEAN = [103.939, 116.779, 123.68] #样本rgb平均值

class Vgg16():
def __init__(self, vgg16_path=None):
if vgg16_path is None:
vgg16_path = os.path.join(os.getcwd(), "vgg16.npy")#加入路径 ,os.getcwd()用于返回当前工作目录
self.data_dict = np.load(vgg16_path, encoding='latin1').item()#遍历,模型参数读入字典

def forward(self, images):

print("build model started")
start_time = time.time() #获取前项传播的开始时间
rgb_scaled = images * 255.0 #逐像素乘上255.0
#从GRB转换色道,到BGR
red, green, blue = tf.split(rgb_scaled,3,3)
bgr = tf.concat([
blue - VGG_MEAN[0],
green - VGG_MEAN[1],
red - VGG_MEAN[2]],3)
#逐像素减去像素平均值,可以移除平均亮度,常用于灰度图像上

#接下来构建VGG的16层网络(包括5段卷积,3层全连接)并逐层根据网络空间读取网络参数
#第一段卷积,有两个卷积层,加最大池化,用来缩小图片尺寸

self.conv1_1 = self.conv_layer(bgr, "conv1_1")
#传入name,获取卷积核和偏置,并卷积运算,经过激活函数后返回。

self.conv1_2 = self.conv_layer(self.conv1_1, "conv1_2")

#池化
self.pool1 = self.max_pool_2x2(self.conv1_2, "pool1")

#第二段卷积,和第一段相同
self.conv2_1 = self.conv_layer(self.pool1, "conv2_1")
self.conv2_2 = self.conv_layer(self.conv2_1, "conv2_2")
self.pool2 = self.max_pool_2x2(self.conv2_2, "pool2")

#第三段卷积,三个卷积层,一个最大池化

self.conv3_1 = self.conv_layer(self.pool2, "conv3_1")
self.conv3_2 = self.conv_layer(self.conv3_1, "conv3_2")
self.conv3_3 = self.conv_layer(self.conv3_2, "conv3_3")
self.pool3 = self.max_pool_2x2(self.conv3_3, "pool3")

#第四段卷积,三个卷积层,一个最大池化
self.conv4_1 = self.conv_layer(self.pool3, "conv4_1")
self.conv4_2 = self.conv_layer(self.conv4_1, "conv4_2")
self.conv4_3 = self.conv_layer(self.conv4_2, "conv4_3")
self.pool4 = self.max_pool_2x2(self.conv4_3, "pool4")
#第五段卷积,三个卷积层,一个最大池化
self.conv5_1 = self.conv_layer(self.pool4, "conv5_1")
self.conv5_2 = self.conv_layer(self.conv5_1, "conv5_2")
self.conv5_3 = self.conv_layer(self.conv5_2, "conv5_3")
self.pool5 = self.max_pool_2x2(self.conv5_3, "pool5")
#第六段全连接层
self.fc6 = self.fc_layer(self.pool5, "fc6")#根据命名空间fc6,做加权求和
self.relu6 = tf.nn.relu(self.fc6) #激活
#第七段全连接层
self.fc7 = self.fc_layer(self.relu6, "fc7")
self.relu7 = tf.nn.relu(self.fc7)
#第八层全连接
self.fc8 = self.fc_layer(self.relu7, "fc8")
self.prob = tf.nn.softmax(self.fc8, name="prob")#softmax分类,得到属于各个类别的概率。

end_time = time.time() #获取结束时间
print(("time consuming: %f" % (end_time-start_time)))#耗时

self.data_dict = None #清空本次读到地模型字典



#卷积计算的相关定义,
def conv_layer(self, x, name):
with tf.variable_scope(name):#根据命名空间找到网络参数。
w = self.get_conv_filter(name) #读取卷积核
conv = tf.nn.conv2d(x, w, [1, 1, 1, 1], padding='SAME')卷积计算
conv_biases = self.get_bias(name) #读取偏置
result = tf.nn.relu(tf.nn.bias_add(conv, conv_biases)) #加上偏置,并激活
return result

def get_conv_filter(self, name):#其中获取卷积核的定义
return tf.constant(self.data_dict[name][0], name="filter")

def get_bias(self, name):#其中获取偏置的定义
return tf.constant(self.data_dict[name][1], name="biases")

def max_pool_2x2(self, x, name):#其中最大池的定义
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

#定义全连接层的前项传播计算
def fc_layer(self, x, name):
with tf.variable_scope(name):#根据命名空间做计算
shape = x.get_shape().as_list() #该层维度信息
dim = 1
for i in shape[1:]:
dim *= i #每层的维度相乘
x = tf.reshape(x, [-1, dim])#改变特征图形状,多维度特征的拉伸操作,只第六层用
w = self.get_fc_weight(name) #读取w
b = self.get_bias(name) #读取b

result = tf.nn.bias_add(tf.matmul(x, w), b)#加权求和加偏置
return result
#定义获取权重
def get_fc_weight(self, name):
return tf.constant(self.data_dict[name][0], name="weights")
utils:辅助函数,包括读取输入图片,计算百分比形势的概率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python
#coding:utf-8
from skimage import io, transform
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from pylab import mpl

mpl.rcParams['font.sans-serif']=['SimHei'] # 正常显示中文标签
mpl.rcParams['axes.unicode_minus']=False # 正常显示正负号

def load_image(path):
fig = plt.figure("Centre and Resize")
img = io.imread(path) #读取
img = img / 255.0 #归一化

ax0 = fig.add_subplot(131)
ax0.set_xlabel(u'Original Picture')
ax0.imshow(img)

short_edge = min(img.shape[:2])
y = (img.shape[0] - short_edge) / 2
x = (img.shape[1] - short_edge) / 2
crop_img = img[y:y+short_edge, x:x+short_edge]

ax1 = fig.add_subplot(132)
ax1.set_xlabel(u"Centre Picture")
ax1.imshow(crop_img)

re_img = transform.resize(crop_img, (224, 224))

ax2 = fig.add_subplot(133)
ax2.set_xlabel(u"Resize Picture")
ax2.imshow(re_img)

img_ready = re_img.reshape((1, 224, 224, 3))

return img_ready

def percent(value):
return '%.2f%%' % (value * 100)
app.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#coding:utf-8
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import vgg16
import utils
from Nclasses import labels

img_path = raw_input('Input the path and image name:')
img_ready = utils.load_image(img_path) #调用并预处理图片

fig=plt.figure(u"Top-5 预测结果")

with tf.Session() as sess:
images = tf.placeholder(tf.float32, [1, 224, 224, 3])#占位,类型维度
vgg = vgg16.Vgg16()
vgg.forward(images)
probability = sess.run(vgg.prob, feed_dict={images:img_ready})
top5 = np.argsort(probability[0])[-1:-6:-1]
print "top5:",top5
values = []
bar_label = []
for n, i in enumerate(top5):
print "n:",n
print "i:",i
values.append(probability[0][i])
bar_label.append(labels[i])
print i, ":", labels[i], "----", utils.percent(probability[0][i])

ax = fig.add_subplot(111)
ax.bar(range(len(values)), values, tick_label=bar_label, width=0.5, fc='g')
ax.set_ylabel(u'probabilityit')
ax.set_title(u'Top-5')
for a,b in zip(range(len(values)), values):
ax.text(a, b+0.0005, utils.percent(b), ha='center', va = 'bottom', fontsize=7)
plt.show()

还有两个文件:
1:vgg16.npy:保存了网络参数
2:Nclasses.py:保存了编号和物体名称的字典

文章目录
  1. 1. 架构:
  2. 2. 分类框架
    1. 2.1. 最近常用tensorflow函数总结:
    2. 2.2. vgg16.py:还原网络和参数
    3. 2.3. utils:辅助函数,包括读取输入图片,计算百分比形势的概率
    4. 2.4. app.py:
,