卷积神经网络Step by Step(四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wblgers1234/article/details/73277541

卷积神经网络Step by Step(四)

系列的第一篇博客对卷积神经网络的几个重要概念进行总结,现在就从代码的角度对”卷积”,”池化”,”反向传播”进行详细的分析。

代码是基于“UFLDL Tutorial”的excercise代码中的cnn部分实现的,系列博客的最后我会把代码的Github地址分享出来。

cnnCost

在这个函数cnnCost.m里,我们实现卷积神经网络的核心操作,包括接下来要介绍的四个步骤:前向传播,cost计算,反向传播,梯度计算。

0. 参数讲解

首先给出函数的定义,如下所示:
function [cost, grad, preds] = cnnCost(theta,images,labels,numClasses,…
filterDim,numFilters,poolDim,pred)

其中输入的参数分别有:
theta
-已经展开的参数向量
images
- 用于训练cnn的所有图像,存储在一个三维矩阵里
labels
- 对应于输入图像images的标签
numClasses
-要预测的分类数量
filterDim
-二维卷积特征提取权重的大小,实际为filterDim*filterDim
numFilters
-二维卷积特征提取滤波器的个数
poolDim
-二维均值池化的大小,实际为poolDim*poolDim
pred
-布尔变量,为true时表示只做前向传播,计算预测值
输出的参数分别有:
cost
-交叉熵cost
grad
-相对于theta的梯度值(偏微分),当pred为false时有效
preds
-预测得到的标签值,当pred为true时有效

1. 前向传播

在这一步中,我们将输入的图像数据通过卷积层(卷积特征提取+均值池化),然后通过softmax层得到输出值。

(1)卷积层
%% 卷积层代码
% 卷积层输出的数据维度
convDim = imageDim-filterDim+1; 
% 均值池化输出的数据维度
outputDim = (convDim)/poolDim; 

% 初始化卷积特征提取后的输出数据
activations = zeros(convDim,convDim,numFilters,numImages);

% 初始化池化后的输出数据
activationsPooled = zeros(outputDim,outputDim,numFilters,numImages);

% 卷积特征提取操作
activations = cnnConvolve(filterDim, numFilters, images, Wc, bc); 
% 均值池化操作
activationsPooled = cnnPool(poolDim, activations);

% 将4-D特征矩阵转换为2-D的,作为softmax层的输入,以图像数numImages为列进行转换
activationsPooled = reshape(activationsPooled,[],numImages);

在这里,我们调用之前讲过的两个函数cnnConvolve.m和cnnPool.m来完成卷积层的处理。其中Wc和bc分别是卷积层的权重系数和bias项。

(2)softmax层

对于softmax处理,我们根据以下的公式进行计算,得出每幅图像经过卷积层处理后得到的特征 x 对应于每个标签(总共有 K 个标签)的概率。

hθ(x)=P(y=1|x;θ)P(y=2|x;θ)...P(y=K|x;θ)=1Kj=1exp(θ(j)Tx)exp(θ(1)Tx)exp(θ(2)Tx)...exp(θ(K)Tx)

%% Softmax Layer
% 将池化后的特征转换为二维矩阵,即隐层特征数hiddenSize×训练图像个数numImages,对其进行softmax计算

% 初始化numClasses*numImages的矩阵,用来存储每一个图像对应于每一个标签的概率
probs = zeros(numClasses,numImages);

% 计算hypothesis-h(x)
M = Wd*activationsPooled+repmat(bd,[1,numImages]); 
M = exp(M);
probs = bsxfun(@rdivide, M, sum(M));

上面的代码片段中,Wd是softmax层的权重矩阵,bd是softmax层的bias项。

2. cost计算

在softmax层计算交叉熵请参考:

http://ufldl.stanford.edu/tutorial/supervised/SoftmaxRegression/

我们直接按照以下公式来计算:

J(θ)=1m[i=1mk=1K1{y(i)=k}logexp(θ(k)Tx(i))Kj=1exp(θ(j)Tx(i))]

%% Softmax层计算cost
% 根据训练样本的正确标记值和上一步得到的概率作为输入,计算交叉熵对象,并且保存在cost里
% cost初始化
cost = 0;

% 首先需要把labels弄成one-hot编码,即2-D矩阵numClasses×numImages
groundTruth = full(sparse(labels, 1:numImages, 1));

% 按照公式计算,矩阵操作进行求和
cost = -1./numImages*groundTruth(:)'*log(probs(:));

% 如果当前只是做预测,那么只返回预测的标签,在做test时会遇到
if pred
    [~,preds] = max(probs,[],1);
    preds = preds';
    grad = 0;
    return
end

请自行在MATLAB中尝试

groundTruth = full(sparse(labels, 1:numImages, 1));

可以发现在代码中经过one-shot编码的groundTruth就是 1{y(i)=k} ,将groundTruth和probs分别进行列向量变换(:),并用矩阵乘法代替求和,可以加快运算而且保持代码的简洁。

3. 反向传播

在详细讲解反向传播之前,我们先看一下整个CNN的网络结构,如下图所示:
这里写图片描述

代码中对反向传播中误差项delta的求解是按照UFLDL的’Multi-Layer Neural Network’中的公式实现的:

http://ufldl.stanford.edu/tutorial/supervised/MultiLayerNeuralNetworks/

对于输出层:

δ(nl)i=z(nl)i12||yhW,b(x)||2

其中 12||yhW,b(x)||2 代表当前样本x通过神经网络预测得到的结果和真实值之间的误差。
对于我们实现的卷积神经网络,输出层的activation函数是softmax,而单个样本的cost function选用的是交叉熵,如下公式:
J(θ)=k=1K1{y(i)=k}logexp(θ(k)Tx)Kj=1exp(θ(j)Tx)

而在上式中,上一层节点的权重和为 zk=θ(k)Tx ,我们假设当前样本对应的标签值为 k ,那么有以下的推导:
jj=k 时:
δ=J(θ)zk=[1logexp(θ(k)Tx)Kj=1exp(θ(j)Tx)]

jjk 时:
δ=J(θ)zjj=logexp(θ(jj)Tx)Kj=1exp(θ(j)Tx)

将上面那个公式合并:
δ=J(θ)zjj=[1{y=jj}logexp(θ(jj)Tx)Kj=1exp(θ(j)Tx)]

可以对应到代码中的这段:

% 输出层的误差项
delta_d = -(groundTruth-probs); 

对于前一个隐层,即均值池化后的网络层.假设这一层是第 l 曾网络,计算第 l 层网络中第 i 个节点的误差项:

δli=(j=1sWljiδ(l+1)j)f(zli)

由于这一层网络并没有做特征激活(activation),所以有下面的条件:

f(zli)=1
,那么可以简化误差项的计算:
δli=j=1sWljiδ(l+1)j

可以对应到这一段代码:

% 反向传播至softmax层的误差项
delta_s = Wd'*delta_d;
delta_s = reshape(delta_s,outputDim,outputDim,numFilters,numImages);

再往前,我们将池化层和卷积层当作一层,原因在于卷积层和池化层之间并没有权重矩阵。由于我们采用的是均值池化,因此对误差项做反向传播时可以用MATLAB自带的kron函数来实现上采样:

delta_pool = (1/poolDim^2) * kron(delta,ones(poolDim));

再往前传播,卷积层的激活函数是sigmoid,那么我们再次利用以下公式来计算卷积层输出的误差项:

δli=(j=1sWljiδ(l+1)j)f(zli)=(j=1sWljiδ(l+1)j)ali(1ali)

对应到这一段代码:

% 反向传播至卷积层的误差项
delta_c = zeros(convDim,convDim,numFilters,numImages);
for i=1:numImages
    for j=1:numFilters
        delta_c(:,:,j,i) = (1./poolDim^2)*kron(delta_s(:,:,j,i), ones(poolDim));
    end
end

delta_c = activations.*(1-activations).*delta_c;

下面给出整个反向传播的整体代码,可以结合着上面的讲解来看:

%% 反向传播
% 从输出层将误差反向传播至softmax层和卷积&池化层,在每一层保存对应的误差项,用于
% 计算梯度值,完成梯度下降法的迭代。

% 网络结构: images--> convolvedFeatures--> activationsPooled--> probs

% 输出层的误差项
delta_d = -(groundTruth-probs); 

% 反向传播至softmax层的误差项
delta_s = Wd'*delta_d;
delta_s = reshape(delta_s,outputDim,outputDim,numFilters,numImages);

% 反向传播至卷积层的误差项
delta_c = zeros(convDim,convDim,numFilters,numImages);
for i=1:numImages
    for j=1:numFilters
        delta_c(:,:,j,i) = (1./poolDim^2)*kron(delta_s(:,:,j,i), ones(poolDim));
    end
end

delta_c = activations.*(1-activations).*delta_c;

4. 梯度计算

softmax层的梯度计算还是按照UFLDL的教程:

http://ufldl.stanford.edu/tutorial/supervised/MultiLayerNeuralNetworks/

计算权重梯度值的公式为:

W(l)ijJ(W,b;x,y)=a(l)jδ(l+1)i

即连接第 l 层网络和第 l+1 层网络的权重的梯度和第 l+1 层的误差值以及第 l 层的激活值有关。

计算bias项梯度值的公式为:

b(l)iJ(W,b;x,y)=δ(l+1)i

从卷积神经网络的输出层往前看,连接均值池化后节点与softmax层的权重系数(Wd)与bias项(bd)的梯度值就按照上面两个公式计算:

% 用delta_d计算softmax层权重系数的梯度值
Wd_grad = (1./numImages)*delta_d*activationsPooled';
% 用delta_d计算softmax层bias项的梯度值,注意这里是要求和
bd_grad = (1./numImages)*sum(delta_d,2);

往前一层网络,连接输入层和卷积层的权重系数(Wc)和bias项(bc)的梯度计算和之前不一样,需要查看UFLDL关于卷积神经网络的这个教程:

http://ufldl.stanford.edu/tutorial/supervised/ConvolutionalNeuralNetwork/

W(l)kJ(W,b;x,y)=i=1m(a(l)i)rot90(δ(l+1)k,2)

b(l)kJ(W,b;x,y)=a,b(δ(l+1)k)a,b

其中 a(l) 指的是第 l 层的输入,对于卷积层来说,输入就是原始训练图像; (a(l)i)rot90(δ(l+1)k,2) 指的是”valid”卷积,下标 k 是指二维特征提取滤波器的下标。

% 用delta_c计算卷积层权重系数和bias项的的梯度值
for i=1:numFilters
    Wc_i = zeros(filterDim,filterDim);
    for j=1:numImages  
        Wc_i = Wc_i+conv2(squeeze(images(:,:,j)),rot90(squeeze(delta_c(:,:,i,j)),2),'valid');
    end

    Wc_grad(:,:,i) = (1./numImages)*Wc_i;

    bc_i = delta_c(:,:,i,:);
    bc_i = bc_i(:);
    bc_grad(i) = sum(bc_i)/numImages;
end

由于在计算Wc_grad时有一个二维卷积,我们将卷积层的误差项delta_c重新按照每个滤波器、每个输入图像来计算,可见代码中的两层循环。此外,二维卷积函数conv2的”valid”使用在第二讲中有详细解决,不再赘述。

猜你喜欢

转载自blog.csdn.net/wblgers1234/article/details/73277541