我一直在尝试用CNN构建一个图像分类器。我的数据集中有2300张图像,分为两类:男性和女性。这是我使用的模型:
early_stopping = EarlyStopping(min_delta = 0.001, patience = 30, restore_best_weights = True)
model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(256, (3, 3), input_shape=X.shape[1:], activation = 'relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Conv2D(256, (3, 3), input_shape=X.shape[1:], activation = 'relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.BatchNormalization())
model.add(tf.keras.layers.Flatten()) # this converts our 3D feature maps to 1D feature vectors
model.add(tf.keras.layers.Dense(64))
model.add(tf.keras.layers.Dense(1, activation='softmax'))
model.compile(loss='binary_crossentropy',
optimizer='adam',
metrics=['accuracy'])
h= model.fit(xtrain, ytrain, validation_data=(xval, yval), batch_size=32, epochs=30, callbacks = [early_stopping], verbose = 0)
该模型的准确度为0.501897,损失7.595693(模型在每个历元中都停留在这些数字上),但如果我用Sigmoid代替Softmax激活,准确度约为0.98,损失0.06。为什么Softmax会发生这样奇怪的事情?我能找到的所有信息都是这两种激活相似,softmax甚至更好,但我找不到任何关于这种异常的信息。如果有人能解释问题出在哪里,我会很高兴。
结果摘要:
- a)具有Softmax激活功能的CNN->精度~0.50,损耗~7.60
- b) 具有Sigmoid激活功能的CNN->精度~0.98,损耗~0.06
TLDR
更新:
现在我还看到您在Softmax中只使用了一个输出神经元,您将无法在二进制分类中捕获第二类使用Softmax,您需要在输出层中定义K个神经元-其中K是您想要预测的类的数量。而对于Sigmoid:1输出神经元就足以进行二元分类。
简而言之,当对两个类使用softmax时,这应该会在代码中发生变化:
#use 2 neurons with softmax
model.add(tf.keras.layers.Dense(2, activation='softmax'))
另外:
当执行二进制分类时,sigmoid函数更适合,因为与更广义的softmax函数(当您有K>2个类时,通常用于多类预测)相比,它在计算上更有效。
进一步阅读:
所选激活函数的某些属性
如果上面的简短答案对你来说还不够,我可以和你分享我从研究NNs激活函数中学到的一些东西,简而言之:
首先,让我们明确术语激活和激活功能
激活(α):是神经元的状态。隐藏层或输出层中神经元的状态将通过来自前一层的输入信号的加权和来量化
激活函数f(alpha):是一个将激活转换为神经元信号的函数。通常是一种非线性且可微的函数,例如sigmoid函数。许多应用程序&研究已经应用于sigmoid函数(见Bengio&Courville,2016,p.67及其后)。在整个神经网络中,大多数情况下使用相同的激活函数,但也可以使用多个(例如,不同层中的不同激活函数)。
现在到激活功能的效果:
激活函数的选择会对神经网络的学习产生巨大影响(正如您在示例中所看到的)。从历史上看,使用S形函数是很常见的,因为它是描述饱和神经元的一个很好的函数。如今,尤其是在细胞神经网络的其他激活函数中,也只有部分线性激活函数(如relu)比sigmoid函数更受欢迎。有许多不同的函数,仅举几个例子:sigmoid、tanh、relu、prelu、elu、maxout、max、argmax、softmax等。
现在我们只比较sigmoid、relu/maxout和softmax:
# pseudo code / formula
sigmoid = f(alpha) = 1 / (1 + exp(-alpha))
relu = f(alpha) = max(0,alpha)
maxout = f(alpha) = max(alpha1, alpha2)
softmax = f(alpha_j) = alpha_j / sum_K(alpha_k)
S形:
二进制分类中的- 优选用于输出层
- 值的范围可以在[0,1]之间,适用于概率解释(+)
- 饱和神经元可以消除梯度(-)
- 不以零为中心(-)
- exp()计算成本很高(-)
relu:
- 阳性区域无饱和神经元(+)
- 计算成本较低(+)
- 不以零为中心(-)
- 负区饱和神经元(-)
最大值:
relu(+)的- 正属性
- 将每个神经元的参数数量增加一倍,通常需要增加学习工作量(-)
softmax:
- 可以看作sigmoid函数的推广
- 主要用作多类预测问题中的输出激活函数
- 数值范围在[0,1]之间,适用于概率解释(+)
- 由于exp()项(-),计算成本更高
一些很好的参考资料供进一步阅读:
- http://cs231n.stanford.edu/2020/syllabus
- http://deeplearningbook.org(Bengio&Courtville)
- https://arxiv.org/pdf/1811.03378.pdf
- https://papers.nips.cc/paper/2018/file/6ecbdd6ec859d284dc13885a37ce8d81-Paper.pdf
看到这些不同结果的原因是输出层的大小——它是1个神经元。
根据定义,Softmax需要一个以上的输出神经元才能有意义。1 Softmax神经元将始终输出1(查找公式并思考)。这就是为什么你看到大约50%的准确率,因为你的网络总是预测类1。
Sigmoid没有这个问题,可以输出任何东西,这就是它训练的原因。
如果你想测试softmax,你必须为每个类制作一个输出神经元,然后";一个热编码";你的ytrain和yval(查找一个热门编码以获得更多解释)。在您的情况下,这意味着:标签0->[1,0],标签1->[0,1]。您可以看到,其中一个的索引对类进行了编码。我不确定,但在这种情况下,我相信你会使用分类交叉熵。我无法从文档中得出结论,但在我看来,二进制交叉熵期望1个输出神经元为0或1(其中Sigmoid是正确的激活),而分类交叉熵期望每个类有一个输出神经元,其中Softmax是有意义的。您甚至可以将Sigmoid用于多输出情况,但它并不常见。
简言之,在我看来,二进制xentropy期望由1神经元的值编码的类,而分类xentropy期望由哪个输出神经元最活跃编码的类。