我很难绕过CNN背后的数学,以及我应该如何修改神经网络层之间的输出形状。
我正在尝试对kaggle https://www.kaggle.com/c/carvana-image-masking-challenge进行carvana图像屏蔽挑战。换句话说,我试图建立一个神经网络,给定汽车图片,可以识别该图像中汽车的边界,并将其从背景的其余部分中裁剪出来。
所以我的输入都是图像,宽度= 959像素,高度= 640像素。我的输入数组的形状是(159,640,959,3),其中159表示输入数组总共保存159个图像的事实。 我创建的目标是具有640行和959列(每个像素的条目)的矩阵,使用布尔值来表示相应的像素是否是汽车边界内的汽车。目标数据的形状为(159,640,959),其中159可能代表目标保存159个图像的事实
我创建了一个过早构造的卷积网络(我只是说,使用的滤波器非常少)。该架构的代码就在这里。
nn = Sequential()
nn.add(Conv2D(8,(3,3), input_shape = (IMG_HEIGHT, IMG_WIDTH, 3), activation = 'relu', padding = 'same'))
nn.add(Conv2D(8, (3,3), activation='relu', padding='same'))
nn.add(Dense(1, activation='softmax'))
摘要()显示以下内容:
# Summary:
Layer (type) Output Shape Param #
=================================================================
conv2d_1 (Conv2D) (None, 640, 959, 8) 224
_________________________________________________________________
conv2d_2 (Conv2D) (None, 640, 959, 8) 584
_________________________________________________________________
dense_1 (Dense) (None, 640, 959, 6) 54
=================================================================
Total params: 862
Trainable params: 862
而我所困扰的错误只是......
ValueError:检查目标时出错:期望dense_1有4个维度,但得到的数组有形状(159,640,959)
目前我实际上不确定如何修改此代码以使其正常工作并通过此错误。我对最后一层应该如何具有4个维度感到困惑。根据Keras的总结,这个输出实际上确实有4个维度,但其中一个维度被标记为无。如果输出不应该具有(640,959)的形状,就像每个目标图像一样......我真的不知道输出的形状应该是什么。 我只是很难把我之前学到的关于卷积网络的知识放到实际的代码中。我无法克服这个错误,而且我正在努力弄清楚如何。我没有做正确的事情......
编辑:最初说这些图像的形状为440px X 959px。这是不正确的,它实际上是640px X 959px。对我来说真的很不方便。
答案 0 :(得分:0)
Dense
上的documentation并不是最清晰的,但从描述输入和输出形状的部分可以清楚地看出。
注意:如果图层的输入的等级大于2,则在使用
kernel
的初始点积之前将其展平。...
输入形状
nD张量形状:
(batch_size, ..., input_dim)
。最常见的情况是形状为(batch_size, input_dim)
的2D输入。输出形状
nD张量形状:
(batch_size, ..., units)
。例如,对于形状为(batch_size, input_dim)
的2D输入,输出的形状为(batch_size, units)
。
这是非常令人困惑的,因为它讨论了如何更高级别的张量将首先被平展(这使得您认为Dense(1)
的整体输出对于批处理中的每个示例都是纯粹的标量值),但是当你通过summary()
的打印输出演示,它保持了张量的相同中间维度。
因此,如果您提供(None, 640, 959, 8)
的输入,则表示Dense
会将最终维度视为完整连接的输入,并将处理指定的640x959位置中的每个单位通过内部维度作为单独的输出神经元...
所以,如果您的网络是这样的:
nn = Sequential()
nn.add(Conv2D(8, (3,3), input_shape = (640, 959, 3), activation='relu', padding='same'))
nn.add(Conv2D(8, (3,3), activation='relu', padding='same'))
nn.add(Dense(1, activation='softmax'))
然后最终的输出形状将是
(None, 640, 959, 1)
也就是说,640x959网格中的每个输出“像素”(i,j)被计算为来自前一层的点(i,j)处的8个不同卷积通道的密集组合。
有多种方法可以实现相同的功能,例如将通道尺寸从8减1到1的1x1卷积也会产生相同的输出形状,具有类似的层,
Conv2D(1, (1,1), activation='relu', padding='same')
或者你可以参考你正在进行的特定Kaggle比赛的"naive Keras" example,它使用了这个:
model = Sequential()
model.add( Conv2D(16, 3, activation='relu', padding='same', input_shape=(320, 480, 12) ) )
model.add( Conv2D(32, 3, activation='relu', padding='same') )
model.add( Conv2D(1, 5, activation='sigmoid', padding='same') )
除了所有这些之外,我们还有两个问题,即您为我们打印的代码中的数据维度不正确。
一个是您声明图像高度为440,但是keras输出显示为640。
另一个是你的最终Dense图层在输出中有6个通道,但你提供的相应代码只能导致1个通道。
因此,您使用的代码与您在此处粘贴的代码之间仍然存在一些不匹配,这使我们无法看到维度问题的完整问题。
例如,此网络的损耗层应该将汽车位置像素的地面实际位掩码与最后一层的640x959 Dense
输出进行比较(一旦解决了显示6个通道的奇怪问题)在输出中。)
但您报告的错误消息是
ValueError:检查目标时出错:期望dense_1有4个维度,但得到的数组有形状(159,640,959)
这表明可能需要将一批目标数据重新整形为一个形状(159, 640, 959, 1)
的张量,只是为了与Dense
图层出来的形状保持一致。