【机器学习基础算法:从零实现到精通】第7章 神经网络与深度学习基础

精选机器学习&深度学习视频资源合集 !(附下载链接) 来源:大数据挖掘DT数据分析 本文长度为1400字,建议阅读3分钟 本文为你分享一份机器学习&深度学习视频资源合集,一起充电吧~ 第一部分 基础语言 pandax视频教程 链接: https://pan.baidu.com/s/1pLqavVX 密码: fath python入门到精通 链接: https://pan.baidu.com/s/1m 阅读详情

目录

7.1 前馈神经网络(MLP)

7.1.1 感知机与多层架构

7.1.2 激活函数与归一化

7.2 正则化与优化进阶

7.2.1 Dropout与集成视角

7.2.2 高级优化器与二阶方法

7.3 卷积神经网络(CNN)基础

7.3.1 卷积运算与反向传播

7.3.2 经典架构解析


7.1 前馈神经网络(MLP)

7.1.1 感知机与多层架构

多层感知机的万有逼近能力由Cybenko定理严格证明,具有单隐藏层与Sigmoid激活的神经网络以任意精度逼近紧集上的连续函数,该证明基于Hahn-Banach定理与Riesz表示定理,通过构造性方法说明有限个神经元的叠加足以表达复杂函数关系。反向传播算法通过计算图自动微分高效求解梯度,层间delta变量传递误差信号,权重梯度由激活值与delta的外积累积,计算复杂度与网络连接数线性相关。动态计算图在运行时构建,支持条件分支与循环结构,适用于复杂控制流;静态计算图预定义运算流程,支持全局优化与序列化部署,两者在表达能力和执行效率间呈现不同权衡。

7.1.2 激活函数与归一化

Sigmoid与Tanh激活函数在输入绝对值较大时进入饱和区,梯度趋近于零导致梯度消失问题,Xavier初始化通过调整权重方差为输入输出维度倒数之和的倒数,保持信号在网络中的方差一致性,避免前向传播时激活值指数级增长或衰减。ReLU激活在负半区硬饱和,大量神经元可能永久失活,LeakyReLU引入负半区微小斜率保留梯度流,ELU通过指数函数平滑负半区加速收敛。批归一化在 mini-batch 维度计算均值与方差,标准化后通过可学习参数恢复表达能力,推理阶段冻结训练集统计量确保确定性输出,有效缓解内部协变量偏移问题,允许使用更大学习率并降低对初始化的敏感性。

脚本17:MLP实现与优化技术

"""
脚本17:前馈神经网络(MLP)实现与优化技术
涉及内容:
- 7.1.1.2 反向传播算法与动态计算图实现
- 7.1.2.1 Xavier/Glorot初始化与He初始化实现
- 7.1.2.2 ReLU/LeakyReLU/ELU激活函数及其梯度
- 7.1.2.3 BatchNorm的实现:训练/推理模式切换

使用方式:
python mlp_implementation.py
依赖:numpy, matplotlib
"""

import numpy as np
import matplotlib.pyplot as plt
from typing import List, Tuple, Optional, Dict
from collections import defaultdict

class ComputationGraph:
    """
    动态计算图实现(PyTorch风格)
    支持自动微分与反向传播
    """
    
    def __init__(self):
        self.operations = []
        self.gradients = {}
        self.cache = {}  # 前向传播缓存用于反向传播
        
    def clear(self):
        self.operations = []
        self.gradients = {}
        self.cache = {}
    
    def add_operation(self, name, inputs, output, backward_func):
        """记录操作及其反向传播函数"""
        self.operations.append({
            'name': name,
            'inputs': inputs,
            'output': output,
            'backward': backward_func
        })
    
    def backward(self, loss_grad=1.0):
        """
        7.1.1.2 反向传播:从输出向输入传播梯度
        """
        self.gradients[self.operations[-1]['output']] = loss_grad
        
        # 逆序遍历操作
        for op in reversed(self.operations):
            grad_output = self.gradients.get(op['output'], 0)
            input_grads = op['backward'](grad_output, self.cache.get(op['name']))
            
            # 累积输入梯度
            if not isinstance(op['inputs'], list):
                inputs = [op['inputs']]
                input_grads = [input_grads] if not isinstance(input_grads, (list, tuple)) else input_grads
            else:
                inputs = op['inputs']
            
            for inp, grad in zip(inputs, input_grads):
                if inp in self.gradients:
                    self.gradients[inp] += grad
                else:
                    self.gradients[inp] = grad


class Initializers:
    """
    7.1.2.1 各种初始化方法实现
    """
    
    @staticmethod
    def xavier_uniform(shape, gain=1.0):
        """
        Xavier/Glorot初始化:适用于Sigmoid/Tanh
        limit = gain * sqrt(6 / (fan_in + fan_out))
        """
        fan_in, fan_out = shape[0], shape[1] if len(shape) > 1 else shape[0]
        limit = gain * np.sqrt(6.0 / (fan_in + fan_out))
        return np.random.uniform(-limit, limit, size=shape)
    
    @staticmethod
    def xavier_normal(shape, gain=1.0):
        """Xavier正态分布初始化"""
        fan_in, fan_out = shape[0], shape[1] if len(shape) > 1 else shape[0]
        std = gain * np.sqrt(2.0 / (fan_in + fan_out))
        return np.random.normal(0, std, size=shape)
    
    @staticmethod
    def he_normal(shape):
        """
        He初始化:适用于ReLU激活
        std = sqrt(2 / fan_in)
        """
        fan_in = shape[0]
        std = np.sqrt(2.0 / fan_in)
        return np.random.normal(0, std, size=shape)
    
    @staticmethod
    def he_uniform(shape):
        """He均匀分布初始化"""
        fan_in = shape[0]
        limit = np.sqrt(6.0 / fan_in)
        return np.random.uniform(-limit, limit, size=shape)


class Activations:
    """激活函数及其导数"""
    
    @staticmethod
    def relu(x):
        return np.maximum(0, x)
    
    @staticmethod
    def relu_grad(x):
        return (x > 0).astype(float)
    
    @staticmethod
    def leaky_relu(x, alpha=0.01):
        """7.1.2.2 LeakyReLU:解决Dying ReLU问题"""
        return np.where(x > 0, x, alpha * x)
    
    @staticmethod
    def leaky_relu_grad(x, alpha=0.01):
        return np.where(x > 0, 1, alpha)
    
    @staticmethod
    def elu(x, alpha=1.0):
        """ELU:指数线性单元"""
        return np.where(x > 0, x, alpha * (np.exp(x) - 1))
    
    @staticmethod
    def elu_grad(x, alpha=1.0):
        return np.where(x > 0, 1, alpha * np.exp(x))
    
    @staticmethod
    def sigmoid(x):
        return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
    
    @staticmethod
    def sigmoid_grad(x):
        s = Activations.sigmoid(x)
        return s * (1 - s)
    
    @staticmethod
    def tanh(x):
        return np.tanh(x)
    
    @staticmethod
    def tanh_grad(x):
        return 1 - np.tanh(x)**2


class BatchNorm:
    """
    7.1.2.3 批归一化层实现
    支持训练与推理模式切换
    """
    
    def __init__(self, num_features, eps=1e-5, momentum=0.1):
        self.num_features = num_features
        self.eps = eps
        self.momentum = momentum
        
        # 可学习参数
        self.gamma = np.ones(num_features)
        self.beta = np.zeros(num_features)
        
        # 运行时统计量(推理用)
        self.running_mean = np.zeros(num_features)
        self.running_var = np.ones(num_features)
        
        self.training = True
        
    def __call__(self, x):
        if self.training:
            # 训练模式:使用mini-batch统计量
            batch_mean = np.mean(x, axis=0)
            batch_var = np.var(x, axis=0)
            
            # 更新运行时统计量(移动平均)
            self.running_mean = (1 - self.momentum) * self.running_mean + \
                               self.momentum * batch_mean
            self.running_var = (1 - self.momentum) * self.running_var + \
                              self.momentum * batch_var
            
            # 标准化
            x_norm = (x - batch_mean) / np.sqrt(batch_var + self.eps)
            
            # 缓存用于反向传播
            self.cache = {
                'x': x, 'x_norm': x_norm, 'mean': batch_mean, 
                'var': batch_var, 'std': np.sqrt(batch_var + self.eps)
            }
        else:
            # 推理模式:使用训练时统计量
            x_norm = (x - self.running_mean) / np.sqrt(self.running_var + self.eps)
        
        # 缩放与偏移
        out = self.gamma * x_norm + self.beta
        return out
    
    def backward(self, dout):
        """BatchNorm反向传播"""
        if not self.training:
            raise RuntimeError("Cannot backprop in eval mode")
        
        x, x_norm, mean, var, std = self.cache['x'], self.cache['x_norm'], \
                                    self.cache['mean'], self.cache['var'], self.cache['std']
        N = x.shape[0]
        
        # 计算梯度
        dbeta = np.sum(dout, axis=0)
        dgamma = np.sum(dout * x_norm, axis=0)
        
        dx_norm = dout * self.gamma
        
        # 计算对x的梯度(复杂链式法则)
        dvar = np.sum(dx_norm * (x - mean), axis=0) * (-0.5) * (var + self.eps)**(-1.5)
        dmean = np.sum(dx_norm * (-1 / std), axis=0) + dvar * np.sum(-2 * (x - mean), axis=0) / N
        
        dx = dx_norm / std + dvar * 2 * (x - mean) / N + dmean / N
        
        return dx, dgamma, dbeta


class MLP:
    """
    多层感知机实现
    包含动态计算图、多种初始化与激活函数
    """
    
    def __init__(self, layer_sizes, activation='relu', use_batchnorm=False, 
                 init_method='he', dropout_rate=0.0):
        self.layer_sizes = layer_sizes
        self.activation_name = activation
        self.use_batchnorm = use_batchnorm
        self.dropout_rate = dropout_rate
        
        # 选择初始化方法
        init_map = {
            'xavier': Initializers.xavier_uniform,
            'he': Initializers.he_normal
        }
        self.initializer = init_map.get(init_method, Initializers.he_normal)
        
        # 初始化权重与偏置
        self.params = {}
        self.batch_norms = []
        
        for i in range(len(layer_sizes) - 1):
            self.params[f'W{i}'] = self.initializer((layer_sizes[i], layer_sizes[i+1]))
            self.params[f'b{i}'] = np.zeros(layer_sizes[i+1])
            
            if use_batchnorm and i < len(layer_sizes) - 2:  # 输出层不加BN
                self.batch_norms.append(BatchNorm(layer_sizes[i+1]))
            else:
                self.batch_norms.append(None)
        
        self.graph = ComputationGraph()
        
    def forward(self, X, training=True):
        """前向传播"""
        self.graph.clear()
        self.cache = {'activations': [], 'z_values': []}
        
        current = X
        
        for i in range(len(self.layer_sizes) - 1):
            # 线性变换
            z = current @ self.params[f'W{i}'] + self.params[f'b{i}']
            
            # BatchNorm
            if self.batch_norms[i] is not None:
                self.batch_norms[i].training = training
                z = self.batch_norms[i](z)
            
            self.cache['z_values'].append(z)
            
            # 激活函数(最后一层除外)
            if i < len(self.layer_sizes) - 2:
                if self.activation_name == 'relu':
                    current = Activations.relu(z)
                elif self.activation_name == 'leaky_relu':
                    current = Activations.leaky_relu(z)
                elif self.activation_name == 'elu':
                    current = Activations.elu(z)
                elif self.activation_name == 'sigmoid':
                    current = Activations.sigmoid(z)
                elif self.activation_name == 'tanh':
                    current = Activations.tanh(z)
            else:
                current = z  # 最后一层通常无激活或Softmax
            
            # Dropout(训练时)
            if training and self.dropout_rate > 0 and i < len(self.layer_sizes) - 2:
                mask = (np.random.rand(*current.shape) > self.dropout_rate).astype(float)
                current = current * mask / (1 - self.dropout_rate)  # 缩放
            
            self.cache['activations'].append(current)
        
        return current
    
    def compute_loss(self, y_pred, y_true, loss_type='mse'):
        """计算损失"""
        if loss_type == 'mse':
            loss = np.mean((y_pred - y_true)**2)
            grad = 2 * (y_pred - y_true) / len(y_true)
        elif loss_type == 'cross_entropy':
            # Softmax + CrossEntropy
            exp_scores = np.exp(y_pred - np.max(y_pred, axis=1, keepdims=True))
            probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
            correct_logprobs = -np.log(probs[range(len(y_true)), y_true] + 1e-8)
            loss = np.sum(correct_logprobs) / len(y_true)
            grad = probs.copy()
            grad[range(len(y_true)), y_true] -= 1
            grad /= len(y_true)
        
        return loss, grad
    
    def backward(self, loss_grad):
        """反向传播计算梯度"""
        # 简化版反向传播(直接从缓存计算)
        grads = {}
        delta = loss_grad
        
        for i in reversed(range(len(self.layer_sizes) - 1)):
            if i < len(self.cache['z_values']) - 1:  # 非最后一层
                # 激活函数梯度
                z = self.cache['z_values'][i]
                if self.activation_name == 'relu':
                    delta *= Activations.relu_grad(z)
                elif self.activation_name == 'leaky_relu':
                    delta *= Activations.leaky_relu_grad(z)
                elif self.activation_name == 'sigmoid':
                    delta *= Activations.sigmoid_grad(z)
                elif self.activation_name == 'tanh':
                    delta *= Activations.tanh_grad(z)
            
            # BatchNorm梯度
            if self.batch_norms[i] is not None:
                delta, dgamma, dbeta = self.batch_norms[i].backward(delta)
                grads[f'bn_gamma{i}'] = dgamma
                grads[f'bn_beta{i}'] = dbeta
            
            # 线性层梯度
            a_prev = self.cache['activations'][i-1] if i > 0 else self.cache['input']
            grads[f'W{i}'] = a_prev.T @ delta
            grads[f'b{i}'] = np.sum(delta, axis=0)
            
            # 传播到前一层
            if i > 0:
                delta = delta @ self.params[f'W{i}'].T
        
        return grads
    
    def train_step(self, X, y, learning_rate=0.01, loss_type='mse'):
        """单步训练"""
        self.cache['input'] = X
        
        # 前向
        output = self.forward(X, training=True)
        loss, loss_grad = self.compute_loss(output, y, loss_type)
        
        # 反向
        grads = self.backward(loss_grad)
        
        # 更新参数
        for key in self.params:
            self.params[key] -= learning_rate * grads[key]
        
        # 更新BatchNorm参数
        for i, bn in enumerate(self.batch_norms):
            if bn is not None:
                bn.gamma -= learning_rate * grads.get(f'bn_gamma{i}', 0)
                bn.beta -= learning_rate * grads.get(f'bn_beta{i}', 0)
        
        return loss


def demo_initialization_activation():
    """演示初始化与激活函数效果"""
    np.random.seed(42)
    
    # 生成数据
    X = np.random.randn(1000, 784)  # 模拟MNIST展平图像
    
    fig, axes = plt.subplots(2, 3, figsize=(18, 12))
    
    # 1. 不同初始化的激活值分布对比
    init_methods = ['xavier', 'he']
    activations = ['sigmoid', 'tanh', 'relu']
    
    for idx, init in enumerate(init_methods):
        for act_idx, act in enumerate(activations):
            ax = axes[idx, act_idx]
            
            # 创建网络
            mlp = MLP([784, 256, 256, 10], activation=act, init_method=init)
            
            # 前向传播到第二层
            h1 = X @ mlp.params['W0'] + mlp.params['b0']
            
            if act == 'sigmoid':
                h1_act = Activations.sigmoid(h1)
            elif act == 'tanh':
                h1_act = Activations.tanh(h1)
            else:
                h1_act = Activations.relu(h1)
            
            # 绘制分布
            ax.hist(h1_act.flatten(), bins=50, alpha=0.7, color='steelblue', edgecolor='black')
            ax.set_title(f'{init.capitalize()} Init + {act.upper()}\n'
                        f'Mean={np.mean(h1_act):.2f}, Std={np.std(h1_act):.2f}')
            ax.set_xlabel('Activation Value')
            ax.set_ylabel('Count')
            ax.grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig('initialization_activation_analysis.png', dpi=150, bbox_inches='tight')
    plt.show()
    
    print("初始化与激活函数分析完成")


if __name__ == "__main__":
    demo_initialization_activation()

7.2 正则化与优化进阶

7.2.1 Dropout与集成视角

Dropout在训练阶段以概率p随机丢弃神经元,相当于训练指数级数量的共享权重子网络集成,推理阶段通过权重缩放(乘以p)近似所有子网络的平均预测,该解释揭示了Dropout作为模型平均正则化的本质。DropConnect扩展至权重层面随机丢弃连接,SpatialDropout在空间维度执行丢弃保留特征图通道一致性,适用于卷积层防止过拟合。Monte Carlo Dropout在推理时保持Dropout开启多次采样,将预测方差作为不确定性估计,为深度网络提供贝叶斯近似推断能力,无需修改网络结构即可获得概率预测。

7.2.2 高级优化器与二阶方法

AdamW解耦权重衰减与L2正则化,传统L2正则化在自适应梯度算法中与学习率耦合,导致正则化效果随学习率调度变化,AdamW将权重衰减直接应用于参数更新步骤,与梯度自适应机制分离,实现真正的权重衰减正则化。学习率调度策略中,Warmup阶段线性增加学习率防止早期震荡,Cosine Annealing按余弦曲线衰减学习率平滑收敛,ReduceLROnPlateau在验证指标停滞时自动降低学习率。L-BFGS利用有限内存近似Hessian逆矩阵,通过两次循环递归计算搜索方向,结合强Wolfe线搜索确保收敛,在中小规模数据集上收敛速度优于随机梯度方法,但内存需求随历史记录长度线性增长。

脚本18:正则化技术与优化器实现

"""
脚本18:正则化技术与优化器实现
涉及内容:
- 7.2.1.1 Dropout与DropConnect实现
- 7.2.1.3 Monte Carlo Dropout不确定性估计
- 7.2.2.1 AdamW优化器(解耦权重衰减)
- 7.2.2.2 学习率调度器(Warmup/Cosine/Plateau)
- 7.2.2.3 L-BFGS简化实现

使用方式:
python regularization_optimizers.py
依赖:numpy, matplotlib
"""

import numpy as np
import matplotlib.pyplot as plt
from typing import List, Dict, Optional, Callable
from collections import deque

class Dropout:
    """
    7.2.1.1 Dropout实现
    训练时随机丢弃,推理时权重缩放
    """
    
    def __init__(self, p=0.5):
        self.p = p  # 保留概率
        self.training = True
        self.mask = None
        
    def forward(self, x):
        if self.training:
            # 生成mask并缩放(inverted dropout)
            self.mask = (np.random.rand(*x.shape) < self.p).astype(float)
            return x * self.mask / self.p  # 缩放确保期望一致
        else:
            # 推理时不做操作(权重已训练时缩放)
            return x
    
    def backward(self, dout):
        return dout * self.mask / self.p if self.mask is not None else dout


class DropConnect:
    """
    7.2.1.2 DropConnect:随机丢弃权重连接
    """
    
    def __init__(self, p=0.5):
        self.p = p
        
    def apply(self, weights):
        """应用于权重矩阵"""
        if self.p < 1.0:
            mask = (np.random.rand(*weights.shape) < self.p).astype(float)
            return weights * mask / self.p
        return weights


class AdamW:
    """
    7.2.2.1 AdamW优化器实现
    解耦权重衰减与L2正则化
    """
    
    def __init__(self, params, lr=0.001, betas=(0.9, 0.999), eps=1e-8, 
                 weight_decay=0.01):
        self.params = params
        self.lr = lr
        self.beta1, self.beta2 = betas
        self.eps = eps
        self.weight_decay = weight_decay  # 解耦的权重衰减系数
        
        self.m = {}  # 一阶矩
        self.v = {}  # 二阶矩
        self.t = 0
        
        for key in params:
            self.m[key] = np.zeros_like(params[key])
            self.v[key] = np.zeros_like(params[key])
    
    def step(self, grads):
        """单步更新"""
        self.t += 1
        
        for key in self.params:
            if key not in grads:
                continue
            
            # 解耦权重衰减(直接应用于参数,不通过梯度)
            if self.weight_decay > 0:
                self.params[key] *= (1 - self.lr * self.weight_decay)
            
            # 计算梯度(仅损失梯度,不含L2正则化)
            g = grads[key]
            
            # 更新一阶矩(动量)
            self.m[key] = self.beta1 * self.m[key] + (1 - self.beta1) * g
            
            # 更新二阶矩(自适应学习率)
            self.v[key] = self.beta2 * self.v[key] + (1 - self.beta2) * (g ** 2)
            
            # 偏差修正
            m_hat = self.m[key] / (1 - self.beta1 ** self.t)
            v_hat = self.v[key] / (1 - self.beta2 ** self.t)
            
            # 参数更新
            self.params[key] -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
        
        return self.params


class LRScheduler:
    """
    7.2.2.2 学习率调度器实现
    """
    
    @staticmethod
    def warmup_cosine(epoch, warmup_epochs, total_epochs, base_lr, min_lr=1e-6):
        """
        Warmup + Cosine Annealing调度
        """
        if epoch < warmup_epochs:
            # 线性warmup
            return base_lr * (epoch + 1) / warmup_epochs
        else:
            # 余弦退火
            progress = (epoch - warmup_epochs) / (total_epochs - warmup_epochs)
            return min_lr + 0.5 * (base_lr - min_lr) * (1 + np.cos(np.pi * progress))
    
    @staticmethod
    def step_decay(epoch, base_lr, drop_rate=0.5, epochs_drop=10):
        """阶梯衰减"""
        return base_lr * (drop_rate ** (epoch // epochs_drop))
    
    @staticmethod
    def exponential_decay(epoch, base_lr, decay_rate=0.95):
        """指数衰减"""
        return base_lr * (decay_rate ** epoch)


class ReduceLROnPlateau:
    """
    验证指标停滞时降低学习率
    """
    
    def __init__(self, optimizer, mode='min', factor=0.5, patience=5, 
                 min_lr=1e-6):
        self.optimizer = optimizer
        self.mode = mode
        self.factor = factor
        self.patience = patience
        self.min_lr = min_lr
        
        self.best = np.inf if mode == 'min' else -np.inf
        self.num_bad_epochs = 0
        
    def step(self, metric):
        """检查并更新学习率"""
        current = metric
        
        if self.mode == 'min':
            improved = current < self.best
        else:
            improved = current > self.best
        
        if improved:
            self.best = current
            self.num_bad_epochs = 0
        else:
            self.num_bad_epochs += 1
        
        if self.num_bad_epochs > self.patience:
            new_lr = max(self.optimizer.lr * self.factor, self.min_lr)
            if new_lr < self.optimizer.lr:
                print(f"Reducing LR: {self.optimizer.lr:.6f} -> {new_lr:.6f}")
                self.optimizer.lr = new_lr
            self.num_bad_epochs = 0


class LBFGS:
    """
    7.2.2.3 L-BFGS简化实现
    有限内存BFGS优化器
    """
    
    def __init__(self, params, lr=1.0, max_iter=20, max_eval=25, 
                 tolerance_grad=1e-5, tolerance_change=1e-9, 
                 history_size=10, line_search_fn='strong_wolfe'):
        self.params = params
        self.lr = lr
        self.max_iter = max_iter
        self.tolerance_grad = tolerance_grad
        self.history_size = history_size
        
        # 存储历史
        self.s = deque(maxlen=history_size)  # 参数变化
        self.y = deque(maxlen=history_size)  # 梯度变化
        self.rho = deque(maxlen=history_size)  # 曲率
        
        self.prev_flat_grad = None
        self.prev_flat_params = None
        
    def _flatten(self, params_dict):
        """将参数字典展平为向量"""
        return np.concatenate([v.ravel() for v in params_dict.values()])
    
    def _unflatten(self, flat_params, template_dict):
        """将向量恢复为字典"""
        result = {}
        idx = 0
        for key, val in template_dict.items():
            size = val.size
            result[key] = flat_params[idx:idx+size].reshape(val.shape)
            idx += size
        return result
    
    def step(self, loss_fn, grad_fn):
        """
        L-BFGS优化步骤
        loss_fn: 计算损失的函数
        grad_fn: 计算梯度的函数
        """
        # 当前参数与梯度
        flat_params = self._flatten(self.params)
        flat_grad = self._flatten(grad_fn())
        
        # 检查收敛
        if np.linalg.norm(flat_grad) < self.tolerance_grad:
            return True  # 已收敛
        
        # 计算搜索方向(Two-Loop Recursion)
        q = flat_grad.copy()
        alphas = []
        
        # First loop
        for s, y, rho in zip(reversed(self.s), reversed(self.y), reversed(self.rho)):
            alpha = rho * np.dot(s, q)
            alphas.append(alpha)
            q -= alpha * y
        
        # 初始Hessian近似(单位矩阵缩放)
        if len(self.s) > 0:
            gamma = np.dot(self.s[-1], self.y[-1]) / np.dot(self.y[-1], self.y[-1])
            r = gamma * q
        else:
            r = q
        
        # Second loop
        for s, y, rho, alpha in zip(self.s, self.y, self.rho, reversed(alphas)):
            beta = rho * np.dot(y, r)
            r += s * (alpha - beta)
        
        direction = -r  # 搜索方向
        
        # 线搜索(简化版:固定步长)
        step_size = self.lr
        new_params = flat_params + step_size * direction
        self.params = self._unflatten(new_params, self.params)
        
        # 计算新的梯度用于曲率更新
        new_flat_grad = self._flatten(grad_fn())
        
        # 更新历史
        if self.prev_flat_grad is not None:
            s = new_params - self.prev_flat_params
            y = new_flat_grad - self.prev_flat_grad
            
            if np.dot(s, y) > 1e-10:  # 确保曲率条件
                self.s.append(s)
                self.y.append(y)
                self.rho.append(1.0 / np.dot(y, s))
        
        self.prev_flat_grad = new_flat_grad.copy()
        self.prev_flat_params = new_params.copy()
        
        return False  # 未收敛


class MCDropout:
    """
    7.2.1.3 Monte Carlo Dropout
    通过多次推理获得不确定性估计
    """
    
    def __init__(self, model, n_samples=100):
        self.model = model
        self.n_samples = n_samples
        
    def predict_with_uncertainty(self, X):
        """
        返回预测均值与方差(不确定性)
        """
        predictions = []
        
        # 启用训练模式(Dropout激活)
        for _ in range(self.n_samples):
            # 确保模型在训练模式(Dropout开启)
            pred = self.model.forward(X, training=True)
            predictions.append(pred)
        
        predictions = np.array(predictions)
        mean_pred = np.mean(predictions, axis=0)
        uncertainty = np.var(predictions, axis=0)  # 预测方差作为不确定性
        
        return mean_pred, uncertainty


def demo_optimizers():
    """演示优化器与学习率调度"""
    from sklearn.datasets import make_classification
    from sklearn.model_selection import train_test_split
    
    np.random.seed(42)
    
    # 生成二分类数据
    X, y = make_classification(n_samples=1000, n_features=20, n_informative=10,
                              n_redundant=5, n_classes=2, random_state=42)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    
    # 转换为one-hot
    y_train_oh = np.eye(2)[y_train]
    
    fig, axes = plt.subplots(2, 2, figsize=(16, 12))
    
    # 1. 不同优化器对比
    optimizers = {
        'SGD': lambda p: {'sgd': True, 'lr': 0.01},  # 简化标记
        'Adam': lambda p: {'adam': True, 'lr': 0.001},
        'AdamW': lambda p: AdamW(p, lr=0.001, weight_decay=0.01)
    }
    
    colors = ['blue', 'green', 'red']
    
    for (name, opt_fn), color in zip(optimizers.items(), colors):
        # 简化:模拟损失曲线
        epochs = 100
        if 'AdamW' in name:
            # AdamW通常收敛更快且更稳定
            loss_curve = 1.0 * np.exp(-0.1 * np.arange(epochs)) + 0.1 + \
                        np.random.randn(epochs) * 0.02
        elif 'Adam' in name:
            loss_curve = 1.0 * np.exp(-0.08 * np.arange(epochs)) + 0.15 + \
                        np.random.randn(epochs) * 0.03
        else:
            loss_curve = 1.0 * np.exp(-0.05 * np.arange(epochs)) + 0.2 + \
                        np.random.randn(epochs) * 0.05
        
        axes[0, 0].plot(loss_curve, label=name, color=color, linewidth=2)
    
    axes[0, 0].set_xlabel('Epoch')
    axes[0, 0].set_ylabel('Loss')
    axes[0, 0].set_title('7.2.2.1 Optimizer Comparison\n(AdamW vs Adam vs SGD)')
    axes[0, 0].legend()
    axes[0, 0].grid(True, alpha=0.3)
    
    # 2. 学习率调度可视化
    epochs = 200
    warmup_epochs = 20
    base_lr = 0.01
    
    lr_warmup_cosine = [LRScheduler.warmup_cosine(e, warmup_epochs, epochs, base_lr) 
                       for e in range(epochs)]
    lr_step = [LRScheduler.step_decay(e, base_lr) for e in range(epochs)]
    lr_exp = [LRScheduler.exponential_decay(e, base_lr) for e in range(epochs)]
    
    axes[0, 1].plot(lr_warmup_cosine, label='Warmup + Cosine', linewidth=2)
    axes[0, 1].plot(lr_step, label='Step Decay', linewidth=2, linestyle='--')
    axes[0, 1].plot(lr_exp, label='Exponential', linewidth=2, linestyle=':')
    axes[0, 1].axvline(warmup_epochs, color='red', linestyle='--', alpha=0.5, label='Warmup End')
    axes[0, 1].set_xlabel('Epoch')
    axes[0, 1].set_ylabel('Learning Rate')
    axes[0, 1].set_title('7.2.2.2 Learning Rate Schedulers')
    axes[0, 1].legend()
    axes[0, 1].grid(True, alpha=0.3)
    axes[0, 1].set_yscale('log')
    
    # 3. MC Dropout不确定性可视化
    x_test = np.linspace(-3, 3, 100).reshape(-1, 1)
    
    # 模拟模型预测(带噪声)
    true_mean = np.sin(x_test).ravel()
    noise = 0.2
    
    # 多次采样模拟MC Dropout
    n_mc = 100
    mc_preds = np.array([true_mean + np.random.randn(100) * noise * (1 + 0.5 * np.abs(x_test.ravel())) 
                        for _ in range(n_mc)])
    
    pred_mean = np.mean(mc_preds, axis=0)
    pred_std = np.std(mc_preds, axis=0)
    
    axes[1, 0].scatter(x_test, true_mean + np.random.randn(100) * 0.1, 
                      alpha=0.3, s=20, label='Observations', color='gray')
    axes[1, 0].plot(x_test, pred_mean, 'b-', linewidth=2, label='MC Dropout Mean')
    axes[1, 0].fill_between(x_test.ravel(), 
                           pred_mean - 2*pred_std, 
                           pred_mean + 2*pred_std,
                           alpha=0.3, color='blue', label='95% Uncertainty')
    axes[1, 0].set_xlabel('Input')
    axes[1, 0].set_ylabel('Output')
    axes[1, 0].set_title('7.2.1.3 MC Dropout Uncertainty Estimation\n(Aleatoric + Epistemic)')
    axes[1, 0].legend()
    axes[1, 0].grid(True, alpha=0.3)
    
    # 4. L-BFGS vs SGD收敛路径(简化2D可视化)
    # 模拟Rosenbrock函数优化路径
    def rosenbrock(x, y):
        return (1 - x)**2 + 100 * (y - x**2)**2
    
    x = np.linspace(-2, 2, 100)
    y = np.linspace(-1, 3, 100)
    X_grid, Y_grid = np.meshgrid(x, y)
    Z = rosenbrock(X_grid, Y_grid)
    
    axes[1, 1].contour(X_grid, Y_grid, Z, levels=20, cmap='viridis', alpha=0.6)
    
    # 模拟SGD路径(带噪声,波动大)
    sgd_path_x = np.linspace(-1.5, 1, 20) + np.random.randn(20) * 0.1
    sgd_path_y = sgd_path_x**2 + np.random.randn(20) * 0.2
    
    # 模拟L-BFGS路径(平滑,直接)
    lbfgs_path_x = np.linspace(-1.5, 1, 10)
    lbfgs_path_y = lbfgs_path_x**2
    
    axes[1, 1].plot(sgd_path_x, sgd_path_y, 'r.-', label='SGD Path (Noisy)', markersize=8)
    axes[1, 1].plot(lbfgs_path_x, lbfgs_path_y, 'b.-', label='L-BFGS Path (Smooth)', markersize=8)
    axes[1, 1].plot(1, 1, 'r*', markersize=15, label='Optimum')
    axes[1, 1].set_xlabel('x')
    axes[1, 1].set_ylabel('y')
    axes[1, 1].set_title('7.2.2.3 L-BFGS vs SGD Optimization Path\n(Second-order vs First-order)')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig('optimization_regularization.png', dpi=150, bbox_inches='tight')
    plt.show()
    
    print("优化器与正则化分析完成")


if __name__ == "__main__":
    demo_optimizers()


7.3 卷积神经网络(CNN)基础

7.3.1 卷积运算与反向传播

卷积运算通过im2col技术转化为矩阵乘法,将输入特征图的局部感受野展开为列向量,卷积核展平为行向量,利用高度优化的GEMM库加速计算,该展开虽增加内存占用但显著提升计算效率。转置卷积(反卷积)用于上采样,通过插入零值并执行常规卷积实现,不当的核大小与步长配置导致棋盘状伪影,Resize-Conv方案先通过插值调整分辨率再执行卷积,有效消除高频伪影。空洞卷积在不增加参数前提下扩大感受野,通过插入空洞扩展卷积核覆盖范围,但连续使用相同空洞率造成网格效应(Gridding),感受野覆盖不连续,Hybrid Dilated Convolution策略采用不同空洞率组合(如1,2,5)确保覆盖连续性,ASPP并行多尺度空洞卷积捕获多尺度上下文。

7.3.2 经典架构解析

ResNet通过残差学习框架解决深层网络退化问题,恒等映射捷径连接允许梯度直接回传,避免多层堆叠导致的梯度消失,残差块学习恒等映射的扰动而非完整映射,降低学习目标复杂度。BatchNorm与残差结构的协同作用显著:归一化层稳定信号分布,恒等连接提供梯度高速公路,两者结合使训练超深网络(100+层)成为可能。深度可分离卷积将标准卷积分解为深度卷积(单通道空间滤波)与点卷积(1×1通道混合),计算复杂度从O(DK2​⋅M⋅N) 降至O(DK2​⋅M+M⋅N) ,在3×3核情况下实现8-9倍计算量减少,MobileNet系列基于此构建轻量级网络,通过宽度乘数与分辨率乘数灵活调整模型规模。

脚本19:CNN实现与架构组件

"""
脚本19:卷积神经网络(CNN)实现与架构组件
涉及内容:
- 7.3.1.1 im2col卷积实现与GEMM优化视角
- 7.3.1.2 转置卷积与Resize-Conv实现
- 7.3.1.3 空洞卷积(Dilated Conv)与Gridding问题
- 7.3.2.1 ResNet残差块实现与梯度流分析
- 7.3.2.3 深度可分离卷积实现与复杂度对比

使用方式:
python cnn_implementation.py
依赖:numpy, matplotlib, scipy
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import convolve2d
from scipy.ndimage import zoom


class Im2ColConv:
    """
    7.3.1.1 基于im2col的卷积实现
    将卷积转换为矩阵乘法(GEMM)
    """
    
    def __init__(self, in_channels, out_channels, kernel_size, stride=1, padding=0):
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.kernel_size = kernel_size
        self.stride = stride
        self.padding = padding
        
        # He初始化卷积核
        self.weights = np.random.randn(out_channels, in_channels, kernel_size, kernel_size) * \
                      np.sqrt(2.0 / (in_channels * kernel_size * kernel_size))
        self.bias = np.zeros(out_channels)
        
    def _im2col(self, x):
        """
        将输入特征图转换为列矩阵
        x: (batch, in_channels, height, width)
        return: (out_height * out_width * batch, in_channels * kernel_size^2)
        """
        batch, C, H, W = x.shape
        k = self.kernel_size
        out_h = (H + 2*self.padding - k) // self.stride + 1
        out_w = (W + 2*self.padding - k) // self.stride + 1
        
        # 添加padding
        if self.padding > 0:
            x = np.pad(x, ((0,0), (0,0), (self.padding, self.padding), (self.padding, self.padding)), 
                      mode='constant')
        
        # 展开为列
        cols = np.zeros((batch, C, k, k, out_h, out_w))
        
        for y in range(k):
            y_max = y + self.stride * out_h
            for x_pos in range(k):
                x_max = x_pos + self.stride * out_w
                cols[:, :, y, x_pos, :, :] = x[:, :, y:y_max:self.stride, x_pos:x_max:self.stride]
        
        cols = cols.transpose(0, 4, 5, 1, 2, 3).reshape(batch * out_h * out_w, -1)
        return cols, out_h, out_w
    
    def forward(self, x):
        """前向传播"""
        batch = x.shape[0]
        cols, out_h, out_w = self._im2col(x)
        
        # 展开权重为矩阵 (out_channels, in_channels*k*k)
        weights_col = self.weights.reshape(self.out_channels, -1)
        
        # GEMM: (out_h*out_w*batch, in_channels*k^2) @ (in_channels*k^2, out_channels)
        out = cols @ weights_col.T + self.bias
        
        # 恢复形状
        out = out.reshape(batch, out_h, out_w, self.out_channels).transpose(0, 3, 1, 2)
        
        # 缓存用于反向传播
        self.cache = {'x': x, 'cols': cols, 'out_h': out_h, 'out_w': out_w}
        
        return out


class DilatedConv:
    """
    7.3.1.3 空洞卷积(Atrous/Dilated Convolution)实现
    """
    
    def __init__(self, in_channels, out_channels, kernel_size, dilation=1):
        self.dilation = dilation
        self.kernel_size = kernel_size
        
        # 实际感受野大小
        self.effective_kernel_size = kernel_size + (kernel_size - 1) * (dilation - 1)
        
        # 初始化权重
        self.weights = np.random.randn(out_channels, in_channels, kernel_size, kernel_size) * \
                      np.sqrt(2.0 / (in_channels * kernel_size * kernel_size))
        self.bias = np.zeros(out_channels)
    
    def forward(self, x):
        """空洞卷积前向传播"""
        batch, C, H, W = x.shape
        k = self.kernel_size
        d = self.dilation
        
        # 输出尺寸(保持尺寸:same padding)
        pad = (self.effective_kernel_size - 1) // 2
        x_padded = np.pad(x, ((0,0), (0,0), (pad, pad), (pad, pad)), mode='constant')
        
        out_h, out_w = H, W
        out = np.zeros((batch, self.weights.shape[0], out_h, out_w))
        
        # 空洞卷积运算
        for b in range(batch):
            for oc in range(self.weights.shape[0]):
                for i in range(out_h):
                    for j in range(out_w):
                        # 提取带空洞的感受野
                        receptive_field = x_padded[b, :, 
                                                  i:i+self.effective_kernel_size:d, 
                                                  j:j+self.effective_kernel_size:d]
                        out[b, oc, i, j] = np.sum(receptive_field * self.weights[oc]) + self.bias[oc]
        
        return out


class TransposedConv:
    """
    转置卷积(Transposed Convolution)实现
    7.3.1.2 包含标准转置卷积与Resize-Conv改进
    """
    
    def __init__(self, in_channels, out_channels, kernel_size, stride=2):
        self.stride = stride
        
        # 初始化权重
        self.weights = np.random.randn(in_channels, out_channels, kernel_size, kernel_size) * 0.01
        
    def forward(self, x):
        """标准转置卷积(可能产生棋盘效应)"""
        batch, C, H, W = x.shape
        out_h, out_w = H * self.stride, W * self.stride
        
        # 上采样(插入零)
        x_upsampled = np.zeros((batch, C, out_h, out_w))
        x_upsampled[:, :, ::self.stride, ::self.stride] = x
        
        # 常规卷积(简化版)
        out = np.zeros((batch, self.weights.shape[1], out_h, out_w))
        
        for b in range(batch):
            for oc in range(self.weights.shape[1]):
                for c in range(C):
                    out[b, oc] += convolve2d(x_upsampled[b, c], self.weights[c, oc], mode='same')
        
        return out


class ResizeConv:
    """
    7.3.1.2 Resize-Conv:先插值再上采样,避免棋盘效应
    """
    
    def __init__(self, in_channels, out_channels, kernel_size, scale_factor=2):
        self.scale_factor = scale_factor
        
        # 常规卷积核
        self.weights = np.random.randn(out_channels, in_channels, kernel_size, kernel_size) * \
                      np.sqrt(2.0 / (in_channels * kernel_size * kernel_size))
        self.bias = np.zeros(out_channels)
    
    def forward(self, x):
        """Resize + Conv"""
        batch, C, H, W = x.shape
        
        # 双线性插值上采样
        x_resized = zoom(x, (1, 1, self.scale_factor, self.scale_factor), order=1)
        
        # 常规卷积
        out_h, out_w = x_resized.shape[2], x_resized.shape[3]
        out = np.zeros((batch, self.weights.shape[0], out_h - 2, out_w - 2))
        
        for b in range(batch):
            for oc in range(self.weights.shape[0]):
                for c in range(C):
                    out[b, oc] += convolve2d(x_resized[b, c], self.weights[oc, c], mode='valid')
                out[b, oc] += self.bias[oc]
        
        return out


class DepthwiseSeparableConv:
    """
    7.3.2.3 深度可分离卷积实现
    Depthwise + Pointwise
    """
    
    def __init__(self, in_channels, out_channels, kernel_size):
        self.in_channels = in_channels
        self.out_channels = out_channels
        
        # Depthwise卷积:每通道一个卷积核
        self.depthwise_weights = np.random.randn(in_channels, 1, kernel_size, kernel_size) * \
                                np.sqrt(2.0 / (kernel_size * kernel_size))
        
        # Pointwise卷积:1x1卷积
        self.pointwise_weights = np.random.randn(out_channels, in_channels, 1, 1) * \
                                np.sqrt(2.0 / in_channels)
        self.bias = np.zeros(out_channels)
    
    def forward(self, x):
        """前向传播"""
        batch, C, H, W = x.shape
        k = self.depthwise_weights.shape[2]
        pad = k // 2
        
        # Depthwise卷积
        x_padded = np.pad(x, ((0,0), (0,0), (pad, pad), (pad, pad)), mode='constant')
        depthwise_out = np.zeros_like(x)
        
        for b in range(batch):
            for c in range(C):
                depthwise_out[b, c] = convolve2d(x_padded[b, c], 
                                                self.depthwise_weights[c, 0], 
                                                mode='valid')
        
        # Pointwise卷积 (1x1)
        out = np.zeros((batch, self.out_channels, H, W))
        for b in range(batch):
            for oc in range(self.out_channels):
                for c in range(C):
                    out[b, oc] += depthwise_out[b, c] * self.pointwise_weights[oc, c, 0, 0]
                out[b, oc] += self.bias[oc]
        
        return out
    
    def compute_complexity(self, input_h, input_w):
        """计算计算复杂度对比"""
        k = self.depthwise_weights.shape[2]
        
        # 深度可分离卷积 FLOPs
        depthwise_flops = k * k * self.in_channels * input_h * input_w
        pointwise_flops = self.in_channels * self.out_channels * input_h * input_w
        total_ds = depthwise_flops + pointwise_flops
        
        # 标准卷积 FLOPs
        standard_flops = k * k * self.in_channels * self.out_channels * input_h * input_w
        
        reduction = standard_flops / total_ds
        
        return {
            'depthwise_flops': depthwise_flops,
            'pointwise_flops': pointwise_flops,
            'total_ds': total_ds,
            'standard_flops': standard_flops,
            'reduction_factor': reduction
        }


class ResidualBlock:
    """
    7.3.2.1 ResNet残差块实现
    包含恒等映射与BatchNorm
    """
    
    def __init__(self, channels, kernel_size=3):
        self.channels = channels
        
        # 第一个卷积层
        self.conv1 = Im2ColConv(channels, channels, kernel_size, padding=kernel_size//2)
        self.bn1 = BatchNorm(channels)
        
        # 第二个卷积层
        self.conv2 = Im2ColConv(channels, channels, kernel_size, padding=kernel_size//2)
        self.bn2 = BatchNorm(channels)
        
        # 激活函数
        self.relu = lambda x: np.maximum(0, x)
        
    def forward(self, x, training=True):
        """前向传播:残差学习 F(x) = H(x) - x"""
        identity = x
        
        # 主路径
        out = self.conv1.forward(x)
        out = self.bn1(out) if hasattr(self, 'bn1') else out
        out = self.relu(out)
        
        out = self.conv2.forward(out)
        out = self.bn2(out) if hasattr(self, 'bn2') else out
        
        # 残差连接
        out += identity  # 恒等映射
        out = self.relu(out)
        
        return out


class BatchNorm:
    """简化版BatchNorm"""
    def __init__(self, num_features, eps=1e-5):
        self.gamma = np.ones(num_features)
        self.beta = np.zeros(num_features)
        self.eps = eps
        
    def __call__(self, x):
        # 假设x是 (batch, channels, h, w)
        mean = np.mean(x, axis=(0, 2, 3), keepdims=True)
        var = np.var(x, axis=(0, 2, 3), keepdims=True)
        x_norm = (x - mean) / np.sqrt(var + self.eps)
        return self.gamma.reshape(1, -1, 1, 1) * x_norm + self.beta.reshape(1, -1, 1, 1)


def demo_cnn_components():
    """演示CNN组件"""
    np.random.seed(42)
    
    # 创建测试输入
    x = np.random.randn(2, 3, 32, 32)  # batch=2, channels=3, 32x32图像
    
    fig, axes = plt.subplots(2, 2, figsize=(16, 12))
    
    # 1. im2col卷积效率对比
    standard_conv = Im2ColConv(3, 64, kernel_size=3, padding=1)
    out = standard_conv.forward(x)
    
    axes[0, 0].text(0.5, 0.5, f'Im2Col Conv Output Shape:\n{out.shape}\n\n'
                   f'Input: {x.shape}\n'
                   f'Weights: {standard_conv.weights.shape}\n\n'
                   f'Computation converted to GEMM:\n'
                   f'cols: {standard_conv.cache["cols"].shape}\n'
                   f'@ weights: {standard_conv.weights.reshape(64, -1).T.shape}',
                   ha='center', va='center', fontsize=12, 
                   bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.8))
    axes[0, 0].set_title('7.3.1.1 im2col Convolution\n(GEMM Optimization)')
    axes[0, 0].axis('off')
    
    # 2. 空洞卷积感受野可视化
    dilated_conv = DilatedConv(3, 16, kernel_size=3, dilation=3)
    
    # 计算有效感受野
    effective_rf = dilated_conv.effective_kernel_size
    
    # 可视化感受野
    rf_grid = np.zeros((effective_rf, effective_rf))
    center = effective_rf // 2
    # 标记实际采样的位置
    for i in range(3):
        for j in range(3):
            y = center - 1 + i * 3  # dilation=3
            x_pos = center - 1 + j * 3
            if 0 <= y < effective_rf and 0 <= x_pos < effective_rf:
                rf_grid[y, x_pos] = 1
    
    axes[0, 1].imshow(rf_grid, cmap='Blues', interpolation='nearest')
    axes[0, 1].set_title(f'7.3.1.3 Dilated Conv Receptive Field\n'
                        f'Kernel=3, Dilation=3, Effective RF={effective_rf}')
    axes[0, 1].grid(True, alpha=0.3)
    
    # 3. 深度可分离卷积复杂度对比
    ds_conv = DepthwiseSeparableConv(64, 128, kernel_size=3)
    complexity = ds_conv.compute_complexity(32, 32)
    
    categories = ['Standard\nConv', 'Depthwise\nSeparable']
    flops = [complexity['standard_flops'] / 1e6, complexity['total_ds'] / 1e6]
    
    bars = axes[1, 0].bar(categories, flops, color=['coral', 'steelblue'], 
                         edgecolor='black', linewidth=1.5)
    axes[1, 0].set_ylabel('FLOPs (Millions)')
    axes[1, 0].set_title(f'7.3.2.3 Computational Complexity\n'
                        f'Reduction Factor: {complexity["reduction_factor"]:.1f}x')
    axes[1, 0].grid(True, alpha=0.3, axis='y')
    
    # 添加数值标签
    for bar, flop in zip(bars, flops):
        height = bar.get_height()
        axes[1, 0].text(bar.get_x() + bar.get_width()/2., height,
                       f'{flop:.1f}M',
                       ha='center', va='bottom', fontweight='bold')
    
    # 4. ResNet梯度流可视化
    # 模拟梯度在不同深度的传播
    depths = [2, 8, 20, 50]
    
    # 无残差连接的梯度(指数衰减)
    grad_plain = [0.9 ** d for d in depths]
    
    # 有残差连接的梯度(稳定)
    grad_resnet = [0.95 + 0.05 * np.exp(-d/20) for d in depths]
    
    axes[1, 1].plot(depths, grad_plain, 'o-', label='Plain CNN (Gradient Vanishing)', 
                   linewidth=2, markersize=8, color='red')
    axes[1, 1].plot(depths, grad_resnet, 's-', label='ResNet (Skip Connections)', 
                   linewidth=2, markersize=8, color='green')
    axes[1, 1].axhline(y=1.0, color='gray', linestyle='--', alpha=0.5, label='Ideal')
    axes[1, 1].set_xlabel('Network Depth')
    axes[1, 1].set_ylabel('Gradient Magnitude')
    axes[1, 1].set_title('7.3.2.1 ResNet Gradient Flow Analysis\n'
                        '(Identity Mapping Preserves Gradients)')
    axes[1, 1].legend()
    axes[1, 1].grid(True, alpha=0.3)
    
    plt.tight_layout()
    plt.savefig('cnn_components_analysis.png', dpi=150, bbox_inches='tight')
    plt.show()
    
    print("=== CNN组件分析 ===")
    print(f"im2col输出形状: {out.shape}")
    print(f"空洞卷积有效感受野: {effective_rf}x{effective_rf}")
    print(f"深度可分离卷积计算量减少: {complexity['reduction_factor']:.1f}x")


if __name__ == "__main__":
    demo_cnn_components()

以上脚本提供了第7章神经网络与深度学习基础核心内容的完整实现,涵盖从基础MLP到现代CNN架构的数值技术细节,可直接用于教学演示与算法原型开发。

深度学习从入门到精通:全面指南 深度学习是当前人工智能领域最热门的研究方向之一,它已经在图像识别、自然语言处理、语音识别等领域取得了显著的成果。本指南旨在帮助读者从零开始,逐步掌握深度学习的基本概念、核心算法和实际应用。我们将通过详细的理论讲解、实战案例和代码示例,带领读者深入理解深度学习的本质,并掌握其应用技能。深度学习是一种通过多层神经网络结构进行特征学习和模式识别的人工智能方法。与传统的机器学习方法不同,深度学习能够自动地从大量数据中学习到复杂的特征表示,从而在图像识别、语音识别、自然语言处理等领域表现出强大的能力。 阅读详情

相关推荐

Cadence Virtuoso实战:用gm/Id方法搞定折叠式共源共栅放大器设计(附完整仿真流程)

本文详细介绍了在Cadence Virtuoso设计环境中,运用gm/Id方法高效设计折叠式共源共栅放大器的完整实战流程。该方法通过工艺特征曲线,将设计指标映射到晶体管尺寸与偏置,有效克服深亚微米工艺下传统方法的不足。文章以TSMC 180nm工艺为例,从指标拆解、参数查找、电路搭建、仿真验证到问题调试,提供了清晰、可复现的操作指南,并探讨了自动化脚本与版图考量,助力工程师和学生提升模拟IC设计效率与成功率。

jwt8token的博客 648

深入探究深度学习、神经网络与卷积神经网络以及它们在多个领域中的应用

深入探究深度学习、神经网络与卷积神经网络以及它们在多个领域中的应用

dvlinker的技术专栏 2万+

UniApp实战:从文件下载到本地保存与预览的一站式解决方案

本文深入解析了UniApp开发中文件下载、保存与预览的完整流程,提供了基于uni.downloadFile、uni.saveFile和uni.openDocument API的一站式实战解决方案。文章详细拆解了核心API的使用方法、多平台(App、H5)的兼容性处理、常见坑点(如文件路径、权限、格式支持)以及进阶优化技巧(如进度提示、文件管理),旨在帮助开发者构建健壮、用户体验良好的跨平台文件处理功能。

d3e4f的博客 614

Python开发从入门到精通:机器学习与深度学习入门

机器学习是一种让计算机从数据中学习规律的方法,不需要明确编程。机器学习的核心思想是“从经验中学习”,通过对大量数据的分析,发现隐藏的模式和规律,并用于预测未来的数据。

编程世界如山海,探索不止。 3万+

Windows正在配置Xftp6在下面的框输入包含安装程序包“Xftp 6.msi“的文件夹的路径

Windows正在配置Xftp6在下面的框输入包含安装程序包“Xftp 6.msi"的文件夹的路径

m0_56588258的博客 2473

UVM对象实例化及覆盖的几种方法

UVM对象实例化的方法有多种,以下以object为例: 1)第一种:直接利用new函数进行实例化 trans t1; t1 = new("t1");//利用new函数进行实例化 2)第二种:uvm_component_registry中提供的方法type_id::create,此种方法也是最常使用的方法 trans t2; t2 = trans::type_id::create("t2",this); 3)第三种:uvm_factory中提供的方法creat_object_by_type trans t

沧月九流的博客 3378

机器学习及深度学习基础---7.20课堂笔记

本文主要回忆上课所讲的一些关于机器学习与深度学习的基本概念,以此来达到强化记忆与深度理解的目的。

KI的博客 8万+

AArch64 ARM64 寄存器介绍

除了上述通用寄存器,还有FPSR(浮点状态寄存器)和 FPCR(浮点控制寄存器)等。X29寄存器和X30寄存器在函数调用和堆栈帧的管理中扮演着关键角色。AArch64架构提供了31个通用寄存器,每个寄存器都可以用作64位X寄存器(X0~X30)或32位W寄存器(W0~W30)。使用X寄存器将导致64位的计算,使用W寄存器将导致32位的计算。EL0、EL1、EL2和EL3是AArch64架构中定义的四个特权级别(Exception Levels),用于支持不同的执行环境和特权级别。

lml435035844的博客 1万+

深度学习入门到精通:基于awesome-ai-ml-dl的学习路径规划

awesome-ai-ml-dl是一个全面的人工智能、机器学习和深度学习学习资源库,包含丰富的学习笔记和精选资源,为深度学习初学者到进阶者提供完整的学习路径指导。无论是数学基础薄弱的新手,还是希望提升实战能力的开发者,都能在这里找到适合自己的学习资料和项目实践。 ## 一、深度学习预备知识:打好基础的关键步骤 在开始深度学习之旅前,掌握必要的基础知识至关重要。项目中的[guides.md](h

gitblog_00084的博客 682

海康视觉脚本开发全流程解析

本文详细解析了海康机器视觉平台(VM)中脚本模块的开发全流程。从创建第一个脚本模块入手,深入讲解了输入参数、输出参数与内部变量的定义与区别,并提供了C#脚本编写、公用函数调用、代码结构组织以及编译调试的实用指南。旨在帮助开发者掌握海康视觉脚本开发的核心技巧,高效实现复杂的自定义视觉逻辑处理。

beer8的博客 603

GAN管CGH40010F设计宽带Doherty功放:如何用ADS搞定输入匹配、合路网络与功分器?

本文详细介绍了基于CGH40010F GaN晶体管的宽带Doherty功放设计流程,重点讲解在ADS平台中实现输入匹配网络、合路网络与功分器的关键技术。通过寄生参数补偿、渐变微带线设计和复合补偿结构等方法,解决了宽带化设计中的核心挑战,最终实现1.7-2.6GHz频段内72%的峰值效率和52%的回退效率。

weixin_42614269的博客 473

从入门到精通:机器学习与深度学习的区别是什么?如何选择研究方向?

这些年,我专注AI转行求职辅导,带过上千名零基础学员从非科班背景转入AI领域,从销售、文员、教师到财务,他们中很多人一开始对机器学习和深度学习一头雾水,但通过精准方向选择和求职指导,几个月就拿到了算法助理、数据工程师或ML工程师的offer。举例,我一个文员背景学员,先学ML,2个月做分类项目,面试时讲“怎么用决策树处理数据不均衡”,轻松过关。- 学员案例:原程序员学员,有ML基,转DL,5个月做生成AI项目,入职腾讯香港分部,年薪HKD 55k。他说:“先ML稳,再DL冲,offer来得快。

AICXYXZ的博客 191

图数据库Neo4j面试内容整理-Neo4j的性能

在执行复杂关系查询时,Neo4j 通过图结构的高效存储和查询机制,能够比传统的关系型数据库提供更快的性能,尤其是在涉及深层关系的查询时。通过合理的配置、优化查询和使用合适的硬件,Neo4j 可以处理大规模的数据集和高并发的查询请求,满足企业级应用的需求。Neo4j 在处理图数据时,通过优化图的存储、查询和遍历等方面,提供了高效的性能,特别适合用于需要处理复杂关系和多层次连接的应用场景,如社交网络、推荐系统、知识图谱等。:Neo4j 会根据查询的结构和数据的分布自动选择最优的查询计划。

不务正业的猿 587

Python深度学习:从入门到精通

亲爱的读者,欢迎您翻开这本书。我们即将探索的,是深度学习的宇宙——一个由数据、算法与算力构筑的奇妙世界。它既是严谨的科学,也是创造的艺术,更是一条通往未来智慧的修行之路。本书将带您从最基础的数学原理出发,亲手搭建神经网络,驾驭Transformer等前沿模型,最终将智慧转化为现实世界的价值。请放下畏惧,保持好奇。这不仅是一次知识的学习,更是一场思维的远行。来,随我一起,开启这趟非凡的旅程吧。

Yuner2000的博客 2619

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

从零到精通:神经网络学习全攻略

想要真正理解神经网络的核心原理吗?🤔 这个名为"Neural Networks: Zero to Hero"的开源项目为你提供了一条从基础到精通的完整学习路径。无论你是AI领域的初学者,还是希望深入理解神经网络内部工作机制的开发者,这个项目都能带领你走进深度学习的神秘世界。 ## 🔍 项目核心价值:为什么选择这个学习路径? 在人工智能飞速发展的今天,神经网络已经成为改变世界的核心技术。然而

gitblog_00304的博客 1131

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Python机器学习:从入门到精通

本书是一本系统讲解机器学习基础与实战的教材。全书分为三大部分:思想与基石、主流模型详解和高级实战演练。 第一部分从机器学习世界观出发,探讨学习的本质与AI发展史,并详细介绍Python数据科学工具链。第二部分深入讲解监督学习(逻辑回归、SVM、随机森林等)和无监督学习(K-Means、PCA等)的核心算法。第三部分通过金融风控和文本分析两大实战项目,完整展示从数据预处理到模型部署的全流程。

weixin_45747731的博客 3422

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

ml-mastery-zh完全指南:从零开始掌握机器学习的终极资源

想要快速掌握机器学习但不知从何开始?ml-mastery-zh项目是您学习机器学习的终极指南!这个开源项目汇集了MachineLearningMastery博客的精华翻译,为中文用户提供了完整的学习路径。无论您是初学者还是有一定经验的开发者,这个资源库都能帮助您系统地学习机器学习算法、深度学习框架和实际应用技巧。从基础概念到高级技术,从理论讲解到实战案例,ml-mastery-zh为您提供了一站式

gitblog_00193的博客 781

【深度学习-Day 12】从零认识神经网络:感知器原理、实现与局限性深度剖析

欢迎来到深度学习系列的第12篇文章!在前面的章节中,我们已经为深度学习之旅奠定了坚实的数学、编程和机器学习基础。我们了解了线性回归和逻辑回归等基本模型,并对模型训练的基本流程有了初步认识。从本篇开始,我们将正式踏入神经网络的核心领域。神经网络是当前人工智能浪潮中最耀眼的技术之一,而理解其最基本的构建单元——感知器(Perceptron)——是至关重要的一步。

吴师兄大模型的博客 1791

【深度学习精通】第1章 | 深度学习概述与神经网络基础 - 从感知机到深度网络

摘要 本章系统介绍了深度学习的发展历程与技术原理。首先回顾了从1943年神经元模型到现代大模型时代的四个发展阶段,分析了当前多模态融合、AI Agent等前沿趋势。重点讲解了感知机的数学模型和学习算法,指出其无法解决异或问题的局限性。进而引入多层感知机(MLP)结构,详细阐述了其网络层次、前向传播计算过程以及激活函数的关键作用。通过对比Sigmoid、Tanh、ReLU等常用激活函数的特性,揭示了深度学习模型通过多层非线性变换实现复杂特征学习的核心机制。本章为后续深度学习技术的学习奠定了理论基础。

帅哥 736

Python深度学习:从入门到进阶

亲爱的读者,欢迎您翻开这本书。我们即将探索的,是深度学习的宇宙——一个由数据、算法与算力构筑的奇妙世界。它既是严谨的科学,也是创造的艺术,更是一条通往未来智慧的修行之路。本书将带您从最基础的数学原理出发,亲手搭建神经网络,驾驭Transformer等前沿模型,最终将智慧转化为现实世界的价值。请放下畏惧,保持好奇。这不仅是一次知识的学习,更是一场思维的远行。来,随我一起,开启这趟非凡的旅程吧。

YunWisdom 4469

100天机器学习实战:从入门到精通的完整指南

**100-Days-Of-ML-Code** 是一个专为机器学习新手打造的系统性学习计划,通过每天实践一个核心算法,帮助你在100天内掌握从数据预处理到深度学习的关键技能。无需复杂理论基础,只需跟随每日任务,即可逐步构建你的机器学习知识体系。 ## ???? 为什么选择100天机器学习挑战? 机器学习入门往往面临三大痛点:理论晦涩、缺乏实践、难以坚持。本项目通过 **"每日一个算法+实战案例"*...

gitblog_01055的博客 1080

AI/ML/DL/NN四层技术关系图谱:工程师的选型决策指南

人工智能(Artificial Intelligence)作为目标导向的能力集合,其落地依赖于分层演进的技术路径:机器学习(Machine Learning)以数据驱动发现模式,深度学习(Deep Learning)突破特征工程瓶颈实现自动表征,而神经网络(Neural Networks)则是贯穿各层的通用数学基座。理解这四者的嵌套逻辑,有助于规避‘小数据硬上Transformer’或‘结构化任务滥用BERT’等典型误用。在金融风控、工业质检、医疗影像等真实场景中,技术选型需综合数据形态、规模、标注成本、实

weixin_34364071的博客 485
上一篇: 【机器学习基础算法:从零实现到精通】第6章 概率图模型与推断
下一篇: 【机器学习基础算法:从零实现到精通】第8章 统计学习理论与模型选择
VectorShift
博客等级 码龄1年 1839粉丝 1109原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

VectorShift

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

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

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

打赏作者

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

抵扣说明:

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

余额充值