吴恩达机器学习笔记(4)神经网络算法

这一部分是对吴恩达机器学习神经网络算法部分内容的总结,主要分为以下几个部分

1.数据绘制
2.前馈神经网络计算
3.反馈神经网络算法详解
4.可视化重要数据

首先神经网络算法的过程图示如下在这里插入图片描述在神经网络中,每一层的theta矩阵的规模是(后一层的激活项数目,前一层激活项数目+1)
在层数比较少的情况下,激活函数可以直接采用小数形式
神经网络的输出仍然是对每一个先验假设的判断,最终的选择概率最高的作为决策

1.数据绘制

代码如下

input_layer_size  = 400;  %输入变量规模
hidden_layer_size = 25;   %隐藏层数目
num_labels = 10;        %输出种类                            
load('ex3data1.mat'); %读取数据
m = size(X, 1); %样本容量
sel = randperm(size(X, 1));%随机排序
sel = sel(1:100);%100displayData(X(sel, :));%这个函数的用法详见ex3
2.前馈神经网络计算

这里先不讨论theta是怎么计算出来的,本小节讨论已知theta的情况下,如何编写程序进行前馈神经网络计算

load('ex3weights.mat');%加载已经算出来的theta
pred = predict(Theta1, Theta2, X);%返回的是一个样本容量那么长的列向量,代表每一次测试的决策值

现讨论函数predict的代码实现

function p = predict(Theta1, Theta2, X)
m = size(X, 1); %样本容量
num_labels = size(Theta2, 1);%先验决策值数目
p = zeros(size(X, 1), 1);
X = [ones(m, 1) X];%添加x0到第一层,即输入层
z2=X*Theta1';
a2=sigmoid(z2);%与第一层的theta进行计算,得到第二层的激活项
a2=[ones(m, 1) a2];
%在第二层继续添加ones向量,注意,神经网络中不管怎样乘,所得到的激活项也好,输出也好,行的数目都是不变的
%列的数目最终会变成先验决策假设值的数目,然后再通过max函数取行最大值作为决策结果
z3=a2*Theta2';
a3=sigmoid(z3);%与第二层的theta进行计算,得到输出项
[a,p]=max(a3,[],2);%这个格式中‘2’代表行最大值,‘1’代表列最大值
end

通过上述的计算我们就得到了一个5000*1的列向量代表5000次测试的决策结果,现设计代码验证结果的正确性
代码如下:

fprintf('\nTraining Set Accuracy: %f\n', mean(double(pred == y)) * 100);
%定量计算算法精确度
rp = randperm(m);%m为样本容量
%接下来设计算法直观地验证算法正确性
for i = 1:m 
    fprintf('\nDisplaying Example Image\n');%一个label
    displayData(X(rp(i), :));
    %只随机打印一个图片
    pred = predict(Theta1, Theta2, X(rp(i),:));
    %只决策一个数据,得到一个整数
    fprintf('\nNeural Network Prediction: %d (digit %d)\n', pred, mod(pred, 10));
    %mod为取余运算,可以把输出10转换成0,而其他不变
    s = input('Paused - press enter to continue, q to exit:','s');
    if s == 'q'
      break
    end
    %设置退出因子
end
3.反馈神经网络算法详解

这一部分非常繁琐和难以理解,现分以下几个部分

a.代价函数的计算

在计算代价函数之前,首先现说明一个矩阵拉长与展开的思想,因为在Matlab里面很多时候要将数据带进函数中进行计算,但是一个维数乱七八糟的矩阵直接带进函数会导致很多问题,所以一般的做法是先将要输入的矩阵拉长成一个很长的矩阵,进入函数之后再通过reshape函数将其还原从而完成矩阵在函数间的传递
拉长代码如下

n_params = [Theta1(:) ; Theta2(:)]; %拉长代码
%其中Theta1(:)这种代码就是把一个任意维度的矩阵拉长成一个超长的矩阵

还原代码如下

Theta1 = reshape(nn_params(1:hidden_layer_size * (input_layer_size + 1)), ...
                 hidden_layer_size, (input_layer_size + 1));
%逐一观察这三个参数
%第一个参数的意思是从nn_params也就是拉长的矩阵中取从1到hidden_layer_size * (input_layer_size + 1)个元素
%然后展开成hidden_layer_size * (input_layer_size + 1)的矩阵
%为啥要这样展开呢?因为在神经网络中,每一层的theta矩阵的规模是(后一层的激活项数目,前一层激活项数目+1%于是同理,theta2可以展开成
Theta2 = reshape(nn_params((1 + (hidden_layer_size * (input_layer_size + 1))):end), ...
                 num_labels, (hidden_layer_size + 1));

有了上述准备,就可以计算代价函数的了,当然,在多结果输出的情况下,要把y转换成0、1形式,即输出是决策值为1,其余假设值为0的列向量
在这里插入图片描述
代码如下:

Y = zeros(m, size(Theta2, 1));        
for i = 1:size(Theta2, 1)
    Y(find(y==i), i) = 1;
end
%这个代码有一个问题,就是它将结果转化成了行向量,一共5000行,每一行代表一个决策
%每一行代表一次实验结果

我们知道,在传统的Logistic回归中,其代价函数为:
在这里插入图片描述即m个实验,把每一次实验的label和h(x)带入公式,然后计算求和,再除以样本数,而后面的正则化θ,因为θ和测试次数无关而和输入的变量有关,所以假设有n个输入变量,就把n个θ全部平方再求和除以实验次数乘以正则化常数,注意不要正则化θ_0
那么在神经网络中输出有K个,就把K个中的每一个都当作Logistic回归算一遍再加起来,至于正则化,则是把每一层theta的第一列给去掉,其他的加起来平方再除以实验次数,公式如下:
在这里插入图片描述记住是每一层除了bias项的每一个θ,实现代码如下:
对了,神经网络中的预测函数可能并没有一个直观的表达式,我们只需要正向一层一层算下去即可,实现代码如下

% Setup some useful variables
m = size(X, 1);%样本数目        
J = 0;%初始化
Theta1_grad = zeros(size(Theta1));
Theta2_grad = zeros(size(Theta2));
%初始化梯度,以后再用
%前馈计算
a1 = [ones(m,1), X];        %1
z2 = a1*Theta1';       
a2 = [ones(m,1), sigmoid(z2)];
z3 = a2*Theta2';       
a3 = sigmoid(z3); %这个就是假设函数h(x)

有了假设函数就可以按照公式进行计算了

J = sum(sum(-(Y.*log(a3)+(1-Y).*log(1-a3))))/m;
%对J进行正则化
J = J + lambda/(2.0*m)* ...
    (sum(sum(Theta1(:,2:size(Theta1,2)).^2))+ ...
    sum(sum(Theta2(:,2:size(Theta2,2)).^2)));

这个正则化的代码就是把每一层θ的第一列去掉

b.随机初始化

如果初始化的θ是一个对称矩阵,那么就会产生高度冗余的情况,为了避免这种情况的发生,我们用一系列随机的很小的初始化theta代替0矩阵初始化
以下是随机初始化的代码:

function W = randInitializeWeights(L_in, L_out)
W = zeros(L_out, 1 + L_in);%L_out就是s_j+1,而L_in就是(s_j)+1,这就是一层theta的规模
epsilon_init = 0.1188;        %这是数字被我改过,计算方法一会儿说
W = rand(L_out, 1+L_in)*(2*epsilon_init)-epsilon_init;
%rand(a,b)函数能生成一个a*b规模的,01的随机矩阵,然后按照公式随机初始化
end

于是在主程序中随机初始化theta1和theta2,再把它们拉长便于进行后面的优化

initial_Theta1 = randInitializeWeights(input_layer_size, hidden_layer_size);
initial_Theta2 = randInitializeWeights(hidden_layer_size, num_labels);

ops,忘了介绍随机值的取法了
在这里插入图片描述

c.梯度计算

这一步是通过delta下降算法求出其梯度
值得注意的是,这个算法小delta的计算中的theta也是没有theta_0的,即没有第一列
我们一步一步展现它的实现过程
首先是算出每一个z和a,即激活函数,最后一层激活函数就是假设函数

%计算各层的z(x)
a1 = [ones(m,1), X];        
z2 = a1*Theta1';       
a2 = [ones(m,1), sigmoid(z2)];
z3 = a2*Theta2';      
a3 = sigmoid(z3);

然后根据公式计算每一个小delta
这里仅讨论其代码实现:
本层的小delta和后一层的小delta以及本层的g’(z)有关系

delta3 = a3 - Y;
delta2 = delta3*Theta2(:,2:end).*sigmoidGradient(z2);

然后根据公式算出大delta
本层的大delta和后一层的小delta以及本层的激活项a有关

DELTA1 = delta2'*a1;
DELTA2 = delta3'*a2;

计算梯度

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);
d.梯度检验

有时候,我们用解析方法弄出一个梯度之后,要用数值方法再算一遍,若解析解和数值解差球不多,则说明我们的梯度是正确的,这就是梯度检验方法,这种方法的具体公式见笔记
在这里插入图片描述
求每一个theta的偏导数与解析解进行对比
在这里插入图片描述
当然,这种方法仅检验梯度的正确性,所以可以弄一个小神经网络进行检验,实现代码如下

function checkNNGradients(lambda)
if ~exist('lambda', 'var') || isempty(lambda)
    lambda = 0;
end
%如果没有输入lambda那么默认不进行正则化
input_layer_size = 3;
hidden_layer_size = 5;
num_labels = 3;
m = 5;
%弄一个样本为5,层数为3,隐藏层规模为5的小型神经网络
Theta1 = debugInitializeWeights(hidden_layer_size, input_layer_size);
Theta2 = debugInitializeWeights(num_labels, hidden_layer_size);
这个debugInitializeWeights()函数使用sin函数制造伪随机值,不重要,后面贴个代码自己研究
X  = debugInitializeWeights(m, input_layer_size - 1);
%各种初始化
y  = 1 + mod(1:m, num_labels)';
%瞎鸡儿弄个输出
nn_params = [Theta1(:) ; Theta2(:)];%拉长
% Short hand for cost function
costFunc = @(p) nnCostFunction(p, input_layer_size, hidden_layer_size, ...
                               num_labels, X, y, lambda);
%这种句柄形式就是只认为p是变量,其他的参数都从前面已经定义过的同名常量里去找
[cost, grad] = costFunc(nn_params);
%所以仅仅输入p就行了,p就是拉长的theta,这样就计算出了解析解,接下来计算数值解与其进行比较
numgrad = computeNumericalGradient(costFunc, nn_params);
这个函数后面讲解
disp([numgrad grad]);
%打印这两个结果,如果梯度正确那么这两列应该会十分接近,可能只有1e-9那么大的误差
end

补充上述函数所用到的两个函数

function W = debugInitializeWeights(fan_out, fan_in)
%DEBUGINITIALIZEWEIGHTS Initialize the weights of a layer with fan_in
%incoming connections and fan_out outgoing connections using a fixed
%strategy, this will help you later in debugging
%   W = DEBUGINITIALIZEWEIGHTS(fan_in, fan_out) initializes the weights 
%   of a layer with fan_in incoming connections and fan_out outgoing 
%   connections using a fix set of values
%
%   Note that W should be set to a matrix of size(1 + fan_in, fan_out) as
%   the first row of W handles the "bias" terms
%

% Set W to zeros
W = zeros(fan_out, 1 + fan_in);

% Initialize W using "sin", this ensures that W is always of the same
% values and will be useful for debugging
W = reshape(sin(1:numel(W)), size(W)) / 10;

% =========================================================================

end
function numgrad = computeNumericalGradient(J, theta)
numgrad = zeros(size(theta));%偏导数的规模应该和原函数一样
perturb = zeros(size(theta));
e = 1e-4;
for p = 1:numel(theta)  %numel函数就是返回一个数组的元素个数
    % 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

end

检查完梯度如果梯度是正确的,那就可以训练了
代码如下:

options = optimset('GradObj', 'on','MaxIter', 50);%基本的一些设置
lambda = 1; %设置正则化常数
costFunction = @(p) nnCostFunction(p, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, X, y, lambda);
                                   
%简化优化函数,这个句柄就是告诉计算机只有p是变量,其他的从已定义的同名常量里面找
[nn_params, cost] = fmincg(costFunction, initial_nn_params, options);
% 上面是训练,接下来把返回的拉长的theta还原回去
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));

这样我们就得到了训练之后的theta,这个东西往往被称作“final weight”

e.重要参数可视化

这个过程主要是为了直观地感受神经网络的计算过程,代码比较简单,直接贴了自己看

displayData(Theta1(:, 2:end));
%不能把bias也就是第一列也display,那是人为加的,弄进去就会发生错误
f.结果展示及算法精确度计算
pred = predict(Theta1, Theta2, X);

fprintf('\nTraining Set Accuracy: %f\n', mean(double(pred == y)) * 100);

其中predict函数为

function p = predict(Theta1, Theta2, X)
%PREDICT Predict the label of an input given a trained neural network
%   p = PREDICT(Theta1, Theta2, X) outputs the predicted label of X given the
%   trained weights of a neural network (Theta1, Theta2)

% Useful values
m = size(X, 1);
num_labels = size(Theta2, 1);

% You need to return the following variables correctly 
p = zeros(size(X, 1), 1);

h1 = sigmoid([ones(m, 1) X] * Theta1');
h2 = sigmoid([ones(m, 1) h1] * Theta2');
[dummy, p] = max(h2, [], 2);

% =========================================================================


end

至此 神经网络部分内容全部结束

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值