神经网络简介

| 研究学术  | 机器学习基础  神经网络  特征学习  回归  BP算法  Matlab 

简介

神经网络结构
图 1: 神经网络结构 [PNG]

神经网络是神经元分层级联构成的网络,除输入层外每个神经元对应一个计算模型。最左边和最右边的层分别称为输入(input)和输出(output)层,中间两层为隐藏层(hidden layer)。

当特征数目巨大时,简单的Logistic回归无法满足需求。神经网络用于解决复杂的非线性问题,可以看成是Logistic回归的组合,上图中每个橙色的神经元(除输入层之外)都对应一个Logistic方程。

对于分类问题,输入层输入原始数据,隐藏层的每个神经元可视为提取一种特征,输出层的每个神经元对应所属类别的概率(不是类别标签)。输入数据所属的类别是输出层概率最大神经元对应的类别。

神经网络通过前向传播计算给定输入对应的输出,通过误差反向传播估计权值矩阵。

前向传播计算

神经网络前向传播计算
图 2: 神经网络前向传播计算 [PNG]

神经网络前向传播,从输入到输出,逐层计算。上图所示[1, P. 23],假设权值矩阵$\Theta^{(l-1)}$已知,第$l$层可通过第$l-1$层和权值矩阵前向计算,

\begin{equation} \mathbf a^{(l)} = g\left(\boldsymbol\Theta^{(l-1)}\mathbf a^{(l-1)}\right), \label{eq:forward-propagation} \end{equation}

$g$是Logistic函数,每层额外增加了一个$a_0^{(l)}= 1$的偏移(bias),$\boldsymbol\Theta^{(l-1)}$的行数为第$l$层神经元个数,列数为第$l-1$层神经元个数加$1$。

输出层(第$L$层)神经元的输出$\mathbf a^{(L)}$确定输入特征所属的类别。

如果神经网络只有输入层和含一个神经元的输出层(上图去掉隐藏层只有输入和输出层),就相当于一个Logistic回归模型。

反向参数估计

神经网络通过反向传播估计权值矩阵$\boldsymbol\Theta$,参数估计仍然是最小化代价函数。通过BP算法(BackPropagation algorithm),输出层的误差向输入层逐层反向传播,利用梯度下降法,估计权值矩阵。

代价函数

神经网络的神经元是Logistic模型,存在和Logistic模型类似的代价函数

\begin{equation} \begin{aligned} J(\boldsymbol\Theta) = &-\frac{1}{m}\sum_{i=1}^{m}\sum_{k=1}^{K}\left(y_k^{(i)}\log \left(h_{\boldsymbol\Theta}\left(\mathbf x^{(i)} \right) \right)_k + \left(1 - y_k^{(i)}\right)\log\left(1 - \left(h_{\boldsymbol\Theta}\left(\mathbf x^{(i)} \right) \right)_k \right) \right) \\ &+\frac{\lambda}{2m}\sum_{l=1}^{L-1}\sum_{i=1}^{s_l}\sum_{j=1}^{s_{l+1}}\left(\Theta_{ji}^{(l)}\right)^2. \end{aligned} \label{eq:cf_nn} \end{equation}

$h_{\boldsymbol\Theta} (\mathbf x) \in \mathbb{R}^K$,$\left(h_{\boldsymbol\Theta} (\mathbf x)\right)_k = \mathbf a_k^{(L)} $是输出层第$k$个神经元的输出,可由前向传播公式\eqref{eq:forward-propagation}计算;$s_l$表示第$l$层神经元的个数(不含bias unit);神经网络有$L$层,$m$个样本,$K$个输出。

如果代价函数\eqref{eq:cf_nn}是非凸(non-convex)函数1,理论上可能会陷入局部极值,事实上,即使不能保证取得全局极值,梯度下降法也能很好的最小化代价函数,使得神经网络工作良好[2, P. 30]

对比正则化Logistic回归的代价函数,由于神经网络有$K$个输出,前半部分相当于$K$个Logistic回归的代价函数之和,后半部分是非bias神经元洗漱组成的正则化项,$a_0^{(l)} = 1$对应的系数$\Theta_{j0}^{(l)}$和Logistic回归一样,不包含在正则化系数中。

参数估计

通过最小化代价函数$\min_{\boldsymbol\Theta} J(\boldsymbol\Theta)$估计模型的所有参数矩阵$\boldsymbol\Theta^{(l)}$,采用梯度下降法时需计算代价函数$J(\boldsymbol\Theta)$及其梯度$D_{ij}^{(l)}$, \begin{equation*} D_{ij}^{(l)} = \frac{\partial J(\boldsymbol\Theta)}{\partial\Theta_{ij}^{(l)}}. \end{equation*}

第$l$层的误差记为$\mathbf\delta^{(l)}$,$\delta_j^{(l)}$表示第$l$层的第$j$个神经元的误差,对于$a_0^{(l)} = 1$的bias节点$\delta_0^{(l)}=0$。输出层($l=L$)的误差为 \begin{equation} \boldsymbol\delta^{(L)} = \mathbf a^{(L)} - \mathbf y, \label{eq:error-bp-1} \end{equation} 对于隐藏层$(l = L-1, L-2, \ldots, 2)$,误差通过权值矩阵$\boldsymbol\Theta^{(l)}$从输出层向各隐藏层反向传播, \begin{equation*} \boldsymbol\delta^{(l)} = \left(\boldsymbol\Theta^{(l)}\right)^T\boldsymbol\delta^{(l+1)} .* g’\left(\mathbf z^{(l)}\right), \end{equation*}

其中,.*借用了Matlab中对应元素相乘的运算符,$\mathbf z^{(l)} = \boldsymbol\Theta^{(l-1)}\mathbf a^{(l-1)}$,$\mathbf a^{(l)} = g\left(\mathbf z^{(l)}\right)$,$g’\left(\mathbf z^{(l)}\right) = \mathbf a^{(l)} .* \left(\mathbf 1 - \mathbf a^{(l)}\right)$2,于是可得误差回传计算公式

\begin{equation} \boldsymbol\delta^{(l)} = \left(\boldsymbol\Theta^{(l)}\right)^T\boldsymbol\delta^{(l+1)} .* \mathbf a^{(l)} .* \left(\mathbf 1 - \mathbf a^{(l)}\right). \label{eq:error-bp-2} \end{equation}

Coursera的课程Wiki和Michael Nielsen[3]的第二章给出了BP算法的推导过程3

BP算法之梯度计算


训练集:$\left\{\left(\mathbf x^{(1)}, \mathbf y^{(1)}\right),\ldots,\left(\mathbf x^{(m)}, \mathbf y^{(m)}\right)\right\}$。

初始化:

  1. $\Delta_{ij}^{(l)} = 0$;
  2. 随机数初始化$\Theta_{ij}^{(l)}$为$[-\epsilon, \epsilon]$的值,$\epsilon=\frac{\sqrt{6}}{\sqrt{L_{in} + L_{out}}}$由神经元数目确定,其中,$L_{in} = s_l$,$L_{out}=s_{l+1}$。4

for $k=1$ to $m$ {

  1. 初始化输入层$\mathbf a^{(1)} = \mathbf x^{(k)}$,利用前向传播\eqref{eq:forward-propagation},计算各层神经元$\mathbf a^{(l)}~~(l = 2,\ldots, L)$;
  2. 利用反向传播\eqref{eq:error-bp-1}和\eqref{eq:error-bp-2},计算各层误差$\boldsymbol \delta^{(l)}~~(l = 2,\ldots, L)$;
  3. 更新$\Delta_{ij}^{(l)}~~(l = 1,\ldots, L - 1)$,$\Delta_{ij}^{(l)} := \Delta_{ij}^{(l)} + a_j^{(l)}\delta_i^{(l+1)}$5($\mathbf a^{(l)}$也须补$a_0^{(l)}=1$);
  4. 计算梯度$D_{ij}^{(l)}~~(l = 1,\ldots, L - 1)$, \begin{equation} D_{ij}^{(l)} := \left\{ \begin{aligned} & \frac{1}{m}\left(\Delta_{ij}^{(l)} + \lambda\Theta_{ij}^{(l)}\right) & (j \neq 0) \\ & \frac{1}{m}\Delta_{ij}^{(l)} & (j = 0) \end{aligned} \right. . \label{eq:gradient-cost-f} \end{equation} }

实现细节

矩阵展成(unroll)向量:

  1. thetaVec = [Theta1(:); Theta2(:); Theta3(:)]
  2. 将向量化的待估参数作为costFunction的参数;
  3. costFunction内部再将向量还原为矩阵计算梯度;
  4. 梯度向量化输出grad = [D1(:); D2(:); D3(:)]

梯度检查(gradient checking):

梯度计算是梯度下降法的关键。梯度检查用以验证推导的梯度计算公式及其代码实现是否正确。通过比较代价函数[比如\eqref{eq:gradient-cost-f}]梯度公式输出结果与数值方法计算代价函数的导数是否一致,判断梯度公式是否正确。梯度检测方法也可推广到其它需要梯度计算的地方,比如Logistic回归的代价函数的参数估计。梯度检查应当在训练神经网络之前,可以通过构造一个新的较小规模的神经网络进行检验;若每次训练都检测梯度,速度很慢。具体实现方法可以参考[2]课程的编程习题。

% 数值方法计算导数
numgrad = zeros(size(theta));
perturb = zeros(size(theta));
e = 1e-4;
for p = 1:numel(theta)
    % Set perturbation vector
    perturb(p) = e;
    loss1 = J(theta - perturb);
    loss2 = J(theta + perturb);
    % Compute Numerical Gradient
    numgrad(p) = (loss2 - loss1) / (2*e);
    perturb(p) = 0;
end

如果梯度计算公式正确,numgrad$\approx$grad,通过比较numgrad与BP算法所得grad的差距判断BP算法的代价函数及其优化算法是否有subtle bugs。

% If your backpropagation implementation is correct, then the relative difference will be small (less than 1e-9). 
diff = norm(numgrad-grad)/norm(numgrad+grad);

注意事项:

不可将$\Theta_{ij}^{(l)}$初始化为$0$,若初始化为$0$,每层的所有神经元都是一样的。随机数初始化$-\epsilon\leq\Theta_{ij}^{(l)}\leq\epsilon$,选择$\epsilon$的有效策略是根据每层神经元的数目取$\epsilon=\frac{\sqrt{6}}{\sqrt{L_{in} + L_{out}}}~(L_{in} = s_l,L_{out}=s_{l+1})$。

代价函数及其梯度计算

function [J, grad] = nnCostFunction(nn_params, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, ...
                                   X, y, lambda)
%NNCOSTFUNCTION Implements the neural network cost function for a two layer
%neural network which performs classification
%   [J, grad] = NNCOSTFUNCTON(nn_params, hidden_layer_size, num_labels, ...
%   X, y, lambda) computes the cost and gradient of the neural network. The
%   parameters for the neural network are "unrolled" into the vector
%   nn_params and need to be converted back into the weight matrices. 
% 
%   The returned parameter grad should be a "unrolled" vector of the
%   partial derivatives of the neural network.
%

% Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
% for our 2 layer neural network
Theta1 = reshape(nn_params(1:hidden_layer_size * (input_layer_size + 1)), ...
                 hidden_layer_size, (input_layer_size + 1));

Theta2 = reshape(nn_params((1 + (hidden_layer_size * (input_layer_size + 1))):end), ...
                 num_labels, (hidden_layer_size + 1));

% Setup some useful variables
m = size(X, 1);
      
% Feedforward Propagation
A1 = X;  % input layer, matrix size: 5000x400 (5000 samples and 400 features)
A2 = sigmoid([ones(m, 1), A1] * Theta1'); % hidden layer, matrix size: 5000x25
A3 = sigmoid([ones(m, 1), A2] * Theta2'); % output layer, matrix size: 5000x10

% Cost Function
% a tick to compute the cost
J_matrix = log(1 - A3); % set all cost
for i = 1 : m
	J_matrix(i, y(i)) = log(A3(i, y(i)));  % overwrite
end
J = -sum(J_matrix(:)) / m;
J = J + (lambda / (2 * m)) * ...
    (sum(sum(Theta1(:, 2:end) .^ 2)) + sum(sum(Theta2(:, 2:end) .^ 2))); % add regular term

% Label matrix
y_matrix = zeros(size(A3));
for i = 1 : m
	y_matrix(i, y(i)) = 1;
end

% BP error
Delta3 = A3 - y_matrix; % 5000x10
Delta2 = (Delta3 * Theta2(:, 2:end)) .* (A2 .* (1 - A2)); % 5000x25

% Gradient
DELTA1 = 0;
DELTA2 = 0;
Delta2 = Delta2';
Delta3 = Delta3';
for i = 1 : m
	DELTA1 = DELTA1 + Delta2(:, i) * [1 A1(i, :)];
	DELTA2 = DELTA2 + Delta3(:, i) * [1 A2(i, :)];
end

Theta1_grad = DELTA1 / m;
Theta2_grad = DELTA2 / m;

Theta1_grad(:, 2:end) = ...
    Theta1_grad(:, 2:end) + (lambda / m) * Theta1(:, 2:end);
Theta2_grad(:, 2:end) = ...
    Theta2_grad(:, 2:end) + (lambda / m) * Theta2(:, 2:end);

grad = [Theta1_grad(:) ; Theta2_grad(:)];

end

神经网络学到了什么?

待识别字符
图 3: 待识别字符 [PNG]

上图展示了部分待识别的字符,通过样本学习建立了3层神经网络的分类器。输入字符图片规格是$20 \times 20$,隐层神经元25个,25个神经元对应的参数向量可视化为下图。从可视化的参数可以看到,每个神经元和字符的笔画相似,输入的图片将激活符合“笔画”的神经元。估计神经网络参数可视为自动学习特征。

参数可视化
图 4: 参数可视化 [PNG]

应用

卡内基梅隆大学基于神经网络的自动驾驶系统[5],一些Matlab代码和数据还可以从这里找到。

参考资料

  1. [1]A. Ng, “Neural Networks: Representation.” Coursera, 2014. [Online]
  2. [2]A. Ng, “Neural Networks: Learning.” Coursera, 2014. [Online]
  3. [3]M. A. Nielsen, Neural Networks and Deep Learning. Determination Press, 2014. [Online]
  4. [4]A. Ng, “Programming Exercise 4: Neural Networks Learning.” Coursera, 2014. [Online]
  5. [5]D. A. Pomerleau, “ALVINN, an autonomous land vehicle in a neural network,” Carnegie Mellon University, 1989. [Online]

脚注

  1. 如果非凸函数,梯度下降法不能确定取得的是全局还是局部极值,可通过取不同初始值多次求解增强鲁棒性。 

  2. Sigmoid函数导数为$g(z)=g(z)(1-g(z))$。 

  3. 这两篇博客(12)也给出了BP算法的推导过程,但是采用了形如线性回归的代价函数。 

  4. 不可将$\Theta_{ij}^{(l)}$初始化为$0$。若初始化为$0$,每层的所有神经元都一样[2, Pp. 25-26],每层只能学习到一种特征。$\epsilon$的取值方案参考课程习题脚注[4, P. 7]课程Wiki。 

  5. 更新的向量形式$\boldsymbol\Delta^{(l)} := \boldsymbol\Delta^{(l)} + \boldsymbol\delta^{(l+1)}\left(\mathbf a^{(l)}\right)^T$。这一步是怎么来的?有何意义? 


打赏作者


上一篇:CSS Essential     下一篇:机器学习资源