ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

机器学习与深度学习入门:从梯度、泛化到第一个实战项目

机器学习与深度学习入门:从梯度、泛化到第一个实战项目 1. 学习本质机器学习与深度学习的分界线在哪里1.1 先破除一个认识误区大家提到“深度学习”第一反应往往是“这个东西好厉害能识别图像、能对话、能下棋”。但如果你追问一句“它和普通机器学习到底有什么区别”很多刚入门的朋友就开始含糊了。其实从学科归属上看深度学习只是机器学习的一个分支——它用“多层神经网络”作为模型从数据中自动学习特征完成预测任务。换句话说凡是深度学习能做的本质上都在机器学习的框架内但深度学习做了一件传统机器学习不太擅长的事自动特征提取。理解这件事我习惯用教小孩认识猫来打比方。传统机器学习的方式相当于我们告诉小孩“猫有尖耳朵、有胡须、会喵喵叫、体型一般不大。”这些规则是人提前定义好的程序只是机械执行。这就是“特征工程”的思路——人类介入特征设计机器负责学规律。深度学习的思路相当于我们给孩子看成千上万张猫的图片并且在孩子答错时告诉他“这张不是猫是狗”。他的大脑内部会自动调整对各种特征的重视程度不需要我们逐条告诉他“看耳朵”、“看胡须”。他最终不是靠某条规则识猫而是通过多层神经网络自动组合出一套判断机制。1.2 机器学习的标准定义与核心流程机器学习研究的问题用一句话概括就是让计算机从数据中自动总结出规律并用这些规律预测未知样本。这里有个关键词是“自动总结”。传统编程的写法是“规则 数据 答案”机器学习的写法则是“数据 答案 规则”。我们在训练时给模型大量“输入”和“标签”模型内部会不断调整自身参数直到输入能比较准确地映射到答案上。训练完成后模型内部保留的就是“规则”。完整流程通常长这样收集数据清洗噪声处理缺失值划分训练集、验证集、测试集选择模型决策树、SVM、线性回归、神经网络等定义损失函数衡量预测值与真实值的差距用优化算法迭代求解模型参数在测试集上评估模型的真实表现很多初学者跳过了第一步和第二步直接把公开数据集塞进模型最后发现训练效果不错但真实场景完全不能用。关于这个问题我在后面“泛化”部分会详细展开。1.3 机器学习的重要分支监督、无监督与强化机器学习内部有三条主要路线很多热词比如“联邦深度强化学习”、“图强化学习与深度强化学习”都和其中的强化学习相关。学习范式数据形态核心目标典型应用监督学习输入 标签学到输入到标签的映射图像分类、语音识别、房价预测无监督学习只有输入发现数据内在结构用户聚类、异常检测、降维强化学习环境交互的奖励信号学到使累计奖励最大化的策略游戏AI、机器人控制、自动驾驶决策这三个方向不是相互排斥的。我见过不少入门者一上来就扎进深度强化学习结果连基础的监督学习都没吃透策略梯度更新都看不懂。我个人的建议是先把监督学习走通理解损失函数、梯度、泛化这些基础概念再去碰另外两个方向会顺畅得多。2. 神经网络的骨架从感知机到深度网络的演进逻辑2.1 感知机最早的人工神经元深度学习之所以叫“深度”是因为它的基础组件是“神经网络”。追根溯源最早的神经网络可以追溯到感知机Perceptron1958年。它的结构极其简单输入几个特征各自乘上权重累加后过一个激活函数输出结果。单个感知机本质上就是一个线性分类器数学形式为output activation(w1 * x1 w2 * x2 ... wn * xn b)其中 (w) 是权重(b) 是偏置activation 是激活函数。感知机学习的过程其实就是不断调整 (w) 和 (b)使输出结果逼近真实标签的过程。但感知机有一个致命缺陷它连异或XOR这种简单的非线性问题都解决不了。这个限制在 1969 年被 Minsky 写进书里直接导致了神经网络研究的第一次寒冬。2.2 激活函数与非线性多层网络存在的前提既然单层只能做线性分类那能不能把多个感知机堆叠起来当然可以但如果激活函数是线性的多层叠加后依然只是线性变换的复合——数学上算一下就会发现最终还是等价于一层线性模型。所以关键突破在于引入非线性激活函数。有了非线性神经网络的表达能力才真正起了质变。以下是几个主流激活函数激活函数函数表达式特点适用场景Sigmoid(1 / (1 e^{-x}))输出范围(0,1)适合概率输出二分类输出层Tanh((e^x - e^{-x}) / (e^x e^{-x}))输出范围(-1,1)零中心较早的全连接层ReLU(max(0, x))计算快缓解梯度消失隐藏层标配Leaky ReLU(max(0.01x, x))缓解ReLU死亡神经元问题深层网络隐藏层ReLU 是当前默认选择不是因为它在理论上碾压一切——其实它小毛病不少——而是因为它计算开销低、梯度传播效率高在大量实践场景中最抗揍。很多刚入门的朋友会纠结选哪个激活函数其实不用太纠结隐藏层无脑 ReLU输出层根据任务选择分类用 Softmax回归用线性先把流程跑通再慢慢试别的。2.3 多层网络的表达能力多层神经网络有一个非常直观的理解方式每一层都在对数据进行某种“重新表达”。第一层可能只认识边缘和纹理第二层把边缘组合成局部形状第三层把局部形状组合成更抽象的语义……网络越深能表达的概念越抽象最终把抽象特征映射到分类结果上。这就像是把一个大乐高模型拆成一堆基础方块再逐层拼装回去。这个过程不需要人显式引导模型自动会学出从低级到高级的特征表示——这正是深度学习和传统机器学习的本质分野。注意这里我说“网络越深表达能力越强”但存在一个实际约束层数加深后随之而来的是参数变多、训练难度上升、梯度在反向传播过程中容易消失或爆炸。所以“深度”不等于“越深越好”需要靠残差连接、归一化、注意力机制等设计来辅助训练。这些都是后续笔记会展开的话题第1篇先建立一个整体认知就够了。3. 训练过程的数学直觉梯度、损失与反向传播3.1 损失函数训练和评估的标尺一个模型好不好不能靠感觉需要一把“尺子”来衡量。这把尺子就是损失函数。以常见的交叉熵损失为例当模型对一张“猫”图输出概率分布 ([0.2, 0.7, 0.1])而真实标签是“第二类”时损失会比较大如果输出的是 ([0.05, 0.9, 0.05])损失就小。训练过程的目的就是让这个损失不断下降。不同任务适用的损失函数不一样回归任务预测房价、气温均方误差MSE二分类二元交叉熵多分类交叉熵目标检测分类损失 框回归损失组合损失函数定义得好不好直接决定模型学出来的效果是不是我们真正关心的。有些任务里高精度并不等于高实用价值——比如医疗诊断场景漏检和误检的代价完全不同就需要用带权重的损失函数去引导模型学习。3.2 梯度下降沿着最陡的方向下山损失函数是参数的函数训练的过程就是找一组参数让损失值最小。绝大多数神经网络没有解析解只能靠迭代逼近。最核心的迭代算法就是梯度下降Gradient Descent。名字听起来高级但直觉其实很简单想象你站在一座山上四周都是雾你需要走到山谷最低处。你不需要知道整座山的形状只需要感受脚下哪个方向最陡沿那个方向迈一步到了新位置再重新感受再迈一步。这个“最陡方向”就是梯度迈步大小就是学习率。记当前参数为 (\theta)损失函数为 (L(\theta))学习率为 (\eta)参数更新规则就是theta theta - eta * gradient(L, theta)这里有个很容易踩的坑学习率太大你可能在谷底附近反复横跳永远不收敛甚至直接发散学习率太小训练速度慢得让人崩溃。我刚入门那会儿把学习率设成 0.5 去训一个简单网络结果损失一路飙升到 NaN怎么找都找不出原因后来才发现纯粹是学习率的问题。新手做项目时可以遵循一个经验法则先从 0.01 或 0.001 起步观察前几百步的损失曲线再按数量级调节。3.3 反向传播误差从输出端逆向传导有了梯度下降还需要计算梯度。对于深层网络直接解析推导数是不现实的于是就有了反向传播算法。反向传播的核心思想是链式法则如果你想求损失函数对第 (k) 层某个权重的偏导可以先把误差从输出层逐层反向传播算出每一层的中间梯度然后用这些中间梯度组合出目标权重上的梯度。它的工作路径可以用下面这张简单的流程描述非图表形式呈现前向传播输入经过每层计算得到输出同时缓存中间变量计算输出层损失对输出结果的导数从输出层往输入层逐层回传每层利用已有导数计算本层参数梯度使用优化器更新所有参数我见过不少学习者把反向传播想得过于玄乎其实它不是一个独立的学习算法只是计算梯度的一种高效工程实现。你完全可以手推一个两层的反向传播但现实中没人手动算框架PyTorch、TensorFlow里的自动微分已经帮你处理了这一切。3.4 梯度相关热词补遗热词里单独出现了“机器学习中的梯度”和“深度学习epoch”这里一并讲掉。epoch完整过完一遍训练数据算作 1 个 epoch。网络参数并不是每看一个样本就更新一轮那是随机梯度下降的极端情况实际常用 Mini-batch 方式每看完一小批数据更新一次参数一批大小叫 batch size。epoch 数量控制的是“数据被反复看多少遍”。梯度消失与梯度爆炸深度网络中反向传播的梯度要逐层相乘如果每层梯度都小于 1乘到最后就会指数级衰减前面层的参数几乎不动这就是梯度消失反之则梯度爆炸。早期用 Sigmoid 激活函数特别容易出现梯度消失这也是 ReLU 成为主流的原因之一。4. 泛化能力与过拟合模型真正学会了吗4.1 训练集上的好成绩可能是“背题”而不是“学会了”我见过太多初学者的第一个模型训练集准确率高达 98%拿到测试集上瞬间掉到 70%。这不是偶然而是“过拟合”的标准症状。用考试来类比一个学生把练习册的题目答案全都背下来了练习题做得全对但期末考试题目稍微变一变他就不会了。他只记住了具体题目的答案没学会背后的解题方法。模型在训练集成绩好、在未见数据上表现差就是同样的道理——它把训练数据里的噪声和细节一起记住了而非抓住真正的规律。4.2 泛化误差界的直觉理解“泛化”是机器学习理论里极其核心的概念热词里专门有“机器学习数学理论:泛化误差界”这里给一个不依赖公式的直觉解读。泛化误差可以粗略理解为模型在训练集上的误差加上一个“因为模型过度拟合训练数据而在新数据上多出的偏差”。后者与模型复杂度有关——模型越复杂越容易把训练数据背下来同时与训练样本量有关——样本越多模型越难背越容易被逼着去学一般规律。用大白话说就是训练数据越少模型越复杂越容易“高分低能”。理论上给出的“误差界”就是在量化这种风险。虽然实际工程中没人会去精确计算泛化误差界但这个思想深刻影响了我们的一切实践选择模型该用多大、数据该准备多少、正则化该加多重。4.3 过拟合的识别信号实践中过拟合有几个明显的信号训练 loss 持续下降但验证 loss 先降后升训练集准确率接近 100%验证集准确率明显偏低模型对训练数据中的个别异常点极其敏感损失曲线在后期出现“剪刀差”训练与验证差距越来越大应对过拟合的手段我在实战中比较常用的是方法原理实操要点增加训练数据让模型看到更多样本模式培养一般规律数据增强、爬取更多数据、半监督扩展降低模型复杂度减少可学习的参数弱化“背题”能力减少层数、减少隐藏单元数L2 正则化对过大的权重加以惩罚让模型不依赖单一路径设置 weight decay 参数Dropout训练时随机丢掉部分神经元防止神经元之间过度依赖通常设置概率 0.2~0.5早停验证集 loss 连续多轮不降就停止训练监控验证指标而非训练指标数据增强对输入做随机扰动相当于变相增加训练数据图像翻转、旋转、加噪声等4.4 一个被忽视的事实欠拟合同样普遍初学者总是在防过拟合但实际项目中“欠拟合”出现频率反而更高——模型复杂度不够连训练集都学不好损失曲线平缓下降后就再也不动了。判断标准很简单如果训练集 loss 都降不下去说明模型表达能力不够或者数据特征没有喂够。这时候应该增加层数、增大隐藏维度、尝试更强的特征表示而不是盲目用正则化手段去压缩模型。可别小看这一步我见过有人欠拟合时疯狂加 Dropout结果越修越差。5. 第一个完整项目从环境到端到端的实践流程5.1 事半功倍的环境选择热词里“深度学习环境配置”、“学校实验室搭建机器学习服务器”、“深度学习云平台”这些搜索词说明大家普遍被环境卡住过。我的建议很朴素第一个项目不要在环境上过度工程化CPU 就可以训练手写数字识别这类小数据集不需要一上来就配 GPU 服务器。工具链推荐这样组合Python 3.9 或 3.10 以上版本PyTorch很多资料、论文复现都在用它社区活跃Jupyter Notebook 或 VS CodeAnaconda 管理 Python 虚拟环境安装 PyTorch 的第一步是去官网选择对应的环境和 CUDA 版本如果本机没有 NVIDIA GPU直接装 CPU 版本就好。等以后真的跑大模型了再用云平台或实验室服务器不迟。一上来就折腾 CUDA、cuDNN大概率会在环境上消耗大量精力偏离第一个项目的核心目标。5.2 项目选型为什么第一个项目应该选 MNIST很多人第一个项目就跃跃欲试想弄目标检测、生成对抗网络我劝你收一收。第一个项目的目标不是“做出最酷的东西”而是“走通完整流程”。在这一点上MNIST手写数字识别依然是经典中的经典理由有三数据集小CPU 训练几分钟就能收敛预处理简单不需要太多专业知识可视化方便你随时可以把识别结果画出来看直观建立“模型在做什么”的感觉如果你嫌 MNIST 太简单下一步可以直接跳到 CIFAR-10它在图像分类任务里引入了彩色小图、更复杂的类别信息但整体规模依然可控。5.3 代码骨架第一次完整跑通我用 PyTorch 给出整个项目最精简但功能完整的骨架每段代码背后的意图我会说明。第一步加载数据并做基础预处理import torch import torch.nn as nn from torch.utils.data import DataLoader from torchvision import datasets, transforms # MNIST是灰度图Normalize(0.1307, 0.3081) 是常见归一化参数 # 让像素分布接近标准正态分布有助于网络收敛 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_dataset datasets.MNIST(root./data, trainTrue, transformtransform, downloadTrue) test_dataset datasets.MNIST(root./data, trainFalse, transformtransform, downloadTrue) train_loader DataLoader(train_dataset, batch_size64, shuffleTrue) test_loader DataLoader(test_dataset, batch_size256, shuffleFalse)这段代码里最值得注意的是batch_size和shuffle的选择batch_size 决定每次更新参数前看多少个样本64 是常用值训练集必须 shuffle否则模型每次看到的数据顺序固定容易学到数据集的排列模式。第二步定义一个简单的多层感知机模型class SimpleNet(nn.Module): def __init__(self): super(SimpleNet, self).__init__() self.fc1 nn.Linear(28 * 28, 128) self.fc2 nn.Linear(128, 64) self.fc3 nn.Linear(64, 10) self.relu nn.ReLU() def forward(self, x): x x.view(x.size(0), -1) # 将28x28展平成784维 x self.relu(self.fc1(x)) x self.relu(self.fc2(x)) x self.fc3(x) # 输出层不需要再激活交给损失函数 return x这里输出层不接 Softmax 是刻意的因为 PyTorch 的交叉熵损失函数CrossEntropyLoss内部已经包含了 LogSoftmax 操作如果你在输出层再加一层 Softmax反而可能导致数值不稳定和梯度错误。这是新手极容易踩的坑。第三步定义损失函数和优化器并开始训练model SimpleNet() criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) for epoch in range(5): running_loss 0.0 for images, labels in train_loader: outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() running_loss loss.item() print(fEpoch {epoch1}, Loss: {running_loss / len(train_loader):.4f})注意optimizer.zero_grad()这行不能省略。PyTorch 的梯度是累积的不清零的话下一轮迭代的梯度会和上一轮叠加参数更新方向完全跑偏。很多初学者出现 loss 不下降、忽高忽低的问题有一大半出在忘记清零上。第四步在测试集上评估correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: outputs model(images) _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() print(fAccuracy: {100 * correct / total:.2f}%)评估阶段用torch.no_grad()包起来是为了让框架不保存中间梯度信息节省内存、加快推理。这行代码对最终结果没有影响但对工程效率有实打实的帮助。跑完整个流程后你基本已经把数据加载、模型定义、训练循环、评估验证这四个核心环节都走了一遍。后续所有深度学习的项目无论多复杂底层骨架都逃不出这些环节。5.4 第一次跑通后如何验证自己是真懂了很多人的学习止步于“代码能跑、准确率 97%”。我建议你按下面的清单做一个自我测试把 batch_size 从 64 改成 16观察训练速度变化思考为什么把学习率改成 0.1看看 loss 会发生什么解释原因把激活函数从 ReLU 换回 Sigmoid比较收敛速度和最终效果增加一层网络观察模型参数变多之后训练时间的变化故意把训练集喂给模型但不 shuffle看看准确率会下降还是不变这几组实验做下来你对训练机制的理解会比看十篇文章还深刻。6. 新手入门的学习路径与避坑建议6.1 资源推荐哪些值得看哪些没必要啃网上教程泛滥刚开始很容易信息过载。根据我自己走过弯路之后留下来的判断给出一个更简洁的资源清单吴恩达《Machine Learning》传统机器学习的基础课程数学要求不高概念讲得非常通透。即使你最终目标是深度学习也值得先把监督学习的思路过一遍李宏毅《机器学习》中文讲解从基础到前沿都有覆盖语言风趣特别适合建立全局视野——热词里频繁出现说明大家已经发现了它《动手学深度学习》PyTorch 和 TensorFlow 两个框架都有配套代码每个概念都配实操适合边学边写代码《深度学习》花书理论性偏强可以作为枕头书查阅不适合零基础从头硬啃《机器学习》西瓜书如果你想打好机器学习理论底子这本是中文资料里难得的系统之作但注意阅读时抓主线即可不用死磕公式推导关于热词里提到的《深度学习鱼书 pdf》——这是斋藤康毅的《深度学习入门基于 Python 的理论与实现》用 NumPy 手写了简单神经网络和反向传播对理解底层原理很有帮助。但我建议不要把“手写代码理解原理”和“用框架实现项目”对立起来两者各有用处交替着学效率最高。6.2 编程语言问题Python 够吗热词里有“深度学习所需要的编程语言”这个问题我几乎每周都会被人问到。直接说结论入门和科研Python 绝对够用不需要先学别的语言做模型训练、数据预处理、算法调试90% 的场景都用 Python 实现C/C 主要用于推理部署和框架底层优化属于进阶需求Julia、Rust 这些是锦上添花不用一上来就学如果你有 Java 或 C 基础学 Python 会非常快如果没有编程基础直接学 Python 也没问题。不要因为“听说深度学习需要很好的编程能力”而畏缩我的经验是入门阶段编程基础够用就行真正卡人的通常是对数学概念的理解和对调试工具的熟练度。6.3 学习路径规划三阶段法我建议初学者按“三阶段”推进每个阶段都有一个可验证的成果阶段一建立概念框架1-2周 目标是理解机器学习的基本流程、损失函数的意义、梯度下降的直觉能说清楚监督/无监督/强化学习的区别。此时的产出是能给别人讲清楚“训练一个模型到底在做什么”。阶段二完成端到端项目2-4周 目标是独立把 MNIST 这种入门项目从数据加载跑到准确率评估期间尽量完成上节列出的几个“自测实验”。此时的产出是一个自己能说清每一行代码作用的完整项目。阶段三扩展网络结构认知4-8周 开始接触卷积神经网络和循环神经网络的基本结构理解它们在图像和序列任务中的设计动机在标准数据集上做出超过简单全连接网络的效果。此时的产出是一篇在小数据集上的对比实验报告。这三个阶段走完你基本已经具备自学任何更高级内容的能力后续无论是看论文还是复现 GitHub 项目都不至于两眼一抹黑。6.4 新手最容易陷入的三个坑第一个坑是“只看不练”。视频看了一百个小时代码一行没跑过。大脑会产生“我都看懂了”的错觉一动手就会发现连 import 都报错。对抗方法很粗暴一个视频最多看两遍第二遍一定跟着代码走。第二个坑是“急于求成”。上来就想复现最新的扩散模型或大语言模型结果光环境配置就劝退了。深度学习是个系统性学科基础概念不牢后面所有内容都会变成空中楼阁。控制自己的好奇心把该走的路走完比你想象的更快。第三个坑是“不求甚解”。代码跑通就觉得大功告成从不去关心每个模块为什么存在。这种人看起来学过很多东西遇到真实问题换个数据集就懵了。排查问题的能力和调试的能力恰恰是从一次次“逼自己解释为什么”中训练出来的。我对新手最真诚的建议是不要试图在第一周搞懂所有数学原理先跑通一个完整项目建立“我确实能让机器从数据中学到东西”的成就感再带着问题回头补理论。机器学习和深度学习的入门本质上是一个“框架先行、细节后补”的过程只要大方向不偏慢一点没有关系。
返回列表