用python简单实现一个反向传播

手写一个反向传播,预测三角函数的值

       国庆假期没力气出去玩了,花了半天时间写个简单的反向传播小例子希望可以帮助到需要了神经网络是如何训练知识的人。例子非常简单,就是一个简单的全连接神经网络来预测三角函数曲线,代码思路也比较简单,就是输入一个值,然后计算网络层参数相对于损失函数的偏导数,然后不停更新迭代,利用链式法则以及矩阵的一些知识点来实现在网络中自动求导,具体的代码如下所示:

#NN.py
import math
import numpy as np

'''定义神经元,这是网络层参数的一个单元,就是简单的一个数值加上这个数值对应的梯度值'''
class Neuron(object):
    def __init__(self,value:float=1.0,grad:float=1.0):
        self.value=value
        self.grad=grad

'''定义Layer的基类,其实正常的线性层,卷积层,这样的有参数的层以及激活函数这样的无参数的层都可以继承这个基类'''
class Layer(object):
    def __init__(self,input_shape,output_shape,pre_layer,next_layer):
        self.input_shape=input_shape
        self.output_shape=output_shape
        self.pre_layer=pre_layer
        self.next_layer=next_layer
        self.input=[]
        self.output=[]

    def ForwardOperator(self,*args, **kw):
        pass
    def PropagateOperator(self,*args, **kw):
        pass
        
'''无参数层'''
class NoParaLayer(Layer):
    def __init__(self,input_shape=None,output_shape=None,pre_layer=None,next_layer=None):
        super(NoParaLayer, self).__init__(input_shape,output_shape,pre_layer,next_layer)
        self.propagate_grad = None
        self.propagate_shape = None

'''有参数层'''
class ParaLayer(Layer):
    def __init__(self,input_shape,output_shape,pre_layer=None,next_layer=None):
        super(ParaLayer, self).__init__(input_shape,output_shape,pre_layer,next_layer)
        self.neurons=None
        self.neurons_shape=None
        self.propagate_grad=None
        self.propagate_shape=None

    def NeuronUpdateOperator(self,*args, **kw):
        pass


'''线性层,该层有三个方法:
ForwardOperator——————计算正向传播的值
NeuronUpdateOperator——————计算反向传播时线性层的参数梯度
PropagateOperator——————计算反向传播时传递到下一层的梯度值'''
class LinearLayer(ParaLayer):
    def __init__(self,input_shape:int,output_shape:int,pre_layer=None,next_layer=None):
        super(LinearLayer, self).__init__(input_shape, output_shape, pre_layer, next_layer)
        self.neurons_shape=[input_shape,output_shape]
        self.neurons=[[Neuron(value=np.random.rand(1)[0],grad=0) for _ in range(output_shape)] for _ in range(input_shape)]

    def ForwardOperator(self,input:list):
        self.input=input
        self.output=[]
        for i in range(self.output_shape):
            tem=0
            for j in range(self.input_shape):
                tem+=self.input[j]*self.neurons[j][i].value
            self.output.append(tem)
        return self.output

    def NeuronUpdateOperator(self,grad_output:list):
        for i in range(self.input_shape):
            for j in range(self.output_shape):
                self.neurons[i][j].grad=grad_output[j]*self.input[i]

    def PropagateOperator(self,grad_output:list):
        self.propagate_grad=[]
        for i in range(self.input_shape):
            tem=0
            for j in range(self.output_shape):
                tem+=grad_output[j]*self.neurons[i][j].value
            self.propagate_grad.append(tem)
            self.propagate_shape=self.input_shape
        return self.propagate_grad

'''激活函数层,自身没有参数,所以只需要定义正向传播的方法以及需要传递给下一层的梯度即可'''
class Sigmoid(NoParaLayer):
    def __init__(self):
        super(Sigmoid, self).__init__()

    def ForwardOperator(self,input):
        self.input=input
        self.input_shape=len(input)
        self.output=[]
        for num in input:
            self.output.append(1.0 / (1.0 + np.power(np.e, -num)))
        return self.output

    def PropagateOperator(self,grad_output):
        self.propagate_grad=[]
        for i,num in enumerate(grad_output):
            self.propagate_grad.append((np.power(np.e, -self.input[i]) / np.power(1 + np.power(np.e, -self.input[i]), 2))*num)
        self.propagate_shape=self.input_shape
        return self.propagate_grad

'''返回mseloss的梯度,注意不是计算损失值而是返回梯度'''
def MSELossGrad(predict:float,label:float):
    return 2*(predict-label)

class NetWork(object):
    def __init__(self,layers:list):
        self.layers=layers
        for i,layer in enumerate(self.layers):
            if i!=0:
                layer.pre_layer=self.layers[i-1]
            if i!=len(self.layers)-1:
                layer.next_layer=self.layers[i+1]
                
'''正向传播,没什么说的,一层一层传就好'''
    def Fordward(self,input):
        layer=self.layers[0]
        x=input
        while(layer!=None):
            x=layer.ForwardOperator(x)
            layer=layer.next_layer

        return x
        
'''反向传播,将每一层输出对于输入的偏导数传递到上一层,并更新线性层的梯度值'''
    def Backward(self,predict,label,loss_grad_fn):
        grad=[loss_grad_fn(predict,label)]
        layer=self.layers[-1]
        while(layer!=None):
            try:
                layer.NeuronUpdateOperator(grad)
            except:
                pass

            grad=layer.PropagateOperator(grad)
            layer=layer.pre_layer
            
'''更新梯度参数'''
    def Update(self):
        for layer in self.layers:
            if hasattr(layer,'neurons'):
                for i in range(len(layer.neurons)):
                    for j in range(len(layer.neurons[0])):
                        layer.neurons[i][j].value-=(layer.neurons[i][j].grad*0.05)

下面是主函数:

#main.py
from tqdm import tqdm

from NN import *
import matplotlib.pyplot as plt
import numpy as np

np.random.seed(1)

x = np.arange(0, 2 * np.pi, 0.01)
x = x.reshape((len(x), 1))
y = (np.sin(x) + 1.0) / 2.0


yt = np.array(y).ravel()
xs = np.array(x).ravel()


'''
绘制三角函数曲线,然后根据x和y值来来训练该网络
'''

layer1=LinearLayer(input_shape=1,output_shape=10)
layer2=LinearLayer(input_shape=10,output_shape=10)
layer3=LinearLayer(input_shape=10,output_shape=10)
layer4=LinearLayer(input_shape=10,output_shape=1)


'''
4个线性层组成的很简单全连接神经网络
'''

network=NetWork([layer1,
                 Sigmoid(),
                 layer2,
                 Sigmoid(),
                 layer3,
                 Sigmoid(),
                 layer4,
                 Sigmoid()
                ])

for i in tqdm(range(1000000)):
    flag=i
    i=i%len(x)

    predict=network.Fordward(x[i])
    network.Backward(predict=predict[0],label=yt[i],loss_grad_fn=MSELossGrad)
    network.Update()

    if flag%10000==0:
        print(predict[0]-yt[i])


y=[network.Fordward([_])[0][0] for _ in x]

plt.plot(x,y)
plt.plot(x,yt)
plt.show()

使用方法非常简单,就是将 N N . p y NN.py NN.py m a i n . p y main.py main.py两个文件放在一个文件夹中,然后直接执行 m a i n . p y main.py main.py就可以了,下面就是正常运行的截图:
在这里插入图片描述
为了节省时间,博主用了那3层网络训练2000000次之后的结果,如下图所示:
在这里插入图片描述
橙色的曲线是 ( s i n ( x ) + 1 ) / 2 (sin(x)+1)/2 (sin(x)+1)/2的函数曲线,蓝色曲线是拟合结果,可以看出已经和橙色曲线比较接近了。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贪睡熊猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值