深度学习之一:神经网络与深度学习

本文详细介绍Andrew Ng的深度学习课程核心概念,包括神经网络的基本原理、梯度下降法的应用、以及从单层到多层神经网络的设计与实现。文章还探讨了激活函数的作用,并展示了如何使用numpy实现这些模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

深度学习之一:神经网络与深度学习


1 简介

本系列内容为Andrew NG的深度学习课程的笔记。深度学习课程在coursera及网易云课堂上都可以免费学习到。课程共计5部分,分别介绍了深度学习,深度学习的优化,深度学习项目的演进策略,卷积神经网络和循环神经网络。本系列笔记也分为5部分,分别与之相对应。

本篇内容主要介绍如何使用numpy,通过梯度下降实现后向传播算法,完成从简单的单个神经元,单层直到多层全连接神经网络的训练实现方法。

2 Logistic回归

2.1 记法说明

x(i)x(i) 上标表示训练集中第ii个数据
xj(i)xRnx∈Rn,下标表示向量x(i)x(i)的第jj维分量
y(i) 表示第ii个数据对应的监督学习的标签

训练数据集表示为:[(x(1),y(1)),(x(2),y(2)),...(x(m),y(m))]

为了便于运算,将X和Y其表示为矩阵的形式,约定每一列为一个训练数据
X=[x(1)x(2)...x(m)]X=[x(1)x(2)...x(m)], 其维度为(dim(xx),m)
Y=[y(1)y(2)...y(m)],其维度为(1,m)
yˆ(i)y^(i) 为算法针对第ii组输入预测的输出

2.2 Logistic回归

Logistic回归是一种有监督的学习算法,可以用来解决二分类问题。神经网络中每个神经元都是一个logistic回归单元。下图来表示逻辑回归的思想:
人工神经元示意图

以判断图片是否为猫的图片这个二分类问题为例。可以把图片表示为一个由n个特征组成的向量x,算法输出当xx作为输入时,结果为猫的概率。即

y^=P(y=1|x)=σ(wTx+b)

其中σσ为sigmoid激活函数:σ(z)=11+ezσ(z)=11+e−z
其函数图像为:sigmoid函数
其导数为:σ=σ(1σ)σ′=σ∗(1−σ)

逻辑回归包含的参数有:wRnw∈Rn , bRb∈R,当我们有一批标注过的图片后,就可以将图片的特征和标签作为输入对其进行训练,从而得到合适的w和b的值,以使其可以对图片是否为猫进行分类。

2.3 Logistic回归的损失函数

损失函数定义了神经元的输出与真实标签之间的误差大小,那么模型分类越准确,则误差就越小。因此我们的目标就是得到一组参数w,b使得在数据集上的累积误差最小。这样回归单元针对这一任务的分类作用就越好。
Logistic神经元单个样本的误差:

L(yˆ,y)=ylogyˆ(1y)log(1yˆ)L(y^,y)=−ylogy^−(1−y)log(1−y^)

所有训练集的整体误差:
J(w,b)=1mi=1mL(yˆ(i),y(i))=1mi=1my(i)logyˆ(i)+(1y(i))log(1yˆ(i))J(w,b)=1m∑i=1mL(y^(i),y(i))=−1m∑i=1my(i)logy^(i)+(1−y(i))log(1−y^(i))

2.4 梯度下降法

梯度下降是一种计算函数极值的方法,在高等数学中,我们知道函数的梯度就是导数,其几何意义是某点上切线的斜率。

针对损失函数J(w,b)J(w,b)梯度下降的过程:

  1. 初始化参数w,b,可以随机初始化或零初始化
  2. 更新参数w,b,记学习率为αα
    w,b=wαJw,bαJbw,b=w−α∗∂J∂w,b−α∗∂J∂b
  3. 直到误差小到可接受的范围或无法再使误差减小时停止更新过程

2.5 逻辑回归计算图

在计算梯度时,会需要通过链式法则来计算复合函数的导数,可以用计算图来解释并实现这一过程。我们先重写逻辑回归的计算函数的符号:
z=wTx+bz=wTx+b
yˆ=a=σ(z)y^=a=σ(z)
L(a,y)=yloga(1y)log(1a)L(a,y)=−yloga−(1−y)log(1−a)

因此逻辑回归的计算图如下:
这里写图片描述

    A["w"]-->|变量w| D
    B["b"] -->|变量b| D
    C["x"] -->|训练集输入| D
    D["z = w.T * x + b"] --> E["a = σ(z)"]
    E --> F["L(a,y) = -yloga - (1-y)log(1-a)"]

梯度下降推导如下:
La=ya+1y1a∂L∂a=−ya+1−y1−a
az=a(1a)∂a∂z=a(1−a)
Lz=Laaz=ay∂L∂z=∂L∂a∂a∂z=a−y

Lw=Lzzw=x(ay)∂L∂w=∂L∂z∂z∂w=x(a−y)
Lb=Lzzb=(ay)∂L∂b=∂L∂z∂z∂b=(a−y)

2.6 逻辑回归的代码实现

在上一节中,我们推导了单个样本时损失函数的梯度下降计算公式,本节我们看下如何使用numpy来实现这些公式并实现m个样本的向量化逻辑回归算法。

多个样本的情况,为了加速计算,我们将样本排列为矩阵,如2.1节所示。而损失函数则需要计算所有样本的损失的加和平均,因此目标是使得整体的损失函数最小化。方法依然是梯度下降法。具体的实现如下:

import numpy as np


learn_rate = 0.01

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def train(train_x, train_y, loops=100):
    input_dim = train_x.shape[0]
    m = train_x.shape[1]

    # 初始化w,b
    w = np.zeros(shape=(input_dim,1))
    b = 0

    for i in range(loops):
        # 前向计算损失
        z = np.dot(w.T, train_x) + b
        A = sigmoid(z)
        cost = - 1/m * np.sum(train_y * np.log(A) + (1-train_y)*np.log(1-A))

        # 后向更新参数
        dZ = A - train_y
        dw = np.dot(train_x, dZ.T)/m
        db = np.sum(dZ)/m
        w -= learn_rate * dw
        b -= learn_rate * db
        print "Afte loop ", i, " get cost ", cost
    return w,b

def predict(w,b,x):
    num = x.shape[1]
    y_pred = np.zeros(shape=(1,num))
    z = np.dot(w.T,x) + b
    A = sigmoid(z)
    for i in range():
        y_pred[i] = 1  if A[0,i]>0.5 else 0

    return y_pred

# load_data()以2.1节说明的格式返回训练与测试数据集    
train_x, train_y, test_x, test_y = load_data()
w,b = train(train_x, train_y)
print predict(w, b, test_x)

3 浅层神经网络

3.1 浅层神经网络表示

这一节介绍浅层神经网络,我们先从只有一个隐层的神经网络开始。
如下图所示,图中有三层,通常称为其为两层神经网络(输入层不计数)。第0层称为输入层,第1层称为隐含层,第2层称为输出层。
为了区分不同层的参数,使用加了中括号上标的参数来表示区分,如w[i]w[i]表示第ii层的权重参数。隐层与输出层的每个单元都是前面介绍过的逻辑回归单元。
各层的单元数用n[l]来表示,如n[1]=4n[1]=4
单隐层神经网络

值得注意的地方是隐层的激活函数使用了tanh
其函数表达式为:tanh(z)=ezezez+eztanh(z)=ez−e−zez+e−z
其函数图像如下:此处输入图片的描述
其导数形式为:tanh=1tanh2(z)tanh′=1−tanh2(z)

以同样的方式,我们先画出上图的计算图:
这里写图片描述

    A["W1"] --> D
    B["b1"] --> D
    C["x"] --> D
    D["z[1]=W[1]*x+b[1]"] --> E
    E["a[1]=tanh(z[1])"] --> F
    F["z[2]=W[2]*a[1]+b[2]"]-->G
    G["a[2]=σ(z[2])"]-->H["L(a[2],y)"]

其中W1为4*2的矩阵,w2为1*4的矩阵。

3.2 浅层神经网络梯度下降推导

正向传播的计算过程在计算图中可以清楚的看出,不再推导。
下面先推导单个样本的反向传播过程的计算公式:
da[2]=La[2]=ya[2]+1y1a[2]da[2]=∂L∂a[2]=−ya[2]+1−y1−a[2]
dz[2]=z[2]a[2]da[2]=a[2]ydz[2]=∂z[2]∂a[2]da[2]=a[2]−y
dW[2]=dz[2]a[1]TdW[2]=dz[2]a[1]T
db[2]=dz[2]db[2]=dz[2]
dz[1]=z[2]a[1]a[1]z[1]dz[2]=W[2]Tdz[2](1a[1]2)dz[1]=∂z[2]∂a[1]∂a[1]∂z[1]dz[2]=W[2]Tdz[2]∗(1−a[1]2)
dW[1]=dz[1]xTdW[1]=dz[1]xT
db[1]=dz[1]db[1]=dz[1]

容易推广到多个样本的反向传播公式:
dZ[2]=A[2]YdZ[2]=A[2]−Y
dW[2]=1mdZ[2]A[1]TdW[2]=1mdZ[2]A[1]T
db[2]=1mnp.sum(dZ[2],axis=1,keepdims=True)db[2]=1mnp.sum(dZ[2],axis=1,keepdims=True)
dZ[1]=W[2]TdZ[2](1A[1]2)dZ[1]=W[2]TdZ[2]∗(1−A[1]2)
dW[1]=dZ[1]XTdW[1]=dZ[1]XT
db[1]=1mnp.sum(dZ[1],axis=1,keepdims=True)db[1]=1mnp.sum(dZ[1],axis=1,keepdims=True)

3.3 shape对齐

矩阵XW1A1/Z1W2A2/Z2/Y
维度(n[0]n[0],m)(n[1]n[1],n[0]n[0])(n[1]n[1],m)(n[2]n[2],n[1]n[1])(n[2]n[2],m)

对以上变量的微分并不改变其shape。依照此表格,在实现代码的时候需要注意要保证相应的矩阵乘法可以正确执行。

3.3 浅层神经网络实现

下面的代码实现上面的公式完成多样本训练过程的浅层神经网络。

import numpy as np

hidden_units = 4
lr = 1.2

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def train(X,Y, loops=1000)
    m = X.shpae[1]
    input_dim = X.shape[0]
    output_dim = Y.shape[0]
    W1 = np.random.randn(hidden_units, dim_input)
    b1 = np.zeros(shape=(hidden_units, 1))
    W2 = np.random.randn(dim_output, hidden_units)
    b2 = np.zeros(shape=(dim_output, 1))

    for i in range(loops):
        # forward propagate
        Z1 = np.dot(W1, X) + b1
        A1 = np.tanh(Z1)
        Z2 = np.dot(W2, A1) + b2
        A2 = sigmoid(Z2)

        loss = Y * np.log(A2) + (1 - Y) * np.log(1 - A2)
        loss = - np.sum(loss)/ m
        print "After loops ",i," get loss ",loss
        # backward propagate
        dZ2 = A2 - Y
        dW2 = np.dot(dZ2, A1.T) / m
        db2 = np.sum(dZ2, axis=1, keepdims=True) / m
        dZ1 = np.dot(W2.T, dZ2) * (1 - np.power(A1, 2))
        dW1 = np.dot(dZ1, X.T) / m
        db1 = np.sum(dZ1, axis=1, keepdims=True) / m

        # update params
        W1 = W1 - lr * dW1
        b1 = b1 - lr * db1
        W2 = W2 - lr * dW2
        b2 = b2 - lr * d
    return {"W1":W1,"b1":b1,"W2":W2,"b2":b2}

def predict(X, Y, params):
    W1 = params['W1']
    b1 = params['b1']
    W2 = params['W2']
    b2 = params['b2']

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

    y_pred = np.round(A2)
    accuracy = (np.dot(Y, y_pred.T) + np.dot(1 - Y, 1 - y_pred.T)) / float(Y.size) * 100)
    return y_pred, accuracy

train_x, train_y, test_x, test_y = load_data()
params = train(train_x, train_y, loops=1000)

_, accuracy = predict(test_x, test_y, params)
print "Accuracy is", accuracy

3.4 为什么需要激活函数

如果没有非线性激活函数,则无论神经网络有多少层,其实际在计算线性激活函数,不如直接去掉全部隐层。也就是说线性隐层没有一点作用,线性层只能计算线性关系,无法计算复杂的关系。

4 深层神经网络

4.1 深层神经网络介绍

与浅层相对,深层是指隐层数量多于两层的神经网络。隐层的数量可以很多,多达上百层。深度神经网络的训练难度也快速增加,需要更多优化的手段。
下图是一个深层神经网络示例:
此处输入图片的描述

4.2 深层神经网络的shape对齐

以输入层,任意隐层和输出层为例

矩阵XWibiZi/AiY
维度(n[0]n[0],m)(n[i]n[i],n[i1]n[i−1])(n[i]n[i],11) (n[i],m)(n[n]n[n],m)

4.3 深层神经网络的前向与后向传播

有了第三章的基础,在深层神经网络中,推导任意一层ll的前向与后向传播的公式。
Z[l]=W[l]A[l1]+b[l]
A[l]=g[l](Z[l])A[l]=g[l](Z[l])
dZ[l]=dA[l]g[l](Z[l])dZ[l]=dA[l]∗g[l]′(Z[l])
dW[l]=1mdZ[l]A[l1]dW[l]=1mdZ[l]A[l−1]
db[l]=1mdZ[l]db[l]=1mdZ[l]
dA[l1]=W[l]TdZ[l]dA[l−1]=W[l]TdZ[l]
输出层dA[end]=YA+1Y1AdA[end]=−YA+1−Y1−A

4.4 深层神经网络的实现

本节将实现一个深层神经网络模型,其隐层的激活函数为relu,输出层的激活函数为sigmoid,层数及各层的维度由参数layer_dims决定。先看下其计算图:
这里写图片描述

    A["W1"] --> D
    B["b1"] --> D
    C["x"] --> D
    D["z[1]=W[1]*x+b[1]"] --> E
    E["a[1]=relu(z[1])"] -->|"...若干relu隐层..."| F
    F["z[l]=W[l]*a[l-1]+b[l]"]-->G
    G["a[l]=σ(z[l])"]-->H["L(a[l],y)"]

下面看下实现代码:

import numpy as np


learn_rate = 0.0075
loops = 3000

def sigmoid(Z):
    A = 1 / (1 + np.exp(-Z))
    return A

def relu(Z):
    A = np.maximum(0, Z)
    return A

def init_parameters(layer_dims):
    np.random.seed(1)
    params = {}
    layers = len(layer_dims)

    for l in range(1, layer):
        w = np.random.randn(layer_dims[l], layer_dims[l - 1])
        w = w * 0.01 / np.sqrt(layer_dims[l - 1])
        params['W' + str(l)] = w 
        params['b' + str(l)] = np.zeros((layer_dims[l], 1))
    return params   

def update_parameters(parameters, grads, learn_rate):
    layers = len(parameters) // 2
    for l in range(layers):
        parameters["W" + str(l + 1)] -= learn_rate * grads["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] -= learn_rate * grads["db" + str(l + 1)]

    return parameters

def compute_cost(AL, Y):
    m = Y.shape[1]
    cost =  (-np.dot(Y, np.log(AL).T) - np.dot(1 - Y, np.log(1 - AL).T)) / m
    cost = np.squeeze(cost)
    return cost

def relu_activation_forward(A_prev, W, b):
    Z = W.dot(A_prev) + b
    A = relu(Z)
    return A, (A_prev, W, b), Z

def sigmoid_activation_forward(A_prev, W, b):
    Z = W.dot(A_prev) + b
    A = sigmoid(Z)
    return A, (A_prev, W, b), Z


def deep_model_forward(X, parameters):
    caches = []
    A = X
    layers = len(parameters) // 2

    for layer in range(1, layers):
        A_prev = A
        A, awb, z = relu_activation_forward(A_prev,parameters['W' + str(layer)],parameters['b' + str(layer)])
        caches.append((awb,z))

    AL, awb, z = sigmoid_activation_forward(A,parameters['W' + str(layers)],parameters['b' + str(layers)])
    caches.append((awb, z))

    return AL, caches


def linear_backward(dZ, awb):
    A_prev, W, b = awb
    m = A_prev.shape[1]

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

    return dA_prev, dW, db


def relu_activation_backward(dA, cache):
    awb, Z = cache
    dZ = np.array(dA, copy=True)
    dZ[Z <= 0] = 0
    dA_prev, dW, db = linear_backward(dZ, awb)
    return dA_prev, dW, db


def sigmoid_activation_backward(dA, cache):
    awb, Z = cache
    s = sigmoid(Z)
    dZ = dA * s * (1 - s)
    dA_prev, dW, db = linear_backward(dZ, awb)
    return dA_prev, dW, db

def deep_model_backward(AL, Y, caches):
    grads = {}
    layers = len(caches) 
    Y = Y.reshape(AL.shape)

    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
    current_cache = caches[layers - 1]
    current_layer = str(layers)
    dA_prev_temp, dW_temp, db_temp = sigmoid_activation_backward(dAL, current_cache)
    grads["dA" + current_layer] = dA_prev_temp
    grads["dW" + current_layer] = dW_temp
    grads["db" + current_layer] = db_temp

    for layer in reversed(range(layers - 1)):
        current_cache = caches[layer]
        dA_prev_temp, dW_temp, db_temp = relu_activation_backward(grads["dA" + str(layer + 2)], current_cache)
        grads["dA" + str(layer + 1)] = dA_prev_temp
        grads["dW" + str(layer + 1)] = dW_temp
        grads["db" + str(layer + 1)] = db_temp

    return grads

def deep_layer_model(X, Y, layers_dims):
    parameters = init_parameters(layers_dims)

    for i in range(0, loops):
        AL, caches = deep_model_forward(X, parameters)
        cost = compute_cost(AL, Y)
        grads = deep_model_backward(AL, Y, caches)
        parameters = update_parameters(parameters, grads, learn_rate)
        print ("Cost after loops %i: %f" % (i, cost))

    return parameters

def predict(X, y, parameters):
    m = X.shape[1]
    n = len(parameters) // 2 
    p = np.zeros((1, m))

    probas, caches = deep_model_forward(X, parameters)

    for i in range(0, probas.shape[1]):
        if probas[0, i] > 0.5:
            p[0, i] = 1
        else:
            p[0, i] = 0
    print("Accuracy: " + str(np.sum((p == y)*1. / m)))
    return p

train_x, train_y, test_x, test_y = load_data()

input_dim = 4
hidden_units = 7
output_dim = 1
layer_dims = (input_dim, hidden_units, output_dim )

model = deep_layer_model(train_x, train_y, layer_dims)
predict(test_x, test_y, model)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值