神经网络与反向传播算法
模型是前馈神经网络,优化方法是梯度下降法,求偏导的方式是反向传播算法,数据集依然是手写数字。
本文关于反向传播算法 BP,附了一些数学解释,详细讲解了算法过程。
关于前向传播算法 FP,可以参考:吴恩达机器学习CS229A_EX3_LR与NN手写数字识别_Python3
特别注意:区分矩阵点乘 @ 和矩阵乘法 * ,写错的话可能会导致难以 debug 的错误。
导入并初始化数据,这里用了 sklearn 的库函数生成读取的标签集对应 one-hot 的输出形式。
将 theta1 和 theta2 以向量形式初始化(用于后续给执行梯度下降的库函数传参),并且将所有数值随机化到 -0.125 ~ +0.125 之间,如果不做随机,神经网络的权重参数会出现大量相同的冗余情况。
import numpy as np
from scipy.io import loadmat
from scipy.optimize import minimize
from sklearn.preprocessing import OneHotEncoder
def loadData(filename):
return loadmat(filename)
def initData(data, input_size, hidden_size, output_size):
# X
X = data['X']
# y
y_load = data['y']
encoder = OneHotEncoder(sparse=False)
y = encoder.fit_transform(y_load)
# 随机化 theta1/theta2 in vectors
params = (np.random.random(size = hidden_size * (input_size + 1) + output_size * (hidden_size + 1)) - 0.5) * 0.25
return X, y_load, y, params
两个辅助函数:
def sigmoid(z):
return 1 / (1 + np.exp(-z))
def sigmoid_gradient(z):
return sigmoid(z) * (1 - sigmoid(z))
神经网络的结构是输入层 400+1,隐藏层 25+1,输出层 10:
前向传播算法(EX3 中已做):
def FP(X, theta1, theta2):
m = X.shape[0]
a1 = np.insert(X, 0, values=np.ones(m), axis=1)
z2 = a1 @ theta1.T
a2 = np.insert(sigmoid(z2), 0, values=np.ones(m), axis=1)
z3 = a2 @ theta2.T
h = sigmoid(z3)
return a1, z2, a2, z3, h
根据公式计算 cost:
def cost(X, y, theta1, theta2, lamda):
m = len(y)
a1, z2, a2, z3, h = FP(X, theta1, theta2)
J = 0
for i in range(m):
first = - y[i,:] * np.log(h[i,:])
second = - (1 - y[i,:]) * np.log(1 - h[i,:])
J += np.sum(first + second)
J = J / m
# 正则化项
J += (float(lamda) / (2 * m)) * (np.sum(np.power(theta1[:,1:], 2)) + np.sum(np.power(theta2[:,1:], 2)))
return J
整个程序的难点是反向传播算法,先简述一下算法原理(这部分吴恩达老师的课程讲的很简略,最好还是找一些资料补一下):
首先要明白一点,BP 做的事情是求代价函数 J</