【问题标题】:Tensorflow: How to Pool over Depth?Tensorflow:如何在深度上进行池化?
【发布时间】:2016-04-24 00:12:31
【问题描述】:

我定义了以下参数,用于在图像深度 (rgb) 上进行最大池以在密集层和读数之前进行压缩...我失败了,因为我无法在深度和其他所有内容上池化:

sunset_poolmax_1x1x3_div_2x2x3_params = \
    {'pool_function':tf.nn.max_pool,
     'ksize':[1,1,1,3],
     'strides':[1,1,1,3],
     'padding': 'SAME'}

我将步幅更改为[1,1,1,3],因此深度是唯一一个被池减小的维度……但它仍然不起作用。我必须压缩所有内容以保持颜色的微小图像无法获得良好的效果......

实际错误:

ValueError:当前的实现不支持批量和深度维度的池化。

【问题讨论】:

  • 错误消息几乎可以解释发生了什么。
  • @etarion 啊...我将其解读为逻辑“与”:批次与深度维度(而不是明显的“批次或深度”维度)。所以,从逻辑上讲,如果我不集中在一个上,我应该能够集中在另一个上。无论如何,如果你已经解释了你在说什么,那将是有道理的,因为错误消息几乎是在说其他东西,而不是它的本意。

标签: python tensorflow


【解决方案1】:

tf.nn.max_pool 不支持在深度维度上进行池化,这就是您收到错误的原因。

您可以使用最大减少来实现您想要的:

tf.reduce_max(input_tensor, reduction_indices=[3], keep_dims=True)

上面的keep_dims 参数确保张量的秩被保留。这确保了最大缩减的行为将与 tf.nn.max_pool 操作在深度维度上支持池化时所做的一致。

【讨论】:

  • 不幸的是,不仅 max_pool 发生,conv2d 也发生。这是我的函数tf.nn.conv2d(img_input, w, strides=[1, 1, 1, 3], padding='SAME'), b),它给了我与@bordeo 相同的错误。我需要保持颜色。我认为这是 conv2d 功能的低效率,不是吗?
  • 我知道这是很久以前问的,但也许@Kyrol 可以看看tf.nn.depthwise_conv2d?我同意@Benoit 关于保留 maxpooling 频道的观点。
  • 此实现假定池大小等于通道数,因此它将所有通道折叠成一个。使用深度池化,如果池大小更小(例如 3)会更好。请参阅我的答案以获得实现。此外,reduction_indices 重命名为axis,keep_dims 重命名为keepdims。
【解决方案2】:

TensorFlow 现在支持使用 tf.nn.max_pool() 进行深度最大池化。例如,这里是如何使用池化内核大小 3、步幅 3 和 VALID 填充来实现它:

import tensorflow as tf

output = tf.nn.max_pool(images,
                        ksize=(1, 1, 1, 3),
                        strides=(1, 1, 1, 3),
                        padding="VALID")

您可以在 Keras 模型中使用它,方法是将其包装在 Lambda 层中:

from tensorflow import keras

depth_pool = keras.layers.Lambda(
    lambda X: tf.nn.max_pool(X,
                             ksize=(1, 1, 1, 3),
                             strides=(1, 1, 1, 3),
                             padding="VALID"))

model = keras.models.Sequential([
    ..., # other layers
    depth_pool,
    ... # other layers
])

或者,您可以编写自定义 Keras 层:

class DepthMaxPool(keras.layers.Layer):
    def __init__(self, pool_size, strides=None, padding="VALID", **kwargs):
        super().__init__(**kwargs)
        if strides is None:
            strides = pool_size
        self.pool_size = pool_size
        self.strides = strides
        self.padding = padding
    def call(self, inputs):
        return tf.nn.max_pool(inputs,
                              ksize=(1, 1, 1, self.pool_size),
                              strides=(1, 1, 1, self.pool_size),
                              padding=self.padding)

然后您可以像使用任何其他层一样使用它:

model = keras.models.Sequential([
    ..., # other layers
    DepthMaxPool(3),
    ... # other layers
])

【讨论】:

  • 这仅适用于 1D 或 2D 输入。对于 3D 输入,如果通道是输入张量的最后一个维度,则不会出现任何错误,但不会执行操作。对于 3D 输入,作为一种解决方法,您可以使用keras.layers.Permute 层将通道尺寸移动到另一个位置,应用 maxpool3d,然后使用另一个置换层恢复形状。
【解决方案3】:

这是 tensorflow 原始问题的一个简短示例。我在具有 3 个通道的大小为 225 x 225 的库存 RGB 图像上对其进行了测试。

导入标准库,启用eager_execution快速查看结果

import tensorflow as tf
from scipy.misc import imread
import matplotlib.pyplot as plt
import numpy as np
tf.enable_eager_execution()

uint8读取图像并投射到tf.float32

x = tf.cast(imread('tiger.jpeg'), tf.float32)
x = tf.reshape(x, shape=[-1, x.shape[0], x.shape[1], x.shape[2]])
print(x.shape)
input_channels = x.shape[3]

为深度卷积创建过滤器

filters = tf.contrib.eager.Variable(tf.random_normal(shape=[3, 3, input_channels, 4]))
print(x.shape)

使用channel multiplier 4 执行深度卷积。注意填充已保持为'SAME'。可以随意更改。

x = tf.nn.depthwise_conv2d(input=x, filter=filters, strides=[1, 1, 1, 1], padding='SAME', name='conv_1')
print(x.shape)

执行max_pooling2d。由于池化层的输出是(input_size - pool_size + 2 * padding)/stride + 1,而填充是'valid',我们应该得到(225 - 2 + 0)/1 + 1 = 223的输出。

x = tf.layers.max_pooling2d(inputs=x, pool_size=2, strides=1,padding='valid', name='maxpool1')
print(x.shape)

绘制数字以确认。

fig, ax = plt.subplots(nrows=4, ncols=3)
q = 0
for ii in range(4):
    for jj in range(3):
        ax[ii, jj].imshow(np.squeeze(x[:,:,:,q]))
        ax[ii,jj].set_axis_off()
        q += 1
plt.tight_layout()
plt.show()

【讨论】:

    【解决方案4】:

    这是《使用 scikit 学习 keras 和 tensorflow 进行机器学习实践》一书的节选。 Keras 不包括深度最大池化层,但 TensorFlow 的低级 深度学习 API 可以:只需使用 tf.nn.max_pool() 函数,并指定 内核大小和步幅为 4 元组(即大小为 4 的元组)。每个的前三个值 应该为 1:这表示内核大小和步幅沿批次、高度和 宽度尺寸应该是 1。最后一个值应该是任何内核大小和 您想要沿深度维度的步幅——例如,3(这必须是 输入深度;如果前一层输出 20 个特征图,它将不起作用,因为 20 不是 3 的倍数):

    output = tf.nn.max_pool(images,
    ksize=(1, 1, 1, 3),
    strides=(1, 1, 1, 3),
    padding="valid")
    

    如果您想将此作为层包含在您的 Keras 模型中,请将其包装在 Lambda 层中 (或创建自定义 Keras 层):

    depth_pool = keras.layers.Lambda(
    lambda X: tf.nn.max_pool(X, ksize=(1, 1, 1, 3), strides=(1, 1, 1, 3),
    padding="valid"))
    

    【讨论】:

      【解决方案5】:

      您可以使用自定义 Keras 层:

      class DepthPool(tf.keras.layers.Layer):
          def __init__(self, pool_size=2, **kwargs):
              super().__init__(**kwargs)
              self.pool_size = pool_size
          
          def call(self, inputs):
              old_shape = tf.shape(inputs)
              num_channels = old_shape[-1]
              num_channel_groups = num_channels // self.pool_size
              new_shape = tf.concat(
                  [old_shape[:-1], [num_channel_groups, self.pool_size]], axis=0)
              reshaped_inputs = tf.reshape(inputs, new_shape)
              return tf.reduce_max(reshaped_inputs, axis=-1)
      

      注意事项:

      • 没有strides 参数:假定它等于池大小
      • TensorFlow 的 tf.nn.max_pool() 操作支持深度池化(见我的另一个答案),但它只适用于 CPU,所以这个自定义层通常更好

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-11
        • 2017-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多