机器学习之三层前向神经网络(matlab仿真)
一、基本原理
三层前向神经网络应该是神经网络最原始、最简单的网络结构了,顾名思义,它只包含了三层结构——输入层、隐藏层、输出层。其基本原理很简单,主要是加权求和,再加个**函数,运算的知识点没有超过初中数学。
1.神经元
人工神经网络是收到人脑结构启发的一种算法。人脑神经元通过突触两两相连,将信息传递给下一个树突,树突将得到的数据经过处理(**),继续传递给下一个神经元。受到这样结构的启发,人们提出了人工神经元结构来模拟真实神经元的工作过程,如下图所示。
假设一个神经元前面连接了d个神经元,该神经元接受的输入是分别来自前一层神经元的输出x1…xd。每个输入对该神经元作用的大小不同,因此每个输入对应不同的权重w1…wd,经过加权求和,这个神经元所接受的输入总量为:w1*x1 + w2*x2 + … + wd*xd。当然,神经元也不会就将这个值全盘输出给下一层,而是有筛选性地输出,就像我们的大脑如果接受大量信息而不经过筛选,那迟早得累死。这个筛选的标准就是一个**函数,以前比较常用tanh、sigmoid函数,现在实际应用中ReLu更常用。这次仿真用的是sigmoid函数作为**,那就以这个函数为例吧。
sigmoid函数图像如上,中间原点对应值为0.5,两边无限趋近于0和1。sigmoid作为**函数,那么神经元输出的值区间是(0,1),当这个输出值很小,那我们就认为这个神经元没有被**。当这个输出值很大,接近于1时,我们就认为这个神经元被**了,可以愉快地传递信息了。
2.输入层
给定n个训练样本X,每个训练样本维度为d,对应标签T。举个实际的例子,用Matlab生成5类不同高斯分布的点,各两百个,每个点的维度为2,即横坐标和纵坐标。
那么,神经网络的输入层需要d+1个节点,多出来一个节点输入1作为样本的偏置。这里的x上标i表示第i个样本,下标j表示第j个节点。如下图所示,因为样本x的维度是2,分别输入每一个样本的每一维度的值,再加上一个偏置1.
3.隐藏层
二、仿真
本次仿真共1000个样本点,取600个作为训练集,400个作为验证集,共有5类样本。采用100样本的小批量下降方法,分别对不同学习速率和不同隐藏节点个数的训练准确率作了比较。结果显示节点个数为10、学习速率为0.01时效果较好。
采用随机梯度下降的方法,每一次的迭代速度快,但是收敛慢且震动大。采用批梯度下降的算法震动最小,但是迭代时间长,迭代1000次左右收敛。100样本小批量梯度下降震荡程度适中,收敛速度最快,差不多训练400次就收敛了,最高准确率在97%左右。下面分别是随机梯度、100小批量、批梯度下降运行结果对比。
三、Matlab代码
参考网上的代码,自己有做了进一步修改
Sigma = [1, 0; 0, 1];
mu1 = [1, -1];
x1 = mvnrnd(mu1, Sigma, 200);
mu2 = [5, -4];
x2 = mvnrnd(mu2, Sigma, 200);
mu3 = [1, 4];
x3 = mvnrnd(mu3, Sigma, 200);
mu4 = [6, 4];
x4 = mvnrnd(mu4, Sigma, 200);
mu5 = [7, 0.0];
x5 = mvnrnd(mu5, Sigma, 200);
%---------------------main---------------------%
%读入数据
X_train = [x1(1:120,:);x2(1:120,:);x3(1:120,:);x4(1:120,:);x5(1:120,:)];
X_test = [x1(121:200,:);x2(121:200,:);x3(121:200,:);x4(121:200,:);x5(121:200,:)];
t = zeros(600,5);
t(1:120,1) = 1;
t(121:240,2) = 1;
t(241:360,3) = 1;
t(361:480,4) = 1;
t(481:600,5) = 1;
tt = zeros(400,5);
tt(1:80, 1) = 1;
tt(81:160, 2) = 1;
tt(161:240, 3) = 1;
tt(241:320, 4) = 1;
tt(321:400, 5) = 1;
param.Nx = 600; %训练数
param.Nd = 3; %输入x的维度
param.Ny = 5;
param.Nh = 10; %隐藏节点个数
param.eta = 0.01; %权重更新率
param.theta = 1; %停止阈值
%初始化
Wh = 0.2*(rand(param.Nd,param.Nh)-0.5);
Wy = 0.2*(rand(param.Nh,param.Ny)-0.5);
w{1} = Wh;
w{2} = Wy;
param.batch_size = 100;
%[J, z, yh] = forward(X_train, w, t, param);
%批梯度下降
[ w1, count1, data1, acc] = batchBP( X_train, X_test, w, t, tt, param );
%单样本方式更新权重
%[w2, count2, data2] = singleBP( X_train, X_test, z, yh, w, t, tt, param );
%绘图
n1 = numel(find(data1 > 0));
data1 = data1(1:n1);
acc = acc(1:n1);
xx1 = 1:n1;
figure(1);
plot(xx1,data1);
xlabel('迭代次数')
ylabel('目标函数loss')
figure(2);
plot(xx1, acc);
xlabel('迭代次数')
ylabel('正确率')
function [J,z,yh] = forward(x, w, t, Nx)
%x为输入样本,w为网络权重,t为输出真值,param为网络参数
%J为损失函数,z为最后一层输出,yh为隐藏层输出
%Nh = param.Nh;
%Nx = param.Nx;
%Nd = param.Nd;
%Ny = param.Ny;
Wh = w{1};
Wy = w{2};
neth = x * Wh;
yh = tanh(neth);
netj = yh * Wy;
z = sigmoid(netj);
J = zeros(Nx,1);
for k = 1:Nx
J(k) = 0.5 * sum((z(k,:) - t(k,:)).^2);
% J(k) = 0;
% for i = 1:Ny
% J(k) = 0.5 * (z(k,i) - t(k,i))^2 + J(k);
% end
end
end
function [ w, q, data, acc] = batchBP( x, x_t, w, t, tt, param )
%BATCHBP 此处显示有关此函数的摘要
% 批量BP算法
Nh = param.Nh;
%Nx = param.Nx;
Nx = param.batch_size;
Nd = param.Nd;
Ny = param.Ny;
eta = param.eta;
theta = param.theta;
flag = 0;
% res = [0 0 0];
% resid = 1;
data = zeros(30000,1);
acc = zeros(30000,1);
q = 0; %迭代次数 q = count
while(flag == 0)
%根据次数划分每一次的训练数据
m = mod(q, 6);
x_batch = x(1+m*100: (m+1)*100, :);
t_batch = t(1+m*100: (m+1)*100, :);
Wh = w{1};
Wy = w{2};
[ J, z, yh ] = forward( x_batch, w, t_batch, Nx );
sj = zeros(Nx,Ny);
sh = zeros(Nx,Nh);
deltaj = zeros(Nh,Ny);
deltah = zeros(Nd,Nh);
for k = 1:Nx
for j = 1:Ny
sj(k,j) = z(k,j)*(1-z(k,j))*(t_batch(k,j)-z(k,j));
for h = 1:Nh
deltaj(h,j) = eta*sj(k,j)*yh(k,h) + deltaj(h,j);
end
end
for h = 1:Nh
sh(k,h) = (1-yh(k,h)^2)*(Wy(h,:)*sj(k,:)');
for i = 1:Nd
deltah(i,h) = eta*sh(k,h)*x_batch(k,i) + deltah(i,h);
end
end
end
Wy = Wy + deltaj;
Wh = Wh + deltah;
w{1} = Wh;
w{2} = Wy;
%[ J, z, yh ] = forward( x, w, t, param );
JJ = sum(abs(J));
[ out ] = test( w, x_t, tt );
q = q + 1;
data(q) = JJ;
acc(q) = out;
% res(resid) = JJ;
% resid = resid + 1;
% if resid == 4
% resid = 1;
% end
if JJ < theta || isnan(JJ) || q==3000 %(round(res(1),3) == round(res(2),3) && round(res(1),3) == round(res(3),3))
flag = 1;
end
disp(['batch迭代第' num2str(q) '次,正确率为:' num2str(out*100) '%, loss为:' num2str(JJ)]);
end
end
function [ out ] = test( w, X_test, tt )
%TEST 此处显示有关此函数的摘要
% 此处显示详细说明
Wh = w{1};
Wy = w{2};
neth = X_test * Wh;
yh = tanh(neth);
netj = yh * Wy;
z = sigmoid(netj);
[~,I] = max(z,[],2); %返回每行最大的数所在的列数
[~,It] = max(tt,[],2);
tr = numel(find(I == It)); %判断正确的数目
out = tr/size(X_test,1);
end
function [ y ] = sigmoid( x )
%SIGMOID 此处显示有关此函数的摘要
% 此处显示详细说明
[a,b] = size(x);
y = zeros(a,b);
for i = 1:a
for j = 1:b
y(i,j) = 1/(1+exp(-x(i,j)));
end
end
end