系列前言
参考文献:
- RNNLM - Recurrent Neural Network Language Modeling Toolkit(点此阅读)
- Recurrent neural network based language model(点此阅读)
- EXTENSIONS OF RECURRENT NEURAL NETWORK LANGUAGE MODEL(点此阅读)
- Strategies for Training Large Scale Neural Network Language
Models(点此阅读)
- STATISTICAL LANGUAGE MODELS BASED ON NEURAL NETWORKS(点此阅读)
- A guide to recurrent neural networks and backpropagation(点此阅读)
- A Neural Probabilistic Language Model(点此阅读)
- Learning Long-Term Dependencies with Gradient Descent is Difficult(点此阅读)
- 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 源码深入剖析系列(一)
原文:http://blog.csdn.net/a635661820/article/details/44755847