无论即将到来的是大数据时代还是人工智能时代, 亦或是传统行业使用人工智能在云上处理大数据的时代, 作为一个有理想有追求的程序员, 不懂深度学习 (Deep Learning) 这个超热的技术, 会不会感觉马上就 out 了?
现在救命稻草来了, 零基础入门深度学习系列文章旨在讲帮助爱编程的你从零基础达到入门级水平. 零基础意味着你不需要太多的数学知识, 只要会写程序就行了, 没错, 这是专门为程序员写的文章. 虽然文中会有很多公式你也许看不懂, 但同时也会有更多的代码, 程序员的你一定能看懂的 (我周围是一群狂热的 Clean Code 程序员, 所以我写的代码也不会很差).
文章列表
零基础入门深度学习 (1) - 感知器
零基础入门深度学习 (2) - 线性单元和梯度下降
零基础入门深度学习 (3) - 神经网络和反向传播算法
零基础入门深度学习 (4) - 卷积神经网络
零基础入门深度学习 (5) - 循环神经网络
零基础入门深度学习 (6) - 长短时记忆网络 (LSTM)
零基础入门深度学习 (7) - 递归神经网络
- #!/usr/bin/env python
- # -*- coding: UTF-8 -*-
- import numpy as np
- from cnn import IdentityActivator
- class TreeNode(object):
- def __init__(self, data, children=[], children_data=[]):
- self.parent = None
- self.children = children
- self.children_data = children_data
- self.data = data
- for child in children:
- child.parent = self
- # 递归神经网络实现
- class RecursiveLayer(object):
- def __init__(self, node_width, child_count,
- activator, learning_rate):
- ''''''
- self.node_width = node_width
- self.child_count = child_count
- self.activator = activator
- self.learning_rate = learning_rate
- # 权重数组 W
- self.W = np.random.uniform(-1e-4, 1e-4,
- (node_width, node_width * child_count))
- # 偏置项 b
- self.b = np.zeros((node_width, 1))
- # 递归神经网络生成的树的根节点
- self.root = None
- def forward(self, *children):
- '''前向计算'''
- children_data = self.concatenate(children)
- parent_data = self.activator.forward(
- np.dot(self.W, children_data) + self.b
- )
- self.root = TreeNode(parent_data, children
- , children_data)
- def concatenate(self, tree_nodes):
- '''将各个树节点中的数据拼接成一个长向量'''
- concat = np.zeros((0,1))
- for node in tree_nodes:
- concat = np.concatenate((concat, node.data))
- return concat
- def backward(self, parent_delta):
- '''BPTS 反向传播算法'''
- self.calc_delta(parent_delta, self.root)
- self.W_grad, self.b_grad = self.calc_gradient(self.root)
- def calc_delta(self, parent_delta, parent):
- '''计算每个节点的 delta'''
- parent.delta = parent_delta
- if parent.children:
- # 根据式 2 计算每个子节点的 delta
- children_delta = np.dot(self.W.T, parent_delta) * (
- self.activator.backward(parent.children_data)
- )
- # slices = [(子节点编号, 子节点 delta 起始位置, 子节点 delta 结束位置)]
- slices = [(i, i * self.node_width,
- (i + 1) * self.node_width)
- for i in range(self.child_count)]
- # 针对每个子节点, 递归调用 calc_delta 函数
- for s in slices:
- self.calc_delta(children_delta[s[1]:s[2]],
- parent.children[s[0]])
- def calc_gradient(self, parent):
- '''计算每个节点权重的梯度, 并将它们求和, 得到最终的梯度'''
- W_grad = np.zeros((self.node_width,
- self.node_width * self.child_count))
- b_grad = np.zeros((self.node_width, 1))
- if not parent.children:
- return W_grad, b_grad
- parent.W_grad = np.dot(parent.delta, parent.children_data.T)
- parent.b_grad = parent.delta
- W_grad += parent.W_grad
- b_grad += parent.b_grad
- for child in parent.children:
- W, b = self.calc_gradient(child)
- W_grad += W
- b_grad += b
- return W_grad, b_grad
- def update(self):
- '''使用 SGD 算法更新权重'''
- self.W -= self.learning_rate * self.W_grad
- self.b -= self.learning_rate * self.b_grad
- def gradient_check():
- '''梯度检查'''
- # 设计一个误差函数, 取所有节点输出项之和
- error_function = lambda o: o.sum()
- rnn = RecursiveLayer(2, 2, IdentityActivator(), 1e-3)
- # 计算 forward 值
- x, d = data_set()
- rnn.forward(x[0], x[1])
- rnn.forward(rnn.root, x[2])
- # 求取 sensitivity map
- sensitivity_array = np.ones((rnn.node_width, 1),
- dtype=np.float64)
- # 计算梯度
- rnn.backward(sensitivity_array)
- # 检查梯度
- epsilon = 10e-4
- for i in range(rnn.W.shape[0]):
- for j in range(rnn.W.shape[1]):
- rnn.W[i,j] += epsilon
- rnn.reset_state()
- rnn.forward(x[0], x[1])
- rnn.forward(rnn.root, x[2])
- err1 = error_function(rnn.root.data)
- rnn.W[i,j] -= 2*epsilon
- rnn.reset_state()
- rnn.forward(x[0], x[1])
- rnn.forward(rnn.root, x[2])
- err2 = error_function(rnn.root.data)
- expect_grad = (err1 - err2) / (2 * epsilon)
- rnn.W[i,j] += epsilon
- print 'weights(%d,%d): expected - actural %.4e - %.4e' % (
- i, j, expect_grad, rnn.W_grad[i,j])
- return rnn
来源: https://juejin.im/entry/5996644f51882524363c2cdb