Recurrent Neural Network - RNN
为了更好地理解RNN的框架,我们将会用numpy手写RNN的各种layers。之后再用tensorflow 的keras来直接用函数定义layers。所以首先要理解RNN,RNN的简单结构我们是知道的,如下图所示,可以注意到对于多次隐层的循环,用的权值是一样的。
下面将会参考这个网站提供的例子来理解 RNN的网络结构:
http://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/
我们将会用python简单的建立 两个input,16个隐藏层,一个output。比如是 加法的运算。a+b = c
比如 a = 12 ,变成二进制 2^3 + 2 ^ 2 ,才用8个数。即[0,0,0,0,1,1,0,0]
b = 4 即 [0,0,0,0,0,1,0,0]
所以c应该为 16 即为[0,0,0,1,0,0,0,0] 作为ground truth。我们将会用RNN来预测 任意的两个二进制数字相加的结果,并跟ground truth做对比。
所以首先是来create 二进制的数据集
我们将会用np.unpackbits来转换十进制整数为二进制。下面便是np.unpackbits的用法
a = np.array([[2], [7], [23]], dtype=np.uint8)
a.shape #3行1列
b = np.unpackbits(a, axis=1)
#变成二进制
b # 2^1 = 2 ; 2^2+2^1+2^0 = 7 ; 2^4+2^2+2^1+2^0 =16+4+2+1=23
所以,我们将会创建2^8 = 256个zheng
Data Generation
import copy
import numpy as np
np.random.seed(123)
#创建 二进制的字典 -- 方便随机提取
int2binary = {
}
#定义8个数字
binary_dim = 8
largest_num = pow(2,binary_dim) #2**8 = 256
#把256个数 全部转化为 二进制 即有256个 二进制
binary = np.unpackbits(np.array([range(largest_num)],dtype=np.uint8).T,axis=1)
#然后把binary放入字典int2binary,方便随机提取
for i in range(largest_num):
int2binary[i] = binary[i]
设置RNN网络参数
#设置网络维度
alpha = 0.1
input_dim = 2
hidden_dim = 16
output_dim = 1
#设置网络weights 权重参数
w0 = 2*np.random.random((input_dim,hidden_dim))-1 #随机设置(2,16)的参数
print(w0.shape)
w1 = 2*np.random.random((hidden_dim,output_dim)) - 1
print(w1.shape)
wh = 2*np.random.random((hidden_dim,hidden_dim)) - 1
print(wh.shape)
(2, 16)
(16, 1)
(16, 16)
同时也要准备好 backprop 的数据 ,即delta gradient的值
dw0 = np.zeros_like(w0)
dw1 = np.zeros_like(w1)
dwh = np.zeros_like(wh)
RNN的作用是 根据 两个二进制 a , b 来 predict a + b 的值。
下面是训练的过程 ,重复10000次 ,然后求导更新pred值,到最后的预测已经很准了。
#定义sigmoid函数
def sigmoid(x):
output = 1/(1+np.exp(-x))
return output
#定义sigmoid的求导 derivate
def sigmoid_grads(s):
d = s*(1-s)
return d
#定义一个函数 把二进制转换为 十进制
def bin2dec(b):
out = 0
for i, x in enumerate(b[::-1]):
out += x * pow(2, i)
return out
#这部分是为了画图~ 画出 accs和errs via epoch
errs = list()
accs = list()
error = 0
accuracy = 0
对于每次feedward之后预测完,便从后面到前面开始求导~来更新权重values
for n in range(10000):
#随机产生数字 a 和 b
############################################################
#256/2 在1-128内中随机找一个参数
a_int = np.random.randint(largest_num/2)
#然后用随机产生的num在字典那里提取相应的二进制数字
a = int2binary[a_int]
#同理,用随机产生的num 来提取二进制的数值
b_int = np.random.randint(largest_num/2)
b = int2binary[b_int]
#然后同理产生 ground true c
c_int = a_int + b_int
c = int2binary[c_int]
############################################################
#现在开始RNN网络的建立 来pred a和b相加
#开始预测 c ,于是创建 d的形状
d = np.zeros_like(c)
#创建error
total_error = 0
#创建forward的时候 layer1的values 值,然后初始化为0 ,有hidden_dim=16个0,作为隐藏层
H = list()
H.append(np.zeros(hidden_dim))
#创建layer2 往后的求导
layer2_grads = list()
############################################################
#现在往前计算 feedward
for i in range(binary_dim):
#往前的公式是
#layer1 = sigmoid(x * w0+ h0 * wh) 这里暂时忽略bias
#layer2 = sigmoid(layer1 * h1),这里也暂时忽略 bias
#首先提取a,b和c的8个数 的每一个 数字
X = np.array([[a[binary_dim - i - 1],b[binary_dim - i - 1]]])
y = np.array([[c[binary_dim - i - 1]]]).T
#开始计算feedward,layer1_values[-1]代表最后一层layer1
layer1 = sigmoid(np.dot(X,w0)+np.dot(H[-1],wh))
#layer1的shape是 (1,16)
#放入隐藏层H
H.append(copy.deepcopy(layer1))
layer2 = sigmoid(np.dot(layer1,w1))
#layer2的shape是 (1)
d[binary_dim - i - 1] = np.round(layer2[0][0])
#feedward之后,需要计算error值,跟c相对比 这里即y和layer2对比。
layer2_error = y - layer2 #目标函数
total_error += np.abs(layer2_err[0])
#然后对目标函数 往后面求导 即 layer2_error * derivate
#求导 求到 w1 那里 的整体 后面需要乘 layer1
grad = layer2_error * sigmoid_derivative(layer2)
layer2_grads.append(grad)
future_layer1_grad = np.zeros(hidden_dim)
############################################################
############################################################
#现在往后计算 backward
for j in range(binary_dim):
#从二进制的前头开始求导~
X = np.array([[a[j],b[j]]])
layer1 = =H[-j - 1] #提取最后的隐藏层
prev_layer1 =H[-j-2] ##再往前提取一层 算数
#从layer2的导数list提取最后一个 来计算 这是为了计算dw1 = layer1* grads(layers)
layer2_grad = layer2_grads[-(j+1)]
dw1 += np.atleast_2d(layer1).T.dot(layer2_grad)
#开始backprop计算 dw0,dwh 这里的backprop也是有关系 前者的back会影响后面的backward
layer1_grad = (future_layer1_grad.dot(wh.T) + layer2_grad.dot(w1.T)) * sigmoid_grads(layer1)
dwh += np.atleast_2d(prev_layer1).T.dot(layer1_grad)
dw0 += X.T.dot(layer1_grad)
future_layer1_grad = layer1_grad
w0 += dw0 * alpha
w1 += dw1 * alpha
wh += dwh * alpha
dw0 *= 0
dw1 *= 0
dwh *= 0
#更新结束
############################################################
############################################################
#这部分是为了画图
error += total_error
if (bin2dec(d) == c_int):
accuracy += 1
if (n % 20 == 0):
errs.append(error / 20)
accs.append(accuracy / 20)
error = 0
accuracy = 0
############################################################
if(n % 1000 == 0):
print('Iter', n)
print('Error is: ', str(total_err))
print("Pred:" , str(d))
print("True:" , str(c))
out = 0
for index,x in enumerate(reversed(d)):
out += x*pow(2,index)
print(str(a_int) , " + " , str(b_int) , " = " , str(out))
print("------------")
#end
############################################################
可视化部分
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(errs, label='error')
plt.plot(accs, label='accuracy')
plt.legend()