
简介这份PDF资料面向机器学习初学者与需要梳理深度学习理论脉络的读者系统讲解深度学习算法的基本原理与典型应用。内容从多层神经网络降维思路切入先介绍输入层、隐藏层、输出层的网络基础结构再展开前向传播与反向传播的训练过程并重点剖析受限玻尔兹曼机作为单层网络基本结构的训练机制。资源以自动编码机为例说明其如何通过学习输入特征重建原始数据实现降维与表示并展示其应用于手写数字识别所带来的性能提升同时客观讨论训练缓慢、参数易收敛于局部最优等局限及未来改进方向。资源包共1个PDF文件约496KB结构紧凑适合作为课程学习或论文写作的参考材料。目前已有94人学习便于快速建立对深度学习核心概念与经典模型的整体认知。1. 从一份 PDF 标题说起深度学习算法到底该学哪几条线很多人第一次搜「深度学习算法的原理及应用.pdf」其实是在找一份能从头到尾把算法讲明白的材料。但真拿到这类 PDF翻到第三章就卡住了公式能看懂代码跑不通环境配三天最后连一个手写数字识别都没跑起来。问题不在你在于大多数资料把「原理」和「应用」割裂了——前半本讲反向传播推导后半本直接甩一个完整项目中间缺了最关键的桥算法到底对应哪几行代码、哪几个参数、哪几个张量形状。这篇笔记不打算复述任何一份 PDF 的目录而是按一线做项目的顺序把深度学习算法从原理到落地拆成一条能走通的路。核心覆盖四条线神经网络基础算法、受限玻尔兹曼机RBM、自动编码机Autoencoder、卷积神经网络CNN。这四条线基本覆盖了「深度学习算法原理及应用」这个标题下最常被检索的知识点也是从入门到能动手改模型的最短路径。适合谁适合已经会 Python、看过一点机器学习、但一直没把深度学习真正跑通的人。下面从最小可跑通的算法开始。2. 神经网络算法的最小闭环从手推一次反向传播到 PyTorch 跑通2.1 为什么先啃全连接网络而不是直接上 CNN很多人一上来就冲 CNN觉得卷积才是深度学习。但 CNN 的反向传播更复杂如果全连接网络的梯度怎么来的都没搞清调 CNN 时只能靠玄学。全连接网络是深度学习算法的「最小闭环」前向传播、损失计算、反向传播、参数更新四步全在里面。把这四步用 NumPy 手写一遍再用 PyTorch 对照一遍后面所有模型都是这个骨架的变体。先明确一个概念神经网络算法本质是复合函数加链式法则。一个两层网络就是y W2 * relu(W1 * x b1) b2损失函数是L MSE(y, target)。反向传播要做的就是求dL/dW1和dL/dW2。链式法则展开后dL/dW1 dL/dy * dy/dh * dh/dW1其中h是隐藏层输出。手写一遍的意义在于你会亲眼看到梯度消失是怎么发生的——当relu输出大量为 0 时dh/dW1直接归零。2.2 用 NumPy 手写两层网络的前向与反向下面这段代码不依赖任何框架只用一个 batch 的数据演示一次完整的前向和反向。重点看注释里每个张量的形状变化。import numpy as np # 构造数据batch4, 输入维度3, 隐藏层5, 输出1 np.random.seed(42) x np.random.randn(4, 3) y np.random.randn(4, 1) # 初始化参数 W1 np.random.randn(3, 5) * 0.1 b1 np.zeros((1, 5)) W2 np.random.randn(5, 1) * 0.1 b2 np.zeros((1, 1)) # 前向传播 z1 x W1 b1 # (4,5) a1 np.maximum(0, z1) # relu z2 a1 W2 b2 # (4,1) loss np.mean((z2 - y) ** 2) # 反向传播 dz2 2 * (z2 - y) / y.shape[0] # (4,1) dW2 a1.T dz2 # (5,1) db2 np.sum(dz2, axis0, keepdimsTrue) da1 dz2 W2.T # (4,5) dz1 da1 * (z1 0) # relu 导数 dW1 x.T dz1 # (3,5) db1 np.sum(dz1, axis0, keepdimsTrue) print(loss:, loss) print(dW1 shape:, dW1.shape, dW2 shape:, dW2.shape)逻辑说明dz2是损失对输出层的梯度dW2用a1.T dz2得到因为z2 a1 W2对W2求导就是a1的转置乘梯度。dz1多了一步(z1 0)这是 ReLU 的导数小于等于 0 的位置梯度直接截断。参数说明W1形状(3,5)表示输入 3 维映射到隐藏 5 维W2形状(5,1)表示隐藏 5 维映射到输出 1 维。学习率这里没写因为只做了一次反向实际训练时W1 - lr * dW1lr一般从 0.01 或 0.001 起调。2.3 换成 PyTorch 后必须对齐的三个参数手写版跑通后用 PyTorch 重写一遍重点不是代码变短而是确认三件事对齐初始化方式、损失函数、优化器。常见翻车点是手写时用np.random.randn * 0.1PyTorch 默认初始化是均匀分布两者初始 loss 差很多导致你以为代码写错了。import torch import torch.nn as nn class Net(nn.Module): def __init__(self): super().__init__() self.fc1 nn.Linear(3, 5) self.fc2 nn.Linear(5, 1) def forward(self, x): x torch.relu(self.fc1(x)) return self.fc2(x) model Net() # 手动对齐初始化用小方差正态 for m in model.modules(): if isinstance(m, nn.Linear): nn.init.normal_(m.weight, std0.1) nn.init.zeros_(m.bias) criterion nn.MSELoss() optimizer torch.optim.SGD(model.parameters(), lr0.01) x_t torch.tensor(x, dtypetorch.float32) y_t torch.tensor(y, dtypetorch.float32) for step in range(200): pred model(x_t) loss criterion(pred, y_t) optimizer.zero_grad() loss.backward() optimizer.step() if step % 50 0: print(step, loss.item())参数说明nn.init.normal_的std0.1对应手写版的* 0.1这是为了两边初始 loss 接近。lr0.01是 SGD 的常见起点如果 loss 震荡就降到 0.001。optimizer.zero_grad()必须放在backward()之前否则梯度会累加这是新手最常踩的坑之一。跑完 200 步loss 应该从初始值降到接近 0如果没降先检查数据是否归一化、学习率是否过大。3. 受限玻尔兹曼机与自动编码机无监督预训练那套算法现在还值不值得碰3.1 RBM 的能量函数与对比散度到底在算什么受限玻尔兹曼机RBM是深度学习中比较早的无监督算法结构是两层可见层v和隐藏层h层内无连接层间全连接。它的核心是能量函数E(v,h) -a^T v - b^T h - v^T W h能量越低表示这个配置越「合理」。训练目标是让模型对真实数据的能量低对随机数据的能量高。直接算梯度不可行因为配分函数Z要遍历所有可能状态所以实际用对比散度CD-k近似从数据出发跑 k 步 Gibbs 采样用采样结果估计梯度。常见做法是 k1也就是 CD-1。步骤拿一个真实样本v0算h0 sigmoid(W v0 b)再从h0采样出v1再算h1 sigmoid(W v1 b)最后更新W lr * (v0 h0^T - v1 h1^T)。这里v0 h0^T是正相位v1 h1^T是负相位。很多 PDF 只给公式不给采样过程导致读者不知道v1怎么来的——其实就是把h0当概率用torch.bernoulli采样。3.2 用 PyTorch 实现 CD-1 并观察重构误差下面代码实现一个二值 RBM在 MNIST 二值化数据上跑 CD-1。重点看sample_h和sample_v两个函数以及正负相位的计算。import torch def sample_h(v, W, b_h): p torch.sigmoid(v W b_h) return p, torch.bernoulli(p) def sample_v(h, W, a_v): p torch.sigmoid(h W.T a_v) return p, torch.bernoulli(p) # 假设 v0 是 (batch, 784) 的二值数据 W torch.randn(784, 256) * 0.01 a_v torch.zeros(784) b_h torch.zeros(256) lr 0.01 for epoch in range(10): for v0 in dataloader: # v0: (batch, 784) v0 (v0 0.5).float() p_h0, h0 sample_h(v0, W, b_h) p_v1, v1 sample_v(h0, W, a_v) p_h1, h1 sample_h(v1, W, b_h) # 更新 W lr * (v0.T p_h0 - v1.T p_h1) / v0.shape[0] a_v lr * torch.mean(v0 - v1, dim0) b_h lr * torch.mean(p_h0 - p_h1, dim0) # 重构误差 recon torch.sigmoid(h0 W.T a_v) err torch.mean((v0 - recon) ** 2) print(epoch, epoch, recon err, err.item())逻辑说明sample_h返回概率p和采样值h更新时用概率p_h0而不是采样值h0这是 CD 算法的标准做法能降低方差。参数说明隐藏层 256 是 MNIST 常用配置太大容易过拟合太小重构差。lr0.01对 RBM 偏大实际可以从 0.001 开始。重构误差降到 0.05 以下说明 RBM 学到了有意义的特征。注意 RBM 训练慢10 个 epoch 在 CPU 上可能要十几分钟这是它现在不流行的主要原因。3.3 自动编码机去掉采样噪声的确定性版本自动编码机Autoencoder可以看成 RBM 的确定性简化版编码器h sigmoid(W_e v b_e)解码器v sigmoid(W_d h b_d)损失是重构误差||v - v||^2。它没有 RBM 的配分函数和采样训练就是普通反向传播所以快得多。常见变体有去噪自动编码机DAE和稀疏自动编码机SAE。DAE 在输入上加噪声让模型学出去噪能力SAE 在损失里加L1正则让隐藏层稀疏。class Autoencoder(nn.Module): def __init__(self): super().__init__() self.enc nn.Linear(784, 128) self.dec nn.Linear(128, 784) def forward(self, x): h torch.relu(self.enc(x)) return torch.sigmoid(self.dec(h)) model Autoencoder() opt torch.optim.Adam(model.parameters(), lr1e-3) for epoch in range(20): for x, _ in dataloader: x x.view(-1, 784) noise torch.randn_like(x) * 0.3 x_noisy torch.clamp(x noise, 0, 1) out model(x_noisy) loss nn.functional.mse_loss(out, x) opt.zero_grad(); loss.backward(); opt.step()参数说明noise * 0.3是去噪强度太大会导致重构模糊太小起不到正则作用。Adam的lr1e-3比 SGD 稳适合自动编码机。隐藏层 128 是压缩比 784:128≈6:1如果做可视化可以降到 2 或 3 维。RBM 和自动编码机现在很少单独用来做预训练了但它们的重构误差是一个很好的异常检测指标正常样本重构误差低异常样本重构误差高这个思路在工业缺陷检测里还在用。4. 卷积神经网络算法落地从 LeNet 到 ResNet 的四个必调参数4.1 卷积核、步长、填充、池化形状怎么算CNN 的核心是卷积层四个参数决定输出形状卷积核大小 K、步长 S、填充 P、输入尺寸 W。输出尺寸公式(W - K 2P) / S 1。这个公式必须背下来因为 90% 的 shape 报错都来自这里。比如输入 32x32K5S1P0输出就是(32-5)/1128这是 LeNet-5 第一层的经典配置。如果 P2输出就是(32-54)/1132尺寸不变这叫 same padding。池化层不改变通道数只缩小空间尺寸。最大池化K2, S2让尺寸减半。常见翻车点在nn.Conv2d里设了padding1但池化后又接全连接忘记算展平后的维度。解决办法是用nn.AdaptiveAvgPool2d(1)替代固定展平这样无论输入多大输出都是 1x1。4.2 用 PyTorch 搭一个能跑 CIFAR-10 的 CNN下面代码是一个 4 层卷积加 2 层全连接的 CNN在 CIFAR-10 上能到 75% 左右准确率。重点看每个卷积层的参数和BatchNorm的位置。import torch.nn as nn class CNN(nn.Module): def __init__(self, num_classes10): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 32, 3, padding1), # 32x32 - 32x32 nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2), # 32x32 - 16x16 nn.Conv2d(32, 64, 3, padding1), nn.BatchNorm2d(64), nn.ReLU(), nn.MaxPool2d(2), # 16x16 - 8x8 nn.Conv2d(64, 128, 3, padding1), nn.BatchNorm2d(128), nn.ReLU(), nn.MaxPool2d(2), # 8x8 - 4x4 ) self.classifier nn.Sequential( nn.AdaptiveAvgPool2d(1), # 4x4 - 1x1 nn.Flatten(), nn.Linear(128, num_classes) ) def forward(self, x): return self.classifier(self.features(x))逻辑说明BatchNorm2d放在卷积和 ReLU 之间这是标准顺序能加速收敛。AdaptiveAvgPool2d(1)把任意空间尺寸压成 1x1避免手动算展平维度。参数说明卷积核统一 3x3这是最常用的配置两个 3x3 堆叠的感受野等于一个 5x5但参数更少。通道数 32→64→128 逐层翻倍这是经典设计。训练时lr0.01配 SGD momentum0.9或者lr1e-3配 Adam。如果 loss 不降先检查数据归一化CIFAR-10 的 mean 和 std 要按通道算。4.3 从 LeNet 到 ResNet残差连接解决了什么LeNet 只有 5 层ResNet 可以到 152 层。深层网络的问题不是参数多而是梯度消失反向传播时梯度经过多层连乘到前面几层就接近 0。ResNet 的残差块y F(x) x让梯度有一条捷径直接回传dL/dx dL/dy * (1 dF/dx)那个1保证梯度不会完全消失。class ResidualBlock(nn.Module): def __init__(self, ch): super().__init__() self.conv1 nn.Conv2d(ch, ch, 3, padding1) self.bn1 nn.BatchNorm2d(ch) self.conv2 nn.Conv2d(ch, ch, 3, padding1) self.bn2 nn.BatchNorm2d(ch) def forward(self, x): out torch.relu(self.bn1(self.conv1(x))) out self.bn2(self.conv2(out)) return torch.relu(out x)参数说明残差块里两个 3x3 卷积通道数不变这样才能直接相加。如果输入输出通道不同需要加一个 1x1 卷积做投影。out x之后再过 ReLU这是 ResNet v1 的做法v2 是先 ReLU 再相加。实际用的时候BatchNorm的momentum默认 0.1如果 batch size 很小比如 8要降到 0.01否则 running mean 波动大验证准确率上不去。5. 避坑与排查深度学习算法落地时最常翻车的五件事5.1 loss 不降反升先查这三点现象训练几个 epoch 后 loss 变成 NaN 或者越来越大。原因学习率过大、数据没归一化、损失函数用错。解决先把学习率降 10 倍如果还不行检查输入数据是否在[0,1]或[-1,1]图像数据除以 255 是最低要求。分类任务用CrossEntropyLoss时标签必须是long类型且从 0 开始不能是 one-hot。5.2 验证集准确率远低于训练集现象训练集 99%验证集 60%。原因过拟合或者训练集和验证集分布不一致。解决先加Dropout和WeightDecayDropout一般设 0.5 放在全连接层前WeightDecay设 1e-4。如果还不行检查数据划分是否随机有时候按类别排序后直接切分会导致验证集只有某几类。5.3 GPU 利用率低训练速度上不去现象nvidia-smi显示 GPU 利用率只有 20%。原因数据加载是瓶颈num_workers设成 0 或者太小。解决DataLoader的num_workers设成 CPU 核心数pin_memoryTruebatch_size适当加大。如果还是慢把数据预处理放到 GPU 上做比如归一化用torchvision.transforms在 CPU 做可以改成在forward里除以 255。5.4 模型保存后加载结果不一致现象训练完保存state_dict加载后预测结果和训练时不一样。原因保存时模型在train()模式加载后忘了调eval()BatchNorm和Dropout行为不同。解决加载后立刻model.eval()并且用torch.no_grad()包住推理。另外保存时用torch.save(model.state_dict(), path)不要直接torch.save(model, path)后者依赖类定义换环境容易报错。5.5 显存溢出但 batch size 已经很小现象CUDA out of memory但 batch size 只有 4。原因计算图没释放或者中间变量一直保留。解决检查训练循环里有没有loss.item()之外还保留了loss本身total_loss loss会累积计算图应该写成total_loss loss.item()。另外用torch.cuda.empty_cache()清理缓存但根本办法是减小模型或输入尺寸。6. 把算法跑成自己的一个验证你是否真懂的小技巧学完这些算法怎么判断自己真懂了我的习惯是关掉 PDF从零写一个最小版本然后故意改坏一个参数看报错能不能定位到根因。比如把 CNN 的padding从 1 改成 0看 shape 在哪一层报错把 RBM 的 CD-k 从 1 改成 5看重构误差是不是更小但训练更慢把自动编码机的隐藏层从 128 改成 2看重构图像是不是变模糊。这个「改坏再修好」的过程比顺着 PDF 抄一遍代码有用得多。再给一个具体的验证方法用同一个数据集比如 MNIST分别跑全连接网络、自动编码机、CNN记录三个指标——训练时间、参数量、测试准确率。全连接网络参数量最大但准确率最低CNN 参数量少但准确率高自动编码机没有准确率但有重构误差。把这三个结果放在一张表里你就能直观看到不同算法的取舍。模型参数量训练时间CPU测试准确率全连接 2 层约 10 万2 分钟92%自动编码机约 20 万5 分钟无监督简单 CNN约 5 万8 分钟98%最后说一个我自己的教训早期学深度学习时我总想找一个「最好」的算法后来发现没有最好的只有最匹配数据和算力的。RBM 在 2006 年很火现在基本被自动编码机和对比学习取代CNN 在图像上很强但换成表格数据可能还不如 XGBoost。把每个算法的输入输出形状、损失函数、参数更新方式这三件事搞清楚比追新算法重要得多。希望帮到你。本文还有配套的精品资源点击获取