全连接层是MLP的重要组成部分。其前向传播的计算过程为:
其中,
为全连接层第
层的输入,是一个
的矩阵,其中
和
分别为一个batch的样本个数和上一层的节点数;
和
为权重和偏置,其中权重为
的矩阵,偏置为一个
的向量,其中
为下一层的节点数。
对于第
层,其反向传播的过程分为求权重与偏置的偏导(误差)和求该层的误差,权重与偏置的偏导即求损失对其的梯度用以进行网络的参数更新,而第
层的误差是为了将误差传给前一层,以进行反向传播。
对于第
层,其权重与偏置的偏导:
其中,
为网络的总层数,
为第
层的输出,因而
为损失对第
层输出的误差。由于全连接网络的层与层之间是串联的结构,因此第
层的输出与第
层的输入相同,即
。
因此在反向传播中,对于第
层来说,只需要获取第
层传来的误差
,并计算当前层的权重与偏置的偏导即可获得该层参数的梯度了。
由于
,因此
。
下面是全连接层FullConnectionLayer类的头文件。
#ifndef FULLCONNECTION_LAYER_H_
#define FULLCONNECTION_LAYER_H_
#include "Operator.hpp"
namespace mario
{
typedef class FullConnectionLayer : public Operator
{
private:
matrix m_w;
matrix m_b;
matrix m_dw;
matrix m_db;
matrix m_dx;
matrix m_in;
matrix m_out;
public:
FullConnectionLayer();
FullConnectionLayer(const int &_lastNeuronNum, const int&_nextNeuronNum);
~FullConnectionLayer();
const matrix& getW() const;
const matrix& getB() const;
const matrix& forward(const matrix&_lastOut);
const matrix& backward(const matrix &_nextDerr);
void update(const double &_learningRate = 0.001);
}Fc;
}
#endif //FULLCONNECTION_LAYER_H_
FullConnectionLayer类的数据成员包括输入输出m_in、m_out,权重m_w、偏置m_b及其梯度m_dw、m_db,该层的误差m_x。成员函数包括前向传播forward(),反向传播求梯度backward(),更新权重与偏置update(),还可以查看权重和偏置getW()、getB()。
下面重点记录一下前向与反向传播的部分,首先是前向传播forward()。
const matrix& FullConnectionLayer::forward(const matrix&_lastOut)
{
if (m_in.cols() != _lastOut.cols())
{
cout << "Error in ullConnectionLayer::forward(): m_in.cols() != _lastOut.cols().n";
return matrix();
}
m_in.release();
m_out.release();
m_in = _lastOut;
m_out = m_in*m_w + m_b;
return m_out;
}
接下来是反向传播。
const matrix& FullConnectionLayer::backward(const matrix &_nextDerr)
{
if (m_in.rows() != _nextDerr.rows())
{
cerr << "Error in FullConnectionLayer::backward(): m_in.rows() != _nextDx.rows().n";
return matrix();
}
m_dw.release();
m_db.release();
m_dx.release();
m_dw = m_in.T()*_nextDerr / m_in.rows();
m_db = _nextDerr.meanByCol();
m_dx = _nextDerr*m_w.T();
return m_dx;
}
最后利用求得的梯度更新参数。
void FullConnectionLayer::update(const double &_learningRate)
{
m_w -= _learningRate*m_dw;
m_b -= _learningRate*m_db;
}
至此,FullConnectionLayer的主要成员函数记录完毕了 。
接下来是损失函数的介绍和如何利用这些数据和单个的层,连接成一整个网络,并进行前向与反向传播。