首页 技术 正文
技术 2022年11月8日
0 收藏 542 点赞 1,719 浏览 8216 个字

RNN求解过程推导与实现

RNN LSTM BPTT matlab code opencv code

BPTT,Back Propagation Through Time.

首先来看看怎么处理RNN。

RNN展开网络如下图

RNN展开结构.jpg

RNN节点结构.jpg

现令第t时刻的输入表示为,隐层节点的输出为,输出层的预测值,输入到隐层的权重矩阵,隐层自循环的权重矩阵,隐层到输出层的权重矩阵,对应的偏执向量分别表示为,输入层的某一个节点使用i标识,如,类似的隐层和输出层某一节点表示为。这里我们仅以三层网络为例。

那么首先正向计算

其中分别表示激活前对应的加权和,表示激活函数。

然后看看误差如何传递。

假设真实的输出应该是,那么误差可以定义为,是训练样本的index。整个网络的误差

我们将RNN再放大一些,看看细节

RNN节点内部连接.jpg

矩阵向量化表示

所以梯度为:

其中是点乘符号,即对应元素乘。

代码实现:

我们可以注意到在计算梯度时需要用到的之前计算过的量,即需要保存的量包括,前向计算时隐层节点和输出节点的输出值,以及由时刻累积的

人人都能用Python写出LSTM-RNN的代码![你的神经网络学习最佳起步]这篇文章里使用python实现了基本的RNN过程。代码功能是模拟二进制相加过程中的依次进位过程,代码很容易明白。

这里改写成matlab代码

  1. function error = binaryRNN( ) 

  2. largestNumber=256; 

  3. T=8; 

  4. dic=dec2bin(0:largestNumber-1)-‘0’;% 将uint8表示成二进制数组,这是一个查找表 

  5. %% 初始化参数 

  6. eta=0.1;% 学习步长 

  7. inputDim=2;% 输入维度 

  8. hiddenDim=16; %隐层节点个数 

  9. outputDim=1; % 输出层节点个数 


  10. W=rand(hiddenDim,outputDim)*2-1;% (-1,1)参数矩阵 

  11. U=rand(hiddenDim,hiddenDim)*2-1;% (-1,1)参数矩阵 

  12. V=rand(inputDim,hiddenDim)*2-1; % (-1,1)参数矩阵 


  13. delta_W=zeros(hiddenDim,outputDim); % 时刻间中间变量 

  14. delta_U=zeros(hiddenDim,hiddenDim); 

  15. delta_V=zeros(inputDim,hiddenDim); 

  16. error=0; 

  17. for p=1:10000 

  18. aInt=randi(largestNumber/2); 

  19. bInt=randi(largestNumber/2); 

  20. a=dic(aInt+1,:); 

  21. b=dic(bInt+1,:); 

  22. cInt=aInt+bInt; 

  23. c=dic(cInt+1,:); 

  24. y=zeros(1,T); 


  25. preh=zeros(1,hiddenDim); 

  26. hDic=zeros(T,hiddenDim); 

  27. %% 前向计算 

  28. for t=T:-1:1 % 注意应该从最低位计算,也就是二进制数组最右端开始计算 

  29. x=[a(t),b(t)];  

  30. h=sigmoid(x*V+preh*U); 

  31. y(t)=sigmoid(h*W);  

  32. hDic(t,:)=h; 

  33. preh=h; 

  34. end 


  35. err=y-c; 

  36. error=error+norm(err,2)/2; 

  37. next_delta_h=zeros(1,hiddenDim); 

  38. %% 反馈 

  39. for t=1:T 

  40. delta_y = err(t).*sigmoidOutput2d(y(t)); 

  41. delta_h=(delta_y*W’+next_delta_h*U’).*sigmoidOutput2d(hDic(t,:)); 


  42. delta_W=delta_W+hDic(t,:)’*delta_y; 

  43. if t<T 

  44. delta_U=delta_U+hDic(t+1,:)’*delta_h; 

  45. end 

  46. delta_V=delta_V+[a(t),b(t)]’*delta_h; 

  47. next_delta_h=delta_h;  

  48. end 

  49. % 梯度下降  

  50. W=W-eta*delta_W; 

  51. U=U-eta*delta_U; 

  52. V=V-eta*delta_V; 


  53. delta_W=zeros(hiddenDim,outputDim); 

  54. delta_U=zeros(hiddenDim,hiddenDim); 

  55. delta_V=zeros(inputDim,hiddenDim); 


  56. if mod(p,1000)==0 

  57. fprintf(‘Samples:%d\n’,p); 

  58. fprintf(‘True:%d\n’,cInt); 

  59. fprintf(‘Predict:%d\n’,bin2dec(int2str(round(y)))); 

  60. fprintf(‘Error:%f\n’,norm(err,2)/2); 

  61. end 

  62. end 

  63. end 


  64. function sx=sigmoid(x) 

  65. sx=1./(1+exp(-x)); 

  66. end 


  67. function dx=sigmoidOutput2d(output) 

  68. dx=output.*(1-output); 

  69. end 

为了更深入理解RNN过程,这里我想用OpenCV和C++实现自己的RNN,简单的单隐层网络。同样类似的功能,模拟多个十进制数的加法进位过程。

  1. # include “highgui.h” 

  2. # include “cv.h” 

  3. # include <iostream> 

  4. #include “math.h” 

  5. #include<cstdlib> 

  6. using namespace std; 


  7. # define random(x) ((rand()*rand())%x) //生成0-x的随机数 


  8. void Display(CvMat* mat) 



  9. cout << setiosflags(ios::fixed); 

  10. for (int i = 0; i < mat->rows; i++) 



  11. for (int j = 0; j < mat->cols; j++) 

  12. cout << cvmGet(mat, i, j) << ” “; 

  13. cout << endl; 








  14. // sigmoid 函数 

  15. float sigmoid(float x) 



  16. return 1 / (1 + exp(-x)); 



  17. CvMat* sigmoidM(CvMat* mat) 



  18. CvMat*mat2 = cvCloneMat(mat); 


  19. for (int i = 0; i < mat2->rows; i++) 



  20. for (int j = 0; j < mat2->cols; j++) 

  21. cvmSet(mat2, i, j, sigmoid(cvmGet(mat, i, j))); 



  22. return mat2; 



  23. //sigmoid 函数的导数 

  24. float diffSigmoid(float x) 



  25. //注意,这里的x已经是sigmoid的结果 

  26. return x*(1 – x); 



  27. CvMat* diffSigmoidM(CvMat* mat) 



  28. CvMat* mat2 = cvCloneMat(mat); 


  29. for (int i = 0; i < mat2->rows; i++) 



  30. for (int j = 0; j < mat2->cols; j++) 



  31. float t = cvmGet(mat, i, j); 

  32. cvmSet(mat2, i, j, t*(1 – t)); 






  33. return mat2; 






  34. /**************随机生成inputdim个整数,并求和****************** 

  35. * inputdim 整数的个数 

  36. * MAX 整数的最大范围 

  37. * Sample 存放整数 

  38. * 返回 整数和 

  39. **************************************************************/ 

  40. int sample(int inputdim, CvMat* Sample,int MAX) 



  41. int sum = 0; 

  42. for (int i = 0; i < inputdim; i++) 



  43. int t = random(MAX); 

  44. cvmSet(Sample, 0, i, t); 

  45. sum += cvmGet(Sample,0,i); 



  46. return sum; 



  47. /********将整数拆分成10以内的数,作为每个时刻的输入************* 

  48. * Sample 存放的整数 大小 1*inputdim 

  49. * 返回 拆分后的输入数据 大小 inputdim*9 

  50. ****************************************************************/ 

  51. CvMat* splitM( CvMat*Sample) 



  52. CvMat* mat = cvCreateMat(Sample->cols, 8, CV_32F); 

  53. cvSetZero(mat); 

  54. for (int i = 0; i < mat->rows; i++) 



  55. int x = cvmGet(Sample,0,i); 

  56. for (int j = 0; j < 8; ++j) 



  57. cvmSet(mat,i,j, x % 10); 

  58. x = x / 10; 





  59. return mat; 




  60. /***************将数字数组整合成一个整数****************************** 

  61. *mat 数字数组,即每个元素是十以内的整数,大小1*9 

  62. *返回 整合后的整数 

  63. *********************************************************************/ 

  64. int merge(CvMat* mat) 



  65. double d = 0; 

  66. for (int i = mat->cols; i >0; i–) 



  67. d = 10 * d + round(10*(cvmGet(mat,0,i-1))); 




  68. return int(d); 



  69. /*****************将输出的数值拆分************************************** 

  70. * y 输出的数值 

  71. * 返回 长度为9的数组,这里转换成了0,1之间的数 

  72. ***********************************************************************/ 

  73. CvMat* split(int y) 



  74. CvMat* mat = cvCreateMat(1, 8, CV_32F); 

  75. for (int i = 0; i < 8; i++) 



  76. cvmSet(mat,0,i, (y % 10) / 10.0); 

  77. y = y / 10; 



  78. return mat; 





  79. /**********************产生随机矩阵****************************** 

  80. * rows, cols, 矩阵的规模 

  81. * a, b, 区间 

  82. * 返回 返回[a,b]之间的随机矩阵 

  83. *****************************************************************/ 

  84. CvMat*randM(int rows,int cols, float a,float b) 



  85. CvMat* mat = cvCreateMat(rows, cols, CV_32FC1); 

  86. float* ptr; 

  87. for (int i = 0; i < mat->rows; i++) 



  88. for (int j = 0; j < mat->cols; j++) 



  89. cvmSet(mat, i, j, random(1000) / 1000.0*(b – a) + a); 





  90. return mat; 




  91. int main() 



  92. srand(time(NULL)); 

  93. //首先,先定义网络 

  94. int inputdim = 2;//不超过10 

  95. int hiddendim = 16; 

  96. int outputdim = 1; 

  97. float eta = 0.1; 

  98. int MAX = 100000000;//令整数最多八位 

  99. //初始化参数矩阵 

  100. CvMat* V = randM(inputdim, hiddendim,-1,1); 

  101. CvMat* U = randM(hiddendim, hiddendim, -1, 1); 

  102. CvMat* W = randM(hiddendim, outputdim, -1, 1); 

  103. CvMat* bh = randM(1, hiddendim, -1, 1); 

  104. CvMat* by = randM(1, outputdim, -1, 1);//偏置 


  105. CvMat*Sample = cvCreateMat(1, inputdim, CV_32F); 

  106. cvSetZero(Sample); 

  107. CvMat* delta_V = cvCloneMat(V); 

  108. CvMat* delta_U = cvCloneMat(U); 

  109. CvMat* delta_W = cvCloneMat(W); 

  110. CvMat* delta_by = cvCloneMat(by); 

  111. CvMat* delta_bh = cvCloneMat(bh); 


  112. //开始训练,训练集大小10000 

  113. for (int p = 0; p < 20000; p++) 



  114. int sum = sample(inputdim,Sample,MAX); 

  115. CvMat* sampleM = splitM(Sample);//每一行对应着一个整数的拆分,个位在前 

  116. CvMat* d = split(sum);//真实结果拆分,每位存放的是除以10后的小数 

  117. //正向计算 

  118. CvMat* pre_h = cvCreateMat(1, hiddendim, CV_32F); 

  119. cvSetZero(pre_h);//初始化最开始的h_{-1} 

  120. CvMat* y = cvCreateMat(1, 8, CV_32F); 

  121. cvSetZero(y);//定义输出量 

  122. CvMat* h = cvCreateMat(8, hiddendim, CV_32F);//每一行存储一个时刻的隐变量输出 


  123. CvMat* temp1 = cvCreateMat(1, hiddendim, CV_32F); 

  124. CvMat* temp2 = cvCreateMat(1, outputdim, CV_32F); 

  125. CvMat* xt = cvCreateMatHeader(inputdim, 1, CV_32S); 

  126. for (int t = 0; t < 8; t++) 



  127. cvGetCol(sampleM, xt, t);//获取第t时刻输入值 

  128. cvGEMM(xt, V, 1,bh, 1, temp1, CV_GEMM_A_T); 

  129. cvGEMM(pre_h, U, 1, temp1, 1, pre_h);// t时刻隐层输出 

  130. pre_h = sigmoidM(pre_h); 


  131. cvGEMM(pre_h, W, 1, by, 1, temp2); 

  132. float yvalue = sigmoid(cvmGet(temp2, 0, 0)); 

  133. cvmSet(y, 0, t, yvalue);//t时刻的输出 


  134. //保存隐层输出 

  135. for (int j = 0; j < hiddendim; j++) 



  136. cvmSet(h, t, j, cvmGet(pre_h, 0, j)); 





  137. cvReleaseMat(&temp1); 

  138. cvReleaseMat(&temp2); 


  139. //观察代码 

  140. int oy = merge(y); 

  141. CvMat* temp = cvCreateMat(1, 8, CV_32F); 

  142. cvSub(y, d, temp); 

  143. double error = 0.5*cvDotProduct(temp, temp); 

  144. if ((p+1)%1000==0) 



  145. cout << “************************第” << p + 1 << “个样本***********” << endl; 

  146. cout << “真实值:” << sum%MAX << endl; 

  147. cout << “预测值:” << oy << endl; 

  148. cout << “误差:” << error << endl; 



  149. //反向传递误差 

  150. cvSetZero(delta_V); 

  151. cvSetZero(delta_U); 

  152. cvSetZero(delta_W); 

  153. cvSetZero(delta_bh); 

  154. cvSetZero(delta_by); 


  155. CvMat* delta_h = cvCreateMat(1, hiddendim, CV_32F); 

  156. cvSetZero(delta_h); 

  157. CvMat* delta_y = cvCreateMat(1, outputdim, CV_32F); 

  158. cvSetZero(delta_y); 

  159. CvMat* next_delta_h = cvCreateMat(1, hiddendim, CV_32F); 

  160. cvSetZero(next_delta_h); 


  161. for (int t = 7; t > 0; t–) 



  162. cvmSet(delta_y, 0, 0, (cvmGet(y, 0, t) – cvmGet(d, 0, t))*diffSigmoid(cvmGet(y, 0, t))); 

  163. cvGEMM(delta_y, W, 1, delta_h, 0, delta_h, CV_GEMM_B_T); 

  164. cvGEMM(next_delta_h, U, 1, delta_h, 1, delta_h, CV_GEMM_B_T); 

  165. cvMul(delta_h, diffSigmoidM(cvGetRow(h, temp, t)), delta_h); 

  166. //更新delta_y,delta_h 

  167. cvGEMM(cvGetRow(h, temp, t), delta_y, 1, delta_W, 1, delta_W, CV_GEMM_A_T); 

  168. if (t>0) 

  169. cvGEMM(cvGetRow(h, temp, t – 1), delta_h, 1, delta_U, 1, delta_U, CV_GEMM_A_T); 

  170. cvGetCol(sampleM, xt, t); 

  171. cvGEMM(xt, delta_h, 1, delta_V, 1, delta_V); 

  172. cvAddWeighted(delta_by, 1, delta_y, 1, 0, delta_by); 

  173. cvAddWeighted(delta_bh, 1, delta_h, 1, 0, delta_bh); 


  174. cvAddWeighted(delta_h, 1, next_delta_h, 0, 0, next_delta_h); 




  175. cvAddWeighted(W, 1, delta_W, -eta, 0, W); 

  176. cvAddWeighted(V, 1, delta_V, -eta, 0, V); 

  177. cvAddWeighted(U, 1, delta_U, -eta, 0, U); 


  178. cvAddWeighted(by, 1, delta_by, -eta, 0, by); 

  179. cvAddWeighted(bh, 1, delta_bh, -eta, 0, bh); 


  180. cvReleaseMat(&sampleM); 

  181. cvReleaseMat(&d); 

  182. cvReleaseMat(&pre_h); 

  183. cvReleaseMat(&y); 

  184. cvReleaseMat(&h); 

  185. cvReleaseMat(&delta_h); 

  186. cvReleaseMat(&delta_y); 



  187. cvReleaseMat(&U); 

  188. cvReleaseMat(&V); 

  189. cvReleaseMat(&W); 

  190. cvReleaseMat(&by); 

  191. cvReleaseMat(&bh); 

  192. cvReleaseMat(&Sample); 

  193. cvReleaseMat(&delta_V); 

  194. cvReleaseMat(&delta_U); 

  195. cvReleaseMat(&delta_W); 

  196. cvReleaseMat(&delta_by); 

  197. cvReleaseMat(&delta_bh); 

  198. system(“PAUSE”); 

  199. return 0; 



下面是代码结果,并没有完全一致。分析下主要原因可能是由于输出层是(0,1)的小数,但我们希望得到的是[0,10]的整数,而通过round或者强制类型转换总会带来较大误差,所以会出现预测值和真实值差别很大,这时候其实比较值的差异意义不大,应该对比每一位上数字的差异。

1479024804302.jpg

再下面是3个输入,32个隐层节点的结果

1479024912622.jpg

PS. 作为opencv新手,觉得matlab半小时搞定的东西,opencv要捣鼓两个小时。。。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,077
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,552
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,401
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,176
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,813
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,896