赞
踩
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
就能很好的解决这样的问题。下面以一个实际案例进行阐述。
我们这里的目标,是用keras构建一个模型,把Attention层接入到CNN层的后面。也就是实现Attention层对接到模型的任意位置。
Attention层使用第三方的工具包:https://pypi.org/project/keras-self-attention/
使用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)
数据的维度见代码中的注释。
首先建立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()
上面的代码是会报错的,模型不能直接这样构建。报错信息如下:
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) )
根据如上报错(及报错的位置),可知是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()
本文提供了一种方式,从技术上,使用keras.layers.Reshape
实现keras不同层的对接。但要注意的是,它毕竟改变了数据的维度,也就改变了数据的组成方式,这可能会对后续的识别产生影响。而这种影响必须要根据情况具体分析。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。