制作自定义 Keras 层时不能使用未知的输入尺寸(批量大小)
Posted
技术标签:
【中文标题】制作自定义 Keras 层时不能使用未知的输入尺寸(批量大小)【英文标题】:Can't Use Unknown Input Dims (Batch Size) When Making Custom Keras Layer 【发布时间】:2019-12-27 02:31:37 【问题描述】:我正在尝试在 Keras(后端 TensorFlow)中构建一个自定义层,该层跨卷积层的过滤器执行 KMeans 聚类。在构建这一层的逻辑时,我遍历了批量大小,但 Keras/TensorFlow 似乎不允许这种情况发生,因为批量大小在运行时是未知的维度。
我试图追踪错误消息,它引导我找到两个文件:keras/engine/training.py 和 keras/engine/training_utils.py,据我所知,错误源于问题使用定义为 NoneType
的 ndim
变量,因为在编译模型时没有已知的批量大小。
在查看各种 *** 和 GitHub 资源时,我还没有看到任何关于如何处理 Keras/TensorFlow 拒绝未知批量大小参数的解决方案。
作为参考,这里是示例代码:
import numpy as np
import tensorflow as tf
import keras
from sklearn.cluster import KMeans
class KMeansLayer(keras.layers.Layer):
def __init__(self, clusters=8, n_init=5, trainable=False, **kwargs):
super(KMeansLayer, self).__init__(**kwargs)
self.clusters = clusters
self.n_init = n_init
def build(self, input_shape):
self.output_s = (input_shape[0],input_shape[1], input_shape[2],1)
self.depth = input_shape[3]
self.built=True
def call(self, inputs):
def KMeansFunc(input_tens,clusters=self.clusters,n_init=self.n_init):
base_mat = np.zeros((input_tens.shape[0],input_tens.shape[1],input_tens.shape[2]))
for frame in range(input_tens.shape[0]):
init_mat = np.zeros((input_tens.shape[1]*input_tens.shape[2]))
# print(init_mat.shape)
reshape_mat = np.reshape(input_tens[frame],(input_tens.shape[1]*input_tens.shape[2],input_tens.shape[3]))
# print(reshape_mat.shape)
kmeans_init = KMeans(n_clusters=clusters, n_init=n_init)
class_pred = kmeans_init.fit_predict(reshape_mat)
for clust in range(self.clusters):
init_mat[class_pred==clust] = np.mean(reshape_mat[class_pred==clust],axis=1)
init_mat[class_pred==clust] = np.mean(reshape_mat[class_pred==clust],None)
# print(base_mat.shape)
base_mat[frame]=np.reshape(init_mat,(input_tens.shape[1],input_tens.shape[2]))
return np.expand_dims(base_mat,axis=-1).astype('float32')
output = tf.py_func(KMeansFunc,[inputs],tf.float32)
return output
def compute_output_shape(self, input_shape):
return self.output_s
input_1 = keras.Input(shape=(28,28,1), name='input_1', dtype='float32')
conv_1 = keras.layers.Conv2D(filters=20, kernel_size=3, strides=1, padding='same', data_format='channels_last', activation='elu', kernel_initializer='glorot_uniform')(input_1)
pool_1 = keras.layers.MaxPooling2D(pool_size=2, padding='same', data_format='channels_last')(conv_1)
up_conv_1 = keras.layers.SeparableConv2D(filters=20, kernel_size=2, strides=1, padding='same', data_format='channels_last', activation='elu', kernel_initializer='glorot_uniform')(pool_1)
up_1 = keras.layers.UpSampling2D(size=(2, 2), interpolation='bilinear')(up_conv_1)
conv_2 = keras.layers.Conv2D(filters=20, kernel_size=3, strides=1, padding='same', data_format='channels_last', activation='elu', kernel_initializer='glorot_uniform')(up_1)
conv_3 = keras.layers.Conv2D(filters=3, kernel_size=3, strides=1, padding='same', data_format='channels_last', activation='elu', kernel_initializer='glorot_uniform')(conv_2)
kmeans_out = KMeansLayer(clusters=8,n_init=5)(conv_3)
model = keras.Model(inputs=[input_1], outputs=kmeans_out)
keras.utils.plot_model(model, show_shapes=True)
model.compile(optimizer='adam', loss='mse', metrics=['mse'])
从上面的代码中可以看出,如果我有一个大小为(batch_size,28,28,3)
的自定义层的输入,我希望创建大小为(batch_size,28,28,1)
的输出。
运行上述代码导致的错误是:
Traceback (most recent call last):
File "example_error_file.py", line 64, in <module>
model.compile(optimizer='adam', loss='mse', metrics=['mse'])
File "~/fluoro/fenv/lib/python3.6/site-packages/keras/engine/training.py", line 347, in compile
sample_weight, mask)
File "~/fluoro/fenv/lib/python3.6/site-packages/keras/engine/training_utils.py", line 426, in weighted
axis=list(range(weight_ndim, ndim)))
TypeError: 'NoneType' object cannot be interpreted as an integer
我有两个主要问题:
在定义我的自定义 Keras 层时,我是否做错了什么? 在这种情况下,有什么方法可以强制 Keras 在不知道批量大小的情况下运行(这似乎是合理的)?TensorFlow 版本:1.7.0 Keras 版本:2.2.4 Python:3.6.6
【问题讨论】:
【参考方案1】:如果我们完全在 tensorflow 中查看这个事件,主要问题似乎是那个事件,因为直到运行时才知道形状,当 tensorflow 尝试编译这个图时,它会在整个循环过程中出现一些错误没有。
考虑使用 tf.function 或 autograph(如果您使用的是 tensorflow 1.14.0 或更高版本),而不是使用 tf.pyfunc。
或者,您可以使用 tf.scan 将函数应用于张量中的每个元素,其中每个元素由从维度 0 的原始张量解包的每个张量组成。
两者都应该工作!
【讨论】:
以上是关于制作自定义 Keras 层时不能使用未知的输入尺寸(批量大小)的主要内容,如果未能解决你的问题,请参考以下文章
使用自定义维度输入 tensorflow 或 keras 神经网络
无法向自定义损失输入数据:渴望执行函数的输入不能是 Keras 符号张量
tf.keras.Concatenate Graph 连接两个输入层时断开连接