您的位置:首页 > 理论基础 > 计算机网络

使用TensorFlow进行特征提取(中层特征)以及mat文件转为tfrecord方法

2017-12-14 20:57 771 查看
在学习TensorFlow这个Google公司的神器一年之后,因为课题的需求,需要提取卷积神经网络的某一层的特征,也就是我们所说的中层特征,似乎中文网站没有关于这方面的介绍,在经过摸索之后,终于搞清楚了流程,给大家分享一下。

我的GitHub地址是https://github.com/zhuzhuxia1994/midfeature-tensorflow,具体的代码陆续都会放到里面,大家如果方便的话可以点个星,嘿嘿额




首先我默认读者已经具有一定的TensorFlow的基础,因为没有基础的同学应该也不会上来就需要提取中层特征。如果有同学对使用TensorFlow训练自己的数据库还不熟悉的话,可以在下面留言,我会考虑写一下。

众所周知,卷积神经网络中的卷积层的目的其实就是特征提取,当卷积层堆叠的越多,获得的特征就越抽象。

下面先贴上一段卷积神经网络VGG16的代码

def conv_op(input_op, name, kh, kw, n_out, dh, dw, p):
Args:
input_op:输入的tensor
name:这一层的名称
kh:kernel height即卷积核的高
kw:kernel weight即卷积核的宽
n_out:卷积核数量即输出通道数
dh:步长的高
dw:步长的宽
p:参数列表
'''
n_in = input_op.get_shape()[-1].value # 获取input_op的通道数

with tf.name_scope(name) as scope: # 设置scope,生成的Variable使用默认的命名
kernel = tf.get_variable(scope+"w", # kernel(即卷积核参数)使用tf.get_variable创建
shape=[kh, kw, n_in, n_out], # 【卷积核的高,卷积核的宽、输入通道数,输出通道数】
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer_conv2d()) # 参数初始化
# 使用tf.nn.conv2d对input_op进行卷积处理,卷积核kernel,步长dh*dw,padding模式为SAME
conv = tf.nn.conv2d(input_op, kernel, (1, dh, dw, 1), padding='SAME')
bias_init_val = tf.constant(0.0, shape=[n_out], dtype=tf.float32) # biases使用tf.constant赋值为0
biases = tf.Variable(bias_init_val, trainable=True, name='b') # 将bias_init_val转成可训练的参数
z = tf.nn.bias_add(conv, biases) # 将卷积结果conv和bias相加
activation = tf.nn.relu(z, name=scope) # 对z进行非线性处理得到activation
p += [kernel, biases] # 创建卷积层时用到的参数kernel和bias添加进参数列表
return activation # 将卷积层的输出activation作为函数结果返回

# 定义全连接层的创建函数
def fc_op(input_op, name, n_out, p):
n_in = input_op.get_shape()[-1].value # 获取tensor的通道数

with tf.name_scope(name) as scope:
kernel = tf.get_variable(scope+"w", # 使用tf.get_variable创建全连接层的参数
shape=[n_in, n_out], # 参数的维度有两个,输入通道数和输出通道数
dtype=tf.float32,
initializer=tf.contrib.layers.xavier_initializer())
# biases赋值0.1以避免dead neuron
biases = tf.Variable(tf.constant(0.1, shape=[n_out], dtype=tf.float32), name='b')
# 对输入变量input_op和kernel做矩阵乘法并加上biases。再做非线性变换activation
activation = tf.nn.relu_layer(input_op, kernel, biases, name=scope)
p += [kernel, biases]
return activation

# 定义最大池化层的创建函数
def mpool_op(input_op, name, kh, kw, dh, dw):
return tf.nn.max_pool(input_op,
ksize=[1, kh, kw, 1], # 池化层尺寸kh*kw
strides=[1, dh, dw, 1], # 步长dh*dw
padding='SAME',
name=name)

#%%
def inference(s_images, batch_size, n_classes):
#model
'''
VGGNet-16的网络结构主要分为6个部分:前五段为卷积网络,最后一段是全连接网络。
Args:
s_images:输入Tensor
keep_prob:控制Dropout的一个placeholder
'''
# 初始化参数列表p
p = []
keep_prob = 0.5
# assume input_op shape is 224x224x3(第一个卷积层的输入input_op)

# 创建第一段卷积网络 -- outputs 112x112x64
# 两个卷积层的卷积核都是3*3,卷积核数量(输出通道数)均为64,步长1*1,全像素扫描。
conv1_1 = conv_op(s_images, name="conv1_1", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p) # outputs 224x224x64
conv1_2 = conv_op(conv1_1, name="conv1_2", kh=3, kw=3, n_out=64, dh=1, dw=1, p=p) # outputs 224x224x64
pool1 = mpool_op(conv1_2, name="pool1", kh=2, kw=2, dw=2, dh=2) # 标准的2*2的最大池化-outputs 112x112x64

# 创建第二段卷积网络 -- outputs 56x56x128
conv2_1 = conv_op(pool1,name="conv2_1", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
conv2_2 = conv_op(conv2_1, name="conv2_2", kh=3, kw=3, n_out=128, dh=1, dw=1, p=p)
pool2 = mpool_op(conv2_2, name="pool2", kh=2, kw=2, dh=2, dw=2)

# 创建第三段卷积网络 -- outputs 28x28x256
conv3_1 = conv_op(pool2,name="conv3_1", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
conv3_2 = conv_op(conv3_1, name="conv3_2", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
conv3_3 = conv_op(conv3_2, name="conv3_3", kh=3, kw=3, n_out=256, dh=1, dw=1, p=p)
pool3 = mpool_op(conv3_3, name="pool3", kh=2, kw=2, dh=2, dw=2)

# 创建第四段卷积网络 -- outputs 14x14x512
conv4_1 = conv_op(pool3,name="conv4_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv4_2 = conv_op(conv4_1, name="conv4_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv4_3 = conv_op(conv4_2, name="conv4_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
pool4 = mpool_op(conv4_3, name="pool4", kh=2, kw=2, dh=2, dw=2)

# 创建第五段卷积网络 -- outputs 7x7x512
conv5_1 = conv_op(pool4,name="conv5_1", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv5_2 = conv_op(conv5_1, name="conv5_2", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
conv5_3 = conv_op(conv5_2, name="conv5_3", kh=3, kw=3, n_out=512, dh=1, dw=1, p=p)
pool5 = mpool_op(conv5_3, name="pool5", kh=2, kw=2, dw=2, dh=2)

# 备注:VGGNet-16的每一段卷积网络都会将图像的边长缩小一半,但是将卷积输出通道数翻倍。
# 第五段卷积输出的通道数不再增加。

# flatten 将第五段卷积网络的输出结果进行扁平化
shp = pool5.get_shape()
flattened_shape = shp[1].value * shp[2].value * shp[3].value

# tf.reshape函数将每个样本化为长度7*7*512 = 25088的向量
resh1 = tf.reshape(pool5, [-1, flattened_shape], name="resh1")

# fully connected 隐含节点4096的全连接层
fc6 = fc_op(resh1, name="fc6", n_out=4096, p=p)
fc6_drop = tf.nn.dropout(fc6, keep_prob, name="fc6_drop")

fc7 = fc_op(fc6_drop, name="fc7", n_out=4096, p=p)
fc7_drop = tf.nn.dropout(fc7, keep_prob, name="fc7_drop")

fc8 = fc_op(fc7_drop, name="fc8", n_out=1000, p=p)
#softmax_linear = tf.nn.softmax(fc8) # 得到分类输出概率
#predictions = tf.argmax(softmax, 1) # tf.argmax求输出概率最大类别
#return predictions, softmax, fc8, p
# softmax
with tf.variable_scope('softmax_linear') as scope:
weights = tf.get_variable('softmax_linear',
shape=[1000, n_classes],
dtype=tf.float32,
initializer=tf.truncated_normal_initializer(stddev=0.005,dtype=tf.float32))
biases = tf.get_variable('biases',
shape=[n_classes],
dtype=tf.float32,
initializer=tf.constant_initializer(0.1))
softmax_linear = tf.add(tf.matmul(fc8, weights), biases, name='softmax_linear')

return softmax_linear

这段代码是VGG16卷积神经网络的主要网络结构部分,由卷积层池化层和全连接层组成,最后输出softmax分类器结果。所谓的提取中层特征就是将中间的卷积层或者池化层或者全连接层的结果直接取出来。‘

大家可能觉得提取中层特征没什么用,我先说一下我使用的场景。我在别的网络中提取的中层特征(不是卷积神经网络),128*128*64维的特征,保存成mat文件,部分代码如下

for i, sample_image in enumerate(sample_images):
idx = i+1
print("sampling image ", idx)
samples = self.sess.run(
self.fake_B_sample,
feed_dict={self.real_data: sample_image}
)
#save_images(samples, [self.batch_size, 1],
#'./{}/{:s}.jpg'.format(args.test_dir, Human[i]))

sio.savemat('/public/multiview/mat6/{:s}.mat'.format(Human[i]),{'image':samples})


在TensorFlow中还有个很关键的问题是如何将数据输入。官方所给的mnist和cifar10的文件压缩成二进制,其实和我们平常所需的要求不一致,所以我这里使用的是TensorFlow的tfrecord形式输入。在我平时的实验中,使用自己的图片数据集输入的方式是队列输入方式,如果数据量不大的话也会使用feed_dict方式。

所以这里的又一个问题是如何将mat文件转化为tfrecord文件。下面贴上关键代码def convert_to_tfrecord(images, labels):

filename = ('/public/multiview/tfrecord5/data.tfrecords')
n_samples = len(labels)

if np.shape(images)[0] != n_samples:
raise ValueError('Images size %d does not match label size %d.' %(images.shape[0], n_samples))

writer = tf.python_io.TFRecordWriter(filename)
print('\nTransform start......')
for i in np.arange(0, n_samples):
try:
Data = sio.loadmat(images[i])
# print "data shape ", Data['image']
data = np.array(Data['image'],dtype=np.float32)
# print "data ", type(Data)
# print "xohah "
# print data.shape
# sys.exit(0)
image_raw = np.reshape(data,[128,128,64]).tostring()
# print image_raw
label = int(labels[i])
example = tf.train.Example(features=tf.train.Features(feature={
'label':int64_feature(label),
'image_raw': bytes_feature(image_raw)}))
writer.write(example.SerializeToString())
except IOError as e:
print('Could not read:', images[i])
print('error: %s' %e)
print('Skip it!\n')
writer.close()
print('Transform done!') 在转换中关于数据的形式问题卡住了很久。关于转化为tfrecord的问题这里不详细讲了,之后的博客中会专门详细讲解。
在这里将中层特征提取出来之后,转化为tfrecord格式,我的目标是将提取出来的128*128*64维特征输入到VGG16中,在这之前提前训练过一个VGG16的网络,在上面贴出了的网络中,我将数据直接输入到conv2_1中,(因为我所用的图片尺寸的原因,这层实际的尺寸也是128*128*64)特征尺寸一定要和网络尺寸相匹配,然后其它层的网络参数都是载入之前训练好的ckpt文件,然后就可以得到想要的输出了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐