[神经网络] 二、使用 BP 神经网络拟合多输入多输出曲线
作者:解琛时间:2021 年 2 月 3 日二、使用 BP 神经网络拟合多输入多输出曲线二、使用 BP 神经网络拟合多输入多输出曲线列表中的数据是某地区20年公路运量数据,其中属性 人口数量、机动车数量 和 公路面积 作为输入,属性 公路客运量 和 公路货运量 作为输出。请用神经网络拟合此多输入多输出曲线。年份人口数量/万人机动车数量/万辆公路面积/万平米公路客运量/万人公路货运量/万吨1990
·
作者:解琛
时间:2021 年 2 月 3 日
- 二、使用 BP 神经网络拟合多输入多输出曲线
二、使用 BP 神经网络拟合多输入多输出曲线
列表中的数据是某地区20年公路运量数据,其中属性 人口数量
、机动车数量
和 公路面积
作为输入,属性 公路客运量
和 公路货运量
作为输出。
请用神经网络拟合此多输入多输出曲线。
年份 | 人口数量/万人 | 机动车数量/万辆 | 公路面积/万平米 | 公路客运量/万人 | 公路货运量/万吨 |
---|---|---|---|---|---|
1990 | 20.55 | 0.6 | 0.09 | 5126 | 1237 |
1991 | 22.44 | 0.75 | 0.11 | 6217 | 1379 |
1992 | 25.37 | 0.85 | 0.11 | 7730 | 1385 |
1993 | 27.13 | 0.9 | 0.14 | 9145 | 1399 |
1994 | 29.45 | 1.05 | 0.2 | 10460 | 1663 |
1995 | 30.1 | 1.35 | 0.23 | 11387 | 1714 |
1996 | 30.96 | 1.45 | 0.23 | 12353 | 1834 |
1997 | 34.06 | 1.6 | 0.32 | 15750 | 4322 |
1998 | 36.42 | 1.7 | 0.32 | 18304 | 8132 |
1999 | 38.09 | 1.85 | 0.34 | 19836 | 8936 |
2000 | 39.13 | 2.15 | 0.36 | 21024 | 11099 |
2001 | 39.99 | 2.2 | 0.36 | 19490 | 11203 |
2002 | 41.93 | 2.25 | 0.38 | 20433 | 10524 |
2003 | 44.59 | 2.35 | 0.49 | 22598 | 11115 |
2004 | 47.3 | 2.5 | 0.56 | 25107 | 13320 |
2005 | 52.89 | 2.6 | 0.59 | 33442 | 16762 |
2006 | 55.73 | 2.7 | 0.59 | 36836 | 18673 |
2007 | 56.76 | 2.85 | 0.67 | 40548 | 20724 |
2008 | 59.17 | 2.95 | 0.69 | 42927 | 20803 |
2009 | 60.63 | 3.1 | 0.79 | 43462 | 21804 |
预测结果如下。
实现的代码如下。
#!/usr/bin/env python
# coding=utf-8
# 作者:解琛;
# 功能:神经网络进行多输入多输出的拟合;
# 时间:2020 年 2 月 3 日;
# 备注:无。
import numpy as np
import matplotlib.pyplot as plt
def getSrcData():
'''获取原始数据;'''
# 初始化的输入数据;
# 人口数量;
population = [
20.55, 22.44, 25.37, 27.13, 29.45, 30.10, 30.96, 34.06, 36.42, 38.09,
39.13, 39.99, 41.93, 44.59, 47.30, 52.89, 55.73, 56.76, 59.17, 60.63
]
# 机动车数量;
vehicle = [
0.6, 0.75, 0.85, 0.9, 1.05, 1.35, 1.45, 1.6, 1.7, 1.85, 2.15, 2.2, 2.25,
2.35, 2.5, 2.6, 2.7, 2.85, 2.95, 3.1
]
# 公路面积;
roadarea = [
0.09, 0.11, 0.11, 0.14, 0.20, 0.23, 0.23, 0.32, 0.32, 0.34, 0.36, 0.36,
0.38, 0.49, 0.56, 0.59, 0.59, 0.67, 0.69, 0.79
]
# 初始化输出数据;
# 公路客运量;
passengertraffic = [
5126, 6217, 7730, 9145, 10460, 11387, 12353, 15750, 18304, 19836, 21024,
19490, 20433, 22598, 25107, 33442, 36836, 40548, 42927, 43462
]
# 公路货运量;
freighttraffic = [
1237, 1379, 1385, 1399, 1663, 1714, 1834, 4322, 8132, 8936, 11099, 11203,
10524, 11115, 13320, 16762, 18673, 20724, 20803, 21804
]
# 返回初始数据;
return population, vehicle, roadarea, passengertraffic, freighttraffic
def normalizeData(population, vehicle, roadarea, passengertraffic, freighttraffic):
'''将数据进行标准化处理'''
global sampleout, sampleinminmax, sampleoutminmax
# 将输入数据改为 3 × 20 的矩阵;
samplein = np.mat([population, vehicle, roadarea])
# print("samplein: ", samplein)
# [
# [20.55 22.44 25.37 27.13 29.45 30.1 30.96 34.06 36.42 38.09 39.13 39.99
# 41.93 44.59 47.3 52.89 55.73 56.76 59.17 60.63]
# [ 0.6 0.75 0.85 0.9 1.05 1.35 1.45 1.6 1.7 1.85 2.15 2.2
# 2.25 2.35 2.5 2.6 2.7 2.85 2.95 3.1 ]
# [ 0.09 0.11 0.11 0.14 0.2 0.23 0.23 0.32 0.32 0.34 0.36 0.36
# 0.38 0.49 0.56 0.59 0.59 0.67 0.69 0.79]
# ]
# 将输出数据改为 2 × 20 的矩阵;
sampleout = np.mat([passengertraffic, freighttraffic])
# print("sampleout: ", sampleout)
# [
# [ 5126 6217 7730 9145 10460 11387 12353 15750 18304 19836 21024 19490
# 20433 22598 25107 33442 36836 40548 42927 43462]
# [ 1237 1379 1385 1399 1663 1714 1834 4322 8132 8936 11099 11203
# 10524 11115 13320 16762 18673 20724 20803 21804]
# ]
# 求得 vehicle 和 roadarea 的最小值和最大值:[最小值 最大值];
sampleinminmax = np.array(
[samplein.min(axis=1).T.tolist()[0],
samplein.max(axis=1).T.tolist()[0]]
).transpose()
# print("sampleinminmax: ", sampleinminmax)
# [[20.55 60.63]
# [ 0.6 3.1 ]
# [ 0.09 0.79]]
# 求得 passengertraffic 和 freighttraffic 的最小值和最大值:[最小值 最大值];
sampleoutminmax = np.array(
[sampleout.min(axis=1).T.tolist()[0],
sampleout.max(axis=1).T.tolist()[0]]
).transpose()
# print("sampleoutminmax: ", sampleoutminmax)
# [[ 5126 43462]
# [ 1237 21804]]
# 将数据进行标准化(归一化);
# 将数据映射到 -1 到 1 的范围内;
sampleinnorm = (
2 *
(np.array(samplein.T) - sampleinminmax.transpose()[0]) /
(sampleinminmax.transpose()[1] - sampleinminmax.transpose()[0])
- 1
).transpose()
# print("sampleinnorm: ", sampleinnorm)
# [
# [-1. -0.90568862 -0.75948104 -0.67165669 -0.55588822 -0.52345309
# -0.48053892 -0.3258483 -0.20808383 -0.1247505 -0.07285429 -0.02994012
# 0.06686627 0.1996008 0.33483034 0.61377246 0.75548902 0.80688623
# 0.92714571 1. ]
# [-1. -0.88 -0.8 -0.76 -0.64 -0.4
# -0.32 -0.2 -0.12 0. 0.24 0.28
# 0.32 0.4 0.52 0.6 0.68 0.8
# 0.88 1. ]
# [-1. -0.94285714 -0.94285714 -0.85714286 -0.68571429 -0.6
# -0.6 -0.34285714 -0.34285714 -0.28571429 -0.22857143 -0.22857143
# -0.17142857 0.14285714 0.34285714 0.42857143 0.42857143 0.65714286
# 0.71428571 1. ]
# ]
# 将数据映射到 -1 到 1 的范围内;
sampleoutnorm = (
2 *
(np.array(sampleout.T).astype(float) - sampleoutminmax.transpose()[0]) /
(sampleoutminmax.transpose()[1] - sampleoutminmax.transpose()[0])
- 1
).transpose()
# print("sampleoutnorm: ", sampleoutnorm)
# [
# [-1. -0.94308222 -0.86414858 -0.79032763 -0.72172371 -0.67336185
# -0.62296536 -0.4457429 -0.3125 -0.23257513 -0.17059683 -0.25062604
# -0.20142947 -0.0884808 0.04241444 0.47725376 0.6543197 0.84797579
# 0.9720889 1. ]
# [-1. -0.98619147 -0.98560801 -0.98424661 -0.95857442 -0.95361501
# -0.94194584 -0.70000486 -0.32950844 -0.25132494 -0.04098799 -0.0308747
# -0.09690281 -0.0394321 0.17498906 0.5097 0.69553168 0.89497739
# 0.9026596 1. ]
# ]
# 返回标准化后的输入、输出数据、原始数据的最值;
return sampleinnorm, sampleoutnorm
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def network(sampleinnorm, sampleoutnorm):
'''神经网络;'''
global sampleoutminmax, errhistory
# 初始化神经网络的参数;
# 训练的次数;
maxepochs = 1000
# 学习率;
learnrate = 0.035
# 认为可以停止训练的理想误差,达到该误差值时停止训练;
errorfinal = 0.5 * 10 ** (-3)
# 样本数,下面反向求解计算时用于产生样本数个 1 参与运算,用于计数;
samnum = 20
# 输入神经网络的数据维度;
indim = 3
# 输出神经网络的数据维度;
outdim = 2
# 隐藏层的节点数;
hiddenunitnum = 8
# 使用 np.random.rand(3, 1) 创建一个数据范围在 [0, 1] 之间的 3 行 1 列的随机矩阵;
# 通过 2 * np.random.rand(3, 1) - 1 的方法将数据范围映射到 [-1, 1] 之间;
# 创建隐藏层的 w 权重和 b 偏置矩阵;
w1 = 2 * np.random.rand(hiddenunitnum, indim) - 1
b1 = 2 * np.random.rand(hiddenunitnum, 1) - 1
# 创建输出层的 w 权重和 b 偏置矩阵;
w2 = 2 * np.random.rand(outdim, hiddenunitnum) - 1
b2 = 2 * np.random.rand(outdim, 1) - 1
# 创建一个列表,用于存储每次训练产生的误差,训练结束后用于绘图,进行可视化,便于分析整个训练过程误差的变化情况;
errhistory = []
for _ in range(maxepochs):
# 通过 h = sigmoid(w1 x + b1) 计算隐藏层的输出;
hiddenout = sigmoid((np.dot(w1, sampleinnorm).transpose() + b1.transpose())).transpose()
# 通过 y = w2 h + b2 计算输出层的输出;
# 因为我们希望使用神经网络进行多输入参数的拟合,不是解决分类问题,所以输出层不能使用激活函数进行非线性化处理;
networkout = (np.dot(w2, hiddenout).transpose() + b2.transpose()).transpose()
# 计算神经网络输出和真实输出之间的误差;
err = sampleoutnorm - networkout
# print("err: ", err)
# [
# [-1.15209228 -1.09856738 -1.03510767 -0.9593632 -0.87692973 -0.81286831
# -0.76319537 -0.55616018 -0.42568621 -0.33624089 -0.2584064 -0.3379644
# -0.28130657 -0.12589438 0.03087629 0.47121104 0.64813855 0.86237073
# 0.9887427 1.03279493]
# [ 0.22413548 0.20693496 0.17399082 0.16539449 0.16536842 0.115607
# 0.1019654 0.31584757 0.64734241 0.6869846 0.83598636 0.82889373
# 0.73975088 0.77749909 0.95387178 1.21997793 1.35997221 1.54430285
# 1.51774009 1.60832199]
# ]
# 对误差进行求和,得到总的误差;
# 使用整个数据集的误差和作为误差,而不是单条数据段,目的是为了更快的去降低误差;
sse = sum(sum(err ** 2))
# print("sse: ", sse)
# 31.580979073426597
# 使用一个列表将误差存储下来;
errhistory.append(sse)
# 如果误差已经在期望的范围内,则停止训练;
if sse < errorfinal:
break
# 开始进行反向传递;
# 因为是使用神经网络进行拟合,所以最终的输出层不适用激活函数进行非线性化;
# 所以最终额输出层的损失函数不需要对最终输出求偏导,直接赋值即可;
delta2 = err
# 隐藏层的损失函数对隐藏层的输出求偏导;
# d_L_d_ypred × d_ypred_d_h
# 使用 x = (w2 × (ytrue - ypred))f'(h) = (w2 × (ytrue - ypred))(h)(1 - h) 进行反向求解;
delta1 = np.dot(w2.transpose(), delta2) * hiddenout * (1 - hiddenout)
# 计算输出层的权重 w2 和偏置 b2;
# dw2 = (ytrue - ypred) × h
dw2 = np.dot(delta2, hiddenout.transpose())
# db2 = (ytrue - ypred) × 1
db2 = np.dot(delta2, np.ones((samnum, 1)))
# 计算隐藏层的权重 w1 和偏置 b1;
# dw1 = d_L_d_ypred × d_ypred_d_h × x
dw1 = np.dot(delta1, sampleinnorm.transpose())
# db1 = d_L_d_ypred × d_ypred_d_h × 1
db1 = np.dot(delta1, np.ones((samnum, 1)))
# 更新输出层的权重 w2 和偏置 b2;
w2 += learnrate * dw2
b2 += learnrate * db2
# 更新隐藏层的权重 w1 和 b1;
w1 += learnrate * dw1
b1 += learnrate * db1
return w1, b1, w2, b2
def predict(w1, b1, w2, b2, sampleinnorm):
'''使用训练好的神经网络进行预测;'''
# 使用新的权重计算隐藏层和输出层的输出;
# h = f(w1 x + b1)
hiddenout = sigmoid((np.dot(w1, sampleinnorm).transpose() + b1.transpose())).transpose()
# o = w2 h + b2
networkout = (np.dot(w2, hiddenout).transpose() + b2.transpose()).transpose()
return networkout
def scalableData(normalizeData, minmax):
# 由于进行了归一化,用最小值和最大值计算原始输出;
# 计算样本原始数据的变化范围:最大值 - 最小值;
diff = minmax[:, 1] - minmax[:, 0]
# 将新权重计算的输出映射到 [0, 1] 范围;
srcData = (normalizeData + 1) / 2
# 将神经网络输出的第 1 个参数还原到原始的数据范围;
srcData[0] = srcData[0] * diff[0] + minmax[0][0]
# 将神经网络输出的第 2 个参数还原到原始的数据范围;
srcData[1] = srcData[1] * diff[1] + minmax[1][0]
return srcData
def plotGraph():
'''结果可视化;'''
global sampleout, errhistory, networkPredict
# 输出转为 numpy;
sampleout = np.array(sampleout)
# 绘制第 1 张图;
plt.figure(1)
plt.plot(errhistory, label="err")
plt.legend(loc='upper left')
plt.show()
# 绘制第 2 张图;
plt.figure(2)
plt.subplot(2, 1, 1)
plt.plot(sampleout[0],
color="blue",
linewidth=1.5,
linestyle="-",
label=u"real passengertraffic")
plt.plot(networkPredict[0],
color="red",
linewidth=1.5,
linestyle="--",
label=u"predict passengertraffic")
plt.legend(loc='upper left')
plt.subplot(2, 1, 2)
plt.plot(sampleout[1],
color="blue",
linewidth=1.5,
linestyle="-",
label="real freighttraffic")
plt.plot(networkPredict[1],
color="red",
linewidth=1.5,
linestyle="--",
label="predict freighttraffic")
plt.legend(loc='upper left')
# 展示第 2 张图;
plt.show()
if __name__ == "__main__":
global sampleoutminmax, networkPredict
# 获取原始数据;
population, vehicle, roadarea, passengertraffic, freighttraffic = getSrcData()
# 将数据进行规划一处理;
sampleinnorm, sampleoutnorm = normalizeData(population, vehicle, roadarea, passengertraffic, freighttraffic)
# 将输入、输出、原始数据的最值传入神经网络,对神经网络进行训练;
w1, b1, w2, b2 = network(sampleinnorm, sampleoutnorm)
# 向训练好的神经网络内传入参数进行预测;
networkPredict = predict(w1, b1, w2, b2, sampleinnorm)
# 将预测好的结果缩放到正常的数据范围内;
networkPredict = scalableData(networkPredict, sampleoutminmax)
# 结论可视化;
plotGraph()
整个训练过程损失值的变化如下。
更多推荐
所有评论(0)