【问题标题】:How to implement two layers of Keras conv1d in numpy?如何在numpy中实现两层Keras conv1d?
【发布时间】:2020-12-13 12:52:21
【问题描述】:

我在 Keras 中有两个 conv1d 层,我正在尝试使用 numpy 进行复制。我的第一层工作正常,但我很难弄清楚第二层。第一个 conv1d 层采用 input_shape=(100,1),输出形状为 (None, 9, 16)。第二个 conv1d 层在 Keras 中输出 (None, 1, 16) 的形状。这是我的 Keras 层:

model.add(Conv1D(16, 12,strides=12, activation=None, padding='same',input_shape=(100,1)))
model.add(Conv1D(16, 12,strides=12, activation=None, padding='same'))

这是我使用 numpy 的 conv1d 代码:

    def unfold(self, x, kernel_size, stride):  # WORK IN PROGRESS
        unfolded_x = np.array([[c for c in x[i*stride:i*stride+kernel_size]] for i in range(int(len(x)/stride))])

        unfolded_x = np.swapaxes(unfolded_x, 0,1)   
        unfolded_x = unfolded_x[np.newaxis, 0:, 0: ]
        return unfolded_x

    def conv1d_layer(self, input_data, weights, biases, kernel_size=1, stride=1, padding='same'):
        # Apply padding to input data
        in_width = len(input_data)
        if padding == 'same':

            if in_width % stride == 0:
                pad_along_width = max(kernel_size - stride, 0)
            else:
                pad_along_width = max(kernel_size - (in_width % stride), 0)

            pad_left = pad_along_width // 2
            pad_right = pad_along_width - pad_left
            input_data = np.concatenate([np.zeros(pad_left), input_data, np.zeros(pad_right)])
        

        # Take the dot product of input_data and weights (or unfolded input data)
        if kernel_size == 1 and stride == 1: #this is for dense layer
            y = np.dot(input_data, weights)
        else:
            #Create sliding window (unfolded) and perform dot product
            unfolded_input = self.unfold(input_data, kernel_size=kernel_size, stride=stride)

            y = np.tensordot(unfolded_input, weights, axes=([1, 0], [0, 1]))

        out = y + biases
        return out        

以及我对 conv1d 层的调用:

out = conv1d_layer(input, weights['W_conv1d'], weights['b_conv1d'], kernel_size=12, stride=12)
conv1d_layer2 = conv1d_layer(out, weights['W_conv1d'], weights['b_conv1d'], kernel_size=12, stride=12)

显然第二层将无法工作,因为它设置为接收 (100,1),那么在给定“相同”填充的情况下,我将如何调整我的代码以接收 (9,16) 的第一层输出, kernel_size=12 and stride=12?

谢谢

【问题讨论】:

  • 您似乎忽略了代码中的过滤器数量。是tf.keras.layers.Conv1D()中的第一个参数

标签: python numpy tensorflow keras conv-neural-network


【解决方案1】:

我认为这应该可以解决问题:

import numpy as np

def pad(x, kernel_size, stride):
    """'same' padding"""
    seq_len, channels = x.shape
    if seq_len % stride == 0:
        pad_width = max(kernel_size - stride, 0)
    else:
        pad_width = max(kernel_size - (seq_len % stride), 0)
    pad_left = pad_width // 2
    pad_right = pad_width - pad_left
    return np.concatenate([np.zeros((pad_left, channels)), x, np.zeros((pad_right, channels))])

def unfold(x, kernel_size, stride):
    return np.stack([[c for c in x[i*stride:i*stride+kernel_size]] for i in range(len(x)//stride)])

def conv1d(x, weight, bias, stride):
    kernel_size = weight.shape[0]
    out = pad(x, kernel_size, stride)
    out = unfold(out, kernel_size, stride)
    # l = sequence, k = kernel, c = input channels, o = output channels
    # you can use np.tensordot here, but I find einsum is more readable, though be less performant
    out = np.einsum("lkc,kco->lo", out, weight) + bias
    return out

# input sequence
x = np.random.randn(100, 1)

# weights are shape [kernel size, input channels, output channels]
# biases are shape [output channels]

w1 = np.random.randn(12, 1, 16)
b1 = np.random.randn(16)
w2 = np.random.randn(12, 16, 16)
b2 = np.random.randn(16)

print("input", x.shape)
out = conv1d(x, w1, b1, stride=12)
print("first conv", out.shape)
out = conv1d(out, w2, b2, stride=12)
print("second conv", out.shape)
input (100, 1)
first conv (9, 16)
second conv (1, 16)

我认为您遇到的问题与额外的渠道维度有关。 - 泰迪

注意:

如果您想要更快的unfold 版本,您可以利用numpy.lib.stride_tricks.as_strided 来实现矢量化方法:

from numpy.lib.stride_tricks import as_strided

def unfold(x, kernel_size, stride):
    return as_strided(
        x,
        shape=(x.shape[0] // stride, kernel_size, x.shape[1]),
    )

as_strided 可能有一些注意事项,请查看文档了解更多信息。

【讨论】:

  • 就是这样,感谢您快速而彻底的回复!也比我的代码更简洁。我必须阅读“stack”和“einsum”函数,这些对我来说是新的。
  • 很好的答案,但是当 kernel_size 比 stride 大 2 倍时,我认为 unfold 不正确,因为最后一个子序列将小于其他子序列。我认为子序列的数量等于int(np.ceil(seq_len / stride)) 其中 seq_len 是数组 before 填充的长度。
猜你喜欢
  • 2021-12-31
  • 1970-01-01
  • 1970-01-01
  • 2019-09-03
  • 1970-01-01
  • 1970-01-01
  • 2021-02-21
  • 1970-01-01
  • 2019-02-17
相关资源
最近更新 更多