系列前言
参考文献:
- 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篇中大概的提到。另外为了懒得跑到另一篇文章去看图解,所以图解可能会反复出现在每篇文章中。
前言完了,上一篇是介绍成员变量的大概含义,这一篇是介绍一些成员函数的功能,仍然是大体了解一下个别函数的功能,在第三篇才正式写实现分析。这一篇稍短,里面大部分是代码,因为很多涉及变量相关的在上一篇注释过了,所以这篇就没注释那么详细。如下:
//构造函数,这里进行很多变量的初始化
CRnnLM() //constructor initializes variables
{
//这里的很多变量在前面都前一篇都有详细的介绍
//这里的初始值只要初始是为非0的可以留意一下
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=(struct vocab_word *)calloc(vocab_max_size, sizeof(struct vocab_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;
//backup
neu0b=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映射为哈希的值小于100000000
vocab_hash_size=100000000;
//动态分配内存,calloc会自动将申请的内存初始化为0,但这里奇怪申请这么大空间,这里没对vocab_hash做检查
vocab_hash=(int *)calloc(vocab_hash_size, sizeof(int));
}
//析构函数,释放申请的空间
~CRnnLM() //destructor, deallocates memory
{
int i;
if (neu0!=NULL) {
free(neu0);
free(neu1);
if (neuc!=NULL) free(neuc);
free(neu2);
free(syn0);
free(syn1);
if (sync!=NULL) free(sync);
if (syn_d!=NULL) free(syn_d);
if (syn_db!=NULL) free(syn_db);
//
free(neu0b);
free(neu1b);
if (neucb!=NULL) free(neucb);
free(neu2b);
free(neu1b2);
free(syn0b);
free(syn1b);
if (syncb!=NULL) free(syncb);
//
for (i=0; i<class_size; i++) free(class_words[i]);
free(class_max_cn);
free(class_cn);
free(class_words);
free(vocab);
free(vocab_hash);
if (bptt_history!=NULL) free(bptt_history);
if (bptt_hidden!=NULL) free(bptt_hidden);
if (bptt_syn0!=NULL) free(bptt_syn0);
//todo: free bptt variables too
}
}
//返回值类型为real且范围在[min, max]的数
real random(real min, real max);
//设置训练数据的文件名
void setTrainFile(char *str);
//设置验证数据集的文件名
void setValidFile(char *str);
//设置测试集的文件名
void setTestFile(char *str);
//设置模型保存文件,即该文件用来存储模型的信息,以及各类参数
void setRnnLMFile(char *str);
//下面的函数没写注释,{}里面有语句,变量的含义可以看前篇
//就不再一一注释了,这些参数都是从main主函数里面传来的,具体来说
//这些参数就是来自shell命令里面的,这里可以看看我的一篇rnnlm toolkit命令参数介绍
void setLMProbFile(char *str) {strcpy(lmprob_file, str);}
void setFileType(int newt) {filetype=newt;}
void setClassSize(int newSize) {class_size=newSize;}
void setOldClasses(int newVal) {old_classes=newVal;}
void setLambda(real newLambda) {lambda=newLambda;}
void setGradientCutoff(real newGradient) {gradient_cutoff=newGradient;}
void setDynamic(real newD) {dynamic=newD;}
void setGen(real newGen) {gen=newGen;}
void setIndependent(int newVal) {independent=newVal;}
void setLearningRate(real newAlpha) {alpha=newAlpha;}
void setRegularization(real newBeta) {beta=newBeta;}
void setMinImprovement(real newMinImprovement) {min_improvement=newMinImprovement;}
void setHiddenLayerSize(int newsize) {layer1_size=newsize;}
void setCompressionLayerSize(int newsize) {layerc_size=newsize;}
void setDirectSize(long long newsize) {direct_size=newsize;}
void setDirectOrder(int newsize) {direct_order=newsize;}
void setBPTT(int newval) {bptt=newval;}
void setBPTTBlock(int newval) {bptt_block=newval;}
void setRandSeed(int newSeed) {rand_seed=newSeed; srand(rand_seed);}
void setDebugMode(int newDebug) {debug_mode=newDebug;}
void setAntiKasparek(int newAnti) {anti_k=newAnti;}
void setOneIter(int newOneIter) {one_iter=newOneIter;}
void setMaxIter(int newMaxIter) {maxIter=newMaxIter;}
//返回单词的哈希值
int getWordHash(char *word);
//从文件中读取一个单词到word
void readWord(char *word, FILE *fin);
//查找word,找到返回word在vocab中的索引,没找到返回-1
int searchVocab(char *word);
//读取当前文件指针所指的单词,并返回该单词在vocab中的索引
int readWordIndex(FILE *fin);
//将word添加到vocab中,并且返回刚添加word在vocab中的索引
int addWordToVocab(char *word);
//从train_file中读数据,相关数据会装入vocab,vocab_hash
//这里假设vocab是空的
void learnVocabFromTrainFile(); //train_file will be used to construct vocabulary
//保存当前的权值,以及神经元信息值
void saveWeights(); //saves current weights and unit activations
//上面是暂存当前权值及神经元值,这里是从前面存下的数据中恢复
void restoreWeights(); //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值
void saveContext();
//恢复隐层神经元的ac值
void restoreContext();
//保存隐层神经元的ac值
void saveContext2();
//恢复隐层神经元的ac值
void restoreContext2();
//初始化网络
void initNet();
//保存网络的所有信息到rnnlm_file
void saveNet();
//从文件流中读取一个字符使其ascii等于delim
//随后文件指针指向delim的下一个
void goToDelimiter(int delim, FILE *fi);
//从rnnlm_file中读取网络的所有信息
void restoreNet();
//清除神经元的ac,er值
void netFlush();
//隐层神经元(论文中的状态层s(t))的ac值置1
//s(t-1),即输入层layer1_size那部分的ac值置1
//bptt+history清0
void netReset(); //will erase just hidden layer state + bptt history + maxent history (called at end of sentences in the independent mode)
//网络前向,计算概率分布
void computeNet(int last_word, int word);
//反传误差,更新网络权值
void learnNet(int last_word, int word);
////将隐层神经元的ac值复制到输出层后layer1_size那部分
void copyHiddenLayerToInput();
//训练网络
void trainNet();
void useLMProb(int use) {use_lmprob=use;}
//测试网络
void testNet();
//这两个我还没看~
void testNbest();
void testGen();
//矩阵和向量相乘
//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的列向量
void matrixXvector(struct neuron *dest, struct neuron *srcvec, struct synapse *srcmatrix, int matrix_width, int from, int to, int from2, int to2, int type);
//构造函数,这里进行很多变量的初始化
CRnnLM() //constructor initializes variables
{
//这里的很多变量在前面都前一篇都有详细的介绍
//这里的初始值只要初始是为非0的可以留意一下
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=(struct vocab_word *)calloc(vocab_max_size, sizeof(struct vocab_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;
//backup
neu0b=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映射为哈希的值小于100000000
vocab_hash_size=100000000;
//动态分配内存,calloc会自动将申请的内存初始化为0,但这里奇怪申请这么大空间,这里没对vocab_hash做检查
vocab_hash=(int *)calloc(vocab_hash_size, sizeof(int));
}
//析构函数,释放申请的空间
~CRnnLM() //destructor, deallocates memory
{
int i;
if (neu0!=NULL) {
free(neu0);
free(neu1);
if (neuc!=NULL) free(neuc);
free(neu2);
free(syn0);
free(syn1);
if (sync!=NULL) free(sync);
if (syn_d!=NULL) free(syn_d);
if (syn_db!=NULL) free(syn_db);
//
free(neu0b);
free(neu1b);
if (neucb!=NULL) free(neucb);
free(neu2b);
free(neu1b2);
free(syn0b);
free(syn1b);
if (syncb!=NULL) free(syncb);
//
for (i=0; i<class_size; i++) free(class_words[i]);
free(class_max_cn);
free(class_cn);
free(class_words);
free(vocab);
free(vocab_hash);
if (bptt_history!=NULL) free(bptt_history);
if (bptt_hidden!=NULL) free(bptt_hidden);
if (bptt_syn0!=NULL) free(bptt_syn0);
//todo: free bptt variables too
}
}
//返回值类型为real且范围在[min, max]的数
real random(real min, real max);
//设置训练数据的文件名
void setTrainFile(char *str);
//设置验证数据集的文件名
void setValidFile(char *str);
//设置测试集的文件名
void setTestFile(char *str);
//设置模型保存文件,即该文件用来存储模型的信息,以及各类参数
void setRnnLMFile(char *str);
//下面的函数没写注释,{}里面有语句,变量的含义可以看前篇
//就不再一一注释了,这些参数都是从main主函数里面传来的,具体来说
//这些参数就是来自shell命令里面的,这里可以看看我的一篇rnnlm toolkit命令参数介绍
void setLMProbFile(char *str) {strcpy(lmprob_file, str);}
void setFileType(int newt) {filetype=newt;}
void setClassSize(int newSize) {class_size=newSize;}
void setOldClasses(int newVal) {old_classes=newVal;}
void setLambda(real newLambda) {lambda=newLambda;}
void setGradientCutoff(real newGradient) {gradient_cutoff=newGradient;}
void setDynamic(real newD) {dynamic=newD;}
void setGen(real newGen) {gen=newGen;}
void setIndependent(int newVal) {independent=newVal;}
void setLearningRate(real newAlpha) {alpha=newAlpha;}
void setRegularization(real newBeta) {beta=newBeta;}
void setMinImprovement(real newMinImprovement) {min_improvement=newMinImprovement;}
void setHiddenLayerSize(int newsize) {layer1_size=newsize;}
void setCompressionLayerSize(int newsize) {layerc_size=newsize;}
void setDirectSize(long long newsize) {direct_size=newsize;}
void setDirectOrder(int newsize) {direct_order=newsize;}
void setBPTT(int newval) {bptt=newval;}
void setBPTTBlock(int newval) {bptt_block=newval;}
void setRandSeed(int newSeed) {rand_seed=newSeed; srand(rand_seed);}
void setDebugMode(int newDebug) {debug_mode=newDebug;}
void setAntiKasparek(int newAnti) {anti_k=newAnti;}
void setOneIter(int newOneIter) {one_iter=newOneIter;}
void setMaxIter(int newMaxIter) {maxIter=newMaxIter;}
//返回单词的哈希值
int getWordHash(char *word);
//从文件中读取一个单词到word
void readWord(char *word, FILE *fin);
//查找word,找到返回word在vocab中的索引,没找到返回-1
int searchVocab(char *word);
//读取当前文件指针所指的单词,并返回该单词在vocab中的索引
int readWordIndex(FILE *fin);
//将word添加到vocab中,并且返回刚添加word在vocab中的索引
int addWordToVocab(char *word);
//从train_file中读数据,相关数据会装入vocab,vocab_hash
//这里假设vocab是空的
void learnVocabFromTrainFile(); //train_file will be used to construct vocabulary
//保存当前的权值,以及神经元信息值
void saveWeights(); //saves current weights and unit activations
//上面是暂存当前权值及神经元值,这里是从前面存下的数据中恢复
void restoreWeights(); //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值
void saveContext();
//恢复隐层神经元的ac值
void restoreContext();
//保存隐层神经元的ac值
void saveContext2();
//恢复隐层神经元的ac值
void restoreContext2();
//初始化网络
void initNet();
//保存网络的所有信息到rnnlm_file
void saveNet();
//从文件流中读取一个字符使其ascii等于delim
//随后文件指针指向delim的下一个
void goToDelimiter(int delim, FILE *fi);
//从rnnlm_file中读取网络的所有信息
void restoreNet();
//清除神经元的ac,er值
void netFlush();
//隐层神经元(论文中的状态层s(t))的ac值置1
//s(t-1),即输入层layer1_size那部分的ac值置1
//bptt+history清0
void netReset(); //will erase just hidden layer state + bptt history + maxent history (called at end of sentences in the independent mode)
//网络前向,计算概率分布
void computeNet(int last_word, int word);
//反传误差,更新网络权值
void learnNet(int last_word, int word);
////将隐层神经元的ac值复制到输出层后layer1_size那部分
void copyHiddenLayerToInput();
//训练网络
void trainNet();
void useLMProb(int use) {use_lmprob=use;}
//测试网络
void testNet();
//这两个我还没看~
void testNbest();
void testGen();
//矩阵和向量相乘
//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的列向量
void matrixXvector(struct neuron *dest, struct neuron *srcvec, struct synapse *srcmatrix, int matrix_width, int from, int to, int from2, int to2, int type);
Recurrent neural network language modeling toolkit 源码深入剖析系列(二)
原文:http://blog.csdn.net/a635661820/article/details/44756545