不用框架,只用numpy书写DNN的美妙篇章(Deep Neural Networks)

一、综述

一个Logistics Regression可以认为是一个简单的只含有一个隐藏层(Hidden Layer)构成的神经网络(如下图所示)。

但是往往Logistic Regression对异常值十分敏感,并且在处理多酚类的问题时候比较困难,所以人们收到人脑神经的启发,在Logistic Regression的基础上又建立了更为复杂层级更多的神经网络模型,又叫做深度神经网络(Deep Neural Networks, DNN)

深度神经网络使机器学习(machine learning)发生了颠覆性的变化,使之变为了深度学习(deep learning)。人们一听到deep learning就会觉得很可怕,但是我想说其实看完下面对于DNN代码的书写,你就会发现,其实deep learning也没有那么难。

二、基本原理

图2 具有两个隐藏层的神经网络

如图一个看上去很复杂的网络结构,他的输入是一个具有三个特征的向量,输出可以认为是一个逻辑斯蒂回归分类器,中间有两个隐藏层,我们如图所示,将所有的层都编号为l_i,其中Input Layer记作l_0Output Layer 记作l_3。根据逻辑斯蒂回归的基础告诉我们,我们应该设计一个向前的网络和一个向后的网络来分别计算\hat{y_i}和梯度grad。如下图所示

三、代码实现

初始化参数

首先我们要初始化我们的参数param(w_i, b_i),所以我们设计一个初始化函数:

def init_param(dim, m):
    param = {}
    L = len(dim)
    for i in range(1, L):
        param["W" + str(i)] = np.random.randn(dim[i], dim[i - 1])
        param["b" + str(i)] = np.random.randn(dim[i], m)

    return param

这个函数输入一个dim是网络各层的维度例如图2的输入应为[3, 4, 3, 1], m为数据量。

激活函数和激活函数的导数

import numpy as np


def sigmoid(Z):
    g = 1.0 / (1.0 + np.exp(-1.0 * Z))

    return g


def d_sigmoid(Z):
    s = 1.0 / (1.0 + np.exp(-1.0 * Z))
    dg = s * (1-s)

    return dg

这里以sigmoid函数为例,如果需要使用其他激活函数,只需加上一个标识识别即可。这里值得注意的是sigmoid函数的导数计算根据斯坦福大学公开课的说法,这里可以表示成dg = s * (1-s),其中s为原函数。

前向传播

由于层次很多我们需要挨个计算前向传播的输出,其中各个步骤之间的关系为

其中n_l是第l层的维数,括号中的是维数,可以根据这个维数对我们的代码进行检验。

def forward(A, param):
    cache1 = [A]
    cache2 = []

    L = len(param) // 2

    for i in range(1, L + 1):
        Z = np.dot(param["W" + str(i)], A) + param["b" + str(i)]
        A = sigmoid(Z)
        cache2.append(Z)
        cache1.append(A)
    cache = cache1, cache2

    return A, cache

这里的输入 A就是初始传入的X,即训练集,param就是参数对(w_i, b_i),这里的parma都是以字典的形式存储。从for循环里就可以看出存储方式。

输出的时候我保留了下一步代价函数中要用的A = \hat{y},还保留了一部分数据放在缓存中为之后的反向传播提速。

代价函数

一般的代价函数都用交叉熵进行计算,如下

Loss = -\frac{1}{m}[ylog\hat{y} + (1-y)log(1-\hat{y})]

其对应代码如下:

def cost(A, y):
    m = y.shape[1]
    J = -(1.0 / m) * np.sum(y* np.log(A) + (1-y)*np.log(1-A))

    return J

这里输入的A就是 \hat{y}但是为了统一,我记作A。

反向传播

反向传播因为涉及python的广播机制,所以思考起来比较麻烦,其实就是通过复合函数的求导以及找规律就可以得到第l个隐藏层的反向传播的普遍形式

具体代码如下:

def backward(y, cache, param):
    grad = {}
    A, Z = cache
    L = len(param) // 2

    dZ = A[L]-y
    grad["dW" + str(L)] = np.dot(dZ, A[L - 1].T) / m
    grad["db" + str(L)] = np.sum(dZ) / m
    for i in range(L-1, 0, -1):
        dA = np.dot(param["W" + str(i+1)].T, dZ)
        dg = d_sigmoid(Z[i - 1])
        dZ = dA * dg
        grad["dW" + str(i)] = np.dot(dZ, A[i - 1].T) / m
        grad["db" + str(i)] = np.sum(dZ) / m

    return grad

输入目标(target)值y,缓存cache,参数param,返回一个梯度grad。

更新参数

这个部分比较简单,公式如下:

W_l = W_l - \alpha dW_l

b_l = b_l - \alpha db_l

代码如下:

def update_param(param, grad, alpha):
    L = len(param) // 2

    for i in range(1, L):
        param["W" + str(i)] -= alpha * grad["dW" + str(i)]
        param["b" + str(i)] -= alpha * grad["db" + str(i)]

    return param

整合model

def train(X, Y, layer_dim, alpha, epochs):
    m = X.shape[1]
    param = init_param(layer_dim, m)
    for i in range(epochs):
        A, cache = forward(X, param)
        J = cost(A, Y)
        grad = backward(Y, cache, param)
        update_param(param, grad, alpha)
        print(J)

    return J, param

将所有的函数放进训练函数里,构成一个模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星宇星静

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值