我正在尝试实现一个神经网络,将图像分类为两个离散类别之一。然而,问题是,它目前总是预测任何输入为 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 左右。
到目前为止我已经尝试过:
PCA 不同的数据集(来自 sklearn 的虹膜和来自 Coursera ML 课程的手写数字,两者的准确率都达到了 95% 左右)。但是,这两个都是批量处理的,所以我可以假设我的一般实现是正确的,但是我提取特征的方式或训练分类器的方式都有问题。尝试了 sklearn 的 SGDClassifier,它的性能并没有好多少,给了我大约 50% 的准确率。那么,这些功能有问题吗?
编辑: h2 的平均输出如下所示:
[0.5004899 0.45264441]
[0.50048522 0.47439413]
[0.50049019 0.46557124]
[0.50049261 0.45297816]
因此,所有验证示例的 sigmoid 输出都非常相似。
0
类,它可能很早就开始关注它们。
predict
调用中返回原始 h2
值。它们也都一样吗?
我的网络确实总是预测同一类。问题是什么?
我有过几次。虽然我目前懒得看你的代码,但我想我可以给出一些一般性的提示,这也可能对其他有相同症状但可能存在不同潜在问题的人有所帮助。
调试神经网络
拟合一项数据集
对于网络应该能够预测的每个类别,请尝试以下操作:
创建一个只有一个 i 类数据点的数据集。使网络适合此数据集。网络是否学会预测“第 i 类”?
如果这不起作用,则有四个可能的错误来源:
错误的训练算法:尝试一个较小的模型,打印很多在两者之间计算的值,看看它们是否符合您的期望。除以 0:在分母上加一个小数 0 的对数 / 负数:就像除以 0 数据:您的数据可能类型错误。例如,您的数据可能需要为 float32 类型,但实际上是整数。模型:也有可能您刚刚创建了一个无法预测您想要什么的模型。当您尝试更简单的模型时,应该会发现这一点。初始化/优化:根据模型,您的初始化和优化算法可能会发挥至关重要的作用。对于使用标准随机梯度下降的初学者,我会说随机初始化权重非常重要(每个权重都有不同的值)。 - 另见:这个问题/答案
学习曲线
有关详细信息,请参阅 sklearn。
https://i.stack.imgur.com/mhAUB.png
这个想法是从一个很小的训练数据集(可能只有一个项目)开始。那么模型应该能够完美地拟合数据。如果这可行,您将制作一个稍大的数据集。你的训练误差应该会在某个时候略微上升。这揭示了您的模型对数据建模的能力。
数据分析
检查其他类出现的频率。如果一个类别支配其他类别(例如,一个类别占数据的 99.9%),这是一个问题。寻找“异常值检测”技术。
更多的
学习率:如果您的网络没有改善并且仅比随机机会稍微好一点,请尝试降低学习率。对于计算机视觉,通常使用 0.001 的学习率/工作。如果您使用 Adam 作为优化器,这也是相关的。
预处理:确保对训练和测试使用相同的预处理。您可能会看到混淆矩阵的差异(请参阅此问题)
常见错误
这是受 reddit 启发的:
你忘了应用预处理
垂死的 ReLU
学习率太小/太大
最后一层的激活函数错误:你的目标不是总和吗? -> 不要使用 softmax 目标的单个元素是负数 -> 不要使用 Softmax、ReLU、Sigmoid。 tanh 可能是一个选择
你的目标不是总和吗? -> 不要使用 softmax
目标的单个元素是否定的 -> 不要使用 Softmax、ReLU、Sigmoid。 tanh 可能是一个选择
网络太深:你没有训练。首先尝试一个更简单的神经网络。
非常不平衡的数据:您可能需要研究不平衡学习
经过一个半星期的研究,我想我明白了问题所在。代码本身没有任何问题。阻止我的实现成功分类的唯一两个问题是花费的学习时间和正确选择学习率/正则化参数。
我现在已经运行了一些学习程序,它已经将准确率提高到 75%,尽管仍有很大的改进空间。
我也发生了同样的事情。我有一个不平衡的数据集(0 类和 1 类之间的样本分布分别约为 66%-33%),并且在第一次迭代后,网络总是为所有样本输出 0.0
。
我的问题只是学习率太高。将其切换为 1e-05
解决了该问题。
更一般地说,我建议在参数更新之前打印:
你的净输出(一批)
对应标签(同批次)
损失值(在同一批次上)可以逐个样本或汇总。
然后在参数更新后检查相同的三项。您应该在下一批中看到净输出的逐渐变化。当我的学习率太高时,已经在第二次迭代中,对于批次中的所有样本,净输出将射向所有 1.0
或所有 0.0
。
我也发生了同样的事情。我的在 deeplearning4j
JAVA
库中用于图像分类。它不断为每次测试提供最后一个训练文件夹的最终输出。我能够通过降低学习率来解决它。
可以使用的方法:
降低学习率。 (第一个我的是 0.01 - 降低到 1e-4 并且它起作用了)增加批量大小(有时随机梯度下降不起作用,那么你可以尝试提供更多的批量大小(32,64,128,256,..)洗牌训练数据
我遇到了模型总是预测相同标签的问题。它困扰了我一个星期。最后,我通过将 RELU 替换为其他激活函数来解决它。RELU 会导致“Dying ReLU”问题。
在我解决问题之前。我试过:
检查正负采样率,从 1:25 到 1:3。但它不起作用改变批量大小和学习率和其他损失。但它不起作用
最后我发现将学习率从 0.005 降低到 0.0002 已经有效。
我也发生了同样的事情。该模型仅预测 7 类 CNN 的一类。我试图改变激活函数、批量大小但没有任何效果。然后改变学习率也对我有用。
opt = keras.optimizers.Adam(learning_rate=1e-06)
正如你所看到的,我不得不选择一个非常低的学习率。我的训练样本数为 5250,验证样本数为 1575。
以防其他人遇到这个问题。我的采用 deeplearning4j
Lenet(CNN) 架构,它不断为每次测试提供最后一个训练文件夹的最终输出。我能够通过 increasing my batchsize
和 shuffling the training data
解决它,因此每批至少包含来自多个文件夹的样本。我的数据类的批次大小为 1,实际上是 dangerous
。
编辑:尽管我最近观察到的另一件事是每个班级的训练样本集有限,尽管 dataset
很大。 eg 训练一个 neural-network
来识别 human faces
,但对于 1 person
最多只能说 2 个不同的面孔,这意味着当数据集由 10,000 persons
组成,因此总共有 20,000 faces
的 dataset
。对于 10,000 persons
,更好的 dataset
将是 1000 个不同的 faces
,因此 dataset
总共为 10,000,000 faces
.如果您希望避免将数据过度拟合到一个类,这是相对必要的,这样您的 network
就可以轻松概括并产生更好的预测。
我也有同样的问题,我通过使用 ResNet50 的迁移学习进行二进制分类,我能够通过替换来解决它:
Dense(output_dim=2048, activation= 'relu')
和
Dense(output_dim=128, activation= 'relu')
并且还通过删除 Keras 增强并重新训练 RestNet50 的最后一层
在尝试了许多解决方案之后,我发现问题出在预测阶段,而不是训练或模型架构。我用于预测的方法在所有情况下都显示为零,即使我的验证准确度相对较高,因为这条线:
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
)
TOPUP 答案真的对我有用。我的情况是,当我使用大型数据集(超过 400 万个样本)训练 bert4reco 模型时,acc 和 log_loss 在整个时期始终保持在 0.5 和 0.8 之间(花费 8 小时,我每 100 步打印一次结果)。然后我使用了一个非常小的数据集和一个更小的模型,终于成功了!模型开始学习一些东西,acc 和 log_loss 开始增加并在 300 个 epoch 后达到收敛!
最后,TOPUP 答案是这类问题的一个很好的清单。有时,如果您在训练开始时看不到任何变化,那么您的模型可能需要很长时间才能真正学到一些东西。最好用户 mini dataset 来断言,然后你可以等待它学习或使用一些有效的设备,如 GPU 或 TPU
tanh
了吗?你还有什么期待? (您总是可以设计自己的;有时线性也是一个不错的选择)