从零开始搭建神经网络(三)含隐藏层的神经网络

 单层感知机的学习能力有限,对于非线性可分的情况下分类效果不好,因此为了让网络拥有更强大的学习能力,需要在输入和输出层之间添加隐藏层。

                                 

下面实现一个简单含隐藏层的神经网络结构,是一个二分问题,输入为两维,输出为两类。

 

1.激活函数

  由于添加了隐藏层,因此激活函数分为隐藏层的激活函数和输出层的激活函数,这里隐藏层的激活函数使用tanh()激活函数,输出层还是使用sigmoid()激活函数,这里使用上一篇的代码,至于tanh()激活函数到时候使用np.tanh()就行了

def sigmoid(z):
    """
    sigmoid激活函数
    :param z: 输入
    :return: sigmoid(z)
    """
    return 1/(1 + np.exp(-z))

2.初始化参数

  初始化参数和不含隐藏层的升级网络没有多大区别,只是要初始化的参数多了一些而已,权重采用随机初始化,偏置零值初始化。这里需要讲一下各变量初始化的维度。以我们的图为例, 当处于在隐藏层这一层时,输入维度为(2,1),输出维度为(3,1),根据矩阵乘法公式和激活函数输出,可以得到权重矩阵W的维度为(3,2),如下图所示。偏置的维度同理。

                                                       

def initialize_parameters(in_n, h_n, out_n):
    """
    初始化权重和偏置
    in_n -- 输入层节点数
    h_n -- 隐藏层节点数
    out_n -- 输出层结点数

    returns:
    params --
              W1 --  权重矩阵,维度为(h_n, in_n)
              b1 -- 偏置向量,维度为(h_n, 1)
              W2 -- 权重矩阵,维度为 (out_n, h_n)
              b2 -- 偏置向量,维度为 (out_n, 1)
    """
    W1 = np.random.randn(h_n, in_n) * 0.01
    b1 = np.zeros((h_n, 1))
    W2 = np.random.randn(out_n, h_n) * 0.01
    b2 = np.zeros((out_n, 1))

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

    return parameters

3.BP算法

    含隐藏层的神经网络的BP算法要复杂一些因为要一层一层的正向传播,然后再反向传播回来。下面讲一下含隐藏层的BP算法。前向传播和不含隐藏层的神经网络一样,从输入层开始依次计算每一层的输入,上一次的输出当做当前层的输入;反向传播则是从输出层开始依次进行梯度下降,公式就不进行具体的推导了,在下面直接给出,可以参考:从零开始搭建神经网络(二)数学公式及代码实现。因为含隐藏层的BP算法比较复杂,所以前向传播和反向传播分开来写,

                                                                           

def forward_propagation(X, parameters):
    """
    前向传播
    X -- 输入数据
    parameters -- 参数(包含W1,b1,W2,b2)

    returns:
    A2 -- 网络输出
    temp -- 包含 Z1, A1, Z2,A2的字典,用于BP算法
    """

    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]

    Z1 = np.dot(W1, X) + b1
    A1 = np.tanh(Z1)
    Z2 = np.dot(W2, A1) + b2
    A2 = sigmoid(Z2)

    temp = {"Z1": Z1,
             "A1": A1,
             "Z2": Z2,
             "A2": A2}
    return A2,temp
def backward_propagation(parameters, temp, X, Y):
    """
    反向传播
    parameters -- 参数(包含W1,b1,W2,b2)
    temp -- 包含 Z1, A1, Z2,A2的字典,用于BP算法
    X -- 输入数据
    Y -- 输入数据标签

    returns:
    grads -- 返回不同参数的梯度
    cost -- 损失函数
    """
    # 取回参数
    W1 = parameters["W1"]
    W2 = parameters["W2"]

    A1 = temp["A1"]
    A2 = temp["A2"]

    # 样本数目
    num = Y.shape[0]
    # 交叉熵损失函数
    cost = -1 / num * np.sum(Y * np.log(A2) + (1 - Y) * np.log(1 - A2))

    # 反向传播
    dZ2 = A2 - Y
    dW2 = 1 / num * np.dot(dZ2, A1.T)
    db2 = 1 / num * np.sum(dZ2, axis=1, keepdims=True)
    dZ1 = np.dot(W2.T, dZ2) * (1 - np.power(A1, 2))
    dW1 = 1 / num * np.dot(dZ1, X.T)
    db1 = 1 / num * np.sum(dZ1, axis=1, keepdims=True)


    gradients = {"dW1": dW1,
             "db1": db1,
             "dW2": dW2,
             "db2": db2}

    return cost,gradients

4.参数更新

    参数更新和其他的完全一样,不懂的参考我上一篇博客。

def update_parameters(parameters, gradients, learning_rate):
    """
    梯度下降法更新参数

    parameters -- 参数(包含W1,b1,W2,b2)
    grads -- 不同参数的梯度

    returns:
    parameters -- 更新后的参数
    """
    #取回参数
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]

    dW1 = gradients["dW1"]
    db1 = gradients["db1"]
    dW2 = gradients["dW2"]
    db2 = gradients["db2"]

    # 更新参数
    W1 -= learning_rate * dW1
    b1 -= learning_rate * db1
    W2 -= learning_rate * dW2
    b2 -= learning_rate * db2

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

    return parameters

5.预测

    根据神经网络的输出值来进行预测,公式如下:

                                                                               

def predict(parameters, X):
    """
    使用学习好的参数来预测

    parameters -- 学习好的参数
    X -- 输入数据

    Returns
    predictions -- 预测结果0/1
    """
    A2, temp = forward_propagation(X, parameters)
    predictions = np.round(A2)

    return predictions

 

6. 完整实例

 

    下图是中的两类点是线性不可分的,利用这些数据测试下含隐藏层的神经网络的学习能力

                        

 

if __name__ == "__main__":
    # X为横坐标,Y为纵坐标
    X = [0, 0, 1, 1]
    Y = [0, 1, 0, 1]
    label = [1, 0, 0, 1]
    # 第一类为蓝色,第二类为红色
    label_color = ['blue', 'red']
    color = []
    for i in label:
        if i == 1:
            color.append(label_color[0])
        else:
            color.append(label_color[1])

    # 数据归一化
    X = np.array(X)
    Y = np.array(Y)
    X = (X - np.average(X))
    Y = (Y - np.average(Y))
    X = X / X.max()
    Y = Y / Y.max()
    for i in range(len(X)):
        plt.scatter(X[i], Y[i], c=color[i])
    plt.title('Normalization Data')
    plt.show()

    data_X = np.vstack((X, Y))
    data_label = np.array([label])

    # 参数设置
    in_n = 2
    h_n = 3
    out_n = 1
    costs = []
    Y_prediction = []
    iters = 2000
    learning_rate = 2
    parameters = initialize_parameters(in_n, h_n, out_n)

    #开始训练
    for i in range(iters):
        # 前向传播
        A2, temp = forward_propagation(data_X, parameters)
        # 反向传播
        costTemp, gradients = backward_propagation(parameters, temp, data_X, data_label)
        costs.append(costTemp)
        # 参数更新
        parameters = update_parameters(parameters, gradients, learning_rate)

    # 预测
    Y_prediction = predict(parameters, data_X)

    plot_decision_boundary(lambda x: predict(parameters, x.T), data_X, data_label)
    plt.show()

    # #画图
    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('iterations')
    plt.title("Learning rate =" + str(learning_rate))
    plt.show()

    分类结果如下图所示,可以发现含隐藏层的神经网络可以对线性不可分的数据进行分类

 

 

P.S. 完整代码

import numpy as np
import matplotlib.pyplot as plt

def sigmoid(z):
    """
    sigmoid激活函数
    :param z: 输入
    :return: sigmoid(z)
    """
    return 1/(1 + np.exp(-z))


def initialize_parameters(in_n, h_n, out_n):
    """
    初始化权重和偏置
    in_n -- 输入层节点数
    h_n -- 隐藏层节点数
    out_n -- 输出层结点数
    returns:
    params --
              W1 --  权重矩阵,维度为(h_n, in_n)
              b1 -- 偏置向量,维度为(h_n, 1)
              W2 -- 权重矩阵,维度为 (out_n, h_n)
              b2 -- 偏置向量,维度为 (out_n, 1)
    """
    W1 = np.random.randn(h_n, in_n) * 0.01
    b1 = np.zeros((h_n, 1))
    W2 = np.random.randn(out_n, h_n) * 0.01
    b2 = np.zeros((out_n, 1))

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

    return parameters


def forward_propagation(X, parameters):
    """
    前向传播
    X -- 输入数据
    parameters -- 参数(包含W1,b1,W2,b2)
    returns:
    A2 -- 网络输出
    temp -- 包含 Z1, A1, Z2,A2的字典,用于BP算法
    """

    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]

    Z1 = np.dot(W1, X) + b1
    A1 = np.tanh(Z1)
    Z2 = np.dot(W2, A1) + b2
    A2 = sigmoid(Z2)

    temp = {"Z1": Z1,
            "A1": A1,
            "Z2": Z2,
            "A2": A2}
    return A2, temp


def backward_propagation(parameters, temp, X, Y):
    """
    反向传播
    parameters -- 参数(包含W1,b1,W2,b2)
    temp -- 包含 Z1, A1, Z2,A2的字典,用于BP算法
    X -- 输入数据
    Y -- 输入数据标签
    returns:
    grads -- 返回不同参数的梯度
    cost -- 损失函数
    """
    # 取回参数
    W1 = parameters["W1"]
    W2 = parameters["W2"]

    A1 = temp["A1"]
    A2 = temp["A2"]

    # 样本数目
    num = Y.shape[0]
    # 交叉熵损失函数
    cost = -1 / num * np.sum(Y * np.log(A2) + (1 - Y) * np.log(1 - A2))

    # 反向传播
    dZ2 = A2 - Y
    dW2 = 1 / num * np.dot(dZ2, A1.T)
    db2 = 1 / num * np.sum(dZ2, axis=1, keepdims=True)
    dZ1 = np.dot(W2.T, dZ2) * (1 - np.power(A1, 2))
    dW1 = 1 / num * np.dot(dZ1, X.T)
    db1 = 1 / num * np.sum(dZ1, axis=1, keepdims=True)

    gradients = {"dW1": dW1,
                 "db1": db1,
                 "dW2": dW2,
                 "db2": db2}

    return cost, gradients


def update_parameters(parameters, gradients, learning_rate):
    """
    梯度下降法更新参数
    parameters -- 参数(包含W1,b1,W2,b2)
    grads -- 不同参数的梯度
    returns:
    parameters -- 更新后的参数
    """
    # 取回参数
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]

    dW1 = gradients["dW1"]
    db1 = gradients["db1"]
    dW2 = gradients["dW2"]
    db2 = gradients["db2"]

    # 更新参数
    W1 -= learning_rate * dW1
    b1 -= learning_rate * db1
    W2 -= learning_rate * dW2
    b2 -= learning_rate * db2

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

    return parameters


def predict(parameters, X):
    """
    使用学习好的参数来预测
    parameters -- 学习好的参数
    X -- 输入数据
    Returns
    predictions -- 预测结果0/1
    """
    A2, temp = forward_propagation(X, parameters)
    predictions = np.round(A2)

    return predictions


def plot_decision_boundary(model, X, y):
    # 设置边界
    x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
    y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
    h = 0.01

    # 生成边界
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    # 预测结构
    Z = model(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)

    # 画出边界
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.ylabel('y')  # 标记y轴名称
    plt.xlabel('x')  # 标记x轴名称
    plt.scatter(X[0, :], X[1, :], c=np.squeeze(y), cmap=plt.cm.Spectral)


if __name__ == "__main__":
    # X为横坐标,Y为纵坐标
    X = [0, 0, 1, 1]
    Y = [0, 1, 0, 1]
    label = [1, 0, 0, 1]
    # 第一类为蓝色,第二类为红色
    label_color = ['blue', 'red']
    color = []
    for i in label:
        if i == 1:
            color.append(label_color[0])
        else:
            color.append(label_color[1])

    # 数据归一化
    X = np.array(X)
    Y = np.array(Y)
    X = (X - np.average(X))
    Y = (Y - np.average(Y))
    X = X / X.max()
    Y = Y / Y.max()
    for i in range(len(X)):
        plt.scatter(X[i], Y[i], c=color[i])
    plt.title('Normalization Data')
    plt.show()

    data_X = np.vstack((X, Y))
    data_label = np.array([label])

    # 参数设置
    in_n = 2
    h_n = 3
    out_n = 1
    costs = []
    Y_prediction = []
    iters = 2000
    learning_rate = 2
    parameters = initialize_parameters(in_n, h_n, out_n)

    # 开始训练
    for i in range(iters):
        # 前向传播
        A2, temp = forward_propagation(data_X, parameters)
        # 反向传播
        costTemp, gradients = backward_propagation(parameters, temp, data_X, data_label)
        costs.append(costTemp)
        # 参数更新
        parameters = update_parameters(parameters, gradients, learning_rate)

    # 预测
    Y_prediction = predict(parameters, data_X)

    plot_decision_boundary(lambda x: predict(parameters, x.T), data_X, data_label)
    plt.show()

    # #画图
    plt.plot(costs)
    plt.ylabel('cost')
    plt.xlabel('iterations')
    plt.title("Learning rate =" + str(learning_rate))
    plt.show()



 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值