MLPRegressor 输出范围

数据挖掘 机器学习 深度学习 scikit-学习 毫升
2022-03-07 07:06:27

我正在使用 Scikit 的MLPRegressor时间序列预测任务。

我的数据使用 0 和 1 缩放MinMaxScaler,我的模型使用以下参数初始化:

MLPRegressor(solver='lbfgs', hidden_layer_sizes=50,
                           max_iter=10000, shuffle=False, random_state=9876,
                           activation='relu')

我期望输出在 0 和 1 之间,但值超出界限(负值和 > 1)。

非标准化数据也有同样的问题,我的预测超出了范围!

知道我可能错在哪里吗?

更新: 根据下面的答案,我修改了输出激活层,得到了一些我认为值得分享的有趣结果。共有三种情况,希望字幕清楚地传达信息: 图例:

黑色实线 = 训练时期

红色实线 = 测试时期

青色虚线 = 对整个数据集的网络预测

  1. 使用“relu”激活层训练网络但 output_activation_ 设置为“logistic”时的输出

使用“relu”激活层训练网络但 output_activation_ 设置为“logistic”时的输出

  1. 使用“relu”激活层和 output_activation_ 显式设置为“relu”训练网络时的输出

使用“relu”激活层和 output_activation_ 显式设置为“relu”训练网络时的输出

  1. 使用“relu”激活层训练网络并且不处理 output_activation 时的输出 使用“relu”激活层训练网络并且不处理 output_activation 时的输出
2个回答

Scikit-Learn 的默认输出激活MLPRegressor是“identity”,它实际上对它接收到的权重没有任何作用。

正如@David Masip 在他的回答中提到的那样,更改最终激活层将允许这样做。在 Pytorch、Keras 和 Tensorflow 等框架中这样做是相当直接的。

在您的代码中MLPRegressor使用非标准参数的对象属性(即output_activation_.

以下是我可以在文档中看到的内置选项

activation : {‘identity’, ‘logistic’, ‘tanh’, ‘relu’}, default ‘relu’

Activation function for the hidden layer.

‘identity’, no-op activation, useful to implement linear bottleneck, returns f(x) = x
‘logistic’, the logistic sigmoid function, returns f(x) = 1 / (1 + exp(-x)).
‘tanh’, the hyperbolic tan function, returns f(x) = tanh(x).
‘relu’, the rectified linear unit function, returns f(x) = max(0, x)

将其值设置logistic为您想要的属性,值介于 0 和 1 之间。


编辑 在 OP 发表评论和更新后:在他们的情况下,使用logistic(sigmoid) 作为最终激活会对结果产生负面影响。所以也许值得尝试所有可能的激活函数来研究哪种激活最适合模型和数据。

进一步说明,至少在深度学习的背景下,通常的做法是不在神经网络的最终输出处使用激活 - 有关该讨论的一些想法,请参阅此线程


话虽如此,下面是一个不设置它的模型的简单工作示例,一个设置它的模型。我使用随机数使其工作,但要注意的是,更改后的模型的预测值始终在 0 到 1 的范围内。尝试更改随机种子并重新运行脚本。

import pandas as pd
import numpy as np
from sklearn.neural_network import MLPRegressor

# To see an example where output falls outside of the range of y
np.random.seed(1)

# Create the default NN as you did
nn = MLPRegressor(
    solver='lbfgs',
    hidden_layer_sizes=50,
    max_iter=10000,
    shuffle=False,
    random_state=9876,
    activation='relu')

# Generate some fake data
num_train_samples = 50
num_test_samples = 50
num_vars = 2

X = np.random.random((num_train_samples, num_vars)) * \
    100  # random numbers between 0 and 100
y = np.random.uniform(0, 1, (num_train_samples, 1))  # uniform numbers between 0 and 1

X_test = np.random.random((num_test_samples, num_vars)) * 100
y_test = np.random.uniform(0, 1, (num_test_samples, 1))

# Fit the network
nn.fit(X, y)

print('*** Before scaling the output via final activation:\n')

# Now see that the output activation is (by default) simply linear i.e. 'identity'
print('Output activation by default: {}'.format(nn.out_activation_))
predictions = nn.predict(X_test)

print('Prediction mean: {:.2f}'.format(predictions.mean()))
print('Prediction max: {:.2f}'.format(predictions.max()))
print('Prediction min: {:.2f}'.format(predictions.min()))


print('\n*** After scaling the output via final activation:\n')

# Need to recreate the NN
nn_logistic = MLPRegressor(
    solver='lbfgs',
    hidden_layer_sizes=50,
    max_iter=10000,
    shuffle=False,
    random_state=9876,
    activation='relu')

# Fit the new network
nn_logistic.fit(X, y)


# --------------- #
#  Crucial step!  #
# --------------- #

# before making predictions = alter the attribute: "output_activation_"
nn_logistic.out_activation_ = 'logistic'
print('New output activation: {}'.format(nn_logistic.out_activation_))

new_predictions = nn_logistic.predict(X_test)

print('Prediction mean: {:.2f}'.format(new_predictions.mean()))
print('Prediction max: {:.2f}'.format(new_predictions.max()))
print('Prediction min: {:.2f}'.format(new_predictions.min()))

使用 Python 3.5.2 测试。

尽管您的数据介于 0 和 1 之间,但与任何统计模型一样,预测可能超出此范围。这表明您无法使用模型正确表示数据。然而,这不是重点。如果要确保输出在 0 和 1 之间,则必须将 relu 激活更改为另一个,例如 sigmoid 或 softmax。这些激活可确保您的输出介于 0 和 1 之间,尽管它们可能会导致其他问题,例如梯度消失。通常建议使用 relu 层直到最后一层,它必须是 sigmoid 或 softmax。