精华内容
下载资源
问答
  • Bi-LSTM matlab代码 机器学习 算法在岗一年的工作总结 基础教程(github 仓库名称) ai-edu Machine-Learning-for-Beginner-by-Python3 Machine-Learning-From-Scratch ML-From-Scratch numpy-ml tinynn tinynn是一个...
  • matlab代码 项目介绍 该项目的目标是:基于PyTorch实现中文孤立手语词识别; 目前使用的基本网络结构是:Bi-LSTM(双向长短期记忆神经网络); 数据集使用的是:中国科学技术大学提供的500-CSL(500类中文手语单词...
  • Bi-LSTM matlab代码 DataScience-Notes 数据科学笔记。数据科学的相关笔记、代码和实例,包含数学、统计、机器学习、深度学习等数据科学基础,以及某些应用场景的实现。 参考来源已在最后说明。 大部分代码都是 ...
  • RNN. LSTM matlab

    千次阅读 2018-01-04 22:21:38
    版权声明:本文为博主原创...最近在学习RNN和LSTM, (1): http://magicly.me/2017/03/09/iamtrask-anyone-can-code-lstm/ (2): https://zybuluo.com/hanbingtao/note/581764 (3): http://blog.sina.com.cn
    
    

    最近在学习RNN和LSTM,

    (1): http://magicly.me/2017/03/09/iamtrask-anyone-can-code-lstm/

    (2):  https://zybuluo.com/hanbingtao/note/581764

    (3): http://blog.sina.com.cn/s/blog_a5fdbf010102w7y8.html

    在资料1中给出了RNN的python代码,广为流传,并且被(3)翻译成了matlab代码。网址(2)是个很好的理论推导的网站,强烈推荐。想学习的可以缘木求鱼,根据上述资料进行学习。

    但是在(1)中提及到的python源码作者说过段时间在twitter上更新LSTM代码,目前还未更新;(3)的作者想根据RNN写LSTM,但是发现并未运行成功,于是我在(2)的基础上对其进行修改,并运行成功,下面是指出作者(2)中的错误:

    1:作者在求H_t_diff时有问题,不应该乘以那么导数,因为从后面传过来的误差是输出层(不是输出门)的误差乘以权值矩阵就可以了。

    2:求各个门的误差都不用成乘激活函数

    3:各个门中均未加偏置


    如果你运行成功,还望点个赞。哈哈!


    以上错误可以根据我下面给出的源码和(3)中的源码进行比较。

     

    下面是我修改后的源代码:

    [plain] view plain copy
    1. %接下来就是LSTM的Matlab代码,我也进行了注释,用英文注释的,也比较容易懂:  
    2. % implementation of LSTM  
    3. clc  
    4. % clear  
    5. close all  
    6.   
    7.   
    8. %% training dataset generation  
    9. binary_dim     = 8;  
    10.   
    11.   
    12. largest_number = 2^binary_dim - 1;  
    13. binary         = cell(largest_number, 1);  
    14.   
    15.   
    16. for i = 1:largest_number + 1  
    17.     binary{i}      = dec2bin(i-1, binary_dim);  
    18.     int2binary{i}  = binary{i};  
    19. end  
    20.   
    21.   
    22. %% input variables  
    23. alpha      = 0.1;  
    24. input_dim  = 2;  
    25. hidden_dim = 32;  
    26. output_dim = 1;  
    27. allErr = [];  
    28. %% initialize neural network weights  
    29. % in_gate     = sigmoid(X(t) * X_i + H(t-1) * H_i)    ------- (1)  
    30. X_i = 2 * rand(input_dim, hidden_dim) - 1;  
    31. H_i = 2 * rand(hidden_dim, hidden_dim) - 1;  
    32. X_i_update = zeros(size(X_i));  
    33. H_i_update = zeros(size(H_i));  
    34. bi = 2*rand(1,1) - 1;  
    35. bi_update = 0;  
    36.   
    37.   
    38. % forget_gate = sigmoid(X(t) * X_f + H(t-1) * H_f)    ------- (2)  
    39. X_f = 2 * rand(input_dim, hidden_dim) - 1;  
    40. H_f = 2 * rand(hidden_dim, hidden_dim) - 1;  
    41. X_f_update = zeros(size(X_f));  
    42. H_f_update = zeros(size(H_f));  
    43. bf = 2*rand(1,1) - 1;  
    44. bf_update = 0;  
    45. % out_gate    = sigmoid(X(t) * X_o + H(t-1) * H_o)    ------- (3)  
    46. X_o = 2 * rand(input_dim, hidden_dim) - 1;  
    47. H_o = 2 * rand(hidden_dim, hidden_dim) - 1;  
    48. X_o_update = zeros(size(X_o));  
    49. H_o_update = zeros(size(H_o));  
    50. bo = 2*rand(1,1) - 1;  
    51. bo_update = 0;  
    52. % g_gate      = tanh(X(t) * X_g + H(t-1) * H_g)       ------- (4)  
    53. X_g = 2 * rand(input_dim, hidden_dim) - 1;  
    54. H_g = 2 * rand(hidden_dim, hidden_dim) - 1;  
    55. X_g_update = zeros(size(X_g));  
    56. H_g_update = zeros(size(H_g));  
    57. bg = 2*rand(1,1) - 1;  
    58. bg_update = 0;  
    59.   
    60.   
    61. out_para = 2 * rand(hidden_dim, output_dim) - 1;  
    62. out_para_update = zeros(size(out_para));  
    63. % C(t) = C(t-1) .* forget_gate + g_gate .* in_gate    ------- (5)  
    64. % S(t) = tanh(C(t)) .* out_gate                       ------- (6)  
    65. % Out  = sigmoid(S(t) * out_para)                     ------- (7)  
    66. % Note: Equations (1)-(6) are cores of LSTM in forward, and equation (7) is  
    67. % used to transfer hiddent layer to predicted output, i.e., the output layer.  
    68. % (Sometimes you can use softmax for equation (7))  
    69.   
    70.   
    71. %% train   
    72. iter = 99999; % training iterations  
    73. for j = 1:iter  
    74.     % generate a simple addition problem (a + b = c)  
    75.     a_int = randi(round(largest_number/2));   % int version  
    76.     a     = int2binary{a_int+1};              % binary encoding  
    77.       
    78.     b_int = randi(floor(largest_number/2));   % int version  
    79.     b     = int2binary{b_int+1};              % binary encoding  
    80.       
    81.     % true answer  
    82.     c_int = a_int + b_int;                    % int version  
    83.     c     = int2binary{c_int+1};              % binary encoding  
    84.       
    85.     % where we'll store our best guess (binary encoded)  
    86.     d     = zeros(size(c));  
    87.     if length(d)<8  
    88.         pause;  
    89.     end  
    90.       
    91.     % total error  
    92.     overallError = 0;  
    93.       
    94.     % difference in output layer, i.e., (target - out)  
    95.     output_deltas = [];  
    96.       
    97.     % values of hidden layer, i.e., S(t)  
    98.     hidden_layer_values = [];  
    99.     cell_gate_values    = [];  
    100.     % initialize S(0) as a zero-vector  
    101.     hidden_layer_values = [hidden_layer_values; zeros(1, hidden_dim)];  
    102.     cell_gate_values    = [cell_gate_values; zeros(1, hidden_dim)];  
    103.       
    104.     % initialize memory gate  
    105.     % hidden layer  
    106.     H = [];  
    107.     H = [H; zeros(1, hidden_dim)];  
    108.     % cell gate  
    109.     C = [];  
    110.     C = [C; zeros(1, hidden_dim)];  
    111.     % in gate  
    112.     I = [];  
    113.     % forget gate  
    114.     F = [];  
    115.     % out gate  
    116.     O = [];  
    117.     % g gate  
    118.     G = [];  
    119.       
    120.     % start to process a sequence, i.e., a forward pass  
    121.     % Note: the output of a LSTM cell is the hidden_layer, and you need to   
    122.     % transfer it to predicted output  
    123.     for position = 0:binary_dim-1  
    124.         % X ------> input, size: 1 x input_dim  
    125.         X = [a(binary_dim - position)-'0' b(binary_dim - position)-'0'];  
    126.           
    127.         % y ------> label, size: 1 x output_dim  
    128.         y = [c(binary_dim - position)-'0']';  
    129.           
    130.         % use equations (1)-(7) in a forward pass. here we do not use bias  
    131.         in_gate     = sigmoid(X * X_i + H(end, :) * H_i + bi);  % equation (1)  
    132.         forget_gate = sigmoid(X * X_f + H(end, :) * H_f + bf);  % equation (2)  
    133.         out_gate    = sigmoid(X * X_o + H(end, :) * H_o + bo);  % equation (3)  
    134.         g_gate      = tan_h(X * X_g + H(end, :) * H_g + bg);    % equation (4)  
    135.         C_t         = C(end, :) .* forget_gate + g_gate .* in_gate;    % equation (5)  
    136.         H_t         = tan_h(C_t) .* out_gate;                          % equation (6)  
    137.           
    138.         % store these memory gates  
    139.         I = [I; in_gate];  
    140.         F = [F; forget_gate];  
    141.         O = [O; out_gate];  
    142.         G = [G; g_gate];  
    143.         C = [C; C_t];  
    144.         H = [H; H_t];  
    145.           
    146.         % compute predict output  
    147.         pred_out = sigmoid(H_t * out_para);  
    148.           
    149.         % compute error in output layer  
    150.         output_error = y - pred_out;  
    151.           
    152.         % compute difference in output layer using derivative  
    153.         % output_diff = output_error * sigmoid_output_to_derivative(pred_out);  
    154.         output_deltas = [output_deltas; output_error];%*sigmoid_output_to_derivative(pred_out)];  
    155. %         output_deltas = [output_deltas; output_error*(pred_out)];  
    156.         % compute total error  
    157.         % note that if the size of pred_out or target is 1 x n or m x n,  
    158.         % you should use other approach to compute error. here the dimension   
    159.         % of pred_out is 1 x 1  
    160.         overallError = overallError + abs(output_error(1));  
    161.           
    162.         % decode estimate so we can print it out  
    163.         d(binary_dim - position) = round(pred_out);  
    164.     end  
    165.       
    166.     % from the last LSTM cell, you need a initial hidden layer difference  
    167.     future_H_diff = zeros(1, hidden_dim);  
    168.       
    169.     % stare back-propagation, i.e., a backward pass  
    170.     % the goal is to compute differences and use them to update weights  
    171.     % start from the last LSTM cell  
    172.     for position = 0:binary_dim-1  
    173.         X = [a(position+1)-'0' b(position+1)-'0'];  
    174.           
    175.         % hidden layer  
    176.         H_t = H(end-position, :);         % H(t)  
    177.         % previous hidden layer  
    178.         H_t_1 = H(end-position-1, :);     % H(t-1)  
    179.         C_t = C(end-position, :);         % C(t)  
    180.         C_t_1 = C(end-position-1, :);     % C(t-1)  
    181.         O_t = O(end-position, :);  
    182.         F_t = F(end-position, :);  
    183.         G_t = G(end-position, :);  
    184.         I_t = I(end-position, :);  
    185.           
    186.         % output layer difference  
    187.         output_diff = output_deltas(end-position, :);  
    188.           
    189.         % hidden layer difference  
    190.         % note that here we consider one hidden layer is input to both  
    191.         % output layer and next LSTM cell. Thus its difference also comes  
    192.         % from two sources. In some other method, only one source is taken  
    193.         % into consideration.  
    194.         % use the equation: delta(l) = (delta(l+1) * W(l+1)) .* f'(z) to  
    195.         % compute difference in previous layers. look for more about the  
    196.         % proof at http://neuralnetworksanddeeplearning.com/chap2.html  
    197. %         H_t_diff = (future_H_diff * (H_i' + H_o' + H_f' + H_g') + output_diff * out_para') ...  
    198. %                    .* sigmoid_output_to_derivative(H_t);  
    199.   
    200.   
    201.         H_t_diff = output_diff * (out_para');% .* sigmoid_output_to_derivative(H_t);  
    202. %         H_t_diff = output_diff * (out_para') .* sigmoid_output_to_derivative(H_t);  
    203. %         future_H_diff = H_t_diff;  
    204. %         out_para_diff = output_diff * (H_t) * sigmoid_output_to_derivative(out_para);  
    205.         out_para_diff =  (H_t') * output_diff;%输出层权重  
    206.   
    207.   
    208.         % out_gate diference  
    209.         O_t_diff = H_t_diff .* tan_h(C_t) .* sigmoid_output_to_derivative(O_t);  
    210.           
    211.         % C_t difference  
    212.         C_t_diff = H_t_diff .* O_t .* tan_h_output_to_derivative(C_t);  
    213.           
    214. %         % C(t-1) difference  
    215. %         C_t_1_diff = C_t_diff .* F_t;  
    216.           
    217.         % forget_gate_diffeence  
    218.         F_t_diff = C_t_diff .* C_t_1 .* sigmoid_output_to_derivative(F_t);  
    219.           
    220.         % in_gate difference  
    221.         I_t_diff = C_t_diff .* G_t .* sigmoid_output_to_derivative(I_t);  
    222.           
    223.         % g_gate difference  
    224.         G_t_diff = C_t_diff .* I_t .* tan_h_output_to_derivative(G_t);  
    225.           
    226.         % differences of X_i and H_i  
    227.         X_i_diff =  X' * I_t_diff;% .* sigmoid_output_to_derivative(X_i);  
    228.         H_i_diff =  (H_t_1)' * I_t_diff;% .* sigmoid_output_to_derivative(H_i);  
    229.           
    230.         % differences of X_o and H_o  
    231.         X_o_diff = X' * O_t_diff;% .* sigmoid_output_to_derivative(X_o);  
    232.         H_o_diff = (H_t_1)' * O_t_diff;% .* sigmoid_output_to_derivative(H_o);  
    233.           
    234.         % differences of X_o and H_o  
    235.         X_f_diff = X' * F_t_diff;% .* sigmoid_output_to_derivative(X_f);  
    236.         H_f_diff = (H_t_1)' * F_t_diff;% .* sigmoid_output_to_derivative(H_f);  
    237.           
    238.         % differences of X_o and H_o  
    239.         X_g_diff = X' * G_t_diff;% .* tan_h_output_to_derivative(X_g);  
    240.         H_g_diff = (H_t_1)' * G_t_diff;% .* tan_h_output_to_derivative(H_g);  
    241.           
    242.         % update  
    243.         X_i_update = X_i_update + X_i_diff;  
    244.         H_i_update = H_i_update + H_i_diff;  
    245.         X_o_update = X_o_update + X_o_diff;  
    246.         H_o_update = H_o_update + H_o_diff;  
    247.         X_f_update = X_f_update + X_f_diff;  
    248.         H_f_update = H_f_update + H_f_diff;  
    249.         X_g_update = X_g_update + X_g_diff;  
    250.         H_g_update = H_g_update + H_g_diff;  
    251.         bi_update = bi_update + I_t_diff;  
    252.         bo_update = bo_update + O_t_diff;  
    253.         bf_update = bf_update + F_t_diff;  
    254.         bg_update = bg_update + G_t_diff;                          
    255.         out_para_update = out_para_update + out_para_diff;  
    256.     end  
    257.       
    258.     X_i = X_i + X_i_update * alpha;   
    259.     H_i = H_i + H_i_update * alpha;  
    260.     X_o = X_o + X_o_update * alpha;   
    261.     H_o = H_o + H_o_update * alpha;  
    262.     X_f = X_f + X_f_update * alpha;   
    263.     H_f = H_f + H_f_update * alpha;  
    264.     X_g = X_g + X_g_update * alpha;   
    265.     H_g = H_g + H_g_update * alpha;  
    266.     bi = bi + bi_update * alpha;  
    267.     bo = bo + bo_update * alpha;  
    268.     bf = bf + bf_update * alpha;  
    269.     bg = bg + bg_update * alpha;  
    270.     out_para = out_para + out_para_update * alpha;  
    271.       
    272.     X_i_update = X_i_update * 0;   
    273.     H_i_update = H_i_update * 0;  
    274.     X_o_update = X_o_update * 0;   
    275.     H_o_update = H_o_update * 0;  
    276.     X_f_update = X_f_update * 0;   
    277.     H_f_update = H_f_update * 0;  
    278.     X_g_update = X_g_update * 0;   
    279.     H_g_update = H_g_update * 0;  
    280.     bi_update = 0;  
    281.     bf_update = 0;  
    282.     bo_update = 0;  
    283.     bg_update = 0;  
    284.     out_para_update = out_para_update * 0;  
    285.       
    286.     if(mod(j,1000) == 0)  
    287.         if 1%overallError > 1  
    288.             err = sprintf('Error:%s\n', num2str(overallError)); fprintf(err);  
    289.         end  
    290.         allErr = [allErr overallError];  
    291. %         try  
    292.             d = bin2dec(num2str(d));  
    293. %         catch  
    294. %             disp(d);  
    295. %         end  
    296.         if 1%overallError>1  
    297.         pred = sprintf('Pred:%s\n',dec2bin(d,8)); fprintf(pred);  
    298.         Tru = sprintf('True:%s\n', num2str(c)); fprintf(Tru);  
    299.         end  
    300.         out = 0;  
    301.         tmp = dec2bin(d,8);  
    302.         for i = 1:8             
    303.             out = out + str2double(tmp(8-i+1)) * power(2,i-1);  
    304.         end  
    305.         if 1%overallError>1  
    306.         fprintf('%d + %d = %d\n',a_int,b_int,out);  
    307.         sep = sprintf('-------%d------\n', j); fprintf(sep);  
    308.         end  
    309.     end  
    310. end  
    311. figure;plot(allErr);  
    312. function output = sigmoid(x)  
    313.     output = 1./(1+exp(-x));  
    314. end  
    315.   
    316.   
    317. function y = sigmoid_output_to_derivative(output)  
    318.     y = output.*(1-output);  
    319. end  
    320.   
    321.   
    322. function y = tan_h_output_to_derivative(x)  
    323.     y = (1-x.^2);  
    324. end  
    325.   
    326.   
    327. function y=tan_h(x)  
    328. y=(exp(x)-exp(-x))./(exp(x)+exp(-x));  
    329. end  

    展开全文
  • lstm代码matlab lstm_matlab 长短期记忆的matlab版本该代码适用于 lstm 模型。 各个文件的作用列举如下: lstmcellsetup.m:为前馈反向传播神经网络创建一个 lstmcell 层。 lstmcellff.m:执行前馈传递。 lstmcellbp...
  • Matlab函数代码lstm-matlab Moritz Nakatenus的Matlab LSTM深度学习框架。 概述 这项工作是Elmar Rueckert指导的荣誉论文的一部分。 该框架可以通过窥Kong连接处理LSTM单元。 所有梯度都是通过完全解析得出的。 有关...
  • # LSTM_MATLAB LSTM_MATLABMATLAB 中的 Long Short-term Memory (LSTM),旨在简洁、说明性并仅用于研究目的。 附有论文供参考:[Revisit Long Short-Term Memory: An Optimization Perspective],NIPS 深度学习...
  • cg法matlab代码LSTM-MATLAB LSTM-MATLABMATLAB中的长短期记忆(LSTM),旨在简洁,说明性且仅用于研究目的。 随附论文供参考:NIPS深度学习研讨会,2014年。 创建者和维护者齐柳 #特征 原始的长期短期记忆 全部...
  • LSTM-Matlab代码

    2017-11-29 17:26:48
    LSTM-Matlab代码,Deep Learning, AI,Machine Learning
  • lstm预测】基于鲸鱼优化算法改进的lstm预测matlab源码
  • lstm_matlab-master.rar

    2020-02-08 17:18:50
    一个简单地利用lstm神经网络学习的例子,可以自定义神经网络的参数,其中包括内部梯度迭代的过程,适合初学者学习。
  • LSTM神经网络MATLAB

    2018-12-06 16:07:36
    本资源是MATLAB代码,LSTM神经网络,用于预测分类。代码中numdely 是用前numdely个点预测当前点,cell_num是隐含层的数目,cost_gate 是误差的阈值。 直接在命令行输入RunLstm(numdely,cell_num,cost_gate)即可。
  • RNN及LSTMmatlab实现

    万次阅读 多人点赞 2016-06-08 13:50:31
    RNN以及LSTMMatlab代码  ▼ 转载自新浪博客:http://blog.sina.com.cn/s/blog_a5fdbf010102w7y8.html#cmt_3541649 最近一致在研究RNN,RNN网络有很多种类型,我主要是对LSTM这种网络比较感兴趣,之前看了Trask...

    RNN以及LSTM的Matlab代码

     
    转载自新浪博客:http://blog.sina.com.cn/s/blog_a5fdbf010102w7y8.html#cmt_3541649
    最近一致在研究RNN,RNN网络有很多种类型,我主要是对LSTM这种网络比较感兴趣,之前看了Trask的博客(https://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/),他给出了基本的RNN的Python代码,我将其用Matlab实现了。此外,在此基础上,我还是实现了LSTM的Matlab版本,但是有一点要说明的是,RNN的实验结果比较好,但是LSTM的结果却不怎么好,我有两方面的怀疑,第一个是LSTM并不适合本实验中的例子;第二就是本人实现的LSTM网络有问题,如果是这样,希望大家帮助我指出来(貌似我感觉原理没有问题)

    下面首先给出RNN的Matlab代码,里面有些地方我进行了注释:
    % implementation of RNN 
    clc
    clear
    close all
    %% training dataset generation
    binary_dim = 8;

    largest_number = 2^binary_dim-1;
    binary = cell(largest_number,1);
    int2binary = cell(largest_number,1);
    for i = 1:largest_number+1
        binary{i} = dec2bin(i-1, 8);
        int2binary{i} = binary{i};
    end

    %% input variables
    alpha = 0.1;
    input_dim = 2;
    hidden_dim = 16;
    output_dim = 1;

    %% initialize neural network weights
    synapse_0 = 2*rand(input_dim,hidden_dim) - 1;
    synapse_1 = 2*rand(hidden_dim,output_dim) - 1;
    synapse_h = 2*rand(hidden_dim,hidden_dim) - 1;

    synapse_0_update = zeros(size(synapse_0));
    synapse_1_update = zeros(size(synapse_1));
    synapse_h_update = zeros(size(synapse_h));

    %% train logic
    for j = 0:19999
        % generate a simple addition problem (a + b = c)
        a_int = randi(round(largest_number/2)); % int version
        a = int2binary{a_int+1}; % binary encoding
        
        b_int = randi(floor(largest_number/2)); % int version
        b = int2binary{b_int+1}; % binary encoding
        
        % true answer
        c_int = a_int + b_int;
        c = int2binary{c_int+1};
        
        % where we'll store our best guess (binary encoded)
        d = zeros(size(c));
        
        if length(d)<8
            pause;
        end
        
        overallError = 0;
        
        layer_2_deltas = [];
        layer_1_values = [];
        layer_1_values = [layer_1_values; zeros(1, hidden_dim)];
        
        % 开始对一个序列进行处理,搞清楚一个东西,一个LSTM单元的输出其实就是隐含层
        for position = 0:binary_dim-1
            X = [a(binary_dim - position)-'0' b(binary_dim - position)-'0'];   % X 是 input
            y = [c(binary_dim - position)-'0']';                               % Y 是label,用来计算最后误差
            
            % 这里是RNN,因此隐含层比较简单
            % X ------------------------> input
            % sunapse_0 ----------------> U_i
            % layer_1_values(end, :) ---> previous hidden layer (S(t-1))
            % synapse_h ----------------> W_i
            % layer_1 ------------------> new hidden layer (S(t))
            layer_1 = sigmoid(X*synapse_0 + layer_1_values(end, :)*synapse_h);
            
            % layer_1 ------------------> hidden layer (S(t))
            % layer_2 ------------------> 最终的输出结果,其维度应该与 label (Y) 的维度是一致的
            % 这里的 sigmoid 其实就是一个变换,将 hidden layer (size: 1 x 16) 变换为 1 x 1
            % 有写时候,如果输入与输出不匹配的话,使可以使用 softmax 进行变化的
            % output layer (new binary representation)
            layer_2 = sigmoid(layer_1*synapse_1);
            
            % 计算误差,根据误差进行反向传播
            % layer_2_error ------------> 此次(第 position+1 次的误差)
            % l 是真实结果
            % layer_2 是输出结果
            % layer_2_deltas 输出层的变化结果,使用了反向传播,见那个求导(输出层的输入是 layer_2,那就对输入求导即可,然后乘以误差就可以得到输出的diff)
            % did we miss?... if so, by how much?
            layer_2_error = y - layer_2;
            layer_2_deltas = [layer_2_deltas; layer_2_error*sigmoid_output_to_derivative(layer_2)];
            
            % 总体的误差(误差有正有负,用绝对值)
            overallError = overallError + abs(layer_2_error(1));
            
            % decode estimate so we can print it out
            % 就是记录此位置的输出,用于显示结果
            d(binary_dim - position) = round(layer_2(1));
            
            % 记录下此次的隐含层 (S(t))
            % store hidden layer so we can use it in the next timestep
            layer_1_values = [layer_1_values; layer_1];
        end
        
        % 计算隐含层的diff,用于求参数的变化,并用来更新参数,还是每一个timestep来进行计算
        future_layer_1_delta = zeros(1, hidden_dim);
        
        % 开始进行反向传播,计算 hidden_layer 的diff,以及参数的 diff
        for position = 0:binary_dim-1
            % 因为是通过输入得到隐含层,因此这里还是需要用到输入的
            % a -> (operation) -> y, x_diff = derivative(x) * y_diff
            % 注意这里从最后开始往前推
            X = [a(position+1)-'0' b(position+1)-'0'];
            % layer_1 -----------------> 表示隐含层 hidden_layer (S(t))
            % prev_layer_1 ------------> (S(t-1))
            layer_1 = layer_1_values(end-position, :);
            prev_layer_1 = layer_1_values(end-position-1, :);
            
            % layer_2_delta -----------> 就是隐含层的diff
            % hidden_layer_diff,根据这个可以推算输入的diff以及上一个隐含层的diff
            % error at output layer
            layer_2_delta = layer_2_deltas(end-position, :);
            % 这个地方的 hidden_layer 来自两个方面,因为 hidden_layer -> next timestep, hidden_layer -> output,
            % 因此其反向传播也是两方面
            % error at hidden layer
            layer_1_delta = (future_layer_1_delta*(synapse_h') + layer_2_delta*(synapse_1')) ...
                            .* sigmoid_output_to_derivative(layer_1);
            
            % let's update all our weights so we can try again
            synapse_1_update = synapse_1_update + (layer_1')*(layer_2_delta);
            synapse_h_update = synapse_h_update + (prev_layer_1')*(layer_1_delta);
            synapse_0_update = synapse_0_update + (X')*(layer_1_delta);
            
            future_layer_1_delta = layer_1_delta;
        end
        
        synapse_0 = synapse_0 + synapse_0_update * alpha;
        synapse_1 = synapse_1 + synapse_1_update * alpha;
        synapse_h = synapse_h + synapse_h_update * alpha;
        
        synapse_0_update = synapse_0_update * 0;
        synapse_1_update = synapse_1_update * 0;
        synapse_h_update = synapse_h_update * 0;
        
        if(mod(j,1000) == 0)
            err = sprintf('Error:%s\n', num2str(overallError)); fprintf(err);
            d = bin2dec(num2str(d));
            pred = sprintf('Pred:%s\n',dec2bin(d,8)); fprintf(pred);
            Tru = sprintf('True:%s\n', num2str(c)); fprintf(Tru);
            out = 0;
            size(c)
            sep = sprintf('-------------\n'); fprintf(sep);
        end
        
    end


    接下来就是LSTM的Matlab代码,我也进行了注释,用英文注释的,也比较容易懂:
    % implementation of LSTM
    clc
    clear
    close all

    %% training dataset generation
    binary_dim     = 8;

    largest_number = 2^binary_dim - 1;
    binary         = cell(largest_number, 1);

    for i = 1:largest_number + 1
        binary{i}      = dec2bin(i-1, binary_dim);
        int2binary{i}  = binary{i};
    end

    %% input variables
    alpha      = 0.1;
    input_dim  = 2;
    hidden_dim = 32;
    output_dim = 1;

    %% initialize neural network weights
    % in_gate     = sigmoid(X(t) * U_i + H(t-1) * W_i)    ------- (1)
    U_i = 2 * rand(input_dim, hidden_dim) - 1;
    W_i = 2 * rand(hidden_dim, hidden_dim) - 1;
    U_i_update = zeros(size(U_i));
    W_i_update = zeros(size(W_i));

    % forget_gate = sigmoid(X(t) * U_f + H(t-1) * W_f)    ------- (2)
    U_f = 2 * rand(input_dim, hidden_dim) - 1;
    W_f = 2 * rand(hidden_dim, hidden_dim) - 1;
    U_f_update = zeros(size(U_f));
    W_f_update = zeros(size(W_f));

    % out_gate    = sigmoid(X(t) * U_o + H(t-1) * W_o)    ------- (3)
    U_o = 2 * rand(input_dim, hidden_dim) - 1;
    W_o = 2 * rand(hidden_dim, hidden_dim) - 1;
    U_o_update = zeros(size(U_o));
    W_o_update = zeros(size(W_o));

    % g_gate      = tanh(X(t) * U_g + H(t-1) * W_g)       ------- (4)
    U_g = 2 * rand(input_dim, hidden_dim) - 1;
    W_g = 2 * rand(hidden_dim, hidden_dim) - 1;
    U_g_update = zeros(size(U_g));
    W_g_update = zeros(size(W_g));

    out_para = 2 * rand(hidden_dim, output_dim) - 1;
    out_para_update = zeros(size(out_para));
    % C(t) = C(t-1) .* forget_gate + g_gate .* in_gate    ------- (5)
    % S(t) = tanh(C(t)) .* out_gate                       ------- (6)
    % Out  = sigmoid(S(t) * out_para)                     ------- (7)
    % Note: Equations (1)-(6) are cores of LSTM in forward, and equation (7) is
    % used to transfer hiddent layer to predicted output, i.e., the output layer.
    % (Sometimes you can use softmax for equation (7))

    %% train 
    iter = 99999; % training iterations
    for j = 1:iter
        % generate a simple addition problem (a + b = c)
        a_int = randi(round(largest_number/2));   % int version
        a     = int2binary{a_int+1};              % binary encoding
        
        b_int = randi(floor(largest_number/2));   % int version
        b     = int2binary{b_int+1};              % binary encoding
        
        % true answer
        c_int = a_int + b_int;                    % int version
        c     = int2binary{c_int+1};              % binary encoding
        
        % where we'll store our best guess (binary encoded)
        d     = zeros(size(c));
        if length(d)<8
            pause;
        end
        
        % total error
        overallError = 0;
        
        % difference in output layer, i.e., (target - out)
        output_deltas = [];
        
        % values of hidden layer, i.e., S(t)
        hidden_layer_values = [];
        cell_gate_values    = [];
        % initialize S(0) as a zero-vector
        hidden_layer_values = [hidden_layer_values; zeros(1, hidden_dim)];
        cell_gate_values    = [cell_gate_values; zeros(1, hidden_dim)];
        
        % initialize memory gate
        % hidden layer
        H = [];
        H = [H; zeros(1, hidden_dim)];
        % cell gate
        C = [];
        C = [C; zeros(1, hidden_dim)];
        % in gate
        I = [];
        % forget gate
        F = [];
        % out gate
        O = [];
        % g gate
        G = [];
        
        % start to process a sequence, i.e., a forward pass
        % Note: the output of a LSTM cell is the hidden_layer, and you need to 
        % transfer it to predicted output
        for position = 0:binary_dim-1
            % X ------> input, size: 1 x input_dim
            X = [a(binary_dim - position)-'0' b(binary_dim - position)-'0'];
            
            % y ------> label, size: 1 x output_dim
            y = [c(binary_dim - position)-'0']';
            
            % use equations (1)-(7) in a forward pass. here we do not use bias
            in_gate     = sigmoid(X * U_i + H(end, :) * W_i);  % equation (1)
            forget_gate = sigmoid(X * U_f + H(end, :) * W_f);  % equation (2)
            out_gate    = sigmoid(X * U_o + H(end, :) * W_o);  % equation (3)
            g_gate      = tan_h(X * U_g + H(end, :) * W_g);    % equation (4)
            C_t         = C(end, :) .* forget_gate + g_gate .* in_gate;    % equation (5)
            H_t         = tan_h(C_t) .* out_gate;                          % equation (6)
            
            % store these memory gates
            I = [I; in_gate];
            F = [F; forget_gate];
            O = [O; out_gate];
            G = [G; g_gate];
            C = [C; C_t];
            H = [H; H_t];
            
            % compute predict output
            pred_out = sigmoid(H_t * out_para);
            
            % compute error in output layer
            output_error = y - pred_out;
            
            % compute difference in output layer using derivative
            % output_diff = output_error * sigmoid_output_to_derivative(pred_out);
            output_deltas = [output_deltas; output_error];
            
            % compute total error
            % note that if the size of pred_out or target is 1 x n or m x n,
            % you should use other approach to compute error. here the dimension 
            % of pred_out is 1 x 1
            overallError = overallError + abs(output_error(1));
            
            % decode estimate so we can print it out
            d(binary_dim - position) = round(pred_out);
        end
        
        % from the last LSTM cell, you need a initial hidden layer difference
        future_H_diff = zeros(1, hidden_dim);
        
        % stare back-propagation, i.e., a backward pass
        % the goal is to compute differences and use them to update weights
        % start from the last LSTM cell
        for position = 0:binary_dim-1
            X = [a(position+1)-'0' b(position+1)-'0'];
            
            % hidden layer
            H_t = H(end-position, :);         % H(t)
            % previous hidden layer
            H_t_1 = H(end-position-1, :);     % H(t-1)
            C_t = C(end-position, :);         % C(t)
            C_t_1 = C(end-position-1, :);     % C(t-1)
            O_t = O(end-position, :);
            F_t = F(end-position, :);
            G_t = G(end-position, :);
            I_t = I(end-position, :);
            
            % output layer difference
            output_diff = output_deltas(end-position, :);
            
            % hidden layer difference
            % note that here we consider one hidden layer is input to both
            % output layer and next LSTM cell. Thus its difference also comes
            % from two sources. In some other method, only one source is taken
            % into consideration.
            % use the equation: delta(l) = (delta(l+1) * W(l+1)) .* f'(z) to
            % compute difference in previous layers. look for more about the
            % proof at http://neuralnetworksanddeeplearning.com/chap2.html
    %         H_t_diff = (future_H_diff * (W_i' + W_o' + W_f' + W_g') + output_diff * out_para') ...
    %                    .* sigmoid_output_to_derivative(H_t);

    %         H_t_diff = output_diff * (out_para') .* sigmoid_output_to_derivative(H_t);
            H_t_diff = output_diff * (out_para') .* sigmoid_output_to_derivative(H_t);
            
    %         out_para_diff = output_diff * (H_t) * sigmoid_output_to_derivative(out_para);
            out_para_diff =  (H_t') * output_diff;

            % out_gate diference
            O_t_diff = H_t_diff .* tan_h(C_t) .* sigmoid_output_to_derivative(O_t);
            
            % C_t difference
            C_t_diff = H_t_diff .* O_t .* tan_h_output_to_derivative(C_t);
            
    %         % C(t-1) difference
    %         C_t_1_diff = C_t_diff .* F_t;
            
            % forget_gate_diffeence
            F_t_diff = C_t_diff .* C_t_1 .* sigmoid_output_to_derivative(F_t);
            
            % in_gate difference
            I_t_diff = C_t_diff .* G_t .* sigmoid_output_to_derivative(I_t);
            
            % g_gate difference
            G_t_diff = C_t_diff .* I_t .* tan_h_output_to_derivative(G_t);
            
            % differences of U_i and W_i
            U_i_diff =  X' * I_t_diff .* sigmoid_output_to_derivative(U_i);
            W_i_diff =  (H_t_1)' * I_t_diff .* sigmoid_output_to_derivative(W_i);
            
            % differences of U_o and W_o
            U_o_diff = X' * O_t_diff .* sigmoid_output_to_derivative(U_o);
            W_o_diff = (H_t_1)' * O_t_diff .* sigmoid_output_to_derivative(W_o);
            
            % differences of U_o and W_o
            U_f_diff = X' * F_t_diff .* sigmoid_output_to_derivative(U_f);
            W_f_diff = (H_t_1)' * F_t_diff .* sigmoid_output_to_derivative(W_f);
            
            % differences of U_o and W_o
            U_g_diff = X' * G_t_diff .* tan_h_output_to_derivative(U_g);
            W_g_diff = (H_t_1)' * G_t_diff .* tan_h_output_to_derivative(W_g);
            
            % update
            U_i_update = U_i_update + U_i_diff;
            W_i_update = W_i_update + W_i_diff;
            U_o_update = U_o_update + U_o_diff;
            W_o_update = W_o_update + W_o_diff;
            U_f_update = U_f_update + U_f_diff;
            W_f_update = W_f_update + W_f_diff;
            U_g_update = U_g_update + U_g_diff;
            W_g_update = W_g_update + W_g_diff;
            out_para_update = out_para_update + out_para_diff;
        end
        
        U_i = U_i + U_i_update * alpha; 
        W_i = W_i + W_i_update * alpha;
        U_o = U_o + U_o_update * alpha; 
        W_o = W_o + W_o_update * alpha;
        U_f = U_f + U_f_update * alpha; 
        W_f = W_f + W_f_update * alpha;
        U_g = U_g + U_g_update * alpha; 
        W_g = W_g + W_g_update * alpha;
        out_para = out_para + out_para_update * alpha;
        
        U_i_update = U_i_update * 0; 
        W_i_update = W_i_update * 0;
        U_o_update = U_o_update * 0; 
        W_o_update = W_o_update * 0;
        U_f_update = U_f_update * 0; 
        W_f_update = W_f_update * 0;
        U_g_update = U_g_update * 0; 
        W_g_update = W_g_update * 0;
        out_para_update = out_para_update * 0;
        
        if(mod(j,1000) == 0)
            err = sprintf('Error:%s\n', num2str(overallError)); fprintf(err);
            d = bin2dec(num2str(d));
            pred = sprintf('Pred:%s\n',dec2bin(d,8)); fprintf(pred);
            Tru = sprintf('True:%s\n', num2str(c)); fprintf(Tru);
            out = 0;
            sep = sprintf('-------------\n'); fprintf(sep);
        end
    end

    我找到了这篇博客里引用的那个大神的博客的中文翻译版:转载自https://blog.csdn.net/zzukun/article/details/49968129#0-tsina-1-23578-397232819ff9a47a7b7e80a40613cfe1

    0. 前言

    本文翻译自博客: iamtrask.github.io ,这次翻译已经获得trask本人的同意与支持,在此特别感谢trask。本文属于作者一边学习一边翻译的作品,所以在用词、理论方面难免会出现很多错误,假如您发现错误或者不合适的地方,可以给我留言,谢谢!

    1. 概要

    我的最佳学习法就是通过玩具代码,一边调试一边学习理论。这篇博客通过一个非常简单的Python玩具代码来讲解递归神经网络。

    那么依旧是废话少说,放‘码’过来!

    [python] view plain copy
    1. import copy, numpy as np  
    2. np.random.seed(0)  
    3.   
    4. # compute sigmoid nonlinearity  
    5. def sigmoid(x):  
    6.     output = 1/(1+np.exp(-x))  
    7.     return output  
    8.   
    9. # convert output of sigmoid function to its derivative  
    10. def sigmoid_output_to_derivative(output):  
    11.     return output*(1-output)  
    12.   
    13.   
    14. # training dataset generation  
    15. int2binary = {}  
    16. binary_dim = 8  
    17.   
    18. largest_number = pow(2,binary_dim)  
    19. binary = np.unpackbits(  
    20.     np.array([range(largest_number)],dtype=np.uint8).T,axis=1)  
    21. for i in range(largest_number):  
    22.     int2binary[i] = binary[i]  
    23.   
    24.   
    25. # input variables  
    26. alpha = 0.1  
    27. input_dim = 2  
    28. hidden_dim = 16  
    29. output_dim = 1  
    30.   
    31.   
    32. # initialize neural network weights  
    33. synapse_0 = 2*np.random.random((input_dim,hidden_dim)) - 1  
    34. synapse_1 = 2*np.random.random((hidden_dim,output_dim)) - 1  
    35. synapse_h = 2*np.random.random((hidden_dim,hidden_dim)) - 1  
    36.   
    37. synapse_0_update = np.zeros_like(synapse_0)  
    38. synapse_1_update = np.zeros_like(synapse_1)  
    39. synapse_h_update = np.zeros_like(synapse_h)  
    40.   
    41. # training logic  
    42. for j in range(10000):  
    43.       
    44.     # generate a simple addition problem (a + b = c)  
    45.     a_int = np.random.randint(largest_number/2# int version  
    46.     a = int2binary[a_int] # binary encoding  
    47.   
    48.     b_int = np.random.randint(largest_number/2# int version  
    49.     b = int2binary[b_int] # binary encoding  
    50.   
    51.     # true answer  
    52.     c_int = a_int + b_int  
    53.     c = int2binary[c_int]  
    54.       
    55.     # where we'll store our best guess (binary encoded)  
    56.     d = np.zeros_like(c)  
    57.   
    58.     overallError = 0  
    59.       
    60.     layer_2_deltas = list()  
    61.     layer_1_values = list()  
    62.     layer_1_values.append(np.zeros(hidden_dim))  
    63.       
    64.     # moving along the positions in the binary encoding  
    65.     for position in range(binary_dim):  
    66.           
    67.         # generate input and output  
    68.         X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])  
    69.         y = np.array([[c[binary_dim - position - 1]]]).T  
    70.   
    71.         # hidden layer (input ~+ prev_hidden)  
    72.         layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))  
    73.   
    74.         # output layer (new binary representation)  
    75.         layer_2 = sigmoid(np.dot(layer_1,synapse_1))  
    76.   
    77.         # did we miss?... if so by how much?  
    78.         layer_2_error = y - layer_2  
    79.         layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2))  
    80.         overallError += np.abs(layer_2_error[0])  
    81.       
    82.         # decode estimate so we can print it out  
    83.         d[binary_dim - position - 1] = np.round(layer_2[0][0])  
    84.           
    85.         # store hidden layer so we can use it in the next timestep  
    86.         layer_1_values.append(copy.deepcopy(layer_1))  
    87.       
    88.     future_layer_1_delta = np.zeros(hidden_dim)  
    89.       
    90.     for position in range(binary_dim):  
    91.           
    92.         X = np.array([[a[position],b[position]]])  
    93.         layer_1 = layer_1_values[-position-1]  
    94.         prev_layer_1 = layer_1_values[-position-2]  
    95.           
    96.         # error at output layer  
    97.         layer_2_delta = layer_2_deltas[-position-1]  
    98.         # error at hidden layer  
    99.         layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + \  
    100.             layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)  
    101.         # let's update all our weights so we can try again  
    102.         synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)  
    103.         synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)  
    104.         synapse_0_update += X.T.dot(layer_1_delta)  
    105.           
    106.         future_layer_1_delta = layer_1_delta  
    107.       
    108.   
    109.     synapse_0 += synapse_0_update * alpha  
    110.     synapse_1 += synapse_1_update * alpha  
    111.     synapse_h += synapse_h_update * alpha      
    112.   
    113.     synapse_0_update *= 0  
    114.     synapse_1_update *= 0  
    115.     synapse_h_update *= 0  
    116.       
    117.     # print out progress  
    118.     if(j % 1000 == 0):  
    119.         print "Error:" + str(overallError)  
    120.         print "Pred:" + str(d)  
    121.         print "True:" + str(c)  
    122.         out = 0  
    123.         for index,x in enumerate(reversed(d)):  
    124.             out += x*pow(2,index)  
    125.         print str(a_int) + " + " + str(b_int) + " = " + str(out)  
    126.         print "------------"  



    运行输出:

    Error:[ 3.45638663]
    Pred:[0 0 0 0 0 0 0 1]
    True:[0 1 0 0 0 1 0 1]
    9 + 60 = 1
    ------------
    Error:[ 3.63389116]
    Pred:[1 1 1 1 1 1 1 1]
    True:[0 0 1 1 1 1 1 1]
    28 + 35 = 255
    ------------
    Error:[ 3.91366595]
    Pred:[0 1 0 0 1 0 0 0]
    True:[1 0 1 0 0 0 0 0]
    116 + 44 = 72
    ------------
    Error:[ 3.72191702]
    Pred:[1 1 0 1 1 1 1 1]
    True:[0 1 0 0 1 1 0 1]
    4 + 73 = 223
    ------------
    Error:[ 3.5852713]
    Pred:[0 0 0 0 1 0 0 0]
    True:[0 1 0 1 0 0 1 0]
    71 + 11 = 8
    ------------
    Error:[ 2.53352328]
    Pred:[1 0 1 0 0 0 1 0]
    True:[1 1 0 0 0 0 1 0]
    81 + 113 = 162
    ------------
    Error:[ 0.57691441]
    Pred:[0 1 0 1 0 0 0 1]
    True:[0 1 0 1 0 0 0 1]
    81 + 0 = 81
    ------------
    Error:[ 1.42589952]
    Pred:[1 0 0 0 0 0 0 1]
    True:[1 0 0 0 0 0 0 1]
    4 + 125 = 129
    ------------
    Error:[ 0.47477457]
    Pred:[0 0 1 1 1 0 0 0]
    True:[0 0 1 1 1 0 0 0]
    39 + 17 = 56
    ------------
    Error:[ 0.21595037]
    Pred:[0 0 0 0 1 1 1 0]
    True:[0 0 0 0 1 1 1 0]
    11 + 3 = 14
    ------------

    第一部分:什么是神经元记忆?

    正向的背一边字母表……你能做到,对吧?

    倒着背一遍字母表……唔……也许有点难。


    那么试试你熟悉的一首歌词?……为什么正常顺序回忆的时候比倒着回忆更简单呢?你能直接跳跃到第二小节的歌词么?……唔唔……同样很难,是吧?


    其实这很符合逻辑……你并不像计算机那样把字母表或者歌词像存储在硬盘一样的记住,你是把它们作为一个序列去记忆的。你很擅长于一个单词一个单词的去回忆起它们,这是一种条件记忆。你只有在拥有了前边部分的记忆了以后,才能想起来后边的部分。如果你对链表比较熟悉的话,OK,我们的记忆就和链表是类似的。


    然而,这并不意味着当你不唱歌时,你的记忆中就没有这首歌。而是说,当你试图直接记忆起某个中间的部分,你需要花费一定的时间在你的脑海中寻找(也许是在一大堆神经元里寻找)。大脑开始在这首歌里到处寻找你想要的中间部分,但是大脑之前并没有这么做过,所以它并没有一个能够指向中间这部分的索引。这就像住在一个附近都是岔路/死胡同的地方,你从大路上到某人的房子很简单,因为你经常那样走。但是把你丢在一家人的后院里,你却怎么也找不到正确的道路了。可见你的大脑并不是用“方位”去寻找,而是通过一首歌的开头所在的神经元去寻找的。如果你想了解更多关于大脑的知识,可以访问:http://www.human-memory.net/processes_recall.html


    就像链表一样,记忆这样去存储是很有效的。这样可以通过脑神经网络很好的找到相似的属性、优势。一些过程、难题、表示、查询也可以通过这种短期/伪条件记忆序列存储的方式,使其更加的高效。


    去记忆一些数据是序列的事情(其实就是意味着你有些东西需要去记住!),假设有一个跳跳球,每个数据点就是你眼中跳跳球运动的一帧图像。如果你想训练一个神经网络去预测下一帧球会在哪里,那么知道上一帧球在哪里就会对你的预测很有帮助!这样的序列数据就是我们为什么要搭建一个递归神经网络。那么,一个神经网络怎么记住它之前的时间它看到了什么呢?


    神经网络有隐藏层,一般来讲,隐藏层的状态只跟输入数据有关。所以一般来说一个神经网络的信息流就会像下面所示的这样:

    input -> hidden ->output


    这很明显,确定的输入产生确定的隐藏层,确定的隐藏层产生确定的输出层。这是一种封闭系统。但是,记忆改变了这种模式!记忆意味着隐藏层是,当前时刻的输入与隐藏层前一时刻的一种组合。

    ( input + prev_hidden ) -> hidden -> output


    为什么是隐藏层呢?其实技术上来说我们可以这样:

    ( input + prev_input ) -> hidden -> output


    然而,我们遗漏了一些东西。我建议你认真想想这两个信息流的不同。给你点提示,演绎一下它们分别是怎么运作的。这里呢,我们给出4步的递归神经网络流程看看它怎么从之前的隐藏层得到信息。

    ( input + empty_hidden ) -> hidden -> output

    ( input + prev_hidden   ) -> hidden -> output

    ( input + prev_hidden   ) -> hidden -> output

    ( input + prev_hidden   ) -> hidden -> output


    然后,我们再给出4步,从输入层怎么得到信息。

    ( input + empty_input ) -> hidden -> output

    ( input + prev_input    ) -> hidden -> output

    ( input + prev_input    ) -> hidden -> output

    ( input + prev_input    ) -> hidden -> output


    或许,如果我把一些部分涂上颜色,一些东西就显而易见了。那么我们再看看这4步隐藏层的递归:

    input + empty_hidden ) ->hidden -> output

    input + prev_hidden   ) ->hidden -> output

    input + prev_hidden   ) ->hidden -> output

    input + prev_hidden   ) ->hidden -> output


    ……以及,4步输入层的递归:

    input + empty_input ) -> hidden -> output

    input + prev_input    ) -> hidden -> output

    input + prev_input    ) -> hidden -> output

    input + prev_input    ) -> hidden -> output


    看一下最后一个隐藏层(第四行)。在隐藏层递归中,我们可以看到所有见过的输入的存在。但是在输入层递归中,我们仅仅能发现上次与本次的输入。这就是为什么我们用隐藏层递归建模。隐藏层递归能学习它到底去记忆什么,但是输入层递归仅仅能记住上次的数据点。


    现在我们对比一下这两种方法,通过反向的字母表与歌词中间部分的练习。隐藏层根据越来越多的输入持续的改变,而且,我们到达这些隐藏状态的唯一方式就是沿着正确的输入序列。现在就到了很重要的一点,输出由隐藏层决定,而且只有通过正确的输入序列才能到达隐藏层。是不是很相似?


    那么有什么实质的区别呢?我们考虑一下我们要预测歌词中的下一个词,假如碰巧在不同的地方有两个相同的词,“输出层递归”就会使你回忆不起来下面的歌词到底是什么了。仔细想想,如果一首歌有一句“我爱你”,以及“我爱萝卜”,记忆网络现在试图去预测下一个词,那它怎么知道“我爱”后边到底是什么?可能是“你”,也可能是“萝卜”。所以记忆网络必须要知道更多的信息,去识别这到底是歌词中的那一段。而“隐藏层递归”不会让你忘记歌词,就是通过这个原理。它巧妙地记住了它看到的所有东西(记忆更巧妙地是它能随时间逐渐忘却)。想看看它是怎么运作的,猛戳这里:http://karpathy.github.io/2015/05/21/rnn-effectiveness/


    好的,现在停下来,然后确认你的脑袋是清醒的。


    第二部分:RNN - 神经网路记忆


    现在我们已经对这个问题有个直观的认识了,让我们下潜的更深一点(什么鬼,你在逗我?)。就像在反向传播这篇博文(http://blog.csdn.net/zzukun/article/details/49556715)里介绍的那样,输入数据决定了我们神经网络的输入层。每行输入数据都被用来产生隐含层(通过正向传播),然后用每个隐含层生成输出层(假设只有一层隐含层)。就像我们刚才看到的,记忆意味着隐含层是输入与上一次隐含层的组合。那么怎么组合呢?其实就像神经网络的其他传播方法,用一个矩阵就行了,这个矩阵定义了之前隐含层与当前的关系。


    从这张图中能看出来很多东西。这里只有三个权值矩阵,其中两个很相似(名字也一样)。SYNAPSE_0把输入数据传播到隐含层,SYNAPSE_1把隐含层数据传播到输出层。新的矩阵(SYNAPSE_h……要递归的),把隐含层(layer_1)传播到下一个时间点的隐含层(仍旧是layer_1)。


    好的,现在停下来,然后确认你的脑袋是清醒的。



    上边的GIF图展现出递归神经网络的奥秘,以及一些非常、非常重要的性质。图中描述了4个时间步数,第一个仅仅受到输入数据的影响,第二个把第二个输入与第一个的隐含层混合,如此继续。有人可能会注意到,在这种方式下,第四个网络“满了”。这样推测的话,第五步不得不选择一个某个节点去替代掉它。是的,这很正确。这就是记忆的“容量”概念。正如你所期望的,更多的隐含层节点能够存储更多的记忆,并使记忆保持更长的时间。同样这也是网络学习去忘记无关的记忆并且记住重要的记忆。你在能从第三步中看出点什么不?为什么有更多的绿色节点呢?


    另外需要注意的是,隐含层是输入与输出中间的一道栅栏。事实上,输出已经不再是对应于输入的一个函数。输入只是改变了记忆中存储的东西,而且输出仅仅依赖于记忆!告诉你另外一个有趣的事情,如果上图中的第2,3,4步没有输入,随着时间的流逝,隐含层仍然会改变。


    好的,好的,我知道你已经停下来了,不过一定要保证刚才的内容你已经差不多理解了。


    第三部分:基于时间的反向传播

    那么现在问题来了,递归神经网络怎么学习的呢?看下面的图片,黑色的是预测,误差是亮黄色,导数是芥末色的(暗黄色)。


    网络通过从1到4的全部传播(通过任意长度的整个序列),然后从4到1反向传播所有的导数值。你也可以认为这仅仅是正常神经网络的一个有意思的变形,除了我们在各自的地方复用了相同的权值(突触synapses 0,1,h)。其他的地方都是很普通的反向传播。


    第四部分:我们的玩具代码

    我们现在使用递归神经网络去建模二进制加法。你看到下面的序列了么?上边这俩在方框里的,有颜色的1是什么意思呢?


    框框中彩色的1表示“携带位”。当每个位置的和溢出时(需要进位),它们“携带这个‘1’”。我们就是要教神经网络学习去记住这个“携带位”。当“和”需要它,它需要去“携带这个‘1’”。


    二进制加法从右边到左边进行计算,我们试图通过上边的数字,去预测横线下边的数字。我们想让神经网络遍历这个二进制序列并且记住它携带这个1与没有携带这个1的时候,这样的话网络就能进行正确的预测了。不要迷恋于这个问题本身,因为神经网络事实上也不在乎。就当作我们有两个在每个时间步数上的输入(1或者0加到每个数字的开头),这两个输入将会传播到隐含层,隐含层会记住是否有携带位。预测值会考虑所有的信息,然后去预测每个位置(时间步数)正确的值。


    下面我推荐同时打开两个这个页面,这样就可以一边看代码,一边看下面的解释。我就是这么写这篇文章的。


    Lines 0-2:导入依赖包,设定随机数生成的种子。我们只需要两个依赖包,numpy和copy。numpy是为了矩阵计算,copy用来拷贝东西。


    Lines 4-11:我们的非线性函数与其导数,更多的细节可见参考我们之前的博客:http://blog.csdn.net/zzukun/article/details/49556715


    Line 15:这一行声明了一个查找表,这个表是一个实数与对应二进制表示的映射。二进制表示将会是我们网路的输入与输出,所以这个查找表将会帮助我们将实数转化为其二进制表示。


    Line 16:这里设置了二进制数的最大长度。如果一切都调试好了,你可以把它调整为一个非常大的数。


    Line 18:这里计算了跟二进制最大长度对应的可以表示的最大十进制数。


    Line 19:这里生成了十进制数转二进制数的查找表,并将其复制到int2binary里面。虽然说这一步不是必需的,但是这样的话理解起来会更方便。


    Line 26:这里设置了学习速率。


    Line 27:我们要把两个数加起来,所以我们一次要输入两位字符。如此以来,我们的网络就需要两个输入。


    Line 28:这是隐含层的大小,回来存储“携带位”。需要注意的是,它的大小比原理上所需的要大。自己尝试着调整一下这个值,然后看看它如何影响收敛速率。更高的隐含层维度会使训练变慢还是变快?更多或是更少的迭代次数?


    Line 29:我们只是预测和的值,也就是一个数。如此,我们只需一个输出。


    Line 33:这个权值矩阵连接了输入层与隐含层,如此它就有“imput_dim”行以及“hidden_dim”列(假如你不改参数的话就是2×16)。


    Line 34:这个权值矩阵连接了隐含层与输出层,如此它就有“hidden_dim”行以及“output_dim”列(假如你不改参数的话就是16×1)。


    Line 35:这个权值矩阵连接了前一时刻的隐含层与现在时刻的隐含层。它同样连接了当前时刻的隐含层与下一时刻的隐含层。如此以来,它就有隐含层维度大小(hidden_dim)的行与隐含层维度大小(hidden_dim)的列(假如你没有修改参数就是16×16)。


    Line 37-39:这里存储权值更新。在我们积累了一些权值更新以后,我们再去更新权值。这里先放一放,稍后我们再详细讨论。


    Line 42:我们迭代训练样例10000次。


    Line 45:这里我们要随机生成一个在范围内的加法问题。所以我们生成一个在0到最大值一半之间的整数。如果我们允许网络的表示超过这个范围,那么把两个数加起来就有可能溢出(比如一个很大的数导致我们的位数不能表示)。所以说,我们只把加法要加的两个数字设定在小于最大值的一半。


    Line 46:我们查找a_int对应的二进制表示,然后把它存进a里面。


    Line 48:原理同45行。


    Line 49:原理同46行。


    Line 52:我们计算加法的正确结果。


    Line 53:把正确结果转化为二进制表示。


    Line 56:初始化一个空的二进制数组,用来存储神经网络的预测值(便于我们最后输出)。你也可以不这样做,但是我觉得这样使事情变得更符合直觉。


    Line 58:重置误差值(这是我们使用的一种记录收敛的方式……可以参考之前关于反向传播与梯度下降的文章)


    Line 60-61:这两个list会每个时刻不断的记录layer 2的导数值与layer 1的值。


    Line 62:在0时刻是没有之前的隐含层的,所以我们初始化一个全为0的。


    Line 65:这个循环是遍历二进制数字。


    Line 68:X跟图片中的“layer_0”是一样的,X数组中的每个元素包含两个二进制数,其中一个来自a,一个来自b。它通过position变量从a,b中检索,从最右边往左检索。所以说,当position等于0时,就检索a最右边的一位和b最右边的一位。当position等于1时,就向左移一位。


    Line 69:跟68行检索的方式一样,但是把值替代成了正确的结果(0或者1)。


    Line 72:这里就是奥妙所在!一定一定一定要保证你理解这一行!!!为了建立隐含层,我们首先做了两件事。第一,我们从输入层传播到隐含层(np.dot(X,synapse_0))。然后,我们从之前的隐含层传播到现在的隐含层(np.dot(prev_layer_1.synapse_h))。在这里,layer_1_values[-1]就是取了最后一个存进去的隐含层,也就是之前的那个隐含层!然后我们把两个向量加起来!!!!然后再通过sigmoid函数。

    那么,我们怎么结合之前的隐含层信息与现在的输入呢?当每个都被变量矩阵传播过以后,我们把信息加起来。


    Line 75:这行看起来很眼熟吧?这跟之前的文章类似,它从隐含层传播到输出层,即输出一个预测值。


    Line 78:计算一下预测误差(预测值与真实值的差)。


    Line 79:这里我们把导数值存起来(上图中的芥末黄),即把每个时刻的导数值都保留着。


    Line 80:计算误差的绝对值,并把它们加起来,这样我们就得到一个误差的标量(用来衡量传播)。我们最后会得到所有二进制位的误差的总和。


    Line 86:将layer_1的值拷贝到另外一个数组里,这样我们就可以下一个时间使用这个值。


    Line 90:我们已经完成了所有的正向传播,并且已经计算了输出层的导数,并将其存入在一个列表里了。现在我们需要做的就是反向传播,从最后一个时间点开始,反向一直到第一个。


    Line 92:像之前那样,检索输入数据。


    Line 93:从列表中取出当前的隐含层。


    Line 94:从列表中取出前一个隐含层。


    Line 97:从列表中取出当前输出层的误差。


    Line 99:这一行计算了当前隐含层的误差。通过当前之后一个时间点的误差和当前输出层的误差计算。


    Line 102-104:我们已经有了反向传播中当前时刻的导数值,那么就可以生成权值更新的量了(但是还没真正的更新权值)。我们会在完成所有的反向传播以后再去真正的更新我们的权值矩阵,这是为什么呢?因为我们要用权值矩阵去做反向传播。如此以来,在完成所有反向传播以前,我们不能改变权值矩阵中的值。


    Line 109-115:现在我们就已经完成了反向传播,得到了权值要更新的量,所以就赶快更新权值吧(别忘了重置update变量)!


    Line 118-end:这里仅仅是一些输出日志,便于我们观察中间的计算过程与效果。


    第五步分:建议与评论

    如果您有什么疑问、意见与建议可以直接留言评论,或者给我email(likun@stu.zzu.edu.cn),或直接联系trask本人,感谢您的支持!


    展开全文
  • 使用 Matlab 和 GPU 的向量化长短期记忆 (LSTM)
  • Matlab LSTM

    千次阅读 2019-07-31 23:21:08
    数据格式 样本一列数据存入cell的一行 标签为一列 categorical类型 网络设置时分类为last,回归为sequence 代码 %数据参数 inputSize = 784; numClasses = 10;... lstmLayer(125,'OutputMode','seq...

    数据格式

    样本一列数据存入cell的一行
    标签为一列 categorical类型
    网络设置时分类为last,回归为sequence

    代码

    %数据参数
    inputSize = 784;
    numClasses = 10;
    
    %结构
    layers = [ ...
        sequenceInputLayer(inputSize)
        lstmLayer(125,'OutputMode','sequence')
        lstmLayer(125,'OutputMode','last')
        fullyConnectedLayer(numClasses)
        softmaxLayer()
        classificationLayer()
        ];
    
    %参数
    options = trainingOptions('adam', ...
        'MaxEpochs',8000, ...
        'MiniBatchSize',7000, ...
        'Verbose',true, ...
        'InitialLearnRate',0.02, ...
        'L2Regularization',0.0010, ...
        'LearnRateDropPeriod',4, ...
        'LearnRateDropFactor',0.04, ...
        'plots','training-progress');
    
    %训练
    net = trainNetwork(TrainData,TrainLabels,layers,options);
    
    %测试
    YPred = classify(net,TestData, ...
        'MiniBatchSize',7000, ...
        'SequenceLength','longest');
    
    %准确率
    acc = sum(YPred == TestLabels)./numel(TestLabels)
    
    展开全文
  • LSTM-matlab -master程序的理解

    千次阅读 2018-05-31 16:49:53
    I download this LSTM-matlab package from github, i will give the website later. 主要目的是:整理自己理解这个程序的整个流程,记录下来,方便自己回顾和分析,写给自己。 1. 待解决的问题: 问题一: -...

    I download this LSTM-matlab package from github, i will give the website later.

    主要目的是:整理自己理解这个程序的整个流程,记录下来,方便自己回顾和分析,写给自己。

    1. 待解决的问题:

    问题一:
    -该程序里,数据是如何产生的,是什么样的形式,比如维度
    -每次运行程序,数据是如何作为输入进入网络的,比如,一个一个,还是以batch为单位进入输入层的。
    问题二:
    -网络是怎么产生的,结构是什么,比如几层,隐藏层有多少节点。数据的流向
    问题三:
    -各个参数的意义,以及参数是如何迭代的

    2. 目前理解的程序的运行流程

    目前知道的要用到的程序:
    ——————————————————————————————————*

    2.1 aStart.m

    作用:设置result,speed,options,problem,sgd各个结构体的值,也是程序的一些基本参数,在最后一行调用Main(result,speed,options,problem,sgd)函数。
    ——————————————————————————————————*

    2.2 Main()的主要流程:
    2.2.1 根据cpu的种类,得到产生参数的基本的匿名函数

    [mones,mzeros,convert,usegpu]=gputype(speed.usegpu);

            mones = @(varargin)ones(varargin{:}, 'double');  % n行m列的一个单位矩阵
            mzeros = @(varargin)zeros(varargin{:}, 'double');   % n行m列的一个0矩阵
            convert = @(x)x;        
            useG=0;
    2.2.2 产生数据
    % genadding函数产生数据
    [data,mask,test,masktest ]=feval(['gen' problem.name],problem);
    % problem.name='adding';
    [data,mask,test,masktest]= genadding(problem)
    
    data=mzeros(problem.numsamples, 4 , ceil(problem.T*1.1));
    mask=mzeros(problem.numsamples, 1 , ceil(problem.T*1.1));
    test=mzeros(problem.numtest, 4 , ceil(problem.Ttest*1.1));
    masktest=mzeros(problem.numtest, 1 , ceil(problem.Ttest*1.1));
    problem.T = 8
    > data size: 20000 * 4 * 7
    > mask size: 20000 * 1 * 9
    > test size: 3000  * 4 * 8
    > masktest size:3000 * 1 * 9
    > 
    % 其中 data
    length = length = T+ fix(rand  * T / 10) = 7
    data(i,3,1:length)=ones(1, length);                  %bias
    data(i,1,1:length)=2*rand(1,length    ) - 1
    data(i,4,1:length)=ones(1,length);
    data(i,2,tmp1)=1;                                   % or -1
    % mask
    mask=mzeros(problem.numsamples, 1 , ceil(problem.T*1.1));     %(20000 * 1 * 9)
    mask(i,1,length)=1;                           % val(:,:,7) = 全部为1,其他,全部为0
    
    %test
    test(i,3,1:length)=ones(1, length);                %bias    
    test(i,1,1:length)=2*rand(1,length    ) - 1;
    test(i,4,1:length) = ones(1,length);               %bias
    test(i,2,tmp2) = 1;                             %  or -1
    finnal_test = [test(:,:,1) test(:,:,1:length)]  %  重点:这是与data的不同点,共8列,第一列重复原来的第一列,从dada的20000 * 4 * 7,变为test的(3000  * 4 * 8)
    % masktets
    masktest=mzeros(problem.numtest, 1 , ceil(problem.Ttest*1.1));  %  3000 *1 *9
    masktest(i,1,length)=1;                                    %  3000 *1 *9
    masktest=masktest(:,:,1:size(test,3));                     %  3000 *1 *8
    finnal_masktest = [masktest(:,:,1) masktest(:,:,1:length)]  %  3000 *1 *9

    想不太明白的地方:
    函数句柄convert = @(x)x;
    经过这个函数句柄处理前后的两个数组,完全一样。
    所以不太明白 已经生成 data、test这些数据集之后,再经过convert函数句柄处理之后的意义
    % 在genadding.m的函数中
    data=convert(data);
    mask=convert(mask);
    test=convert(test);
    masktest=convert(masktest);

    2.2.3 初始化网络 netInit()

    一共有几层网络,每层网络各有多少个节点呢 ???
    [in_size,gate_size,out_size,share_size,share_size2,numMmcell,W,in,ingate,cellstate,cells,outgate,…
    node_outgateInit,cellinInit,node_cellbiasInit,delta_outInit,cellstatusInit ]=netInit(problem);

    in_size                    = 2  % 后来变为in_size + bias1 = 3
    gate_size                  = 2  %  problem.gate_size
    out_size                   = 1
    
    % share_size2 is weights for a layer of lstm, 
    % lstm has 4 layer:      ingate, cell, outgate, out     ,      added up to psize
    % share_size include:    ingate, status, outgate, cellout 
    share_size                = 15  % in_size + gate_size * (2+2*problem.numMmcell) ; 
    share_size2               = 2*share_size            
    numMmcell                 = 2
    W
    in              (1,2,3)         size = 3
    ingate          (4,5)           size = 2  % gate_size * 1
    cellstate       (6,7,8,9)       size = 4  % gate_size * numMmcell
    cells           (10,11,12,13)   size = 4  % gate_size * numMmcell
    outgate         (14,15)         size = 2  % gate_size * 1
    % 一下5个矩阵,全部初始化为cell(1,8)的[](problem.T), 
    % 然后每个矩阵的每个,cell{i},分别进行如下操作
    node_outgateInit   % 0.5*mones(200,gate_size) 
    cellinInit         %  mzeros(200,numMmcell*gate_size)   % cells   (10,11,12,13) 
    node_cellbiasInit  %  mones(200,1);
    delta_outInit      % mzeros(200,out_size);
    cellstatusInit     % mzeros(200,numMmcell*gate_size);   % 对应cellstate   (6,7,8,9)
    
    bias1=1 ;          % other bias
    bias2=12 ;         % output bias
    ingatebias = -1;   % 难道是 输入 门 的bias

    此函数的功能:
    生成网络结果,对 各层网络的权重矩阵 进行初始化。包括两部分:
    1.确定各层网络权重矩阵的维度,m*n中m和n的值,
    2.确定各个矩阵的初始值

    到目前为止,还没有搞明白网络层,输入输出状态cell等这些层的具体是怎么回事、以及各个层的意义,输入输出。
    所以,我尽量把每个变量的值写出来,尽量根据变量值来把各个变量的意义分成一类,然后看能否整理出些内容

    W =   1e-4 *  (mod( fix( 2^50  *  rand(psize,1    ) ) , 2000 )    -1000);
    for i=1:psize /gate_size
        W( ceil(rand*psize) )=0;
    end
    % 随机把一般的权值赋值为0,一半(125/2)
    share_size   = in_size + gate_size * (2+2*problem.numMmcell) ;  % = 15 
    % share_size = in_size + gate_size + gate_size + gate_size*numMmcell + gate_size*numMmcell; 
    %              in_size + ingate    + outgate   + cellstate           + cells    
    Wingate    15*2   %  share_size ,gate_size
    Wcell      15*4   %  share_size, numMmcell*gate_size
    Woutgate   15*2   %  share_size , gate_size
    Wout       5*1    %  gate_size*numMmcell+1 ,  out_size
    2.2.4 开始优化,更新w和cost,

    [W, cost] = minFunc( Bpfunc,W,options);

    Bpfunc=@batch_cell_lstm;
    options.Method = 'lbfgs';
    [W, cost] = minFunc( Bpfunc,W,options);
    if toc(timer)>2
        timearray{1}( record  )= toc(timer);
        [errorarray{1}(record),~,~,rightarray{1}(record) ]=testmodel(W,test,masktest ,problem);
        disp([ 'lbfgs error '  num2str(errorarray{1}(record) )]  )
        record=record+1;
        timer=tic;
    end
    
    for repeat =1 :5
            for inner =1:50
                for i=1:fix(problem.numsamples/problem.batchsize) 
                    globalData =  bashdata{i};
                    globalMask= maskdata{i};
    
                    [~,dw,~,~]=Bpfunc(W);     
                    oldGradient = alpha*dw + momentum* oldGradient;
                    W= W - alpha* oldGradient;
                end
                timearray{2}(record  )= toc(timer);
                [errorarray{2}(record),~,~, rightarray{2}(record) ]=testmodel(W,test,masktest ,problem);
                record=record+1;
                timer=tic;
            end
            disp([ 'sgd error '  num2str(errorarray{2}(record-1) )]  )
        end

    关键问题:
    1. batch_cell_lstm
    2. minFunc 非线性优化的包
    3. testmodel

    % 分别是什么东西,怎求的
    errorarray{1}(record)  rightarray{1}(record) 
    errorarray{2}(record)  rightarray{2}(record) 
    展开全文
  • 本示例说明如何使用长短期记忆(LSTM)网络对序列数据进行分类。要训练深度神经网络对序列数据进行分类,可以使用LSTM网络。LSTM网络使您可以将序列数据输入网络,并根据序列数据的各个时间步进行预测。本示例使用日语...
  • MATLABLSTM预测

    2020-09-23 15:01:52
    matlab深度学习工具箱之LSTM, 采用历史序列进行预测, MATLAB应用实例 直接采用工具箱进行序列预测
  • 长短期记忆网络LSTM(matlab)

    千次阅读 2018-08-17 16:11:50
    load dataset stop = 0; %控制预测的序列索引(0-倒数第1 1-倒数第2 2-倒数第3,..以此类推) series = dataset(1:end-stop,1); %导入时间序列数据 timespan = 5; %时间跨度(即历史数据条数) %构造...
  • 111111matlabLSTM.zip

    2019-09-01 09:58:46
    matlabLSTM的实现 长短期记忆网络(LSTM,Long Short-Term Memory)是一种时间循环神经网络,是为了解决一般的RNN(循环神经网络)存在的长期依赖问题而专门设计出来的,所有的RNN都具有一种重复神经网络模块的链式...
  • LSTM简单例子(MATLAB code)

    万次阅读 多人点赞 2017-07-10 11:18:03
    简易的MATLAB版本LSTM 代码
  • 点击“这里”,可以下载MATLAB源码。 1.1 灵感   鲸鱼被认为是世界上最大的哺乳动物。一头成年鲸可以长达 30 米,重 180 吨。这种巨型哺乳动物有 7 种不同的主要物种,如虎鲸,小须鲸,鳁鲸,座头鲸,露脊鲸,...
  • LSTM_RNN_MATLAB.zip

    2020-04-20 16:54:44
    使用matlab实现LSTM、RNN神经网络结构,简单易学.代码详细,入门必看.重磅推荐!(仅供个人学习使用)
  • lstm代码matlab 场景-LSTM 此代码/实现可用于研究目的。 如果您在工作中使用此代码/数据,请引用以下论文: Huynh、Manh 和 Gita Alaghband。 “通过将场景 LSTM 与人体运动 LSTM 相结合来预测轨迹。” 视觉计算国际...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,251
精华内容 900
关键字:

lstmmatlab

matlab 订阅