使用keras.layers.Reshape实现不同维度任意层之间的对接

1. 引入

keras提供了很多不同的layer给我们使用,常用的比如Dense, Dropout, Flatten, Activation, BatchNormalization, GlobalAveragePooling2D,Conv2D, MaxPooling2D, ZeroPadding2D,LSTM等等。我们使用这些layer,像搭积木一样逐层链接,就能实现深度学习模型。

但是,keras提供的layer都是传统的layer。有一些比较新的layer,keras是不能那么快提供给我们用的。所以一些比较特别的layer,我们还是会去github上找到其他开发者写好的拿来用。

那么,问题就来了,这些第三方提供的layer,和keras提供的layer,对接到一起时,常常是会报错的。大部分情况下,这样直接的对接layer,会得到这个错误:ValueError: Dimensions must be equal。

错误的原因,是因为第三方的layer,可能已经写死了要一个2D的输入,但我们keras输出的layer数据维度可能是3D的,所以维度不匹配。

那我们应该怎么样把不同类型的layer,正确的对接到一起组成模型呢?用keras.layers.Reshape就能很好的解决这样的问题。下面以一个实际案例进行阐述。

2. 案例

  1. 案例目标

我们这里的目标,是用keras构建一个模型,把Attention层接入到CNN层的后面。也就是实现Attention层对接到模型的任意位置。

  1. Attention

Attention层使用第三方的工具包:https://pypi.org/project/keras-self-attention/

  1. 训练数据

使用numpy的random生成随机的训练数据:

import numpy as np

num_classes = 6 # classification number
x_train = np.random.randn(100, 15, 20, 3) # x_train.shape = (100, 15, 20, 3)
y_train = np.random.randint(1, size=(100,num_classes)) # y_train.shape = (100, 6)
input_shape = x_train.shape[-3:]# (15, 20, 3)

数据的维度见代码中的注释。

  1. 构建模型

首先建立CNN模型,然后在CNN后面加上SeqSelfAttention层。具体代码如下:

from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras_self_attention import SeqSelfAttention
import keras

model = Sequential()
model.add(Conv2D(32, (2,11), activation='relu', padding='same', input_shape=input_shape))
model.add(Conv2D(32, (2,11), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 1)))
model.add(Conv2D(128, (2,7), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 1)))
model.add( SeqSelfAttention( attention_type=SeqSelfAttention.ATTENTION_TYPE_MUL) )
model.add(Flatten())
model.add(Dense(440, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
model.summary()                            
  1. 报错

上面的代码是会报错的,模型不能直接这样构建。报错信息如下:

    ValueError: Dimensions must be equal, but are 128 and 16 for '{{node seq_self_attention/MatMul}} = MatMul[T=DT_FLOAT, transpose_a=false, transpose_b=false](seq_self_attention/Reshape, seq_self_attention/Reshape_1)' with input shapes: [?,128], [16,16].

分析:这种错误,一般就是进行计算的多个矩阵的维度不匹配。比如两个矩阵相乘,必须要满足一定的维度要求。

我们也可以看到,报错的具体位置,是CNN后面加入Attention的那行代码:

---> 23 model.add( SeqSelfAttention( attention_type=SeqSelfAttention.ATTENTION_TYPE_MUL) )

3. 使用keras.layers.Reshape实现不同层的良好对接

根据如上报错(及报错的位置),可知是SeqSelfAttention上一层的输出数据维度,与SeqSelfAttention要求的输入数据维度不匹配。所以需要让数据进入SeqSelfAttention之前,做一个Reshape。

把模型部分的代码改为:

model = Sequential()
model.add(Conv2D(32, (2,11), activation='relu', padding='same', input_shape=input_shape))
model.add(Conv2D(32, (2,11), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 1)))
model.add(Conv2D(128, (2,7), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 1)))
model.add( keras.layers.Reshape((5,128)) )
model.add( SeqSelfAttention( attention_type=SeqSelfAttention.ATTENTION_TYPE_MUL) )
model.add(Flatten())
model.add(Dense(440, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
model.summary()

至于keras.layers.Reshape的具体shape,先随便设置一个值,这里设置的是(5,128)。运行程序,得到报错如下:

ValueError: total size of new array must be unchanged, input_shape = [3, 16, 128], output_shape = [5, 128]

从这个报错中,可以知道,输入Reshape的数据维度是[3, 16, 128],所以Reshape后的数据维度,也必须等于3x16x128。而我们刚才设置的(5,128),5x128是不等于3x16x128的。

把(5,128)改为(48,128),模型就够构建成功了。修改后能正确运行的完整代码如下:

from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras_self_attention import SeqSelfAttention
import keras
import numpy as np

num_classes = 6 # classification number
x_train = np.random.randn(100, 15, 20, 3) # x_train.shape = (100, 15, 20, 3)
y_train = np.random.randint(1, size=(100,num_classes)) # y_train.shape = (100, 6)
input_shape = x_train.shape[-3:]# (15, 20, 3)


model = Sequential()
model.add(Conv2D(32, (2,11), activation='relu', padding='same', input_shape=input_shape))
model.add(Conv2D(32, (2,11), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 1)))
model.add(Conv2D(128, (2,7), activation='relu', padding='same'))
model.add(MaxPooling2D(pool_size=(3, 3), strides=(2, 1)))
model.add( keras.layers.Reshape((48,128)) )
model.add( SeqSelfAttention( attention_type=SeqSelfAttention.ATTENTION_TYPE_MUL) )
model.add(Flatten())
model.add(Dense(440, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam')
model.summary()                            

4. 总结

本文提供了一种方式,从技术上,使用keras.layers.Reshape实现keras不同层的对接。但要注意的是,它毕竟改变了数据的维度,也就改变了数据的组成方式,这可能会对后续的识别产生影响。而这种影响必须要根据情况具体分析。

5. 参考

  1. https://keras.io/zh/layers/core/
  2. https://stackoverflow.com/questions/49628739/reshaping-keras-layers
  3. https://blog.csdn.net/u014535666/article/details/106183386
  • 15
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值