1,简介:
BP网络(Back-Propagation Network)是1986年被提出的,是一种按误差逆向传播算法训练的多层前馈网络,是目前应用最广泛的神经网络模型之一,用于函数逼近、模型识别分类、数据压缩和时间序列预测等。
2,预测网络常用模型结构:
前馈(Feed-Forward)网络
前馈网络是感知器的集合,其中有三种基本类型的层: 输入层、隐藏层和输出层。在每个连接过程中,来自前一层的信号被乘以一个权重,增加一个偏置,然后通过一个激活函数。前馈网络使用反向传播迭代更新参数,直到达到理想的性能。
BP网络又称为反向传播神经网络,它是一种有监督的学习算法,具有很强的自适应、自学习、非线性映射能力,能较好地解决数据少、信息贫、不确定性问题,且不受非线性模型的限制。一个典型的BP网络应该包括三层:输入层、隐含层和输出层。(关于神经网络的基础术语等知识可以查看我之前的博客)各层之间全连接,同层之间无连接。隐含层可以有很多层,对于一般的神经网络而言,单层的隐含层已经足够了。上图是一个典型的BP神经网络结构图。
3,学习过程:
BP 神经网络的学习过程主要包含正向传播与反向传播两个关键阶段。
正向传播阶段
在这个阶段,输入信号会从输入层出发,依次经过各个隐含层,最终抵达输出层进行传播。当信号传播到输出层后,便可在此处获取到实际的响应值。倘若该实际值和期望值相比,二者之间存在较大的误差,那么学习过程就会随即进入到误差反向传播的阶段。
反向传播阶段
此阶段是依据梯度下降的方法来开展的。具体而言,是从输出层开始,反向经过各个隐含层,并且在这个过程中逐层不断地对各神经元的连接权值以及阈值做出相应调整。如此反复进行迭代操作,直至网络输出所产生的误差降低到能够被接受的程度为止,或者一直持续到预先设定好的学习次数已完成。
BP 神经网络采用的是有指导的学习方式来展开训练与学习活动。对于标准的 BP 算法而言,它是运用误差函数按照梯度下降的方法来进行学习的,其目的在于让网络的设计输出值和期望输出值之间的均方误差达到最小化。在 BP 神经网络中,其传输函数通常会选用 sigmoid 函数,而输出层则一般采用线性传输函数。
(整一个圆圈代表一个神经元,z就是下面的线性函数,g(z)就是下面的Sigmoid函数,这个g(z)也叫激活函数)
以下是sigmoid函数和输出层常用线性函数的公式介绍:
(1)Sigmoid函数 sigmoid函数也称为Logistic函数,其数学公式为:
其中,是自变量,可以是实数;是自然常数,约等于2.71828。
该函数的输出值范围在(0, 1)之间,当趋近于正无穷时,趋近于1;
当趋近于负无穷时,趋近于0。
它具有将输入值映射到一个可解释为概率或某种程度的激活水平的特性,在神经网络中常用于将神经元的输入转换为输出,起到激活神经元的作用。
(2)输出层常用的线性函数 :
在神经网络输出层较为常用的线性函数其实就是简单的线性变换,其公式可以表示为:
其中,是输入向量(对于输出层来说,就是前一层传递过来的信息经过相应处理后的向量);
是权重向量,它决定了输入向量各个分量对输出的影响程度;
是偏置项(也叫阈值),起到平移函数图像的作用;
就是最终的输出向量。
这个线性函数使得输出层的输出能够在更广泛的取值范围内变化,而不像sigmoid函数那样局限于(0, 1)区间,更适合用于一些需要输出不受限的预测任务,比如回归任务等。
4,原理:
(一)输入层
输入层在整个神经网络结构中承担着接收外界输入信息的重要职责。其具体构成是由多个神经元组成,每个神经元负责接收来自外界的特定输入信息,随后将这些信息传递给中间的隐含层各神经元。这里输入层神经元的节点数是由输入变量的个数所决定的,也就是说,有多少个输入变量,输入层就会设置与之对应的相同数量的神经元节点。
举个例子:
假设现在我有一个预测任务,有500条样本,每一个样本有4个特征值和一个目标值,那我的输入层的神经元的个数为多少个?
回答:每一个样本有 4 个特征值,那么输入层的神经元个数就应该设置为 4 个。因为输入层的作用是接收来自外界的输入信息,这里的外界输入信息就是样本的各个特征值。
(二)输出层
输出层的主要功能是向外界输出经过整个网络信息处理后的最终结果。同样,它也是由若干神经元构成,其节点个数取决于输出变量的个数。即依据需要输出的不同变量的数量,来确定输出层所设置的神经元节点数量。
(三)隐含层设计
网络中隐层神经元的数目与多个因素有着直接的关联,这些因素包括实际问题本身的复杂程度、输入层和输出层的神经元数以及对期望误差的设定等。就目前来看,对于隐层神经元数目的确定并没有一个明确且通用的公式,只有一些基于过往经验总结出来的公式可供参考,比如常见的以下几种:
在上述公式中,代表输入层神经元个数,代表输出层神经元个数,则是取值范围在到之间的一个常数。最终隐层神经元的个数还是需要依靠经验的积累以及通过多次试验来进行确定,以找到最适合特定问题的神经元数量设置。
5,实战:股票数据预测
(导入需要的模块)
import pandas as pd
from keras import Sequential, Input
from scikeras.wrappers import KerasRegressor
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam, RMSprop
1,读取数据(数据是模拟生成的,可自行生成):
# 第一步:读取数据
data = pd.read_csv('A:\数模代码\数据\股票数据.csv')
# 特征选择
X = data[['Opening_Price', 'Highest_Price', 'Lowest_Price']]
y = data['Closing_Price']
2,分割数据:
# 第二步:分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
3,数据预处理:
# 第三步:数据预处理
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
3.1数据处理解释:
一、使用的方法
这里使用的方法是数据标准化,通过StandardScaler
类来实现。它对数据进行了两步操作:先拟合(fit
)数据以计算出数据的均值和标准差等统计信息,然后基于这些统计信息对数据进行变换(transform
),使得数据符合特定的标准化分布。
二、使用该方法的原因
1. 提升模型训练效果
在机器学习和深度学习模型训练中,不同特征的取值范围可能差异很大。例如,在股票数据中,股价可能在几十元到上百元不等,而成交量可能是几万、几十万甚至更多的量级。如果直接使用原始数据进行模型训练,那些取值范围较大的特征可能会在模型训练过程中占据主导地位,导致模型对取值范围较小特征的学习不够充分,影响模型的准确性和泛化能力。通过数据标准化,可以将所有特征的取值范围调整到相对一致的区间,让模型能够更均衡地学习各个特征的信息,从而提升模型训练效果。
2. 加快模型收敛速度
标准化后的数据在梯度下降等优化算法的迭代过程中,能够更稳定地朝着最优解的方向收敛。因为原始数据未经处理时,不同特征由于量级差异可能导致梯度更新步长不一致,使得模型训练过程中收敛速度变慢甚至出现难以收敛的情况。数据标准化有助于解决这个问题,使得模型能够更快地收敛到一个较好的解。
三、方法的原理
StandardScaler
的原理是基于统计学中的均值和标准差概念,将原始数据进行线性变换,使其转换为均值为 0,标准差为 1 的标准正态分布(近似)。具体来说,对于每一个特征维度(列)的数据,它先计算该特征的均值和标准差,然后通过以下公式对每个数据点进行变换。
四、计算公式
-
计算均值:
-
计算标准差:
-
数据标准化变换公式:
这样可以保证训练集和测试集数据在相同的尺度下进行处理,符合模型训练和评估的要求。
4,模型构建:
# 第四步:构建BP神经网络模型
model = Sequential()
model.add(Dense(units=64, activation='relu', input_shape=(X_train.shape[1],))) # 输入层和第一个隐藏层
model.add(Dense(units=32, activation='relu')) # 第二个隐藏层
model.add(Dense(units=1, activation='linear')) # 输出层
# 第五步:编译模型
model.compile(loss='mean_squared_error', optimizer='adam') # 使用均方误差作为损失函数,Adam优化器
# 第六步:训练模型
history = model.fit(X_train, y_train, epochs=100, batch_size=10, validation_split=0.1)
4.1,网络结构:
构建的是一个简单的多层前馈 BP(Back Propagation,反向传播)神经网络,它由输入层、两个隐藏层和输出层组成,各层之间通过全连接的方式相互连接,即上一层的每个神经元都与下一层的每个神经元相连。
各层详细解释
1. 输入层和第一个隐藏层
输入层:这里并没有显式地单独定义输入层,而是在添加第一个隐藏层时通过 input_shape
参数指定了输入数据的形状,从而隐含地确定了输入层的结构。input_shape=(X_train.shape[1],)
表示输入数据的特征数量,也就是输入层神经元的个数,它与训练数据 X_train
的列数相对应。
第一个隐藏层:紧接着输入层的是第一个隐藏层,它由 Dense
层构建而成。Dense
表示这是一个全连接层,意味着该层的每个神经元都与上一层(这里就是输入层)的所有神经元相连。
units=64
表示这个隐藏层设置了 64 个神经元。神经元数量的多少会影响网络的学习能力和复杂度,较多的神经元可能能够学习到更复杂的模式,但也可能导致过拟合等问题;较少的神经元可能无法充分学习数据中的特征,导致欠拟合。
activation='relu'
指定了该层神经元的激活函数为 ReLU(Rectified Linear Unit,修正线性单元)。ReLU 函数的公式为f(x) = max(0, x)
,其特点是当输入值小于 0 时输出为 0,当输入值大于等于 0 时输出等于输入值。使用 ReLU 激活函数可以引入非线性因素,使得神经网络能够学习到数据中的非线性关系,并且在一定程度上缓解了梯度消失问题,有助于加快网络的训练速度。
2,第二个隐藏层
这是网络的第二个隐藏层,同样是一个全连接层 Dense
。
units=32
说明该层设置了 32 个神经元,相比第一个隐藏层神经元数量有所减少。在神经网络中,隐藏层的神经元数量通常可以根据具体问题和经验进行调整,一般会随着网络深度的增加而适当减少神经元数量,当然这并不是绝对的规则。- 依然使用
activation='relu'
作为激活函数,继续保持引入非线性因素以学习数据中的复杂关系,并且维持缓解梯度消失问题等优势。
3. 输出层
- 输出层也是由
Dense
层构建,用于产生网络的最终输出。 units=1
表示输出层只有一个神经元,这通常对应于预测一个连续的数值,比如在预测股票价格的场景中,就是预测一个具体的价格数值。activation='linear'
指定了输出层的激活函数为线性函数。线性激活函数使得输出层的输出能够直接反映输入的线性变换结果,适用于回归类型的预测任务,因为在回归任务中,我们希望输出能够在较宽泛的取值范围内变化,而不像某些分类任务那样需要将输出限制在特定区间(如使用sigmoid
函数将输出限制在(0, 1)
区间用于二分类任务)。
4.2,网络参数的选择:
使用网格搜索和交叉验证来求解出较优的参数:
# 定义构建模型的函数
def create_model(learning_rate=0.001):
model = Sequential()
model.add(Input(shape=(X_train.shape[1],))) # 明确定义输入形状
model.add(Dense(units=64, activation='relu')) # 输入层和第一个隐藏层
model.add(Dense(units=32, activation='relu')) # 第二个隐藏层
model.add(Dense(units=1, activation='linear')) # 输出层
model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=learning_rate))
return model
# 包装模型
model = KerasRegressor(model=create_model, epochs=100, batch_size=10, verbose=0)
# 定义参数网格
param_grid = {
'model__learning_rate': [0.001, 0.01, 0.1],
'epochs': [50, 100],
'batch_size': [10, 20]
}
# 创建GridSearchCV对象
grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=3, scoring='neg_mean_squared_error')
# 执行网格搜索
grid_result = grid.fit(X_train, y_train)
# 输出最优参数
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
# 输出结果为:Best: -1.980870 using {'batch_size': 20, 'epochs': 100, 'model__learning_rate': 0.01}
网格搜索:
- 概念:
- 网格搜索是一种超参数优化技术。超参数是在模型训练之前需要手动设置的参数,例如神经网络中的学习率、隐藏层神经元数量、训练轮数(epochs)、批次大小(batch size)等。这些参数的值会对模型的性能产生重大影响。
- 网格搜索的基本思想是穷举搜索,它会在用户指定的超参数取值范围内,对所有可能的超参数组合进行遍历。例如,在之前的代码中,学习率有 3 个取值(
[0.001, 0.01, 0.1]
),训练轮数有 2 个取值([50,100]
),批次大小有 2 个取值([10, 20]
),那么网格搜索总共会尝试 3×2×2 = 12 种不同的超参数组合。
- 工作流程:
- 对于每一种超参数组合,模型都会使用相同的训练数据集进行训练。以神经网络为例,它会根据当前组合中的学习率、训练轮数和批次大小等参数来训练模型。
- 在训练完成后,会使用指定的评估指标(如均方误差的相反数)来评估模型在验证数据集上的性能。
- 记录每种超参数组合下模型的性能评估结果。
交叉验证(Cross - Validation):
- 概念:
- 交叉验证是一种用于评估模型性能的统计方法,主要目的是更准确地估计模型在未见过数据上的性能,避免模型过度拟合训练数据。
- 它将给定的数据集(通常是训练集)划分为多个子集,常见的有 K - 折交叉验证(K - Fold Cross - Validation),其中 K 表示折数。例如,在 3 - 折交叉验证中,数据集会被均匀地划分为 3 个子集。
- 工作流程(以 K - 折交叉验证为例):
- 对于每一轮交叉验证:
- 将数据集划分为 K 个子集,其中 K - 1 个子集作为训练子集,剩下的 1 个子集作为验证子集。例如,在 3 - 折交叉验证的第一轮中,第 1 个子集作为验证集,第 2 和第 3 个子集作为训练集。
- 使用训练子集训练模型,然后在验证子集上评估模型的性能,记录评估指标(如准确率、均方误差等)的值。
- 重复这个过程 K 次,每次选择不同的子集作为验证集。这样,每个子集都有机会作为验证集被使用一次。
- 最后,将 K 次评估指标的结果进行综合,例如求平均值,得到一个更稳定、更能代表模型真实性能的评估结果。
- 注意,交叉验证并不能提高模型的准确度,只能描述模型的准确度。
- 对于每一轮交叉验证:
# 输出结果为:Best: -1.980870(这里是均方误差的相反数) using {'batch_size': 20, 'epochs': 100, 'model__learning_rate': 0.01}
Model Loss: 0.996681845410352
补充:通过 model.compile
方法对模型进行编译,指定了损失函数为 mean_squared_error
(均方误差),用于衡量模型预测值与实际值之间的差异,然后进行反向传播优化权重,优化器使用 Adam
,并传入当前设置的 learning_rate
参数值,Adam
优化器能够自适应地调整学习率,有助于加快模型训练速度并提高收敛效果。
均方误差(Mean Squared Error, MSE)
- 应用场景:回归问题
- 数学公式:
- 其中,是样本数量,是真实值, 是预测值。
5,模型训练:
# 第六步:训练模型
history = model.fit(X_train, y_train, epochs=100, batch_size=20, validation_split=0.01)
6,BP神经网络预测:
# 第八步:进行预测
predictions = model.predict(X_test)
# 使 predictions 一维
predictions = predictions.flatten()
7,可视化预测结果,可视化评估结果等等
7.1模型评估图像:
-
拟合图像(True vs. Predicted Values)
- 图像内容:
- 这是一个散点图,其 x 轴是测试集的真实值(
y_test
),y 轴是模型对测试集的预测值(predictions
)。每个数据点代表一个测试样本,点的位置由其真实值和预测值共同确定。
- 这是一个散点图,其 x 轴是测试集的真实值(
- 好坏判断:
- 理想情况:散点应该紧密地分布在直线y=x周围。这意味着模型的预测值与真实值几乎相等,预测精度非常高,模型能够很好地拟合数据。
- 一般情况:如果散点大致围绕直线y=x分布,但存在一定的离散程度,说明模型有一定的预测能力,但存在一些误差。离散程度越大,误差可能越大。
- 较差情况:若散点分布比较杂乱,没有明显围绕直线y=x的趋势,或者离直线较远,这表明模型的预测值与真实值偏差较大,模型拟合效果差。
- 图像内容:
-
残差图(Residuals vs. Predicted Values)
- 图像内容:
- 它以预测值(
predictions
)为 x 轴,以残 差(predictions -y_test.to_numpy()
)为 y 轴绘制散点图,并且有一条水平的红线作为参考。残差表示预测值与真实值之间的差值,它反映了模型在每个预测点上的误差大小。
- 它以预测值(
- 好坏判断:
- 理想情况:残差在 x 轴方向上均匀地分布在红线两侧,并且大部分残差的绝对值较小。这说明模型的预测误差在不同预测值下比较稳定,且误差较小,模型性能良好。
- 一般情况:残差分布在红线两侧,但可能出现一侧分布稍多或者部分残差绝对值较大的情况。这意味着模型在某些预测值上的误差较大,但整体还具有一定的可用性。
- 较差情况:如果残差明显偏向红线一侧,或者存在大量绝对值较大的残差,且分布很不均匀,这表示模型存在系统性偏差,预测效果差。
- 图像内容:
-
损失图像(Model Loss)
- 图像内容:
- 这个图像展示了模型在训练过程中的损失变化情况。有两条曲线,一条是训练集损失(
train loss
),另一条是验证集损失(val_loss
)。损失函数在这里是均方误差(mean_squared_error
),损失值衡量了模型预测结果与真实结果之间的差异程度。曲线的 x 轴是训练轮数(epoch
),y 轴是损失值。
- 这个图像展示了模型在训练过程中的损失变化情况。有两条曲线,一条是训练集损失(
- 好坏判断:
- 理想情况:训练集损失和验证集损失都随着训练轮数的增加而逐渐降低,并且最终都趋于一个较低的值且两者比较接近。这表明模型能够有效地学习数据中的特征,没有出现过拟合(训练集损失持续下降而验证集损失后期上升)和欠拟合(训练集和验证集损失都很高且下降缓慢)的情况。
- 一般情况:训练集损失和验证集损失整体呈下降趋势,但可能有波动,或者两者之间有一定差距。这说明模型在学习过程中有一定效果,但可能需要进一步调整,如调整超参数、增加训练数据等。
- 较差情况:如果训练集损失持续下降但验证集损失上升,这是过拟合的典型表现,模型过度学习了训练数据,对新数据的泛化能力差。如果训练集和验证集损失都很高且基本不下降,那就是欠拟合,模型没有充分学习到数据的特征。
- 图像内容:
-
误差图像(Error Distribution)
- 图像内容:
- 这是一个直方图,以预测误差(
predictions - y_test.to_numpy()
)为变量,展示了预测误差在不同区间的分布频率。x 轴是预测误差的值,y 轴是误差落在相应区间的样本数量(计数)。
- 这是一个直方图,以预测误差(
- 好坏判断:
- 理想情况:直方图应该以 0 为中心,并且分布比较集中,即大部分预测误差都集中在 0 附近。这意味着模型的预测误差较小,预测准确性高。
- 一般情况:直方图以 0 为中心,但分布范围较宽,说明模型有一定的预测误差,且误差的波动范围较大,但仍有一定的预测能力。
- 较差情况:如果直方图偏离 0 中心较远,或者分布非常分散,这表明模型的预测误差较大,模型性能不佳。
- 图像内容:
7.2,预测未来数据
- 股票市场预测:
- 可以根据历史的股票价格数据(如开盘价、最高价、最低价等特征)以及对应的收盘价数据来训练模型。训练好的模型能够对未来一段时间内的股票收盘价进行预测,帮助投资者了解股票价格的潜在走势,辅助他们做出买入、卖出或持有股票的决策。不过需要注意的是,股票市场受众多复杂因素影响,模型预测结果仅能作为参考之一。
(在使用训练好的 BP 神经网络模型预测股票收盘价时,需要先为模型提供输入特征值才能得到预测的目标值(收盘价))
完整代码:
1,参数选择:
import pandas as pd
from keras import Sequential
from scikeras.wrappers import KerasRegressor
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.optimizers import Adam
# 第一步:读取数据
data = pd.read_csv('A:\\数模代码\\数据\\股票数据.csv')
# 特征选择
X = data[['Opening_Price', 'Highest_Price', 'Lowest_Price']]
y = data['Closing_Price']
# 第二步:分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 第三步:数据预处理
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 定义构建模型的函数
def create_model(learning_rate=0.001):
model = Sequential()
model.add(Input(shape=(X_train.shape[1],))) # 明确定义输入形状
model.add(Dense(units=64, activation='relu')) # 输入层和第一个隐藏层
model.add(Dense(units=32, activation='relu')) # 第二个隐藏层
model.add(Dense(units=1, activation='linear')) # 输出层
model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=learning_rate))
return model
# 包装模型
model = KerasRegressor(model=create_model, epochs=100, batch_size=10, verbose=0)
# 定义参数网格
param_grid = {
'model__learning_rate': [0.001, 0.01, 0.1],
'epochs': [50, 100],
'batch_size': [10, 20]
}
# 创建GridSearchCV对象
grid = GridSearchCV(estimator=model, param_grid=param_grid, cv=3, scoring='neg_mean_squared_error')
# 执行网格搜索
grid_result = grid.fit(X_train, y_train)
# 输出最优参数
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
# 使用最优参数构建模型
best_model = grid_result.best_estimator_
# 第六步:评估最优模型
loss = best_model.score(X_test, y_test)
print(f'Model Loss: {loss}')
# 第八步:进行预测
predictions = best_model.predict(X_test)
# 使 predictions 一维
predictions = predictions.flatten()
2,根据最优参数训练模型:
import pandas as pd
from keras import Sequential
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.layers import Dense
import matplotlib.pyplot as plt
import os
# 第一步:读取数据
data = pd.read_csv('A:\数模代码\数据\股票数据.csv')
# 特征选择
X = data[['Opening_Price', 'Highest_Price', 'Lowest_Price']]
y = data['Closing_Price']
# 第二步:分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 第三步:数据预处理
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
# 第四步:构建BP神经网络模型
model = Sequential()
model.add(Dense(units=64, activation='relu', input_shape=(X_train.shape[1],))) # 输入层和第一个隐藏层
model.add(Dense(units=32, activation='relu')) # 第二个隐藏层
model.add(Dense(units=1, activation='linear')) # 输出层
# 第五步:编译模型
model.compile(loss='mean_squared_error', optimizer='adam') # 使用均方误差作为损失函数,Adam优化器
# 第六步:训练模型
history = model.fit(X_train, y_train, epochs=100, batch_size=20, validation_split=0.01)
# 第七步:评估模型
loss = model.evaluate(X_test, y_test)
print(f'Model Loss: {loss}')
# 第八步:进行预测
predictions = model.predict(X_test)
# 使 predictions 一维
predictions = predictions.flatten()
# 第九步:可视化结果
plt.figure(figsize=(12, 8))
# 1. 拟合图像
plt.subplot(2, 2, 1)
plt.scatter(y_test, predictions)
plt.xlabel('True Values')
plt.ylabel('Predictions')
plt.title('True vs. Predicted Values')
# 2. 残差图
plt.subplot(2, 2, 2)
plt.scatter(predictions, predictions - y_test.to_numpy())
plt.axhline(0, color='red', linestyle='--')
plt.xlabel('Predicted Values')
plt.ylabel('Residuals')
plt.title('Residuals vs. Predicted Values')
# 3. 损失图像
plt.subplot(2, 2, 3)
plt.plot(history.history['loss'], label='train loss')
plt.plot(history.history['val_loss'], label='validation loss')
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend()
# 4. 误差图像
plt.subplot(2, 2, 4)
plt.hist(predictions - y_test.to_numpy(), bins=50)
plt.title('Error Distribution')
plt.xlabel('Prediction Error')
plt.ylabel('Count')
plt.tight_layout()
# 设置保存图像的路径,这里假设保存到当前目录下的 "images" 文件夹中,如果该文件夹不存在则创建它
save_path = os.path.join(os.getcwd(), "images")
if not os.path.exists(save_path):
os.makedirs(save_path)
# 保存图像
plt.savefig(os.path.join(save_path, 'model.png'))
plt.show()