我对机器学习有些陌生,我想做一个简单的实验来更熟悉神经网络自动编码器:制作一个非常基本的自动编码器,学习身份函数。
我使用Keras是为了让生活更轻松,所以我首先这样做是为了确保它有效:
# Weights are given as [weights, biases], so we give
# the identity matrix for the weights and a vector of zeros for the biases
weights = [np.diag(np.ones(84)), np.zeros(84)]
model = Sequential([Dense(84, input_dim=84, weights=weights)])
model.compile(optimizer='sgd', loss='mean_squared_error')
model.fit(X, X, nb_epoch=10, batch_size=8, validation_split=0.3)
正如预期的那样,列车和验证数据中的损失均为零:
Epoch 1/10
97535/97535 [==============================] - 27s - loss: 0.0000e+00 - val_loss: 0.0000e+00
Epoch 2/10
97535/97535 [==============================] - 28s - loss: 0.0000e+00 - val_loss: 0.0000e+00
然后我试着做同样的事情,但没有将权重初始化为单位函数,希望经过一段时间的训练,它会学会。但没有。我已经让它在不同的配置中运行了200个时期,使用不同的优化器、损失函数,并添加L1和L2活动正则化子。结果各不相同,但我得到的最好的结果仍然很糟糕,看起来一点也不像原始数据,只是在同一个数字范围内。数据只是一些在1.1左右振荡的数字。我不知道激活层是否对这个问题有意义,我应该使用一个吗?
如果这个一层的"神经网络"不能学习像身份函数这样简单的东西,我怎么能指望它学习更复杂的东西呢?我做错了什么?
编辑
为了获得更好的上下文,这里有一种生成与我使用的数据集非常相似的数据集的方法:
X = np.random.normal(1.1090579, 0.0012380764, (139336, 84))
我怀疑这些值之间的变化可能太小了。损失函数最终具有不错的值(约为1e-6
),但其精度不足以使结果具有与原始数据相似的形状。也许我应该以某种方式缩放/规范它?谢谢你的建议!
更新
最后,正如有人所说,问题在于数据集在84个值之间的变化太小,因此最终的预测在绝对值(损失函数)方面实际上相当不错,但与原始数据相比,变化相差甚远。我通过将每个样本中的84个值标准化为样本的平均值,然后除以样本的标准差来解决这个问题。然后,我使用原始平均值和标准差来对另一端的预测进行反规范化。我想这可以用几种不同的方式来实现,但我通过使用一些对张量进行操作的Lambda层,将这种规范化/非规范化添加到模型本身中。通过这种方式,所有的数据处理都被纳入到模型中,这使得它更易于使用。如果您想查看实际代码,请告诉我。
我认为问题可能是epoch的数量,也可能是X的序列化方式。我用我的X运行了你的代码100个时期,并打印了argmax()和权重的max值,它非常接近身份函数。
我正在添加我使用的代码片段
from keras.models import Sequential
from keras.layers import Dense
import numpy as np
import random
import pandas as pd
X = np.array([[random.random() for r in xrange(84)] for i in xrange(1,100000)])
model = Sequential([Dense(84, input_dim=84)], name="layer1")
model.compile(optimizer='sgd', loss='mean_squared_error')
model.fit(X, X, nb_epoch=100, batch_size=80, validation_split=0.3)
l_weights = np.round(model.layers[0].get_weights()[0],3)
print l_weights.argmax(axis=0)
print l_weights.max(axis=0)
我得到了:
Train on 69999 samples, validate on 30000 samples
Epoch 1/100
69999/69999 [==============================] - 1s - loss: 0.2092 - val_loss: 0.1564
Epoch 2/100
69999/69999 [==============================] - 1s - loss: 0.1536 - val_loss: 0.1510
Epoch 3/100
69999/69999 [==============================] - 1s - loss: 0.1484 - val_loss: 0.1459
.
.
.
Epoch 98/100
69999/69999 [==============================] - 1s - loss: 0.0055 - val_loss: 0.0054
Epoch 99/100
69999/69999 [==============================] - 1s - loss: 0.0053 - val_loss: 0.0053
Epoch 100/100
69999/69999 [==============================] - 1s - loss: 0.0051 - val_loss: 0.0051
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83]
[ 0.85000002 0.85100001 0.79799998 0.80500001 0.82700002 0.81900001
0.792 0.829 0.81099999 0.80800003 0.84899998 0.829 0.852
0.79500002 0.84100002 0.81099999 0.792 0.80800003 0.85399997
0.82999998 0.85100001 0.84500003 0.847 0.79699999 0.81400001
0.84100002 0.81 0.85100001 0.80599999 0.84500003 0.824
0.81999999 0.82999998 0.79100001 0.81199998 0.829 0.85600001
0.84100002 0.792 0.847 0.82499999 0.84500003 0.796
0.82099998 0.81900001 0.84200001 0.83999997 0.815 0.79500002
0.85100001 0.83700001 0.85000002 0.79900002 0.84100002 0.79699999
0.838 0.847 0.84899998 0.83700001 0.80299997 0.85399997
0.84500003 0.83399999 0.83200002 0.80900002 0.85500002 0.83899999
0.79900002 0.83399999 0.81 0.79100001 0.81800002 0.82200003
0.79100001 0.83700001 0.83600003 0.824 0.829 0.82800001
0.83700001 0.85799998 0.81999999 0.84299999 0.83999997]
当我只使用5个数字作为输入并打印实际重量时,我得到了这个:
array([[ 1., 0., -0., 0., 0.],
[ 0., 1., 0., -0., -0.],
[-0., 0., 1., 0., 0.],
[ 0., -0., 0., 1., -0.],
[ 0., -0., 0., -0., 1.]], dtype=float32)