原文链接:https://blog.csdn.net/weixin_44177568/article/details/115873212
文章目录
使用函数式模型构建复杂网络
前面两节分别讲述了:基本分类模型和回归模型 ,但是这两种都是基于Keras中的顺序模型进行构建的,即神经网络只有一个输入和一个输出;如下图所示:
![](https://img-blog.csdnimg.cn/img_convert/4a2487b3f873645c8bd4c741fef24202.png)
而Keras中模型的构建方式分为两种:
顺序模型(Sequential)
函数式模型(Model)
相比顺序模型,函数式模型可以构建更加深层、更加宽度的网络,其在大量数据基础上,可以有更强的拟合能力。
1、加宽网络(输入、输出不变)
在不改变数据的基础上,加宽网络的宽度!直接修改上一节中的网络结构,新的网络结构如下:
input = keras.layers.Input(shape=x_train_all.shape[1:]) # 定义输入层(指定输入数据的维度)
dense_1 = keras.layers.Dense(30, activation="relu")(input) # 全连接1
dense_2 = keras.layers.Dense(30, activation="relu")(dense_1) # 全连接2
concat_layer = keras.layers.Concatenate()([input, dense_2]) # 将输入层 和 全连接2 拼接
dense_3 = keras.layers.Dense(30, activation="relu")(concat_layer) # 全连接3
output = keras.layers.Dense(1)(dense_3) # 输出层
model = keras.Model(inputs = [input], outputs = [output]) # 创建函数式Model,指定输入层和输出层
![](https://img-blog.csdnimg.cn/img_convert/9b2ad7cbc0beea78d3e53929e83b4d23.png)
上图为上一节中的顺序式模型。
![](https://img-blog.csdnimg.cn/img_convert/ae4706f08eefd10c146d989ee717f98d.png)
上图为函数式模型。两种神经网络最终效果不同,网络更宽的模型效果更好!
1.1、完整代码:
# 导入相关工具库
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import keras
# 加载数据集
housing_data = fetch_california_housing()
# 划分训练集和测试集
x_train_all, x_test_all, y_train_all , y_test_all = train_test_split(housing_data["data"], housing_data["target"])
# 数据处理(标准化)
scaler = StandardScaler()
x_train_all = scaler.fit_transform(x_train_all)
x_test_all = scaler.transform(x_test_all)
# 构建模型
input = keras.layers.Input(shape=x_train_all.shape[1:])
dense_1 = keras.layers.Dense(30, activation="relu")(input)
dense_2 = keras.layers.Dense(30, activation="relu")(dense_1)
concat_layer = keras.layers.Concatenate()([input, dense_2])
dense_3 = keras.layers.Dense(30, activation="relu")(concat_layer)
output = keras.layers.Dense(1)(dense_3)
model = keras.Model(inputs = [input], outputs = [output])
# 打印模型结构信息
model.summary()
# 模型编译
model.compile(optimizer=keras.optimizers.SGD(), loss=keras.losses.mean_squared_error)
# 模型训练
history = model.fit(x_train_all, y_train_all, epochs= 50, validation_split=0.1)
# 在测试集上评估
loss = model.evaluate(x_test_all, y_test_all) # 返回测试集的loss
print(loss)
2、多个输入层
和上述改变网络中宽度不同,有时也想改变数据,例如,数据集中一部分特征直接输入网络深层;一部分特征数据依旧一步一步传递下去:如下图所示:
![](https://img-blog.csdnimg.cn/img_convert/0b82032428dedae32a2dbf5fb8cbbd2b.png)
上述中,输入层1、输入层2中的特征划分需要依据数据类型以及实际场景!两个输入层的数据可以重叠也不可以不重叠!
具体网络结构如下所示:
input_1 = keras.layers.Input(shape=[5]) # 特征 0-5 # 总特征有8维,取前4维特征数据
input_2 = keras.layers.Input(shape=[6]) # 特征 2-7 # 总特征有8维,取第2-7维特征数据
dense_1 = keras.layers.Dense(30, activation="relu")(input_2) # 全连接1
dense_2 = keras.layers.Dense(30, activation="relu")(dense_1) # 全连接2
concat_layer = keras.layers.Concatenate()([input_1, dense_2]) # 输入层1 和 全连接2 拼接
dense_3 = keras.layers.Dense(30, activation="relu")(concat_layer) # 全连接3
output = keras.layers.Dense(1)(dense_3) # 输出层
model = keras.Model(inputs = [input_1, input_2], outputs = [output]) # 创建函数式Model,注意输入层有两个!
由于输入层有两个,因此,后期训练、预测时都要指定两个输入层!
2.2、完整代码
# 导入相关工具库
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import keras
# 加载数据集
housing_data = fetch_california_housing()
# 划分训练集和测试集
x_train_all, x_test_all, y_train_all , y_test_all = train_test_split(housing_data["data"], housing_data["target"])
# 数据处理(标准化)
scaler = StandardScaler()
x_train_all = scaler.fit_transform(x_train_all)
x_test_all = scaler.transform(x_test_all)
# 构建模型
input_1 = keras.layers.Input(shape=[5]) # 特征 0-4 # 总特征有8维,取前4维特征数据
input_2 = keras.layers.Input(shape=[6]) # 特征 2-7 # 总特征有8维,取第2-7维特征数据
dense_1 = keras.layers.Dense(30, activation="relu")(input_2)
dense_2 = keras.layers.Dense(30, activation="relu")(dense_1)
concat_layer = keras.layers.Concatenate()([input_1, dense_2])
dense_3 = keras.layers.Dense(30, activation="relu")(concat_layer)
output = keras.layers.Dense(1)(dense_3)
model = keras.Model(inputs = [input_1, input_2], outputs = [output])
# 打印模型结构信息
model.summary()
# 模型编译
model.compile(optimizer=keras.optimizers.SGD(), loss=keras.losses.mean_squared_error)
# 将训练集划分子集
x_train_1, x_train_2 = x_train_all[:, :5], x_train_all[:, 2:]
x_test_1, x_test_2 = x_test_all[:, :5], x_test_all[:, 2:]
# 模型训练
history = model.fit([x_train_1, x_train_2], y_train_all, epochs= 50, validation_split=0.1)
# 在测试集上评估
loss = model.evaluate([x_test_1, x_test_2], y_test_all) # 返回测试集的loss
print(loss)
3、多个输出层
有些任务不仅仅是分类或回归,而是多任务结合的,例如目标检测任务,不仅要在图片中识别出目标(分类)还要给出目标位置、宽高等信息(回归),因此,需要模型多个输出!并且多任务模型训练,有时可以增加模型整体的鲁棒性
对上面的代码继续修改,得到多输出的神经网络模型,图下图所示:
![](https://img-blog.csdnimg.cn/img_convert/83e72a2f025a29b3df099e24c7ff1a19.png)
input_1 = keras.layers.Input(shape=[5]) # 特征 0-4 # 总特征有8维,取前4维特征数据
input_2 = keras.layers.Input(shape=[6]) # 特征 2-7 # 总特征有8维,取第2-7维特征数据
dense_1 = keras.layers.Dense(30, activation="relu")(input_2)
dense_2 = keras.layers.Dense(30, activation="relu")(dense_1)
concat_layer = keras.layers.Concatenate()([input_1, dense_2])
dense_3 = keras.layers.Dense(30, activation="relu")(concat_layer)
output1 = keras.layers.Dense(1)(dense_3)
output2 = keras.layers.Dense(1)(dense_2)
model = keras.Model(inputs = [input_1, input_2], outputs = [output1, output2]) # 多输入,多输出
3.3 完整代码
# 导入相关工具库
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import keras
# 加载数据集
housing_data = fetch_california_housing()
# 划分训练集和测试集
x_train_all, x_test_all, y_train_all , y_test_all = train_test_split(housing_data["data"], housing_data["target"])
# 数据处理(标准化)
scaler = StandardScaler()
x_train_all = scaler.fit_transform(x_train_all)
x_test_all = scaler.transform(x_test_all)
# 构建模型
input_1 = keras.layers.Input(shape=[5]) # 特征 0-4 # 总特征有8维,取前4维特征数据
input_2 = keras.layers.Input(shape=[6]) # 特征 2-7 # 总特征有8维,取第2-7维特征数据
dense_1 = keras.layers.Dense(30, activation="relu")(input_2)
dense_2 = keras.layers.Dense(30, activation="relu")(dense_1)
concat_layer = keras.layers.Concatenate()([input_1, dense_2])
dense_3 = keras.layers.Dense(30, activation="relu")(concat_layer)
output1 = keras.layers.Dense(1)(dense_3)
output2 = keras.layers.Dense(1)(dense_2)
model = keras.Model(inputs = [input_1, input_2], outputs = [output1, output2])
# 打印模型结构信息
model.summary()
# 模型编译
model.compile(optimizer=keras.optimizers.SGD(), loss=keras.losses.mean_squared_error)
# 划分特征子集
x_train_1, x_train_2 = x_train_all[:, :5], x_train_all[:, 2:]
x_test_1, x_test_2 = x_test_all[:, :5], x_test_all[:, 2:]
# 模型训练
history = model.fit([x_train_1, x_train_2], [y_train_all, y_train_all], epochs= 50, validation_split=0.1)
# 在测试集上评估,返回总loss、每个输出的loss
sum_loss, output1_loss, output2_loss = model.evaluate([x_test_1, x_test_2], [y_test_all, y_test_all]) # 返回测试集的loss
print(sum_loss, output1_loss, output2_loss)
注意: 由于是多输出,那么每个输出的损失函数都是可以定义的,上述在模型编译时,仅仅指定了loss=keras.losses.mean_squared_error,那么两个输出的损失函数都是均方误差!也可以分开定义。如下;
指定每个输出的损失函数:loss=[“sgd”, “sgd”]
model.compile(optimizer=keras.optimizers.SGD(), loss=["sgd", "sgd"])
指定每个loss的权重,给与某分支较大的决策权:loss_weights=[0.7,0.3]
model.compile(optimizer=keras.optimizers.SGD(), loss=["sgd", "sgd"], loss_weights=[0.7,0.3])