我正在尝试训练一个神经网络将整数转换为罗马数字,但我的损失值始终无法低于0.3。你能帮我找出我做错的地方吗?
我的输入是0到4000之间的整数。我尝试了三种处理方式:1. 直接使用,2. 标准化为z值,3. 进行最小-最大缩放。
对于输出y,我有21个二进制类别。它们看起来像这样:
{'MMM': 0, 'MM': 0, 'CM': 0, 'M': 0, 'CD': 0, 'D': 0, 'CCC': 0, 'CC': 0, 'XC': 0, 'C': 0, 'XL': 0, 'L': 0, 'XXX': 0, 'XX': 0, 'IX': 0, 'X': 0, 'IV': 0, 'V': 0, 'III': 0, 'II': 0, 'I': 0}
这个模板允许我明确地表示1到3,999之间的任何整数。例如:
17 会变为:
{'MMM': 0, 'MM': 0, 'CM': 0, 'M': 0, 'CD': 0, 'D': 0, 'CCC': 0, 'CC': 0, 'XC': 0, 'C': 0, 'XL': 0, 'L': 0, 'XXX': 0, 'XX': 0, 'IX': 0, 'X': 1, 'IV': 0, 'V': 1, 'III': 0, 'II': 1, 'I': 0}
而3885 会变为:
{'MMM': 1, 'MM': 0, 'CM': 0, 'M': 0, 'CD': 0, 'D': 1, 'CCC': 1, 'CC': 0, 'XC': 0, 'C': 0, 'XL': 0, 'L': 1, 'XXX': 1, 'XX': 0, 'IX': 0, 'X': 0, 'IV': 0, 'V': 1, 'III': 0, 'II': 0, 'I': 0}
我的模型结构如下:
model = tf.keras.models.Sequential()model.add(Dense(56, input_shape=(1,), activation='relu'))model.add(Dense(56, activation='relu'))model.add(Dense(48, activation='relu'))model.add(Dense(21))
我还尝试了使用elu
激活函数,并尝试了稍微增加或减少神经元数量。我还尝试增加了最多两层网络。
我尝试了0.1到0.001之间的学习率。
opt = Adam(learning_rate=0.1)
对于损失函数,我使用了二元交叉熵。
loss = tf.keras.losses.BinaryCrossentropy(from_logits=True)model.compile(optimizer=opt, loss=loss)
我还尝试在最后一层添加sigmoid
激活函数,同时设置from_logits=False
。
这些尝试都没有奏效。损失值始终无法低于0.3。
我已经训练了高达5000个epoch,批次大小从500到2000不等。
h = model.fit(scaled_x, y, batch_size=512, epochs=400, verbose=1, shuffle=True)
完整的Google Colab工作簿在这里:
https://colab.research.google.com/drive/15InEGmaURdGqtGIWvXwlQ8kGM1NfRl_V?usp=sharing
你认为损失值无法低于0.3的原因是什么?你建议我接下来尝试什么?
回答:
我建议你重新添加sigmoid激活函数,并取消logits设置。
你还应该使用某种准确率指标,因为单独的损失值并不能告诉你太多信息,除了原始的进展情况。这可以由Keras自动推断:
model.compile(optimizer=opt, loss=loss, metrics=['accuracy'])
我还建议你创建一个验证集来测试模型在未见数据上的表现。这样可以防止模型过度学习训练集的模式(过拟合),因为这会导致模型在未见数据上的表现更差:
h = model.fit(scaled_x, y, validation_data=(val_x, val_y), batch_size=512, epochs=400, verbose=1, shuffle=True)
请注意,无论你传递什么作为度量标准,实际上都不会影响模型的学习过程,它只是为了人类可读的输出。影响模型如何判断性能以及权重更新步骤大小的,是损失函数。所以你可以考虑使用不同的损失函数:
!pip install tensorflow_addonsimport tensorflow_addons as tfaloss = tfa.losses.SigmoidFocalCrossEntropy()
我之前在处理类似这样的多标签问题时,使用上述损失函数取得了不错的效果。
另一个想法是引入学习率调度器,当在一定数量的epoch内monitor
没有变化时,自动降低学习率:
reduce_lr = ReduceLROnPlateau(monitor='val_acc',min_delta=0.005 ,patience=2, factor=0.1, verbose=1, mode='max')
我们监控的是验证准确率,但你可以指定’val_loss’、’loss’等。
我们等待2个epoch,如果val_acc没有增加(注意mode=’max’,所以它检查的是增加),增加了半个百分点(min_delta=0.005
),那么学习率将降低10%(factor=0.1
)。
然后你将这个作为回调函数传递给fit函数:
h = model.fit(scaled_x, y, validation_data=(val_x, val_y), batch_size=512, epochs=400, verbose=1, shuffle=True, callbacks=[reduce_lr])
更新
你关于准确率可能具有误导性的观点是完全正确的。对于多标签分类,我通常使用top_k_categorical_accuracy,所以当k=5时(据我记得是某个谷歌论文推荐的),如果真实标签出现在前5个预测中,模型就被认为是正确的。但请记住,这不会实际影响模型的学习过程,它只会改变你对模型是否需要调整的解释。
要使用它,你需要在compile
函数的metrics
参数中添加它:
metrics=[tf.keras.metrics.TopKCategoricalAccuracy(k=5)]
PS 我运行了你修改后的代码,准确率一度达到93%,然而这毫无意义,你必须使用一些验证数据来查看模型在未见数据上的表现,因为这正是创建模型的初衷。它可能在训练集上达到93%,但在验证集上只有85%。
当你完成了所有这些并且快要崩溃时,我建议你查看Weights & Biases,特别是称为“sweep”的过程。虽然有一点学习曲线,但我将其用于所有机器学习项目。它允许你为任何参数设置一系列值,例如learning_rate = [0.1,0.001,0.0001等],并会多次运行模型以搜索最佳的超参数集。