zoukankan      html  css  js  c++  java
  • RNN推导

    http://www.cnblogs.com/YiXiaoZhou/p/6058890.html

    RNN求解过程推导与实现

    BPTT,Back Propagation Through Time.

    首先来看看怎么处理RNN。

    RNN展开网络如下图

    enter description here

    RNN展开结构.jpg
    enter description here

    RNN节点结构.jpg

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

    那么首先正向计算

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

    然后看看误差如何传递。

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

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

    enter description here

    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.  
    11. W=rand(hiddenDim,outputDim)*2-1;% (-1,1)参数矩阵 
    12. U=rand(hiddenDim,hiddenDim)*2-1;% (-1,1)参数矩阵 
    13. V=rand(inputDim,hiddenDim)*2-1; % (-1,1)参数矩阵 
    14.  
    15. delta_W=zeros(hiddenDim,outputDim); % 时刻间中间变量 
    16. delta_U=zeros(hiddenDim,hiddenDim); 
    17. delta_V=zeros(inputDim,hiddenDim); 
    18. error=0; 
    19. for p=1:10000 
    20. aInt=randi(largestNumber/2); 
    21. bInt=randi(largestNumber/2); 
    22. a=dic(aInt+1,:); 
    23. b=dic(bInt+1,:); 
    24. cInt=aInt+bInt; 
    25. c=dic(cInt+1,:); 
    26. y=zeros(1,T); 
    27.  
    28. preh=zeros(1,hiddenDim); 
    29. hDic=zeros(T,hiddenDim); 
    30. %% 前向计算 
    31. for t=T:-1:1 % 注意应该从最低位计算,也就是二进制数组最右端开始计算 
    32. x=[a(t),b(t)];  
    33. h=sigmoid(x*V+preh*U); 
    34. y(t)=sigmoid(h*W);  
    35. hDic(t,:)=h; 
    36. preh=h; 
    37. end 
    38.  
    39. err=y-c; 
    40. error=error+norm(err,2)/2; 
    41. next_delta_h=zeros(1,hiddenDim); 
    42. %% 反馈 
    43. for t=1:T 
    44. delta_y = err(t).*sigmoidOutput2d(y(t)); 
    45. delta_h=(delta_y*W'+next_delta_h*U').*sigmoidOutput2d(hDic(t,:)); 
    46.  
    47. delta_W=delta_W+hDic(t,:)'*delta_y; 
    48. if t<T 
    49. delta_U=delta_U+hDic(t+1,:)'*delta_h; 
    50. end 
    51. delta_V=delta_V+[a(t),b(t)]'*delta_h; 
    52. next_delta_h=delta_h;  
    53. end 
    54. % 梯度下降  
    55. W=W-eta*delta_W; 
    56. U=U-eta*delta_U; 
    57. V=V-eta*delta_V; 
    58.  
    59. delta_W=zeros(hiddenDim,outputDim); 
    60. delta_U=zeros(hiddenDim,hiddenDim); 
    61. delta_V=zeros(inputDim,hiddenDim); 
    62.  
    63. if mod(p,1000)==
    64. fprintf('Samples:%d ',p); 
    65. fprintf('True:%d ',cInt); 
    66. fprintf('Predict:%d ',bin2dec(int2str(round(y)))); 
    67. fprintf('Error:%f ',norm(err,2)/2); 
    68. end 
    69. end 
    70. end 
    71.  
    72. function sx=sigmoid(x) 
    73. sx=1./(1+exp(-x)); 
    74. end 
    75.  
    76. function dx=sigmoidOutput2d(output) 
    77. dx=output.*(1-output); 
    78. 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.  
    8. # define random(x) ((rand()*rand())%x) //生成0-x的随机数 
    9.  
    10. void Display(CvMat* mat) 

    11. cout << setiosflags(ios::fixed); 
    12. for (int i = 0; i < mat->rows; i++) 

    13. for (int j = 0; j < mat->cols; j++) 
    14. cout << cvmGet(mat, i, j) << " "; 
    15. cout << endl; 

    16.  

    17.  
    18.  
    19. // sigmoid 函数 
    20. float sigmoid(float x) 

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

    22. CvMat* sigmoidM(CvMat* mat) 

    23. CvMat*mat2 = cvCloneMat(mat); 
    24.  
    25. for (int i = 0; i < mat2->rows; i++) 

    26. for (int j = 0; j < mat2->cols; j++) 
    27. cvmSet(mat2, i, j, sigmoid(cvmGet(mat, i, j))); 

    28. return mat2; 

    29. //sigmoid 函数的导数 
    30. float diffSigmoid(float x) 

    31. //注意,这里的x已经是sigmoid的结果 
    32. return x*(1 - x); 

    33. CvMat* diffSigmoidM(CvMat* mat) 

    34. CvMat* mat2 = cvCloneMat(mat); 
    35.  
    36. for (int i = 0; i < mat2->rows; i++) 

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

    38. float t = cvmGet(mat, i, j); 
    39. cvmSet(mat2, i, j, t*(1 - t)); 

    40.  

    41. return mat2; 
    42.  

    43.  
    44.  
    45. /**************随机生成inputdim个整数,并求和****************** 
    46. * inputdim 整数的个数 
    47. * MAX 整数的最大范围 
    48. * Sample 存放整数 
    49. * 返回 整数和 
    50. **************************************************************/ 
    51. int sample(int inputdim, CvMat* Sample,int MAX) 

    52. int sum = 0; 
    53. for (int i = 0; i < inputdim; i++) 

    54. int t = random(MAX); 
    55. cvmSet(Sample, 0, i, t); 
    56. sum += cvmGet(Sample,0,i); 

    57. return sum; 

    58. /********将整数拆分成10以内的数,作为每个时刻的输入************* 
    59. * Sample 存放的整数 大小 1*inputdim 
    60. * 返回 拆分后的输入数据 大小 inputdim*9 
    61. ****************************************************************/ 
    62. CvMat* splitM( CvMat*Sample) 

    63. CvMat* mat = cvCreateMat(Sample->cols, 8, CV_32F); 
    64. cvSetZero(mat); 
    65. for (int i = 0; i < mat->rows; i++) 

    66. int x = cvmGet(Sample,0,i); 
    67. for (int j = 0; j < 8; ++j) 

    68. cvmSet(mat,i,j, x % 10); 
    69. x = x / 10; 


    70. return mat; 

    71.  
    72. /***************将数字数组整合成一个整数****************************** 
    73. *mat 数字数组,即每个元素是十以内的整数,大小1*9 
    74. *返回 整合后的整数 
    75. *********************************************************************/ 
    76. int merge(CvMat* mat) 

    77. double d = 0; 
    78. for (int i = mat->cols; i >0; i--) 

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

    81. return int(d); 

    82. /*****************将输出的数值拆分************************************** 
    83. * y 输出的数值 
    84. * 返回 长度为9的数组,这里转换成了0,1之间的数 
    85. ***********************************************************************/ 
    86. CvMat* split(int y) 

    87. CvMat* mat = cvCreateMat(1, 8, CV_32F); 
    88. for (int i = 0; i < 8; i++) 

    89. cvmSet(mat,0,i, (y % 10) / 10.0); 
    90. y = y / 10; 

    91. return mat; 
    92.  

    93.  
    94. /**********************产生随机矩阵****************************** 
    95. * rows, cols, 矩阵的规模 
    96. * a, b, 区间 
    97. * 返回 返回[a,b]之间的随机矩阵 
    98. *****************************************************************/ 
    99. CvMat*randM(int rows,int cols, float a,float b) 

    100. CvMat* mat = cvCreateMat(rows, cols, CV_32FC1); 
    101. float* ptr; 
    102. for (int i = 0; i < mat->rows; i++) 

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

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


    105. return mat; 

    106.  
    107. int main() 

    108. srand(time(NULL)); 
    109. //首先,先定义网络 
    110. int inputdim = 2;//不超过10 
    111. int hiddendim = 16; 
    112. int outputdim = 1; 
    113. float eta = 0.1; 
    114. int MAX = 100000000;//令整数最多八位 
    115. //初始化参数矩阵 
    116. CvMat* V = randM(inputdim, hiddendim,-1,1); 
    117. CvMat* U = randM(hiddendim, hiddendim, -1, 1); 
    118. CvMat* W = randM(hiddendim, outputdim, -1, 1); 
    119. CvMat* bh = randM(1, hiddendim, -1, 1); 
    120. CvMat* by = randM(1, outputdim, -1, 1);//偏置 
    121.  
    122. CvMat*Sample = cvCreateMat(1, inputdim, CV_32F); 
    123. cvSetZero(Sample); 
    124. CvMat* delta_V = cvCloneMat(V); 
    125. CvMat* delta_U = cvCloneMat(U); 
    126. CvMat* delta_W = cvCloneMat(W); 
    127. CvMat* delta_by = cvCloneMat(by); 
    128. CvMat* delta_bh = cvCloneMat(bh); 
    129.  
    130. //开始训练,训练集大小10000 
    131. for (int p = 0; p < 20000; p++) 

    132. int sum = sample(inputdim,Sample,MAX); 
    133. CvMat* sampleM = splitM(Sample);//每一行对应着一个整数的拆分,个位在前 
    134. CvMat* d = split(sum);//真实结果拆分,每位存放的是除以10后的小数 
    135. //正向计算 
    136. CvMat* pre_h = cvCreateMat(1, hiddendim, CV_32F); 
    137. cvSetZero(pre_h);//初始化最开始的h_{-1} 
    138. CvMat* y = cvCreateMat(1, 8, CV_32F); 
    139. cvSetZero(y);//定义输出量 
    140. CvMat* h = cvCreateMat(8, hiddendim, CV_32F);//每一行存储一个时刻的隐变量输出 
    141.  
    142. CvMat* temp1 = cvCreateMat(1, hiddendim, CV_32F); 
    143. CvMat* temp2 = cvCreateMat(1, outputdim, CV_32F); 
    144. CvMat* xt = cvCreateMatHeader(inputdim, 1, CV_32S); 
    145. for (int t = 0; t < 8; t++) 

    146. cvGetCol(sampleM, xt, t);//获取第t时刻输入值 
    147. cvGEMM(xt, V, 1,bh, 1, temp1, CV_GEMM_A_T); 
    148. cvGEMM(pre_h, U, 1, temp1, 1, pre_h);// t时刻隐层输出 
    149. pre_h = sigmoidM(pre_h); 
    150.  
    151. cvGEMM(pre_h, W, 1, by, 1, temp2); 
    152. float yvalue = sigmoid(cvmGet(temp2, 0, 0)); 
    153. cvmSet(y, 0, t, yvalue);//t时刻的输出 
    154.  
    155. //保存隐层输出 
    156. for (int j = 0; j < hiddendim; j++) 

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


    158. cvReleaseMat(&temp1); 
    159. cvReleaseMat(&temp2); 
    160.  
    161. //观察代码 
    162. int oy = merge(y); 
    163. CvMat* temp = cvCreateMat(1, 8, CV_32F); 
    164. cvSub(y, d, temp); 
    165. double error = 0.5*cvDotProduct(temp, temp); 
    166. if ((p+1)%1000==0) 

    167. cout << "************************第" << p + 1 << "个样本***********" << endl; 
    168. cout << "真实值:" << sum%MAX << endl; 
    169. cout << "预测值:" << oy << endl; 
    170. cout << "误差:" << error << endl; 

    171. //反向传递误差 
    172. cvSetZero(delta_V); 
    173. cvSetZero(delta_U); 
    174. cvSetZero(delta_W); 
    175. cvSetZero(delta_bh); 
    176. cvSetZero(delta_by); 
    177.  
    178. CvMat* delta_h = cvCreateMat(1, hiddendim, CV_32F); 
    179. cvSetZero(delta_h); 
    180. CvMat* delta_y = cvCreateMat(1, outputdim, CV_32F); 
    181. cvSetZero(delta_y); 
    182. CvMat* next_delta_h = cvCreateMat(1, hiddendim, CV_32F); 
    183. cvSetZero(next_delta_h); 
    184.  
    185. for (int t = 7; t > 0; t--) 

    186. cvmSet(delta_y, 0, 0, (cvmGet(y, 0, t) - cvmGet(d, 0, t))*diffSigmoid(cvmGet(y, 0, t))); 
    187. cvGEMM(delta_y, W, 1, delta_h, 0, delta_h, CV_GEMM_B_T); 
    188. cvGEMM(next_delta_h, U, 1, delta_h, 1, delta_h, CV_GEMM_B_T); 
    189. cvMul(delta_h, diffSigmoidM(cvGetRow(h, temp, t)), delta_h); 
    190. //更新delta_y,delta_h 
    191. cvGEMM(cvGetRow(h, temp, t), delta_y, 1, delta_W, 1, delta_W, CV_GEMM_A_T); 
    192. if (t>0) 
    193. cvGEMM(cvGetRow(h, temp, t - 1), delta_h, 1, delta_U, 1, delta_U, CV_GEMM_A_T); 
    194. cvGetCol(sampleM, xt, t); 
    195. cvGEMM(xt, delta_h, 1, delta_V, 1, delta_V); 
    196. cvAddWeighted(delta_by, 1, delta_y, 1, 0, delta_by); 
    197. cvAddWeighted(delta_bh, 1, delta_h, 1, 0, delta_bh); 
    198.  
    199. cvAddWeighted(delta_h, 1, next_delta_h, 0, 0, next_delta_h); 
    200.  

    201. cvAddWeighted(W, 1, delta_W, -eta, 0, W); 
    202. cvAddWeighted(V, 1, delta_V, -eta, 0, V); 
    203. cvAddWeighted(U, 1, delta_U, -eta, 0, U); 
    204.  
    205. cvAddWeighted(by, 1, delta_by, -eta, 0, by); 
    206. cvAddWeighted(bh, 1, delta_bh, -eta, 0, bh); 
    207.  
    208. cvReleaseMat(&sampleM); 
    209. cvReleaseMat(&d); 
    210. cvReleaseMat(&pre_h); 
    211. cvReleaseMat(&y); 
    212. cvReleaseMat(&h); 
    213. cvReleaseMat(&delta_h); 
    214. cvReleaseMat(&delta_y); 

    215. cvReleaseMat(&U); 
    216. cvReleaseMat(&V); 
    217. cvReleaseMat(&W); 
    218. cvReleaseMat(&by); 
    219. cvReleaseMat(&bh); 
    220. cvReleaseMat(&Sample); 
    221. cvReleaseMat(&delta_V); 
    222. cvReleaseMat(&delta_U); 
    223. cvReleaseMat(&delta_W); 
    224. cvReleaseMat(&delta_by); 
    225. cvReleaseMat(&delta_bh); 
    226. system("PAUSE"); 
    227. return 0; 

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

    enter description here

    1479024804302.jpg

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

    enter description here

    1479024912622.jpg
  • 相关阅读:
    area标签的使用,图片中某一个部分可以点击跳转,太阳系中点击某个行星查看具体信息
    使用window.localStorage,window.localStorage记录点击次数
    js判断是否是在微信浏览器中打开
    点击按钮回到页面顶端的几种方式
    一行代码实现F11的功能,即让浏览器窗口全屏
    容器居中
    判断某个窗口是否是活动窗口(当前正在使用、操作的窗口)
    对数组每项进行处理、判断的几种方法
    form文件上传、下载,获取返回值
    scss基本使用及操作函数
  • 原文地址:https://www.cnblogs.com/xqnq2007/p/7306038.html
Copyright © 2011-2022 走看看