100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 循环神经网络(RNN Recurrent Neural Networks)学习笔记:源码分析(一)

循环神经网络(RNN Recurrent Neural Networks)学习笔记:源码分析(一)

时间:2021-04-30 20:58:45

相关推荐

循环神经网络(RNN  Recurrent Neural Networks)学习笔记:源码分析(一)

前面帖子给出了RNN的基础理论,里面也提到了神牛Mikolov,这个帖子就基于此牛开源出的一个语言建模工具箱(RNN Language Modeling Tookit)进行代码走读,会加速理解RNN算法及利用RNN进行语言建模,代码在github上的链接在这里:/mspandit/rnnlm。btw:大致把github上几个RNN的代码看了一遍,感觉还是Mikolov这个最容易上手,很适合初学者入门使用,当然,我没有测试这个project的性能,因为我的精力主要是基于ML做语音增强相关的工作,暂时顾不上LM方面。

另外,对这个代码的分析也参考了这位同学的blog,/a635661820/article/details/44755847。

首先来一张大牛论文中抽象出的RNN网络结构图,这个图跟前面帖子中的图稍有不同,无非就是前面基础理论中的输出层只是y(t),而LM中神牛增加了一个class层c(t),存放所有单词的类别。

#defineMAX_STRING100#ifndefWEIGHTTYPE#defineWEIGHTTYPEdouble#endif//real用于rnn中神经元的激活值,误差值的数值精度typedefWEIGHTTYPEreal;// NN weights//direct_t表示最大熵模型中输入层到输出层权值的数值精度typedefWEIGHTTYPEdirect_t;// ME weights//rnn中神经元结构,两部分//ac表示激活值,er表示误差值,er用在网络学习时structneuron {realac;//actual value stored in neuron realer;//error value in neuron, used by learning algorithm};//神经突触(权值),这里是表示网络层与层之间参数权值的结构//其实就是浮点类型,只是包上了一层,这样更形象structsynapse {realweight;//weight of synapse};//这是一个word的结构定义structvocab_word {//cn表示这个word在train_file中出现的频数intcn;//这个表示word本身,是字符串,但长度不能超过MAX_STRING,可调charword[MAX_STRING];//这个应该是在概率分布时表示当前词在历史下的条件概率//但是后面的代码中我没看到怎么使用这个定义,感觉可以忽略 realprob;//这个表示当前词所属的类别,训练时要把所有的word进行分组归类(分组的依据暂不清楚)intclass_index;};//PRIMES[]这个数组存的都是质数,用来做散列函数constunsignedintPRIMES[]={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数组长度constunsignedintPRIMES_SIZE=sizeof(PRIMES)/sizeof(PRIMES[0]);//最大阶数,这个是用来限制最大熵模型的N元模型特征的,N不能无穷大,这里最大是20constintMAX_NGRAM_ORDER=20;//文件存储类型,TEXT表示ASCII存储,对存储网络权值时,有点浪费空间//BINARY表示二进制方式存储,对网络权值进行存储时,能更省空间,但是不便于阅读enumFileTypeEnum {TEXT,BINARY, COMPRESSED};//COMPRESSED not yet implementedclassCRnnLM{protected://训练数据集的文件名chartrain_file[MAX_STRING];//验证数据集的文件名charvalid_file[MAX_STRING];//测试数据集的文件名chartest_file[MAX_STRING];//RNN训练好后的模型所存储的文件charrnnlm_file[MAX_STRING];//其他语言模型对测试数据的生成文件,比如用SRILMcharlmprob_file[MAX_STRING];//随机种子,不同的rand_seed,可以导致网络权值初始化为不同的随机数intrand_seed;//debug_mode分为两个级别,debug_mode>0会输出一些基本信息//debug_mode>1会输出更详细的信息intdebug_mode;intversion;//用来指示存储模型参数时用TEXT, 还是用BINARYintfiletype;//控制开关,use_lmprob为0时表示不使用//为1时表示使用了其他语言模型,并会将RNN和其他语言模型插值intuse_lmprob;//上面所说的将RNN和其他语言模型插值使用的插值系数reallambda;//防止误差过大增长,用gradient_cutoff进行限制//gradient_cutoff的使用在矩阵相乘那个函数里面可以看到 realgradient_cutoff;//dynamic如果大于0表示在测试时,边测试边学习realdynamic;//学习率realalpha;//训练初始的学习率realstarting_alpha;//变量控制开关,为0表明不将alpha减半,具体见代码intalpha_divide;//logp表示累计对数概率,即logp = log10w1 + log10w2 + log10w3...//llogp是last logp,即上一个logpdoublelogp,llogp;//最小增长倍数floatmin_improvement;//iter表示整个训练文件的训练次数intiter;//vocab_max_size表示vocab最大容量,但是在代码中这个是动态增加的intvocab_max_size;//表示vocab的实际容量intvocab_size;//记录train_file有多少wordinttrain_words;//指示当前所训练的词在train_file中的位置(序号)inttrain_cur_pos;intcounter;//one_iter==1的话,只会训练一遍intone_iter;//对train_file最大的训练遍数intmaxIter;//表示每训练anti_k个word,会将网络信息保存到rnnlm_fileintanti_k;//L2正规化因子//实际在用的时候,是用的beta*alpharealbeta;//指定单词所要分类别intclass_size;//class_words[i-1][j-1]表示第i类别中的第j个词在vocab中的下标int**class_words;//class_cn[i-1]表示第i个类别中有多少wordint*class_cn;//class_max_cn[i-1]表示第i类别最多有多少wordint*class_max_cn;//old_classes大于0时用一种分类词的算法,否则用另一种intold_classes;//vocab里面存放的是不会重复的word,类型为vocab_wordstructvocab_word*vocab;//选择排序,将vocab[1]到vocab[vocab_size-1]按照他们出现的频数从大到小排序,排序算法应可以优化voidsortVocab();//里面存放word在vocab中的下标,这些下标是通过哈希函数映射来的int*vocab_hash;//vocab_hash的大小intvocab_hash_size;//输入层的大小intlayer0_size;//隐层的大小intlayer1_size;//压缩层的大小intlayerc_size;//输出层的大小intlayer2_size;//表示输出层到输出层直接连接的权值数组的大小longlongdirect_size;//最大熵模型所用特征的阶数intdirect_order;//history从下标0开始存放的是wt, wt-1,wt-2...inthistory[MAX_NGRAM_ORDER];//bptt<=1的话,就是常规的bptt,即只从st展开到st-1intbptt;//每训练bptt_block个单词时,才会使用BPTT(或设置indenpendt不等于0,在句子结束时也可以进行BPTT)intbptt_block;//bptt_history从下标0开始存放的是wt,wt-1,wt-2...int*bptt_history;//bptt_hidden从下标0开始存放的是st,st-1,st-2...neuron *bptt_hidden;//隐层到输入层的权值,这个使用在BPTT时的structsynapse*bptt_syn0;intgen;//independent非0,即表示要求每个句子独立训练//如果independent==0,表面上一个句子对下一个句子的训练时算作历史信息的//这控制还得看句子与句子之间的相关性如何了intindependent;structneuron *neu0;//neurons in input layerstructneuron*neu1;//neurons in hidden layerstructneuron*neuc;//neurons in copressed layerstructneuron*neu2;//neurons in output layerstructsynapse*syn0;//weights between input and hidden layerstructsynapse *syn1;//weights between hidden and output layer (or hidden and compression if compression>0)structsynapse *sync;//weights between output and compression layerdirect_t *syn_d;//direct parameters between input and output layer (similar to Maximum Entropy model parameters)//backup used in training:structneuron*neu0b;structneuron *neu1b;structneuron *neucb;structneuron *neu2b;structsynapse *syn0b;structsynapse *syn1b;structsynapse *syncb;direct_t *syn_db;//backup used in n-bset rescoring:structneuron*neu1b2;public:intalpha_set,train_file_set;CRnnLM()//constructor initializes variables {version=10;filetype=TEXT;use_lmprob=0;lambda=0.75;gradient_cutoff=15;dynamic=0;train_file[0]=0;valid_file[0]=0;test_file[0]=0;rnnlm_file[0]=0;alpha_set=0;train_file_set=0;alpha=0.1;beta=0.0000001;//beta=0.00000;alpha_divide=0;logp=0;llogp=-100000000;iter=0;min_improvement=1.003;train_words=0;train_cur_pos=0;vocab_max_size=100;vocab_size=0;vocab=(structvocab_word *)calloc(vocab_max_size,sizeof(structvocab_word));layer1_size=30;direct_size=0;direct_order=0;bptt=0;bptt_block=10;bptt_history=NULL;bptt_hidden=NULL;bptt_syn0=NULL;gen=0;independent=0;neu0=NULL;neu1=NULL;neuc=NULL;neu2=NULL;syn0=NULL;syn1=NULL;sync=NULL;syn_d=NULL;syn_db=NULL;//backupneu0b=NULL;neu1b=NULL;neucb=NULL;neu2b=NULL;neu1b2=NULL;syn0b=NULL;syn1b=NULL;syncb=NULL;//rand_seed=1;class_size=100;old_classes=0;one_iter=0;maxIter=0;debug_mode=1;srand(rand_seed);//word映射为哈希的值小于100000000vocab_hash_size=100000000;//动态分配内存,calloc会自动将申请的内存初始化为0,但这里奇怪申请这么大空间,这里没对vocab_hash做检查vocab_hash=(int*)calloc(vocab_hash_size,sizeof(int));}~CRnnLM()//destructor, deallocates memory{//析构,省略}//返回值类型为real且范围在[min, max]的数realrandom(real min,real max);voidsetTrainFile(char*str);voidsetValidFile(char*str);voidsetTestFile(char*str);//设置模型保存文件,即该文件用来存储模型的信息,以及各类参数voidsetRnnLMFile(char*str);voidsetLMProbFile(char*str){strcpy(lmprob_file,str);}voidsetFileType(intnewt) {filetype=newt;}voidsetClassSize(intnewSize){class_size=newSize;}voidsetOldClasses(intnewVal) {old_classes=newVal;}voidsetLambda(realnewLambda){lambda=newLambda;}voidsetGradientCutoff(realnewGradient){gradient_cutoff=newGradient;}voidsetDynamic(realnewD) {dynamic=newD;}voidsetGen(realnewGen) {gen=newGen;}voidsetIndependent(intnewVal) {independent=newVal;}voidsetLearningRate(realnewAlpha){alpha=newAlpha;}voidsetRegularization(realnewBeta){beta=newBeta;}voidsetMinImprovement(realnewMinImprovement){min_improvement=newMinImprovement;}voidsetHiddenLayerSize(intnewsize){layer1_size=newsize;}voidsetCompressionLayerSize(intnewsize){layerc_size=newsize;}voidsetDirectSize(longlongnewsize) {direct_size=newsize;}voidsetDirectOrder(intnewsize){direct_order=newsize;}voidsetBPTT(intnewval) {bptt=newval;}voidsetBPTTBlock(intnewval) {bptt_block=newval;}voidsetRandSeed(intnewSeed){rand_seed=newSeed;srand(rand_seed);}voidsetDebugMode(intnewDebug){debug_mode=newDebug;}voidsetAntiKasparek(intnewAnti){anti_k=newAnti;}voidsetOneIter(intnewOneIter){one_iter=newOneIter;}voidsetMaxIter(intnewMaxIter){maxIter=newMaxIter;}//返回单词的哈希值intgetWordHash(char*word);//从文件中读取一个单词到wordvoidreadWord(char*word,FILE *fin);//查找word,找到返回word在vocab中的索引,没找到返回-1intsearchVocab(char*word);//读取当前文件指针所指的单词,并返回该单词在vocab中的索引intreadWordIndex(FILE*fin);//将word添加到vocab中,并且返回刚添加word在vocab中的索引intaddWordToVocab(char*word);//从train_file中读数据,相关数据会装入vocab,vocab_hash//这里假设vocab是空的voidlearnVocabFromTrainFile();//train_file will be used to construct vocabulary//保存当前的权值,以及神经元信息值voidsaveWeights();//saves current weights and unit activations//上面是暂存当前权值及神经元值,这里是从前面存下的数据中恢复voidrestoreWeights();//restores current weights and unit activations from backup copy//void saveWeights2(); //allows 2. copy to be stored, useful for dynamic rescoring of nbest lists//void restoreWeights2();//保存隐层神经元的ac值voidsaveContext();//恢复隐层神经元的ac值voidrestoreContext();//保存隐层神经元的ac值voidsaveContext2();//恢复隐层神经元的ac值voidrestoreContext2();//初始化网络voidinitNet();//保存网络的所有信息到rnnlm_filevoidsaveNet();//从文件流中读取一个字符使其ascii等于delim//随后文件指针指向delim的下一个voidgoToDelimiter(intdelim,FILE *fi);//从rnnlm_file中读取网络的所有信息voidrestoreNet();//清除神经元的ac,er值voidnetFlush();//隐层神经元(论文中的状态层s(t))的ac值置1//s(t-1),即输入层layer1_size那部分的ac值置1//bptt+history清0voidnetReset();//will erase just hidden layer state + bptt history + maxent history (called at end of sentences in the independent mode)//网络前向,计算概率分布voidcomputeNet(intlast_word,intword);//反传误差,更新网络权值voidlearnNet(intlast_word,intword);//将隐层神经元的ac值复制到输入层后layer1_size那部分voidcopyHiddenLayerToInput();//训练网络voidtrainNet();voiduseLMProb(intuse) {use_lmprob=use;}//测试网络voidtestNet();voidtestNbest();voidtestGen();//矩阵和向量相乘//1.type == 0时,计算的是神经元ac值,相当于计算srcmatrix × srcvec, 其中srcmatrix是(to-from)×(to2-from2)的矩阵//srcvec是(to2-from2)×1的列向量,得到的结果是(to-from)×1的列向量,该列向量的值存入dest中的ac值//2.type == 1, 计算神经元的er值,即(srcmatrix)^T × srcvec,T表示转置,转置后是(to2-from2)×(to-from),srcvec是(to-from)×1的列向量voidmatrixXvector(structneuron *dest,structneuron *srcvec,structsynapse*srcmatrix,intmatrix_width,intfrom,intto,intfrom2,intto2,inttype);};

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。