文章目录
一、理论基础
径向基神经网络(Radial Basis Function Neural Network,简称RBFNN)是一种具有较强映射功能的三层前向网络,其原理与反向传播神经网络(BPNN)较为接近,最主要的特征为以径向基函数作为隐含层激活函数,数据从输入层传入隐含层后,通过径向基函数对其进行非线性映射,然后经过线性计算传递至输出层进行输出。
反向传播神经网络(BPNN)原理参考:
反向传播神经网络(BPNN)的实现(Python,附源码及数据集)
1、径向基神经网络结构
径向基神经网络的结构图如下图所示:
2、前向传播过程
径向基神经网络的前向传播过程类似于无监督学习,首先通过使用聚类算法如K-means对数据进行聚类,将聚类生成的中心点作为隐含层径向基函数的中心点,其中径向基函数一般选用高斯函数,然后利用中心点信息计算得出径向基函数的宽度向量,宽度向量的计算公式如下:
其中c_max为中心点之间的最大距离,h为节点数。
之后输入数据分别经过隐含层、输出层进行相关计算,输入样本x_i在隐含层的第j个节点的输出由以下公式计算得出:
其中c_j与σ_j分别为隐含层第j个节点的中心点与宽度向量,除上述方法外,还可直接随机生成隐含层的中心点与宽度向量。
输入样本x_i在输出层的第m个节点的输出由以下公式计算得出:
其中ω_m为该节点的权值,φ为激活函数。
激活函数原理参考:
神经网络基础知识之激活函数
3、反向传播过程
径向基神经网络的反向传播过程类似于监督学习,主要是对网络隐含层的中心点、宽度向量以及输出层的权值、阈值进行不断修正的过程,这一过程主要通过损失函数计算出每个参数的梯度值,然后使用反向传播算法如随机梯度下降法(SGD)对权值进行不断修正的过程,以输出层的权值为例,其更新公式如下:
其中E为损失函数,μ为学习率。
损失函数原理参考:
机器学习基础知识之损失函数
反向传播原理参考:
神经网络之反向传播算法(梯度、误差反向传播算法BP)
4、建模步骤
以使用径向基神经网络进行预测为例,可以将径向基神经网络预测模型的建模步骤总结如下:
- 根据输入数据的相关特征确定径向基神经网络输入层、隐含层以及输出层的节点数;
- 使用K-means算法对模型的输入数据进行聚类,将聚类生成的中心点作为隐含层径向基函数的中心点,通过中心点计算获得隐含层径向基函数的宽度向量;
- 选择一种参数初始化方法对径向基神经网络输出层的连接权值以及阈值进行随机初始化;
- 数据由输入层输入径向基神经网络,传递至隐含层后经径向基函数对数据进行非线性转换;
- 数据在隐含层输出后传递至输出层,在与输出层的连接权值进行线性计算后由激活函数进行非线性转换,最后得到网络的前向传播输出;
- 选择一种损失函数对网络的前向传播输出以及目标值进行相关计算得到损失值;
- 以输出层的损失值计算得到输出层连接权值以及阈值的梯度,选择一种反向传播算法对它们进行调整;
- 损失值传递至隐含层,同样使用相同的反向传播算法对隐含层的中心点以及宽度向量进行调整;
- 获得一个参数得到更新后的径向基神经网络;
- 在达到最大迭代次数或满足停止迭代条件之前,重复步骤4到步骤9,在达到最大迭代次数后,输出隐含层与输出层参数确定的径向基神经网络。
参数初始化方法参考:
神经网络基础知识之参数初始化
二、径向基神经网络的实现
以数据预测为例,下面介绍基于Python实现径向基神经网络的过程。
选用某省市的表层土壤重金属元素数据集作为实验数据,该数据集总共96组,随机选择其中的24组作为测试数据集,72组作为训练数据集。选取重金属Ti的含量作为待预测的输出特征,选取重金属Co、Cr、Mg、Pb作为模型的输入特征。
1、训练过程(RBFNN.py)
#库的导入import numpy as npimport pandas as pdfrom sklearn.cluster import KMeans#激活函数def tanh(x): return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))#激活函数偏导数def de_tanh(x): return (1-x**2)#参数设置samnum = 72 #输入数据数量hiddenunitnum = 8 #隐含层节点数indim = 4 #输入层节点数outdim = 1 #输出层节点数maxepochs = 500 #最大训练次数errorfinal = 0.65*10**(-3) #停止迭代训练条件learnrate = 0.001 #学习率#输入数据的导入df = pd.read_csv("train.csv")df.columns = ["Co", "Cr", "Mg", "Pb", "Ti"]Co = df["Co"]Co = np.array(Co)Cr = df["Cr"]Cr = np.array(Cr)Mg=df["Mg"]Mg=np.array(Mg)Pb = df["Pb"]Pb =np.array(Pb)Ti = df["Ti"]Ti = np.array(Ti)samplein = np.mat([Co,Cr,Mg,Pb])#数据归一化,将输入数据压缩至0到1之间,便于计算,后续通过反归一化恢复原始值sampleinminmax = np.array([samplein.min(axis=1).T.tolist()[0],samplein.max(axis=1).T.tolist()[0]]).transpose()#对应最大值最小值sampleout = np.mat([Ti])sampleoutminmax = np.array([sampleout.min(axis=1).T.tolist()[0],sampleout.max(axis=1).T.tolist()[0]]).transpose()#对应最大值最小值sampleinnorm = ((np.array(samplein.T)-sampleinminmax.transpose()[0])/(sampleinminmax.transpose()[1]-sampleinminmax.transpose()[0])).transpose()sampleoutnorm = ((np.array(sampleout.T)-sampleoutminmax.transpose()[0])/(sampleoutminmax.transpose()[1]-sampleoutminmax.transpose()[0])).transpose()#给归一化后的数据添加噪声noise = 0.03*np.random.rand(sampleoutnorm.shape[0],sampleoutnorm.shape[1])sampleoutnorm += noise#聚类生成隐含层径向基函数的中心点w1x = sampleinnorm.transpose()estimator=KMeans(n_clusters=8,max_iter=10000)estimator.fit(x)w1 = estimator.cluster_centers_#计算得到隐含层的宽度向量b1b1 = np.mat(np.zeros((hiddenunitnum,outdim)))for i in range(hiddenunitnum): cmax = 0 for j in range(hiddenunitnum): temp_dist=np.sqrt(np.sum(np.square(w1[i,:]-w1[j,:]))) if cmax<temp_dist: cmax=temp_dist b1[i] = cmax/np.sqrt(2*hiddenunitnum)#随机生成输出层的权值w2、阈值b2scale = np.sqrt(3/((indim+outdim)*0.5))w2 = np.random.uniform(low=-scale,high=scale,size=[hiddenunitnum,outdim])b2 = np.random.uniform(low=-scale, high=scale, size=[outdim,1])#将输入数据、参数设置为矩阵,便于计算inputin=np.mat(sampleinnorm.T)w1=np.mat(w1)b1=np.mat(b1)w2=np.mat(w2)b2=np.mat(b2)#errhistory存储误差errhistory = np.mat(np.zeros((1,maxepochs)))#开始训练for i in range(maxepochs): #前向传播计算 #hidden_out为隐含层输出 hidden_out = np.mat(np.zeros((samnum, hiddenunitnum))) for a in range(samnum): for j in range(hiddenunitnum): d=(inputin[a, :] - w1[j, :]) * (inputin[a, :] - w1[j, :]).T c=2 * b1[j, :] * b1[j, :] hidden_out[a, j] = np.exp((-1.0 )* (d/c)) #output为输出层输出 output = tanh(hidden_out * w2 + b2) # 计算误差 out_real = np.mat(sampleoutnorm.transpose()) err = out_real - output loss = np.sum(np.square(err)) #判断是否停止训练 if loss < errorfinal: break errhistory[:,i] = loss #反向传播计算 output=np.array(output.T) belta=de_tanh(output).transpose() #分别计算每个参数的误差项 dw1now = np.zeros((8,4)) db1now = np.zeros((8,1)) dw2now = np.zeros((8,1)) db2now = np.zeros((1,1)) for j in range(hiddenunitnum): sum1 = 0.0 sum2 = 0.0 sum3 = 0.0 sum4 = 0.0 for a in range(samnum): sum1 +=err[a,:] * belta[a,:] * hidden_out[a,j] * (inputin[a,:]-w1[j,:]) sum2 +=err[a,:] * belta[a,:] * hidden_out[a,j] * (inputin[a,:]-w1[j,:])*(inputin[a,:]-w1[j,:]).T sum3 +=err[a,:] * belta[a,:] * hidden_out[a,j] sum4 +=err[a,:] * belta[a,:] dw1now[j,:]=(w2[j,:]/(b1[j,:]*b1[j,:])) * sum1 db1now[j,:] =(w2[j,:]/(b1[j,:]*b1[j,:]*b1[j,:])) * sum2 dw2now[j,:] =sum3 db2now = sum4 #根据误差项对四个参数进行更新 w1 += learnrate * dw1now b1 += learnrate * db1now w2 += learnrate * dw2now b2 += learnrate * db2now print("the iteration is:",i+1,",the loss is:",loss)print('更新的权重w1:',w1)print('更新的偏置b1:',b1)print('更新的权重w2:',w2)print('更新的偏置b2:',b2)print("The loss after iteration is :",loss)#保存训练结束后的参数,用于测试np.save("w1.npy",w1)np.save("b1.npy",b1)np.save("w2.npy",w2)np.save("b2.npy",b2)
2、测试过程(test.py)
#库的导入import numpy as npimport pandas as pd#激活函数tanhdef tanh(x): return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))#输入数据的导入,用于测试数据的归一化与返归一化df = pd.read_csv("train.csv")df.columns = ["Co", "Cr", "Mg", "Pb", "Ti"]Co = df["Co"]Co = np.array(Co)Cr = df["Cr"]Cr = np.array(Cr)Mg=df["Mg"]Mg=np.array(Mg)Pb = df["Pb"]Pb =np.array(Pb)Ti = df["Ti"]Ti = np.array(Ti)samplein = np.mat([Co,Cr,Mg,Pb])sampleinminmax = np.array([samplein.min(axis=1).T.tolist()[0],samplein.max(axis=1).T.tolist()[0]]).transpose()#对应最大值最小值sampleout = np.mat([Ti])sampleoutminmax = np.array([sampleout.min(axis=1).T.tolist()[0],sampleout.max(axis=1).T.tolist()[0]]).transpose()#对应最大值最小值#导入训练的参数w1=np.load('w1.npy')w2=np.load('w2.npy')b1=np.load('b1.npy')b2=np.load('b2.npy')w1 = np.mat(w1)w2 = np.mat(w2)b1 = np.mat(b1)b2 = np.mat(b2)#测试数据的导入df = pd.read_csv("test.csv")df.columns = ["Co", "Cr", "Mg", "Pb", "Ti"]Co = df["Co"]Co = np.array(Co)Cr = df["Cr"]Cr = np.array(Cr)Mg=df["Mg"]Mg=np.array(Mg)Pb = df["Pb"]Pb =np.array(Pb)Ti = df["Ti"]Ti = np.array(Ti)input=np.mat([Co,Cr,Mg,Pb])#测试数据数量testnum = 24#隐含层节点数量hiddenunitnum = 8#测试数据中输入数据的归一化inputnorm=(np.array(input.T)-sampleinminmax.transpose()[0])/(sampleinminmax.transpose()[1]-sampleinminmax.transpose()[0])#hidden_out2用于保存隐含层输出hidden_out2 = np.mat(np.zeros((testnum,hiddenunitnum)))#计算隐含层输出for a in range(testnum): for j in range(hiddenunitnum): d = (inputnorm[a, :] - w1[j, :]) * (inputnorm[a, :] - w1[j, :]).T c = 2 * b1[j, :] * b1[j, :].T hidden_out2[a, j] = np.exp((-1.0) * (d / c))#计算输出层输出output = tanh(hidden_out2 * w2 + b2)#对输出结果进行反归一化diff = sampleoutminmax[:,1]-sampleoutminmax[:,0]networkout2 = output*diff+sampleoutminmax[0][0]networkout2 = np.array(networkout2).transpose()output1=networkout2.flatten()#降成一维数组output1=output1.tolist()for i in range(testnum): output1[i] = float('%.2f'%output1[i])print("the prediction is:",output1)#将输出结果与真实值进行对比,计算误差output=Tierr = output1 - outputrmse = (np.sum(np.square(output-output1))/len(output)) ** 0.5mae = np.sum(np.abs(output-output1))/len(output)average_loss1=np.sum(np.abs((output-output1)/output))/len(output)mape="%.2f%%"%(average_loss1*100)f1 = 0for m in range(testnum): f1 = f1 + np.abs(output[m]-output1[m])/((np.abs(output[m])+np.abs(output1[m]))/2)f2 = f1 / testnumsmape="%.2f%%"%(f2*100)print("the MAE is :",mae)print("the RMSE is :",rmse)print("the MAPE is :",mape)print("the SMAPE is :",smape)#计算预测值与真实值误差与真实值之比的分布A=0B=0C=0D=0E=0for m in range(testnum): y1 = np.abs(output[m]-output1[m])/np.abs(output[m]) if y1 <= 0.1: A = A + 1 elif y1 > 0.1 and y1 <= 0.2: B = B + 1 elif y1 > 0.2 and y1 <= 0.3: C = C + 1 elif y1 > 0.3 and y1 <= 0.4: D = D + 1 else: E = E + 1print("Ratio <= 0.1 :",A)print("0.1< Ratio <= 0.2 :",B)print("0.2< Ratio <= 0.3 :",C)print("0.3< Ratio <= 0.4 :",D)print("Ratio > 0.4 :",E)
3、测试结果
注:由于每次初始化生成的参数不同,因此对参数设置相同的神经网络进行多次训练和预测,测试结果不会完全一致,此外测试结果的好坏也会受到隐含层节点数、学习率、训练次数等参数的影响。
4、参考源码及实验数据集
来源地址:https://blog.csdn.net/weixin_42051846/article/details/128765163