如何防止卷积神经网络变得懒惰?我在使用KERAS训练后得到一个“懒惰的CNN”。无论输入是什么,输出都是恒定的。你认为问题出在哪里?
我试图重复NVIDIA的《自主驾驶汽车的端到端学习》实验论文。当然,我没有真实的汽车,而是一个Udacity的模拟器。模拟器生成汽车前景的图像。
CNN接收图像,并给出保持汽车在轨道上的转向角度。游戏规则是让模拟汽车安全地在轨道上行驶。这并不难。
奇怪的是,有时在使用KERAS训练后,我得到一个懒惰的CNN,它给出恒定的转向角度。模拟汽车会偏离轨道,但CNN的输出没有变化。特别是当层数加深时,例如论文中的CNN。
如果我使用这样的CNN,训练后可以得到一个有用的模型。
model = Sequential()model.add(Lambda(lambda x: x/255.0 - 0.5, input_shape = (160,320,3)))model.add(Cropping2D(cropping=((70,25),(0,0))))model.add(Conv2D(24, 5, strides=(2, 2)))model.add(Activation('relu'))model.add(Conv2D(36, 5, strides=(2, 2)))model.add(Activation('relu'))model.add(Conv2D(48, 5, strides=(2, 2)))model.add(Activation('relu'))model.add(Flatten())model.add(Dense(50))model.add(Activation('sigmoid'))model.add(Dense(10))model.add(Activation('sigmoid'))model.add(Dense(1))
但是,如果我使用更深的CNN,我更有可能得到一个懒惰的CNN。特别是,如果我使用类似于NVIDIA的CNN,几乎每次训练后我都会得到一个懒惰的CNN。
model = Sequential()model.add(Lambda(lambda x: x/255.0 - 0.5, input_shape = (160,320,3)))model.add(Cropping2D(cropping=((70,25),(0,0))))model.add(Conv2D(24, 5, strides=(2, 2)))model.add(Activation('relu'))model.add(Conv2D(36, 5, strides=(2, 2)))model.add(Activation('relu'))model.add(Conv2D(48, 5, strides=(2, 2)))model.add(Activation('relu'))model.add(Conv2D(64, 3, strides=(1, 1)))model.add(Activation('relu'))model.add(Conv2D(64, 3, strides=(1, 1)))model.add(Activation('relu'))model.add(Flatten())model.add(Dense(1164))model.add(Activation('sigmoid'))model.add(Dense(100))model.add(Activation('sigmoid'))model.add(Dense(50))model.add(Activation('sigmoid'))model.add(Dense(10))model.add(Activation('sigmoid'))model.add(Dense(1))
我对卷积层使用‘relu’激活函数,而全连接层的激活函数是‘sigmoid’。我尝试更改激活函数,但没有效果。
这是我的分析。我不认为我的程序有bug,因为我可以用相同的代码和一个更简单的CNN成功驾驶汽车。我认为原因在于模拟器或神经网络的结构。在真实的自主驾驶汽车中,训练信号,即转向角度,应该包含噪声;因此,驾驶员在真实道路上永远不会保持方向盘不动。但在模拟器中,训练信号非常干净。几乎60%的转向角度为零。优化器可以通过将CNN的输出接近零来轻松完成任务。优化器似乎也很懒惰。然而,当我们真正希望这个CNN输出一些东西时,它也给出零。因此,我为这些零转向角度添加了少量噪声。我得到懒惰CNN的几率变小了,但并没有消失。
你对我分析的看法如何?还有其他我可以使用的策略吗?我想知道在CNN研究的漫长历史中是否已经解决了类似的问题。
资源:
相关文件已上传到GitHub。你可以使用这些文件重复整个实验。
回答:
我无法运行你的模型,因为问题和GitHub仓库都没有包含数据。这就是为什么我对我的答案有90%的把握。
但我认为你的网络的主要问题是密集层后的sigmoid
激活函数。我假设,当只有两个这样的层时,它会训练得很好,但四个就太多了。
不幸的是,NVIDIA的《自主驾驶汽车的端到端学习》论文没有明确指定,但如今默认的激活函数不再是sigmoid
(曾经是),而是relu
。如果你对为什么会这样感兴趣,可以查看这个讨论。所以我提出的解决方案是尝试这个模型:
model = Sequential()model.add(Lambda(lambda x: x/255.0 - 0.5, input_shape = (160,320,3)))model.add(Cropping2D(cropping=((70,25),(0,0))))model.add(Conv2D(24, (5, 5), strides=(2, 2), activation="relu"))model.add(Conv2D(36, (5, 5), strides=(2, 2), activation="relu"))model.add(Conv2D(48, (5, 5), strides=(2, 2), activation="relu"))model.add(Conv2D(64, (3, 3), strides=(1, 1), activation="relu"))model.add(Conv2D(64, (3, 3), strides=(1, 1), activation="relu"))model.add(Flatten())model.add(Dense(1164, activation="relu"))model.add(Dense(100, activation="relu"))model.add(Dense(50, activation="relu"))model.add(Dense(10, activation="relu"))model.add(Dense(1))
它模仿了NVIDIA的网络架构,并且不会受到梯度消失的影响。