码迷,mamicode.com
首页 > Web开发 > 详细

Recurrent neural network language modeling toolkit 源码深入剖析系列(一)

时间:2015-03-31 09:16:51      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:rnnlm

系列前言
参考文献:
  1. RNNLM - Recurrent Neural Network  Language Modeling Toolkit(点此阅读)
  2. Recurrent neural network based language model(点此阅读)
  3. EXTENSIONS OF RECURRENT NEURAL NETWORK LANGUAGE MODEL(点此阅读)
  4. Strategies for Training Large Scale Neural Network  Language Models(点此阅读)
  5. STATISTICAL LANGUAGE MODELS BASED ON NEURAL  NETWORKS(点此阅读)
  6. A guide to recurrent neural networks and backpropagation(点此阅读)
  7. A Neural Probabilistic Language Model(点此阅读)
  8. Learning Long-Term Dependencies with Gradient Descent is Difficult(点此阅读)
  9. Can Artificial Neural Networks Learn Language Models?(点此阅读)

最近学习完系列神经网络语言模型后,最好的更为深刻的理解方式就是阅读源码,看一看究竟怎么实现的,认真的分析过源码,并且画出内部的数据结构图之后,才觉的了解的更透彻,甚至感觉自己能换一种语言重写这个开源工具。而且从阅读源码的过程中学到了很多实现技巧。当然自己的理解很多地方是比较狭隘的,可能有许多错误,而且某些地方是感觉那么回事,也没太仔细的思考,可能很多就没想清楚。而有些地方只有几行代码,却无奈的坐在那里想了一个下午╮(╯▽╰)╭,连吃饭、走路的时候也在想,我想这是快走火入魔了吧,哈哈。另外不知道对不对的起标题中的"深入"两个字啊,反正看上去高端点,就先这么标着吧。由于我在代码中注释很多,几乎是每行一注释,很多想仔细说的也在源码注释里面,所以内容主要以代码注释为主,外加对源码内部数据结构的图解。并且原谅我喜欢把参考内容放到最上面,养成习惯了,上面的8篇文章一开始读的话最好读第5篇,其他的内容都会在第5篇中大概的提到。另外为了懒得跑到另一篇文章去看图解,所以图解可能会反复出现在每篇文章中。

前言完毕了,第一篇本来是想直接把rnnlmlib.h直接放上来,然后介绍整个网络轮廓,大概看一下相关接口函数功能,但是那样内容发现有点长了,看上去都容易打瞌睡。于是把rnnlmlib.h分成两部分,第一篇文章介绍其中的成员变量,因为理解这些成语变量才能清楚的知道rnnlm toolkit命令参数的具体含义;第二篇介绍其中的成员函数,不涉及具体实现(那是后续文章的事儿),只是大概知道函数什么功能即可,估计第二篇内容稍短。

我先把整个网络的图放上来,然后可以对应着看,下面的图是对应源代码所抽象出来的,我会把实际论文的模型图放上来做参照对比。
技术分享


下面的图是论文中的图,可以很明显的注意到实现和模型图还是有点差别,实现的时候把w(t)和s(t-1)、y(t)和c(t)合并成一个层了.
技术分享
技术分享

这里的图的输出层是经过分解的,为了加速。我后面的文章会介绍到和未分解的图和分解的计算。第一篇的目的就在于大概的了解真个网络的结构,宏观的看一看。下面是rnnlmlib.h文件的内容,虽然直接把成员变量切出来让类不完整,但没办法,成员函数在第二篇中介绍。
rnnlmlib.h内容如下:

///////////////////////////////////////////////////////////////////////  
//  
// Recurrent neural network based statistical language modeling toolkit  
// Version 0.4a  
// (c) 2010-2012 Tomas Mikolov (tmikolov@gmail.com)  
// (c) 2013 Cantab Research Ltd (info@cantabResearch.com)  
//  
///////////////////////////////////////////////////////////////////////  
  
  
//这里的作用是防止rnnlmlib.h重复被include  
//如果程序第一次包含rnnlmlib.h,将会把#ifndef到文件最后一行的#endif之间的内容都执行  
//如果程序不是第一次包含rnnlmlib.h,则该文件的内容会被跳过  
#ifndef _RNNLMLIB_H_          
#define _RNNLMLIB_H_          
  
//最大字符串的长度                        
#define MAX_STRING 100  
  
//防止WEIGHTTYPE被重复定义  
#ifndef WEIGHTTYPE        
//权重类型,这里可以手动更改为float     
#define WEIGHTTYPE double  
#endif  
  
//real用于rnn中神经元的激活值,误差值类型  
typedef WEIGHTTYPE real;    // NN weights  
  
  
//direct_t表示最大熵模型中输入层到输出层权值类型  
typedef WEIGHTTYPE direct_t;    // ME weights  
  
  
//rnn中神经元结构,两部分  
//ac表示激活值,er表示误差值,er用在网络学习时  
struct neuron {  
    real ac;        //actual value stored in neuron  
    real er;        //error value in neuron, used by learning algorithm  
};  
  
  
//突触,这里是表示网络层与层之间参数权值的结构  
//其实就是浮点类型,只是包上了一层,这样更形象                  
struct synapse {  
    real weight;    //weight of synapse  
};  
  
  
//这是一个word的结构定义  
struct vocab_word {  
      
    //cn表示这个word在train_file中出现的频数  
    int cn;  
      
    //这个表示word本身,是字符串,但长度不能超过100  
    char word[MAX_STRING];  
      
    //这个应该是在概率分布时表示当前词在历史下的条件概率  
    //但是后面的代码中我没看到怎么使用这个定义,感觉可以忽略  
    real prob;  
      
    //这个表示当前词所在哪个类别  
    int class_index;  
};  
  
  
  
//PRIMES[]这个数组装都是质数,质数的用处是来做散列函数的  
//对散列函数了解不多,个人理解可以使散列函数更少的冲突吧  
const unsigned int PRIMES[]={108641969, 116049371, 125925907, 133333309, 145678979, 175308587, 197530793, 234567803, 251851741, 264197411, 330864029, 399999781,  
407407183, 459258997, 479012069, 545678687, 560493491, 607407037, 629629243, 656789717, 716048933, 718518067, 725925469, 733332871, 753085943, 755555077,  
782715551, 790122953, 812345159, 814814293, 893826581, 923456189, 940740127, 953085797, 985184539, 990122807};  
  
//PRIMES数组长度,这个用法可以积累一下,以后自己的程序也可以使用  
const unsigned int PRIMES_SIZE=sizeof(PRIMES)/sizeof(PRIMES[0]);  
  
  
//最大阶数,这个是用来限制最大熵模型的N元模型特征的,N不能无穷大,这里最大是20  
const int MAX_NGRAM_ORDER=20;  
  
  
//文件存储类型,TEXT表示ASCII存储,对存储网络权值时,有点浪费空间  
//BINARY表示二进制方式存储,对网络权值进行存储时,能更省空间,但是不便于阅读  
enum FileTypeEnum {TEXT, BINARY, COMPRESSED};       //COMPRESSED not yet implemented  
  
  
//这个类就是RNN的结构定义  
class CRnnLM{  
protected:  
      
    ////训练数据集的文件名  
    char train_file[MAX_STRING];          
      
    //验证数据集的文件名  
    char valid_file[MAX_STRING];                  
      
    //测试数据集的文件名  
    char test_file[MAX_STRING];           
      
    //RNN训练好后的模型所存储的文件  
    char rnnlm_file[MAX_STRING];  
      
    //其他语言模型对测试数据的生成文件,比如用SRILM  
    char lmprob_file[MAX_STRING];  
      
      
    //随机种子,不同的rand_seed,可以导致网络权值初始化为不同的随机数  
    int rand_seed;  
      
    //debug_mode分为两个级别,debug_mode>0会输出一些基本信息  
    //debug_mode>1会输出更详细的信息  
    int debug_mode;  
      
    //rnn toolkit的版本号  
    int version;  
      
    //用来指示存储模型参数时用TEXT, 还是用BINARY  
    int filetype;  
      
    //控制开关,use_lmprob为0时表示不使用  
    //为1时表示使用了其他语言模型,并会将RNN和其他语言模型插值  
    int use_lmprob;  
      
    //上面所说的插值系数  
    real lambda;  
      
    //防止误差过大增长,用gradient_cutoff进行限制  
    //gradient_cutoff的使用在矩阵相乘那个函数里面可以看到  
    real gradient_cutoff;  
      
    //dynamic如果大于0表示在测试时,边测试边学习   
    real dynamic;  
      
    //学习率  
    real alpha;  
      
    //训练初始的学习率  
    real starting_alpha;  
      
    //变量控制开关,为0表明不将alpha减半,具体见代码  
    int alpha_divide;  
      
    //logp表示累计对数概率,即logp = log10w1 + log10w2 + log10w3...  
    //llogp是last logp,即上一个logp  
    double logp, llogp;   
      
    //最小增长倍数  
    float min_improvement;  
      
    //iter表示整个训练文件的训练次数  
    int iter;         
      
    //vocab_max_size表示vocab最大容量,但是在代码中这个是动态增加的  
    int vocab_max_size;  
      
    //表示vocab的实际容量  
    int vocab_size;  
      
    //记录train_file有多少word  
    int train_words;          
      
    //指示当前所训练的词在train_file是第几个  
    int train_cur_pos;  
    int counter;  
      
    //one_iter==1的话,只会训练一遍  
    int one_iter;  
      
    //对train_file最大的训练遍数  
    int maxIter;  
      
    //表示每训练anti_k个word,会将网络信息保存到rnnlm_file  
    int anti_k;       
      
      
    //L2正规化因子  
    //实际在用的时候,是用的beta*alpha  
    real beta;  
      
    //指定单词所要分类别  
    int class_size;  
      
    //class_words[i-1][j-1]表示第i类别中的第j个词在vocab中的下标  
    int **class_words;  
      
    //class_cn[i-1]表示第i个类别中有多少word  
    int *class_cn;  
      
    //class_max_cn[i-1]表示第i类别最多有多少word  
    int *class_max_cn;  
      
    //old_classes大于0时用一种分类词的算法,否则用另一种  
    int old_classes;  
      
      
    //vocab里面存放的是不会重复的word,类型为vocab_word  
    struct vocab_word *vocab;  
      
    //选择排序,将vocab[1]到vocab[vocab_size-1]按照他们出现的频数从大到小排序  
    void sortVocab();  
      
    //里面存放word在vocab中的下标,这些下标是通过哈希函数映射来的  
    int *vocab_hash;  
      
    //vocab_hash的大小  
    int vocab_hash_size;  
      
    //输入层的大小  
    int layer0_size;  
      
    //隐层的大小  
    int layer1_size;  
      
    //压缩层的大小  
    int layerc_size;  
      
    //输出层的大小  
    int layer2_size;  
      
    //表示输出层到输出层直接连接的权值数组的大小  
    long long direct_size;  
      
    //最大熵模型所用特征的阶数  
    int direct_order;  
      
    //history从下标0开始存放的是wt, wt-1,wt-2...  
    int history[MAX_NGRAM_ORDER];         
      
    //bptt<=1的话,就是常规的bptt,即只从st展开到st-1  
    int bptt;  
      
    //每训练bptt_block个单词时,才会使用BPTT(或设置indenpendt不等于0,在句子结束时也可以进行BPTT)  
    int bptt_block;  
      
    //bptt_history从下标0开始存放的是wt,wt-1,wt-2...  
    int *bptt_history;        
      
    //bptt_hidden从下标0开始存放的是st,st-1,st-2...  
    neuron *bptt_hidden;      
      
    //隐层到输入层的权值,这个使用在BPTT时的  
    struct synapse *bptt_syn0;  
      
    int gen;  
      
    //independent非0,即表示要求每个句子独立训练  
    //如果independent==0,表面上一个句子对下一个句子的训练时算作历史信息的  
    //这控制还得看句子与句子之间的相关性如何了  
    int independent;  
      
    //下面就只用用源码中的英文注释了,懒得敲了,感觉英文注释的很清楚  
    //neurons in input layer  
    struct neuron *neu0;          
      
    //neurons in hidden layer  
    struct neuron *neu1;          
      
    //neurons in hidden layer  
    struct neuron *neuc;          
      
    //neurons in output layer  
    struct neuron *neu2;          
      
    //weights between input and hidden layer  
    struct synapse *syn0;         
      
    //weights between hidden and output layer (or hidden and compression if compression>0)  
    struct synapse *syn1;         
      
    //weights between hidden and compression layer  
    struct synapse *sync;         
      
    //direct parameters between input and output layer (similar to Maximum Entropy model parameters)  
    direct_t *syn_d;          
      
    //backup used in training:  
    struct neuron *neu0b;  
    struct neuron *neu1b;  
    struct neuron *neucb;  
    struct neuron *neu2b;  
      
    struct synapse *syn0b;  
    struct synapse *syn1b;  
    struct synapse *syncb;  
    direct_t *syn_db;  
      
    //backup used in n-bset rescoring:  
    struct neuron *neu1b2;              
    public:            
        int alpha_set, train_file_set;  

};  
  
#endif  




Recurrent neural network language modeling toolkit 源码深入剖析系列(一)

标签:rnnlm

原文地址:http://blog.csdn.net/a635661820/article/details/44755847

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!