简单的Keras神经网络无法学习

3
我正在尝试使用Keras复制神经网络与深度学习中的一些示例,但我在基于第1章架构训练网络时遇到了问题。目标是对MNIST数据集中的手写数字进行分类。
架构:
  • 784个输入(代表MNIST图像中的28 * 28个像素)
  • 30个神经元的隐藏层
  • 10个神经元的输出层
  • 权重和偏差从均值为0,标准差为1的高斯分布初始化。
  • 损失/成本函数为平均平方误差。
  • 优化器为随机梯度下降。
超参数:
  • 学习率=3.0
  • 批处理大小=10
  • epochs = 30
我的代码:
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.optimizers import SGD
from keras.initializers import RandomNormal


# import data
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# input image dimensions
img_rows, img_cols = 28, 28

x_train = x_train.reshape(x_train.shape[0], img_rows * img_cols)
x_test = x_test.reshape(x_test.shape[0], img_rows * img_cols)
input_shape = (img_rows * img_cols,)

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
num_classes = 10
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('y_train shape:', y_train.shape)

# Construct model
# 784 * 30 * 10
# Normal distribution for weights/biases
# Stochastic Gradient Descent optimizer
# Mean squared error loss (cost function)
model = Sequential()
layer1 = Dense(30,
               input_shape=input_shape,
               kernel_initializer=RandomNormal(stddev=1),
               bias_initializer=RandomNormal(stddev=1))
model.add(layer1)
layer2 = Dense(10,
               kernel_initializer=RandomNormal(stddev=1),
               bias_initializer=RandomNormal(stddev=1))
model.add(layer2)
print('Layer 1 input shape: ', layer1.input_shape)
print('Layer 1 output shape: ', layer1.output_shape)
print('Layer 2 input shape: ', layer2.input_shape)
print('Layer 2 output shape: ', layer2.output_shape)

model.summary()
model.compile(optimizer=SGD(lr=3.0),
              loss='mean_squared_error',
              metrics=['accuracy'])

# Train 
model.fit(x_train,
          y_train,
          batch_size=10,
          epochs=30,
          verbose=2)

# Run on test data and output results
result = model.evaluate(x_test,
                        y_test,
                        verbose=1)
print('Test loss: ', result[0])
print('Test accuracy: ', result[1])

输出(使用Python 3.6和TensorFlow后端):

Using TensorFlow backend.
x_train shape: (60000, 784)
60000 train samples
10000 test samples
y_train shape: (60000, 10)
Layer 1 input shape:  (None, 784)
Layer 1 output shape:  (None, 30)
Layer 2 input shape:  (None, 30)
Layer 2 output shape:  (None, 10)
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_1 (Dense)              (None, 30)                23550     
_________________________________________________________________
dense_2 (Dense)              (None, 10)                310       
=================================================================
Total params: 23,860
Trainable params: 23,860
Non-trainable params: 0
_________________________________________________________________
Epoch 1/30
 - 7s - loss: nan - acc: 0.0987
Epoch 2/30
 - 7s - loss: nan - acc: 0.0987

(重复30个时期)

Epoch 30/30
 - 6s - loss: nan - acc: 0.0987
10000/10000 [==============================] - 0s 22us/step
Test loss:  nan
Test accuracy:  0.098

如您所见,这个网络根本没有学习,我不确定原因。就我所知,形状看起来还不错。是什么阻止了网络的学习?
(顺便说一下,我知道交叉熵损失和softmax输出层会更好;然而,根据链接的书籍,它们似乎并不是必需的。第1章中手动实现的网络成功地学习了;我正在尝试复制它,然后再继续。)

为什么要使用 MSE?你这里有一个分类问题。 - seralouk
@serafeim 正如我在上一段所说的,我正在尝试复制链接书中手动实现的网络,该网络使用均方误差(尽管书中称其为二次代价函数)。 - DylanSp
2个回答

3
你需要指定每个层的激活函数。对于每个层,应该像这样:
layer2 = Dense(10,
           activation='sigmoid',
           kernel_initializer=RandomNormal(stddev=1),
           bias_initializer=RandomNormal(stddev=1))

注意,我在这里指定了激活参数。对于最后一层,由于有多个类别,应该使用activation="softmax"
另一件要考虑的事情是,分类(与回归相反)最好使用熵损失。因此,您可能需要将model.compile中的损失值更改为loss='categorical_crossentropy'。但是,这并不是必要的,即使使用mean_square_error损失,您仍将获得结果。
如果您仍然获得损失的nan值,则可以尝试更改SGD的学习率。
我使用您展示的脚本,并将第一层的激活函数更改为sigmoid,第二层更改为softmax,获得了测试准确率为0.9425

没有指定激活函数确实是问题所在;将两个层都设置为sigmoid后,测试准确率达到了0.9468。感谢您的帮助! - DylanSp

2
在分类问题中选择均方误差作为损失函数确实很奇怪,我不确定链接的书籍章节中介绍性质是一个好的理由。尽管如此:
  1. 您的学习率 lr 为 3.0,非常大;尝试使用至少0.1甚至更低的值。
  2. 当前您的层完全缺乏激活函数;尝试在所有层上添加 activation='sigmoid'(因为您明确想要避免在最后一层使用softmax)。
  3. 您在初始化器中使用的 stddev=1 值再次非常大;尝试使用范围在0.05(默认值)内的值。另外,标准做法是将偏置初始化为零。
最好先从 Keras MNIST MLP示例开始,并根据您的学习需求进行调整(涉及层数、激活函数等)。

缺乏激活函数是问题所在。话虽如此,如果超参数与典型值相差太远,我正在重新考虑使用这本书! - DylanSp
@DylanSp 好的,确实MNIST拥有一个声誉,即无论您使用什么超参数,它都将收敛到一个不错的解决方案... ;) - desertnaut
有基本的理论原因吗?还是说MNIST只是一个相对简单的问题? - DylanSp
@DylanSp 深度学习在理论方面仍然声名狼藉,主要由经验结果驱动(即“这是一些有效的东西,尽管我们不知道它为什么有效...”) - desertnaut
1
我同意@desertnaut关于学习率的观点。在我的看法中,将其设置为“3”是一个巨大的错误。你是从提供的书中获取这个值吗?因为如果是的话,那么它肯定存在严重的问题。 - Marcin Możejko

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