浅层神经网络:原理与Python实现

发布于:2025-07-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

神经网络概述

浅层神经网络(Single Hidden Layer Neural Network)是最基础的深度学习模型,包含输入层、一个隐藏层输出层。这种网络能够学习非线性关系,解决逻辑回归无法处理的复杂问题。

网络架构示例

我们构建一个具有以下结构的浅层神经网络:

  • 输入层:3个神经元(特征)
  • 隐藏层:4个神经元(使用非线性激活函数)
  • 输出层:1个神经元(二元分类)
输出层
隐藏层
输入层
o
h1
h2
h3
h4
x1
x2
x3

数学表示

  1. 前向传播
    Z [ 1 ] = W [ 1 ] X + b [ 1 ] A [ 1 ] = g [ 1 ] ( Z [ 1 ] ) Z [ 2 ] = W [ 2 ] A [ 1 ] + b [ 2 ] Y ^ = A [ 2 ] = g [ 2 ] ( Z [ 2 ] ) \begin{aligned} Z^{[1]} &= W^{[1]} X + b^{[1]} \\ A^{[1]} &= g^{[1]}(Z^{[1]}) \\ Z^{[2]} &= W^{[2]} A^{[1]} + b^{[2]} \\ \hat{Y} &= A^{[2]} = g^{[2]}(Z^{[2]}) \end{aligned} Z[1]A[1]Z[2]Y^=W[1]X+b[1]=g[1](Z[1])=W[2]A[1]+b[2]=A[2]=g[2](Z[2])

  2. 参数维度

    • W [ 1 ] W^{[1]} W[1]: (4, 3) 矩阵
    • b [ 1 ] b^{[1]} b[1]: (4, 1) 向量
    • W [ 2 ] W^{[2]} W[2]: (1, 4) 向量
    • b [ 2 ] b^{[2]} b[2]: (1, 1) 标量

激活函数详解

激活函数引入非线性,使神经网络能够学习复杂模式。以下是三种常用激活函数:

1. Tanh (双曲正切)

Tanh函数图
Tanh函数图

g ( z ) = e z − e − z e z + e − z g(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}} g(z)=ez+ezezez

导数:
g ′ ( z ) = 1 − g ( z ) 2 g'(z) = 1 - g(z)^2 g(z)=1g(z)2

特点:

  • 输出范围: (-1, 1)
  • 均值接近0,有助于中心化数据
  • 优于sigmoid函数

Tanh 函数存在和 sigmoid 函数一样的缺点:当 z 趋近于无穷大(或无穷小),导数的梯度就趋近于 0 ,这使得梯度算法的速度会减慢。

def tanh(z):
    return np.tanh(z)

def tanh_derivative(z):
    return 1 - np.tanh(z)**2

2. ReLU (修正线性单元)

ReLU函数图
ReLU函数图

g ( z ) = max ⁡ ( 0 , z ) g(z) = \max(0, z) g(z)=max(0,z)

导数:
g ′ ( z ) = { 1 if  z > 0 0 otherwise g'(z) = \begin{cases} 1 & \text{if } z > 0 \\ 0 & \text{otherwise} \end{cases} g(z)={10if z>0otherwise

特点:

  • 当 z > 0 时,梯度始终为1,能提高运算速度
  • 缓解梯度消失问题
  • 广泛用于隐藏层
  • 当 z < 0 时,梯度一直为0,但在实际应用中影响不大
def relu(z):
    return np.maximum(0, z)

def relu_derivative(z):
    return (z > 0).astype(float)

3. Leaky ReLU

Leaky ReLU 函数图
Leaky ReLU 函数图

g ( z ) = { z if  z > 0 α z otherwise g(z) = \begin{cases} z & \text{if } z > 0 \\ \alpha z & \text{otherwise} \end{cases} g(z)={zαzif z>0otherwise

导数:
g ′ ( z ) = { 1 if  z > 0 α otherwise g'(z) = \begin{cases} 1 & \text{if } z > 0 \\ \alpha & \text{otherwise} \end{cases} g(z)={1αif z>0otherwise

特点:

  • 解决ReLU的"死亡神经元"问题
  • α \alpha α通常设为0.01
def leaky_relu(z, alpha=0.01):
    return np.where(z > 0, z, alpha * z)

def leaky_relu_derivative(z, alpha=0.01):
    return np.where(z > 0, 1, alpha)

梯度计算(反向传播)

反向传播通过链式法则计算损失函数对参数的梯度:

先计算输出层梯度,再计算隐藏层梯度

梯度公式

  1. 输出层梯度:
    d Z [ 2 ] = A [ 2 ] − Y d W [ 2 ] = 1 m d Z [ 2 ] A [ 1 ] T d b [ 2 ] = 1 m ∑ d Z [ 2 ] \begin{aligned} dZ^{[2]} &= A^{[2]} - Y \\ dW^{[2]} &= \frac{1}{m} dZ^{[2]} A^{[1]T} \\ db^{[2]} &= \frac{1}{m} \sum dZ^{[2]} \end{aligned} dZ[2]dW[2]db[2]=A[2]Y=m1dZ[2]A[1]T=m1dZ[2]

  2. 隐藏层梯度:
    d Z [ 1 ] = W [ 2 ] T d Z [ 2 ] ⊙ g [ 1 ] ′ ( Z [ 1 ] ) = W [ 2 ] T d Z [ 2 ] ⊙ ( 1 − A [ 1 ] ) 2 d W [ 1 ] = 1 m d Z [ 1 ] X T d b [ 1 ] = 1 m ∑ d Z [ 1 ] \begin{aligned} dZ^{[1]} &= W^{[2]T} dZ^{[2]} \odot g^{[1]\prime}(Z^{[1]})= W^{[2]T} dZ^{[2]} \odot (1-A^{[1]})^2 \\ dW^{[1]} &= \frac{1}{m} dZ^{[1]} X^T \\ db^{[1]} &= \frac{1}{m} \sum dZ^{[1]} \end{aligned} dZ[1]dW[1]db[1]=W[2]TdZ[2]g[1](Z[1])=W[2]TdZ[2](1A[1])2=m1dZ[1]XT=m1dZ[1]

反向传播流程

计算输出层误差 dZ2 = A2 - Y
计算输出层梯度 dW2 = dZ2·A1ᵀ/m
计算隐藏层误差 dZ1 = W2ᵀ·dZ2 ⊙ g' [1]
计算隐藏层梯度 dW1 = dZ1·Xᵀ/m
计算偏置梯度 db2 = sum(dZ2)/m
计算偏置梯度 db1 = sum(dZ1)/m

完整Python实现

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

class ShallowNeuralNetwork:
    def __init__(self, input_size, hidden_size, activation='relu', learning_rate=0.01, epochs=1000):
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.lr = learning_rate
        self.epochs = epochs
        self.activation_type = activation
        
        # 初始化参数
        self.W1 = np.random.randn(hidden_size, input_size) * 0.01
        self.b1 = np.zeros((hidden_size, 1))
        self.W2 = np.random.randn(1, hidden_size) * 0.01
        self.b2 = np.zeros((1, 1))
        
        # 设置激活函数
        self.activation, self.activation_derivative = self._get_activation_functions()
        
    def _get_activation_functions(self):
        """选择激活函数及其导数"""
        if self.activation_type == 'tanh':
            return tanh, tanh_derivative
        elif self.activation_type == 'leaky_relu':
            return leaky_relu, leaky_relu_derivative
        else:  # 默认使用ReLU
            return relu, relu_derivative
    
    def _sigmoid(self, z):
        """输出层激活函数"""
        return 1 / (1 + np.exp(-z))
    
    def _forward_propagation(self, X):
        """前向传播"""
        # 隐藏层计算
        self.Z1 = np.dot(self.W1, X) + self.b1
        self.A1 = self.activation(self.Z1)
        
        # 输出层计算
        self.Z2 = np.dot(self.W2, self.A1) + self.b2
        self.A2 = self._sigmoid(self.Z2)
        
        return self.A2
    
    def _backward_propagation(self, X, Y):
        """反向传播"""
        m = X.shape[1]  # 样本数
        
        # 输出层梯度
        dZ2 = self.A2 - Y
        dW2 = (1/m) * np.dot(dZ2, self.A1.T)
        db2 = (1/m) * np.sum(dZ2, axis=1, keepdims=True)
        
        # 隐藏层梯度
        dZ1 = np.dot(self.W2.T, dZ2) * self.activation_derivative(self.Z1)
        dW1 = (1/m) * np.dot(dZ1, X.T)
        db1 = (1/m) * np.sum(dZ1, axis=1, keepdims=True)
        
        return dW1, db1, dW2, db2
    
    def _compute_loss(self, Y):
        """计算交叉熵损失"""
        m = Y.shape[1]
        loss = - (1/m) * np.sum(Y * np.log(self.A2) + (1 - Y) * np.log(1 - self.A2))
        return loss
    
    def fit(self, X, Y):
        """训练模型"""
        # 转置数据以匹配网络维度
        X = X.T
        Y = Y.reshape(1, -1)
        
        losses = []
        
        for epoch in range(self.epochs):
            # 前向传播
            _ = self._forward_propagation(X)
            
            # 计算损失
            loss = self._compute_loss(Y)
            losses.append(loss)
            
            # 反向传播
            dW1, db1, dW2, db2 = self._backward_propagation(X, Y)
            
            # 更新参数
            self.W1 -= self.lr * dW1
            self.b1 -= self.lr * db1
            self.W2 -= self.lr * dW2
            self.b2 -= self.lr * db2
            
            # 每100次迭代打印损失
            if epoch % 100 == 0:
                print(f"Epoch {epoch}, Loss: {loss:.4f}")
        
        return losses
    
    def predict(self, X, threshold=0.5):
        """预测类别"""
        X = X.T
        A2 = self._forward_propagation(X)
        predictions = (A2 > threshold).astype(int)
        return predictions.ravel()
    
    def predict_prob(self, X):
        """预测概率"""
        X = X.T
        return self._forward_propagation(X).ravel()

# 激活函数实现
def tanh(z):
    return np.tanh(z)

def tanh_derivative(z):
    return 1 - np.tanh(z)**2

def relu(z):
    return np.maximum(0, z)

def relu_derivative(z):
    return (z > 0).astype(float)

def leaky_relu(z, alpha=0.01):
    return np.where(z > 0, z, alpha * z)

def leaky_relu_derivative(z, alpha=0.01):
    return np.where(z > 0, 1, alpha)

# 测试与可视化
if __name__ == "__main__":
    # 创建非线性可分数据集
    X, y = make_classification(n_samples=500, n_features=3, n_redundant=0,
                               n_informative=3, n_clusters_per_class=1,
                               flip_y=0.1, class_sep=1.5, random_state=42)
    
    # 划分训练测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    
    # 创建并训练模型
    model = ShallowNeuralNetwork(input_size=3, hidden_size=4, 
                                activation='relu', learning_rate=0.1, epochs=2000)
    losses = model.fit(X_train, y_train)
    
    # 评估模型
    train_preds = model.predict(X_train)
    test_preds = model.predict(X_test)
    
    train_acc = np.mean(train_preds == y_train)
    test_acc = np.mean(test_preds == y_test)
    
    print(f"\n训练准确率: {train_acc:.4f}")
    print(f"测试准确率: {test_acc:.4f}")
    
    # 可视化训练过程
    plt.figure(figsize=(10, 6))
    plt.plot(losses)
    plt.title("Training Loss over Epochs")
    plt.xlabel("Epochs")
    plt.ylabel("Loss")
    plt.grid(True)
    plt.show()
    
    # 可视化决策边界 (使用前两个特征)
    plt.figure(figsize=(10, 8))
    plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap=plt.cm.Paired, 
                edgecolors='k', s=50, label="Training Data")
    plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap=plt.cm.Paired, 
                marker='s', edgecolors='k', s=50, label="Test Data")
    
    # 创建网格
    x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 100),
                         np.linspace(y_min, y_max, 100))
    
    # 固定第三个特征为中值
    zz = np.median(X[:, 2]) * np.ones_like(xx)
    grid = np.c_[xx.ravel(), yy.ravel(), zz.ravel()]
    
    # 预测概率
    probs = model.predict_prob(grid).reshape(xx.shape)
    
    # 绘制决策边界
    plt.contourf(xx, yy, probs > 0.5, alpha=0.3, cmap=plt.cm.Paired)
    plt.contour(xx, yy, probs, levels=[0.5], colors='red', linewidths=2)
    
    plt.title("Decision Boundary Visualization")
    plt.xlabel("Feature 1")
    plt.ylabel("Feature 2")
    plt.legend()
    plt.colorbar(plt.cm.ScalarMappable(cmap=plt.cm.Paired), label="Probability")
    plt.show()
  • 示例输出
损失率函数图
损失率函数图
Leaky ReLU 函数图
可视化决策边界图

代码解析

1. 神经网络类结构

class ShallowNeuralNetwork:
    def __init__(self, input_size, hidden_size, activation='relu', learning_rate=0.01, epochs=1000):
        # 参数初始化
        self.W1 = np.random.randn(hidden_size, input_size) * 0.01
        self.b1 = np.zeros((hidden_size, 1))
        # ... 其他参数
  • 参数初始化:权重使用小随机数,偏置初始化为零
  • 激活函数选择:根据参数选择tanh、ReLU或Leaky ReLU

2. 前向传播

def _forward_propagation(self, X):
    # 隐藏层计算
    self.Z1 = np.dot(self.W1, X) + self.b1
    self.A1 = self.activation(self.Z1)
    
    # 输出层计算
    self.Z2 = np.dot(self.W2, self.A1) + self.b2
    self.A2 = self._sigmoid(self.Z2)
  • 隐藏层使用选择的激活函数
  • 输出层使用sigmoid函数进行二元分类

3. 反向传播

def _backward_propagation(self, X, Y):
    # 输出层梯度
    dZ2 = self.A2 - Y
    dW2 = (1/m) * np.dot(dZ2, self.A1.T)
    
    # 隐藏层梯度
    dZ1 = np.dot(self.W2.T, dZ2) * self.activation_derivative(self.Z1)
    dW1 = (1/m) * np.dot(dZ1, X.T)
  • 计算输出层和隐藏层的梯度
  • 使用激活函数的导数进行链式法则计算

4. 训练过程

def fit(self, X, Y):
    for epoch in range(self.epochs):
        # 前向传播
        _ = self._forward_propagation(X)
        
        # 计算损失
        loss = self._compute_loss(Y)
        
        # 反向传播
        dW1, db1, dW2, db2 = self._backward_propagation(X, Y)
        
        # 更新参数
        self.W1 -= self.lr * dW1
        self.b1 -= self.lr * db1
        # ... 其他参数更新
  • 迭代训练过程
  • 每次迭代包含前向传播、损失计算、反向传播和参数更新

关键概念总结

  1. 网络架构

    • 输入层 → 隐藏层 → 输出层
    • 隐藏层引入非线性能力
  2. 激活函数选择

    函数 优点 缺点 适用场景
    Tanh 中心化输出,梯度更强 梯度消失问题 隐藏层
    ReLU 计算高效,缓解梯度消失 神经元"死亡"问题 最常用隐藏层
    Leaky ReLU 解决神经元死亡问题 额外超参数 需要更稳定训练时
  3. 梯度计算

    • 反向传播算法高效计算梯度
    • 链式法则分解复杂导数
    • 向量化实现提高计算效率
  4. 训练技巧

    • 小随机数初始化打破对称性
    • 学习率控制更新步长
    • 批量梯度下降稳定训练

应用与扩展

浅层神经网络适用于:

  • 中小规模数据集
  • 中等复杂度的非线性问题
  • 二元分类任务

可扩展为:

  • 多分类(使用softmax输出)
  • 深度神经网络(添加更多隐藏层)
  • 不同任务(回归、多标签分类等)

本文展示了神经网络的核心原理,通过调整超参数(隐藏层大小、学习率、激活函数)可以优化模型性能。理解这些基础知识是掌握更复杂深度学习模型的关键。