01 第一题参考


1.两种网络权系数学习公式

(1) 结构1

结构1网络是标准的分层(作业题中结构以是两层)前馈网络。可以根据 BP算法 ,利用误差反向传播关系写出各层权系数调整值算法公式。

下面给出课件中的对于具有神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播层网络中,第神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_02层的权系数神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_03的调整公式:

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_04

其中 神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_03 表示第神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_06层的第神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_07个神经元连接到第神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_02层的第神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_09个神经元的权系数。

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_10


▲ 具有h+1层的前馈网络结构示意图

公式中:

  • 神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_11:是第神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_12层中的第神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_13个神经元的学习信号。它是从网络的输出层反向传播过来的。对于神经网络输出层的学习信号神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_14就定义为误差:神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_15
  • 神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_16:表示第神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_17层神经元输出信息的导数;
  • 神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_18:表示神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_19层中第神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_20个神经元的输出。

本质上讲,上面的权系数调整公示都符合统一的神经元学习算法公式:

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_21

即对于神经元线性加权的权系数神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_22的调整神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_23与学习信号神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_24,输入向量神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_25的乘积成正比。

根据以上公式,可以写出结构1中网络各个权系数的修改公式。对于网络中的变量做如下约束:神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_26

  • 神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_27是学习速率。
  • 神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_28分别是神经网络中神经元1,2,3的输出;
  • 输入节点4,5的输出分别是神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_29

神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_30

由于结构1中的所有神经元的激励函数都是sigmoid函数:神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_31

对应的函数导数为:
神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_32

这样,神经元神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_33的输出导数可以表示成:

神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_34

  • 输出层神经元权系数:

神经元1的学习信号:神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_35

那么它的三个神经元权向量的修正公式为:
神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_36

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_37

神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_38

注意:对于权系数神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_39,由于网络中规定的输入为-1,所以在修正公式前具有负号(-1)。对于后面的神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_40也是一样的。

  • 隐层神经元权系数:
    隐层神经元2,3的学习信号分别为:
    神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_41

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_42

因此隐层神经元的各个权系数修正公式分别为:

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_43

神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_44

神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_45

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_46

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_47

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_48

(2) 结构2

相比于结构1,结构2不是一个严格的分层网络,因为它的输入层的信息神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_49直接越过隐层,到达输出层。

该网络只有两个神经元1,2。它们的输出分别记为:神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_50。对于输入层的神经元3,4的输出就是输入信号神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_49

神经元的传递函数采用双曲正切函数:

神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_52神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_53

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_54

由于结构2的网络比较简单,可以直接根据公式(1-2)写出其中两个神经元1,2的各个权系数的修正公式:

  • 神经元1:
    学习信号:神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_55
    四个权系数的修正公式为:
    神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_56

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_57

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_58

神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_59

  • 神经元2:

学习信号:
神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_60

三个权系数修正公式:

神经网络分类 前馈 后馈 前馈神经网络计算例题_人工神经网络_61

神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_62

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_63

2.使用编程语言实现上述基本算法

由于结构1属于典型的前馈网络,所以可以在标准的网络平台上实现该算法,比如MATLAB,TensorFlow,Keras,PaddlePaddle上等等。

对于结构2,由于它不属于标准的前馈网络,所以实现该算法,则需要通过C、Python等通用的编程语言来实现。

(1) 使用Python实现第一种结构算法

第一接驳算法的Python程序参见附录中:第一种结构(BP网络程序)

网络参数:

  • 中间隐层结构2各节点,输出节点传递函数为线性函数。
  • 使用学习速率:0.5。

下面是进行训练,网络误差曲线降低的过程。

神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络_64


▲ 训练误差收敛曲线

训练之后,对于异或问题的计算结构为:

样本

S1

S2

S3

S4

期望输出

0

1

1

0

神经网络输出

0.0404143

0.94139378

0.92436339

0.09499248

讨论1: 输入采用线性传递函数,相当于对于损失函数,不是使用均方差:神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_65
而是使用的对数似然函数:
神经网络分类 前馈 后馈 前馈神经网络计算例题_神经网络分类 前馈 后馈_66

讨论2: 如果输出的传递函数采用sigmoid函数,同时误差仍然采用均方差,上述网络在训练过程中对于XOR问题无法收敛。对于逻辑与则可以收敛到一个误差大约最小为0.05的结果。

(2) 使用Python 实现第二种结构

神经元1采用线性输出传递函数;神经元2采用双曲正切传递函数。

具体Python程序参见本文最后附录中的:第二种结构的程序

如下是在学习速率为0.5, 使用(-1,1)来表示异或逻辑样本是,网络训练误差收敛过程:

神经网络分类 前馈 后馈 前馈神经网络计算例题_反向传播_67


▲ 使用双极性训练网络,误差收敛曲线

网络输出结果:

样本

x1

x2

x3

x3

期望输出

-1

1

1

-1

实际输出

-0.99503755

0.99212455

0.99503753

-0.99503758

  • 讨论1: 使用(0,1)来表示异或逻辑,上述训练过程不收敛。而使用(-1,1)逻辑来表示,则训练过程中很快就收敛了。

 

➤※ 作业1-1中的程序



1.第一种结构(BP网络)程序

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# HWDATA.PY                    -- by Dr. ZhuoQing 2020-11-17
#
# Note:
#============================================================

from headm import *

#------------------------------------------------------------
# Samples data construction

xor_x = array([[0,0],[1,0],[0,1],[1,1]])       # row->sample
xor_y = array([0, 1, 1, 0]).reshape(1, -1)     # col->sample

xor_x0 = array([[-1,-1],[1,-1],[-1,1],[1,1]])  # row->sample
xor_y0 = array([-1, 1, 1, -1]).reshape(1, -1)  # col->sample

#------------------------------------------------------------
def shuffledata(X, Y):
    id = list(range(X.shape[0]))
    random.shuffle(id)
    return X[id], (Y.T[id]).T

#------------------------------------------------------------
# Define and initialization NN
def initialize_parameters(n_x, n_h, n_y):
    random.seed(2)

    W1 = random.randn(n_h, n_x) * 0.5          # dot(W1,X.T)
    W2 = random.randn(n_y, n_h) * 0.5          # dot(W2,Z1)
    b1 = zeros((n_h, 1))                       # Column vector
    b2 = zeros((n_y, 1))                       # Column vector

    parameters = {'W1':W1,
                  'b1':b1,
                  'W2':W2,
                  'b2':b2}

    return parameters

#------------------------------------------------------------
# Forward propagattion
# X:row->sample;
# Z2:col->sample
def forward_propagate(X, parameters):
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']

    Z1 = dot(W1, X.T) + b1                    # X:row-->sample; Z1:col-->sample
    A1 = 1/(1+exp(-Z1))

    Z2 = dot(W2, A1) + b2                     # Z2:col-->sample
#    A2 = 1/(1+exp(-Z2))                       # A:col-->sample
    A2 = Z2                                   # Linear output

    cache = {'Z1':Z1,
             'A1':A1,
             'Z2':Z2,
             'A2':A2}
    return Z2, cache

#------------------------------------------------------------
# Calculate the cost
# A2,Y: col->sample
def calculate_cost(A2, Y, parameters):
    err = A2 - Y
    cost = dot(err, err.T) / Y.shape[1]
    return cost

#------------------------------------------------------------
# Backward propagattion
def backward_propagate(parameters, cache, X, Y):
    m = X.shape[0]                  # Number of the samples

    W1 = parameters['W1']
    W2 = parameters['W2']
    A1 = cache['A1']
    A2 = cache['A2']

    dZ2 = (A2 - Y) #* (A2 * (1-A2))
    dW2 = dot(dZ2, A1.T) / m
    db2 = sum(dZ2, axis=1, keepdims=True) / m

    dZ1 = dot(W2.T, dZ2) * (A1 * (1-A1))
    dW1 = dot(dZ1, X) / m
    db1 = sum(dZ1, axis=1, keepdims=True) / m

    grads = {'dW1':dW1,
             'db1':db1,
             'dW2':dW2,
             'db2':db2}

    return grads

#------------------------------------------------------------
# Update the parameters
def update_parameters(parameters, grads, learning_rate):
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']

    dW1 = grads['dW1']
    db1 = grads['db1']
    dW2 = grads['dW2']
    db2 = grads['db2']

    W1 = W1 - learning_rate * dW1
    W2 = W2 - learning_rate * dW2
    b1 = b1 - learning_rate * db1
    b2 = b2 - learning_rate * db2

    parameters = {'W1':W1,
                  'b1':b1,
                  'W2':W2,
                  'b2':b2}

    return parameters

#------------------------------------------------------------
# Define the training
def train(X, Y, num_iterations, learning_rate, print_cost=False):
#    random.seed(3)

    n_x = 2
    n_y = 1
    n_h = 3

    lr = learning_rate

    parameters = initialize_parameters(n_x, n_h, n_y)
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']

    XX,YY = shuffledata(X, Y)

    costdim = []

    for i in range(0, num_iterations):
        A2, cache = forward_propagate(XX, parameters)
        cost = calculate_cost(A2, YY, parameters)
        grads = backward_propagate(parameters, cache, XX, YY)
        parameters = update_parameters(parameters, grads, lr)

        if print_cost and i % 50 == 0:
            printf('Cost after iteration:%i: %f'%(i, cost))
            costdim.append(cost[0][0])

            if cost < 0.01:
                break

#            XX, YY = shuffledata(X, Y)

    return parameters, costdim

#------------------------------------------------------------
parameter,costdim = train(xor_x, xor_y, 10000, 0.5, True)

A2, cache = forward_propagate(xor_x, parameter)
printf(A2, xor_y)

plt.plot(arange(len(costdim))*50, costdim)
plt.xlabel("Step(10)")
plt.ylabel("Cost")
plt.grid(True)
plt.tight_layout()
plt.show()

#------------------------------------------------------------
#        END OF FILE : HWDATA.PY
#============================================================


2.第二种结构Python程序

#!/usr/local/bin/python
# -*- coding: gbk -*-
#============================================================
# HWXOR1.PY                    -- by Dr. ZhuoQing 2020-11-17
#
# Note:
#============================================================

from headm import *

#------------------------------------------------------------
# Samples data construction

xor_x = array([[0,0],[1,0],[0,1],[1,1]])       # row->sample
xor_y = array([0, 1, 1, 1]).reshape(1, -1)     # col->sample

xor_x0 = array([[-1,-1],[1,-1],[-1,1],[1,1]])  # row->sample
xor_y0 = array([-1, 1, 1, -1]).reshape(1, -1)  # col->sample

#------------------------------------------------------------
def shuffledata(X, Y):
    id = list(range(X.shape[0]))
    random.shuffle(id)
    return X[id], (Y.T[id]).T

#------------------------------------------------------------
# Define and initialization NN
def initialize_parameters():
    random.seed(2)

    w10 = random.randn(1) * 0.1
    w20 = random.randn(1) * 0.1
    w13 = random.randn(1) * 0.1
    w12 = random.randn(1) * 0.1
    w14 = random.randn(1) * 0.1
    w23 = random.randn(1) * 0.1
    w24 = random.randn(1) * 0.1

    parameters = {'w10':w10, 'w20':w20,
                  'w13':w13, 'w12':w12, 'w14':w14,
                  'w23':w23, 'w24':w24}

    return parameters

#------------------------------------------------------------
# Forward propagattion
# X:row->sample;
# Z2:col->sample
def forward_propagate(X, parameters):
    w10 = parameters['w10']
    w20 = parameters['w20']
    w13 = parameters['w13']
    w12 = parameters['w12']
    w14 = parameters['w14']
    w23 = parameters['w23']
    w24 = parameters['w24']

    W2 = array([w23, w24])
    W1 = array([w13, w14])
    Z2 = dot(W2.T, X.T) - w20
#    A2 = 1/(1+exp(-Z2))
    A2 = (1-exp(-Z2)) / (1+exp(-Z2))

    Z1 = dot(W1.T, X.T) + w12 * A2 - w10
    A1 = Z1

    cache = {'Z1':Z1,
             'A1':A1,
             'Z2':Z2,
             'A2':A2}
    return Z1, cache

#------------------------------------------------------------
# Calculate the cost
# A2,Y: col->sample
def calculate_cost(A2, Y, parameters):
    err = A2 - Y
    cost = dot(err, err.T) / Y.shape[1]
    return cost

#------------------------------------------------------------
# Backward propagattion
def backward_propagate(parameters, cache, X, Y):
    m = X.shape[0]                  # Number of the samples

    w10 = parameters['w10']
    w20 = parameters['w20']
    w13 = parameters['w13']
    w12 = parameters['w12']
    w14 = parameters['w14']
    w23 = parameters['w23']
    w24 = parameters['w24']

    A1 = cache['A1']
    A2 = cache['A2']

    dZ1 = A1 - Y
    d10 = -1 * sum(dZ1, axis=1, keepdims=True) / m
    d13 = dot(dZ1, X.T[0].T) / m
    d12 = dot(dZ1, A2.T) / m
    d14 = dot(dZ1, X.T[1].T) / m

    dZ2 = w12 * dZ1 * (1 - power(A2, 2))
    d23 = dot(dZ2, X.T[0].T) / m
    d24 = dot(dZ2, X.T[1].T) / m
    d20 = -1 * sum(dZ2, axis=1, keepdims=True) / m

    grads = {'d10':d10, 'd20':d20,
             'd13':d13, 'd12':d12, 'd14':d14,
             'd23':d23, 'd24':d24}

    return grads

#------------------------------------------------------------
# Update the parameters
def update_parameters(parameters, grads, learning_rate):
    w10 = parameters['w10']
    w20 = parameters['w20']
    w13 = parameters['w13']
    w12 = parameters['w12']
    w14 = parameters['w14']
    w23 = parameters['w23']
    w24 = parameters['w24']

    d10 = grads['d10']
    d20 = grads['d20']
    d13 = grads['d13']
    d12 = grads['d12']
    d14 = grads['d14']
    d23 = grads['d23']
    d24 = grads['d24']

    w10 = w10 - learning_rate * d10
    w20 = w20 - learning_rate * d20
    w13 = w13 - learning_rate * d13
    w12 = w12 - learning_rate * d12
    w14 = w14 - learning_rate * d14
    w23 = w23 - learning_rate * d23
    w24 = w24 - learning_rate * d24

    parameters = {'w10':w10, 'w20':w20,
                  'w13':w13, 'w12':w12, 'w14':w14,
                  'w23':w23, 'w24':w24}

    return parameters

#------------------------------------------------------------
# Define the training
def train(X, Y, num_iterations, learning_rate, print_cost=False):
#    random.seed(3)

    lr = learning_rate

    parameters = initialize_parameters()

    XX,YY = X, Y #shuffledata(X, Y)
    costdim = []

    for i in range(0, num_iterations):
        A2, cache = forward_propagate(XX, parameters)
        cost = calculate_cost(A2, YY, parameters)
        grads = backward_propagate(parameters, cache, XX, YY)
        parameters = update_parameters(parameters, grads, lr)

        if print_cost and i % 50 == 0:
            printf('Cost after iteration:%i: %f'%(i, cost))
            costdim.append(cost[0][0])

            if cost < 0.01:
                break

#            XX, YY = shuffledata(X, Y)

    return parameters, costdim

#------------------------------------------------------------
parameter,costdim = train(xor_x0, xor_y0, 1000, 0.5, True)

A2, cache = forward_propagate(xor_x0, parameter)
printf(A2, xor_y)

plt.plot(arange(len(costdim))*50, costdim)
plt.xlabel("Step(10)")
plt.ylabel("Cost")
plt.grid(True)
plt.tight_layout()
plt.show()

#------------------------------------------------------------
#        END OF FILE : HWXOR1.PY
#============================================================