神经网络总是预测相同的类别。

83

我正在尝试实现一个神经网络,将图像分类为两个离散的类别之一。然而问题在于,它目前对任何输入始终预测为0,而我不确定原因。

这是我的特征提取方法:

def extract(file):
    # Resize and subtract mean pixel
    img = cv2.resize(cv2.imread(file), (224, 224)).astype(np.float32)
    img[:, :, 0] -= 103.939
    img[:, :, 1] -= 116.779
    img[:, :, 2] -= 123.68
    # Normalize features
    img = (img.flatten() - np.mean(img)) / np.std(img)

    return np.array([img])

这是我的梯度下降算法:

def fit(x, y, t1, t2):
    """Training routine"""
    ils = x.shape[1] if len(x.shape) > 1 else 1
    labels = len(set(y))

    if t1 is None or t2 is None:
        t1 = randweights(ils, 10)
        t2 = randweights(10, labels)

    params = np.concatenate([t1.reshape(-1), t2.reshape(-1)])
    res = grad(params, ils, 10, labels, x, y)
    params -= 0.1 * res

    return unpack(params, ils, 10, labels)

以下是前向传播和反向传播(梯度)的内容:

def forward(x, theta1, theta2):
    """Forward propagation"""

    m = x.shape[0]

    # Forward prop
    a1 = np.vstack((np.ones([1, m]), x.T))
    z2 = np.dot(theta1, a1)

    a2 = np.vstack((np.ones([1, m]), sigmoid(z2)))
    a3 = sigmoid(np.dot(theta2, a2))

    return (a1, a2, a3, z2, m)

def grad(params, ils, hls, labels, x, Y, lmbda=0.01):
    """Compute gradient for hypothesis Theta"""

    theta1, theta2 = unpack(params, ils, hls, labels)

    a1, a2, a3, z2, m = forward(x, theta1, theta2)
    d3 = a3 - Y.T
    print('Current error: {}'.format(np.mean(np.abs(d3))))

    d2 = np.dot(theta2.T, d3) * (np.vstack([np.ones([1, m]), sigmoid_prime(z2)]))
    d3 = d3.T
    d2 = d2[1:, :].T

    t1_grad = np.dot(d2.T, a1.T)
    t2_grad = np.dot(d3.T, a2.T)

    theta1[0] = np.zeros([1, theta1.shape[1]])
    theta2[0] = np.zeros([1, theta2.shape[1]])

    t1_grad = t1_grad + (lmbda / m) * theta1
    t2_grad = t2_grad + (lmbda / m) * theta2

    return np.concatenate([t1_grad.reshape(-1), t2_grad.reshape(-1)])

这是我的预测函数:

def predict(theta1, theta2, x):
    """Predict output using learned weights"""
    m = x.shape[0]

    h1 = sigmoid(np.hstack((np.ones([m, 1]), x)).dot(theta1.T))
    h2 = sigmoid(np.hstack((np.ones([m, 1]), h1)).dot(theta2.T))

    return h2.argmax(axis=1)

我可以看到,随着每次迭代,错误率逐渐降低,通常在1.26e-05左右收敛。
我尝试过以下方法:
  1. 主成分分析(PCA)
  2. 不同的数据集(sklearn中的Iris和Coursera ML课程中的手写数字,都能够达到约95%的准确率)。然而,这两个数据集都是批处理的,因此我可以假设我的一般实现是正确的,但是提取特征的方式或训练分类器的方式可能存在问题。
  3. 尝试了sklearn的SGDClassifier,但表现并不好,只给出了大约50%的准确率。那么特征有问题吗?
编辑: h2的平均输出如下:
[0.5004899   0.45264441]
[0.50048522  0.47439413]
[0.50049019  0.46557124]
[0.50049261  0.45297816]

因此,所有验证示例的Sigmoid输出非常相似。


4
你是否在对训练集进行随机化?如果前几批数据中0类别的数量很多,那么模型可能会过早地将注意力集中在这些数据上。请确认一下。 - cdeterman
只是提供信息:我尝试随机化输入数据,结果仍然相同。 - Yurii Dolhikh
1
尝试从最终的“predict”调用中返回原始的“h2”值。它们也都相同吗? - cdeterman
这种现象有一个名称吗?即模型“学习”独立于输入而产生相同结果的现象? - Viktor
显示剩余3条评论
10个回答

144
我的网络总是预测相同的类别,这是什么问题?
我遇到过几次这种情况。虽然我目前懒得查看你的代码,但我认为我可以给出一些常见的提示,这些提示可能也有助于其他人解决相同的症状,但可能存在不同的根本问题。
神经网络调试
对于每个类i,网络应该能够预测,请尝试以下操作:
1. 创建一个仅包含i类数据点的数据集。 2. 将网络拟合到此数据集。 3. 网络是否学会预测“i类”?
如果这样做还不起作用,则可能有四个可能的错误来源: 错误的训练算法:尝试使用较小的模型,在计算中打印大量值,并查看这些值是否符合您的预期。
  1. 除以0:将一个小数加到分母上。
  2. 对数为0/负数:与除以0类似。
数据:您的数据可能具有错误的类型。例如,您的数据可能需要是float32 类型,但实际上是整数。 模型:您创建的模型可能无法准确预测您想要的结果。当您尝试使用更简单的模型时,这一点应该会显现出来。 初始化/优化:根据模型,您的初始化和优化算法可能起着至关重要的作用。对于使用标准随机梯度下降的初学者,我建议随机初始化权重(每个权重都有不同的值)。- 参见:此问题/答案

学习曲线

详细信息请参见sklearn

Learning Curve showing the training error / test error curves to approach each other

这个想法是从一个微小的训练数据集(可能只有一个项目)开始。然后模型应该能够完美地拟合数据。如果这样做成功了,你可以使用稍微大一点的数据集。你的训练误差应该在某个时候略微上升。这揭示了你的模型对数据建模的能力。

数据分析

检查其他类别出现的频率。如果一个类别占据了主导地位(例如,一个类别占据了99.9%的数据),这是一个问题。寻找"异常检测"技术。

更多

  • 学习率:如果你的网络没有得到改进,只比随机猜测略好,尝试降低学习率。对于计算机视觉,通常使用/工作的学习率为0.001。如果你使用Adam作为优化器,这也是相关的。
  • 预处理:确保你在训练和测试中使用相同的预处理。你可能会看到混淆矩阵中的差异(参见this question)。

常见错误

这是受reddit启发的:

  • 您忘记应用预处理
  • Dying ReLU
  • 学习率太小/太大
  • 最后一层激活函数错误:
    • 您的目标不是总和为一吗?-> 不要使用softmax
    • 您的目标单个元素为负数-> 不要使用Softmax,ReLU,Sigmoid。tanh可能是一个选项
  • 网络过深: 您无法进行训练。首先尝试更简单的神经网络。
  • 数据极不平衡: 您可能需要查看imbalanced-learn

1
您的目标中单个元素为负数 -> 不要使用Softmax、ReLU、Sigmoid。tanh可能是一个选择。在这种情况下,请您建议正确的激活函数是什么? - omilus
1
你看到我建议使用“tanh”了吗?你还期待什么呢?(你总是可以设计自己的函数;有时线性也是一个不错的选择) - Martin Thoma
2
我看错了。我以为tanh在不使用的函数列表中。也许它应该是Tanh,因为它是句子中的第一个词。 - omilus
1
谢谢您,先生。我遇到了学习率的问题,您救了我。作为一般的调试或模型构建步骤,我建议任何人都从一些琐碎的东西开始,并且只改变一个东西来构建模型,同时关注指标。如果有太多的变量,找出问题就会变得更加棘手。 - Yash Jakhotiya
谢谢。在我的情况下,大而深的模型无法学习。然而,在切换到较小的模型后,它可以学习数据。 - Suen
显示剩余2条评论

32
在经过一周半的研究后,我认为我理解了问题所在。代码本身没有问题。唯一导致我的实现无法成功分类的两个问题是学习时间和正确选择学习率/正则化参数。
我已经让学习程序运行了一段时间,已经达到了75%的准确率,尽管仍有很大的改进空间。

2
你能告诉我在你注意到它之前和之后运行了多久吗?我自己也遇到了一些问题,但即使过了更长的时间,它似乎仍然没有自我纠正,仍然只是一遍又一遍地预测相同的类别。 - BotMaster3000
我也遇到了同样的问题。 - Amr Mahmoud
1
我也遇到了同样的问题。尝试使用调度程序设置学习率,并进行更多次数的训练,在500个epochs之后成功将数据过拟合,达到100%的准确性。 - Asmita Poddar
3
如果有人遇到相同的问题,你需要花更多时间调整学习率——这就是答案。 - John Stud
通常是学习率过高或过低导致了这个问题吗? - md1630

18

我也遇到过同样的问题。我有一个不平衡的数据集(类别0和1之间约为66%-33%的样本分布),在第一次迭代后,网络总是对所有样本输出0.0

我的问题很简单,就是学习率过高。将其切换为1e-05解决了这个问题。

更一般地说,我建议在参数更新之前打印以下内容:

  • 您的网络输出(针对一个批次)
  • 相应的标签(针对同一批次)
  • 损失值(在同一批次上)的值,可以逐个样本或汇总。

然后,在参数更新后检查相同的三个项目。在下一个批次中,你应该看到网络输出的逐渐变化。当我的学习率太高时,已经在第二次迭代中,网络输出就会对于批次中的所有样本变成所有的1.0或所有的0.0


8
我也遇到了类似的问题。我的问题出现在用于图像分类的 deeplearning4j JAVA 库中。每次测试时,它都会给出最后一个训练文件夹的最终输出。我通过降低学习率来解决了这个问题。
可以尝试以下方法:
  1. 降低学习率。(首先我的学习率为 0.01,将其降低到 1e-4 后问题得以解决)
  2. 增加批量大小(有时随机梯度下降无法正常工作,您可以尝试使用更多的批次大小(32、64、128、256 等等))
  3. 对训练数据进行随机排列

在我的情况下,我用了你的解决方案解决了问题。我将学习率从0.001改为0.0001。谢谢。 - MVS_beginner
感谢您的建议。我的模型中批量大小是问题所在。 - revy
当批处理大小过小时,模型将无法收敛。 - wolfe

3

我也遇到了同样的问题。这个模型只能为七类卷积神经网络预测一个类别。我尝试更改激活函数、批量大小,但都没有效果。最后,我改变了学习率,这对我也起了作用。

opt = keras.optimizers.Adam(learning_rate=1e-06)

正如您所看到的,我不得不选择非常低的学习率。我的训练样本数量为5250个,验证样本数量为1575个。


2
我遇到了一个问题,模型总是预测相同的标签。这让我困惑了一个星期。最后,我通过将RELU替换为其他激活函数来解决它。RELU会导致“Dying ReLU”问题。
在解决问题之前,我尝试了以下方法:
1. 检查正负样本比率,从1:25到1:3。但是没有效果。 2. 更改批量大小、学习率和其他损失。但是没有效果。
最终,我发现将学习率从0.005降低到0.0002已经有效。

1

如果其他人遇到这个问题,我的问题是与 deeplearning4jLenet(CNN) 架构有关,它一直为每次测试给出最后一个训练文件夹的最终输出。 我通过 增加批次大小打乱训练数据 来解决它,以便每个批次至少包含来自多个文件夹的样本。我的数据类批次大小为 1,这真的很 危险

编辑: 最近我观察到的另一件事是,尽管有一个大型数据集,但每个类别的训练样本集合却很有限。例如,训练一个神经网络来识别人脸,但每个最多只有2张不同的脸,而数据集由10,000组成,因此总共有20,000人脸。更好的数据集应该是每个1000张不同的,共10,000,000人脸数据集。这是相对必要的,如果你想避免将数据过度拟合到一个类别中,以便你的网络可以轻松地推广并产生更好的预测。

1
在尝试了许多解决方案后,我发现问题不在训练或模型架构上,而是在预测阶段。我用于预测的方法对所有情况都显示零,尽管我有相对较高的验证准确性,因为这一行代码:
predicted_class_indices=np.argmax(scores,axis=1)

如果你正在处理二元分类问题,请尝试以下方法:
predict = model.predict(
    validation_generator, steps=None, callbacks=None, max_queue_size=10, workers=1,
    use_multiprocessing=False, verbose=0
)

1

我也遇到了同样的问题,我使用ResNet50进行迁移学习进行二分类,通过替换以下代码,我成功地解决了这个问题:

Dense(output_dim=2048, activation= 'relu')

使用

Dense(output_dim=128, activation= 'relu')

另外,通过移除Keras增强和重新训练RestNet50的最后几层


0

TOPUP 的答案对我真的很有帮助。我的情况是,在使用大规模数据集(4百万+样本)训练 bert4reco 模型时,整个 epoch 中的 acc 和 log_loss 始终保持在 0.5 和 0.8 之间(耗时 8 小时,我每 100 步打印一次结果)。然后我使用了一个非常小的数据集和更小的模型,最终它成功了!模型开始学习东西,acc 和 log_loss 开始增加,并在 300 个 epoch 后达到收敛。

总之,TOPUP 的答案是这类问题的良好检查清单。有时,如果您在训练开始时看不到任何变化,可能需要花费很长时间才能让您的模型真正学习到某些东西。最好使用迷你数据集进行验证,然后等待它学习或使用一些有效的设备,如 GPU 或 TPU。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接