python徒手实现反向传播算法

1. 项目背景

假设有如上图所示的全连接神经网络,该网络共有三层L1、L2、L3,输入层为X。每层输出经过Sigmoid激活函数。

其中X为100*3的矩阵,即表示有100个样本,每个样本有3个特征。L1层有5个神经元,L2层有8个神经元,L3有10个神经元。y为100*1的向量,是数据集X对应的标签。W1,W2,W3矩阵形状如图所示。

 

2. 导入所需的库

import numpy as np
import pandas as pd

for i in [np, pd]:
    print(i.__name__,": ",i.__version__,sep="")

输出:

numpy: 1.17.4
pandas: 0.25.3

3. 导入数据

根据如上所述,X为1003的矩阵,y为1001的矩阵

df = pd.read_csv("admission.txt")

# 构造X和y
X = df[["Exam1","Exam2","Exam3"]].as_matrix()
y = df["Admitted"].as_matrix().reshape(100,1)
print(X.shape, y.shape)

输出:

(100, 3) (100, 1)
print(X[:5])
print(y[:5])
[[34.62365962 78.02469282 56.32417622]
 [30.28671077 43.89499752 37.09085415]
 [35.84740877 72.90219803 54.3748034 ]
 [60.18259939 86.3085521  73.24557574]
 [79.03273605 75.34437644 77.18855624]]
[[0]
 [0]
 [0]
 [1]
 [1]]

4. 前向传播

注意:为了方便计算与推导,省略偏置项b。对于偏置项b,在实际应用中可以在数据集第一列添加全1的方式进行处理,则在训练的模型结果参数W中第一行即是b的值。

4.1 Sigmoid函数

Sigmoid函数用f(x)表示,则f(x)及其导数如公式(1)所示:

 

def Sigmoid(x):
    return 1/(1+np.exp(-x))

def SigmoidPrime(y):
    return y*(1-y)

4.2 初始化权重W

根据上面的分析,W1是3*5的矩阵,W2是5*8的矩阵,W3是8*1的矩阵

np.random.seed(1)  # 指定随机种子,方便复现

W1 = 2 * np.random.random((3,5))-1  # random函数默认返回(0,1)区间上的值
W2 = 2 * np.random.random((5,8))-1
W3 = 2 * np.random.random((8,1))-1

for i in [W1, W2, W3]:
    print(i.shape)

输出:

(3, 5)
(5, 8)
(8, 1)

4.3 各层输出

  • 输入层:X=X,X为100*3的矩阵
  • L1层:L1=f(XW1),L1为100*5的矩阵
  • L2层:L2=f(L1W2),L2为100*8的矩阵
  • L3层:L3=f(L2W3),L3为100*1的矩阵
L1 = Sigmoid(np.dot(X,W1))
L2 = Sigmoid(np.dot(L1,W2))
L3 = Sigmoid(np.dot(L2,W3))

5. 反向传播

5.1 损失函数

定义模型的目标函数(即损失函数)为均方误差,即:

根据误差反向传播的思想,需要求出损失函数关于W1、W2和W3的偏导数,再根据偏层数进行W参数的更新。

5.2 求偏导

求导过程中注意

  • a. 矩阵点乘(矩阵元素对应位置相乘)和矩阵乘法的区别(上式中绿色标部分)。
  • b. 注意矩阵求导法则(上式中红色标注部分):

为了简化上式的书写及方便python编程,令:

 

则根据(5)式,(3)式中的偏导数可表示为:

 

5.3 反向传播过程

error = L3 - y
delta3 = error * SigmoidPrime(L3)
delta2 = delta3.dot(W3.T)*SigmoidPrime(L2)
delta1 = delta2.dot(W2.T)*SigmoidPrime(L1)
 
W3 -= alpha * np.dot(L2.T, delta3)
W2 -= alpha * np.dot(L1.T, delta2)
W1 -= alpha * np.dot(X.T, delta1) 

6. 完整代码训练

import numpy as np
import pandas as pd


iterations = 500000   # 设置迭代次数
alpha = 0.1  # 设置学习率

def Sigmoid(x):
    return 1/(1+np.exp(-x))

def SigmoidPrime(y):
    return y*(1-y)

df = pd.read_csv("admission.txt")
# 构造X和y
X = df[["Exam1","Exam2","Exam3"]].as_matrix()
y = df["Admitted"].as_matrix().reshape(100,1)

W1 = 2 * np.random.random((3,5))-1  # random函数默认返回(0,1)区间上的值
W2 = 2 * np.random.random((5,8))-1
W3 = 2 * np.random.random((8,1))-1

for i in range(iterations):
    L1 = Sigmoid(np.dot(X,W1))
    L2 = Sigmoid(np.dot(L1,W2))
    L3 = Sigmoid(np.dot(L2,W3))
    
    error = L3 - y
    delta3 = error * SigmoidPrime(L3)
    delta2 = delta3.dot(W3.T)*SigmoidPrime(L2)
    delta1 = delta2.dot(W2.T)*SigmoidPrime(L1)
 
    W3 -= alpha * np.dot(L2.T, delta3)
    W2 -= alpha * np.dot(L1.T, delta2)
    W1 -= alpha * np.dot(X.T, delta1) 
    if i%10000 == 0:
        print("Error:",np.mean(np.abs(error)))

输出:

Error: 0.4744584876195863
Error: 0.47999999998146775
Error: 0.47999999998126625
Error: 0.47999999998106035
Error: 0.47999999998084986
Error: 0.4799999999806344
Error: 0.4799999999804141
Error: 0.47999999998018855
Error: 0.479999999979958
Error: 0.4799999999797217
Error: 0.47999999997947973
Error: 0.479999999979232
Error: 0.4799999999789782
Error: 0.47999999997871784
Error: 0.479999999978451
Error: 0.4799999999781772
Error: 0.4799999999778965
Error: 0.4799999999776082
Error: 0.4799999999773125
Error: 0.4799999999770086
Error: 0.4799999999766964
Error: 0.47999999997637555
Error: 0.47999999997604553
Error: 0.4799999999757064

 

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
反向传播算法是神经网络中常用的一种训练方法,其本质是利用链式法则求导,来更新网络参数使得损失函数最小化。下面是用 Python 实现反向传播算法的基本步骤: 1. 前向传播计算损失函数,得到网络输出。 2. 计算每个输出节点对输入的偏导数。 3. 计算每个中间节点对输入的偏导数。 4. 计算每个权重对输入的偏导数。 5. 利用梯度下降法更新每个权重。 下面是一个简单的神经网络反向传播算法Python 代码实现: ```python import numpy as np # 1. 定义神经网络 class NeuralNetwork: def __init__(self, input_size, hidden_size, output_size): self.W1 = np.random.randn(input_size, hidden_size) / np.sqrt(input_size) self.b1 = np.zeros((1, hidden_size)) self.W2 = np.random.randn(hidden_size, output_size) / np.sqrt(hidden_size) self.b2 = np.zeros((1, output_size)) # 2. 前向传播计算损失函数 def forward(self, X): self.z1 = np.dot(X, self.W1) + self.b1 self.a1 = np.tanh(self.z1) self.z2 = np.dot(self.a1, self.W2) + self.b2 self.y_hat = np.exp(self.z2) / np.sum(np.exp(self.z2), axis=1, keepdims=True) # 3. 计算每个输出节点对输入的偏导数 def backward_output(self, X, y): self.delta3 = self.y_hat self.delta3[range(X.shape[0]), y] -= 1 # 4. 计算每个中间节点对输入的偏导数 def backward_hidden(self): self.delta2 = np.dot(self.delta3, self.W2.T) * (1 - np.power(self.a1, 2)) # 5. 计算每个权重对输入的偏导数 def gradient(self, X): self.dW2 = np.dot(self.a1.T, self.delta3) self.db2 = np.sum(self.delta3, axis=0, keepdims=True) self.dW1 = np.dot(X.T, self.delta2) self.db1 = np.sum(self.delta2, axis=0) # 6. 利用梯度下降法更新每个权重 def update(self, learning_rate): self.W1 -= learning_rate * self.dW1 self.b1 -= learning_rate * self.db1 self.W2 -= learning_rate * self.dW2 self.b2 -= learning_rate * self.db2 # 测试 X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]]) y = np.array([0, 1, 1, 0]) nn = NeuralNetwork(2, 3, 2) for i in range(10000): nn.forward(X) nn.backward_output(X, y) nn.backward_hidden() nn.gradient(X) nn.update(0.01) print(nn.y_hat) ``` 上面的代码实现了一个简单的两层神经网络,并且用反向传播算法训练了一个异或门。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值