BP 神经网络算法及MATLAB实现

2018年7月11日

这源自实验课上的一个小作业。做的一个PPT,在文章底部附件中。

人工神经网络

是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统,通俗的讲就是具备学习功能。

BP神经网络算法


反向传播(英语:Backpropagation,缩写为BP)是“误差反向传播”的简称,是用来训练人工神经网络的常见方法。该方法对网络中所有权重计算损失函数的梯度。这个梯度会反馈给最优化方法,用来更新权值以最小化损失函数。

BP 算法主要阶段:

第1阶段:激励传播

1.(前向传播阶段)将训练输入送入网络以获得激励响应;
2.(反向传播阶段)将激励响应同训练输入对应的目标输出求差,从而获得隐层和输出层的响应误差。

第2阶段:权重更新

1.将输入激励和响应误差相乘,从而获得权重的梯度;
2.将这个梯度乘上一个比例并取反后加到权重上。

这个比例(百分比)将会影响到训练过程的速度和效果,因此成为“训练因子”。梯度的方向指明了误差扩大的方向,因此在更新权重的时候需要对其取反,从而减小权重引起的误差。

第 1 和第 2 阶段可以反复循环迭代,直到网络对输入的响应达到满意的预定的目标范围为止。

流程图

相关公式

  1. 激活函数: g(x)=\frac{1}{1+e^{-x}}

  2. 隐含层输出: H_j = g(\sum_{i=1}^{n}{\omega_{ij} * x_i + a_j})

  3. 输出层输出: O_k = \sum_{j=1}^{l}{H_j*\omega_{jk}+b_k}

  4. 误差计算:E=\frac{1}{2}\sum_{k=1}^{m}e_k^2

  5. 权值更新:\begin{cases}
    \omega_{ij}=\omega_{ij}+\eta H_j(1-H_j)x_i\sum_{k=1}^m\omega_{jk}e_{k} \\
    \omega_{jk}=\omega_{jk}+\eta H_j e_k
    \end{cases}

  6. 偏置更新:\begin{cases}
    a_{j}=a_{j}+\eta H_j(1-H_j)\sum_{k=1}^m\omega_{jk}e_{k} \\
    b_{k}=b_{k}+\eta H_j e_k
    \end{cases}

源码实现

这里根据上面的流程及公式实现了源码,并用它来处理 Iris 数据集分类的问题。Iris数据集可以在 http://en.wikipedia.org/wiki/Iris_flower_data_set 找到。

第一个文件,实现BP算法

classdef bpnn < handle
    properties
        input;  %训练输入
        output; %训练输出
        inputps; %归一化变量
        testInput;  %测试输入
        testOutput; %测试输出
        n; %训练次数
        inputNum; %输入层的节点数
        hiddenNum; %隐含层的节点数
        outputNum; %输出层的节点数
        w1; w2; b1; b2; %权重及偏置
        alpha; % 学习速率
    end
    methods
        function obj = bpnn(x, y, n)
            %  BP神经网络
            %  bpnn(x, y, n),x 训练输入,y训练输出,n训练次数
            [obj.input, obj.inputps] = mapminmax(x'); %训练数据归一化
            obj.output = y;
            obj.n = n;
            obj.inputNum = size(x, 2); %输入层的节点数
            obj.hiddenNum = 3; %隐含层的节点数
            obj.outputNum = size(y, 2); %输出层的节点数

            % 权重和偏置的初始化
            obj.w1 = rands(obj.inputNum, obj.hiddenNum);
            obj.b1 = rands(obj.hiddenNum, 1);
            obj.w2 = rands(obj.hiddenNum, obj.outputNum);
            obj.b2 = rands(obj.outputNum, 1);
            obj.alpha = 0.1;
        end
        function obj = train(obj)
            disp('训练开始...')
            for r = 1 : obj.n
                E(r) = 0; % 统计误差
                for m = 1 : size(obj.input, 2)
                    % 信息的正向流动
                    x = obj.input(:, m);
                    % 隐含层的输出
                    for j = 1: obj.hiddenNum
                        hiddenOutput(j,:) = sigmoid(obj.w1(:,j)' * x + obj.b1(j,:));
                    end
                    % 输出层的输出
                    outputOutput = obj.w2' * hiddenOutput + obj.b2;

                    % 计算误差
                    e = obj.output(m,:)' - outputOutput;
                    E(r) = E(r) + sum(abs(e));

                    % 修改权重和偏置
                    % 隐含层到输出层的权重和偏置调整
                    dw2 = hiddenOutput * e';
                    db2 = e;

                    % 输入层到隐含层的权重和偏置调整
                    for j = 1 : obj.hiddenNum
                        partOne(j) = hiddenOutput(j) * (1 - hiddenOutput(j));
                        partTwo(j) = obj.w2(j,:) * e;
                    end

                    for i = 1 : obj.inputNum
                        for j = 1 : obj.hiddenNum
                            dw1(i,j) = partOne(j) * x(i,:) * partTwo(j);
                            db1(j,:) = partOne(j) * partTwo(j);
                        end
                    end

                    obj.w1 = obj.w1 + obj.alpha * dw1;
                    obj.w2 = obj.w2 + obj.alpha * dw2;
                    obj.b1 = obj.b1 + obj.alpha * db1;
                    obj.b2 = obj.b2 + obj.alpha * db2;
                end
            end
            fprintf('训练完毕,共训练 %d 次,%d 项\n', obj.n, size(obj.input, 2));
        end
        function test(obj)
            %测试函数,需要给定testInput、testOutput,否则无法测试。输出测试结果
            result = predict(obj);
            k = size(obj.testInput, 1);
            ok = 0;
            for i = 1 : k
               fprintf('输入:[%.1f %.1f %.1f %.1f]  预测: %d,实际: %d\n', ...
               obj.testInput(i, 1), obj.testInput(i, 2), obj.testInput(i, 3), obj.testInput(i, 4), ...
               result(i), find(obj.testOutput(i, :)));

               if  result(i) == find(obj.testOutput(i, :))
                   ok = ok + 1;
               end
            end
            fprintf('测试 %d 项,正确 %d 项,准确率 %.2f%%\n', k, ok, ok / k * 100);
        end
        function [output_fore] = predict(obj)
            %预测函数,给定testInput,返回预测结果
            test = mapminmax('apply', obj.testInput', obj.inputps); %分类
            k = size(obj.testInput, 1);
            for m = 1 : k
                for j = 1 : obj.hiddenNum
                    hiddenTestOutput(j,:) = sigmoid(obj.w1(:,j)' * test(:,m) + obj.b1(j,:));
                end
                outputOfTest(:,m) = obj.w2' * hiddenTestOutput + obj.b2;
            end
            %根据网络输出找出数据所属类别
            output_fore = zeros(k, 1);
            for m = 1 : k
                output_fore(m) = find(outputOfTest(:,m) == max(outputOfTest(:,m)));  
            end
        end
    end
end
function [y] = sigmoid(x)
% 激活函数
    y = 1 ./ (1 + exp(-x));
end

第二个文件,运用BP算法处理分类问题

clear; clc;

% 读取数据
[f1,f2,f3,f4,class,~] = textread('Iris.txt' , '%f%f%f%f%f%s', 150);
input = [f1, f2, f3, f4];

%构造输出矩阵
s = length(class);
output = zeros(s, 3);
for i = 1 : s
   output(i , class(i)) = 1 ;
end

% 随机提取80%的数据用于训练,20%的数据用于测试
num = size(input, 1) * 0.8;
k = rand(1, 150);
[m, n] = sort(k);
trainInput=input(n(1:num),:);
trainOutput=output(n(1:num),:);
testInput=input(n(num + 1:end),:);
testOutput=output(n(num + 1:end),:);

% 调用BP神经训练函数
a = bpnn(trainInput, trainOutput, 30);
a.train();
a.testInput = testInput;
a.testOutput = testOutput;
a.test()

附件

bpnn

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注