为什么这个模型在 keras 中不收敛?

数据挖掘 Python 喀拉斯 收敛
2022-02-27 18:39:36

这个案例有一个潜在的故事,但我基本上把它归结为尽可能简单的可重现的例子。

基本上让我们认为我有多达 1000 个节点,每个节点由一个小的(本例是一个 3 单元向量)向量表示,我将这些节点连接起来并表示为一个填充的 3*1000 输入向量,需要找出哪个一个更合适。所以模型试图预测 1000 个浮点值,每个节点一个。

让我们想象对节点进行评分的函数是这个任意代码:

def score_vector(v):
  a, b, c = tuple(v)
  if a == 0 or b == 0 or a - c < 2:
    return float(Defs.INVALID_SCORE)
  return float(a * math.sqrt(a - c) / math.log(b + 2, 5))

基本上我的模型应该学习这个函数加上一个 argmax 来找到得分最高的节点。与我迄今为止解决的问题相比,这在我看来是一个非常简单的问题(但它也不同)。

所以我的问题是为什么这个模型不收敛?我认为这可能是由于它的可区分性,但真的有点迷失并开始怀疑我对 NN 的了解(这不是很多)。

这是重现代码:

import numpy as np
import math

from keras import Sequential, Input
from keras.layers import Flatten, Activation, Dense
from keras.optimizers import Adam

class Defs:
  VECTOR_SIZE=3
  NODE_COUNT=1000
  MAX_REAL_NODE_COUNT=400
  MIN_REAL_NODE_COUNT=20
  INVALID_SCORE=0

def score_vector(v):
  a, b, c = tuple(v)
  if a == 0 or b == 0 or a - c < 2:
    return float(Defs.INVALID_SCORE)
  return float(a * math.sqrt(a - c) / math.log(b + 2, 5))

def build_vector():
  a = np.random.randint(1, 100)
  c = np.random.randint(1, 50) if np.random.choice([False, True, True]) else 0
  b = 0 if c == 0 else np.random.randint(c, c*3)
  return [float(a), float(b), float(c)]

def build_vectorset_score():
  n = np.random.randint(Defs.MIN_REAL_NODE_COUNT, Defs.MAX_REAL_NODE_COUNT)
  vectorset = []
  for i in range(0, n):
    vectorset += build_vector()

  # pad it
  vectorset += [0. for i in range((Defs.NODE_COUNT-n) * Defs.VECTOR_SIZE)]
  scores = [score_vector(vectorset[i*Defs.VECTOR_SIZE:(i+1)*Defs.VECTOR_SIZE]) for i in range(0, Defs.NODE_COUNT)]
  index = np.argmax(scores)
  scores = [1. if index == i else 0. for i in range(0, len(scores))]
  return vectorset, scores

def build_model():
  model = Sequential()
  model.add(Dense(Defs.VECTOR_SIZE * Defs.NODE_COUNT, input_dim=Defs.VECTOR_SIZE * Defs.NODE_COUNT, activation='relu'))
  model.add(Dense(Defs.NODE_COUNT, activation='relu'))
  model.add(Dense(Defs.NODE_COUNT))
  model.add(Activation('softmax'))
  print(model.summary())
  model.compile(loss="categorical_crossentropy",
                optimizer=Adam(lr=0.001), metrics=['categorical_accuracy'])
  return model


if __name__ == '__main__':
  SAMPLE_SIZE = 1 * 1000
  X = []
  Y = []
  for i in range(0, SAMPLE_SIZE):
    x, y = build_vectorset_score()
    X.append(np.array(x))
    Y.append(np.array(y))
  model = build_model()
  model.fit(np.array(X),
                  np.array(Y), batch_size=100, epochs=200, verbose=1)
1个回答

我不完全确定你在用你的评分方程做什么,但你需要看的第一件事是你的损失函数。分类交叉熵用于多标签分类,您正在尝试预测浮点值。

所以,你应该让你的网络输出是一个单一的值(除非你的函数范围是(0,1),否则不要通过 sigmoid 压缩它)。你应该使用回归损失函数——我肯定会从均方误差开始。在此处查看回归示例(在回归预测下)以获取一些示例代码。

编辑:进一步讨论下面,网络无法预测最高值节点的索​​引,因为每个节点的分数与节点在被评分的 1000 个节点中的位置无关。由于最佳节点在每个位置的概率相同,因此没有要学习的最佳预测。