无法获得非常简单的Keras模型来学习

时间:2020-05-11 06:13:09

标签: python tensorflow keras

我刚刚开始自学Keras,我正在解决一些琐碎的挑战。我已经成功上手,并已经训练了一些简单的模型。但是,如果我对输入应用简单的线性变换,而其他情况并非如此,则模型会训练得很好,这是一个奇怪的情况。这是我正在使用的代码。其中一项学习,另一项则没有。该模型的目的是学习根据给定的出生年份和月份来确定某人是否是成年人。

from random import random
import numpy as np
from keras.models import Sequential
from keras.layers import Dense


X = [[1980 + int(39*random()), int(12*random())] for _ in range(0, 5000)]
y = [1 if (12 * (2020 - x[0]) + (5 - x[1])) > 216 else 0 for x in X]
# X = [[12 * (2020 -x[0]) + (5 - x[1])] for x in X]

X = np.array(X)
y = np.array(y)

model = Sequential()
layer = Dense(1, input_dim=2, kernel_initializer='random_normal', activation='sigmoid')
model.add(layer)
model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(X, y, epochs=1000, batch_size=100)

现在,该模型似乎根本无法学习。损失始终停留在0.68左右。

即使我使用以下方法重新缩放输入,事情也不会改善:

X = [[x[0] / 1000.0, x[1] / 12.0] for x in X]

但是,如果我在上面的代码中取消注释该行并将图层的输入尺寸更改为1,则模型会很好地收敛。当前模型也不应该收敛。我看不到我在做什么错。

为澄清起见,此处的输入基本上是出生的年份和月份。我们假设我们正在检查一个人是否在2020年5月成年。

-

编辑:

由于@catalina关于“多样性”的观点,我似乎找到了答案。由于输入变量都是整数,因此系统仅需学习一些实际示例。为了更好地定义渐变,您需要在要学习的表面上有更多点。因此,在这种情况下,如果我将代码的相关部分更改为以下内容:

X = [[1980 + int(39*random()), int(12*random())] for _ in range(0, 5000)]

# adding random noise to create "variety"
X = [[x[0] + random(), x[1] + random()] for x in X]
y = [1 if (12 * (2020 - x[0]) + (5 - x[1])) > 216 else 0 for x in X]

# approximately centering the inputs 
X = [[x[0] - 2002, x[1] - 6] for x in X]

模型收敛良好。我仍然不清楚为什么需要居中(没有它,它就不会收敛)。我怀疑这是因为在权重开始时,梯度会太平坦,从而很难找到移动的方向。

P.S。 -我绝对知道这不是NN的正确用例。但是我现在的尝试是理解底层机制,以至于我所做的一切都源于第一原理。例如,根据成员的建议,x [1]是“噪声”并且应完全删除,这一事实在这种情况下是不正确的。

2 个答案:

答案 0 :(得分:2)

有很多问题,例如网络的容量太小,难以确定阈值以构成真正困难的损失表面等等。

但是,我发现的要点是第一种情况的输入高度偏斜。

X = [[1980 + int(39*random()), int(12*random())] for _ in range(0, 5000)]

输入中的两个值极度倾斜,一个值比另一个值大150倍。

还有其他一些解决方法可以使网络融合,但是最简单的方法是重新调整输入范围。

例如,以下输入也很容易收敛。

X = [[1980 + int(39*random()), 1000 + int(12*random())] for _ in range(0, 5000)]

我唯一更改的是输入范围。

更新:

在您发表评论之后,我刚刚对数据进行了绘图,并意识到您的x 1对数据来说是纯噪声,这完全破坏了预测。

plt.plot(X[:100,0])
plt.plot(X[:100,1])
plt.plot(y[:100])
plt.legend(['x0', 'x1', 'y'])
plt.show()

一个人的年龄与出生月份无关,如您在图表中所见,该月份完全在预测中添加了纯噪声。如果一个人是成年人还是不仅仅取决于出生年份,那么出生月份将对该模型是一个完全的噪音,对于这样一个简单的模型,它无法消除该噪音,从而降低了性能。

但是,如果您以适当的比例增加网络容量,则可以从技术上使模型存储样本。

enter image description here

只需给出一些基本想法,

cnt = 0
idx = 0
for x in X:
  if x[1] == 1:
    print(x)
    print(y[idx])
    cnt += 1
    if cnt == 100:
      break
  idx += 1
[1987, 1]
1
[1986, 1]
1
[2001, 1]
1
[1983, 1]
1
[2011, 1]
0
[2003, 1]
0
[1990, 1]
1
[2016, 1]
0
[1980, 1]
1
[2002, 1]
1
[1987, 1]
1
[1996, 1]
1
[1997, 1]
1
[1984, 1]
1
[2005, 1]
0
[2016, 1]
0
[1987, 1]
1
[1984, 1]
1
[1986, 1]
1
[1990, 1]
1
[1983, 1]
1
[2006, 1]
0
[2018, 1]
0
[2012, 1]
0
[1992, 1]
1
[1992, 1]
1
[2012, 1]
0
[2013, 1]
0
[1988, 1]
1
[2014, 1]
0
[1992, 1]
1
[2018, 1]
0
[2013, 1]
0
[2006, 1]
0
[1984, 1]
1
[1992, 1]
1
[2003, 1]
0
[1991, 1]
1
[1993, 1]
1
[2001, 1]
1
[2015, 1]
0
[2013, 1]
0
[1997, 1]
1
[2000, 1]
1
[2011, 1]
0
[2000, 1]
1
[1987, 1]
1
[1985, 1]
1
[1983, 1]
1
[1999, 1]
1
[2015, 1]
0
[2018, 1]
0
[1996, 1]
1
[1987, 1]
1
[1997, 1]
1
[2015, 1]
0
[1982, 1]
1
[1995, 1]
1
[2016, 1]
0
[1986, 1]
1
[2009, 1]
0
[2009, 1]
0
[2007, 1]
0
[2009, 1]
0
[2013, 1]
0
[1998, 1]
1
[1994, 1]
1
[2011, 1]
0
[1997, 1]
1
[2004, 1]
0
[2015, 1]
0
[2015, 1]
0
[1983, 1]
1
[1984, 1]
1
[2014, 1]
0
[1988, 1]
1
[2008, 1]
0
[2010, 1]
0
[2009, 1]
0
[2004, 1]
0
[2002, 1]
1
[1991, 1]
1
[2001, 1]
1
[1982, 1]
1
[2011, 1]
0
[2002, 1]
1
[2013, 1]
0
[2001, 1]
1
[2000, 1]
1
[1998, 1]
1
[1999, 1]
1
[2017, 1]
0
[2001, 1]
1
[1981, 1]
1
[1994, 1]
1
[2000, 1]
1
[2011, 1]
0
[1988, 1]
1
[1982, 1]
1

您可以看到,很显然,即使所有这些数据的月份均为1,也只有年份使标签为1或0。月份对数据绝对没有影响,这是没有用的。如果月份较高,则标签为0,反之亦然。

答案 1 :(得分:-3)

我看到了您是如何生成模型的,但是没有看到您的数据集和模型的可视化。因此,根据我的知识和经验,我所说的只是一般性的陈述。我只能在经过测试并获得具体结果后才能确定。

通常来说,当您使用线性模型获得良好结果而使用神经网络获得较差结果时,问题可能出在数据的多样性程度不高。如果您拥有大量的数据并且种类繁多,那么神经网络就可以很好地工作。否则,最好使用线性模型,因为NN不是合适的解决方案。

在您的情况下,如果您想尝试使用神经网络,建议您找到并下载一些开放数据(我可能说至少5000个样本)并对其运行NN,而不要像您那样生成数据。

您还应该阅读有关神经网络的一些文档,请开始阅读Wikipedia

我看到您只有两个功能。我建议您添加更多功能,让我们通过转换现有功能来说。这将增加多样性。