卷积神经网络全面解析之代码注释.docx

上传人:小飞机 文档编号:3349227 上传时间:2023-03-12 格式:DOCX 页数:16 大小:44.30KB
返回 下载 相关 举报
卷积神经网络全面解析之代码注释.docx_第1页
第1页 / 共16页
卷积神经网络全面解析之代码注释.docx_第2页
第2页 / 共16页
卷积神经网络全面解析之代码注释.docx_第3页
第3页 / 共16页
卷积神经网络全面解析之代码注释.docx_第4页
第4页 / 共16页
卷积神经网络全面解析之代码注释.docx_第5页
第5页 / 共16页
亲,该文档总共16页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《卷积神经网络全面解析之代码注释.docx》由会员分享,可在线阅读,更多相关《卷积神经网络全面解析之代码注释.docx(16页珍藏版)》请在三一办公上搜索。

1、卷积神经网络全面解析之代码注释卷积神经网络全面解析之代码注释 自己平时看了一些论文,但老感觉看完过后就会慢慢的淡忘,某一天重新拾起来的时候又好像没有看过一样。所以想习惯地把一些感觉有用的论文中的知识点总结整理一下,一方面在整理过程中,自己的理解也会更深,另一方面也方便未来自己的勘察。更好的还可以放到博客上面与大家交流。因为基础有限,所以对论文的一些理解可能不太正确,还望大家不吝指正交流. 下面是自己对代码的注释: cnnexamples.m plain view plain copy 1. clear all; close all; clc; 2. addpath(./data); 3. ad

2、dpath(./util); 4. load mnist_uint8; 5. 6. train_x = double(reshape(train_x,28,28,60000)/255; 7. test_x = double(reshape(test_x,28,28,10000)/255; 8. train_y = double(train_y); 9. test_y = double(test_y); 10. 11. % ex1 12. %will run 1 epoch in about 200 second and get around 11% error. 13. %With 100 e

3、pochs youll get around 1.2% error 14. 15. cnn.layers = 16. struct(type, i) %input layer 17. struct(type, c, outputmaps, 6, kernelsize, 5) %convolution layer 18. struct(type, s, scale, 2) %sub sampling layer 19. struct(type, c, outputmaps, 12, kernelsize, 5) %convolution layer 20. struct(type, s, sca

4、le, 2) %subsampling layer 21. ; 22. 23. % 这里把cnn的设置给cnnsetup,它会据此构建一个完整的CNN网络,并返回 24. cnn = cnnsetup(cnn, train_x, train_y); 25. 26. % 学习率 27. opts.alpha = 1; 28. % 每次挑出一个batchsize的batch来训练,也就是每用batchsize个样本就调整一次权值,而不是 29. % 把所有样本都输入了,计算所有样本的误差了才调整一次权值 30. opts.batchsize = 50; 31. % 训练次数,用同样的样本集。我训练

5、的时候: 32. % 1的时候 11.41% error 33. % 5的时候 4.2% error 34. % 10的时候 2.73% error 35. opts.numepochs = 10; 36. 37. % 然后开始把训练样本给它,开始训练这个CNN网络 38. cnn = cnntrain(cnn, train_x, train_y, opts); 39. 40. % 然后就用测试样本来测试 41. er, bad = cnntest(cnn, test_x, test_y); 42. 43. %plot mean squared error 44. plot(cnn.rL);

6、45. %show test error 46. disp(num2str(er*100) % error); cnnsetup.m plain view plain copy 1. function net = cnnsetup(net, x, y) 2. inputmaps = 1; 3. % B=squeeze(A) 返回和矩阵A相同元素但所有单一维都移除的矩阵B,单一维是满足size(A,dim)=1的维。 4. % train_x中图像的存放方式是三维的reshape(train_x,28,28,60000),前面两维表示图像的行与列, 5. % 第三维就表示有多少个图像。这样squ

7、eeze(x(:, :, 1)就相当于取第一个图像样本后,再把第三维 6. % 移除,就变成了28x28的矩阵,也就是得到一幅图像,再size一下就得到了训练样本图像的行数与列数了 7. mapsize = size(squeeze(x(:, :, 1); 8. 9. % 下面通过传入net这个结构体来逐层构建CNN网络 10. % n = numel(A)返回数组A中元素个数 11. % net.layers中有五个struct类型的元素,实际上就表示CNN共有五层,这里范围的是5 12. for l = 1 : numel(net.layers) % layer 13. if strcmp

8、(net.layersl.type, s) % 如果这层是 子采样层 14. % subsampling层的mapsize,最开始mapsize是每张图的大小28*28 15. % 这里除以scale=2,就是pooling之后图的大小,pooling域之间没有重叠,所以pooling后的图像为14*14 16. % 注意这里的右边的mapsize保存的都是上一层每张特征map的大小,它会随着循环进行不断更新 17. mapsize = floor(mapsize / net.layersl.scale); 18. for j = 1 : inputmaps % inputmap就是上一层有多

9、少张特征图 19. net.layersl.bj = 0; % 将偏置初始化为0 20. end 21. end 22. if strcmp(net.layersl.type, c) % 如果这层是 卷积层 23. % 旧的mapsize保存的是上一层的特征map的大小,那么如果卷积核的移动步长是1,那用 24. % kernelsize*kernelsize大小的卷积核卷积上一层的特征map后,得到的新的map的大小就是下面这样 25. mapsize = mapsize - net.layersl.kernelsize + 1; 26. % 该层需要学习的参数个数。每张特征map是一个(后

10、层特征图数量)*(用来卷积的patch图的大小) 27. % 因为是通过用一个核窗口在上一个特征map层中移动,遍历上一个特征map 28. % 层的每个神经元。核窗口由kernelsize*kernelsize个元素组成,每个元素是一个独立的权值,所以 29. % 就有kernelsize*kernelsize个需要学习的权值,再加一个偏置值。另外,由于是权值共享,也就是 30. % 说同一个特征map层是用同一个具有相同权值元素的kernelsize*kernelsize的核窗口去感受输入上一 31. % 个特征map层的每个神经元得到的,所以同一个特征map,它的权值是一样的,共享的,权

11、值只取决于 32. % 核窗口。然后,不同的特征map提取输入上一个特征map层不同的特征,所以采用的核窗口不一样,也 33. % 就是权值不一样,所以outputmaps个特征map就有* outputmaps那么多的权值了 34. % 但这里fan_out只保存卷积核的权值W,偏置b在下面独立保存 35. fan_out = net.layersl.outputmaps * net.layersl.kernelsize 2; 36. for j = 1 : net.layersl.outputmaps % output map 37. % fan_out保存的是对于上一层的一张特征map,

12、我在这一层需要对这一张特征map提取outputmaps种特征, 38. % 提取每种特征用到的卷积核不同,所以fan_out保存的是这一层输出新的特征需要学习的参数个数 39. % 而,fan_in保存的是,我在这一层,要连接到上一层中所有的特征map,然后用fan_out保存的提取特征 40. % 的权值来提取他们的特征。也即是对于每一个当前层特征图,有多少个参数链到前层 41. fan_in = inputmaps * net.layersl.kernelsize 2; 42. for i = 1 : inputmaps % input map 43. % 随机初始化权值,也就是共有ou

13、tputmaps个卷积核,对上层的每个特征map,都需要用这么多个卷积核 44. % 去卷积提取特征。 45. % rand(n)是产生nn的 0-1之间均匀取值的数值的矩阵,再减去0.5就相当于产生-0.5到0.5之间的随机数 46. % 再 *2 就放大到 -1, 1。然后再乘以后面那一数,why? 47. % 反正就是将卷积核每个元素初始化为-sqrt(6 / (fan_in + fan_out), sqrt(6 / (fan_in + fan_out) 48. % 之间的随机数。因为这里是权值共享的,也就是对于一张特征map,所有感受野位置的卷积核都是一样的 49. % 所以只需要保存

14、的是 inputmaps * outputmaps 个卷积核。 50. net.layersl.kij = (rand(net.layersl.kernelsize) - 0.5) * 2 * sqrt(6 / (fan_in + fan_out); 51. end 52. net.layersl.bj = 0; % 将偏置初始化为0 53. end 54. % 只有在卷积层的时候才会改变特征map的个数,pooling的时候不会改变个数。这层输出的特征map个数就是 55. % 输入到下一层的特征map个数 56. inputmaps = net.layersl.outputmaps; 57

15、. end 58. end 59. 60. % fvnum 是输出层的前面一层的神经元个数。 61. % 这一层的上一层是经过pooling后的层,包含有inputmaps个特征map。每个特征map的大小是mapsize。 62. % 所以,该层的神经元个数是 inputmaps * 63. % prod: Product of elements. 64. % For vectors, prod(X) is the product of the elements of X 65. % 在这里 mapsize = 特征map的行数 特征map的列数,所以prod后就是 特征map的行*列 66

16、. fvnum = prod(mapsize) * inputmaps; 67. % onum 是标签的个数,也就是输出层神经元的个数。你要分多少个类,自然就有多少个输出神经元 68. onum = size(y, 1); 69. 70. % 这里是最后一层神经网络的设定 71. % ffb 是输出层每个神经元对应的基biases 72. net.ffb = zeros(onum, 1); 73. % ffW 输出层前一层 与 输出层 连接的权值,这两层之间是全连接的 74. net.ffW = (rand(onum, fvnum) - 0.5) * 2 * sqrt(6 / (onum +

17、fvnum); 75. end cnntrain.m plain view plain copy 1. function net = cnntrain(net, x, y, opts) 2. m = size(x, 3); % m 保存的是 训练样本个数 3. numbatches = m / opts.batchsize; 4. % rem: Remainder after division. rem(x,y) is x - n.*y 相当于求余 5. % rem(numbatches, 1) 就相当于取其小数部分,如果为0,就是整数 6. if rem(numbatches, 1) = 0

18、 7. error(numbatches not integer); 8. end 9. 10. net.rL = ; 11. for i = 1 : opts.numepochs 12. % disp(X) 打印数组元素。如果X是个字符串,那就打印这个字符串 13. disp(epoch num2str(i) / num2str(opts.numepochs); 14. % tic 和 toc 是用来计时的,计算这两条语句之间所耗的时间 15. tic; 16. % P = randperm(N) 返回1, N之间所有整数的一个随机的序列,例如 17. % randperm(6) 可能会返回

19、 2 4 5 6 1 3 18. % 这样就相当于把原来的样本排列打乱,再挑出一些样本来训练 19. kk = randperm(m); 20. for l = 1 : numbatches 21. % 取出打乱顺序后的batchsize个样本和对应的标签 22. batch_x = x(:, :, kk(l - 1) * opts.batchsize + 1 : l * opts.batchsize); 23. batch_y = y(:, kk(l - 1) * opts.batchsize + 1 : l * opts.batchsize); 24. 25. % 在当前的网络权值和网络输

20、入下计算网络的输出 26. net = cnnff(net, batch_x); % Feedforward 27. % 得到上面的网络输出后,通过对应的样本标签用bp算法来得到误差对网络权值 28. %的导数 29. net = cnnbp(net, batch_y); % Backpropagation 30. % 得到误差对权值的导数后,就通过权值更新方法去更新权值 31. net = cnnapplygrads(net, opts); 32. if isempty(net.rL) 33. net.rL(1) = net.L; % 代价函数值,也就是误差值 34. end 35. net

21、.rL(end + 1) = 0.99 * net.rL(end) + 0.01 * net.L; % 保存历史的误差值,以便画图分析 36. end 37. toc; 38. end 39. 40. end cnnff.m plain view plain copy 1. function net = cnnff(net, x) 2. n = numel(net.layers); % 层数 3. net.layers1.a1 = x; % 网络的第一层就是输入,但这里的输入包含了多个训练图像 4. inputmaps = 1; % 输入层只有一个特征map,也就是原始的输入图像 5. 6.

22、for l = 2 : n % for each layer 7. if strcmp(net.layersl.type, c) % 卷积层 8. % !below can probably be handled by insane matrix operations 9. % 对每一个输入map,或者说我们需要用outputmaps个不同的卷积核去卷积图像 10. for j = 1 : net.layersl.outputmaps % for each output map 11. % create temp output map 12. % 对上一层的每一张特征map,卷积后的特征map

23、的大小就是 13. % * 14. % 对于这里的层,因为每层都包含多张特征map,对应的索引保存在每层map的第三维 15. % 所以,这里的z保存的就是该层中所有的特征map了 16. z = zeros(size(net.layersl - 1.a1) - net.layersl.kernelsize - 1 net.layersl.kernelsize - 1 0); 17. for i = 1 : inputmaps % for each input map 18. % convolve with corresponding kernel and add to temp output

24、 map 19. % 将上一层的每一个特征map与该层的卷积核进行卷积 20. % 然后将对上一层特征map的所有结果加起来。也就是说,当前层的一张特征map,是 21. % 用一种卷积核去卷积上一层中所有的特征map,然后所有特征map对应位置的卷积值的和 22. % 另外,有些论文或者实际应用中,并不是与全部的特征map链接的,有可能只与其中的某几个连接 23. z = z + convn(net.layersl - 1.ai, net.layersl.kij, valid); 24. end 25. % add bias, pass through nonlinearity 26. %

25、加上对应位置的基b,然后再用sigmoid函数算出特征map中每个位置的激活值,作为该层输出特征map 27. net.layersl.aj = sigm(z + net.layersl.bj); 28. end 29. % set number of input maps to this layers number of outputmaps 30. inputmaps = net.layersl.outputmaps; 31. elseif strcmp(net.layersl.type, s) % 下采样层 32. % downsample 33. for j = 1 : inputma

26、ps 34. % ! replace with variable 35. % 例如我们要在scale=2的域上面执行mean pooling,那么可以卷积大小为2*2,每个元素都是1/4的卷积核 36. z = convn(net.layersl - 1.aj, ones(net.layersl.scale) / (net.layersl.scale 2), valid); 37. % 因为convn函数的默认卷积步长为1,而pooling操作的域是没有重叠的,所以对于上面的卷积结果 38. % 最终pooling的结果需要从上面得到的卷积结果中以scale=2为步长,跳着把mean pool

27、ing的值读出来 39. net.layersl.aj = z(1 : net.layersl.scale : end, 1 : net.layersl.scale : end, :); 40. end 41. end 42. end 43. 44. % concatenate all end layer feature maps into vector 45. % 把最后一层得到的特征map拉成一条向量,作为最终提取到的特征向量 46. net.fv = ; 47. for j = 1 : numel(net.layersn.a) % 最后一层的特征map的个数 48. sa = size(

28、net.layersn.aj); % 第j个特征map的大小 49. % 将所有的特征map拉成一条列向量。还有一维就是对应的样本索引。每个样本一列,每列为对应的特征向量 50. net.fv = net.fv; reshape(net.layersn.aj, sa(1) * sa(2), sa(3); 51. end 52. % feedforward into output perceptrons 53. % 计算网络的最终输出值。sigmoid(W*X + b),注意是同时计算了batchsize个样本的输出值 54. net.o = sigm(net.ffW * net.fv + re

29、pmat(net.ffb, 1, size(net.fv, 2); 55. 56. end cnnbp.m plain view plain copy 1. function net = cnnbp(net, y) 2. n = numel(net.layers); % 网络层数 3. 4. % error 5. net.e = net.o - y; 6. % loss function 7. % 代价函数是 均方误差 8. net.L = 1/2* sum(net.e(:) . 2) / size(net.e, 2); 9. 10. % backprop deltas 11. % 这里可以参

30、考 UFLDL 的 反向传导算法 的说明 12. % 输出层的 灵敏度 或者 残差 13. net.od = net.e .* (net.o .* (1 - net.o); % output delta 14. % 残差 反向传播回 前一层 15. net.fvd = (net.ffW * net.od); % feature vector delta 16. if strcmp(net.layersn.type, c) % only conv layers has sigm function 17. net.fvd = net.fvd .* (net.fv .* (1 - net.fv);

31、18. end 19. 20. % reshape feature vector deltas into output map style 21. sa = size(net.layersn.a1); % 最后一层特征map的大小。这里的最后一层都是指输出层的前一层 22. fvnum = sa(1) * sa(2); % 因为是将最后一层特征map拉成一条向量,所以对于一个样本来说,特征维数是这样 23. for j = 1 : numel(net.layersn.a) % 最后一层的特征map的个数 24. % 在fvd里面保存的是所有样本的特征向量,所以这里需要重新 25. % 变换回来

32、特征map的形式。d 保存的是 delta,也就是 灵敏度 或者 残差 26. net.layersn.dj = reshape(net.fvd(j - 1) * fvnum + 1) : j * fvnum, :), sa(1), sa(2), sa(3); 27. end 28. 29. % 对于 输出层前面的层 30. for l = (n - 1) : -1 : 1 31. if strcmp(net.layersl.type, c) 32. for j = 1 : numel(net.layersl.a) % 该层特征map的个数 33. % net.layersl.dj 保存的是

33、第l层 的 第j个 map 的 灵敏度map。 也就是每个神经元节点的delta的值 34. % expand的操作相当于对l+1层的灵敏度map进行上采样。然后前面的操作相当于对该层的输入a进行sigmoid求导 35. % 这条公式请参考 Notes on Convolutional Neural Networks 36. % for k = 1:size(net.layersl + 1.dj, 3) 37. % net.layersl.dj(:,:,k) = net.layersl.aj(:,:,k) .* (1 - net.layersl.aj(:,:,k) .* kron(net.l

34、ayersl + 1.dj(:,:,k), ones(net.layersl + 1.scale) / net.layersl + 1.scale 2; 38. % end 39. net.layersl.dj = net.layersl.aj .* (1 - net.layersl.aj) .* (expand(net.layersl + 1.dj, net.layersl + 1.scale net.layersl + 1.scale 1) / net.layersl + 1.scale 2); 40. end 41. elseif strcmp(net.layersl.type, s)

35、42. for i = 1 : numel(net.layersl.a) % 第l层特征map的个数 43. z = zeros(size(net.layersl.a1); 44. for j = 1 : numel(net.layersl + 1.a) % 第l+1层特征map的个数 45. z = z + convn(net.layersl + 1.dj, rot180(net.layersl + 1.kij), full); 46. end 47. net.layersl.di = z; 48. end 49. end 50. end 51. 52. % calc gradients 5

36、3. % 这里与 Notes on Convolutional Neural Networks 中不同,这里的 子采样 层没有参数,也没有 54. % 激活函数,所以在子采样层是没有需要求解的参数的 55. for l = 2 : n 56. if strcmp(net.layersl.type, c) 57. for j = 1 : numel(net.layersl.a) 58. for i = 1 : numel(net.layersl - 1.a) 59. % dk 保存的是 误差对卷积核 的导数 60. net.layersl.dkij = convn(flipall(net.lay

37、ersl - 1.ai), net.layersl.dj, valid) / size(net.layersl.dj, 3); 61. end 62. % db 保存的是 误差对于bias基 的导数 63. net.layersl.dbj = sum(net.layersl.dj(:) / size(net.layersl.dj, 3); 64. end 65. end 66. end 67. % 最后一层perceptron的gradient的计算 68. net.dffW = net.od * (net.fv) / size(net.od, 2); 69. net.dffb = mean(net.od, 2); 70. 71. function X = rot180(X) 72. X = flipdim(flipdim(X, 1), 2); 73. end 74. end cnnapplygrads.m plain view plain copy 1. 2. 3. 4. 5. 6. function net = cnnapplygrads(net, opts) for l = 2 : numel(net.layers) if strcmp(net.layersl.type, c) for j = 1 : n

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号