在人工智能飞速发展的今天,深度学习已成为推动计算机视觉、自然语言处理、语音识别等领域的核心技术。但对于刚入门的同学来说,面对“激活函数”“反向传播”“梯度下降”等术语,常常感到一头雾水。本文将从最基础的逻辑回归出发,逐步带你理解深度学习的核心思想,并解释为什么我们需要非线性、为什么向量化如此重要、以及浅层神经网络是如何工作的。

1.机器学习 vs 深度学习:有何不同?

很多人会混淆“机器学习”和“深度学习”。其实,深度学习是机器学习的一个子集

  • 机器学习:依赖人工设计特征(feature engineering),使用如逻辑回归、支持向量机(SVM)、决策树等算法进行建模。适用于中小规模数据。
  • 深度学习:通过多层神经网络自动从原始数据中学习特征,无需大量人工干预。擅长处理图像、语音、文本等高维复杂数据,但需要大量数据和强大算力支持。

2.逻辑回归:不只是“回归”

        尽管名字里有“回归”,但逻辑回归实际上是一个分类算法,尤其用于二分类问题(比如判断邮件是否为垃圾邮件)。

2.1核心:Sigmoid 函数

逻辑回归通过 Sigmoid 函数将线性输出映射为概率:

 y^​=σ(z)=1+e−z1​,其中 z=wTx+b

输出 y^​ 表示 P(y=1∣x),即在给定输入 x 的条件下,标签为 1 的概率。

2.2为什么不用平方误差做损失函数?

        如果用平方误差(MSE)作为损失函数,代价函数 J(w,b) 会是非凸的,存在多个局部最小值,导致梯度下降难以找到全局最优解。

因此,逻辑回归采用对数损失函数(Log Loss)

L(y^​,y)=−[ylog(y^​)+(1−y)log(1−y^​)]

这个函数是凸函数,只有一个全局最小值,非常适合优化。

最终的代价函数是对所有训练样本的平均损失:

J(w,b)=m1​i=1∑m​L(y^​(i),y(i))

3.梯度下降:寻找最优参数

我们的目标是找到一组参数 (w,b),使得 J(w,b) 最小。这时就用到梯度下降法

基本更新规则:

w:=w−α∂w∂J​,b:=b−α∂b∂J​

其中 α 是学习率

  • 学习率太大:可能“一步跨过”最优解,甚至发散;
  • 学习率太小:收敛速度慢,训练时间长。

实践中常通过实验或学习率调度策略(如衰减)来平衡。

4.向量化:告别 for 循环

        在实现神经网络时,如果对每个样本都用 for 循环计算,效率极低。而向量化编程利用 NumPy 等库,将整个批量(batch)的数据一次性送入计算,实现并行加速。

例如,原本需要循环 m 次的前向传播:

for i in range(m):
    z[i] = np.dot(w.T, X[:, i]) + b

可以简化为一行:

Z = np.dot(W.T, X) + b

不仅代码简洁,运行速度也提升数十倍!

5.正向传播 vs 反向传播

  • 正向传播(Forward Propagation):从输入层开始,逐层计算每一层的激活值,直到输出层得到预测结果。
  • 反向传播(Backpropagation):从输出层开始,利用链式法则计算损失函数对每个参数的梯度,用于更新权重。

反向传播的本质是高效计算梯度,它是深度学习得以训练深层网络的关键。

6.为什么必须用非线性激活函数?

这是很多初学者容易忽略的关键点!

假设我们只使用线性激活函数(比如 a=z),那么无论神经网络有多少层,最终的输出仍然是输入的线性组合

y=W2​(W1​x+b1​)+b2​=(W2​W1​)x+(W2​b1​+b2​)

这等价于一个单层线性模型——多层网络失去了意义

因此,非线性激活函数是神经网络表达复杂函数能力的源泉。没有它,深度学习就只是“深度”的幻觉。

7.常用激活函数对比

1. Sigmoid

  • 输出范围:(0, 1)
  • 缺点:梯度在两端接近 0,易导致梯度消失;输出不是以 0 为中心,影响收敛。

2. Tanh(双曲正切)

  • 输出范围:(-1, 1),以 0 为中心,比 Sigmoid 更好。
  • 但同样存在梯度消失问题。

3. ReLU(Rectified Linear Unit)

  • 定义:ReLU(z)=max(0,z)
  • 优点:计算简单,梯度恒为 1(当 z > 0),极大缓解梯度消失,显著加快训练速度
  • 缺点:存在“神经元死亡”问题(负区间梯度为 0),但实践中效果极佳。

目前,ReLU 及其变体(如 Leaky ReLU、ELU)已成为默认选择

8.浅层神经网络的反向传播:直观理解

对于一个含一个隐藏层的浅层网络,反向传播可以这样理解:

  1. 先写出最终损失 L 关于输出 y^​ 的表达式;
  2. 利用链式法则,一层层“回溯”到输入层,求出对 W[2],b[2],W[1],b[1] 的偏导;
  3. 用这些梯度更新参数。

        虽然公式看起来复杂,但一旦掌握链式法则和矩阵求导规则,就能轻松推导。更重要的是,现代框架(如 PyTorch、TensorFlow)已自动完成这一过程,我们只需关注模型结构和数据。

10.浅层神经网络举例实现

'''
            O
  O         O       
  O         O      O
            O

n_x = 2
n_h = 4
n_y = 1
'''
import numpy as np
import matplotlib.pyplot as plt
import sklearn
import sklearn.datasets
import sklearn.linear_model
from load_data import load_planar_dataset


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

# 定义网络结构
# 网络输入输出以及隐藏层神经元个数
def layer_sizes(X, Y):
    n_x = X.shape[]     # 输入层神经元个数
    n_h = 4             # 隐藏层神经元个数
    n_y = Y.shape[]     # 输出层神经元个数
    return (n_x,n_h,n_y)

# 初始化模型参数
# 随机初始化权重以及偏置为0
def initialize_parameters(n_x,n_h,n_y):
    '''
    输入每层的神经元数量
    返回:隐藏层、输出层的参数
    '''
    # 1.初始化权重
    np.random.seed(2) #指定随机种子

    # 2.创建隐藏层的两个参数
    # randn:标准正态分布
    # rand:均匀分布[0,1)
    # 0.01权重缩放因子,激活值更合适,训练更稳定
    # 为什么是(n_h,n_x)?  矩阵乘法 W·X + b   X是(n_x,2)  
    W1 = np.random.randn(n_h,n_x)*0.01
    # zeros :接受一个矩阵元组
    b1 = np.zeros((n_h,1)) # 4行1列

    # 3.创建输出层的两个参数
    W2 = np.random.randn(n_y,n_h)*0.01
    b2 = np.randm.zeros((n_y,1))

    # 4.使用断言确保数据格式正确
    assert(W1.shape == (n_h,n_x))
    assert(b1.shape == (n_h,1))
    assert(W2.shape == (n_y,n_h))
    assert(b2.shape == (n_y,1))

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


# 循环中的第一步:前向传播
def forward_propagation(X,parameters):
    '''
    输入:(n_x,)
    '''
    # 1.取出每一层的参数
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]   
    b2 = parameters["b2"]

    # 2.计算隐藏层的激活值
    Z1 = np.dot(W1,X) + b1  
    A1 = np.tanh(Z1)      # 激活函数tanh

    # 3.计算输出层的激活值
    Z2 = np.dot(W2,A1) + b2
    A2 = sigmoid(Z2)     # 激活函数sigmoid

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

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

# 计算损失函数
def compute_cost(A2,Y,parameters):
    m = Y.shape[1]  # 样本数量
    logpro = np.multiply(np.log(A2),Y) + np.multiply(np.log(1-A2),1-Y)
    cost = (-1 / m)* np.sum(logpro)
    
    cost = np.squeeze(cost)  #确保cost是一个标量
    assert(isinstance(cost,float))
    return cost

# 循环中的第二步:反向传播
def backward_propagation(parameters,cache,X,Y):
    # 1.获取参数和缓存中的输出
    W1 = parameters["W1"]
    W2 = parameters["W2"]
    A1 = cache["A1"]
    A2 = cache["A2"]
    m = X.shape[1]

    # 2.反向传播计算梯度
    # 最后一层的参数梯度计算
    dZ2 = A2 - Y                      
    dW2 = (1/m) * np.dot(dZ2,A1.T)
    db2 = (1/m) * np.sum(dZ2,axis=1)  
    
    # 隐藏层的参数梯度计算
    dZ1 = np.dot(W2.T,dZ2) * (1 - np.power(A1,2))  
    dW1 = (1/m) * np.dot(dZ1,X.T)
    db1 = (1/m) * np.sum(dZ1,axis=1)

    grads = {
        "dW1":dW1,
        "db1":db1,
        "dW2":dW2,
        "db2":db2
    }
    return grads

# 循环中的第三步:参数更新
def update_parameters(parameters,grads,learning_rate=1.2):
    # 1.获取参数和梯度
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]

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

    # 2.更新参数
    W1 = W1 - learning_rate * dW1
    b1 = b1 - learning_rate * db1.reshape(b1.shape)
    W2 = W2 - learning_rate * dW2
    b2 = b2 - learning_rate * db2.reshape(b2.shape)

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


# 整合模型
def nn_model(X,Y,n_h,num_iterations=10000,print_cost=False):
    np.random.seed(3)
    n_x = layer_sizes(X,Y)[0]
    n_y = layer_sizes(X,Y)[2]

    # 1.初始化参数
    parameters = initialize_parameters(n_x,n_h,n_y)

    # 2.循环迭代
    for i in range(0,num_iterations):
        # 前向传播
        A2,cache = forward_propagation(X,parameters)

        # 计算损失函数
        cost = compute_cost(A2,Y,parameters)

        # 反向传播
        grads = backward_propagation(parameters,cache,X,Y)

        # 参数更新
        parameters = update_parameters(parameters,grads,learning_rate=1.2)

        # 每1000次打印一次损失函数
        if print_cost and i % 1000 == 0:
            print("迭代第 %i 次,损失函数为:%f" % (i,cost))
    return parameters

        从逻辑回归到浅层神经网络,我们看到了深度学习如何一步步构建起强大的表达能力。理解这些基础概念,不仅能帮助你写出更高效的代码,更能让你在面对复杂模型时保持清晰的思路。

        希望这篇文章能为你打开深度学习的大门。如果你觉得有收获,欢迎点赞、收藏、转发!也欢迎在评论区交流你的学习心得~

Logo

更多推荐