MLP模型原理(python代码实现)

注:

目前仅提供原理的python代码实现,并未实例进行。如果有需要可以评论亦或者是查阅其他博客的实现过程

在本文的代码原理实现中,使用了Linear层进行搭建

MLP介绍:

 MLP简称多层感知机,其结构与我的一篇博客BP神经网络(python实现)-CSDN博客相似,其实也可以讲大部分的神经网络结构相似,就比如多层感知机(MLP)、全连接神经网络(FCNN)、前馈神经网络(FNN)、深度神经网络(DNN)这些网络其实侧重点不同,但在大体结构上并未过多的差别,在此篇博客中有详细讲解:多层感知机(MLP)、全连接神经网络(FCNN)、前馈神经网络(FNN)、深度神经网络(DNN)与BP算法详解_mlp神经网络-CSDN博客

回到我们的正题,了解多层感知机之前,我们可以先了解一下单层感知机结构:

而在此基础上添加多层神经元,这样就是实现了多层感知机

python实现:

在此处的实现来源于此篇博客用python实现多层感知机_多层感知机python csdn-CSDN博客的代码,也可以参考他的实现过程。本文在其代码上进行了解读(具体看代码,最后已附上)

此图为class Linear中的不易于理解的部分,其中包括def forward部分中的self.Onet计算过程,原作者并未给出注释,因此此处为笔者的推测。-->首先我们定义的权重是根据该层输入特征数量in_feature和out_feature给出的维度,然而self.Oin则是根据输入的x,因此两者之间的维度关系不确定,故而我们给定x这一维度时需要确保其维度大小为(in_feature,batch_size)

下图为对应的维度计算,以及所得维度,其中in_feature加1是因为经过了row_stack

注:此段代码对于原作者的代码并未更改,笔者仅在其基础上进行解读,关于实践部分并未进行尝试,同时此代码最具特色的点在于将神经网络拆分称为一层一层的,即代码中class:Linear的实现。与笔者之前读过的BP神经网络代码不太一样,它将神经网络拆分称为输入层节点,隐含层节点,输出层节点。这样能进行细致的更改,但却仅实现了单隐层,而这里的module则可以实现多隐层,但原作者也仅定义了单隐层(请先读后续的Linear,这是关键)

import numpy as np
import math
from collections import OrderedDict

class Module:

    def __init__(self,lr=None):
        self.linear1=Linear(7,15,batch_size=20)
        self.linear1.first_layer=True
        self.linear1.add_Motivation('sigmoid')
        self.linear2=Linear(15,5,batch_size=20)
        self.linear2.add_Motivation('relu')
        self.linear3=Linear(5,1,batch_size=20)
        self.linear3.last_layer=True
        self.lr=1e-3  #学习率
        if(lr!=None):
            self.lr=lr

    def forward(self,x):  #对整个三层的前向预测
        x=x.copy()
        x=self.linear1.forward(x)
        x=self.linear2.forward(x)
        x=self.linear3.forward(x)
        return x

    def compute_gradient(self,x):
        x=x.copy()
        self.linear3.compute_local_gradient(x)
        x=self.linear3.to_last.copy()
        self.linear2.compute_local_gradient(x)
        x=self.linear2.to_last.copy()
        self.linear1.compute_local_gradient(x)

    def backward(self):
        self.linear3.backward(self.lr)
        self.linear2.backward(self.lr)
        self.linear1.backward(self.lr)

class Linear:
    def __init__(self,in_feature,out_feature,batch_size=None,bias=None):  #其中none表示可以选择传入,如果没有传入则其值为none
        self.in_feature=in_feature  #定义该层输入特征数量,也可以理解为该层接入的箭头多少个(可以看看图)
        self.out_feature=out_feature  #定义该层输出特征数量
        self.Oin=None   #用于存储输入数据x_i
        self.Oout=None  #用于存储该层的输出output
        self.Onet=None  #用于存储每一个神经元的加权和
        self.bias=True  #此处用ture表示在我们定义的类Linear中含有偏置项bias
        self.batch_size=1  #batch_size指一次性输入给模型的样本数,即我在训练过程中训练集为10个,但由于此处batch_size=1,则一次迭代只取一个样本
        if(batch_size!=None):
            self.batch_size=batch_size
        if(bias):
            self.bias=bias
        # 此处用于生成输出特征数量,输入特征数量+1的矩阵 按正常情况来说应该是o行i列,但多余的一列用于初始化偏置bias
        self.Weights=np.random.randn(self.out_feature,self.in_feature+1)
        self.local_gradient=None  #local_gradient称为局部梯度
        self.motivation=None  #激活函数
        self.last_layer=False  #此处用于标识是否为最后一层
        self.to_last=None
        self.first_layer=None  #此处用于标识是否为第一层

    def sigmoid(self,x):
        return 1/(1+np.exp(-x))

    def relu(self,x):
        return np.maximum(0,x)

    def forward(self,x):  #这里具体的x应为(in_feature,batch_size)维度
        self.Oin=x.copy()  #x表示传入的参数,而x.copy则表示将此参数拷贝
        ones=np.ones((1,self.batch_size))
        self.Oin=np.row_stack((self.Oin,ones))   #此处引入row_stack函数,将ones数据这一行拼接在self.Oin后一行上
        self.Onet=self.Weights.dot(self.Oin)     #将最开始随机生成的权重与后续拼接之后的self.Oin进行矩阵乘法计算,这样就能实现权重乘以输入x_i加上偏置
        if(self.motivation==None):
            self.Oout=self.Onet.copy() #没有激活函数则直接输出
        elif(self.motivation=='sigmoid'):
            self.Oout=self.sigmoid(self.Onet)
        elif(self.motivation=='relu'):
            self.Oout=self.relu(self.Onet)
        return self.Oout



    def add_Motivation(self,TT):
        if(TT=='sigmoid'):
            self.motivation='sigmoid'

        elif(TT=='relu'):
            self.motivation='relu'

    def compute_local_gradient(self,x):  #梯度计算
        if(self.last_layer):
            tp=-(x-self.Oout)  #此处应为实际值与预测值的差值
            self.local_gradient=tp*(self.motivation_back)  #此处的motivation_back用于计算激活函数的导数,在后续定义函数
        else:
            #这里的self.local_gradient为该层计算得到的梯度,我们称之为局部梯度
            self.local_gradient= self.motivation_back * x
        to_l = []
        for i in range(self.batch_size):
            a=[]
            for j in range(self.in_feature):
                a.append(self.Weights[:,j]*self.local_gradient[:,i])  #这里计算得到权重更新值
            to_l.append(np.sum(a,axis=1))
        to_l = np.array(to_l).T
        self.to_last = to_l.copy()

    def backward(self,lr):
        # 这里局部梯度乘以self.Oin的转置矩阵得到维度(out,in+1)每个权重的梯度,然后除以批次能够减小方差
        g=self.local_gradient.dot(self.Oin.T/self.batch_size)
        self.Weights=self.Weights-g*lr  #更新得到新的权重


    @property  #此处用于声明定义的函数motivation_back为属性,即在调用此函数是self.motivation_back而不是motivation_back()
    def motivation_back(self):
       if(self.motivation):
            if(self.motivation=='sigmoid'):
                the_loss=self.sigmoid(self.Onet)*(1-self.sigmoid(self.Onet))
                return the_loss
            elif(self.motivation=='relu'):
                tp: object=self.Onet.copy() #此处创建新的对象tp
                tp[tp>0]=1
                tp[tp<=0]=0
                return tp  #经过上述更新得到tp中大于0的数变为1,小于等于0的数变为0

       else:
            return np.ones(np.shape(self.Onet))

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值