一、综述
一个Logistics Regression可以认为是一个简单的只含有一个隐藏层(Hidden Layer)构成的神经网络(如下图所示)。
但是往往Logistic Regression对异常值十分敏感,并且在处理多酚类的问题时候比较困难,所以人们收到人脑神经的启发,在Logistic Regression的基础上又建立了更为复杂层级更多的神经网络模型,又叫做深度神经网络(Deep Neural Networks, DNN)
深度神经网络使机器学习(machine learning)发生了颠覆性的变化,使之变为了深度学习(deep learning)。人们一听到deep learning就会觉得很可怕,但是我想说其实看完下面对于DNN代码的书写,你就会发现,其实deep learning也没有那么难。
二、基本原理
如图一个看上去很复杂的网络结构,他的输入是一个具有三个特征的向量,输出可以认为是一个逻辑斯蒂回归分类器,中间有两个隐藏层,我们如图所示,将所有的层都编号为,其中Input Layer记作Output Layer 记作。根据逻辑斯蒂回归的基础告诉我们,我们应该设计一个向前的网络和一个向后的网络来分别计算和梯度grad。如下图所示
三、代码实现
初始化参数
首先我们要初始化我们的参数param(),所以我们设计一个初始化函数:
def init_param(dim, m):
param = {}
L = len(dim)
for i in range(1, L):
param["W" + str(i)] = np.random.randn(dim[i], dim[i - 1])
param["b" + str(i)] = np.random.randn(dim[i], m)
return param
这个函数输入一个dim是网络各层的维度例如图2的输入应为[3, 4, 3, 1], m为数据量。
激活函数和激活函数的导数
import numpy as np
def sigmoid(Z):
g = 1.0 / (1.0 + np.exp(-1.0 * Z))
return g
def d_sigmoid(Z):
s = 1.0 / (1.0 + np.exp(-1.0 * Z))
dg = s * (1-s)
return dg
这里以sigmoid函数为例,如果需要使用其他激活函数,只需加上一个标识识别即可。这里值得注意的是sigmoid函数的导数计算根据斯坦福大学公开课的说法,这里可以表示成dg = s * (1-s),其中s为原函数。
前向传播
由于层次很多我们需要挨个计算前向传播的输出,其中各个步骤之间的关系为
其中是第层的维数,括号中的是维数,可以根据这个维数对我们的代码进行检验。
def forward(A, param):
cache1 = [A]
cache2 = []
L = len(param) // 2
for i in range(1, L + 1):
Z = np.dot(param["W" + str(i)], A) + param["b" + str(i)]
A = sigmoid(Z)
cache2.append(Z)
cache1.append(A)
cache = cache1, cache2
return A, cache
这里的输入 A就是初始传入的X,即训练集,param就是参数对(),这里的parma都是以字典的形式存储。从for循环里就可以看出存储方式。
输出的时候我保留了下一步代价函数中要用的A = ,还保留了一部分数据放在缓存中为之后的反向传播提速。
代价函数
一般的代价函数都用交叉熵进行计算,如下
其对应代码如下:
def cost(A, y):
m = y.shape[1]
J = -(1.0 / m) * np.sum(y* np.log(A) + (1-y)*np.log(1-A))
return J
这里输入的A就是 但是为了统一,我记作A。
反向传播
反向传播因为涉及python的广播机制,所以思考起来比较麻烦,其实就是通过复合函数的求导以及找规律就可以得到第l个隐藏层的反向传播的普遍形式
具体代码如下:
def backward(y, cache, param):
grad = {}
A, Z = cache
L = len(param) // 2
dZ = A[L]-y
grad["dW" + str(L)] = np.dot(dZ, A[L - 1].T) / m
grad["db" + str(L)] = np.sum(dZ) / m
for i in range(L-1, 0, -1):
dA = np.dot(param["W" + str(i+1)].T, dZ)
dg = d_sigmoid(Z[i - 1])
dZ = dA * dg
grad["dW" + str(i)] = np.dot(dZ, A[i - 1].T) / m
grad["db" + str(i)] = np.sum(dZ) / m
return grad
输入目标(target)值y,缓存cache,参数param,返回一个梯度grad。
更新参数
这个部分比较简单,公式如下:
代码如下:
def update_param(param, grad, alpha):
L = len(param) // 2
for i in range(1, L):
param["W" + str(i)] -= alpha * grad["dW" + str(i)]
param["b" + str(i)] -= alpha * grad["db" + str(i)]
return param
整合model
def train(X, Y, layer_dim, alpha, epochs):
m = X.shape[1]
param = init_param(layer_dim, m)
for i in range(epochs):
A, cache = forward(X, param)
J = cost(A, Y)
grad = backward(Y, cache, param)
update_param(param, grad, alpha)
print(J)
return J, param
将所有的函数放进训练函数里,构成一个模型。