【问题标题】:Neural net fails on toy dataset神经网络在玩具数据集上失败
【发布时间】:2018-10-22 17:40:47
【问题描述】:

我创建了以下玩具数据集:

我正在尝试使用 keras 中的神经网络来预测类别:

model = Sequential()
model.add(Dense(units=2, activation='sigmoid', input_shape= (nr_feats,)))
model.add(Dense(units=nr_classes, activation='softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

nr_featsnr_classes 设置为 2。 神经网络只能以 50% 的准确率预测返回全 1 或全 2。使用逻辑回归可以达到 100% 的准确率。

我找不到这里出了什么问题。

如果你想快速尝试一下,我已经将notebook 上传到 github。

编辑 1

我大幅增加了 epoch 的数量,准确度最终从 epoch 72 的 0.5 开始提高,并在 epoch 98 收敛到 1.0。 对于这样一个简单的数据集,这似乎仍然非常缓慢。

我知道最好使用带有 sigmoid 激活的单个输出神经元,但我更想了解为什么它不适用于两个输出神经元和 softmax 激活。

我按如下方式预处理我的数据框:

from sklearn.preprocessing import LabelEncoder

x_train = df_train.iloc[:,0:-1].values
y_train = df_train.iloc[:, -1]

nr_feats = x_train.shape[1]
nr_classes = y_train.nunique()

label_enc = LabelEncoder()
label_enc.fit(y_train)

y_train = keras.utils.to_categorical(label_enc.transform(y_train), nr_classes)

培训和评估:

model.fit(x_train, y_train, epochs=500, batch_size=32, verbose=True)
accuracy_score(model.predict_classes(x_train),  df_train.iloc[:, -1].values)

编辑 2

在将输出层更改为具有 sigmoid 激活的单个神经元并按照模式建议使用 binary_crossentropy 损失后,准确度在 200 个 epoch 内仍保持在 0.5,并在 1.0 100 个 epoch 后收敛。

【问题讨论】:

  • 您是否尝试过增加epochs 的数量,例如增加到 20、50 或 100?由于您的 keras 模型非常小(即参数很少),我怀疑需要更多的时间才能看到准确性的任何提高,因为损失正在减少,因此正在发生一些进展。
  • @today 损失正在减少,因为它收敛到根据我的答案预测所有 1 个类。这给出了单类猜测的交叉熵损失。没有任何神经网络会花费大量时间来收敛一个如此简单的示例。
  • @today 感谢您的帮助。我之前增加了一点,现在我进一步增加了它们,它最终从 epoch 72 的 0.5 开始改善,并在 epoch 98 收敛到 1.0。对于这样一个简单的数据集,这似乎非常慢。
  • @modesitt 我没有注意到标签是12。你说的对。它应该被编码为01。正如您所提到的,最好使用一个具有一个单元的密集层,最后一层的激活函数为sigmoid。但是我仍然怀疑,在解决了这些问题之后,考虑到如此小的网络和(相对大量的)随机输入数据,您无法获得高于 60% 的准确度。在我看来,这需要超过 5 个 epoch。
  • @modesitt 不!实际上,您对标签的看法是错误的。它们已经被编码为一个热向量,因此在这方面没有问题。

标签: python machine-learning neural-network keras classification


【解决方案1】:

注意:如果您想了解真正的原因,请阅读我回答末尾的“更新”部分。在这种情况下,我提到的另外两个原因仅在学习率设置为较低值(小于1e-3)时才有效。


我整理了一些代码。它和你的很相似,但我只是稍微清理了一下,让自己变得更简单了。正如你所看到的,我使用了一个密集层,其中最后一层有一个带有sigmoid 激活函数的单元,只需将​​优化器从adam 更改为rmsprop(这并不重要,你可以使用@987654326 @如果你喜欢):

import numpy as np
import random

# generate random data with two features
n_samples = 200
n_feats = 2

cls0 = np.random.uniform(low=0.2, high=0.4, size=(n_samples,n_feats))
cls1 = np.random.uniform(low=0.5, high=0.7, size=(n_samples,n_feats))
x_train = np.concatenate((cls0, cls1))
y_train = np.concatenate((np.zeros((n_samples,)), np.ones((n_samples,))))

# shuffle data because all negatives (i.e. class "0") are first
# and then all positives (i.e. class "1")
indices = np.arange(x_train.shape[0])
np.random.shuffle(indices)
x_train = x_train[indices]
y_train = y_train[indices]

from keras.models import Sequential
from keras.layers import Dense


model = Sequential()
model.add(Dense(2, activation='sigmoid', input_shape=(n_feats,)))
model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy',
              optimizer='rmsprop',
              metrics=['accuracy'])

model.summary()

model.fit(x_train, y_train, epochs=5, batch_size=32, verbose=True)

这是输出:

Layer (type)                 Output Shape              Param #   
=================================================================
dense_25 (Dense)             (None, 2)                 6         
_________________________________________________________________
dense_26 (Dense)             (None, 1)                 3         
=================================================================
Total params: 9
Trainable params: 9
Non-trainable params: 0
_________________________________________________________________
Epoch 1/5
400/400 [==============================] - 0s 966us/step - loss: 0.7013 - acc: 0.5000
Epoch 2/5
400/400 [==============================] - 0s 143us/step - loss: 0.6998 - acc: 0.5000
Epoch 3/5
400/400 [==============================] - 0s 137us/step - loss: 0.6986 - acc: 0.5000
Epoch 4/5
400/400 [==============================] - 0s 149us/step - loss: 0.6975 - acc: 0.5000
Epoch 5/5
400/400 [==============================] - 0s 132us/step - loss: 0.6966 - acc: 0.5000

如您所见,准确率永远不会从 50% 增加。如果您将 epoch 数增加到 50 会怎样:

Layer (type)                 Output Shape              Param #   
=================================================================
dense_35 (Dense)             (None, 2)                 6         
_________________________________________________________________
dense_36 (Dense)             (None, 1)                 3         
=================================================================
Total params: 9
Trainable params: 9
Non-trainable params: 0
_________________________________________________________________
Epoch 1/50
400/400 [==============================] - 0s 1ms/step - loss: 0.6925 - acc: 0.5000
Epoch 2/50
400/400 [==============================] - 0s 136us/step - loss: 0.6902 - acc: 0.5000
Epoch 3/50
400/400 [==============================] - 0s 133us/step - loss: 0.6884 - acc: 0.5000
Epoch 4/50
400/400 [==============================] - 0s 160us/step - loss: 0.6866 - acc: 0.5000
Epoch 5/50
400/400 [==============================] - 0s 140us/step - loss: 0.6848 - acc: 0.5000
Epoch 6/50
400/400 [==============================] - 0s 168us/step - loss: 0.6832 - acc: 0.5000
Epoch 7/50
400/400 [==============================] - 0s 154us/step - loss: 0.6817 - acc: 0.5000
Epoch 8/50
400/400 [==============================] - 0s 146us/step - loss: 0.6802 - acc: 0.5000
Epoch 9/50
400/400 [==============================] - 0s 161us/step - loss: 0.6789 - acc: 0.5000
Epoch 10/50
400/400 [==============================] - 0s 140us/step - loss: 0.6778 - acc: 0.5000
Epoch 11/50
400/400 [==============================] - 0s 177us/step - loss: 0.6766 - acc: 0.5000
Epoch 12/50
400/400 [==============================] - 0s 180us/step - loss: 0.6755 - acc: 0.5000
Epoch 13/50
400/400 [==============================] - 0s 165us/step - loss: 0.6746 - acc: 0.5000
Epoch 14/50
400/400 [==============================] - 0s 128us/step - loss: 0.6736 - acc: 0.5000
Epoch 15/50
400/400 [==============================] - 0s 125us/step - loss: 0.6728 - acc: 0.5000
Epoch 16/50
400/400 [==============================] - 0s 165us/step - loss: 0.6718 - acc: 0.5000
Epoch 17/50
400/400 [==============================] - 0s 161us/step - loss: 0.6710 - acc: 0.5000
Epoch 18/50
400/400 [==============================] - 0s 170us/step - loss: 0.6702 - acc: 0.5000
Epoch 19/50
400/400 [==============================] - 0s 122us/step - loss: 0.6694 - acc: 0.5000
Epoch 20/50
400/400 [==============================] - 0s 110us/step - loss: 0.6686 - acc: 0.5000
Epoch 21/50
400/400 [==============================] - 0s 142us/step - loss: 0.6676 - acc: 0.5000
Epoch 22/50
400/400 [==============================] - 0s 142us/step - loss: 0.6667 - acc: 0.5000
Epoch 23/50
400/400 [==============================] - 0s 149us/step - loss: 0.6659 - acc: 0.5000
Epoch 24/50
400/400 [==============================] - 0s 125us/step - loss: 0.6651 - acc: 0.5000
Epoch 25/50
400/400 [==============================] - 0s 134us/step - loss: 0.6643 - acc: 0.5000
Epoch 26/50
400/400 [==============================] - 0s 143us/step - loss: 0.6634 - acc: 0.5000
Epoch 27/50
400/400 [==============================] - 0s 137us/step - loss: 0.6625 - acc: 0.5000
Epoch 28/50
400/400 [==============================] - 0s 131us/step - loss: 0.6616 - acc: 0.5025
Epoch 29/50
400/400 [==============================] - 0s 119us/step - loss: 0.6608 - acc: 0.5100
Epoch 30/50
400/400 [==============================] - 0s 143us/step - loss: 0.6601 - acc: 0.5025
Epoch 31/50
400/400 [==============================] - 0s 148us/step - loss: 0.6593 - acc: 0.5350
Epoch 32/50
400/400 [==============================] - 0s 161us/step - loss: 0.6584 - acc: 0.5325
Epoch 33/50
400/400 [==============================] - 0s 152us/step - loss: 0.6576 - acc: 0.5700
Epoch 34/50
400/400 [==============================] - 0s 128us/step - loss: 0.6568 - acc: 0.5850
Epoch 35/50
400/400 [==============================] - 0s 155us/step - loss: 0.6560 - acc: 0.5975
Epoch 36/50
400/400 [==============================] - 0s 136us/step - loss: 0.6552 - acc: 0.6425
Epoch 37/50
400/400 [==============================] - 0s 140us/step - loss: 0.6544 - acc: 0.6150
Epoch 38/50
400/400 [==============================] - 0s 120us/step - loss: 0.6538 - acc: 0.6375
Epoch 39/50
400/400 [==============================] - 0s 140us/step - loss: 0.6531 - acc: 0.6725
Epoch 40/50
400/400 [==============================] - 0s 135us/step - loss: 0.6523 - acc: 0.6750
Epoch 41/50
400/400 [==============================] - 0s 136us/step - loss: 0.6515 - acc: 0.7300
Epoch 42/50
400/400 [==============================] - 0s 126us/step - loss: 0.6505 - acc: 0.7450
Epoch 43/50
400/400 [==============================] - 0s 141us/step - loss: 0.6496 - acc: 0.7425
Epoch 44/50
400/400 [==============================] - 0s 162us/step - loss: 0.6489 - acc: 0.7675
Epoch 45/50
400/400 [==============================] - 0s 161us/step - loss: 0.6480 - acc: 0.7775
Epoch 46/50
400/400 [==============================] - 0s 126us/step - loss: 0.6473 - acc: 0.7575
Epoch 47/50
400/400 [==============================] - 0s 124us/step - loss: 0.6464 - acc: 0.7625
Epoch 48/50
400/400 [==============================] - 0s 130us/step - loss: 0.6455 - acc: 0.7950
Epoch 49/50
400/400 [==============================] - 0s 191us/step - loss: 0.6445 - acc: 0.8100
Epoch 50/50
400/400 [==============================] - 0s 163us/step - loss: 0.6435 - acc: 0.8625

准确度开始提高(请注意,如果您多次训练此模型,每次可能需要不同数量的 epoch 才能达到可接受的准确度,从 10 到 100 个 epoch)。

此外,在我的实验中,我注意到增加第一个密集层中的单元数量,例如增加到 5 或 10 个单元,可以更快地训练模型(即快速收敛)。

为什么需要这么多 epoch?

我认为是因为这两个原因(综合):

1) 尽管这两个类很容易分离,但您的数据是由随机样本组成的,并且

2)数据点的数量相比神经网络的大小(即可训练参数的数量,在上面的示例代码中为9)比较大。

因此,模型需要更多的 epoch 来学习权重。好像模型非常受限,需要越来越多的经验才能正确找到合适的权重。作为证据,只需尝试增加第一个密集层中的单元数量即可。每次尝试训练此模型时,几乎可以保证在少于 10 个 epoch 的情况下达到 +90% 的准确度。在这里你增加了容量,因此模型收敛(即训练)更快(应该注意,如果容量太高或者你训练模型太多时期,它就会开始过度拟合。你应该有一个验证方案来监控这个问题)。

旁注:

不要将high 参数设置为小于numpy.random.uniformlow 参数的数字,因为根据documentation,在这种情况下结果将是“官方未定义”。

更新:

这里更重要的一点(也许是本场景中最重要的一点)是优化器的学习率。如果学习率太低,模型收敛缓慢。尝试提高学习率,您可以看到不到 5 个 epoch 就达到了 100% 的准确率:

from keras import optimizers

model.compile(loss='binary_crossentropy',
              optimizer=optimizers.RMSprop(lr=1e-1),
              metrics=['accuracy'])

# or you may use adam
model.compile(loss='binary_crossentropy',
              optimizer=optimizers.Adam(lr=1e-1),
              metrics=['accuracy'])

【讨论】:

  • 谢谢,优化器似乎对默认学习率过于谨慎。你能把你的答案编辑成学习率部分吗?这将使其他人更容易快速了解正在发生的事情。那我就接受你的回答。
  • 我在开头添加了一条注释,将读者指向“更新”部分。我仍然认为,正如我在回答中提到的,在使用低学习率的情况下,其他两个原因是相关的(在这种情况下)。使用低学习率但增加隐藏层中的单元数量(即使模型更大)会导致更快的收敛。
【解决方案2】:

问题是您的标签是 12,而不是 0 和 1。Keras 在看到 2 时不会引发错误,但它无法预测 2

从所有 y 值中减去 1。附带说明一下,在深度学习中,使用 1 个neuronsigmoid 进行二元分类(0 或 1)与使用 2 个 softmax 的类是很常见的。最后,使用binary_crossentropy 来处理二元分类问题的损失。

【讨论】:

  • 感谢您的建议,在将它们传递给 keras 模型之前,我已经对我的类标签进行了一次热编码。我也听说最好使用具有 sigmoid 激活函数的单个输出神经元,但是我在问题中将输出保留为 softmax,因为我很困惑为什么它不能以这种方式工作。
猜你喜欢
  • 1970-01-01
  • 2012-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-07
  • 1970-01-01
  • 2017-08-18
  • 2019-09-13
相关资源
最近更新 更多