没有合适的资源?快使用搜索试试~ 我知道了~
Project5-goodexamples-2021fall-by廖铭骞1
需积分: 0 0 下载量 134 浏览量
2022-08-03
17:45:39
上传
评论
收藏 2.14MB PDF 举报
温馨提示
试读
35页
Project5-goodexamples-2021fall-by廖铭骞1
资源详情
资源评论
资源推荐
CS205 C/ C++ Programming - Project5
name: 廖铭骞
SID: 12012919
CS205 C/ C++ Programming - Project5
name: 廖铭骞
SID: 12012919
Part1 - Analysis
CNN基本组成
张量(tensor)
神经元和神经层(neuron and layer)
核权重以及核偏差(kernel weights and biases)
CNN层次结构
输入层(Input Layer)
卷积层(Convolutional Layers)
激活层(Activative Layer)
池化层(Pooling Layers)
扁平层(Flatten Layer)
图像读入与转换
卷积层分析与实现
单通道单步长卷积运算分析
单通道多步长卷积运算分析
多通道卷积运算分析
减少循环中计算量的优化分析
传入参数合法性检查
Relu 激活函数分析与实现
池化分析与实现
全连接分析与实现
Softmax 函数分析与实现
CNN层级结构实现
计时的方式分析
比较时间差距使用的方法
矩阵乘法卷积优化
CNN程序通用性与鲁棒性分析
通用性
鲁棒性
CMakeLists.txt 的编写以及分析
Part2 - Code
图片的读取与转换
卷积层次运算与计时
朴素卷积实现
矩阵乘法优化卷积层实现
Relu激活函数
池化函数实现
全连接层实现
Softmax 归一化实现
Matrix.hpp 中对于矩阵乘法的优化实现
Part3 - Result and Verification
在循环内部调用获取像素方法以及将矩阵数组开放程度提高效率的对比与分析
将矩阵类成员的类型从 int 更改为 size_t 的对比分析
使用OpenBLAS对矩阵运算进行加速的效率对比与分析
使用并行计算(openmp)对加速卷积运算的对比与分析
对Relu层的优化
不同方式实现卷积的效率对比以及分析
X86和ARM架构下卷积运算效率对比分析
结果正确性检验
卷积朴素实现与矩阵乘法卷积实现的各层卷积对比结果
两种实现对不同图片的识别正确性检验
Part4 - Difficulties and Solutions
卷积出现nan问题
矩阵存储方式对卷积运行时间的影响
Part5 -Thinking and Summary
Part1 - Analysis
CNN基本组成
张量(tensor)
在CNN当中,张量可以看作是一个三维的矩阵。对于张量来说,一般有三个维度,分别代表展张量的高
度H、宽度W以及通道数C,对于一张图片来说,通道数就代表着颜色通道的数量,对于彩色图像来说,
颜色通道数为3,而对于灰度图像来说,颜色通道为1。
神经元和神经层(neuron and layer)
对于CNN来说,神经元可以看做一个将多个输入转化为单个输出的函数,而神经层是一个执行相同操作
的神经元的集合
核权重以及核偏差(kernel weights and biases)
每一个神经元的核权重与偏差都是唯一的,并且会随着神经网络的训练而不断调整,而在图像处理当
中,不同的权重往往代表不同的特征,通过使用固定大小的权重矩阵与图像比对匹配可以提取出相应的
特征。
CNN层次结构
输入层(Input Layer)
在本程序当中,输入层即为三维的彩色图像,包含RGB三个颜色通道,是一个长度宽度分别为H和W组
成的三维像素矩阵
卷积层(Convolutional Layers)
卷积层通常用作对输入层输入数据进行特征提取,卷积操作的原理实际上就是对两个矩阵进行对应元素
点乘求和的过程,其中两个矩阵分别是输入的像素矩阵以及卷积核矩阵。
对于图像张量而言,卷积是在图像当中利用核/过滤器进行操作,每次卷积计算之后都会缩小图像的尺
寸。假设原图是 的输入矩阵,核为 ,则进行卷积运算之后,得到的矩阵大小为
,为了使得卷积之后图像张量的尺寸能按照目标尺寸进行转化以及减少图
像边缘信息的损失,可以在原图像张量周围填充像素点,填充数量p满足关系 ,解
得 ,而核通常为奇数,便于找到核所在的位置,并且p可以被整除得到。假设卷积步长是 ,则
卷积后的矩阵大小为
激活层(Activative Layer)
激活层主要由激活函数组成,一般是在卷积层的输出结果矩阵之上使用一个非线性的激活函数。
如果在输出结果矩阵之上是使用的是线性的函数,那么,无论卷积神经网络有多少层,输出的结果矩阵
永远是输入矩阵的线性组合,由于数据大部分情况并不是线性可分的,所以使用线性函数进行训练效果
不佳,并且这样深度的CNN架构也会退化成单一的等效卷积层,这时候可以通过使用非线性激活函数进
行对数据的变换,使其接近线性可分。
在CNN当中通常使用ReLU激励函数来组成激活层,相比于其他的激励函数,ReLU在训练当中可以更快
地收敛、更有效率地梯度下降以及反向传播。
池化层(Pooling Layers)
池化层可以对输入矩阵的特征进行筛选,提取区域内最具代表性的特征,能够有效地降低输出矩阵的大
小,进而减少网络的空间范围,从而减少神经网络的参数和整体计算,在本程序的实现当中,使用最大
化池化方法,通过提取出一块区域中最大的值来代表这一块区域,通过减少不必要的参数来提升神经网
络计算的效率
扁平层(Flatten Layer)
在神经网络的最后是展平层,该层将神经网络中的输出三维矩阵转换成一维向量,通过将之前网络卷积
层从输入张量(图像)提取的特征通过使用 softmax 函数进行分类。
图像读入与转换
通过使用 OpenCV 中的 imread 方法将一个 128*128 的彩色图像进行读入。
由于通过文件读取图像的时候可能由于文件缺失或是权限不允许等原因导致读取失败,此时如果程序继
续运行就会导致一些列危险的错误,所以有必要在读取文件之后对读取是否成功做检查,即判断
Mat::data 是否为空指针,如果是空指针,则报错并退出程序。
为了确认读入图像的通道数以及长度和宽度,使用 channel() 以及 size() 方法获取图像的相关信息。
如果读入图像的通道数,长度或者宽度不符合条件,则会导致后续运算出现错误,所以为了保证程序运
算的正确性,同样需要进行相关信息的检查。
同时,为了便于之后的一系列操作,需要将字符类型矩阵 Mat 使用 project4 中实现的多通道矩阵类型
进行存储,由于在OpenCV当中的通道顺序是 GBR ,而需要进行运算的顺序是 RGB ,所以需要将 Mat 当
中的信息转存到 Matrix 类当中,并将相应的元素进行赋值。
相关代码如下所示:
卷积层分析与实现
将图像张量信息转化正确之后,下面进行卷积层的运算。
下图形象地展示了卷积层的实现原理,即通过将原图像的一块区域分别与滤波器 filter (一个带有不
同权重值的矩阵)做内积后等到新的二维数据,作为新的矩阵。(如下图所示)
单通道单步长卷积运算分析
首先考虑最基础的情况,先通过两层循环定位到图像矩阵的起始运算点
,再通过两层循环遍历滤波器的每一个点
,分别进行点积运算,将对于同一个运算点 的点积
结果累加作为输出矩阵对应位置的值。
对应进行卷积区域的左上角的点
对应卷积核左上角的点
Mat img = imread("face.jpg");
if(!img.data){//读入图片失败的检查
cerr <<"Error in file " << __FILE__ <<" while loading image in line " <<
__LINE__ << endl;
exit(EXIT_FAILURE);
}else{
cout << "Successfully loading image" << endl;
}
if(img.rows != ROW || img.cols != COL || img.channels() != CHANNEL){//确保矩阵
规模正确的参数检查
cerr << "Image Size Mismatch in File " << __FILE__ << " in line " <<
__LINE__<< endl;
exit(EXIT_FAILURE);
}
Matrix<float> mat(ROW, COL, CHANNEL);//创建行数、列数均为128,通道数为3的浮点数矩阵
//将BRG通道转换存储为RBG通道
for(int r = 0; r < ROW; r++){
for(int c = 0; c < COL; c++){
mat(r, c, 2) = img.at<Vec3b>(r, c)[1] / 255.0;//规范化//G
mat(r, c, 3) = img.at<Vec3b>(r, c)[0] / 255.0;//B
mat(r, c, 1) = img.at<Vec3b>(r, c)[2] / 255.0;//R
}
}
为了保持矩阵运算前后的规模不发生改变,会采取在矩阵周围填充 0像素点 的操作,对于有填充的情
况,进行点积运算可以直接忽略填充 0像素点 的位置,即在运算前先判断运算区域点是否在原矩阵的有
效运算范围内,如果在,则进行点积运算,否则不进行相关运算,即默认将滤波器对应元素乘以0。
单通道多步长卷积运算分析
进一步地,分析步长不为 1 的情况,此时,矩阵起始运算点 的移动并不是逐个移动遍历了,
而是每次加上步长的"跳步"遍历,设步长为 ,则 对应结果矩阵的位置为
。而点积运算依然与基础情况一样,经过验证,通过上述公式对应到结果
矩阵的点恰好是连续的,且对应的值也是正确的。
多通道卷积运算分析
在多通道矩阵卷积运算下,输出通道的每一个结果矩阵都是所有传入通道矩阵与对应卷积核的点积结果
矩阵累加并在相应区域加上偏置(bias)的结果。
所以在最外层的循环当中循环的是输出矩阵的对应通道,之后对传入通道的每一个通道的矩阵都进行点
积运算,并将点积运算之后得到的对应点累加之后的结果加上偏置(bias)之后作为输出通道矩阵的对
应元素。
减少循环中计算量的优化分析
由于朴素方法当中实现卷积运算需要的循环层数非常多,则在循环当中的计算会被重复很多次,带来的
开销是很大的,可以事先将对应的变量在循环外部先进行计算,这样可以有效减少在循环当中的计算以
及函数的调用,提升效率。如可以将获取矩阵行数、列数以及通道数的函数调用在所有循环外层都事先
进行调用获取相应值;
通过分析循环内部的冗杂计算,发现在使用给定的 weight 数组当中寻找对应通道的权重时,在寻找的
时候会使用到最外两层的输出通道数以及输入通道数,如下列代码所示:
由于 out_ch*(in_channel*3*3) + in_ch*(3*3) 在最外层已经计算得到,如果依然放在最内层就会导
致冗杂的计算被重复了很多次,造成效率的降低,所以可以在最外的两层循环就使用变量将其计算并存
储。
ans += mat(img_height + ker_height - padding, img_width + ker_width - padding,
in_ch + 1) *
conv.p_weight[out_ch*(in_channel*3*3) + in_ch*
(3*3) + ker_height * ker_width];
for(int out_ch = 0; out_ch < out_channel; out_ch++){//输出通道数量
//...
for(int in_ch = 0; in_ch < in_channel; in_ch++){//传入通道数量
//...
int conv_move = out_ch * (in_channel * 3 * 3) + in_ch * (3 * 3);
//for(){}...
ans += mat(img_height + ker_height - padding, img_width +
ker_width - padding, in_ch + 1) *
conv.p_weight[conv_move + ker_height *
ker_width];
剩余34页未读,继续阅读
牛站长
- 粉丝: 26
- 资源: 299
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0