一、直接卷积
卷积在深度学习中非常常见。并且实际上我们可以不经过训练直接使用卷积,一个常见的用途是进行图片模糊:
为了在tensorflow中使用卷积,我们可以使用几个内置图层。你可以使用一维卷积(输入为2维),二维卷积(输入为3维),三维卷积(输入为4维)。我们主要关注二维卷积,关于其他维度卷积的介绍,参考runhani’s StackOverflow answer 。
tf.nn.conv2d(
input,
filter,
strides,
padding,
use_cudnn_on_gpu=True,
data_format='NHWC',
dilations=[1, 1, 1, 1],
name=None
)
Input: Batch size (N) x Height (H) x Width (W) x Channels (C)
Filter: Height x Width x Input Channels x Output Channels
(e.g. [5, 5, 3, 64])
Strides: 4 element 1-D tensor, strides in each direction
(often [1, 1, 1, 1] or [1, 2, 2, 1])
Padding: 'SAME' or 'VALID'
Dilations: The dilation factor. If set to k > 1, there will be k-1 skipped cells between each filter element on that dimension.
Data_format: default to NHWC
对于步幅,不要在第一个和第四个维度中使用除1之外的任何数字,因为我们不想跳过批次中的任何样本或图像中的任何通道。 扩张也是一样。还有其他的卷积操作,更多信息参考官方文档。
conv2d: Filters that mix channels together.
depthwise_conv2d: Filters that operate on each channel independently.
separable_conv2d: A depthwise spatial filter followed by a pointwise filter.
二、在MNIST中使用CNN
我们之前完成过单层全连接网络的逻辑拟合,效果不尽人意,让我们看看使用卷积网络的效果如何。
对于MNIST,我们将使用两层卷积网络,每层后面跟着ReLU和Max Pooling,以及两层全连接层,每个卷积层的步长都是[1,1,1,1]。(关于ReLU,参考知乎如何理解ReLU activation function?)
因为我们将重复多次相同操作,可重复使用的代码是一个好主意。使用variable scope同样重要,这样我们可以在不同图层间使用同名变量。一个名为'weights'的变量在变量域'conv1'中将会成为'conv1/weights'。为每个图层创建一个变量域,这样就不会出现名称冲突。
卷积层
一个通用操作是将卷积层和非线性化归为同一组,我们将创建conv_relu以供两层共同使用。
def conv_relu(inputs, filters, k_size, stride, padding, scope_name):
with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
in_channels = inputs.shape[-1]
kernel = tf.get_variable('kernel', [k_size, k_size, in_channels, filters],
initializer=tf.truncated_normal_initializer())
biases = tf.get_variable('biases', [filters],
initializer=tf.random_normal_initializer())
conv = tf.nn.conv2d(inputs, kernel, strides=[1, stride, stride, 1], padding=padding)
return tf.nn.relu(conv + biases, name=scope.name)
池化层
pooling是一种下采样技术,用于降低从卷积层提取的特征映射的维度,以减少处理时间。 pooling layer用(希望)代替数据的一个子区域,其最具代表性的特征。 最流行的池化算法,即max pooling,用其最大值替换数据的子区域。 另一种算法是average pooling,对一个子区域中的所有值进行平均。在pooling layer使用tf.nn.maxpool。
def maxpool(inputs, ksize, stride, padding='VALID', scope_name='pool'):
with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
pool = tf.nn.max_pool(inputs,
ksize=[1, ksize, ksize, 1],
strides=[1, stride, stride, 1],
padding=padding)
return pool
全连接层
def fully_connected(inputs, out_dim, scope_name='fc'):
with tf.variable_scope(scope_name, reuse=tf.AUTO_REUSE) as scope:
in_dim = inputs.shape[-1]
w = tf.get_variable('weights', [in_dim, out_dim],
initializer=tf.truncated_normal_initializer())
b = tf.get_variable('biases', [out_dim],
initializer=tf.constant_initializer(0.0))
out = tf.matmul(inputs, w) + b
return out
整合
def inference(self):
conv1 = conv_relu(inputs=self.img,
filters=32,
k_size=5,
stride=1,
padding='SAME',
scope_name='conv1')
pool1 = maxpool(conv1, 2, 2, 'VALID', 'pool1')
conv2 = conv_relu(inputs=pool1,
filters=64,
k_size=5,
stride=1,
padding='SAME',
scope_name='conv2')
pool2 = maxpool(conv2, 2, 2, 'VALID', 'pool2')
feature_dim = pool2.shape[1] * pool2.shape[2] * pool2.shape[3]
pool2 = tf.reshape(pool2, [-1, feature_dim])
fc = tf.nn.relu(fully_connected(pool2, 1024, 'fc'))
dropout = tf.layers.dropout(fc, self.keep_prob, training=self.training, name='dropout')
self.logits = fully_connected(dropout, self.n_classes, 'logits')
查看结果
三、tf.layers
事实上,我们无需编写conv_relu等图层,tensorflow的tf.layers提供了很多现成的图层,更高级别的库如Keras也有很多可直接使用的模型。
使用tf.layers创建relu非线性化的卷积层:
conv1 = tf.layers.conv2d(inputs=self.img,
filters=32,
kernel_size=[5, 5],
padding='SAME',
activation=tf.nn.relu,
name='conv1')
池化层和全连接层:
pool1 = tf.layers.max_pooling2d(inputs=conv1,
pool_size=[2, 2],
strides=2,
name='pool1')
fc = tf.layers.dense(pool2, 1024, activation=tf.nn.relu, name='fc')
使用tf.layers时要注意一点:需要一个变量来指示它处于训练模式还是评估模式。我们在训练时会剔除(drop out)神经元,但评估时使用全部。
dropout = tf.layers.dropout(fc,
self.keep_prob,
training=self.training,
name='dropout')
更多参考:
A Guide to tf.layers