numpy如何实现神经网络反向传播算法
小编给大家分享一下numpy如何实现神经网络反向传播算法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
一、任务
实现一个4 层的全连接网络实现二分类任务,网络输入节点数为2,隐藏层的节点数设计为:25,50,25,输出层2 个节点,分别表示属于类别1 的概率和类别2 的概率,如图所示。我们并没有采用Softmax 函数将网络输出概率值之和进行约束,而是直接利用均方差误差函数计算与One-hot 编码的真实标签之间的误差,所有的网络激活函数全部采用Sigmoid 函数,这些设计都是为了能直接利用梯度推导公式。
二、数据集
通过scikit-learn 库提供的便捷工具生成2000 个线性不可分的2 分类数据集,数据的特征长度为2,采样出的数据分布如图 所示,所有的红色点为一类,所有的蓝色点为一类,可以看到数据的分布呈月牙状,并且是是线性不可分的,无法用线性网络获得较好效果。为了测试网络的性能,按照7: 3比例切分训练集和测试集,其中2000 ∗ 0 3 =600个样本点用于测试,不参与训练,剩下的1400 个点用于网络的训练。
importmatplotlib.pyplotasplt importseabornassns#要注意的是一旦导入了seaborn,matplotlib的默认作图风格就会被覆盖成seaborn的格式 fromsklearn.datasetsimportmake_moons fromsklearn.model_selectionimporttrain_test_split N_SAMPLES=2000#采样点数 TEST_SIZE=0.3#测试数量比率 #利用工具函数直接生成数据集 X,y=make_moons(n_samples=N_SAMPLES,noise=0.2,random_state=100) #将2000个点按着7:3分割为训练集和测试集 X_train,X_test,y_train,y_test=train_test_split(X,y, test_size=TEST_SIZE,random_state=42) print(X.shape,y.shape) #绘制数据集的分布,X为2D坐标,y为数据点的标签 defmake_plot(X,y,plot_name,file_name=None,XX=None,YY=None,preds=None,dark=False): if(dark): plt.style.use('dark_background') else: sns.set_style("whitegrid") plt.figure(figsize=(16,12)) axes=plt.gca() axes.set(xlabel="$x_1$",ylabel="$x_2$") plt.title(plot_name,fontsize=30) plt.subplots_adjust(left=0.20) plt.subplots_adjust(right=0.80) if(XXisnotNoneandYYisnotNoneandpredsisnotNone): plt.contourf(XX,YY,preds.reshape(XX.shape),25,alpha=1,cmap=plt.cm.Spectral) plt.contour(XX,YY,preds.reshape(XX.shape),levels=[.5],cmap="Greys",vmin=0,vmax=.6) #绘制散点图,根据标签区分颜色 plt.scatter(X[:,0],X[:,1],c=y.ravel(),s=40,cmap=plt.cm.Spectral,edgecolors='none') plt.savefig('dataset.svg') plt.close() #调用make_plot函数绘制数据的分布,其中X为2D坐标,y为标签 make_plot(X,y,"ClassificationDatasetVisualization") plt.show()
三、网络层
通过新建类Layer 实现一个网络层,需要传入网络层的数据节点数,输出节点数,激活函数类型等参数,权值weights 和偏置张量bias 在初始化时根据输入、输出节点数自动生成并初始化:
classLayer: #全连接网络层 def__init__(self,n_input,n_neurons,activation=None,weights=None, bias=None): """ :paramintn_input:输入节点数 :paramintn_neurons:输出节点数 :paramstractivation:激活函数类型 :paramweights:权值张量,默认类内部生成 :parambias:偏置,默认类内部生成 """ #通过正态分布初始化网络权值,初始化非常重要,不合适的初始化将导致网络不收敛 self.weights=weightsifweightsisnotNoneelse np.random.randn(n_input,n_neurons)*np.sqrt(1/n_neurons) self.bias=biasifbiasisnotNoneelsenp.random.rand(n_neurons)*0.1 self.activation=activation#激活函数类型,如'sigmoid' self.last_activation=None#激活函数的输出值o self.error=None#用于计算当前层的delta变量的中间变量 self.delta=None#记录当前层的delta变量,用于计算梯度 defactivate(self,x): #前向传播 r=np.dot(x,self.weights)+self.bias#X@W+b #通过激活函数,得到全连接层的输出o self.last_activation=self._apply_activation(r) returnself.last_activation #其中self._apply_activation实现了不同的激活函数的前向计算过程: def_apply_activation(self,r): #计算激活函数的输出 ifself.activationisNone: returnr#无激活函数,直接返回 #ReLU激活函数 elifself.activation=='relu': returnnp.maximum(r,0) #tanh elifself.activation=='tanh': returnnp.tanh(r) #sigmoid elifself.activation=='sigmoid': return1/(1+np.exp(-r)) returnr #针对于不同的激活函数,它们的导数计算实现如下: defapply_activation_derivative(self,r): #计算激活函数的导数 #无激活函数,导数为1 ifself.activationisNone: returnnp.ones_like(r) #ReLU函数的导数实现 elifself.activation=='relu': grad=np.array(r,copy=True) grad[r>0]=1. grad[r<=0]=0. returngrad #tanh函数的导数实现 elifself.activation=='tanh': return1-r**2 #Sigmoid函数的导数实现 elifself.activation=='sigmoid': returnr*(1-r) returnr
四、网络模型
完成单层网络类后,再实现网络模型的类NeuralNetwork,它内部维护各层的网络层Layer 类对象,可以通过add_layer 函数追加网络层,实现如下:
classNeuralNetwork: #神经网络大类 def__init__(self): self._layers=[]#网络层对象列表 defadd_layer(self,layer): #追加网络层 self._layers.append(layer) #网络的前向传播只需要循环调用个网络层对象的前向计算函数即可 deffeed_forward(self,X): #前向传播 forlayerinself._layers: #依次通过各个网络层 X=layer.activate(X) returnX #网络模型的反向传播实现稍复杂,需要从最末层开始,计算每层的?变量,根据我们 #推导的梯度公式,将计算出的?变量存储在Layer类的delta变量中 #因此,在backpropagation函数中,反向计算每层的?变量,并根据梯度公式计算每层参数的梯度值, #按着梯度下降算法完成一次参数的更新。 defbackpropagation(self,X,y,learning_rate): #反向传播算法实现 #前向计算,得到输出值 output=self.feed_forward(X) foriinreversed(range(len(self._layers))):#反向循环 layer=self._layers[i]#得到当前层对象 #如果是输出层 iflayer==self._layers[-1]:#对于输出层 layer.error=y-output#计算2分类任务的均方差的导数 #关键步骤:计算最后一层的delta,参考输出层的梯度公式 layer.delta=layer.error*layer.apply_activation_derivative(output) else:#如果是隐藏层 next_layer=self._layers[i+1]#得到下一层对象 layer.error=np.dot(next_layer.weights,next_layer.delta) #关键步骤:计算隐藏层的delta,参考隐藏层的梯度公式 layer.delta=layer.error*layer.apply_activation_derivative(layer.last_activation) #在反向计算完每层的?变量后,只需要按着式计算每层的梯度,并更新网络参数即可。 #由于代码中的delta计算的是−?,因此更新时使用了加号。 #循环更新权值 foriinrange(len(self._layers)): layer=self._layers[i] #o_i为上一网络层的输出 o_i=np.atleast_2d(Xifi==0elseself._layers[i-1].last_activation) #梯度下降算法,delta是公式中的负数,故这里用加号 layer.weights+=layer.delta*o_i.T*learning_rate deftrain(self,X_train,X_test,y_train,y_test,learning_rate,max_epochs): #网络训练函数 #one-hot编码 y_onehot=np.zeros((y_train.shape[0],2)) y_onehot[np.arange(y_train.shape[0]),y_train]=1 mses=[] foriinrange(max_epochs):#训练1000个epoch forjinrange(len(X_train)):#一次训练一个样本 self.backpropagation(X_train[j],y_onehot[j],learning_rate) ifi%10==0: #打印出MSELoss mse=np.mean(np.square(y_onehot-self.feed_forward(X_train))) mses.append(mse) print('Epoch:#%s,MSE:%f'%(i,float(mse))) #统计并打印准确率 print('Accuracy:%.2f%%'%(self.accuracy(self.predict(X_test),y_test.flatten())*100)) returnmses defaccuracy(self,y_pre,y_true): returnnp.mean((np.argmax(y_pre,axis=1)==y_true)) defpredict(self,X_test): returnself.feed_forward(X_test)
五、实例化NeuralNetwork类,进行训练
nn=NeuralNetwork()#实例化网络类 nn.add_layer(Layer(2,25,'sigmoid'))#隐藏层1,2=>25 nn.add_layer(Layer(25,50,'sigmoid'))#隐藏层2,25=>50 nn.add_layer(Layer(50,25,'sigmoid'))#隐藏层3,50=>25 nn.add_layer(Layer(25,2,'sigmoid'))#输出层,25=>2 learning_rate=0.01 max_epochs=1000 nn.train(X_train,X_test,y_train,y_test,learning_rate,max_epochs)
以上是“numpy如何实现神经网络反向传播算法”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!
推荐阅读
-
怎么使用numpy中的norm()函数求范数
-
Python如何使用Numpy库
-
使用numpy对数组求平均时怎么忽略nan值
-
numpy中np.nanmax和np.max的区别及坑是什么
-
python numpy中怎么对ndarry按照index增删改查
python numpy中怎么对ndarry按照index增删改查...
-
NumPy下的索引与切片的用法
-
Python3.7.1中无法导入Numpy如何解决
-
numpy 中dot()函数的计算方式有哪些
-
怎么在numpy库中使用concatenate函数拼接数组
这篇文章给大家介绍怎么在numpy库中使用concatenate函数拼接数组,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大...
-
怎么在Numpy中使用向量和矩阵
这篇文章将为大家详细讲解有关怎么在Numpy中使用向量和矩阵,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章...