Deep Learning
本文是学习深度学习入门时系统笔记。
参考资料:《deep learning》 MIT出版社和图灵参考丛书《深度学习入门(基于python的理论与实现)》。
[TOC]
Perception
多输入单输出逻辑元。两个过程:计算-->激活
典型逻辑电路AND,OR,NOT均可以表示为单层感知器,具有以下要素:
- weight : w=[w1,w2]
- bias : theta
- input : x=[x1,x2]
- output : y
- z : activation function
并有
根据真值表设定w,b后代码如下
def AND(x):
w=np.array([0.5,0.5])
theta=-0.7
y=np.sum(x*w)+theta
if y>0:
return 1
elif y<0:
return 0
A simple network
-
初始化(设定每层个数、weight & bias)
-
设置中间变量和激活函数
z1=sigmoid(np.dot(x,W1)+b1) z2=sigmoid(np.dot(z1,W2)+b2
-
设置一层softmax:取为真概率最大的预测(用于分类)
-
预测
Softmax
用于分类的输出层,输出0-1的实数而且所有的函数值之和为1,作为输出属于某类别的概率
注意:softmax进行了e函数计算,容易发生溢出错误
改进为
Loss function
衡量预测值与真值差距的性能指标,一般用均方差(mean squared error)或者交叉熵(Cross entropy error)
def mean_square_error(y,t):
return(np.sum((y-t)^2)/2)
def cross_entropy_error(y,t):
delta=1e-10# avoid log(0)
return(np.sum(t*np.log(y+delta)))
def cross_entropy_error_batch(y,t,):
if y.ndim=1:
t=t.reshape(1,t.size)
y=y.reshape(1,y.size)
batch_num=y.shape[0]
return(np.sum(t*np.log(y+1e-10))/batch_num)
为什么设定损失函数而不是识别精度作为指标?
识别精度是离散的,而损失函数是连续的,离散函数作为优化目标导致导数大部分为0,无法使用梯度相关的算法优化。
activiation function
表示计算值使得神经元被激活(由计算值到output)的方式。
Functions:
- step-function
- sigmoid
- Relu
等激活函数
激活函数可以写成一个类,如ReLu写为
class Relu:
def __init__(self):
self.mask = None
def forward(self, x):
self.mask = (x <= 0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self, dout):
dout[self.mask] = 0
dx = dout
return dx
# sigmoid
class Sigmoid:
def __init__(self):
self.out = None
def forward(self, x):
out = sigmoid(x)
self.out = out
return out
def backward(self, dout):
dx = dout * (1.0 - self.out) * self.out
return dx
Gradient
即数值微分在多元情况下。
# 求近似微分
def numerical_diff(f, x):
h = 1e-4 # 0.0001
return (f(x+h) - f(x-h)) / (2*h)
def numerical_grad(f,x):
h=1e-4
grad=np.zeros_like(x)
for i in range(x.size):
fx1=(float(x[i])+h)
fx2=(float(x[i])-h)
grad[i]=(fx1-fx2)/(2*h)
return grad
def numerical_grad_batch(f, X):
if x.ndim ==1:
return numerical_grad(f,X)
else:
grad =np.zeros_like(X)
for i,x in enumerate(X):
grad[i]=numerical_grad(f,x)
return grad
Gradient descent
计算步骤:
- 选定初始点
- 执行梯度计算
- 梯度下降
- 返回
def gradient_descent(f,learn_rate):
x=np.array([np.random.rand(),np.random.rand()])
x_history=[]
x_new=x-learn_rate*gd.numerical_grad(f,x)
while ((x_new-x).all()>1e-5):
x=x_new
x_new=x-learn_rate*gd.numerical_grad(f,x)
x_history.append(x_new.copy())
return x_new,x_history
Gradient in DL
指的是损失函数关于权重的梯度
Backforward
链式法则向后传播求取导数
此处应有计算图
Simple Layer
将神经网络层实现为一个单位。
Multiplelayer:
class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self, x, y):
self.x = x
self.y = y
out = x * y
return out
def backward(self, dout):
dx = dout * self.y
dy = dout * self.x
return dx, dy
Addlayer
加法层无需特别的初始化。
Affline
仿射变换:矩阵的乘积运算。
在affline层中务必注意变量的Shape。
完成的是y=wx+b操作。
class Affine:
def __init__(self, W, b):
self.W =W
self.b = b
self.x = None
self.original_x_shape = None
# 权重和偏置参数的导数
self.dW = None
self.db = None
def forward(self, x):
# 对应张量
self.original_x_shape = x.shape
x = x.reshape(x.shape[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis=0)
dx = dx.reshape(*self.original_x_shape) # 还原输入数据的形状(对应张量)
return dx
SoftmaxwithLoss
带交叉熵损失层的softmax反向传播可以得到漂亮的结果
可见,反向传播中该结果直接衡量了与t标签的差距,即误差传播法。
[图片上传失败...(image-5945d8-1576044079302)]
Optimization
优化器一般定义为一个optimizer
父类,在父类下定义不同的更新方法Update(grad,param)
,在神经网络中选取不同optimizer
优化。
Stochastic gradient descent
随机梯度下降=在随机选取的mini-batch数据中实行梯队下降优化,即最简单的方法。
class SGD:
def __init__(self, lr=0.01):
self.lr = lr
def update(self, params, grads):
for key in params.keys():
params[key] -= self.lr * grads[key]
Momentum
v表示速度,在梯度方向受力的动量增加
class Momentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
params[key] += self.v[key]
AdaGrad
自适应学习率的优化方法,更新参数时除h能减少变动较大的元素学习率。
class AdaGrad:
def __init__(self, lr=0.01):
self.lr = lr
self.h = None
def update(self, params, grads):
if self.h is None:
self.h = {}
for key, val in params.items():
self.h[key] = np.zeros_like(val)
for key in params.keys():
self.h[key] += grads[key] * grads[key]
params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
Adam
融合了momemum和adagrad(adaptive momemum),MIT DL教材189,论文来自
衰减矩估计来自适应学习。g为梯度,上面为有偏估计,下面是无偏修正。
class Adam:
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.iter = 0
self.m = None
self.v = None
def update(self, params, grads):
if self.m is None:
self.m, self.v = {}, {}
for key, val in params.items():
self.m[key] = np.zeros_like(val)
self.v[key] = np.zeros_like(val)
self.iter += 1
lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)
for key in params.keys():
self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
Regularization
过拟合可能来自于过大权值、数据量过少等等。
防止过拟合的措施
Dropout
随机删除(前向传播时x删除比例,后向传播时不返回值)部分隐藏层神经元。
weight decay
对较大的权值进行惩罚(在损失函数中加上惩罚项L2范数),L2范数即所有项的平方和。
其中 为衰减率。
网友评论