BP神经网络原理与matlab实现

个人博客文章链接: http://www.huqj.top/article?id=166

BP神经网络,即“反向传播”神经网络,是一种被广泛运用的人工神经网络,它的思想就是通过反向传播不断调整模型权重,最终使得模型输出与预期输出的误差控制在一个小范围内。其中反向传播的算法(BP算法)是核心。

    一、模型架构

    一个普通的神经网络模型大致如下图:

    image.png

    其中第一层称为“输入层”,最后一层称为“输出层”,其他层都是“隐含层”,每一层由若干个神经元(也就是图中的小圆圈)组成,输入层的神经元数目就是训练数据每个X的维度值,或者说是特征数目,输出层的神经元数目就是数据集的因变量维度。隐含层的数目和每层的神经元个数是随意的,一般根据模型需要调节。相邻层之间通过权重相连,每一层的每个神经元都与下一层的每个神经元相连。

    BP神经网络训练的计算过程分为两种:前向传播反向传播

    前向传播就是从数据数据前向计算出最终结果的过程,这也是模型训练好之后应用的方式。反向传播是一种参数调节方法,它从前向传播计算出的最终结果与实际数据相比计算误差,并根据数学公式逆向推导各个神经元和权重的误差,然后更新权重,重复前向传播的过程。在前向传播的过程中,除了第一层,每个神经元都接受上一层的输入,然后使用一个 激励函数 输出值到下一层。

    二、数学原理以下公式都为矩阵表示形式

    BP神经网络,本质上也是一个参数调优的模型,其中参数就是各个权重。

    使用 a 表示每个神经元的输出, z 表示每个神经元的输入, W 表示权重, g(z) 表示神经元的激励函数,则前向传播的计算公式如下:

    a(1) = Xi        

    i 表示是第i组训练数据

    z(2) = W(1) * a(1)  Add(a0 = 1)        

    W 是一个 Sl+1 * (Sl  + 1) 的矩阵, Sk 表示是第 层神经元的数目,这里添加了一个值为1的偏置单元

    a(2) = g(z(2))

    g是激励函数,在分类问题中,通常使用sigmoid函数

    以此类推,即可完成前向传播的过程,直到计算到输出层。

    而对于反向传播(BP)的过程,实际就是求代价函数偏导数的过程,神经网络模型的代价函数如下:

    image.png

    为了计算这个代价函数的偏导数,我们定义了两个矩阵变量:δ 和 Δ,分别是二维和三维矩阵,δ 用来存储每个神经元的偏差,Δ 用来存储每个权重的偏差。

    反向传播的计算公式如下:

    δ(end) = a(end) - yi

    输出层的偏差就是预测值与实际值的插值, i 表示是第 i  组训练数据

    δ(j) = (W(j))T * δ(j+1) * g'(z(j))

    即上一层的误差是由下一层的误差和两层之间的连接权重,以及该层神经元的激励函数导数值决定的。对于激励函数为sigmoid函数的模型而言,可以通过导数计算得到如下公式:

    δ(j) = (W(j))T * δ(j+1) * a(j) * (1 - a(j))

    这样依次从后往前计算,就可以得到所有神经元的δ值。

    对于Δ值的计算,有如下公式:

    Δ(j) = Δ(j) + δ(j+i) * (a(j))T

    注意 Δ 是累加值,累加的是每组训练数据。当累加完所有训练数据之后,就可以计算出代价函数的导数了:

    D = Δ / m + lambda * W / m

    可以从数学上证明,此时D就是代价函数的导数(加上了正则化参数),然后要做的就是应用梯度下降法调整权重W,再重复此过程。

    三、matlab实现

    有了上面的数学原理和公式推导,我们可以使用Matlab很快的实现一个BP神经网络模型:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

%% 主函数

function [W, delta, iterNum] = BPNN(size_, alpha, lambda, threshold, maxIter, X, y)

    %BP神经网络实现

    %参数含义:

    %size - 一维数组,表示神经网络架构(不包含偏置单元)

    %alpha - 学习率α

    %lambda - 正则化参数

    %threshold - 误差阈值

    %maxIter - 最大迭代次数

    %X,y - 训练数据

     

    %判断参数合法性

    if size(X, 2) ~= size_(1) || size(y, 2) ~= size_(end) || size(y, 1) ~= size(X, 1)

       fprintf('训练数据集与模型结构不符\n'); 

    end

    %初始化一些参数

    layerNum = length(size_);

    maxUnitNum = max(size_);

    m = size(X, 1);

    iterNum = 0;

     

    %每个神经元的输出

    a = zeros(layerNum, maxUnitNum);

    %每个神经元的误差

    d = zeros(layerNum, maxUnitNum);

    %初始随机权重

    W = rand(layerNum - 1, maxUnitNum + 1, maxUnitNum + 1);

     

    %迭代训练

    while iterNum < maxIter

       iterNum = iterNum + 1;

       D = zeros(layerNum - 1, maxUnitNum + 1, maxUnitNum + 1);

       delta = 0;

       for i = 1 : m

           %前向传播

           [out, a] = forwardPropagation(size_, W, X(i, :), a);

           d(end, 1 : length(out)) = out - y(i, :);

           delta = delta + sum(abs(out - y(i, :)));

           %反向传播

           [d, D] = backPropagation(size_, W, d, D, a);

       end

       delta = delta / m;

       if delta <= threshold

           fprintf('误差小于阈值,训练结束');

           break;

       end

       %调整权重

       W = adjustWeight(size_, W, D, alpha, lambda, m);

    end

end

%% 一次前向传播

function [out, a] = forwardPropagation(size_, W, x, a)

    out = x;

    a(1, 1 : size_(1)) = x;

    for i = 1 : length(size_) - 1

        out = reshape(W(i, 2 : size_(i + 1) + 1, 1 : size_(i) + 1), size_(i + 1), size_(i) + 1) * [1, out]';

        out = sigmoid(out');

        a(i + 1, 1 : size_(i + 1)) = out;

    end

end

%% 一次反向传播

function [d, D] = backPropagation(size_, W, d, D, a)

    for i = length(size_) - 1 : -1 : 1

        %调节每个神经元的误差值

        d(i, 1 : size_(i)) = d(i + 1, 1 : size_(i + 1)) * ...

            reshape(W(i, 2 : size_(i + 1) + 1, 2 : size_(i) + 1), size_(i + 1), size_(i))...

            .* (a(i, 1 : size_(i)) .* (1 - a(i, 1 : size_(i))));

        % 累加调节每个权重的误差

        D(i, 2 : size_(i + 1) + 1, 1 : size_(i) + 1) = D(i, 2 : size_(i + 1) + 1, 1 : size_(i) + 1) + ...

            reshape(([1; a(i, 1 : size_(i))'] * d(i + 1, 1 : size_(i + 1)))', 1, size_(i + 1), size_(i) + 1);

    end

end

%% 调整权重

function W = adjustWeight(size_, W, D, alpha, lambda, m)

    D = D / m;

    for i2 = 1 : length(size_) - 1

        %非常数项系数加上正则化参数

        D(i2, 2 : size_(i2 + 1) + 1, 2 : size_(i2) + 1) = D(i2, 2 : size_(i2 + 1) + 1, 2 : size_(i2) + 1) + ...

           lambda * W(i2, 2 : size_(i2 + 1) + 1, 2 : size_(i2) + 1) / m;

       %梯度下降调整权重

       W(i2, 1 : size_(i2 + 1) + 1, 1 : size_(i2) + 1) = W(i2, 1 : size_(i2 + 1) + 1, 1 : size_(i2) + 1) -...

           alpha * D(i2, 1 : size_(i2 + 1) + 1, 1 : size_(i2) + 1);

    end

end

    使用这个函数进行一次神经网络训练实验的测试代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

% 测试BP神经网络函数

%% 读取数据

clc;

clear;

data = load('machine-learning-ex2\ex2\ex2data2.txt');

%% 初始化参数

m = size(data, 1);

X = data(:,[1, 2]);

y = data(:, 3);

size_ = [2, 4, 1];

lambda = 0;

alpha = 2;

threshold = 0.1;

%% 画出点集

hold on

LogisticPlotData([ones(m, 1), X], y);

%% 开始训练

[W, delta, times] = BPNN(size_, alpha, lambda, threshold, 2000, X, y);

%% 测试准确度

[~, precious] = BPNNPredict(size_, W, X, y)

%% 画出决策边界

u = min(X(:, 1)) - 0.5: 0.1: max(X(:, 1)) + 0.5;

v = min(X(:, 2)) - 0.5: 0.1: max(X(:, 2)) + 0.5;

z = zeros(length(u), length(v));

for i = 1 : length(u)

   for j = 1 : length(v)

      [z(i, j), precious] = BPNNPredict(size_, W, [u(i), v(j)], 0); 

   end

end

z = z';

contour(u, v, z, [0.5, 0.5], 'LineWidth', 2);

hold off;

    这是一个分类问题,可以使用逻辑回归的方法实现,可以参考博客:http://www.huqj.top/article?id=165  ,这里使用BP神经网络实现,最终决策边界如下。

    image.png

    在训练集上的准确度可以达到87%.BP神经网络相对于逻辑回归的优势就在于它不需要手动选择模型,而是通过训练可以自动选择一个合适的模型来模拟。

猜你喜欢

转载自blog.csdn.net/qq_32216775/article/details/86072072