简介:这份PDF资料面向机器学习初学者与需要梳理深度学习理论脉络的读者,系统讲解深度学习算法的基本原理与典型应用。内容从多层神经网络降维算法切入,先介绍输入层、隐藏层、输出层的网络基础结构,再展开前向传播与反向传播的训练过程,并重点剖析受限玻尔兹曼机作为单层网络基本结构的原理,最后以自动编码机在手写数字识别中的性能提升为例说明实际应用效果,同时客观讨论训练缓慢、易收敛于局部最优等局限与未来方向。资源包共1个PDF文件,约496KB,篇幅精炼,适合作为课程学习或论文写作的参考材料。目前已有94人学习下载,可帮助读者快速建立深度学习从原理到应用的完整认知框架。
1. 从一份 PDF 标题说起:深度学习算法到底该按什么顺序学
很多人第一次搜「深度学习算法的原理及应用.pdf」,其实不是想找一份文档,而是想找一条能走通的路。我见过太多人把 PDF 收藏了几十份,环境还是没配好,模型还是跑不起来。问题不在资料少,而在于没人告诉你:原理要按什么顺序啃,代码要按什么顺序写,哪些知识点是必须亲手推一遍的,哪些只要会调库就行。
这份标题里其实藏着两条线:一条是「原理」,对应神经网络算法、受限波尔兹曼机、自动编码机这些听起来就头大的名词;另一条是「应用」,对应图像分类、目标检测、预测建模这些能落地的任务。两条线不能分开走,先啃三个月数学再碰代码,大概率中途放弃;反过来只会调库不懂原理,遇到 loss 不降就只能玄学调参。我一般建议按「最小可跑通闭环 → 逐层补原理 → 换任务验证」的节奏推进,下面就把这条路径拆开讲清楚。
2. 原理先立住:从感知机到 CNN 的最小知识骨架
2.1 神经网络算法的最小闭环:前向、损失、反向
不管网络多深,核心就三件事:前向传播算输出,损失函数量误差,反向传播求梯度。很多人卡在反向传播的链式法则上,其实用计算图理解会轻松很多。每个节点只关心两件事:输入怎么变成输出,以及输出端的梯度怎么传回输入端。
以单个神经元为例,前向是 $z = wx + b$,经过激活函数 $a = \sigma(z)$。反向时,损失对 $w$ 的梯度等于损失对 $a$ 的梯度乘以 $a$ 对 $z$ 的梯度再乘以 $z$ 对 $w$ 的梯度。这个链式结构在代码里就是一层层.backward()的累加。
import torch import torch.nn as nn # 最小前向+反向闭环:一个线性层加一个激活 model = nn.Sequential( nn.Linear(10, 5), # 输入10维,输出5维 nn.ReLU(), # 非线性激活,没有它多层等价于一层 nn.Linear(5, 1) # 输出1维,做回归 ) optimizer = torch.optim.SGD(model.parameters(), lr=0.01) criterion = nn.MSELoss() x = torch.randn(8, 10) # 8个样本,每个10维 y = torch.randn(8, 1) # 对应标签 for step in range(100): pred = model(x) # 前向 loss = criterion(pred, y) # 算损失 optimizer.zero_grad() # 清空上一轮梯度,不清会累加 loss.backward() # 反向求梯度 optimizer.step() # 更新参数这段代码里zero_grad()是最容易被忽略的一步。PyTorch 默认梯度累加,如果不清零,第二次迭代的梯度会叠加第一次的,loss 会直接飞掉。lr=0.01是学习率,太大会震荡,太小收敛慢,新手可以从 0.01 或 0.001 起步。MSELoss适合回归,分类任务要换成CrossEntropyLoss。
2.2 受限波尔兹曼机和自动编码机:为什么现在还要了解它们
热搜词里出现「受限波尔兹曼机」和「自动编码机」,说明很多人还在翻早期深度学习的资料。这两个东西在 2006 年前后是深度网络预训练的主力,现在直接用的场景少了,但理解它们对搞懂「无监督特征提取」很有帮助。
受限波尔兹曼机(RBM)是一个两层结构:可见层和隐藏层,层内无连接,层间全连接。它的能量函数定义了联合概率,训练目标是让模型对训练数据的概率最大。实际训练用对比散度(CD-k),k 通常取 1 就够。自动编码机(Autoencoder)更直观:编码器把输入压成低维表示,解码器再还原,损失是重构误差。中间那个低维表示就是特征。
import torch.nn as nn class Autoencoder(nn.Module): def __init__(self): super().__init__() self.encoder = nn.Sequential( nn.Linear(784, 256), # 输入784维,比如28x28图像展平 nn.ReLU(), nn.Linear(256, 64) # 压缩到64维瓶颈层 ) self.decoder = nn.Sequential( nn.Linear(64, 256), nn.ReLU(), nn.Linear(256, 784), nn.Sigmoid() # 输出归一化到0-1,配合BCELoss ) def forward(self, x): z = self.encoder(x) # 编码,得到低维特征 return self.decoder(z) # 解码,还原输入瓶颈层维度 64 是超参,太小会丢信息,太大起不到压缩作用。损失用BCELoss时输入必须归一化到 0-1,用MSELoss则不用。RBM 和自动编码机现在更多作为「理解无监督预训练」的跳板,实际项目里特征提取基本被预训练 CNN 替代了,但面试和论文里还会遇到。
2.3 CNN 为什么能处理图像:卷积、池化、权值共享
卷积神经网络是绕不开的。全连接层处理 224x224x3 的图像,第一层就要 15 万个权重,参数量爆炸且容易过拟合。卷积层用一个小窗口(比如 3x3)在图上滑动,同一个窗口的权重在整个图共享,参数量直接降到几百个。池化层进一步降维,同时带来一点平移不变性。
import torch.nn as nn cnn = nn.Sequential( nn.Conv2d(3, 16, kernel_size=3, padding=1), # 3通道输入,16个卷积核,3x3窗口 nn.ReLU(), nn.MaxPool2d(2), # 2x2最大池化,尺寸减半 nn.Conv2d(16, 32, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2), nn.Flatten(), # 展平后接全连接 nn.Linear(32 * 56 * 56, 10) # 假设输入224x224,经过两次池化变56x56 )padding=1保证 3x3 卷积后尺寸不变,不加的话每层缩 2。MaxPool2d(2)把长宽各减半。Flatten后的维度要自己算清楚,算错就是经典的size mismatch报错。卷积核数量 16、32 是常见起步值,深层可以加到 64、128。
3. 动手跑通:环境配置到第一个训练脚本
3.1 深度学习环境配置:Ubuntu 20.04 加 GPU 的稳妥路径
热搜里「ubuntu20.04配置深度学习」和「深度学习环境配置gpu版」出现频率很高,说明环境这一步卡住了大量人。我一般按「驱动 → CUDA → cuDNN → 框架」的顺序来,版本对应关系是最大的坑。
先确认显卡和驱动:
nvidia-smi输出里能看到驱动版本和最高支持的 CUDA 版本。比如显示CUDA Version: 11.8,那装 CUDA 11.8 及以下都行。不要直接装最新版,框架对 CUDA 版本有要求。
# 以 CUDA 11.8 为例,去官网找对应命令,不要照抄来源不明的脚本 wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda_11.8.0_520.61.05_linux.run sudo sh cuda_11.8.0_520.61.05_linux.run安装时取消勾选驱动(已经装过),只装 CUDA Toolkit。装完在~/.bashrc里加:
export PATH=/usr/local/cuda-11.8/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64:$LD_LIBRARY_PATH然后source ~/.bashrc,用nvcc -V验证。cuDNN 下载后解压拷贝到 CUDA 目录即可。最后装 PyTorch,去官网选对应 CUDA 版本的命令,不要用pip install torch默认版,那个可能不带 GPU 支持。
提示:conda 环境能隔离不同项目的 CUDA 版本,但 conda 装的 cudatoolkit 和系统 CUDA 是两回事,PyTorch 用的是 conda 里的运行时,版本对不上照样报错。
3.2 用 PyTorch 写一个完整训练循环:数据、模型、验证
环境好了之后,跑通一个完整流程比看十篇原理都有用。下面以 MNIST 为例,把数据加载、模型定义、训练、验证串起来。
import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, transforms # 数据准备:转张量+归一化 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) # MNIST均值和标准差 ]) train_set = datasets.MNIST('./data', train=True, download=True, transform=transform) test_set = datasets.MNIST('./data', train=False, transform=transform) train_loader = DataLoader(train_set, batch_size=64, shuffle=True) test_loader = DataLoader(test_set, batch_size=64, shuffle=False) # 模型 model = nn.Sequential( nn.Flatten(), nn.Linear(784, 128), nn.ReLU(), nn.Linear(128, 10) ) device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) criterion = nn.CrossEntropyLoss() # 训练循环 for epoch in range(5): model.train() for x, y in train_loader: x, y = x.to(device), y.to(device) pred = model(x) loss = criterion(pred, y) optimizer.zero_grad() loss.backward() optimizer.step() # 验证 model.eval() correct = 0 with torch.no_grad(): for x, y in test_loader: x, y = x.to(device), y.to(device) pred = model(x) correct += (pred.argmax(1) == y).sum().item() print(f'epoch {epoch}, acc {correct / len(test_set):.4f}')batch_size=64是显存和收敛速度的折中,显存小就降到 32 或 16。Adam学习率 1e-3 是默认起步值,比 SGD 省心。model.train()和model.eval()在只有全连接层时区别不大,但有 Dropout 和 BatchNorm 时必须切换,否则验证结果会飘。torch.no_grad()关掉梯度计算,省显存也加速。
3.3 从 MNIST 换到自己的数据集:三个必须改的地方
跑通 MNIST 只是开始,换成自己的数据要改三处:数据读取、输入维度、输出类别数。
数据读取用Dataset和DataLoader自定义:
from torch.utils.data import Dataset from PIL import Image class MyDataset(Dataset): def __init__(self, file_list, labels, transform=None): self.file_list = file_list # 图片路径列表 self.labels = labels # 对应标签列表 self.transform = transform def __len__(self): return len(self.file_list) def __getitem__(self, idx): img = Image.open(self.file_list[idx]).convert('RGB') if self.transform: img = self.transform(img) return img, self.labels[idx]输入维度改nn.Linear的第一个参数,比如 224x224x3 经过几层卷积后的展平尺寸。输出类别数改最后一层的输出维度,10 类就写 10,2 类可以写 2 配CrossEntropyLoss,也可以写 1 配BCELoss。标签必须是 0 到 N-1 的整数,从 1 开始会报IndexError。
4. 避坑与排查:训练不收敛时先看这五处
4.1 loss 不降反升:学习率和归一化先查
现象是训练几个 epoch 后 loss 变成 nan 或者持续上升。原因通常是学习率太大,或者输入没归一化。解决是把学习率降一个数量级,比如从 0.01 降到 0.001,同时检查数据是否减了均值除了标准差。图像数据用transforms.Normalize,表格数据手动算均值和方差。
4.2 验证集准确率远低于训练集:过拟合还是数据泄漏
现象是训练集 99%,验证集 70%。先查数据有没有泄漏,比如验证集图片混进了训练集。排除泄漏后就是过拟合,加 Dropout、加权重衰减、做数据增强。nn.Dropout(0.5)放在全连接层之间,weight_decay=1e-4加在优化器里。
4.3 GPU 显存不够:batch size 和梯度累积
现象是CUDA out of memory。先把 batch size 减半,还不行就用梯度累积:小 batch 跑几次再更新一次参数,等效大 batch。
accum_steps = 4 for i, (x, y) in enumerate(train_loader): loss = criterion(model(x), y) / accum_steps loss.backward() if (i + 1) % accum_steps == 0: optimizer.step() optimizer.zero_grad()4.4 训练速度慢:数据加载和 pin_memory
现象是 GPU 利用率低,大部分时间在等数据。把DataLoader的num_workers调到 4 或 8,pin_memory=True加速 CPU 到 GPU 的拷贝。但num_workers在 Windows 上容易出问题,设 0 最稳。
4.5 模型保存后加载结果不对:state_dict 和结构要一致
现象是保存的模型重新加载后预测全乱。原因是只保存了state_dict但加载时模型结构变了,或者保存时用了torch.save(model)整个对象。推荐只存state_dict:
torch.save(model.state_dict(), 'model.pth') # 加载时先实例化同样结构的模型 model = MyModel() model.load_state_dict(torch.load('model.pth')) model.eval()5. 进阶技巧:用预训练模型和迁移学习快速出结果
自己从零训一个 CNN 在小数据集上很难出好结果,迁移学习是性价比最高的做法。拿 ImageNet 上预训练的 ResNet,把最后一层换成自己的类别数,只训最后一层,或者全部微调但用很小的学习率。
import torchvision.models as models import torch.nn as nn model = models.resnet18(pretrained=True) # 加载预训练权重 for param in model.parameters(): param.requires_grad = False # 先冻结所有层 model.fc = nn.Linear(512, 10) # 替换最后一层,10类 # 只优化fc层 optimizer = torch.optim.Adam(model.fc.parameters(), lr=1e-3)冻结训练几轮后,可以解冻后面几层做微调,学习率降到 1e-4 或更低。数据增强用RandomResizedCrop、RandomHorizontalFlip、ColorJitter,能明显提升泛化。验证时记得model.eval()并关掉增强。
我自己的习惯是:任何新任务先跑一个预训练模型加线性探测,拿到 baseline 再决定要不要自己设计网络。这样一周内能知道任务可不可做,而不是花一个月调一个可能方向就错的模型。希望帮到你。
本文还有配套的精品资源,点击获取