人人都能懂的机器学习——用Keras搭建人工神经网络07

2020-03-08  本文已影响0人  苏小菁在编程

使用Sequential API搭建回归MLP

前面的文章讲述了如何用Sequential API搭建分类MLP,接下来我们要用它来搭建回归MLP来解决加州房价问题。为了方便起见,我们使用Scikit-Learn的fetch_california_housing()函数来加载数据。这个数据集相对简单,只包含数值特征,并且没有缺失值。加载数据后,我们将其分为训练集、验证集和测试集,并缩放所有的特性:

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

housing = fetch_california_housing()
X_train_full, X_test, y_train_full, y_test = train_test_split(housing.data, housing.target)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_valid = scaler.transform(X_valid)
X_test = scaler.transform(X_test)

使用Sequential API构建、训练、评估和使用回归MLP进行预测与我们分类MLP所作的工作非常相似。主要的区别是输出层只有一个神经元(因为我们只想预测一个值),没有使用激活函数,而损失函数是均方误差。由于数据集有很多的噪声,为了避免过度拟合,我们只使用了一个神经元比以前少的隐藏层:

model = keras.models.Sequential([
    keras.layers.Dense(30, activation="relu", input_shape=X_train.shape[1:]),
    keras.layers.Dense(1)
])
model.compile(loss="mean_squared_error", optimizer="sgd")
history = model.fit(X_train, y_train, epochs=20,
                    validation_data=(X_valid, y_valid))
mse_test = model.evaluate(X_test, y_test)
X_new = X_test[:3]
y_pred = model.predict(X_new)

我们可以看到,Sequential API使用方法非常简单。然而,尽管Sequential模型很常用,但是搭建一些更加复杂拓扑结构的模型也是十分有用的,有时还会增加一些输入或输出神经元。出于这个目的,Keras提供了Functional API。

使用Functional API搭建复杂模型

非Sequential神经网络的一个例子是Wide & Deep神经网络。这种神经网络结构是由Heng-Tze Cheng等在2016年的一篇论文中提出的。如图1.14所示,它将所有或部分输入神经元直接连接到输出层,这种体系结构使神经网络能够同时学习深层规则(使用深路径)和简单规则(通过短路径)。

图1.14 Wide&Deep神经网络.jpg

让我们建立这样一个神经网络来解决加州的房价问题:

input_ = keras.layers.Input(shape=X_train.shape[1:])
hidden1 = keras.layers.Dense(30, activation="relu")(input_)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.Concatenate()([input_, hidden2])
output = keras.layers.Dense(1)(concat)
model = keras.Model(inputs=[input_], outputs=[output])

让我们逐行来看一下上面的代码:

在构建了Keras模型之后,所有的工作就都跟前文一样了,编译,训练,评估,预测,这里就不再次赘述了。

但是,如果想要用宽路径发送特征的子集,而通过深路径发送不同的子集(也有可能重叠),那该怎么办呢(见图10-15)?在这种情况下,一种解决办法是使用多个输入。例如,假设我们想通过深路径发送5个特征(特征0到4),通过宽路径发送6个特征(特征2到7):

input_A = keras.layers.Input(shape=[5], name="wide_input")
input_B = keras.layers.Input(shape=[6], name="deep_input")
hidden1 = keras.layers.Dense(30, activation="relu")(input_B)
hidden2 = keras.layers.Dense(30, activation="relu")(hidden1)
concat = keras.layers.concatenate([input_A, hidden2])
output = keras.layers.Dense(1, name="output")(concat)
model = keras.Model(inputs=[input_A, input_B], outputs=[output])
图1.15 使用多个输入.jpg

代码本身很好理解,不过在搭建模型时注意至少要将重要的层命名,特别是当模型像这样变得有些复杂的时候。注意我们在创建模型时指定了inputs=[input_A, input_B]。现在,我们可以像往常一样编译模型,但是当我们调用fit()方法时,就不可以直接传递单个输入矩阵了,而必须传递一对矩阵(X_train_A, X_train_B),这样每个矩阵与每个输入对应。在评估和预测的时候,X_valid,X_test和X_new也需要用相同的方法处理。

model.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=1e-3))
X_train_A, X_train_B = X_train[:, :5], X_train[:, 2:]
X_valid_A, X_valid_B = X_valid[:, :5], X_valid[:, 2:]
X_test_A, X_test_B = X_test[:, :5], X_test[:, 2:]
X_new_A, X_new_B = X_test_A[:3], X_test_B[:3]
history = model.fit((X_train_A, X_train_B), y_train, epochs=20,
                    validation_data=((X_valid_A, X_valid_B), y_valid))
mse_test = model.evaluate((X_test_A, X_test_B), y_test)
y_pred = model.predict((X_new_A, X_new_B))

在很多情况下,还可能会用到多个输出:

图1.16 多个输出-用于正则化的辅助输出.jpg

添加额外的输出非常简单:只需将输出层连接到适当的层,并将它们添加到模型的输出列表中。例如,下面的代码构建图1.16中所示的网络:

output = keras.layers.Dense(1, name="main_output")(concat)
aux_output = keras.layers.Dense(1, name="aux_output")(hidden2)
model = keras.Model(inputs=[input_A, input_B], outputs=[output, aux_output])

每一个输出层都需要一个损失函数,因此我们在编译模型时,我们应向模型传递一个损失函数列表(看注释)(如果我们只传递了单个损失,那么Keras会认为所有输出层都使用同一个损失函数)。 Keras会默认将计算所有损失,并将它们简单相加,得到用于训练的最终损失。 我们更关心主要输出的表现,而不是辅助输出(因为它只是用于正则化),所以我们希望赋予主输出更大的权重。所以Keras也提供了在编译模型时设置所有损失权重的功能:

model.compile(loss=["mse", "mse"], loss_weights=[0.9, 0.1], optimizer="sgd")

现在,当我们训练模型时,我们需要为每个输出提供标签。在这个例子当中,主输出和辅助输出应该尝试预测相同的内容,因此它们应该使用相同的标签。所以,我们需要输入(y_train, y_train)(对于y_valid和y_test也一样)。

history = model.fit([X_train_A, X_train_B], [y_train, y_train], epochs=20,
                    validation_data=([X_valid_A, X_valid_B], [y_valid, y_valid]))

当对模型进行评估时,Keras会返回总损失,以及每个单独损失:

total_loss, main_loss, aux_loss = model.evaluate([X_test_A, X_test_B], [y_test, y_test])

类似地,predict()方法将为每个输出进行预测:

y_pred_main, y_pred_aux = model.predict([X_new_A, X_new_B])

正如上文所述,我们可以很简单地用Functional API构建任何类型的模型架构。下一篇文章我们将介绍如何搭建动态模型,以及模型的一些常用操作。

敬请期待吧!

上一篇 下一篇

猜你喜欢

热点阅读