深层神经网络

时间:2021-11-22 10:12:43

1.神经网络中的参数

深层神经网络

假设神经网络共有L层,m为样本数量,n[l]代表网络中第l层的节点数,w[l]和b[l]为第l层的参数,X为输入,Y为输出。则各参数的维度为:

w[l]:(n[l],n[l-1]);  b[l] :(n[l],1);  X:(n[1],m);  Y:(n[L],m);  Z[l],A[l]:(n[l],m)

其中Z[l]、A[l]的计算如下(A[0]即为X):

Z[l]  = w[l]A[l-1]  + b[l]   

A[l] = g(Z[l] )

其中g(z)函数为激活函数,常用的激活函数有sigmoid函数、tanh函数、ReLU函数、Leaky ReLU函数,目前隐藏层一般使用ReLU函数或Leaky ReLU函数,sigmoid函数和tanh函数在z值较大时斜率趋近于0,会拖慢梯度下降法,所以不推荐使用(tanh函数相较于sigmoid函数表现好一些),除非是解决二元分类问题,才会在输出层使用sigmoid函数。

w参数在初始化时不可初始化为0,可以采用随机数进行初始化,例如在Python中,可以对w[l]进行如下初始化:np.random.randn(n[l], n[l-1]) * 0.01。b参数可以直接初始化为0。

2.梯度下降法

loss function(损失函数)和cost function(成本函数):损失函数是对于一个样本来说,预测的结果与真实的结果之间的差值,成本函数则是对于所有的样本来说,对应的误差值。它们的求取公式如下:

L(y^,y) = -(ylogy^ + (1-y)log(1-y^)),其中y^为网络中最后一层的输出,也即A[L]

深层神经网络

对式子中的w,b等参数进行求导,得到以下的式子:

dZ[l] = dA[l] * g'(Z[l]),其中*表示矩阵中逐个元素相乘,g'(Z[l])是对激活函数进行求导

dw[l] = (dZ[l]A[l-1]T)/m,这里是矩阵相乘,A[l-1]T代表A[l-1]矩阵的转置矩阵

db[l] = np.sum(dZ[l],axis=1,keepdims=True)/m,其实就是对矩阵dZ[l]每行相加求和,之后除以m,得到列数为1的矩阵

dA[l-1] = w[l]TdZ[l],矩阵相乘

其中最初始的dA[L] = -(np.divide(Y,AL) - np.divide(1-Y,1-AL))。该式子运用了微积分知识,不需要深入理解该公式的推导。

关于a = g(z),g'(z])为函数的导数。例如当g为relu函数时,在z大于时,其值为1;否则,为0;当g为sigmoid函数时,求导为a-a2;当g为tanh函数时,求导为1-a2。详情可见下一部分的Python代码。

下面进行参数的更新:

w = w - learning_rate*dw

b = b - learning_rate*db

3.正则化

为了防止神经网络在训练过程中发生过拟合,需要进行正则化操作。下面先简单介绍下过拟合、欠拟合。

过拟合:也称高方差(high variance),具体表现为测试集误差相较于训练集误差来说,误差较大。针对过拟合常用的解决方法有:1)使用更多的训练数据;2)进行正则化操作;3)尝试使用更合适的神经网络架构。

欠拟合:也称高偏差(high bias),具体表现为无论是对测试集来说还是训练集来说,误差都很大。针对欠拟合常用的解决方法有:1)尝试使用更大的神经网络(例如,增加隐藏层层数、增加层中的节点数等);2)尝试使用新的神经网络架构。

深层神经网络

 

上面的图片很直观地表现出了过拟合、欠拟合的特点。下面介绍下解决过拟合常用的一种方法:正则化,这里介绍两种正则化方法:1)L2正则化;2)Dropout正则化。

(1)L2正则化

L2正则化是在原来的cost function后面加上了一个正则化项:

深层神经网络

cost function相应变成了(其中L表示神经网络的层数,没有把输入层算进去):

深层神经网络

由于cost function发生了变化,则back propagation中的dw也发生改变:

dw[l] = (dZ[l]A[l-1]T)/m + (lambda/m)w[l]

lambada是一个超参数,需要不断尝试才能找到效果最好的值。

(2)Dropout正则化

Dropout正则化方法在训练过程中随机地忽略一些神经元,因为是随机的,所以对于不同的训练样本,忽略的神经单元也不同。这么做的效果就是,网络模型对神经元特定的权重不那么敏感。这反过来又提升了模型的泛化能力,不容易对训练数据过拟合。具体做法为:

1)设定一个概率阈值keep_prob,代表神经网络中网络节点被保留的概率,例如设keep_prob=0.8,则表示节点被忽略的概率为0.2,被保留的概率为0.8;

2)神经网络每一层都设置一个对应的过滤矩阵D,用来随机忽略该层的节点。对矩阵Dx进行初始化,它对应神经网络的第x层,Dx中元素的值域为[0,1),神经网络第x层的输出为Ax。用Python实现即,Dx = np.random.rand(Ax.shape[0], Ax.shape[1]);

3)对Dx中的元素,把小于0.8的置为1,否则置为0。用Python实现即,Dx = Dx<keep_prob;

4)随机过滤神经网络中x层的节点,即把该节点的输出结果忽略掉。用Python实现:Ax = np.multiply(Ax, Dx);

5)弥补Ax中被忽略的元素,试图使Ax的期望值保持不变:Ax = Ax/keep_prob;

6)在back propagation中dAx也要乘以Dx,且最后也要除以keep_prob。

以上即是Dropout方法的执行过程,注意:在测试阶段不使用Dropout,并且神经网络中不同层的keep_prob值可以不同。

Dropout正则化方法最常使用在计算机视觉中。

 

谈一点关于数据集的东西。一般把数据集分为训练集、验证集和测试集三类。训练集就是用来训练模型的;验证集则是在训练出的模型的基础上调整一些参数,以期得到最优的模型;测试集用来检验“最优模型”的性能。一般训练集占到总数据集的60%,验证集和测试集各占20%。不过在数据集很大时,比如已经达到了百万级别,训练集可以占到98%,99%甚至更高。有时数据集中没有测试集,这也是可以的,测试集的主要目的是对最终选定的神经网络做出无偏估计,但如果不需要无偏估计,也可以不设置测试集。所以如果只有验证集,没有测试集,我们要做的就是在训练集上训练,尝试不同的模型框架,在验证集上评估这些模型,然后迭代出适用的模型。不过人们经常在只有训练集和验证集的情况下,把验证集称作测试集,但在实际应用中,人们只是把所谓的“测试集”当成简单交叉验证集使用。

3.提升神经网络训练速度

通过对输入进行归一化,以及合理地初始化w的值,可以提升神经网络的训练速度。

(1)归一化

零均值化:

深层神经网络

深层神经网络

归一化方差:

深层神经网络

深层神经网络

其原理可以同过以下两张图来解释。其中左边的那张图是归一化之前的,右边那张图是归一化之后的,中心的圆点即是cost function最小的地方。

深层神经网络

注意:要使用相同的均值和标准差来归一化测试集。

(2)初始化w

给一个好的起点,也能使神经网络尽快训练出好的模型。当激活函数为relu时,可以这样初始化w:

深层神经网络

而当激活函数为tanh时,后面相乘的那一项可以是:

深层神经网络

4.编程示例

import numpy as np
import matplotlib.pyplot as plt
from pylab import *

def sigmoid(z):
s = 1/(1+np.exp(-z))
return s, z

def relu(z):
s = np.maximum(z, 0)
return s, z

def sigmoid_derivative(x):
s = 1/(1+np.exp(-x))
dz = s - np.power(s, 2)
return dz

def relu_derivative(x):
dz = (x > 0) * 1 + (x <= 0) * 0
return dz

def init_parameters(layer_dims):
L = len(layer_dims)
parameters = {}
for l in range(1, L):
parameters["W" + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1])*0.01
parameters["b" + str(l)] = np.zeros((layer_dims[l], 1))

return parameters

def linear_forword(A, W, b):
Z = np.dot(W, A) + b
cache = (A, W, b) #保存A, W和b
return Z, cache

def linear_activation_forword(A_prev, W, b, activation):
if activation == "relu":
Z, linear_cache = linear_forword(A_prev, W, b)
A, activation_cache = relu(Z)
elif activation == "sigmoid":
Z, linear_cache = linear_forword(A_prev, W, b)
A, activation_cache = sigmoid(Z)

cache = (linear_cache, activation_cache) #前一个保存A_prev,W,b;后一个保存z
return A, cache

def L_model_forward(X, parameters):
caches = []
A = X
L = len(parameters) // 2
for l in range(1, L):
A_prev = A
A, cache = linear_activation_forword(A_prev, parameters['W'+str(l)], parameters['b'+str(l)], 'relu')
caches.append(cache)

AL, cache = linear_activation_forword(A, parameters['W'+str(L)], parameters['b'+str(L)], 'sigmoid')
caches.append(cache)

return AL, caches

def compute_cost(AL, Y):
m = Y.shape[1]
cost = -(np.dot(Y, np.log(AL).T) + np.dot(1-Y, np.log(1-AL).T))/m
cost = np.squeeze(cost)
return cost

def linear_backward(dZ, cache):
A_prev, W, b = cache
m = A_prev.shape[1]

dW = np.dot(dZ, A_prev.T)/m
db = np.sum(dZ, axis=1, keepdims=True)/m
dA_prev = np.dot(W.T, dZ)

return dA_prev, dW, db

def linear_activation_backward(dA, cache, activation):
linear_cache, activation_cache = cache
if activation == "sigmoid":
dZ = dA * sigmoid_derivative(activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
elif activation == "relu":
dZ = dA * relu_derivative(activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)

return dA_prev, dW, db

def L_model_backward(AL, Y, caches):
grads = {}
L = len(caches) #神经网络层数
m = AL.shape[1]
Y = Y.reshape(AL.shape)

dAL = -(np.divide(Y,AL) - np.divide(1-Y,1-AL))

current_cache = caches[L-1]
grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, "sigmoid")

for l in reversed(range(L-1)):
current_cache = caches[l]

grads["dA" + str(l+1)], grads["dW" + str(l+1)], grads["db" + str(l+1)] = linear_activation_backward(grads["dA" + str(l+2)], current_cache, "relu")

return grads

def update_parameters(parameters, grads, learning_rate):
L = len(parameters) // 2

for l in reversed(range(L)):
parameters["W" + str(l+1)] = parameters["W" + str(l+1)] - learning_rate*grads["dW" + str(l+1)]
parameters["b" + str(l+1)] = parameters["b" + str(l+1)] - learning_rate*grads["db" + str(l+1)]

return parameters

 

#主函数 

tmp=open('ex2data2.txt').read()
data=tmp.replace(',',' ')
data=data.replace('\n',' ')
#data=data.strip('\n')
data=data.split(' ')
data = [float(x) for x in data ]
data = np.array(data)
data = data.reshape(-1,3)
#print(data);

X = data[:,0:2]
Y = data[:,2]
Y = Y.reshape(118,1)
X = X.T
Y = Y.T

layers_dims = [2, 5, 1] # 5-layer model
learning_rate = 0.5

parameters = init_parameters(layers_dims)

for i in range(0, 5000):
AL, caches = L_model_forward(X, parameters)
cost = compute_cost(AL, Y)
grads = L_model_backward(AL, Y, caches)
parameters = update_parameters(parameters, grads, learning_rate)
print(cost)