Keras 中的自定义层 - 维度问题

Posted

技术标签:

【中文标题】Keras 中的自定义层 - 维度问题【英文标题】:Custom Layer in Keras - Dimension Problem 【发布时间】:2020-04-04 17:55:26 【问题描述】:

我想要一个 CNN,它将原始信号作为输入,并作为第一步处理短时间傅立叶变换。因此我想用 Keras 创建一个自定义层。

我按照here的解释,将复杂度降低为如下代码:

class CreateSFTF(Layer):
    def __init__(self, units=32,n_fft=1000,hop_length=0,log=True, **kwargs):
      super(CreateSFTF, self).__init__(**kwargs)
      self.units = units
      self.n_fft = n_fft
      self.hop_length = hop_length
      self.log = log

    def build(self, input_shape):
      super(CreateSFTF, self).build(input_shape)

    def call(self, inputs):
      def _tf_log10(x):
          numerator = tf.math.log(x)
          denominator = tf.math.log(tf.constant(10, dtype=numerator.dtype))
          return numerator / denominator

      stfts = tf.signal.stft(
          input,
          frame_length=self.n_fft,
          frame_step=self.hop_length,
          window_fn=tf.signal.hann_window,
          pad_end=False
      )
      mag_stfts = tf.abs(stfts)  
      return tf.expand_dims(mag_stfts, 3)

    def get_config(self):
      config = super(CreateSFTF, self).get_config()
      config.update('units': self.units)
      return config

我在这里使用图层:

def DefineCNNWithFFTAtBeginning(length_signal):
  input = Input(shape = (length_signal,1))
  x = CreateSFTF(n_fft=1000,hop_length=100,log=True)(input)
  x = BatchNormalization()(x) # recommended

  x = layers.Conv2D(8, (3, 3), activation='relu',padding='valid')(x)
  x = layers.Conv2D(8, (3, 3),activation='relu', padding='valid')(x)
  x = layers.MaxPooling2D(pool_size=(4, 4))(x)

  x = layers.Conv2D(16, (5, 5),activation='relu', padding='valid')(x)
  x = layers.Conv2D(4, (5, 5),activation='relu', padding='valid')(x)
  x = layers.MaxPooling2D(pool_size=(5, 5))(x)
  x = Flatten()(x)

  x = layers.Dropout(0.5)(x)

  x = Dense(1,activation='sigmoid')(x)

  model = Model(input, x)

  model.compile(loss='binary_crossentropy',
                optimizer='adam',            
                metrics=['accuracy'])

  model.summary()
  return model

我在这里称呼它:

model = DefineCNNWithFFTAtBeginning(76000)
history =model.fit(X_train.values, y_train,
                            validation_data=(X_test.values, y_test),
                            epochs=50, batch_size=32,
                          shuffle = True)

Model: "model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 76000)]           0         
_________________________________________________________________
create_sftf (CreateSFTF)     (None, 751, 513, 1)       0         
_________________________________________________________________
batch_normalization (BatchNo (None, 751, 513, 1)       4         
_________________________________________________________________
conv2d (Conv2D)              (None, 749, 511, 8)       80        
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 747, 509, 8)       584       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 186, 127, 8)       0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 182, 123, 16)      3216      
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 178, 119, 4)       1604      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 35, 23, 4)         0         
_________________________________________________________________
flatten (Flatten)            (None, 3220)              0         
_________________________________________________________________
dropout (Dropout)            (None, 3220)              0         
_________________________________________________________________
dense (Dense)                (None, 1)                 3221      
=================================================================
Total params: 8,709
Trainable params: 8,707
Non-trainable params: 2
_________________________________________________________________
Train on 987 samples, validate on 247 samples
Epoch 1/50
 32/987 [..............................] - ETA: 29s

X_train 的形状是(xxxxx,76000)

旧错误消息

检查输入时出错:预期 input_1 有 3 个维度,但是 得到形状为 (987, 1) 的数组 - 已解决

有人知道解决方案吗?提前致谢。

更新

Flatten 丢失 // 抱歉 Pandas Dataframe ".values" 没有提供合适的输入

新的错误信息

Eager execution 函数的输入不能是 Keras 符号张量,但发现 tf.Tensor 'input_4:0' shape=(None, 76000) dtype=float32

试过

experimental_run_tf_function=False 作为 Model.Compile 中的参数 - 没用 如果我用 x = 交换我的自定义层 Reshape(target_shape=(380,200,1))(input),它确实有效,所以错误必须 位于自定义层内。

【问题讨论】:

试试 input = Input(shape = (length_signal,)) 同样的错误信息 【参考方案1】:

您的模型期望形状张量 (None, 76000, 1) 但您正在通过 (987,1)

您必须预处理您的数据以满足标准。

987 不是此模型的有效尺寸,您必须使用 76000 X 1。如果不正确,您必须在模型中进行更正。

如果您想一次添加额外的维度或对单个火车数据进行训练,您可以使用以下代码扩展维度

x = np.random.randn((76000,1)) # shape = (76000 X 1)
x = x[None] # shape = (1 X 76000 X 1)

【讨论】:

是的,你是对的,Pandas Dataframe ".Values" 函数没有提供合适的输入 - 因此我创建了一个 np.array 提供:(xxx, 76000) 维度。 在 Python 脚本中运行您的代码,而不是在 Jupiter 笔记本中。检查这是否有效? 确实如此,但它在反向传播时停止(我假设)。 这是我在“新错误消息”下提到的:渴望执行函数的输入不能是 Keras 符号张量,但发现 tf.Tensor 'input_4:0' shape=(None, 76000) dtype =float32【参考方案2】:

输入层为批量大小添加了一个维度:

输入形状

nD 张量,形状为:(batch_size, ..., input_dim)。最常见的情况是形状为 (batch_size, input_dim) 的 2D 输入。

因此,正如 keras 的文档所说(上面引用的),您指定的批量大小为 length_signal

试试(length_signal, )( ,length_signal)(或类似的)。

【讨论】:

另外,您在模型中说输入 laer 将是 76000,而您的实际输入是 987。

以上是关于Keras 中的自定义层 - 维度问题的主要内容,如果未能解决你的问题,请参考以下文章

使用具有附加属性的自定义层保存和加载 keras 模型

在TF 2.0中使用tf.keras,如何定义依赖于学习阶段的自定义层?

使用自定义维度输入 tensorflow 或 keras 神经网络

谷歌 BigQuery 中的自定义维度

图像分割 - Keras 中的自定义损失函数

Keras 中的自定义损失函数 - 遍历 TensorFlow