真正掌握一种算法,最实际的方法,完全手写出来。
LSTM(Long Short Tem Memory)特殊递归神经网络,神经元保存历史记忆,解决自然语言处理统计方法只能考虑最近n个词语而忽略更久前词语的问题。用途:word representation(embedding)(词语向量)、sequence to sequence learning(输入句子预测句子)、机器翻译、语音识别等。
100多行原始python代码实现基于LSTM二进制加法器。https://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/ ,翻译http://blog.youkuaiyun.com/zzukun/article/details/49968129 :
import copy, numpy as np
np.random.seed(0)
最开始引入numpy库,矩阵操作。
def sigmoid(x):
output = 1/(1+np.exp(-x))
return output
声明sigmoid激活函数,神经网络基础内容,常用激活函数sigmoid、tan、relu等,sigmoid取值范围[0, 1],tan取值范围[-1,1],x是向量,返回output是向量。
def sigmoid_output_to_derivative(output):
return output*(1-output)
声明sigmoid求导函数。
加法器思路:二进制加法是二进制位相加,记录满二进一进位,训练时随机c=a+b样本,输入a、b输出c是整个lstm预测过程,训练由a、b二进制向c各种转换矩阵和权重,神经网络。
int2binary = {}
声明词典,由整型数字转成二进制,存起来不用随时计算,提前存好读取更快。
binary_dim = 8
largest_number = pow(2,binary_dim)
声明二进制数字维度,8,二进制能表达最大整数2^8=256,largest_number。
binary = np.unpackbits(
np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
for i in range(largest_number):
int2binary[i] = binary[i]
预先把整数到二进制转换词典存起来。
alpha = 0.1
input_dim = 2
hidden_dim = 16
output_dim = 1
设置参数,alpha是学习速度,input_dim是输入层向量维度,输入a、b两个数,是2,hidden_dim是隐藏层向量维度,隐藏层神经元个数,output_dim是输出层向量维度,输出一个c,是1维。从输入层到隐藏层权重矩阵是2*16维,从隐藏层到输出层权重矩阵是16*1维,隐藏层到隐藏层权重矩阵是16*16维:
synapse_0 = 2*np.random.random((input_dim,hidden_dim)) - 1
synapse_1 = 2*np.random.random((hidden_dim,output_dim)) - 1
synapse_h = 2*np.random.random((hidden_dim,hidden_dim)) - 1
2x-1,np.random.random生成从0到1之间随机浮点数,2x-1使其取值范围在[-1, 1]。
synapse_0_update = np.zeros_like(synapse_0)
synapse_1_update = np.zeros_like(synapse_1)
synapse_h_update = np.zeros_like(synapse_h)
声明三个矩阵更新,Delta。
for j in range(10000):
进行10000次迭代。
a_int = np.random.randint(largest_number/2)
a = int2binary[a_int]
b_int = np.random.randint(largest_number/2)
b = int2binary[b_int]
c_int = a_int + b_int
c = int2binary[c_int]
随机生成样本,包含二进制a、b、c,c=a+b,a_int、b_int、c_int分别是a、b、c对应整数格式。
d = np.zeros_like(c)
d存模型对c预测值。
overallError = 0
全局误差,观察模型效果。
layer_2_deltas = list()
存储第二层(输出层)残差,输出层残差计算公式推导公式http://deeplearning.stanford.edu/wiki/index.php/%E5%8F%8D%E5%90%91%E4%BC%A0%E5%AF%BC%E7%AE%97%E6%B3%95 。
layer_1_values = list()
layer_1_values.append(np.zeros(hidden_dim))
存储第一层(隐藏层)输出值,赋0值作为上一个时间值。
for position in range(binary_dim):
遍历二进制每一位。
X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])
y = np.array([[c[binary_dim - position - 1]]]).T
X和y分别是样本输入和输出二进制值第position位,X对于每个样本有两个值,分别是a和b对应第position位。把样本拆成每个二进制位用于训练,二进制加法存在进位标记正好适合利用LSTM长短期记忆训练,每个样本8个二进制位是一个时间序列。
layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))
公式Ct = sigma(W0·Xt + Wh·Ct-1)
layer_2 = sigmoid(np.dot(layer_1,synapse_1))
这里使用的公式是C2 = sigma(W1·C1),
layer_2_error = y - layer_2
计算预测值和真实值误差。
layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2))
反向传导,计算delta,添加到数组layer_2_deltas
overallError += np.abs(layer_2_error[0])
计算累加总误差,用于展示和观察。
d[binary_dim - position - 1] = np.round(layer_2[0][0])
存储预测position位输出值。
layer_1_values.append(copy.deepcopy(layer_1))
存储中间过程生成隐藏层值。
future_layer_1_delta = np.zeros(hidden_dim)
存储下一个时间周期隐藏层历史记忆值,先赋一个空值。
for position in range(binary_dim):
遍历二进制每一位。
X = np.array([[a[position],b[position]]])
取出X值,从大位开始更新,反向传导按时序逆着一级一级更新。
layer_1 = layer_1_values[-position-1]
取出位对应隐藏层输出。
prev_layer_1 = layer_1_values[-position-2]
取出位对应隐藏层上一时序输出。
layer_2_delta = layer_2_deltas[-position-1]
取出位对应输出层delta。
layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)
神经网络反向传导公式,加上隐藏层?值。
synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
累加权重矩阵更新,对权重(权重矩阵)偏导等于本层输出与下一层delta点乘。
synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
前一时序隐藏层权重矩阵更新,前一时序隐藏层输出与本时序delta点乘。
synapse_0_update += X.T.dot(layer_1_delta)
输入层权重矩阵更新。
future_layer_1_delta = layer_1_delta
记录本时序隐藏层delta。
synapse_0 += synapse_0_update * alpha
synapse_1 += synapse_1_update * alpha
synapse_h += synapse_h_update * alpha
权重矩阵更新。
synapse_0_update *= 0
synapse_1_update *= 0
synapse_h_update *= 0
更新变量归零。
if(j % 1000 == 0):
print "Error:" + str(overallError)
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 "------------"
每训练1000个样本输出总误差信息,运行时看收敛过程。
LSTM最简单实现,没有考虑偏置变量,只有两个神经元。
完整LSTM python实现。完全参照论文great intro paper实现,代码来源https://github.com/nicodjimenez/lstm ,作者解释http://nicodjimenez.github.io/2014/08/08/lstm.html ,具体过程参考http://colah.github.io/posts/2015-08-Understanding-LSTMs/ 图。
import random
import numpy as np
imp