【问题标题】:Basic 1d convolution in tensorflow张量流中的基本一维卷积
【发布时间】:2016-06-30 05:20:57
【问题描述】:

好的,我想在 Tensorflow 中对时间序列数据进行一维卷积。根据theseticketsthe manual,这显然是使用tf.nn.conv2d 支持的。唯一的要求是设置strides=[1,1,1,1]。听起来很简单!

但是,即使在非常小的测试用例中,我也无法弄清楚如何做到这一点。我做错了什么?

让我们设置一下吧。

import tensorflow as tf
import numpy as np
print(tf.__version__)
>>> 0.9.0

好的,现在在两个小数组上生成一个基本的卷积测试。我将通过使用 1 的批量大小来简化操作,并且由于时间序列是一维的,因此我的“图像高度”将为 1。由于它是单变量时间序列,显然“通道”的数量也是1,这样就很简单了吧?

g = tf.Graph()
with g.as_default():
    # data shape is "[batch, in_height, in_width, in_channels]",
    x = tf.Variable(np.array([0.0, 0.0, 0.0, 0.0, 1.0]).reshape(1,1,-1,1), name="x")
    # filter shape is "[filter_height, filter_width, in_channels, out_channels]"
    phi = tf.Variable(np.array([0.0, 0.5, 1.0]).reshape(1,-1,1,1), name="phi")
    conv = tf.nn.conv2d(
        phi,
        x,
        strides=[1, 1, 1, 1],
        padding="SAME",
        name="conv")

轰隆隆。错误。

ValueError: Dimensions 1 and 5 are not compatible

好的,首先,我不明白 any 维度应该如何发生这种情况,因为我已经指定我在卷积 OP 中填充参数。

但是很好,也许有限制。我一定把文档弄糊涂了,并在张量的错误轴上设置了这个卷积。我会尝试所有可能的排列:

for i in range(4):
    for j in range(4):
        shape1 = [1,1,1,1]
        shape1[i] = -1
        shape2 = [1,1,1,1]
        shape2[j] = -1
        x_array = np.array([0.0, 0.0, 0.0, 0.0, 1.0]).reshape(*shape1)
        phi_array = np.array([0.0, 0.5, 1.0]).reshape(*shape2)
        try:
            g = tf.Graph()
            with g.as_default():
                x = tf.Variable(x_array, name="x")
                phi = tf.Variable(phi_array, name="phi")
                conv = tf.nn.conv2d(
                    x,
                    phi,
                    strides=[1, 1, 1, 1],
                    padding="SAME",
                    name="conv")
                init_op = tf.initialize_all_variables()
            sess = tf.Session(graph=g)
            sess.run(init_op)
            print("SUCCEEDED!", x_array.shape, phi_array.shape, conv.eval(session=sess))
            sess.close()
        except Exception as e:
            print("FAILED!", x_array.shape, phi_array.shape, type(e), e.args or e._message)

结果:

FAILED! (5, 1, 1, 1) (3, 1, 1, 1) <class 'ValueError'> ('Filter must not be larger than the input: Filter: (3, 1) Input: (1, 1)',)
FAILED! (5, 1, 1, 1) (1, 3, 1, 1) <class 'ValueError'> ('Filter must not be larger than the input: Filter: (1, 3) Input: (1, 1)',)
FAILED! (5, 1, 1, 1) (1, 1, 3, 1) <class 'ValueError'> ('Dimensions 1 and 3 are not compatible',)
FAILED! (5, 1, 1, 1) (1, 1, 1, 3) <class 'tensorflow.python.framework.errors.InvalidArgumentError'> No OpKernel was registered to support Op 'Conv2D' with these attrs
     [[Node: conv = Conv2D[T=DT_DOUBLE, data_format="NHWC", padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](x/read, phi/read)]]
FAILED! (1, 5, 1, 1) (3, 1, 1, 1) <class 'tensorflow.python.framework.errors.InvalidArgumentError'> No OpKernel was registered to support Op 'Conv2D' with these attrs
     [[Node: conv = Conv2D[T=DT_DOUBLE, data_format="NHWC", padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](x/read, phi/read)]]
FAILED! (1, 5, 1, 1) (1, 3, 1, 1) <class 'ValueError'> ('Filter must not be larger than the input: Filter: (1, 3) Input: (5, 1)',)
FAILED! (1, 5, 1, 1) (1, 1, 3, 1) <class 'ValueError'> ('Dimensions 1 and 3 are not compatible',)
FAILED! (1, 5, 1, 1) (1, 1, 1, 3) <class 'tensorflow.python.framework.errors.InvalidArgumentError'> No OpKernel was registered to support Op 'Conv2D' with these attrs
     [[Node: conv = Conv2D[T=DT_DOUBLE, data_format="NHWC", padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](x/read, phi/read)]]
FAILED! (1, 1, 5, 1) (3, 1, 1, 1) <class 'ValueError'> ('Filter must not be larger than the input: Filter: (3, 1) Input: (1, 5)',)
FAILED! (1, 1, 5, 1) (1, 3, 1, 1) <class 'tensorflow.python.framework.errors.InvalidArgumentError'> No OpKernel was registered to support Op 'Conv2D' with these attrs
     [[Node: conv = Conv2D[T=DT_DOUBLE, data_format="NHWC", padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](x/read, phi/read)]]
FAILED! (1, 1, 5, 1) (1, 1, 3, 1) <class 'ValueError'> ('Dimensions 1 and 3 are not compatible',)
FAILED! (1, 1, 5, 1) (1, 1, 1, 3) <class 'tensorflow.python.framework.errors.InvalidArgumentError'> No OpKernel was registered to support Op 'Conv2D' with these attrs
     [[Node: conv = Conv2D[T=DT_DOUBLE, data_format="NHWC", padding="SAME", strides=[1, 1, 1, 1], use_cudnn_on_gpu=true](x/read, phi/read)]]
FAILED! (1, 1, 1, 5) (3, 1, 1, 1) <class 'ValueError'> ('Dimensions 5 and 1 are not compatible',)
FAILED! (1, 1, 1, 5) (1, 3, 1, 1) <class 'ValueError'> ('Dimensions 5 and 1 are not compatible',)
FAILED! (1, 1, 1, 5) (1, 1, 3, 1) <class 'ValueError'> ('Dimensions 5 and 3 are not compatible',)
FAILED! (1, 1, 1, 5) (1, 1, 1, 3) <class 'ValueError'> ('Dimensions 5 and 1 are not compatible',)

嗯。好的,现在看起来有两个问题。首先,ValueError 是关于沿错误的轴应用过滤器,我猜,虽然有两种形式。

但是我可以沿其应用过滤器的轴也令人困惑 - 请注意,它实际上使用输入形状 (5, 1, 1, 1) 和过滤器形状 (1, 1, 1, 3) 构建图形。文档中的 AFAICT,这应该是一个过滤器,它查看批处理中的示例,一个“像素”和一个“通道”并输出 3 个“通道”。那么,当其他人不工作时,为什么这个工作有效?

无论如何,有时它在构建图形时不会失败。 有时它会构建图形;然后我们得到tensorflow.python.framework.errors.InvalidArgumentError。从一些confusing github tickets 我认为这可能是由于我在 CPU 而不是 GPU 上运行的事实,反之亦然卷积 Op 仅针对 32 位浮点数定义的事实,而不是64位浮点数。如果有人能阐明我应该在 what 上对齐 what 的轴,以便将时间序列与内核进行卷积,我将不胜感激。 p>

【问题讨论】:

标签: python tensorflow


【解决方案1】:

我很抱歉这么说,但您的第一个代码几乎是正确的。你只是将xphi 倒置在tf.nn.conv2d 中:

g = tf.Graph()
with g.as_default():
    # data shape is "[batch, in_height, in_width, in_channels]",
    x = tf.Variable(np.array([0.0, 0.0, 0.0, 0.0, 1.0]).reshape(1, 1, 5, 1), name="x")
    # filter shape is "[filter_height, filter_width, in_channels, out_channels]"
    phi = tf.Variable(np.array([0.0, 0.5, 1.0]).reshape(1, 3, 1, 1), name="phi")
    conv = tf.nn.conv2d(
        x,
        phi,
        strides=[1, 1, 1, 1],
        padding="SAME",
        name="conv")

更新: TensorFlow 现在从 r0.11 版本开始支持一维卷积,使用 tf.nn.conv1d。我之前在我粘贴在这里的 stackoverflow 文档(现已绝版)中制作了使用它们的指南:


一维卷积指南

考虑一个长度为10,维度为16 的基本示例。批量大小为32。因此,我们有一个占位符,其输入形状为 [batch_size, 10, 16]

batch_size = 32
x = tf.placeholder(tf.float32, [batch_size, 10, 16])

然后我们创建一个宽度为 3 的过滤器,我们将 16 频道作为输入,同时将 16 频道作为输出。

filter = tf.zeros([3, 16, 16])  # these should be real values, not 0

最后,我们应用tf.nn.conv1d 与一个步幅和一个填充: - 步幅:整数s - 填充:这就像在 2D 中一样,您可以在 SAMEVALID 之间进行选择。 SAME 将输出相同的输入长度,而VALID 不会添加零填充。

对于我们的示例,我们采用 2 的步幅和一个有效的填充。

output = tf.nn.conv1d(x, filter, stride=2, padding="VALID")

输出形状应为[batch_size, 4, 16]
使用padding="SAME",我们的输出形状将是[batch_size, 5, 16]

【讨论】:

  • facepalm 谢谢!接得好!这解决了我眼前的问题。另外:我认为带有 padding='SAME' 的 conv2d 的行为很奇怪 - 通常信号处理中的卷积是 相同空间 的两个向量上的函数,因此内核长度的这种不对称性令人烦恼。这一点以及他们推翻一个论点的事实导致了不必要的混乱。无论如何,这不是当前的问题......
  • 阅读代码中各个维度的含义。输入的第二和第三维是它的高度和宽度。过滤器的第一和第二维度是它的高度和宽度。基本上你必须像处理高度为 1 的图像一样思考。
  • 我有一个高度为 1 和宽度为 3 的过滤器,作用于高度为 1 和宽度为 5 的输入。如果您想要多个过滤器,您可以将 out_channels 修改为 12。如果您想要大小 2,您可以将其宽度从 3 修改为 2。形状为 [1, 2, 1, 12]
  • in_channel 匹配输入的 in_channel。在您的情况下,您应该重塑为 [batch_size, 1, 784, 1]。过滤器可以是 [1, 2, 1, 12],宽度为 2 和 12 个过滤器。输出暗淡将是 [batch_size, 1, 784, 12] 填充“SAME”
  • @Pinocchio:我添加了关于一维卷积的文档
【解决方案2】:

在新版本的TF(从0.11开始)你有conv1d,所以不需要用2d卷积来做1d卷积。下面是一个如何使用 conv1d 的简单示例:

import tensorflow as tf
i = tf.constant([1, 0, 2, 3, 0, 1, 1], dtype=tf.float32, name='i')
k = tf.constant([2, 1, 3], dtype=tf.float32, name='k')

data   = tf.reshape(i, [1, int(i.shape[0]), 1], name='data')
kernel = tf.reshape(k, [int(k.shape[0]), 1, 1], name='kernel')

res = tf.squeeze(tf.nn.conv1d(data, kernel, stride=1, padding='VALID'))
with tf.Session() as sess:
    print sess.run(res)

要了解 conv1d 是如何计算的,请查看various examples

【讨论】:

  • 嗨萨尔瓦多,对于conv2d和conv3d,有对应的conv2d_transpose和conv3d_transpose。 conv1d_transpose 怎么样? conv1d_transpose 有什么实现吗?谢谢。
【解决方案3】:

我认为我可以满足我需要的要求。其工作原理的 cmets/详细信息在代码中:

import numpy as np

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

task_name = 'task_MNIST_flat_auto_encoder'
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
X_train, Y_train = mnist.train.images, mnist.train.labels # N x D
X_cv, Y_cv = mnist.validation.images, mnist.validation.labels
X_test, Y_test = mnist.test.images, mnist.test.labels

# data shape is "[batch, in_height, in_width, in_channels]",
# X_train = N x D
N, D = X_train.shape
# think of it as N images with height 1 and width D.
X_train = X_train.reshape(N,1,D,1)
x = tf.placeholder(tf.float32, shape=[None,1,D,1], name='x-input')
#x = tf.Variable( X_train , name='x-input')
# filter shape is "[filter_height, filter_width, in_channels, out_channels]"
filter_size, nb_filters = 10, 12 # filter_size , number of hidden units/units
# think of it as having nb_filters number of filters, each of size filter_size
W = tf.Variable( tf.truncated_normal(shape=[1, filter_size, 1,nb_filters], stddev=0.1) )
stride_convd1 = 2 # controls the stride for 1D convolution
conv = tf.nn.conv2d(input=x, filter=W, strides=[1, 1, stride_convd1, 1], padding="SAME", name="conv")

with tf.Session() as sess:
    sess.run( tf.initialize_all_variables() )
    sess.run(fetches=conv, feed_dict={x:X_train})

感谢 Olivier 的帮助(请参阅他的 cmets 中的讨论以获得进一步的说明)。


手动检查:

X_train_org = np.array([[0,1,2,3]])
N, D = X_train_org.shape
X_train_1d = X_train_org.reshape(N,1,D,1)
#X_train = tf.constant( X_train_org )
# think of it as N images with height 1 and width D.
xx = tf.placeholder(tf.float32, shape=[None,1,D,1], name='xx-input')
#x = tf.Variable( X_train , name='x-input')
# filter shape is "[filter_height, filter_width, in_channels, out_channels]"
filter_size, nb_filters = 2, 2 # filter_size , number of hidden units/units
# think of it as having nb_filters number of filters, each of size filter_size
filter_w = np.array([[1,3],[2,4]]).reshape(1,filter_size,1,nb_filters)
#W = tf.Variable( tf.truncated_normal(shape=[1,filter_size,1,nb_filters], stddev=0.1) )
W = tf.Variable( tf.constant(filter_w, dtype=tf.float32) )
stride_convd1 = 2 # controls the stride for 1D convolution
conv = tf.nn.conv2d(input=xx, filter=W, strides=[1, 1, stride_convd1, 1], padding="SAME", name="conv")

#C = tf.constant( (np.array([[4,3,2,1]]).T).reshape(1,1,1,4) , dtype=tf.float32 ) #
#tf.reshape( conv , [])
#y_tf = tf.matmul(conv, C)


##
x = tf.placeholder(tf.float32, shape=[None,D], name='x-input') # N x 4
W1 = tf.Variable( tf.constant( np.array([[1,2,0,0],[3,4,0,0]]).T, dtype=tf.float32 ) ) # 2 x 4
y1 = tf.matmul(x,W1) # N x 2 = N x 4 x 4 x 2
W2 = tf.Variable( tf.constant( np.array([[0,0,1,2],[0,0,3,4]]).T, dtype=tf.float32 ))
y2 = tf.matmul(x,W2) # N x 2 = N x 4 x 4 x 2
C1 = tf.constant( np.array([[4,3]]).T, dtype=tf.float32 ) # 1 x 2
C2 = tf.constant( np.array([[2,1]]).T, dtype=tf.float32 )

p1 = tf.matmul(y1,C1)
p2 = tf.matmul(y2,C2)
y = p1 + p2
with tf.Session() as sess:
    sess.run( tf.initialize_all_variables() )
    print 'manual conv'
    print sess.run(fetches=y1, feed_dict={x:X_train_org})
    print sess.run(fetches=y2, feed_dict={x:X_train_org})
    #print sess.run(fetches=y, feed_dict={x:X_train_org})
    print 'tf conv'
    print sess.run(fetches=conv, feed_dict={xx:X_train_1d})
    #print sess.run(fetches=y_tf, feed_dict={xx:X_train_1d})

输出:

manual conv
[[ 2.  4.]]
[[  8.  18.]]
tf conv
[[[[  2.   4.]
   [  8.  18.]]]]

【讨论】:

  • 请注意,要添加偏差,只需使用与 nb_filters 相同数量的过滤器创建 var bias。然后只需添加它们,广播应该为您添加。注意,不要在展平后添加偏差,在之前添加,否则每个过滤器都不会有偏差。
猜你喜欢
  • 2018-03-14
  • 2020-05-14
  • 1970-01-01
  • 2018-05-14
  • 2017-09-12
  • 1970-01-01
  • 2017-03-18
  • 2020-07-16
  • 1970-01-01
相关资源
最近更新 更多