使用Keras构建的Regressor神经网络只能预测一个值

时间:2018-09-06 10:07:05

标签: python tensorflow machine-learning neural-network keras

我正在尝试使用Keras和Tensorflow构建一个NN,以预测歌曲的最终图表位置(给定5个功能)。

玩了几天之后,我意识到尽管我的MAE越来越低,这是因为该模型刚刚学会预测所有输入的训练集的平均值,这是最佳的解决方案。 (下面的散点图对此进行了说明)

scatter plot

  

这是我的测试集中随机抽取的50个数据点,与网络认为的应该是

起初,我意识到这可能是因为我的网络太复杂了。我有一个形状为(5,)的输入层,在输出层中有一个节点,但是有3个隐藏层,每个都有32个以上的节点。

然后我剥离掉多余的层并移动到只有几个节点的单个隐藏层,如下所示:

self.model = keras.Sequential([
keras.layers.Dense(4,
    activation='relu',
    input_dim=num_features,
    kernel_initializer='random_uniform',
    bias_initializer='random_uniform'
    ),
keras.layers.Dense(1)
])

使用梯度下降优化器对其进行训练仍会导致始终进行完全相同的预测。

然后我想到,也许我要解决的实际问题对于网络来说还不够困难,也许它是线性可分离的。因为这样可以更好地应对根本没有隐藏层的问题,本质上只是进行常规的线性回归,所以我尝试了一下。我将模型更改为:

inp = keras.Input(shape=(num_features,))
out = keras.layers.Dense(1, activation='relu')(inp)
self.model = keras.Model(inp,out)

这也没有改变。我的MAE,预测值都一样。 我尝试了许多不同的事情,优化功能的不同排列,学习率,网络配置,但没有任何帮助。我很确定数据很好,但是为了防止万一,我提供了一个示例。

chartposition,tagcount,dow,artistscore,timeinchart,finalpos
121,3925,5,35128,7,227
131,4453,3,85545,25,130
69,2583,4,17594,24,523
145,1165,3,292874,151,187
96,1679,5,102593,111,540
134,3494,5,1252058,37,370
6,34895,7,6824048,22,5
  

我的数据集样本finalpos是我试图预测的值。数据集包含约40,000条记录,分为80/20条-培训/测试

def __init__(self, validation_split, num_features, should_log):
    self.should_log = should_log
    self.validation_split = validation_split

    inp = keras.Input(shape=(num_features,))
    out = keras.layers.Dense(1, activation='relu')(inp)
    self.model = keras.Model(inp,out)

    optimizer = tf.train.GradientDescentOptimizer(0.01)
    self.model.compile(loss='mae',
                  optimizer=optimizer,
                  metrics=['mae'])

def train(self, data, labels, plot=False):

    early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=20)

    history = self.model.fit(data,
                              labels,
                              epochs=self.epochs,
                              validation_split=self.validation_split,
                              verbose=0,
                              callbacks = [PrintDot(), early_stop])
    if plot: self.plot_history(history)
  

与构建和培训网络有关的所有代码

def normalise_dataset(df, mini, maxi):
    return (df - mini)/(maxi-mini)
  

输入数据的归一化。我的测试和训练数据都被标准化为测试集的最大值和最小值

loss hidden

  

我的损失与验证曲线的图,其中一个隐层网络具有adamoptimiser,学习率为0.01

loss linear

  

同一图,但具有线性回归和梯度下降优化器。

2 个答案:

答案 0 :(得分:1)

因此,我很确定您的标准化是问题所在:您不是按功能进行标准化(就像事实上的行业标准一样),而是对所有数据进行标准化 。 这意味着,如果您具有两个具有不同数量级/范围的不同特征(在您的情况下,请将timeinchartartistscore进行比较。

相反,您可能想使用scikit-learn的StandardScaler之类的东西进行标准化。这样不仅可以对每一列进行归一化(因此您可以一次传递所有功能),而且还可以对单位方差进行归一化(这是关于数据的一些假设,但也可能有帮助)。

要转换数据,请沿以下方向使用

from sklearn.preprocessing import StandardScaler
import numpy as np

raw_data = np.array([[1,40], [2, 80]])
scaler = StandardScaler()
processed_data = scaler.fit_transform(raw_data) 
# fit() calculates mean etc, transform() puts it to the new range.
print(processed_data) # returns [[-1, -1], [1,1]]

请注意,您有两种方法可以标准化/标准化训练数据: 要么将它们与您的训练数据一起缩放,然后再之后进行拆分, 或者您只适合训练数据,然后使用相同的缩放器来转换您的测试数据。
切勿fit_变换您的测试集和训练数据!
由于您可能具有不同的均值/最小值/最大值,因此最终可能会得出完全错误的预测!从某种意义上说,StandardScaler是您对“数据源分布”的定义,即使您的测试集可能是不完全遵循相同属性的子集(由于样本量较小等),其本质上仍与测试集相同。

此外,您可能希望使用更高级的optimizer,例如Adam,或者为SGD指定一些动量属性(实际上,根据经验,0.9是一个不错的选择)。

答案 1 :(得分:0)

发现错误是一个非常愚蠢且容易遗漏的错误。

导入数据集时,我对其进行了混洗,但是,在执行混洗时,我不小心将混洗仅应用于标签集,而不是整个数据集。

结果,每个标签都被分配了一个完全随机的特征集,当然,模型不知道该怎么做。

感谢@dennlinger建议我去我最终发现此错误的地方。