首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[手把手系列之二]实现多层神经网络

[手把手系列之二]实现多层神经网络

作者头像
公众号-不为谁写的歌
发布2020-07-22 17:48:19
5170
发布2020-07-22 17:48:19
举报
文章被收录于专栏:桃花源记桃花源记

完整代码:>>点我 欢迎star,fork,一起学习

网络用途

或者说应用场景:使用单层神经网络来识别一张图片是否是猫咪的图片。

数学表示

给定一张图片XXX 送到网络中,判断这张图片是否是猫咪的照片?

网络架构

多层神经网络处理过程:

  • X –> [linear+relu](L−1)[linear+relu](L−1)[linear + relu]^{(L-1)} —>[linear + sigmoid] —> y^y^\hat{y}

数学表示

训练集: X=[x(1),x(2),...,x(i),....,x(m)]X=[x(1),x(2),...,x(i),....,x(m)]X = [x^{(1)},x^{(2)},...,x^{(i)},....,x^{(m)}] ;对应标签:Y=[y(1),y(2),...,y(i),...,y(m)]Y=[y(1),y(2),...,y(i),...,y(m)]Y=[y^{(1)},y^{(2)},...,y^{(i)},...,y^{(m)}] ;

对于训练集中的每张照片x(i)x(i)x^{(i)} 的处理过程:

repeat:

​ z(i)=wTx(i)+bz(i)=wTx(i)+bz^{(i)} = w^Tx^{(i)}+b

​ y^(i)=a(i)=g(z(i))y^(i)=a(i)=g(z(i))\hat{y}^{(i)} = a^{(i)} = g(z^{(i)})

L(a(i),y(i))=−y(i)log(a(i))−(1−y(i))log(1−a(i))L(a(i),y(i))=−y(i)log(a(i))−(1−y(i))log(1−a(i))L(a^{(i)},y^{(i)}) = -y^{(i)}log(a^{(i)})-(1-y^{(i)})log(1-a^{(i)})

成本函数:

J=1m∑mi=1L(a(i),y(i))J=1m∑i=1mL(a(i),y(i))J = \frac{1}{m} \sum_{i=1}^{m} L(a^{(i)},y^{(i)})

最后通过反向传播算法,计算参数WWW 和 bbb 。

模型定义

模型定义步骤

  1. 定义模型结构(如输入向量的特征数目)
  2. 初始化模型参数;
  3. 循环:
    • 前向传播,计算loss;
    • 反向传播,计算梯度;
    • 梯度下降,更新参数;

代码实现

激活函数
  1. sigmoid 激活函数及其反向传播过程
def sigmoid(Z):
    """
    sigmoid激活函数;
    :param Z:
    :return:
    - A: 激活函数值sigmoid(z),
    - cache: (存储Z值,方便反向传播时直接使用)
    """
    A = 1.0/(1+np.exp(-Z))
    cache = Z
    return A, cache

def sigmoid_backward(dA,cache):
    """
    激活函数的反向传播
    :param dA: loss对A的导数
    :param cache:前向传播中缓存的sigmoid输入Z;
    :return:dZ
    """
    Z = cache
    s = 1.0/(1 + np.exp(-Z))
    dZ = dA * s * (1-s)
    return dZ
  1. relu激活函数及其反向传播过程
def relu(Z):
    """
    relu激活函数;
    :param Z:
    :return:
    - A:
    - cache:
    """
    A = np.maximum(0,Z)# max适合单个数值间的比较
    cache = Z
    return A, cache

def relu_backward(dA,cache):
    """
    relu 反向传播计算方法;relu = np.maximum(0,A);导数值:1 or 0.----> dZ= dA or 0
    :param dA:
    :param cache:
    :return: dZ
    """
    Z = cache
    dZ = np.array(dA, copy=True)

    #当Z<=0时,dZ=0
    dZ[Z <= 0] = 0
    assert(dZ.shape == Z.shape) #确保维度相同
    return dZ
参数初始化

权重系数WWW和bbb 全都初始化为0.

def initialize_parameters_deep(layer_dims,type='he'):
    """
    深度神经网络系数初始化函数
    :param layer_dims: 神经网络各层神经元列表, eg:[12288,100,10,1]
    :param type: 系数初始化方法:zeros,random,he;
    :return: parameters:系数字典
    """
    np.random.seed(10)

    parameters = {}
    L = len(layer_dims)

    if type == "zeros":
        for i in range(1, L):
            parameters['W'+str(i)] = np.zeros((layer_dims[i], layer_dims[i-1]))
            parameters['b'+str(i)] = np.zeros((layer_dims[i], 1))

            assert (parameters['W' + str(i)].shape == (layer_dims[i], layer_dims[i - 1]))
            assert (parameters['b' + str(i)].shape == (layer_dims[i], 1))
    elif type == "random":
        for i in range(1, L):
            parameters['W'+str(i)] = np.random.randn(layer_dims[i],layer_dims[i-1]) * 0.01
            parameters['b'+str(i)] = np.zeros((layer_dims[i], 1))

            assert (parameters['W' + str(i)].shape == (layer_dims[i], layer_dims[i - 1]))
            assert (parameters['b' + str(i)].shape == (layer_dims[i], 1))
    elif type == "he":
        for i in range(1, L):
            parameters['W'+str(i)] = np.random.randn(layer_dims[i], layer_dims[i-1]) / np.sqrt(layer_dims[i-1])
            parameters['b'+str(i)] = np.zeros((layer_dims[i], 1))

            assert (parameters['W' + str(i)].shape == (layer_dims[i], layer_dims[i - 1]))
            assert (parameters['b' + str(i)].shape == (layer_dims[i], 1))

    return parameters
前向传播
前向传播过程

训练集:

X=[x(1),x(2),...,x(i),....,x(m)]X=[x(1),x(2),...,x(i),....,x(m)]

X = [x^{(1)},x^{(2)},...,x^{(i)},....,x^{(m)}] ;对应标签:

Y=[y(1),y(2),...,y(i),...,y(m)]Y=[y(1),y(2),...,y(i),...,y(m)]

Y=[y^{(1)},y^{(2)},...,y^{(i)},...,y^{(m)}] ;

对于训练集中的每张照片x(i)x(i)x^{(i)} 的处理过程:

z(i)=wTx(i)+bz(i)=wTx(i)+bz^{(i)} = w^Tx^{(i)}+b

y^(i)=a(i)=sigmoid(z(i))y^(i)=a(i)=sigmoid(z(i))\hat{y}^{(i)} = a^{(i)} = sigmoid(z^{(i)})

L(a(i),y(i))=−y(i)log(a(i))−(1−y(i))log(1−a(i))L(a(i),y(i))=−y(i)log(a(i))−(1−y(i))log(1−a(i))L(a^{(i)},y^{(i)}) = -y^{(i)}log(a^{(i)})-(1-y^{(i)})log(1-a^{(i)})

成本函数:J=1m∑mi=1L(a(i),y(i))J=1m∑i=1mL(a(i),y(i))J = \frac{1}{m} \sum_{i=1}^{m} L(a^{(i)},y^{(i)})

image
image
代码实现
  1. 线性部分的前向传播过程
def linear_forward(A_pre,W,b):
    """
    前向传播-线性部分
    :param A_pre:前一层的输出值-激活值
    :param W:系数矩阵
    :param b:偏置矩阵
    :return:线性部分Z,cache(A,W,b)
    """
    Z = np.dot(W, A_pre) + b
    assert(Z.shape == (W.shape[0], A_pre.shape[1])) #可能有多个样本A_pre.shape[1]样本容量
    cache = (A_pre, W, b)
    return Z, cache
  1. 单层网络的前向传播过程[线性部分 + 激活函数]
def linear_activation_forward(A_pre, W, b, activation):
    """
    单层网络(构成:线性部分+激活函数) 的输出结果;
    :param A_pre: 上一层的输出激活值;
    :param W: 本层网络的系数矩阵
    :param b: 偏置
    :param activation: 本层网络的激活函数类型:sigmoid, relu;
    :return:
    - A:激活函数值;
    - cache:linear_cache, activation_cache;加快反向传播计算速度;
    """
    if activation == 'sigmoid':
        Z, linear_cache = linear_forward(A_pre, W, b)
        A, activation_cache = sigmoid(Z)
    elif activation == 'relu':
        Z, linear_cache = linear_forward(A_pre, W, b)
        A, activation_cache = relu(Z)

    assert(A.shape == (W.shape[0], A_pre.shape[1]))
    cache = (linear_cache, activation_cache)

    return A, cache
  1. 神经网络的前向传播过程
def L_model_forward(X, parameters):
    """
    L层深度神经网络的前向传播过程;
    网络架构:X-->(linear-relu)[L-1]-->(linear-sigmoid)-->AL;
    :param X: 输入
    :param parameters: 各层网络系数字典;
    :return:
    - AL:最终的输出值;水平排列
    - caches:各层网络的cache列表;
    """
    caches = []
    A = X
    L = len(parameters) // 2 #确保是一个整数值;

    #前(L-1)层都是相同的架构,可以用for循环计算;最后一层单独计算;
    for i in range(1, L):
        A_pre = A
        A, cache = linear_activation_forward(A_pre,parameters['W'+str(i)],parameters['b'+str(i)],\
                                             activation='relu')
        caches.append(cache)
    AL, cache = linear_activation_forward(A, parameters['W'+str(L)], parameters['b'+str(L)],\
                                          activation='sigmoid')
    caches.append(cache)

    assert (AL.shape == (1, X.shape[1]))

    return AL, caches

由于网络为单层神经网络,前向传播过程和反向传播过程比较简单,所以整合到一起。直接计算出相应的成本函数和相应的系数梯度。

反向传播
反向传播过程
image
image
编码实现
  1. 线性部分的反向传播
def linear_backward(dZ, cache):
    """
    反向传播的线性部分
    :param dZ:
    :param cache: 前向传播中的缓存值(A_pre, W, b)
    :return:
    - dA_pre:关于前一层A的导数值;
    - dW:关于权重的偏导数;
    - db:关于偏置的偏导数;
    """
    A_pre, W, b = cache
    m = A_pre.shape[1]

    dA_pre = np.dot(W.T, dZ)
    dW = 1./m * np.dot(dZ, A_pre.T)
    db = 1./m * np.sum(dZ, axis=1, keepdims=True)

    assert (dA_pre.shape == A_pre.shape)
    assert (dW.shape == W.shape)
    assert (db.shape == b.shape)

    return dA_pre, dW, db
  1. 单层网络的反向传播过程[线性部分+激活函数]
def linear_activation_backward(dA,cache,activation):
    """
    反向传播-单层网络;一个网络层的反向传播计算方法
    :param dA: 对本层网络输出的偏导数
    :param cache:前向传播过程中缓存的元组(linear_cache,activation_cache)
    :param activation:激活函数类型:sigmoid,relu
    :return:
    - dA_pre:
    - dW:
    - db:
    """
    linear_cache, activation_cache = cache
    if activation == 'relu':
        dZ = relu_backward(dA,activation_cache)
        dA_pre, dW, db = linear_backward(dZ, linear_cache)
    elif activation == 'sigmoid':
        dZ = sigmoid_backward(dA, activation_cache)
        dA_pre, dW, db = linear_backward(dZ, linear_cache)

    return dA_pre, dW, db
  1. 神经网络的反向传播过程
def L_model_backward(AL, Y, caches):
    """
    反向传播-L层深度NN;整合到一块
    网络架构:X-->(linear+relu)[L-1]-->(linear+sigmoid)-->AL
    :param AL: 最终输出值;
    :param Y: 标签;
    :param caches: 各层网络的系数
    :return: grads 各层网络系数变量的梯度计算值;
    """
    grads = {}
    L = len(caches)
    m = AL.shape[1]
    Y = Y.reshape(AL.shape) # 确保AL和Y shape相同;

    #cost:交叉熵函数
    dAL = -(np.divide(Y, AL) - np.divide(1-Y, 1-AL))
    #最后一层单独计算,之后for loop循环;
    current_cache = caches[L-1]
    grads['dA'+str(L)], grads['dW'+str(L)], grads['db'+str(L)] = linear_activation_backward(dAL, current_cache,\
                                                                                            activation='sigmoid')
    # 从倒数第二层开始 for-loop:linear+relu
    for i in reversed(range(L-1)):
        #backward: relu->linear
        current_cache = caches[i]
        dA_pre_temp, dW_temp, db_temp = linear_activation_backward(grads['dA'+str(i+2)],current_cache,activation='relu')
        grads['dA'+str(i+1)] = dA_pre_temp
        grads["dW"+str(i+1)] = dW_temp
        grads["db"+str(i+1)] = db_temp

    return grads
参数优化

参数更新过程–使用梯度下降算法;

def update_parameters_with_gd(parameters,grads,learning_rate):
    """
    系数更新
    :param parameters: 系数;
    :param grads: 关于系数的梯度值;
    :param learning_rate: 学习率更新速度
    :return: parameters更新后的系数
    """
    L = len(parameters) // 2
    for i in range(L):
        parameters['W'+str(i+1)] = parameters['W'+str(i+1)] - learning_rate * grads["dW"+str(i+1)]
        parameters['b'+str(i+1)] = parameters['b'+str(i+1)] - learning_rate * grads["db"+str(i+1)]

    return parameters
模型评测

用带标签的数据集评测模型训练效果如何。

def score(params, X, y):
    """
    由测试集判断训练模型的好坏
    :param params: 训练得到的参数
    :param X: 测试集 [n_px*n_px*3, m]
    :param y: 测试集标签 [1, m]
    :return: accuracy 准确率
    """
    m = X.shape[1]
    result = np.zeros((1, m))

    probs, _ = L_model_forward(X, params)


    for i in range(probs.shape[1]):
        if probs[0, i] >= 0.5:
            result[0, i] = 1

    accuracy = np.mean(result == y)

    return accuracy
模型预测

输入测试集,输出测试标签.

运算过程:做一次前向传播,得到输出;再对输出和threshold阈值作比较,得出类别标签。

def predict(params, X):
    """
    给定图片进行测试,输出预测标签
    :param params: 训练的参数
    :param X: 待预测数据
    :return: 预测结果
    """
    preds = np.zeros((1,X.shape[1]))
    probs, _ = self.__model_forward(X,params)

    for i in range(X.shape[1]):
        if probs[0, i] >= 0.5:
            preds[0, i] = 1

    preds = np.squeeze(preds)

    return preds

函数整合

def L_layer_model(X, Y, layer_dims, learning_rate=0.0052, num_iters=5000, print_cost=True):
    """
    L层网络模型:包括初始化、训练;
    :param X: 训练数据
    :param Y: 数据标签
    :param layer_dims: 各网络层神经元数目
    :param learning_rate: 学习率
    :param num_iters: 迭代次数
    :param print_cost: 输出cost变化
    :return: paramters 训练后的系数
    """
    np.random.seed(12)
    costs = []
    parameters = initialize_parameters_deep(layer_dims,type='he')

    for i in range(0, num_iters):
        AL, caches = L_model_forward(X, parameters)
        cost = compute_cost(AL, Y)
        grads = L_model_backward(AL,Y,caches)
        parameters = update_parameters_with_gd(parameters,grads,learning_rate)

        if print_cost and i % 100==0:
            print("Cost after iteration %i:%f" %(i, cost))
            costs.append(cost)

    return parameters

测试:1000次迭代、学习率为0.001;

layers_dims = [12288, 100, 20, 1]
params = model(X_train,y_train,layers_dims,num_iters=1000,learning_rate=0.001)
results = score(parameters,test_X,test_Y)
print(results)

输出结果变化:

Cost after iteration 0:0.697
Cost after iteration 100:0.620
Cost after iteration 200:0.599
Cost after iteration 300:0.581
Cost after iteration 400:0.564
Cost after iteration 500:0.549
Cost after iteration 600:0.534
Cost after iteration 700:0.520
Cost after iteration 800:0.506
Cost after iteration 900:0.492
Accuracy on test set: 52%

比随机猜测效果好一点点。网络层更深,优化梯度算法,超参数优化—提高准确率!

重点是我们自己实现了一个神经网络

小结

  1. 理解网络运算过程时,画一个运算图很很大程度上帮助理解;
  2. 编码实现时,注意变量的shape变化是否正确!
  3. 优化算法:Momentum、RMSprop、Adam
  4. 批量梯度更新算法
  5. 网络模型越大,参数越多,训练时间越长
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-06-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 网络用途
    • 数学表示
    • 网络架构
      • 数学表示
      • 模型定义
        • 模型定义步骤
          • 代码实现
            • 激活函数
            • 参数初始化
            • 前向传播
            • 反向传播
            • 参数优化
            • 模型评测
            • 模型预测
          • 函数整合
          • 小结
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档