使用基于 ConvLSTM2D 的 Keras 模型从较低的图像估计高分辨率图像

Posted

技术标签:

【中文标题】使用基于 ConvLSTM2D 的 Keras 模型从较低的图像估计高分辨率图像【英文标题】:Estimating high resolution images from lower ones using a Keras model based on ConvLSTM2D 【发布时间】:2018-09-01 03:19:13 【问题描述】:

我正在尝试使用以下ConvLSTM2D 架构从低分辨率图像序列中估计高分辨率图像序列:

import numpy as np, scipy.ndimage, matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, ConvLSTM2D, MaxPooling2D, UpSampling2D
from sklearn.metrics import accuracy_score, confusion_matrix, cohen_kappa_score
from sklearn.preprocessing import MinMaxScaler, StandardScaler
np.random.seed(123)

raw = np.arange(96).reshape(8,3,4)
data1 = scipy.ndimage.zoom(raw, zoom=(1,100,100), order=1, mode='nearest') #low res
print (data1.shape)
#(8, 300, 400)

data2 = scipy.ndimage.zoom(raw, zoom=(1,100,100), order=3, mode='nearest') #high res
print (data2.shape)
#(8, 300, 400)

X_train = data1.reshape(data1.shape[0], 1, data1.shape[1], data1.shape[2], 1)
Y_train = data2.reshape(data2.shape[0], 1, data2.shape[1], data2.shape[2], 1)
#(samples,time, rows, cols, channels)

model = Sequential()
input_shape = (data1.shape[0], data1.shape[1], data1.shape[2], 1)
#samples, time, rows, cols, channels
model.add(ConvLSTM2D(16, kernel_size=(3,3), activation='sigmoid',padding='same',input_shape=input_shape))     
model.add(ConvLSTM2D(8, kernel_size=(3,3), activation='sigmoid',padding='same'))

print (model.summary())

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

model.fit(X_train, Y_train, 
          batch_size=1, epochs=10, verbose=1)

x,y = model.evaluate(X_train, Y_train, verbose=0)
print (x,y)

此声明将导致以下Value 错误:

ValueError: Input 0 is in compatible with layer conv_lst_m2d_2: expected ndim=5, found ndim=4

我该如何纠正这个ValueError?我认为问题出在输入形状上,但无法弄清楚到底出了什么问题。 请注意,输出也应该是图像序列,而不是分类结果。

【问题讨论】:

【参考方案1】:

发生这种情况是因为 LSTMs 需要时间数据,但您的第一个模型被声明为 many-to-one 模型,它输出形状为 (batch_size, 300, 400, 16) 的张量。也就是批量图片:

model.add(ConvLSTM2D(16, kernel_size=(3,3), activation='sigmoid',padding='same',input_shape=input_shape))     
model.add(ConvLSTM2D(8, kernel_size=(3,3), activation='sigmoid',padding='same'))

您希望输出为形状为 (batch_size, 8, 300, 400, 16) 的张量(即图像序列),以便第二个 LSTM 可以使用它们。解决这个问题的方法是在第一个 LSTM 定义中添加return_sequences

model.add(ConvLSTM2D(16, kernel_size=(3,3), activation='sigmoid',padding='same',input_shape=input_shape,
                     return_sequences=True))
model.add(ConvLSTM2D(8, kernel_size=(3,3), activation='sigmoid',padding='same'))

你提到了分类。如果你缩进的是对整个序列进行分类,那么你需要一个分类器:

model.add(ConvLSTM2D(16, kernel_size=(3,3), activation='sigmoid',padding='same',input_shape=input_shape,
                     return_sequences=True))
model.add(ConvLSTM2D(8, kernel_size=(3,3), activation='sigmoid',padding='same'))
model.add(GlobalAveragePooling2D())
model.add(Dense(10, activation='softmax'))  # output shape: (None, 10)

但是,如果您尝试对序列中的每个图像进行分类,那么您可以简单地使用TimeDistributed 重新应用分类器:

x = Input(shape=(300, 400, 8))
y = GlobalAveragePooling2D()(x)
y = Dense(10, activation='softmax')(y)
classifier = Model(inputs=x, outputs=y)

x = Input(shape=(data1.shape[0], data1.shape[1], data1.shape[2], 1))
y = ConvLSTM2D(16, kernel_size=(3, 3),
               activation='sigmoid',
               padding='same',
               return_sequences=True)(x)
y = ConvLSTM2D(8, kernel_size=(3, 3),
               activation='sigmoid',
               padding='same',
               return_sequences=True)(y)
y = TimeDistributed(classifier)(y)  # output shape: (None, 8, 10)

model = Model(inputs=x, outputs=y)

最后,看看 keras 存储库中的示例。有一个generative model using ConvLSTM2D。


编辑:从 data1 估计 data2...

如果我这次做对了,X_train 应该是 8 个 (300, 400, 1) 图像堆栈中的 1 个样本,而不是 1 个形状 (300, 400, 1) 图像堆栈中的 8 个样本. 如果是真的,那么:

X_train = data1.reshape(data1.shape[0], 1, data1.shape[1], data1.shape[2], 1)
Y_train = data2.reshape(data2.shape[0], 1, data2.shape[1], data2.shape[2], 1)

应更新为:

X_train = data1.reshape(1, data1.shape[0], data1.shape[1], data1.shape[2], 1)
Y_train = data2.reshape(1, data2.shape[0], data2.shape[1], data2.shape[2], 1)

另外,accuracy 在您的损失为 mse 时通常没有意义。您可以使用其他指标,例如 mae

现在你只需要更新你的模型以返回序列并在最后一层有一个单元(因为你试图估计的图像有一个通道):

model = Sequential()
input_shape = (data1.shape[0], data1.shape[1], data1.shape[2], 1)
model.add(ConvLSTM2D(16, kernel_size=(3, 3), activation='sigmoid', padding='same',
                     input_shape=input_shape,
                     return_sequences=True))
model.add(ConvLSTM2D(1, kernel_size=(3, 3), activation='sigmoid', padding='same',
                     return_sequences=True))

model.compile(loss='mse', optimizer='adam')

之后model.fit(X_train, Y_train, ...)会正常开始训练:

Using TensorFlow backend.
(8, 300, 400)
(8, 300, 400)
Epoch 1/10

1/1 [==============================] - 5s 5s/step - loss: 2993.8701
Epoch 2/10

1/1 [==============================] - 5s 5s/step - loss: 2992.4492
Epoch 3/10

1/1 [==============================] - 5s 5s/step - loss: 2991.4536
Epoch 4/10

1/1 [==============================] - 5s 5s/step - loss: 2989.8523

【讨论】:

我不太明白你的评论。 time 是时间暗淡,1 是输入通道数,是的。经过您声明的两个ConvLSTM2D 之后,输出形状将为(batch_size, time, 300, 400, 8),因为它正在返回序列并且最后一个LSTM 中有8 个单元。分类器接受形状为(batch_size, 300, 400, 8) 的输入,即成批的图像。包裹在TimeDistributed 周围,它将能够处理来自第二个ConvLSTM2D 的序列。 好的,所以有两个堆栈,你想融合它们。你想怎么做?您打算比较它们还是将它们连接起来? 哦,我明白了。我已经更新了答案以匹配您的图像估计任务。 这与您的原始问题略有不同,其中y_trainx_train 具有相同的形状。我建议从最后一层删除return_sequences=True(这会创建一个输出形状为(300, 400, 1) 的图像的模型)。请注意,y_train 需要从 (samples, 1, 300, 400, 1) 重新调整为 (samples, 300, 400, 1)。如果这不能带来好的结果,那么也许保留return_sequences=True并在最后添加一些Conv3d层,这将通过卷积LSTM输出的(8, 300, 400)特征空间产生图像。

以上是关于使用基于 ConvLSTM2D 的 Keras 模型从较低的图像估计高分辨率图像的主要内容,如果未能解决你的问题,请参考以下文章

Keras 中的“细胞类”是啥?

如何在 Keras 中定义 ConvLSTM 编码器解码器?

keras简单介绍与使用

基于Keras 的VGG16神经网络模型的Mnist数据集识别并使用GPU加速

tf.keras 和 tf.python.keras 有啥区别?

尝试在 Keras 中创建 BLSTM 网络时出现 TypeError