欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

python徒手实现反向传播算法

程序员文章站 2024-01-26 10:20:22
...

1. 项目背景

python徒手实现反向传播算法

假设有如上图所示的全连接神经网络,该网络共有三层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)所示:

python徒手实现反向传播算法

 

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 损失函数

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

python徒手实现反向传播算法

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

5.2 求偏导

python徒手实现反向传播算法

求导过程中注意

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

python徒手实现反向传播算法

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

python徒手实现反向传播算法

 

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

python徒手实现反向传播算法

 

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