设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 重新 试卷 文件
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

100行Python代码,轻松搞定神经网络(2)

发布时间:2019-05-05 23:07 所属栏目:21 来源:大数据文摘
导读:核心想法其实始终未变。从我们在学校学习如何求导时, 就应该知道这一点了。如果我们能够追踪最终求出标量输出的计算, 并且我们知道如何对简单操作求导 (例如加法、乘法、幂、指数、对数等等), 我们就可以算出输出的

核心想法其实始终未变。从我们在学校学习如何求导时, 就应该知道这一点了。如果我们能够追踪最终求出标量输出的计算, 并且我们知道如何对简单操作求导 (例如加法、乘法、幂、指数、对数等等), 我们就可以算出输出的梯度。

假设我们有一个线性的中间层f,由矩阵乘法表示(暂时不考虑偏置):

为了用梯度下降法调整w值,我们需要计算梯度∂l/∂w。这里我们可以观察到,改变y从而影响l是一个关键。

每一层都必须满足下面这个条件: 如果给出了损失函数相对于这一层输出的梯度, 就可以得到损失函数相对于这一层输入(即上一层的输出)的梯度。

现在应用两次链式法则得到损失函数相对于w的梯度:

相对于x的是:

因此, 我们既可以后向传递一个梯度, 使上一层得到更新并更新层间权重, 以优化损失, 这就行啦!

动手实践

先来看看代码, 或者直接试试Colab Notebook:

https://colab.research.google.com/github/eisenjulian/slides/blob/master/NN_from_scratch/notebook.ipynb

我们从封装了一个张量及其梯度的类(class)开始。

现在我们可以创建一个layer类,关键的想法是,在前向传播时,我们返回这一层的输出和可以接受输出梯度和输入梯度的函数,并在过程中更新权重梯度。

然后, 训练过程将有三个步骤, 计算前向传递, 然后后向传递, 最后更新权重。这里关键的一点是把更新权重放在最后, 因为权重可以在多个层中重用,我们更希望在需要的时候再更新它。

  1. class Layer: 
  2.   def __init__(self): 
  3.     self.parameters = [] 
  4.  
  5.   def forward(self, X): 
  6.     """ 
  7.     Override me! A simple no-op layer, it passes forward the inputs 
  8.     """ 
  9.     return X, lambda D: D 
  10.  
  11.   def build_param(self, tensor): 
  12.     """ 
  13.     Creates a parameter from a tensor, and saves a reference for the update step 
  14.     """ 
  15.     param = Parameter(tensor) 
  16.     self.parameters.append(param) 
  17.     return param 
  18.  
  19.   def update(self, optimizer): 
  20.     for param in self.parameters: optimizer.update(param) 

标准的做法是将更新参数的工作交给优化器, 优化器在每一批(batch)后都会接收参数的实例。最简单和最广为人知的优化方法是mini-batch随机梯度下降。

  1. class SGDOptimizer(): 
  2.   def __init__(self, lr=0.1): 
  3.     self.lr = lr 
  4.  
  5.   def update(self, param): 
  6.     param.tensor -= self.lr * param.gradient 
  7.     param.gradient.fill(0) 

在此框架下, 并使用前面计算的结果后, 线性层如下所示:

  1. class Linear(Layer): 
  2.   def __init__(self, inputs, outputs): 
  3.     super().__init__() 
  4.     tensor = np.random.randn(inputs, outputs) * np.sqrt(1 / inputs) 
  5.     selfself.weights = self.build_param(tensor) 
  6.     selfself.bias = self.build_param(np.zeros(outputs)) 
  7.  
  8.   def forward(self, X): 
  9.     def backward(D): 
  10.       self.weights.gradient += X.T @ D 
  11.       self.bias.gradient += D.sum(axis=0) 
  12.       return D @ self.weights.tensor.T 
  13.     return X @ self.weights.tensor +  self.bias.tensor, backward 

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读