前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习笔记CB012: LSTM 简单实现、完整实现、torch、小说训练word2vec lstm机器人

学习笔记CB012: LSTM 简单实现、完整实现、torch、小说训练word2vec lstm机器人

原创
作者头像
利炳根
修改2018-05-01 14:11:00
1.3K2
修改2018-05-01 14:11:00
举报
文章被收录于专栏:利炳根的专栏利炳根的专栏

真正掌握一种算法,最实际的方法,完全手写出来。

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.csdn.net/zzukun/article/details/49968129

代码语言:txt
复制
import copy, numpy as np
代码语言:txt
复制
np.random.seed(0)

最开始引入numpy库,矩阵操作。

代码语言:txt
复制
def sigmoid(x):
代码语言:txt
复制
    output = 1/(1+np.exp(-x))
代码语言:txt
复制
    return output

声明sigmoid激活函数,神经网络基础内容,常用激活函数sigmoid、tan、relu等,sigmoid取值范围0, 1,tan取值范围-1,1,x是向量,返回output是向量。

代码语言:txt
复制
def sigmoid_output_to_derivative(output):
代码语言:txt
复制
    return output*(1-output)

声明sigmoid求导函数。

加法器思路:二进制加法是二进制位相加,记录满二进一进位,训练时随机c=a+b样本,输入a、b输出c是整个lstm预测过程,训练由a、b二进制向c各种转换矩阵和权重,神经网络。

代码语言:txt
复制
int2binary = {}

声明词典,由整型数字转成二进制,存起来不用随时计算,提前存好读取更快。

代码语言:txt
复制
binary_dim = 8

largest_number = pow(2,binary_dim)

声明二进制数字维度,8,二进制能表达最大整数2^8=256,largest_number。

代码语言:txt
复制
binary = np.unpackbits(
代码语言:txt
复制
                       np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
代码语言:txt
复制
for i in range(largest_number):
代码语言:txt
复制
    int2binary[i] = binary[i]

预先把整数到二进制转换词典存起来。

代码语言:txt
复制
alpha = 0.1
代码语言:txt
复制
input_dim = 2
代码语言:txt
复制
hidden_dim = 16
代码语言:txt
复制
output_dim = 1

设置参数,alpha是学习速度,input_dim是输入层向量维度,输入a、b两个数,是2,hidden_dim是隐藏层向量维度,隐藏层神经元个数,output_dim是输出层向量维度,输出一个c,是1维。从输入层到隐藏层权重矩阵是216维,从隐藏层到输出层权重矩阵是161维,隐藏层到隐藏层权重矩阵是16*16维:

代码语言:txt
复制
synapse_0 = 2*np.random.random((input_dim,hidden_dim)) - 1
代码语言:txt
复制
synapse_1 = 2*np.random.random((hidden_dim,output_dim)) - 1
代码语言:txt
复制
synapse_h = 2*np.random.random((hidden_dim,hidden_dim)) - 1

2x-1,np.random.random生成从0到1之间随机浮点数,2x-1使其取值范围在-1, 1。

代码语言:txt
复制
synapse_0_update = np.zeros_like(synapse_0)
代码语言:txt
复制
synapse_1_update = np.zeros_like(synapse_1)
代码语言:txt
复制
synapse_h_update = np.zeros_like(synapse_h)

声明三个矩阵更新,Delta。

代码语言:txt
复制
for j in range(10000):

进行10000次迭代。

代码语言:txt
复制
a_int = np.random.randint(largest_number/2)
代码语言:txt
复制
a = int2binary[a_int]
代码语言:txt
复制
b_int = np.random.randint(largest_number/2)
代码语言:txt
复制
b = int2binary[b_int]
代码语言:txt
复制
c_int = a_int + b_int
代码语言:txt
复制
c = int2binary[c_int]

随机生成样本,包含二进制a、b、c,c=a+b,a_int、b_int、c_int分别是a、b、c对应整数格式。

代码语言:txt
复制
d = np.zeros_like(c)

d存模型对c预测值。

代码语言:txt
复制
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

代码语言:txt
复制
layer_1_values = list()
代码语言:txt
复制
layer_1_values.append(np.zeros(hidden_dim))

存储第一层(隐藏层)输出值,赋0值作为上一个时间值。

代码语言:txt
复制
for position in range(binary_dim):

遍历二进制每一位。

代码语言:txt
复制
X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])
代码语言:txt
复制
y = np.array([[c[binary_dim - position - 1]]]).T

X和y分别是样本输入和输出二进制值第position位,X对于每个样本有两个值,分别是a和b对应第position位。把样本拆成每个二进制位用于训练,二进制加法存在进位标记正好适合利用LSTM长短期记忆训练,每个样本8个二进制位是一个时间序列。

代码语言:txt
复制
layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))

公式Ct = sigma(W0·Xt + Wh·Ct-1)

代码语言:txt
复制
layer_2 = sigmoid(np.dot(layer_1,synapse_1))

这里使用的公式是C2 = sigma(W1·C1),

代码语言:txt
复制
layer_2_error = y - layer_2

计算预测值和真实值误差。

代码语言:txt
复制
layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2))

反向传导,计算delta,添加到数组layer_2_deltas

代码语言:txt
复制
overallError += np.abs(layer_2_error[0])

计算累加总误差,用于展示和观察。

代码语言:txt
复制
d[binary_dim - position - 1] = np.round(layer_2[0][0])

存储预测position位输出值。

代码语言:txt
复制
layer_1_values.append(copy.deepcopy(layer_1))

存储中间过程生成隐藏层值。

代码语言:txt
复制
future_layer_1_delta = np.zeros(hidden_dim)

存储下一个时间周期隐藏层历史记忆值,先赋一个空值。

代码语言:txt
复制
for position in range(binary_dim):

遍历二进制每一位。

代码语言:txt
复制
X = np.array([[a[position],b[position]]])

取出X值,从大位开始更新,反向传导按时序逆着一级一级更新。

代码语言:txt
复制
layer_1 = layer_1_values[-position-1]

取出位对应隐藏层输出。

代码语言:txt
复制
prev_layer_1 = layer_1_values[-position-2]

取出位对应隐藏层上一时序输出。

代码语言:txt
复制
layer_2_delta = layer_2_deltas[-position-1]

取出位对应输出层delta。

代码语言:txt
复制
layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)

神经网络反向传导公式,加上隐藏层?值。

代码语言:txt
复制
synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)

累加权重矩阵更新,对权重(权重矩阵)偏导等于本层输出与下一层delta点乘。

代码语言:txt
复制
synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)

前一时序隐藏层权重矩阵更新,前一时序隐藏层输出与本时序delta点乘。

代码语言:txt
复制
synapse_0_update += X.T.dot(layer_1_delta)

输入层权重矩阵更新。

代码语言:txt
复制
future_layer_1_delta = layer_1_delta

记录本时序隐藏层delta。

代码语言:txt
复制
synapse_0 += synapse_0_update * alpha
代码语言:txt
复制
synapse_1 += synapse_1_update * alpha
代码语言:txt
复制
synapse_h += synapse_h_update * alpha

权重矩阵更新。

代码语言:txt
复制
synapse_0_update *= 0
代码语言:txt
复制
synapse_1_update *= 0
代码语言:txt
复制
synapse_h_update *= 0

更新变量归零。

代码语言:txt
复制
if(j % 1000 == 0):
代码语言:txt
复制
        print "Error:" + str(overallError)
代码语言:txt
复制
        print "Pred:" + str(d)
代码语言:txt
复制
        print "True:" + str(c)
代码语言:txt
复制
        out = 0
代码语言:txt
复制
        for index,x in enumerate(reversed(d)):
代码语言:txt
复制
            out += x*pow(2,index)
代码语言:txt
复制
        print str(a_int) + " + " + str(b_int) + " = " + str(out)
代码语言:txt
复制
        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/ 图。

代码语言:txt
复制
import random
代码语言:txt
复制
import numpy as np
代码语言:txt
复制
import math
代码语言:txt
复制
def sigmoid(x):
代码语言:txt
复制
    return 1. / (1 + np.exp(-x))

声明sigmoid函数。

代码语言:txt
复制
def rand_arr(a, b, *args):
代码语言:txt
复制
    np.random.seed(0)
代码语言:txt
复制
    return np.random.rand(*args) * (b - a) + a

生成随机矩阵,取值范围[a,b),shape用args指定。

代码语言:txt
复制
class LstmParam:
代码语言:txt
复制
    def __init__(self, mem_cell_ct, x_dim):
代码语言:txt
复制
        self.mem_cell_ct = mem_cell_ct
代码语言:txt
复制
        self.x_dim = x_dim
代码语言:txt
复制
        concat_len = x_dim + mem_cell_ct
代码语言:txt
复制
        # weight matrices
代码语言:txt
复制
        self.wg = rand_arr(-0.1, 0.1, mem_cell_ct, concat_len)
代码语言:txt
复制
        self.wi = rand_arr(-0.1, 0.1, mem_cell_ct, concat_len)
代码语言:txt
复制
        self.wf = rand_arr(-0.1, 0.1, mem_cell_ct, concat_len)
代码语言:txt
复制
        self.wo = rand_arr(-0.1, 0.1, mem_cell_ct, concat_len)
代码语言:txt
复制
        # bias terms
代码语言:txt
复制
        self.bg = rand_arr(-0.1, 0.1, mem_cell_ct)
代码语言:txt
复制
        self.bi = rand_arr(-0.1, 0.1, mem_cell_ct)
代码语言:txt
复制
        self.bf = rand_arr(-0.1, 0.1, mem_cell_ct)
代码语言:txt
复制
        self.bo = rand_arr(-0.1, 0.1, mem_cell_ct)
代码语言:txt
复制
        # diffs (derivative of loss function w.r.t. all parameters)
代码语言:txt
复制
        self.wg_diff = np.zeros((mem_cell_ct, concat_len))
代码语言:txt
复制
        self.wi_diff = np.zeros((mem_cell_ct, concat_len))
代码语言:txt
复制
        self.wf_diff = np.zeros((mem_cell_ct, concat_len))
代码语言:txt
复制
        self.wo_diff = np.zeros((mem_cell_ct, concat_len))
代码语言:txt
复制
        self.bg_diff = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.bi_diff = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.bf_diff = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.bo_diff = np.zeros(mem_cell_ct)

LstmParam类传递参数,mem_cell_ct是lstm神经元数目,x_dim是输入数据维度,concat_len是mem_cell_ct与x_dim长度和,wg是输入节点权重矩阵,wi是输入门权重矩阵,wf是忘记门权重矩阵,wo是输出门权重矩阵,bg、bi、bf、bo分别是输入节点、输入门、忘记门、输出门偏置,wg_diff、wi_diff、wf_diff、wo_diff分别是输入节点、输入门、忘记门、输出门权重损失,bg_diff、bi_diff、bf_diff、bo_diff分别是输入节点、输入门、忘记门、输出门偏置损失,初始化按照矩阵维度初始化,损失矩阵归零。

代码语言:txt
复制
    def apply_diff(self, lr = 1):
代码语言:txt
复制
        self.wg -= lr * self.wg_diff
代码语言:txt
复制
        self.wi -= lr * self.wi_diff
代码语言:txt
复制
        self.wf -= lr * self.wf_diff
代码语言:txt
复制
        self.wo -= lr * self.wo_diff
代码语言:txt
复制
        self.bg -= lr * self.bg_diff
代码语言:txt
复制
        self.bi -= lr * self.bi_diff
代码语言:txt
复制
        self.bf -= lr * self.bf_diff
代码语言:txt
复制
        self.bo -= lr * self.bo_diff
代码语言:txt
复制
        # reset diffs to zero
代码语言:txt
复制
        self.wg_diff = np.zeros_like(self.wg)
代码语言:txt
复制
        self.wi_diff = np.zeros_like(self.wi)
代码语言:txt
复制
        self.wf_diff = np.zeros_like(self.wf)
代码语言:txt
复制
        self.wo_diff = np.zeros_like(self.wo)
代码语言:txt
复制
        self.bg_diff = np.zeros_like(self.bg)
代码语言:txt
复制
        self.bi_diff = np.zeros_like(self.bi)
代码语言:txt
复制
        self.bf_diff = np.zeros_like(self.bf)
代码语言:txt
复制
        self.bo_diff = np.zeros_like(self.bo)

定义权重更新过程,先减损失,再把损失矩阵归零。

代码语言:txt
复制
class LstmState:
代码语言:txt
复制
    def __init__(self, mem_cell_ct, x_dim):
代码语言:txt
复制
        self.g = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.i = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.f = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.o = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.s = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.h = np.zeros(mem_cell_ct)
代码语言:txt
复制
        self.bottom_diff_h = np.zeros_like(self.h)
代码语言:txt
复制
        self.bottom_diff_s = np.zeros_like(self.s)
代码语言:txt
复制
        self.bottom_diff_x = np.zeros(x_dim)

LstmState存储LSTM神经元状态,包括g、i、f、o、s、h,s是内部状态矩阵(记忆),h是隐藏层神经元输出矩阵。

代码语言:txt
复制
class LstmNode:
代码语言:txt
复制
    def __init__(self, lstm_param, lstm_state):
代码语言:txt
复制
        # store reference to parameters and to activations
代码语言:txt
复制
        self.state = lstm_state
代码语言:txt
复制
        self.param = lstm_param
代码语言:txt
复制
        # non-recurrent input to node
代码语言:txt
复制
        self.x = None
代码语言:txt
复制
        # non-recurrent input concatenated with recurrent input
代码语言:txt
复制
        self.xc = None

LstmNode对应样本输入,x是输入样本x,xc是用hstack把x和递归输入节点拼接矩阵(hstack是横拼矩阵,vstack是纵拼矩阵)。

代码语言:txt
复制
    def bottom_data_is(self, x, s_prev = None, h_prev = None):
代码语言:txt
复制
        # if this is the first lstm node in the network
代码语言:txt
复制
        if s_prev == None: s_prev = np.zeros_like(self.state.s)
代码语言:txt
复制
        if h_prev == None: h_prev = np.zeros_like(self.state.h)
代码语言:txt
复制
        # save data for use in backprop
代码语言:txt
复制
        self.s_prev = s_prev
代码语言:txt
复制
        self.h_prev = h_prev
代码语言:txt
复制
        # concatenate x(t) and h(t-1)
代码语言:txt
复制
        xc = np.hstack((x,  h_prev))
代码语言:txt
复制
        self.state.g = np.tanh(np.dot(self.param.wg, xc) + self.param.bg)
代码语言:txt
复制
        self.state.i = sigmoid(np.dot(self.param.wi, xc) + self.param.bi)
代码语言:txt
复制
        self.state.f = sigmoid(np.dot(self.param.wf, xc) + self.param.bf)
代码语言:txt
复制
        self.state.o = sigmoid(np.dot(self.param.wo, xc) + self.param.bo)
代码语言:txt
复制
        self.state.s = self.state.g * self.state.i + s_prev * self.state.f
代码语言:txt
复制
        self.state.h = self.state.s * self.state.o
代码语言:txt
复制
        self.x = x
代码语言:txt
复制
        self.xc = xc

bottom和top是两个方向,输入样本从底部输入,反向传导从顶部向底部传导,bottom_data_is是输入样本过程,把x和先前输入拼接成矩阵,用公式wx+b分别计算g、i、f、o值,激活函数tanh和sigmoid。

每个时序神经网络有四个神经网络层(激活函数),最左边忘记门,直接生效到记忆C,第二个输入门,依赖输入样本数据,按照一定“比例”影响记忆C,“比例”通过第三个层(tanh)实现,取值范围是-1,1可以正向影响也可以负向影响,最后一个输出门,每一时序产生输出既依赖输入样本x和上一时序输出,还依赖记忆C,设计模仿生物神经元记忆功能。

代码语言:txt
复制
    def top_diff_is(self, top_diff_h, top_diff_s):
代码语言:txt
复制
        # notice that top_diff_s is carried along the constant error carousel
代码语言:txt
复制
        ds = self.state.o * top_diff_h + top_diff_s
代码语言:txt
复制
        do = self.state.s * top_diff_h
代码语言:txt
复制
        di = self.state.g * ds
代码语言:txt
复制
        dg = self.state.i * ds
代码语言:txt
复制
        df = self.s_prev * ds
代码语言:txt
复制
        # diffs w.r.t. vector inside sigma / tanh function
代码语言:txt
复制
        di_input = (1. - self.state.i) * self.state.i * di
代码语言:txt
复制
        df_input = (1. - self.state.f) * self.state.f * df
代码语言:txt
复制
        do_input = (1. - self.state.o) * self.state.o * do
代码语言:txt
复制
        dg_input = (1. - self.state.g ** 2) * dg
代码语言:txt
复制
        # diffs w.r.t. inputs
代码语言:txt
复制
        self.param.wi_diff += np.outer(di_input, self.xc)
代码语言:txt
复制
        self.param.wf_diff += np.outer(df_input, self.xc)
代码语言:txt
复制
        self.param.wo_diff += np.outer(do_input, self.xc)
代码语言:txt
复制
        self.param.wg_diff += np.outer(dg_input, self.xc)
代码语言:txt
复制
        self.param.bi_diff += di_input
代码语言:txt
复制
        self.param.bf_diff += df_input
代码语言:txt
复制
        self.param.bo_diff += do_input
代码语言:txt
复制
        self.param.bg_diff += dg_input
代码语言:txt
复制
        # compute bottom diff
代码语言:txt
复制
        dxc = np.zeros_like(self.xc)
代码语言:txt
复制
        dxc += np.dot(self.param.wi.T, di_input)
代码语言:txt
复制
        dxc += np.dot(self.param.wf.T, df_input)
代码语言:txt
复制
        dxc += np.dot(self.param.wo.T, do_input)
代码语言:txt
复制
        dxc += np.dot(self.param.wg.T, dg_input)
代码语言:txt
复制
        # save bottom diffs
代码语言:txt
复制
        self.state.bottom_diff_s = ds * self.state.f
代码语言:txt
复制
        self.state.bottom_diff_x = dxc[:self.param.x_dim]
代码语言:txt
复制
        self.state.bottom_diff_h = dxc[self.param.x_dim:]

反向传导,整个训练过程核心。假设在t时刻lstm输出预测值h(t),实际输出值是y(t),之间差别是损失,假设损失函数为l(t) = f(h(t), y(t)) = ||h(t) - y(t)||^2,欧式距离,整体损失函数是L(t) = ∑l(t),t从1到T,T表示整个事件序列最大长度。最终目标是用梯度下降法让L(t)最小化,找到一个最优权重w使得L(t)最小,当w发生微小变化L(t)不再变化,达到局部最优,即L对w偏导梯度为0。

dL/dw表示当w发生单位变化L变化多少,dh(t)/dw表示当w发生单位变化h(t)变化多少,dL/dh(t)表示当h(t)发生单位变化时L变化多少,(dL/dh(t)) * (dh(t)/dw)表示第t时序第i个记忆单元w发生单位变化L变化多少,把所有由1到M的i和所有由1到T的t累加是整体dL/dw。

第i个记忆单元,h(t)发生单位变化,整个从1到T时序所有局部损失l的累加和,是dL/dh(t),h(t)只影响从t到T时序局部损失l。

假设L(t)表示从t到T损失和,L(t) = ∑l(s)。

h(t)对w导数。

L(t) = l(t) + L(t+1),dL(t)/dh(t) = dl(t)/dh(t) + dL(t+1)/dh(t),用下一时序导数得出当前时序导数,规律推导,计算T时刻导数往前推,在T时刻,dL(T)/dh(T) = dl(T)/dh(T)。

代码语言:txt
复制
class LstmNetwork():
代码语言:txt
复制
    def __init__(self, lstm_param):
代码语言:txt
复制
        self.lstm_param = lstm_param
代码语言:txt
复制
        self.lstm_node_list = []
代码语言:txt
复制
        # input sequence
代码语言:txt
复制
        self.x_list = []
代码语言:txt
复制
    def y_list_is(self, y_list, loss_layer):
代码语言:txt
复制
        """
代码语言:txt
复制
        Updates diffs by setting target sequence
代码语言:txt
复制
        with corresponding loss layer.
代码语言:txt
复制
        Will *NOT* update parameters.  To update parameters,
代码语言:txt
复制
        call self.lstm_param.apply_diff()
代码语言:txt
复制
        """
代码语言:txt
复制
        assert len(y_list) == len(self.x_list)
代码语言:txt
复制
        idx = len(self.x_list) - 1
代码语言:txt
复制
        # first node only gets diffs from label ...
代码语言:txt
复制
        loss = loss_layer.loss(self.lstm_node_list[idx].state.h, y_list[idx])
代码语言:txt
复制
        diff_h = loss_layer.bottom_diff(self.lstm_node_list[idx].state.h, y_list[idx])
代码语言:txt
复制
        # here s is not affecting loss due to h(t+1), hence we set equal to zero
代码语言:txt
复制
        diff_s = np.zeros(self.lstm_param.mem_cell_ct)
代码语言:txt
复制
        self.lstm_node_list[idx].top_diff_is(diff_h, diff_s)
代码语言:txt
复制
        idx -= 1
代码语言:txt
复制
        ### ... following nodes also get diffs from next nodes, hence we add diffs to diff_h
代码语言:txt
复制
        ### we also propagate error along constant error carousel using diff_s
代码语言:txt
复制
        while idx >= 0:
代码语言:txt
复制
            loss += loss_layer.loss(self.lstm_node_list[idx].state.h, y_list[idx])
代码语言:txt
复制
            diff_h = loss_layer.bottom_diff(self.lstm_node_list[idx].state.h, y_list[idx])
代码语言:txt
复制
            diff_h += self.lstm_node_list[idx + 1].state.bottom_diff_h
代码语言:txt
复制
            diff_s = self.lstm_node_list[idx + 1].state.bottom_diff_s
代码语言:txt
复制
            self.lstm_node_list[idx].top_diff_is(diff_h, diff_s)
代码语言:txt
复制
            idx -= 1
代码语言:txt
复制
        return loss

diff_h(预测结果误差发生单位变化损失L多少,dL(t)/dh(t)数值计算),由idx从T往前遍历到1,计算loss_layer.bottom_diff和下一个时序bottom_diff_h和作为diff_h(第一次遍历即T不加bottom_diff_h)。

loss_layer.bottom_diff:

代码语言:txt
复制
    def bottom_diff(self, pred, label):
代码语言:txt
复制
        diff = np.zeros_like(pred)
代码语言:txt
复制
        diff[0] = 2 * (pred[0] - label)
代码语言:txt
复制
        return diff

l(t) = f(h(t), y(t)) = ||h(t) - y(t)||^2导数l'(t) = 2 * (h(t) - y(t))

。当s(t)发生变化,L(t)变化来源s(t)影响h(t)和h(t+1),影响L(t)。

h(t+1)不会影响l(t)。

左边式子(dL(t)/dh(t)) * (dh(t)/ds(t)),由t+1到t来逐级反推dL(t)/ds(t)。

神经元self.state.h = self.state.s self.state.o,h(t) = s(t) o(t),dh(t)/ds(t) = o(t),dL(t)/dh(t)是top_diff_h。

top_diff_is,Bottom means input to the layer, top means output of the layer. Caffe also uses this terminology. bottom表示神经网络层输入,top表示神经网络层输出,和caffe概念一致。

def top_diff_is(self, top_diff_h, top_diff_s):

top_diff_h表示当前t时序dL(t)/dh(t), top_diff_s表示t+1时序记忆单元dL(t)/ds(t)。

代码语言:txt
复制
        ds = self.state.o * top_diff_h + top_diff_s
代码语言:txt
复制
        do = self.state.s * top_diff_h
代码语言:txt
复制
        di = self.state.g * ds
代码语言:txt
复制
        dg = self.state.i * ds
代码语言:txt
复制
        df = self.s_prev * ds

前缀d表达误差L对某一项导数(directive)。

ds是在根据公式dL(t)/ds(t)计算当前t时序dL(t)/ds(t)。

do是计算dL(t)/do(t),h(t) = s(t) o(t),dh(t)/do(t) = s(t),dL(t)/do(t) = (dL(t)/dh(t)) (dh(t)/do(t)) = top_diff_h * s(t)。

di是计算dL(t)/di(t)。s(t) = f(t) s(t-1) + i(t) g(t)。dL(t)/di(t) = (dL(t)/ds(t)) (ds(t)/di(t)) = ds g(t)。

dg是计算dL(t)/dg(t),dL(t)/dg(t) = (dL(t)/ds(t)) (ds(t)/dg(t)) = ds i(t)。

df是计算dL(t)/df(t),dL(t)/df(t) = (dL(t)/ds(t)) (ds(t)/df(t)) = ds s(t-1)。

代码语言:txt
复制
        di_input = (1. - self.state.i) * self.state.i * di
代码语言:txt
复制
        df_input = (1. - self.state.f) * self.state.f * df
代码语言:txt
复制
        do_input = (1. - self.state.o) * self.state.o * do
代码语言:txt
复制
        dg_input = (1. - self.state.g ** 2) * dg

sigmoid函数导数,tanh函数导数。di_input,(1. - self.state.i) * self.state.i,sigmoid导数,当i神经元输入发生单位变化时输出值有多大变化,再乘di表示当i神经元输入发生单位变化时误差L(t)发生多大变化,dL(t)/d i_input(t)。

代码语言:txt
复制
        self.param.wi_diff += np.outer(di_input, self.xc)
代码语言:txt
复制
        self.param.wf_diff += np.outer(df_input, self.xc)
代码语言:txt
复制
        self.param.wo_diff += np.outer(do_input, self.xc)
代码语言:txt
复制
        self.param.wg_diff += np.outer(dg_input, self.xc)
代码语言:txt
复制
        self.param.bi_diff += di_input
代码语言:txt
复制
        self.param.bf_diff += df_input
代码语言:txt
复制
        self.param.bo_diff += do_input
代码语言:txt
复制
        self.param.bg_diff += dg_input

w_diff是权重矩阵误差,b_diff是偏置误差,用于更新。

代码语言:txt
复制
        dxc = np.zeros_like(self.xc)
代码语言:txt
复制
        dxc += np.dot(self.param.wi.T, di_input)
代码语言:txt
复制
        dxc += np.dot(self.param.wf.T, df_input)
代码语言:txt
复制
        dxc += np.dot(self.param.wo.T, do_input)
代码语言:txt
复制
        dxc += np.dot(self.param.wg.T, dg_input)

累加输入xdiff,x在四处起作用,四处diff加和后作xdiff。

代码语言:txt
复制
        self.state.bottom_diff_s = ds * self.state.f
代码语言:txt
复制
        self.state.bottom_diff_x = dxc[:self.param.x_dim]
代码语言:txt
复制
        self.state.bottom_diff_h = dxc[self.param.x_dim:]

bottom_diff_s是在t-1时序上s变化和t时序上s变化时f倍关系。dxc是x和h横向合并矩阵,分别取两部分diff信息bottom_diff_x和bottom_diff_h。

代码语言:txt
复制
def x_list_clear(self):
代码语言:txt
复制
        self.x_list = []
代码语言:txt
复制
    def x_list_add(self, x):
代码语言:txt
复制
        self.x_list.append(x)
代码语言:txt
复制
        if len(self.x_list) > len(self.lstm_node_list):
代码语言:txt
复制
            # need to add new lstm node, create new state mem
代码语言:txt
复制
            lstm_state = LstmState(self.lstm_param.mem_cell_ct, self.lstm_param.x_dim)
代码语言:txt
复制
            self.lstm_node_list.append(LstmNode(self.lstm_param, lstm_state))
代码语言:txt
复制
        # get index of most recent x input
代码语言:txt
复制
        idx = len(self.x_list) - 1
代码语言:txt
复制
        if idx == 0:
代码语言:txt
复制
            # no recurrent inputs yet
代码语言:txt
复制
            self.lstm_node_list[idx].bottom_data_is(x)
代码语言:txt
复制
        else:
代码语言:txt
复制
            s_prev = self.lstm_node_list[idx - 1].state.s
代码语言:txt
复制
            h_prev = self.lstm_node_list[idx - 1].state.h
代码语言:txt
复制
            self.lstm_node_list[idx].bottom_data_is(x, s_prev, h_prev)

添加训练样本,输入x数据。

代码语言:txt
复制
def example_0():
代码语言:txt
复制
    # learns to repeat simple sequence from random inputs
代码语言:txt
复制
    np.random.seed(0)
代码语言:txt
复制
    # parameters for input data dimension and lstm cell count
代码语言:txt
复制
    mem_cell_ct = 100
代码语言:txt
复制
    x_dim = 50
代码语言:txt
复制
    concat_len = x_dim + mem_cell_ct
代码语言:txt
复制
    lstm_param = LstmParam(mem_cell_ct, x_dim)
代码语言:txt
复制
    lstm_net = LstmNetwork(lstm_param)
代码语言:txt
复制
    y_list = [-0.5,0.2,0.1, -0.5]
代码语言:txt
复制
    input_val_arr = [np.random.random(x_dim) for _ in y_list]
代码语言:txt
复制
    for cur_iter in range(100):
代码语言:txt
复制
        print "cur iter: ", cur_iter
代码语言:txt
复制
        for ind in range(len(y_list)):
代码语言:txt
复制
            lstm_net.x_list_add(input_val_arr[ind])
代码语言:txt
复制
            print "y_pred[%d] : %f" % (ind, lstm_net.lstm_node_list[ind].state.h[0])
代码语言:txt
复制
        loss = lstm_net.y_list_is(y_list, ToyLossLayer)
代码语言:txt
复制
        print "loss: ", loss
代码语言:txt
复制
        lstm_param.apply_diff(lr=0.1)
代码语言:txt
复制
        lstm_net.x_list_clear()

初始化LstmParam,指定记忆存储单元数为100,指定输入样本x维度是50。初始化LstmNetwork训练模型,生成4组各50个随机数,分别以-0.5,0.2,0.1, -0.5作为y值训练,每次喂50个随机数和一个y值,迭代100次。

lstm输入一串连续质数预估下一个质数。小测试,生成100以内质数,循环拿出50个质数序列作x,第51个质数作y,拿出10个样本参与训练1w次,均方误差由0.17973最终达到了1.05172e-06,几乎完全正确:

代码语言:txt
复制
import numpy as np
代码语言:txt
复制
import sys
代码语言:txt
复制
from lstm import LstmParam, LstmNetwork
代码语言:txt
复制
class ToyLossLayer:
代码语言:txt
复制
    """
代码语言:txt
复制
    Computes square loss with first element of hidden layer array.
代码语言:txt
复制
    """
代码语言:txt
复制
    @classmethod
代码语言:txt
复制
    def loss(self, pred, label):
代码语言:txt
复制
        return (pred[0] - label) ** 2
代码语言:txt
复制
    @classmethod
代码语言:txt
复制
    def bottom_diff(self, pred, label):
代码语言:txt
复制
        diff = np.zeros_like(pred)
代码语言:txt
复制
        diff[0] = 2 * (pred[0] - label)
代码语言:txt
复制
        return diff
代码语言:txt
复制
class Primes:
代码语言:txt
复制
    def __init__(self):
代码语言:txt
复制
        self.primes = list()
代码语言:txt
复制
        for i in range(2, 100):
代码语言:txt
复制
            is_prime = True
代码语言:txt
复制
            for j in range(2, i-1):
代码语言:txt
复制
                if i % j == 0:
代码语言:txt
复制
                    is_prime = False
代码语言:txt
复制
            if is_prime:
代码语言:txt
复制
                self.primes.append(i)
代码语言:txt
复制
        self.primes_count = len(self.primes)
代码语言:txt
复制
    def get_sample(self, x_dim, y_dim, index):
代码语言:txt
复制
        result = np.zeros((x_dim+y_dim))
代码语言:txt
复制
        for i in range(index, index + x_dim + y_dim):
代码语言:txt
复制
            result[i-index] = self.primes[i%self.primes_count]/100.0
代码语言:txt
复制
        return result
代码语言:txt
复制
def example_0():
代码语言:txt
复制
    mem_cell_ct = 100
代码语言:txt
复制
    x_dim = 50
代码语言:txt
复制
    concat_len = x_dim + mem_cell_ct
代码语言:txt
复制
    lstm_param = LstmParam(mem_cell_ct, x_dim)
代码语言:txt
复制
    lstm_net = LstmNetwork(lstm_param)
代码语言:txt
复制
    primes = Primes()
代码语言:txt
复制
    x_list = []
代码语言:txt
复制
    y_list = []
代码语言:txt
复制
    for i in range(0, 10):
代码语言:txt
复制
        sample = primes.get_sample(x_dim, 1, i)
代码语言:txt
复制
        x = sample[0:x_dim]
代码语言:txt
复制
        y = sample[x_dim:x_dim+1].tolist()[0]
代码语言:txt
复制
        x_list.append(x)
代码语言:txt
复制
        y_list.append(y)
代码语言:txt
复制
    for cur_iter in range(10000):
代码语言:txt
复制
        if cur_iter % 1000 == 0:
代码语言:txt
复制
            print "y_list=", y_list
代码语言:txt
复制
        for ind in range(len(y_list)):
代码语言:txt
复制
            lstm_net.x_list_add(x_list[ind])
代码语言:txt
复制
            if cur_iter % 1000 == 0:
代码语言:txt
复制
                print "y_pred[%d] : %f" % (ind, lstm_net.lstm_node_list[ind].state.h[0])
代码语言:txt
复制
        loss = lstm_net.y_list_is(y_list, ToyLossLayer)
代码语言:txt
复制
        if cur_iter % 1000 == 0:
代码语言:txt
复制
            print "loss: ", loss
代码语言:txt
复制
        lstm_param.apply_diff(lr=0.01)
代码语言:txt
复制
        lstm_net.x_list_clear()
代码语言:txt
复制
if __name__ == "__main__":
代码语言:txt
复制
    example_0()

质数列表全都除以100,这个代码训练数据必须是小于1数值。

torch是深度学习框架。1)tensorflow,谷歌主推,时下最火,小型试验和大型计算都可以,基于python,缺点是上手相对较难,速度一般;2)torch,facebook主推,用于小型试验,开源应用较多,基于lua,上手较快,网上文档较全,缺点是lua语言相对冷门;3)mxnet,Amazon主推,主要用于大型计算,基于python和R,缺点是网上开源项目较少;4)caffe,facebook主推,用于大型计算,基于c++、python,缺点是开发不是很方便;5)theano,速度一般,基于python,评价很好。

torch github上lstm实现项目比较多。

在mac上安装torch。https://github.com/torch/torch7/wiki/Cheatsheet#installing-and-running-torch

代码语言:txt
复制
git clone https://github.com/torch/distro.git ~/torch --recursive
代码语言:txt
复制
cd ~/torch; bash install-deps;
代码语言:txt
复制
./install.sh

qt安装不成功问题,自己单独安装。

代码语言:txt
复制
brew install cartr/qt4/qt

安装后需要手工加到~/.bash_profile中。

代码语言:txt
复制
. ~/torch/install/bin/torch-activate

source ~/.bash_profile后执行th使用torch。

安装itorch,安装依赖

代码语言:txt
复制
brew install zeromq
代码语言:txt
复制
brew install openssl
代码语言:txt
复制
luarocks install luacrypto OPENSSL_DIR=/usr/local/opt/openssl/
代码语言:txt
复制
git clone https://github.com/facebook/iTorch.git
代码语言:txt
复制
cd iTorch
代码语言:txt
复制
luarocks make 

用卷积神经网络实现图像识别。

创建pattern_recognition.lua:

代码语言:txt
复制
require 'nn'
代码语言:txt
复制
require 'paths'
代码语言:txt
复制
if (not paths.filep("cifar10torchsmall.zip")) then
代码语言:txt
复制
    os.execute('wget -c https://s3.amazonaws.com/torch7/data/cifar10torchsmall.zip')
代码语言:txt
复制
    os.execute('unzip cifar10torchsmall.zip')
代码语言:txt
复制
end
代码语言:txt
复制
trainset = torch.load('cifar10-train.t7')
代码语言:txt
复制
testset = torch.load('cifar10-test.t7')
代码语言:txt
复制
classes = {'airplane', 'automobile', 'bird', 'cat',
代码语言:txt
复制
'deer', 'dog', 'frog', 'horse', 'ship', 'truck'}
代码语言:txt
复制
setmetatable(trainset,
代码语言:txt
复制
{__index = function(t, i)
代码语言:txt
复制
    return {t.data[i], t.label[i]}
代码语言:txt
复制
end}
代码语言:txt
复制
);
代码语言:txt
复制
trainset.data = trainset.data:double() -- convert the data from a ByteTensor to a DoubleTensor.
代码语言:txt
复制
function trainset:size()
代码语言:txt
复制
    return self.data:size(1)
代码语言:txt
复制
end
代码语言:txt
复制
mean = {} -- store the mean, to normalize the test set in the future
代码语言:txt
复制
stdv  = {} -- store the standard-deviation for the future
代码语言:txt
复制
for i=1,3 do -- over each image channel
代码语言:txt
复制
    mean[i] = trainset.data[{ {}, {i}, {}, {}  }]:mean() -- mean estimation
代码语言:txt
复制
    print('Channel ' .. i .. ', Mean: ' .. mean[i])
代码语言:txt
复制
    trainset.data[{ {}, {i}, {}, {}  }]:add(-mean[i]) -- mean subtraction
代码语言:txt
复制
    stdv[i] = trainset.data[{ {}, {i}, {}, {}  }]:std() -- std estimation
代码语言:txt
复制
    print('Channel ' .. i .. ', Standard Deviation: ' .. stdv[i])
代码语言:txt
复制
    trainset.data[{ {}, {i}, {}, {}  }]:div(stdv[i]) -- std scaling
代码语言:txt
复制
end
代码语言:txt
复制
net = nn.Sequential()
代码语言:txt
复制
net:add(nn.SpatialConvolution(3, 6, 5, 5)) -- 3 input image channels, 6 output channels, 5x5 convolution kernel
代码语言:txt
复制
net:add(nn.ReLU())                       -- non-linearity
代码语言:txt
复制
net:add(nn.SpatialMaxPooling(2,2,2,2))     -- A max-pooling operation that looks at 2x2 windows and finds the max.
代码语言:txt
复制
net:add(nn.SpatialConvolution(6, 16, 5, 5))
代码语言:txt
复制
net:add(nn.ReLU())                       -- non-linearity
代码语言:txt
复制
net:add(nn.SpatialMaxPooling(2,2,2,2))
代码语言:txt
复制
net:add(nn.View(16*5*5))                    -- reshapes from a 3D tensor of 16x5x5 into 1D tensor of 16*5*5
代码语言:txt
复制
net:add(nn.Linear(16*5*5, 120))             -- fully connected layer (matrix multiplication between input and weights)
代码语言:txt
复制
net:add(nn.ReLU())                       -- non-linearity
代码语言:txt
复制
net:add(nn.Linear(120, 84))
代码语言:txt
复制
net:add(nn.ReLU())                       -- non-linearity
代码语言:txt
复制
net:add(nn.Linear(84, 10))                   -- 10 is the number of outputs of the network (in this case, 10 digits)
代码语言:txt
复制
net:add(nn.LogSoftMax())                     -- converts the output to a log-probability. Useful for classification problems
代码语言:txt
复制
criterion = nn.ClassNLLCriterion()
代码语言:txt
复制
trainer = nn.StochasticGradient(net, criterion)
代码语言:txt
复制
trainer.learningRate = 0.001
代码语言:txt
复制
trainer.maxIteration = 5
代码语言:txt
复制
trainer:train(trainset)
代码语言:txt
复制
testset.data = testset.data:double()   -- convert from Byte tensor to Double tensor
代码语言:txt
复制
for i=1,3 do -- over each image channel
代码语言:txt
复制
    testset.data[{ {}, {i}, {}, {}  }]:add(-mean[i]) -- mean subtraction
代码语言:txt
复制
    testset.data[{ {}, {i}, {}, {}  }]:div(stdv[i]) -- std scaling
代码语言:txt
复制
end
代码语言:txt
复制
predicted = net:forward(testset.data[100])
代码语言:txt
复制
print(classes[testset.label[100]])
代码语言:txt
复制
print(predicted:exp())
代码语言:txt
复制
for i=1,predicted:size(1) do
代码语言:txt
复制
    print(classes[i], predicted[i])
代码语言:txt
复制
end
代码语言:txt
复制
correct = 0
代码语言:txt
复制
for i=1,10000 do
代码语言:txt
复制
    local groundtruth = testset.label[i]
代码语言:txt
复制
    local prediction = net:forward(testset.data[i])
代码语言:txt
复制
    local confidences, indices = torch.sort(prediction, true)  -- true means sort in descending order
代码语言:txt
复制
    if groundtruth == indices[1] then
代码语言:txt
复制
        correct = correct + 1
代码语言:txt
复制
    end
代码语言:txt
复制
end
代码语言:txt
复制
print(correct, 100*correct/10000 .. ' % ')
代码语言:txt
复制
class_performance = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
代码语言:txt
复制
for i=1,10000 do
代码语言:txt
复制
    local groundtruth = testset.label[i]
代码语言:txt
复制
    local prediction = net:forward(testset.data[i])
代码语言:txt
复制
    local confidences, indices = torch.sort(prediction, true)  -- true means sort in descending order
代码语言:txt
复制
    if groundtruth == indices[1] then
代码语言:txt
复制
        class_performance[groundtruth] = class_performance[groundtruth] + 1
代码语言:txt
复制
    end
代码语言:txt
复制
end
代码语言:txt
复制
for i=1,#classes do
代码语言:txt
复制
    print(classes[i], 100*class_performance[i]/1000 .. ' %')
代码语言:txt
复制
end

执行th pattern_recognition.lua。

首先下载cifar10torchsmall.zip样本,有50000张训练用图片,10000张测试用图片,分别都标注,包括airplane、automobile等10种分类,对trainset绑定__index和size方法,兼容nn.Sequential使用,绑定函数看lua教程:http://tylerneylon.com/a/learn-lua/ ,trainset数据正规化,数据转成均值为1方差为1的double类型张量。初始化卷积神经网络模型,包括两层卷积、两层池化、一个全连接以及一个softmax层,进行训练,学习率为0.001,迭代5次,模型训练好后对测试机第100号图片做预测,打印出整体正确率以及每种分类准确率。https://github.com/soumith/cvpr2015/blob/master/Deep%20Learning%20with%20Torch.ipynb

torch可以方便支持gpu计算,需要对代码做修改。

比较流行的seq2seq基本都用lstm组成编码器解码器模型实现,开源实现大都基于one-hot embedding(没有词向量表达信息量大)。word2vec词向量 seq2seq模型,只有一个lstm单元机器人。

下载《甄环传》小说原文。上网随便百度“甄环传 txt”,下载下来,把文件转码成utf-8编码,把windows回车符都替换成n,以便后续处理。

对甄环传切词。切词工具word_segment.py到github下载,地址在https://github.com/warmheartli/ChatBotCourse/blob/master/word_segment.py

代码语言:txt
复制
python ./word_segment.py zhenhuanzhuan.txt zhenhuanzhuan.segment

生成词向量。用word2vec,word2vec源码 https://github.com/warmheartli/ChatBotCourse/tree/master/word2vec 。make编译即可执行。

代码语言:txt
复制
./word2vec -train ./zhenhuanzhuan.segment -output vectors.bin -cbow 1 -size 200 -window 8 -negative 25 -hs 0 -sample 1e-4 -threads 20 -binary 1 -iter 15

生成一个vectors.bin文件,基于甄环传原文生成的词向量文件。

训练代码。

代码语言:txt
复制
# -*- coding: utf-8 -*-
代码语言:txt
复制
import sys
代码语言:txt
复制
import math
代码语言:txt
复制
import tflearn
代码语言:txt
复制
import chardet
代码语言:txt
复制
import numpy as np
代码语言:txt
复制
import struct
代码语言:txt
复制
seq = []
代码语言:txt
复制
max_w = 50
代码语言:txt
复制
float_size = 4
代码语言:txt
复制
word_vector_dict = {}
代码语言:txt
复制
def load_vectors(input):
代码语言:txt
复制
    """从vectors.bin加载词向量,返回一个word_vector_dict的词典,key是词,value是200维的向量
代码语言:txt
复制
    """
代码语言:txt
复制
    print "begin load vectors"
代码语言:txt
复制
    input_file = open(input, "rb")
代码语言:txt
复制
    # 获取词表数目及向量维度
代码语言:txt
复制
    words_and_size = input_file.readline()
代码语言:txt
复制
    words_and_size = words_and_size.strip()
代码语言:txt
复制
    words = long(words_and_size.split(' ')[0])
代码语言:txt
复制
    size = long(words_and_size.split(' ')[1])
代码语言:txt
复制
    print "words =", words
代码语言:txt
复制
    print "size =", size
代码语言:txt
复制
    for b in range(0, words):
代码语言:txt
复制
        a = 0
代码语言:txt
复制
        word = ''
代码语言:txt
复制
        # 读取一个词
代码语言:txt
复制
        while True:
代码语言:txt
复制
            c = input_file.read(1)
代码语言:txt
复制
            word = word + c
代码语言:txt
复制
            if False == c or c == ' ':
代码语言:txt
复制
                break
代码语言:txt
复制
            if a < max_w and c != 'n':
代码语言:txt
复制
                a = a + 1
代码语言:txt
复制
        word = word.strip()
代码语言:txt
复制
        vector = []
代码语言:txt
复制
        for index in range(0, size):
代码语言:txt
复制
            m = input_file.read(float_size)
代码语言:txt
复制
            (weight,) = struct.unpack('f', m)
代码语言:txt
复制
            vector.append(weight)
代码语言:txt
复制
        # 将词及其对应的向量存到dict中
代码语言:txt
复制
        word_vector_dict[word.decode('utf-8')] = vector
代码语言:txt
复制
    input_file.close()
代码语言:txt
复制
    print "load vectors finish"
代码语言:txt
复制
def init_seq():
代码语言:txt
复制
    """读取切好词的文本文件,加载全部词序列
代码语言:txt
复制
    """
代码语言:txt
复制
    file_object = open('zhenhuanzhuan.segment', 'r')
代码语言:txt
复制
    vocab_dict = {}
代码语言:txt
复制
    while True:
代码语言:txt
复制
        line = file_object.readline()
代码语言:txt
复制
        if line:
代码语言:txt
复制
            for word in line.decode('utf-8').split(' '):
代码语言:txt
复制
                if word_vector_dict.has_key(word):
代码语言:txt
复制
                    seq.append(word_vector_dict[word])
代码语言:txt
复制
        else:
代码语言:txt
复制
            break
代码语言:txt
复制
    file_object.close()
代码语言:txt
复制
def vector_sqrtlen(vector):
代码语言:txt
复制
    len = 0
代码语言:txt
复制
    for item in vector:
代码语言:txt
复制
        len += item * item
代码语言:txt
复制
    len = math.sqrt(len)
代码语言:txt
复制
    return len
代码语言:txt
复制
def vector_cosine(v1, v2):
代码语言:txt
复制
    if len(v1) != len(v2):
代码语言:txt
复制
        sys.exit(1)
代码语言:txt
复制
    sqrtlen1 = vector_sqrtlen(v1)
代码语言:txt
复制
    sqrtlen2 = vector_sqrtlen(v2)
代码语言:txt
复制
    value = 0
代码语言:txt
复制
    for item1, item2 in zip(v1, v2):
代码语言:txt
复制
        value += item1 * item2
代码语言:txt
复制
    return value / (sqrtlen1*sqrtlen2)
代码语言:txt
复制
def vector2word(vector):
代码语言:txt
复制
    max_cos = -10000
代码语言:txt
复制
    match_word = ''
代码语言:txt
复制
    for word in word_vector_dict:
代码语言:txt
复制
        v = word_vector_dict[word]
代码语言:txt
复制
        cosine = vector_cosine(vector, v)
代码语言:txt
复制
        if cosine > max_cos:
代码语言:txt
复制
            max_cos = cosine
代码语言:txt
复制
            match_word = word
代码语言:txt
复制
    return (match_word, max_cos)
代码语言:txt
复制
def main():
代码语言:txt
复制
    load_vectors("./vectors.bin")
代码语言:txt
复制
    init_seq()
代码语言:txt
复制
    xlist = []
代码语言:txt
复制
    ylist = []
代码语言:txt
复制
    test_X = None
代码语言:txt
复制
    #for i in range(len(seq)-100):
代码语言:txt
复制
    for i in range(10):
代码语言:txt
复制
        sequence = seq[i:i+20]
代码语言:txt
复制
        xlist.append(sequence)
代码语言:txt
复制
        ylist.append(seq[i+20])
代码语言:txt
复制
        if test_X is None:
代码语言:txt
复制
            test_X = np.array(sequence)
代码语言:txt
复制
            (match_word, max_cos) = vector2word(seq[i+20])
代码语言:txt
复制
            print "right answer=", match_word, max_cos
代码语言:txt
复制
    X = np.array(xlist)
代码语言:txt
复制
    Y = np.array(ylist)
代码语言:txt
复制
    net = tflearn.input_data([None, 20, 200])
代码语言:txt
复制
    net = tflearn.lstm(net, 200)
代码语言:txt
复制
    net = tflearn.fully_connected(net, 200, activation='linear')
代码语言:txt
复制
    net = tflearn.regression(net, optimizer='sgd', learning_rate=0.1,
代码语言:txt
复制
                                     loss='mean_square')
代码语言:txt
复制
    model = tflearn.DNN(net)
代码语言:txt
复制
    model.fit(X, Y, n_epoch=500, batch_size=10,snapshot_epoch=False,show_metric=True)
代码语言:txt
复制
    model.save("model")
代码语言:txt
复制
    predict = model.predict([test_X])
代码语言:txt
复制
    #print predict
代码语言:txt
复制
    #for v in test_X:
代码语言:txt
复制
    #    print vector2word(v)
代码语言:txt
复制
    (match_word, max_cos) = vector2word(predict[0])
代码语言:txt
复制
    print "predict=", match_word, max_cos
代码语言:txt
复制
main()

load_vectors从vectors.bin加载词向量,init_seq加载甄环传切词文本并存到一个序列里,vector2word求距离某向量最近词,模型只有一个lstm单元。

经过500个epoch训练,均方损失降到0.33673,以0.941794432002余弦相似度预测出下一个字。

强大gpu,调整参数,整篇文章都训练,修改代码predict部分,不断输出下一个字,自动吐出甄环体。基于tflearn实现,tflearn官方文档examples实现seq2seq直接调用tensorflow中的tensorflow/python/ops/seq2seq.py,基于one-hot embedding方法,一定没有词向量效果好。

参考资料:

《Python 自然语言处理》

http://www.shareditor.com/blogshow?blogId=116

http://www.shareditor.com/blogshow?blogId=117

http://www.shareditor.com/blogshow?blogId=118

欢迎推荐上海机器学习工作机会,我的微信:qingxingfengzi

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
NLP 服务
NLP 服务(Natural Language Process,NLP)深度整合了腾讯内部的 NLP 技术,提供多项智能文本处理和文本生成能力,包括词法分析、相似词召回、词相似度、句子相似度、文本润色、句子纠错、文本补全、句子生成等。满足各行业的文本智能需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com