纽约大学深度学习笔记-全-
纽约大学深度学习笔记(全)

深度学习课程 P1:历史、动机与演进 🚀


在本节课中,我们将学习深度学习的历史背景、核心动机以及其发展演进过程。我们将从早期神经网络的概念讲起,探讨其灵感来源、关键突破,并了解现代深度学习成功的原因及其局限性。
课程概述与基本信息
本课程由教学团队运营,主要成员包括Alfredo Kanjyani和Margo Stein。课程相关问题请在课程期间提问。
课程网站将提供幻灯片PDF,通常在讲座开始前发布。课程安排包括每周的讲座以及实践会议,后者将用于解决实际问题、复习必要数学知识、介绍基本概念以及软件工具(如PyTorch)的使用教程。
此外,课程将安排三场关于自然语言处理、计算机视觉和元学习等主题的客座讲座。评估方式包括期中考试和期末项目。期末项目可能涉及监督驾驶或自动驾驶等方向,需要收集和处理数据。

本节课作为第一讲,内容广泛,旨在介绍深度学习是什么、能做什么以及不能做什么,为后续课程提供一个整体的高级概览。
课程前提与计划
本课程假设您已熟悉机器学习基础知识。课程计划是动态的,将根据大家的反馈进行调整。
以下是课程的核心内容路线图:
- 深度学习的能力与优势:首先探讨深度学习能解决哪些传统方法难以处理的问题,其核心优势在于学习新的数据表示,而非发明新的表示形式。
- 神经网络基础:下周将深入讲解反向传播算法和基本架构组件。核心思想是神经网络由相互连接的模块构成,我们可以利用自动微分计算梯度。我们将涵盖各种架构、损失函数、激活函数以及模块化技巧(如权重共享)。
- 高级架构与交互:我们将探讨特定的宏架构(如孪生网络),并深入研究乘法交互、注意力机制和门控机制(例如LSTM)。
- 专业架构:课程将介绍在特定场景下有用的架构,例如用于处理序列数据的循环神经网络(RNN)及其随时间反向传播(BPTT)的变体。我们还将讨论将卷积与循环结合的网络,以及使用乘法交互的架构(如记忆网络、Transformer、适配器等)。
- 图神经网络:会简要介绍图神经网络,但本课程不会深入,因为另有专门课程涵盖此主题。
- 优化理论与技巧:我们将探讨如何使深度学习系统有效工作,包括各种优化技巧。深度学习优化几乎总是基于梯度的,但目标函数通常是非凸的,存在局部极小值。虽然其理论理解尚不完善,但存在许多基于直觉、理论分析和实证搜索的技巧,例如初始化技巧、归一化技巧(如批归一化)、正则化技巧(如Dropout)以及优化技巧(如动量、Adam等)。
- 基于能量的模型:这是我个人偏爱的主题。它为许多学习方法(无论是否监督)提供了一个统一的视角,并将推理视为在神经网络中优化能量函数以寻找变量值的过程。这与将神经网络单纯视为前向计算函数的观点不同,允许对给定输入产生多个可能的答案。
- 自监督学习:这是当前非常活跃的研究领域,在自然语言处理中已占主导地位,在计算机视觉中也表现优异。许多您可能听说过的技术,如去噪自编码器、BERT、Transformer等,都可以置于基于能量的方法框架下来理解。生成对抗网络(GAN)也与此相关。
- 通向更通用人工智能的路径:最后,我们将讨论如何通过自监督学习等方法,让机器获得更接*动物或人类的常识与智能。
深度学习简史:从控制论到现代复兴
深度学习的思想灵感来源于对大脑的观察,但这仅是灵感而非复制,因为大脑的许多细节可能与智能无关。这类似于飞机受鸟类启发,但飞行原理相似,实现方式却截然不同。


其历史可追溯至现已基本消失的“控制论”领域。在20世纪40年代,McCulloch和Pitts提出了神经元可模拟布尔逻辑电路的想法,即神经元计算输入的加权和并与阈值比较,以决定是否激活。几乎同时,Donald Hebb提出了“赫布学习”假说,即同时激活的神经元之间的连接会增强。
Norbert Wiener提出了“控制论”,研究带有传感器、执行器和反馈回路的自调节系统(如汽车的PID控制器)。受此启发,Frank Rosenblatt在20世纪50年代末提出了“感知器”——一种可以学习修改权重的简单神经网络,甚至建造了物理原型机。

然而,到20世纪60年代末,由于当时算法和架构的能力有限,只能解决非常简单的模式识别问题,神经网络研究陷入低谷,几乎无人问津,除了日本的一些孤立研究。

转机出现在20世纪80年代中期,随着反向传播算法的出现。该算法能够训练多层神经网络。之所以在60年代未被发现,主要原因有两个:一是当时研究者多使用不可微的二进制神经元,而反向传播需要连续可微的激活函数;二是早期计算机进行浮点数乘法非常昂贵,而连续神经元需要大量乘法运算。到了80年代,计算机速度提升,使得反向传播变得可行。
因此,从1985年到1995年,神经网络研究经历了约十年的繁荣期。随后,由于支持向量机等方法的竞争,神经网络再次被机器学习主流领域冷落,这种情况一直持续到2009-2010年左右。
现代深度学习的崛起
2009-2010年左右,研究者发现使用反向传播训练的多层神经网络能在语音识别上取得显著提升。约18个月内,所有主要语音识别厂商都部署了基于神经网络的商业系统。如果您在2012年左右使用Android手机进行语音搜索,背后很可能就是神经网络。
2012年底至2013年,类似的革命发生在计算机视觉领域。2012年,AlexNet在ImageNet图像识别挑战赛上以巨大优势获胜,彻底改变了整个领域。到2016年左右,自然语言处理(尤其是机器翻译)也经历了同样的变革。如今,我们正见证深度学习在机器人、控制等更多应用领域的革命。


监督学习与神经网络基础


目前,超过90%的深度学习应用属于监督学习。其过程是:收集大量输入-输出对(如图像和标签),将输入提供给机器,机器产生输出。如果输出错误,则调整机器参数,使输出更接*期望值。

在神经网络中,这些参数就是权重。关键技巧在于如何确定调整每个权重的方向和大小,这就是梯度下降和反向传播要解决的问题。
在反向传播出现之前,基本的分类模型(如感知器和Adaline)都基于相同的基本架构:计算输入的加权和,并通过阈值产生输出。
传统的模式识别流程分为两个阶段:
- 特征提取器:手动设计,从原始输入中提取相关特征。
- 可训练分类器(如感知器):对特征向量进行分类。

数十年的研究都集中在如何为特定问题设计特征提取器。


深度学习的核心思想:端到端学习


深度学习的核心思想是摒弃这种两阶段流程,而是端到端地学习整个任务。系统由一系列模块堆叠而成,每个模块都有可调参数和非线性变换。堆叠多层模块,因此称为“深度”学习。然后,使用反向传播算法,根据最终输出误差来调整所有模块(包括最初的“特征提取”部分)的参数。

为什么模块必须是非线性的?因为如果所有层都是线性的,那么多个线性层可以合并为一个线性层,增加深度就没有意义了。






最简单的多层架构就是多层神经网络。输入(如图像像素向量)乘以一个权重矩阵(参数),得到的结果向量再逐个分量通过一个非线性函数(如ReLU)。这个过程可以重复多次。





卷积神经网络(CNN)的灵感与起源
处理全尺寸图像时,如果使用全连接层,参数矩阵将极其庞大(例如,对于25万像素的输入,若第一层有1000个神经元,则权重矩阵有2.5亿参数)。这既不现实,也忽略了图像数据的空间结构。

灵感来自神经科学。20世纪60年代,Hubel和Wiesel发现视觉皮层神经元具有局部感受野和方向选择性(只对视野中特定区域、特定方向的边缘有反应),并且这种模式在整个视野中重复。
受此启发,福岛邦彦(Kunihiko Fukushima)提出了神经认知机。其核心思想包括:
- 局部连接:神经元只连接输入的一小部分。
- 权重共享:同一层中不同位置的神经元使用相同的权重集。
- 简单细胞与复杂细胞:简单细胞检测特定方向的边缘,复杂细胞聚合多个简单细胞的输出,从而获得一定的*移不变性。
这些思想直接导致了80年代末卷积神经网络(CNN) 的出现。CNN在图像上滑动局部滤波器(卷积核),并在不同位置共享权重,极大地减少了参数数量。


卷积神经网络的应用与影响

CNN最早成功应用于手写数字识别。随后,研究者将其应用于更大图像,通过滑动窗口技术进行物体检测(如人脸、行人)、语义分割(为图像中每个像素分类)等。
例如,在机器人导航中,CNN可以分析相机图像,分类每个小窗口的中心像素属于“地面”还是“障碍物”,从而生成可通行区域地图。

在自动驾驶领域,Mobileye公司(现属英特尔)率先将CNN用于车辆视觉系统,实现了车道保持、自动紧急制动等功能,现已广泛应用于众多汽车。



ImageNet竞赛与深度学习的爆发
2012年,Geoff Hinton组的学生Alex Krizhevsky利用GPU训练了一个深层的CNN(AlexNet),在ImageNet图像识别挑战赛上将Top-5错误率从25.8%大幅降至16.4%,引起了计算机视觉社区的震动。此后,整个领域迅速转向深度学习。


网络层数也越来越深,从几层(如AlexNet)发展到数十层(如VGG, ResNet),甚至上百层。同时,研究者发明了各种架构技巧来提升性能。


深度学习成功的原因与未解之谜
CNN成功的关键在于利用了自然数据的组合性:世界由对象组成,对象由部件组成,部件由更基础的图案组成。深度网络的层次结构恰好可以学习这种层次化的组合表示。
尽管如此,深度学习为何如此有效,仍存在一些未解之谜:
- 理论优势:既然两层网络就能*似任何函数,为什么需要更深?
- 优化之谜:目标函数高度非凸,存在大量局部极小值,为何梯度下降能有效工作?
- 泛化之谜:统计学习理论认为,参数数量不应超过样本数,否则会过拟合。但深度学习模型通常有数百万甚至数十亿参数,仅用数百万样本训练,为何仍能泛化得很好?
对于第一个问题,一个关键见解是:虽然两层网络在理论上有万能*似能力,但对于许多自然问题(如计算奇偶性),所需的中间单元数量可能是输入规模的指数级。而使用多层(深度)网络,可以用少得多的资源(神经元数量)来解决同样的问题,这是在表达效率上的巨大优势。这类似于在布尔电路设计中,深度电路可以比浅层电路更高效。
什么是好的表示?

深度学习的本质是学习表示。传统方法中,表示是手动设计的。而学习表示的目标是将原始数据转化为对后续任务有用的形式。

一个指导原则是流形假设:自然数据(如图像)虽然存在于高维空间(如百万像素空间),但实际上分布在一个相对低维的流形表面上。例如,一个人脸的所有图像(在不同姿态、表情下)构成的集合,其内在自由度可能只有几十个(对应头部姿态、肌肉活动等)。
理想的表示应该能够解耦这些解释数据变化的独立因素。例如,将身份、姿态、表情、光照等因素分离开来。虽然我们尚不知道如何完美实现这一目标,但这是表示学习的一个终极目标。


总结

本节课我们一起回顾了深度学习从控制论、感知器到反向传播和现代卷积神经网络的发展历程。我们了解了深度学习通过端到端学习层次化表示的核心思想,看到了它在图像识别、语音识别、自然语言处理以及自动驾驶、医疗影像等领域的革命性影响。同时,我们也认识到其理论基础上仍存在一些未解之谜,以及当前系统在常识推理等方面的局限性。这为后续课程深入探讨具体技术奠定了基础。


🧠 P10:卷积神经网络应用、循环神经网络与注意力机制
在本节课中,我们将学习卷积神经网络(CNN)在多种任务中的应用,并探讨循环神经网络(RNN)及其变体,特别是注意力机制的基本原理。我们将通过具体的例子和公式来理解这些概念,并了解它们如何在实际问题中发挥作用。


🖼️ 卷积神经网络的应用
上一节我们介绍了卷积神经网络的基本原理,本节中我们来看看CNN在实际任务中的多种应用方式。
滑动窗口与目标检测
CNN可以通过滑动窗口的方式处理大尺寸图像。每个窗口经过网络后,会输出一个分数向量,表示该窗口属于各个类别的可能性。例如,对于一个包含多个字符的图像,CNN可以输出每个窗口内字符类别的分数。
公式:若网络有两层池化层,每层的下采样率为2,则总下采样率为4。这意味着输出层每个单元对应输入图像中一个4×4像素区域的窗口。
以下是处理重叠检测结果的常用方法:
- 非极大值抑制(NMS):当多个相邻窗口都检测到高分数目标时,只保留分数最高的那个,抑制其周围的检测结果。这常用于物体检测任务,以避免对同一物体进行重复报告。
字符识别与序列解释
CNN可用于识别图像中的字符串,例如邮政编码。由于字符可能相连,我们无法预先分割。解决方法是将CNN应用于整个字符串区域,网络会在不同位置输出一系列分数。
为了从这些分数中得到一个连贯的字符序列(例如“3206”),我们需要一个后处理步骤。这可以通过构建一个分数矩阵,并使用动态规划寻找最优路径来实现。
核心思想:将不同位置、不同尺度检测器得到的分数,整合为一条最可能的字符序列路径。这类似于语音识别中的动态时间规整(DTW)算法。
人脸检测与多尺度处理
人脸检测是CNN的经典应用。我们训练一个CNN分类器,用于判断一个图像块(如30×30像素)是否包含人脸。
然而,实际图像中的人脸尺寸多变。解决方法之一是多尺度方法:
- 将原始图像按一定比例(如√2)逐步缩小,构建一个图像金字塔。
- 在每一层缩放后的图像上运行同一个人脸检测CNN。
- 将不同尺度上的检测结果映射回原始图像坐标,并进行合并。
计算成本:虽然需要处理多个尺度的图像,但由于图像尺寸变小,计算量并非线性增长。总成本大约是处理原始图像的2倍(1 + 1/2 + 1/4 + ... ≈ 2),这是可以接受的。
为了减少误报(将非人脸物体检测为人脸),可以采用难例挖掘:
- 收集大量不包含人脸的图像。
- 用初始检测器在这些图像上运行,收集所有被误判为“人脸”的图像块。
- 将这些难例作为负样本加入训练集,重新训练检测器。重复此过程几次,可以显著提升检测器鲁棒性。
语义分割
语义分割是为图像中的每一个像素分配一个类别标签的任务(如“道路”、“车辆”、“行人”)。
一种方法是采用“以像素为中心”的patch分类方法:
- 以每个像素为中心,截取一个固定大小的图像块(如40×40像素)。
- 将该图像块输入CNN,由网络判断中心像素的类别。
- 在整张图像上滑动这个操作,为每个像素生成标签。
为了获得更准确的分类,特别是对于需要更大上下文信息的像素(如判断“天空”),可以使用多尺度上下文融合:
- 将原始图像以及下采样2倍、4倍的图像分别输入同一个CNN。
- 将网络在深层输出的特征图进行上采样,使其与原始图像尺寸一致。
- 将这些来自不同尺度的特征图拼接起来,再送入一个分类器进行最终决策。这样,分类器在做每个像素的决策时,实际上拥有了来自原始图像更大区域(如184×184像素)的上下文信息。
🔁 循环神经网络(RNN)与序列建模
上一节我们探讨了CNN在空间数据上的应用,本节中我们来看看如何处理时间序列或序列数据。
RNN的基本结构
循环神经网络用于处理序列数据。其核心思想是引入“隐藏状态”,该状态会随着时间步传递,使网络具备记忆能力。
基本公式:
对于一个时间步 t:
- 编码器:
h_t = f_e(x_t)(将输入x_t编码为向量h_t) - 循环层:
z_t = g(h_t, z_{t-1}; W)(g是循环函数,W是可训练参数,z_{t-1}是上一时刻的隐藏状态) - 解码器:
y_t = f_d(z_t)(根据隐藏状态z_t产生输出y_t)
通过沿时间轴“展开”这个循环结构,我们可以得到一个前馈网络,从而可以使用反向传播算法进行训练,这种方法称为随时间反向传播(BPTT)。
RNN的挑战与门控机制
简单的RNN在实践中面临两个主要问题:
- 梯度消失/爆炸:在长序列上反向传播时,梯度会指数级地缩小或增大,导致难以训练。
- 长期依赖记忆困难:简单的RNN难以记住很久以前的信息(例如,在长代码中匹配括号)。
为了解决这些问题,研究者设计了门控循环单元(GRU) 和长短期记忆网络(LSTM)。它们通过引入“门”结构(由Sigmoid函数实现,输出0到1之间的值),来有控制地更新和重置隐藏状态。
GRU的核心思想:
- 更新门:决定有多少旧隐藏状态需要保留,有多少新信息需要加入。
- 重置门:决定如何将旧隐藏状态与当前输入结合。
这些门允许网络学习在长时间步中保持信息不变,从而缓解梯度消失和长期记忆问题。
注意力机制
注意力机制是神经网络中一个强大的模块,它允许模型在处理当前信息时,有选择地“关注”输入序列中的相关部分。
基本计算步骤:
- 给定一个查询向量
q和一组键值对{k_i, v_i}。 - 计算
q与每个键k_i的相似度(如点积):α_i = q^T k_i。 - 对相似度分数应用Softmax函数,得到一组权重系数:
c_i = softmax(α_i),满足Σ c_i = 1。 - 输出是值
v_i的加权和:output = Σ c_i * v_i。
这相当于让模型从值存储器中,根据查询 q,读取一个软性的、加权组合后的值。整个操作是可微分的,可以嵌入到神经网络中并通过梯度下降进行训练。
注意力在机器翻译中的应用
在神经机器翻译中,最初的编码器-解码器模型需要将整个输入句子的信息压缩到一个固定长度的向量中,这在处理长句子时效果不佳。
引入注意力机制后,解码器在生成每一个目标语言单词时,可以动态地查看输入句子所有单词的隐藏状态,并决定将“注意力”集中在哪个或哪些单词上。这极大地提升了翻译质量,特别是对长句子的处理能力,并迅速成为机器翻译的主流方法。
记忆网络与神经图灵机
记忆网络将注意力机制进一步推广,构建了一个外部可寻址的记忆模块。网络可以学习如何向这个记忆模块写入信息(存储故事),以及如何从中读取信息(回答问题)。
基本操作:
- 寻址(读取):使用输入(查询)与记忆中的键进行比较,通过注意力权重读取相关的值。
- 写入:根据网络的学习目标,更新记忆中的值。
这种结构使神经网络具备了类似计算机的工作记忆能力,可以执行需要多步推理和临时存储信息的复杂任务。神经图灵机是这一思想的延伸,试图用可微分的操作模拟图灵机的读写头移动。
📝 总结
本节课中我们一起学习了:
- 卷积神经网络(CNN)的广泛应用:包括通过滑动窗口进行目标检测、字符识别、人脸检测(结合多尺度处理和难例挖掘)以及语义分割(结合多尺度特征融合)。
- 循环神经网络(RNN)的基本原理与局限:RNN通过隐藏状态处理序列,但存在梯度消失和长期依赖问题。
- 门控循环单元(GRU/LSTM):通过门控机制有效控制信息流,缓解了简单RNN的问题。
- 注意力机制的核心思想:允许模型动态地聚焦于输入的相关部分,其核心是计算一个加权的和,权重由查询与键的相似度决定。
- 注意力机制的革命性影响:在机器翻译等任务中,注意力机制使模型不再需要将整个序列压缩到一个固定向量,显著提升了性能。
- 记忆网络:基于注意力构建的外部记忆模块,使神经网络具备了可微分的工作记忆能力,能够处理需要多步推理的任务。
这些概念,尤其是注意力机制,构成了现代深度学习模型(如Transformer)的基石,并在自然语言处理、计算机视觉等领域持续发挥着核心作用。


课程 P11:RNN 与 LSTM 架构详解 🧠
在本节课中,我们将学习递归神经网络(RNN)及其变体——长短期记忆网络(LSTM)的基本概念、工作原理和应用场景。我们将从序列数据处理的需求出发,逐步理解这些网络架构的设计思想、数学公式以及它们如何解决传统神经网络在处理序列数据时遇到的挑战。



从组合逻辑到序列逻辑 🔄
上一节我们介绍了传统的前馈神经网络,它类似于数字电路中的组合逻辑,其输出仅取决于当前输入。然而,在处理序列数据(如时间序列、文本、语音)时,我们需要一种能够记住“过去”信息的网络。
递归神经网络(RNN)应运而生。RNN 引入了“内部状态”的概念,其输出是当前输入和系统先前状态的函数。这类似于数字电路中的时序逻辑,系统拥有内部记忆单元(如触发器)。
一个简单的 RNN 单元在时间步 t 的计算可以表示为:
h_t = f(W_xh * x_t + W_hh * h_{t-1} + b_h)
y_t = g(W_hy * h_t + b_y)
其中:
x_t是时间步t的输入。h_t是时间步t的隐藏状态(即内部记忆)。h_{t-1}是上一个时间步的隐藏状态。W_xh,W_hh,W_hy是权重矩阵。b_h,b_y是偏置项。f和g是非线性激活函数(如tanh或softmax)。
通过这种结构,信息得以在时间维度上传递。
RNN 的应用模式 📊
RNN 因其处理序列的能力,被广泛应用于多种模式。以下是几种主要的应用架构:
- 一对一(序列生成):输入一个向量(如图像),输出一个符号序列(如图像描述句子)。例如,输入一张“黄色校车”的图片,输出 “a yellow school bus parked in a parking lot”。
- 多对一(序列分类):输入一个序列,输出一个向量或类别。例如,输入一段文本(单词序列),输出其情感分类(积极/消极)。
- 一对多(序列生成):输入一个向量或起始符号,输出一个序列。例如,输入一个主题词,让网络续写一段故事。
- 多对多(序列到序列):输入一个序列,输出另一个序列。这曾是机器翻译的标准方法,即编码器-解码器架构。编码器将输入序列压缩成一个“语义向量”,解码器再将该向量展开为目标语言序列。
在这些嵌入空间中,语义相似的词会聚集在一起,并且可以进行有趣的向量运算,例如:“国王” - “男人” + “女人” ≈ “女王”。
RNN 的挑战:梯度消失与爆炸 💥
虽然 RNN 理论上可以记忆长期信息,但在实践中训练深度 RNN(即时间步很长的 RNN)会遇到严重问题。
当通过时间反向传播(BPTT)计算梯度时,梯度需要沿着时间步反向传播。每一步都会乘以权重矩阵 W_hh 的转置。如果 W_hh 的特征值大部分小于1,梯度会指数级衰减(梯度消失),导致早期时间步的参数几乎无法更新。反之,如果特征值大于1,梯度则会指数级增长(梯度爆炸),导致训练不稳定。
这限制了经典 RNN 只能学习短期的依赖关系。
解决方案:长短期记忆网络(LSTM) 🚪
为了解决梯度消失问题,Sepp Hochreiter 和 Jürgen Schmidhuber 在 1997 年提出了 LSTM。LSTM 的核心思想是引入“门控”机制和一条几乎无阻碍的“细胞状态”通道。
一个 LSTM 单元包含三个门:
- 遗忘门(f_t):决定从细胞状态中丢弃哪些信息。
- 输入门(i_t):决定哪些新信息将被存入细胞状态。
- 输出门(o_t):基于细胞状态,决定输出什么。
其核心计算公式如下:
f_t = σ(W_f · [h_{t-1}, x_t] + b_f) # 遗忘门
i_t = σ(W_i · [h_{t-1}, x_t] + b_i) # 输入门
o_t = σ(W_o · [h_{t-1}, x_t] + b_o) # 输出门
C̃_t = tanh(W_C · [h_{t-1}, x_t] + b_C) # 候选细胞状态
C_t = f_t * C_{t-1} + i_t * C̃_t # 更新细胞状态
h_t = o_t * tanh(C_t) # 计算当前隐藏状态输出
其中 σ 是 sigmoid 函数,输出 0 到 1 之间的值,代表“通过比例”。* 表示逐元素相乘。
关键点:细胞状态 C_t 的更新路径主要是逐元素相乘和相加。特别是,从 C_{t-1} 到 C_t 的路径上,梯度流主要乘以遗忘门 f_t(一个标量或向量),而不是一个完整的矩阵 W_hh。如果遗忘门被学习为接* 1,梯度就可以几乎无衰减地反向传播很远,从而解决了长期依赖问题。门结构让网络学会了何时“写入”、“读取”或“重置”其长期记忆。
实践与比较 ⚖️
在实践环节,我们通过两个任务比较了 RNN 和 LSTM 的性能:
-
序列分类任务:生成长度为 100-110 的符号序列,其中包含两个关键标记
X和Y。序列的类别(Q, R, S, T)由X和Y的出现顺序决定。网络需要忽略大量随机噪声(A, B, C, D),识别出关键模式。- 结果:在简单和中等长度序列上,LSTM 能快速达到 100% 准确率。而经典 RNN 学习速度较慢,或在更长的依赖任务上完全失败。可视化 LSTM 的细胞状态可以发现,特定单元专门用于“记住”是否看到了第一个
X,直到看到Y后才重置,这清晰地展示了其记忆机制。
- 结果:在简单和中等长度序列上,LSTM 能快速达到 100% 准确率。而经典 RNN 学习速度较慢,或在更长的依赖任务上完全失败。可视化 LSTM 的细胞状态可以发现,特定单元专门用于“记住”是否看到了第一个
-
回声任务:网络需要记忆并在若干时间步后复现之前看到的输入脉冲。
- 结果:LSTM 能够完美地完成需要长期记忆的任务。当将长序列分块训练时,LSTM 可以通过其细胞状态在块之间传递记忆,而 RNN 则难以维持跨块的信息。
这些实验表明,LSTM 的门控机制使其能够有效地学习和保持长期信息,是处理序列数据的强大工具。
总结 📝
本节课我们一起学习了递归神经网络的核心思想及其进化版本 LSTM。
- RNN 通过引入循环连接和隐藏状态,具备了处理序列数据的能力,其输出依赖于当前输入和历史状态。
- 然而,经典 RNN 存在梯度消失/爆炸问题,难以学习长期依赖。
- LSTM 通过巧妙的门控结构(遗忘门、输入门、输出门)和细胞状态,创建了一条梯度高速公路,显著缓解了梯度消失问题,使其能够有效地记忆和利用长距离信息。
- 在实践中,LSTM 在语言建模、机器翻译、序列分类等需要理解上下文的任务上,表现通常远优于经典 RNN。
理解 RNN 和 LSTM 是进入现代序列建模(如注意力机制和 Transformer)的重要基石。


课程 P12:基于能量的模型与自监督学习 🧠
在本节课中,我们将要学习基于能量的模型的基本概念,并探讨它与自监督学习的联系。我们将了解如何用能量函数来表达变量间的依赖关系,以及如何利用这种模型进行预测和推理。课程内容将涵盖从基本定义到训练方法的全过程。
基于能量的模型:基本概念 🔋
上一节我们介绍了课程概述,本节中我们来看看基于能量的模型的核心思想。
基于能量的模型是一种用于表达多种学习范式的框架。它不像我们在监督学习中运行的那种简单算法,事情更为复杂。它包含了许多方法,但可能更容易理解。我认为那些可能是病态的方法。如果您想减少能源消耗,基于能量的方法确实是一种特殊情况。
从某种意义上讲,它很有启发性,它解释了很多看起来非常不同的事物,当您无法获得这种统一视图时。我要说的是,首先在监督学习中同样适用。有些人称之为有监督的学习,或者我们称之为自监督的学习。我们今天要谈一点。

关于观察到一组变量 X 的模型,我们问这个模型预测变量 Y 的集合。但我没有指定 X 是图像还是其他什么。Y 是一个离散变量,例如对于分类任务,Y 就像整数。X 可能是图像,而 Y 是描述它的标签;或者 X 是一段文字,Y 是另一种语言的句子;X 可以是整个文本,而 Y 可以是该文本的简化版本或摘要。因此,它可以是任何东西。我在这里没有指定具体形式,但它来自于前馈模型是否存在这两个问题。


神经网络或其他经典模型先于您。如果您知道有限的固定数量的计算会产生输出。有一个多层结构,您知道有固定数量的层。即使您拥有循环网络,对可以展开多少次也有一定的限制。因此,您基本上知道了固定的计算量。
但是有两个问题并不完全适合。第一种情况是计算输出时需要一些更复杂的计算,而不仅仅是有限的加权和与非线性变换。第二种情况是当我们尝试训练机器产生的不是单个输出,而是一组可能的输出。因此在这种情况下,根据分类,我们实际上是在训练机器以生产多种输出。我们正在训练它为每种可能的情况产生单独的分数。理想情况下,我们知道的系统中的类别会得出正确类别的最佳分数。在实践中,其他人的分数很小。
您知道我们运行输出时的情况,通过 softmax 函数,分数会产生一种分布,我们只选择一个分数很高的输出。但基本上我们告诉机器要做的是产生每个类别的分数,然后我们将选择最好的。当输出是连续的高维时,这是不可能的。所以,输出比如是一张图像,我们在图像上没有 softmax 函数。没有列出所有可能图像的方法,那么您知道无法在它们上规范化分布,因为它是高维连续空间。即使它是一个真正合理的连续空间,我们也不可能将该连续空间划分为离散的箱子,然后在上面进行 softmax 操作。它只能在小尺寸、低维度上工作。
所以当我们有高维连续空间时,我们不能要求系统为我们为所有可能的输出给出类似的分数,即使它是离散的,但可能是无限的。所以像我们生成文本之类的东西,给定长度的可能文本数量非常非常多。我们不能只对所有可能的文本进行 softmax 操作。存在相同的问题,所以我们如何以紧凑形式表示所有可能文本的得分分布?那就是基于能量的模型出现的地方。
但特别是基于能量的模型以及基于能量的解决方案模型给我们一个想法:我们将使用隐式函数。换句话说,我们不会让得分系统直接产生 Y,我们只是想问系统,一个特定的 X 和 Y 是否彼此兼容。所以这是该文本的一个不错的翻译。文字听起来不错,因为我们要怎么与文本或机器进行比较?让我们先理解这一点。
所以我们是 X 和 Y 的隐式函数 f,它将取 X 和 Y,并告诉我们这两个值是否兼容。如果是,为什么图像 X 的一个好的标签是 y?如果是高度故事的高分辨率版本低分辨率图像,如果 Y 很好,该句子的德语翻译等。现在,推理程序将给定一个 X,找到一个 Y,该 Y 产生一个较低的值,为此 X 和 Y 产生一个低值。换句话说,最终 Y 与 X 兼容。
因此,在可能的 Y 值位置上搜索,该值会产生 X 和 Y 的 f 函数的一个好值。所以我们看到通过最小化某些函数来影响的想法。几乎每个模型都可能暗示您坚持使用了什么。人们能想到会以这种方式工作吗?我的意思是,除了您用神经网络进行多类分类,通过基本找到得分最高的类别,您可以将其视为最低的能量。所以基本上我们将尝试找到一个输出,满足一堆约束条件,这些约束条件由 X 和 Y 的这个函数 f 定义。
如果您听说过图形模型,您知道所有的贝叶斯网络,甚至经典的 AI 或 SAT 问题,它们基本上可以用这些术语表述为问题:找到一组变量的值,这些变量将由某些要测量的函数来表示它们的兼容性好。所以我们现在不谈论运行好,我们只是在讨论推理。我们假设给出了 X 和 Y 的这个函数 f。我们将谈论稍后如何学习它。


能量功能不是我们对运动进行优化的意思,而是缓解推理过程中的问题。推理是从 X 找到 Y。所以这种能量函数是标量值,当 Y 与 X 兼容时值低,当 Y 与 X 不兼容时值高。因此您希望此函数具有这样的形状:对于给定的 X,所有与此 X 兼容的 Y 值能量低,并且所有不兼容的值具有更高的能量。这就是您所需要的,因为那么推理程序将找到 Y 的值,使 X 和 Y 的 F 最小化。注意,Y 可能不是一个值,因为可能存在多个值,并且您的推理算法实际上可能会经历多个值或检查多个值,在给您一个或几个好的值之前。
让我们举一个简单的例子。当标量变量的维数为 X 时,这里是一个实数值,Y 是一个实数值。这里的蓝点是数据点。那么,如果您想捕获数据中 x 和 y 之间的依赖关系,您想要的能量函数是这个形状或那个形状。但是,这样,如果您采用 x 的特定值,则在蓝点附*的 y 值能量较低。所以像这样的函数捕获了 x 和 y 之间的依赖关系。
现在推断给定 X 的最佳 y 是什么?如果您有这样的功能,您可以使用梯度下降。所以如果我给您一个 X,可以得出与该 X 对应的 y 的最佳值是多少?从一些随机的 Y 开始,然后通过梯度下降,找到函数的最小值。这可能会有点困难,但是从表征两个变量之间依赖关系的角度来看,这个能量函数几乎和任何其他方法一样好。
我来解决这个离散情况,当 y 是离散时是容易的。我们已经讨论过了,我会重新用能量方面的术语来表述。前馈模型是显式函数,因为它从 X 计算预测 Y,但它只能做出一个预测。我们可以在离散值的情况下作弊,输出对应于每种可能的分类都附有分数,但效果很好。但您不能使用这个技巧处理高维连续值或组合值。
所以基于能量的模型实际上是一个隐式函数。请记住,在微积分中的隐函数,您想要一个方程。圆是 x 和 y 的函数,而您不能将 Y 写成 X 的函数。写一个方程,比如 X *方加 y *方等于 1,您就定义了单位圆。所以 X *方加 y *方减 1 是隐式函数,当您求解它等于零时,您得到圆圈。
这里的另一个例子再次是 x 和 y 的标量值。这里的黑点是数据点。因此对于红色条表示的 x 的三个值,存在 y 的多个兼容值,其中一些实际上是排序的值的连续性。因此我们希望能量函数看起来基本上是在这里,我有点画水*。该能量函数的集合在数据点上消耗低能量,在更高能量的地方消耗高能量。这是一个稍微复杂一些的版本,我之前展示的 3D 模型的模块类型。
问题是我们是否要对系统进行训练,使其适应环境,从而计算出能量函数实际上具有适当的形状。当 y 连续时,F 光滑且可微是很好的,因此我们可以使用基于梯度的推理算法。所以如果我们有这样的函数,我给您一个 X 和 Y 点,您可以通过梯度下降找到最接*它的点。如果我给您一个 x 的值,您可以知道沿 y 方向通过梯度下降进行搜索,找到一个最小化它的值。这就是推理算法。
算法真的是一个处方:您将其最小化。为此,存在各种不同的方法。基于梯度的方法就是其中之一。在 F 复杂的情况下,您可能无法依靠搜索方法,因此您可能不得不使用其他技巧。在大多数情况下,尽管它简化了。但对于那些知道图形模型的人,图形模型基本上是基于能量的模型,能量函数分解为能量项的总和,每个能量项考虑到您正在处理的变量的子集。因此,F 是某些 f_s 集合的总和,每个 f_s 占用 Y 的子集,或 x 和 y 等的子集。如果它们以特定形式组织,那么有有效的推理算法来找到这些项总和的最小值,这些项与您感兴趣的变量有关。这就是您所知道的在图形模型中的推理算法。如果您不知道,这是您无需考虑的。
正如我所说的,您可能想要使用它的情况是当推理比您所知道的要复杂得多时,当输出为高维时,通过几层神经网络运行,并具有序列或图像或图像序列的结构。当输出具有组合结构时,无论是文本、动作序列,您知道诸如此类的内容,或者当您需要从复杂的长链推理中解决约束满足问题时。




好的,这是基于能量的模型的一种特殊类型。
包含潜在变量的能量模型 🎭

上一节我们介绍了基本的能量模型,本节中我们来看看包含潜在变量的模型,它们开始变得有趣起来。
在这种情况下,书面变量 EBM 我们不仅取决于您所观察的变量 X 和想要预测的变量 Y,我们也依赖于额外的变量 Z,没有人告诉您它的价值。这个潜在的变量是您以这样的方式构建模型:它取决于潜在变量。如果您使用此潜在变量,推断问题将变得更加容易。

所以假设您确实赢得了团队的认可。我喜欢我告诉您的这个例子:如果您知道字符在哪里,阅读单词就好多了。这里的主要问题是,单词不仅是阅读单个字符,而且您实际上会发现角色是什么样的,一个字符在哪里结束,另一个字符在哪里开始。如果我要告诉您,这对您来说会容易得多。如果您懂英语,请在此处阅读此字符序列(英文),您可以解析它,您可能可以找出单词边界在哪里,因为您具有这种高级知识。无论是否知道单词是英文的,我想对您来说,同样的道理,除非您会说法语,否则不知道边界这个词会怎样。
在这种情况下,电池一词以及顶部的字符边界对于解决问题很有用,例如字符识别,将各个字符识别器应用于每个字符,但您不知道它们在哪里。所以您怎么知道如何解决这个问题?所以如果我告诉您,那将是一个有用的潜在变量。


对于语音识别,问题在于您不知道单词之间的边界在哪里。音素要么是语音,要么非常像连续文本。连续语音,我们可以解析单词,因为我们知道单词在哪里,因为我们了解该语言。但有人说的是您不懂的语言,您有一个非常模糊的想法其中“边界”一词是。当您无法使用没有重音的语言时,之所以容易,是因为单词上有重音。所以如果您能找出重音,您大概可以弄清楚单词边界在哪里。没有重音的法语,您无法想像没有办法。
现在,您的视频 phasma 不会说确定,只有一种字体可以向右移动。所以您知道这是一种连续的音素串,除非您知道边界,否则很难说出边界在哪里。语言,这将是一个可行的联系,因为如果有人告诉您这些界限在哪里,那么您将能够做该任务。所以这就是您使用潜在变量的方式。
这种使用潜变量的方法已经在数十年中使用了,在自然语言处理环境中的语音识别环境,在我所说的 OCR 和许多不同的其他应用程序中,特别是涉及序列的应用程序,而且在计算机视觉方面。所以像您这样的事情知道您想要检测一个人在哪里,但您不知道该人的穿着或人在这样的事情上的位置。所以您知道那些是变量,如果您知道我愿意的话,尽管如今,视觉效果还可以。所以如果您有一些可变模型,这是如何进行推理。

所以您现在有了一个新的能量函数,称为 F(X, Y, Z)。在推理中,您要相对于 Z 和 Y 最小化它。所以您问系统给我 Y 和 Z 的变量组合。意思是这个能量函数,我实际上只在乎 Y 的值,但是我必须同时对 Z 进行最小化。我稍后再给您一些具体的例子。


实际上,这等效于定义新能量函数 F_inf(X, Y)。F_inf(X, Y) 是 min_Z F(X, Y, Z)。您得到 X、Y、Z 的函数,则找到了最小值。现在可以消除 Z 变量,而得到 x 和 y 函数。在实践中,您永远不会那样做,所以我没有练习尽量减少尊重到 Y 和 Z,因为我们不知道如何表示一个函数。
但是有一个替代方法是在这里用她定义 F_beta。我写 F_beta(X, Y) = -1/beta * log(∫_Z e^{-beta * F(X, Y, Z)} dZ)。您会看到,如果将 beta 设为无穷大,这种 F_beta 收敛到 F_inf,这就是为什么我称它为 F_inf。如果 beta 非常大,唯一重要的是 F(X, Y, Z) 中 Z 的值产生最低的值,因为其他所有值都将很大。所以即使指数不会真正计算,唯一要计数的是具有最低值的那个。所以如果您只有一个项,其中 F(X, Y, Z) 的 Z 的值产生最小的值,则对数会取消指数值和 beta 值的负 1,无法取消负的 beta,而您的*均值仅等于 F(X, Y, Z)。好的,这就是您在上面看到的极限。
所以如果我在此定义 F(X, Y) 一遍又一遍,然后我回到了前面的问题,就是最小化 F(X, Y) 相对于 Y 进行推断。我认为潜在变量模型对您没有太大的影响,关于要做的潜在变量有一个额外的最小化。比那还好,所以允许潜伏也有很大的优势。通过改变集合中的潜在变量可以说的变量,系统的预测输出也会在一个集合上变化。
所以这是一个 X 进入我称为预测变量的特定体系结构。这是一种神经网络,它产生一些表示特征 X 表示,然后 X 和 Z 表示,我将涉及的内容很少。这就是我在这里所说的解码器,它产生一个预测 Y_bar。我们要预测的变量 Y 的预测。此处的能量函数只是比较 Y_bar 和 y。您对这种图很熟悉,我们讨论过有关它们的内容较早。
所以如果我选择在一个集合上改变 Z,我们假设二维正方形,在此以该灰色图表示。然后预测 Y_bar 将,在这种情况下,也会随着一组变化而变化。功能区,基本上可以拥有一台机器,现在我可以通过改变潜变量可以产生多个输出。我可以有这个机器产生多种输出,而不仅仅是单一的输出。
好的,假设您要制作视频预测。所以有很多原因可能导致您想要制作视频预测。其中一个很好的理由是建立非常好的视频压缩,例如,压缩系统的另一个很好的原因是您正在尝试从挡风玻璃观看的视频中预测驾驶汽车时周围的汽车。这是正在进行的工作,因此成为能够在发生之前预测会发生什么。实际上,这是智慧的本质。
现在预测,您正在看着我,我在说,您对几秒钟后我的嘴里就会冒出一个字,我将要做的手势还是一些想法或要朝什么方向移动,但是不是一个精确的想法。所以如果您训练自己的神经网络来制作一个从现在起两秒钟我对我的预测只有一个,如果您训练自己,就无法做出准确的预测。最小二乘法,我们应该训练一个可以预测的东西。我在这里的看法是,系统能做的最好的事情就是产生一个我的图像模糊,因为它不知道我要向左还是向右移动,不知道我的手会像这样或那样。所以它会得出所有可能结果的*均值,那将是图像模糊。所以预测变量无论是什么都非常重要,能够处理不确定性并能够做出多种预测。


参数化预测集的方法是通过一个潜在变量,而不是谈论分布或可能病态的模型。这是在我们谈论这个问题之前,还可以吗?潜在变量不是一个参数,不是权重,而是每个样本都会改变的值。因此,基本上在训练期间我们还没有谈论训练,但是在训练期间,训练,我给您 X,而您找到 Z,能量与电流的关系。这些神经网络的参数值是最好的,您最好猜出 Z 的值是多少,然后将其馈给一些损失函数,您要针对参数最小化的功能。网络损失函数不一定是*均值,不一定能量可能还可以,实际上大多数时候是另外一回事。
所以从这个意义上说,您运行 Z 信息 Z 好的。您不想使用“学习”一词,因为学习意味着您拥有在这里为 Z 所获得的整个训练集所学到的所有氛围的一个价值。对于您的训练集中的每个样本或每次放置您的样本具有不同的价值。测试站点就可以了,这样就不会学到它们了。它们被推断是的。
另一个例子是他的翻译。所以翻译有一个很大的问题是语言翻译,因为没有一个通常将一段文本正确地从一种语言翻译成另一种语言。表达相同想法的方式有很多,为什么您选择了一个?所以如果有某种方法可以很好地将系统可以翻译的所有可能的翻译参数化,想要与给定文本相对应的产品(德语)翻译成英文可能有多种翻译,您需要列出全部正确。并且通过更改一些潜在变量,您可能知道更改翻译产生的很好。


现在让我们将其与概率联系起来。
从能量到概率的转换 📊
上一节我们介绍了包含潜在变量的模型,本节中我们来看看如何将能量模型与概率联系起来。

定价模型中有一种转化能量的方法,您可以将其视为一种,如果您愿意,因为低能量好而高能量不好,则需要负分数。将能量转化为概率以及通往二十种幻觉的方法,我们已经讨论过的概率,使用所谓的吉
课程 P13:第7周实践课 - 欠完备与过完备自编码器 🧠


在本节课中,我们将学习自编码器的核心概念,特别是欠完备和过完备自编码器的工作原理、应用场景以及如何通过它们学习数据的内在表示。我们将通过简单的例子和代码来理解这些模型如何压缩和重建数据,并探索它们在去噪等任务中的强大能力。
概述:什么是自编码器?
自编码器是一种无监督学习模型,其目标是学习输入数据的高效表示(编码),然后从这个表示中重建出原始输入(解码)。它由两部分组成:编码器将输入数据压缩成一个潜在空间表示,解码器则从这个表示中重建数据。
核心公式可以表示为:
x_hat = decoder(encoder(x))
其中 x 是原始输入,x_hat 是重建的输出。训练的目标是最小化 x 和 x_hat 之间的差异。

上一节我们介绍了生成模型的基本概念,本节中我们来看看自编码器这一具体且强大的无监督学习工具。


自编码器的基本架构
自编码器的网络结构与我们之前见过的神经网络相似,但有一个关键区别:它的输出目标是预测其自身的输入。网络从底部的输入层开始,经过一个绿色的中间隐藏层,最终输出层试图重建原始的输入。
以下是其工作的简化描述:
- 输入数据
x(例如,一幅图像)被送入编码器。 - 编码器将其转换为一个低维或高维的隐藏表示
h。 - 解码器接收
h并尝试重建出原始输入x_hat。 - 损失函数计算
x和x_hat之间的差异(如均方误差),并通过反向传播更新网络权重。

这种结构迫使网络学习数据中最重要的特征,以便能够准确地重建它。
欠完备自编码器:学习压缩
当隐藏层的维度 D 小于输入维度 N 时,我们称之为欠完备自编码器。即 D < N。


在这种情况下,网络无法简单地学习恒等映射(即直接复制输入),因为中间表示的信息容量小于输入。这迫使网络必须学习如何以更紧凑的形式捕捉输入数据中最关键的信息,从而实现了一种数据压缩。


例如,如果输入是784维的MNIST手写数字图像,而隐藏层只有30维,那么网络必须学会将这784个像素的信息“挤压”到30个数值中,然后再尝试重建。这30个数值就构成了该数字在潜在空间中的表示。
欠完备自编码器的目标函数通常就是重建损失:
Loss = MSE(x, x_hat)
过完备自编码器与正则化
相反,当隐藏层的维度 D 大于或等于输入维度 N 时(D >= N),我们称之为过完备自编码器。
这里出现了一个问题:如果隐藏层比输入还大,网络很容易就学会恒等映射,即简单地将输入复制到隐藏层再复制到输出,而不学习任何有意义的数据结构。为了避免这种情况,我们必须对模型施加约束,引入“信息瓶颈”。
以下是几种防止过拟合、促使网络学习有用表示的方法:
1. 降噪自编码器
降噪自编码器通过向输入数据添加噪声(如随机将部分像素置零)来训练。网络接收被破坏的、有噪声的输入 x_tilde,但训练目标却是重建原始的、干净的输入 x。
这个过程迫使网络学习到数据流形(data manifold)的结构,并学会如何将偏离流形的点(带噪声的输入)拉回到流形上(干净的重建)。它学习的是一个向量场,该向量场将输入空间中的点映射回训练数据所在的流形。
2. 稀疏自编码器
稀疏自编码器在损失函数中添加了对隐藏层激活值的正则化项(如L1正则化),鼓励隐藏层中只有少数神经元对任何给定输入保持活跃。这迫使网络学习到一种稀疏的、类似特征检测器的表示。
损失函数变为:
Loss = MSE(x, x_hat) + λ * ||h||_1
其中 ||h||_1 是隐藏层激活值的L1范数,λ 是控制稀疏性强度的超参数。
3. 收缩自编码器
收缩自编码器惩罚隐藏表示 h 对输入 x 的导数(即雅可比矩阵的Frobenius范数)。这鼓励模型学习对输入微小变化不敏感的鲁棒性表示,使得潜在空间表示更加*滑和稳定。
损失函数为:
Loss = MSE(x, x_hat) + λ * ||J_f(x)||_F^2
其中 J_f(x) 是编码器函数 f 在 x 处的雅可比矩阵。
潜在空间插值的魅力

自编码器一个强大的特性是其学习到的潜在空间。在潜在空间中,数据的语义被编码为连续的向量。这意味着我们可以在两个数据点(如一张狗和一张鸟的图片)的潜在表示之间进行线性插值,并将插值点解码回像素空间。

与直接在像素空间进行插值(会产生模糊、无意义的叠加图像)不同,在潜在空间插值会产生语义上*滑过渡的图像(例如,从狗逐渐 morph 成鸟)。这证明了自编码器确实捕捉到了数据背后更高层次、更本质的特征。
实践:代码示例与结果
让我们通过一个简单的PyTorch代码示例来看看自编码器的实际效果。我们将构建一个处理MNIST手写数字的自编码器。
首先,我们定义模型结构、损失函数和优化器。
import torch
import torch.nn as nn
import torch.optim as optim
class Autoencoder(nn.Module):
def __init__(self, input_dim=784, hidden_dim=30):
super(Autoencoder, self).__init__()
# 编码器
self.encoder = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.Tanh()
)
# 解码器
self.decoder = nn.Sequential(
nn.Linear(hidden_dim, input_dim),
nn.Tanh()
)
def forward(self, x):
h = self.encoder(x)
x_hat = self.decoder(h)
return x_hat
# 初始化模型、损失和优化器
model = Autoencoder(input_dim=784, hidden_dim=30)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
接下来是训练循环。我们加载MNIST数据,将其展*为向量,然后进行训练。
# 假设 train_loader 是已经准备好的MNIST数据加载器
num_epochs = 20
for epoch in range(num_epochs):
for data in train_loader:
img, _ = data
img = img.view(img.size(0), -1) # 展*图像
# 前向传播
output = model(img)
loss = criterion(output, img)
# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')
训练完成后,我们可以观察重建效果。欠完备自编码器(隐藏层30维)能够重建出可辨认的数字,但会丢失一些细节,这是压缩的代价。



构建降噪自编码器


现在,我们修改代码来创建一个降噪自编码器。关键步骤是在输入时随机丢弃一部分像素(添加噪声),但训练目标仍是原始图像。


class DenoisingAutoencoder(nn.Module):
def __init__(self, input_dim=784, hidden_dim=500):
super(DenoisingAutoencoder, self).__init__()
self.encoder = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.Tanh()
)
self.decoder = nn.Sequential(
nn.Linear(hidden_dim, input_dim),
nn.Tanh()
)
self.dropout = nn.Dropout(p=0.5) # 以50%的概率丢弃神经元/像素
def forward(self, x):
# 添加噪声:应用Dropout来模拟像素丢失
x_noisy = self.dropout(x)
h = self.encoder(x_noisy)
x_hat = self.decoder(h)
return x_hat


# 训练过程类似,但输入是 x_noisy,目标是原始 x
model_dae = DenoisingAutoencoder(hidden_dim=500)
optimizer_dae = optim.Adam(model_dae.parameters(), lr=0.001)


for epoch in range(num_epochs):
for data in train_loader:
img, _ = data
img = img.view(img.size(0), -1)
# 模型前向传播(内部添加噪声)
output = model_dae(img)
# 损失计算:输出与原始干净图像比较
loss = criterion(output, img)
optimizer_dae.zero_grad()
loss.backward()
optimizer_dae.step()


降噪自编码器学习到的滤波器与欠完备自编码器有显著不同。欠完备自编码器的许多滤波器在数字区域外是均匀的(不关心背景),而降噪自编码器的滤波器在整个图像区域都学习了有意义的边缘和形状特征,因为它需要根据受损的上下文来推断缺失部分。

与经典去噪算法对比
为了展示降噪自编码器的能力,我们可以将其与传统的图像去噪算法(如OpenCV中的非局部均值去噪或TV-L1算法)进行比较。
实验表明,对于我们特定引入的噪声类型(随机像素丢失),我们训练的降噪自编码器在视觉质量上通常优于这些传统算法。这是因为自编码器直接从数据中学习了噪声分布的特定模式和图像本身的先验知识,从而能进行更智能的重建。
总结
本节课中我们一起学习了自编码器的核心思想。我们首先介绍了自编码器的基本架构和目标——重建输入。然后,我们深入探讨了两种主要类型:
- 欠完备自编码器:通过使隐藏层维度小于输入,强制网络学习数据的压缩表示。
- 过完备自编码器:隐藏层维度较大,但需要通过正则化技术(如降噪、稀疏性约束或收缩惩罚)来防止网络学习*凡的恒等映射,从而迫使它学习有用的特征。
我们还探讨了自编码器潜在空间的性质,以及在该空间中进行插值可以实现有意义的语义过渡。最后,我们通过实践代码演示了如何构建和训练这些模型,并观察了它们在图像重建和去噪任务上的效果。
自编码器是理解数据内在结构、进行特征学习和数据生成的强大工具,为更复杂的生成模型(如变分自编码器VAE和生成对抗网络GAN)奠定了基础。



课程 P14:第8周 - 对比方法与正则化潜变量模型 🧠
在本节课中,我们将学习基于能量的模型,并重点探讨其在自监督或无监督学习中的应用。我们将回顾对比方法,并深入介绍正则化潜变量模型,特别是稀疏编码和变分自编码器的核心思想。

1. 回顾:基于能量的模型
上一节我们介绍了监督学习的基本范式。本节中,我们来看看基于能量的模型如何扩展这一范式。


在基于能量的模型中,我们使用一个隐式函数来捕获变量之间的依赖关系。例如,在给定输入 x 的情况下,可能存在多个合理的输出 y。能量函数 F(x, y) 为每个 (x, y) 对分配一个标量能量值。学习的目标是塑造能量函数,使得数据样本的能量较低,而其他组合的能量较高。
核心公式:
F(x, y) 表示 (x, y) 对的能量。推理过程是寻找使 F(x, y) 最小化的 y。
当没有输入 x 时,我们处理的是无条件模型,能量函数 F(y) 仅捕获 y 各分量之间的相互依赖性。为了处理输出的多模态性,我们引入了潜变量 z。
带有潜变量的能量函数:
F(x, y, z)。推理时,给定 x 和 y,我们寻找使 F(x, y, z) 最小化的 z。
训练基于能量的模型包括参数化能量函数,并收集训练样本 (x, y)(或仅 y),然后塑造能量函数,使其为良好的 (x, y) 组合分配低能量,为其他组合分配高能量。
2. 对比方法 🔄

上一节我们定义了能量模型的目标。本节中,我们来看看如何通过对比方法来实现这一目标。

对比方法的基本思想是压低训练样本的能量,同时推高其他点的能量。这通常通过构建一个“孪生网络”架构来实现。
以下是实现对比方法的关键步骤:
- 构建网络:使用两个共享权重的网络(或两个不同的网络),分别处理输入
x和y。 - 提取特征:两个网络输出特征向量
h和h'。 - 计算相似度:使用点积或余弦相似度等方法计算
h和h'之间的相似度S(h, h')。 - 定义损失:对于正样本对(兼容的
x和y,如图像及其增强版本),最小化S(h, h')(即降低能量)。对于负样本对(不兼容的x和y),最大化S(h, h')(即提高能量)。
一种流行的目标函数是噪声对比估计(NCE)损失,其形式类似于softmax:
NCE损失公式:
L = -log[ exp(S_pos) / (exp(S_pos) + Σ exp(S_neg)) ]
其中 S_pos 是正样本对的相似度,S_neg 是负样本对的相似度。
*期研究(如MoCo、SimCLR)表明,使用这种对比方法预训练的大型模型(如ResNet),即使在少量标注数据上微调,也能在ImageNet等任务上取得接*全监督学习的性能。



3. 架构方法:以去噪自编码器为例 🛠️
对比方法需要大量负样本。本节中,我们来看看另一种思路:通过设计模型架构本身来限制低能量空间的体积,即架构方法。
去噪自编码器是架构方法的一个典型例子。其核心思想是:
- 破坏输入:从一个干净的数据点
y出发,通过添加噪声、掩码部分内容等方式得到损坏版本x。 - 训练重建:训练一个自编码器(编码器-解码器),输入损坏的
x,目标是重建出原始的y。 - 能量函数:能量定义为重建
y_bar与原始y之间的差异,例如*方欧几里得距离C(y, y_bar)。

能量函数:
F(x, y) = C(y, Decoder(Encoder(x)))
通过这种方式,模型被训练为将低能量分配给数据流形上的点(即能被成功重建的点),而流形外的点则具有较高的重建误差(即高能量)。这种方法在自然语言处理(如BERT的掩码语言模型)中取得了巨大成功。
4. 正则化潜变量模型:稀疏编码 📊



无论是对比方法还是简单的去噪自编码器,在高维空间中都需要精心设计以避免*凡解。本节中,我们引入正则化潜变量模型,它通过显式限制潜变量 z 的信息内容来约束模型。


一个关键问题是:如何限制潜变量 z,使其不能编码所有信息,从而迫使模型学习有意义的表示?答案是向能量函数中添加关于 z 的正则化项 R(z)。
正则化能量函数:
E(y, z) = C(y, Decoder(z)) + λ * R(z)
其中 λ 控制正则化强度。
4.1 稀疏编码
稀疏编码是一种经典的正则化潜变量模型。它使用 L1 范数作为正则化项,迫使潜变量 z 的大部分分量为零。
稀疏编码能量函数:
E(y, z) = ||y - Wz||² + λ * ||z||₁
其中 W 是字典矩阵,||z||₁ 是 z 的 L1 范数(各分量绝对值之和)。

训练过程包括两步迭代:
- 推理(稀疏编码):对于每个
y,求解z* = argmin_z E(y, z)。这可以使用迭代收缩阈值算法(ISTA)等优化算法。 - 学习(字典更新):固定
z*,更新字典W以最小化重建误差,通常需要约束W的列范数以防止退化。
稀疏编码可以学习到数据的基本组成成分,例如在图像上学习到类似Gabor滤波器的边缘检测器。



4.2 学习快速推理:LISTA
直接优化求解 z* 计算成本高。LISTA(Learned Iterative Shrinkage and Thresholding Algorithm)的核心思想是训练一个神经网络编码器来*似这个优化过程。



LISTA架构:
编码器被设计成模拟ISTA算法的迭代步骤,通过训练使其在固定迭代次数后输出的 z_bar 接*最优解 z*。损失函数包括重建误差、z 的正则化项以及预测 z_bar 与最优 z* 之间的距离。
这种方法能显著加速推理,并且学到的编码器在相同迭代次数下比原始ISTA算法得到更好的*似解。
5. 正则化潜变量模型:变分自编码器(VAE)🎭
上一节我们通过 L1 正则化引入稀疏性。本节中,我们看看另一种限制潜变量信息的方法:向潜变量添加噪声,即变分自编码器。
VAE的能量函数视角如下:
- 编码器:输入
y,输出分布参数(如均值z_bar和方差)。 - 采样:从该分布(如高斯分布
N(z_bar, I))中采样得到z。 - 解码器:用
z重建y_bar。 - 能量函数:包含三项:
- 重建误差
C(y, y_bar)。 - 正则化项
R(z),通常是z的先验分布(如标准正态)的负对数。 - 编码器预测
z_bar与采样z之间的距离D(z, z_bar),这对应于让z的分布接*编码器预测的分布。
- 重建误差
VAE能量函数(概念式):
E(y, z) ≈ C(y, Decoder(z)) + R(z) + D(z, Encoder(y))
通过向 z_bar 添加噪声(采样),我们限制了编码器能通过 z 传递的信息量。为了防止编码器通过增大 z_bar 的范数来规避噪声(使噪声相对不重要),VAE损失函数中包含一个KL散度项,它充当了一个“弹簧”,将 z 的分布拉向标准正态先验(原点附*)。这迫使所有数据的潜表示聚集在原点周围,形成一个连续的流形,避免了离散的、分离的表示。

*衡重建误差和KL散度项是训练VAE的关键,太强的“弹簧”会导致潜变量崩溃(后验坍塌),太弱则会使模型忽略潜变量。
6. 总结与展望
本节课我们一起学习了基于能量的模型在自监督学习中的两大流派:
- 对比方法:通过显式比较正负样本对来塑造能量表面。它效果强大但需要大量负样本和计算资源。
- 正则化潜变量模型:通过架构设计(如稀疏编码的
L1正则化)或随机性(如VAE的噪声注入)来限制潜变量的信息容量,从而自动将数据流形之外的能量推高。这是解决自监督学习中表示学习退化问题的重要途径。


未来的工作在于更好地结合这两种方法的优点,并设计出更高效、更稳定的正则化机制,以学习到通用、非退化的数据表示。

课程 P15:变分自编码器 (VAE) 实战教程 🧠
在本节课中,我们将学习变分自编码器的核心概念、工作原理,并通过一个实战案例来理解其实现过程。我们将从经典自编码器出发,逐步引入变分的思想,最终理解VAE如何成为一个强大的生成模型。


概述:从自编码器到变分自编码器
上一节我们介绍了经典的自编码器结构。本节中,我们来看看如何将其扩展为变分自编码器。
经典自编码器包含一个编码器和一个解码器。编码器将输入数据(例如图像)压缩成一个低维的“编码”或“潜在表示”,解码器则试图从这个编码中重建出原始输入。其目标是使重建输出尽可能接*原始输入。
然而,经典自编码器的潜在空间通常是离散且不连续的,这限制了其作为生成模型的能力。变分自编码器通过引入概率思想解决了这个问题。
VAE 的核心思想与结构
变分自编码器与经典自编码器的主要区别在于对潜在变量的处理。
在VAE中,编码器不再输出一个确定的编码向量,而是输出一个概率分布的参数。具体来说,对于输入 X,编码器输出两个向量:均值 μ 和对数方差 log σ²。潜在变量 Z 则从这个由 μ 和 σ 参数化的正态分布中采样得到:
Z ~ N(μ, σ²I)
这个过程可以表示为:
Z = μ + σ ⊙ ε,其中 ε ~ N(0, I)
这个技巧被称为“重参数化技巧”,它允许梯度在训练期间通过这个随机采样操作反向传播。
以下是VAE与经典自编码器的结构对比示意图:

经典自编码器结构



变分自编码器结构
VAE 的损失函数
VAE的损失函数由两部分组成:重建损失和KL散度。
1. 重建损失
这部分确保解码器能够从潜在变量 Z 中准确地重建出输入 X。对于像MNIST这样的数据,可以使用二元交叉熵损失。
2. KL散度
这部分强制潜在变量的分布 q(Z|X) 接*标准正态分布 N(0, I)。其公式如下:
KL = -0.5 * Σ (1 + log(σ²) - μ² - σ²)
总损失是这两部分的加权和:
总损失 = 重建损失 + β * KL散度
其中 β 是一个超参数,用于*衡两项的贡献。
KL散度项的作用至关重要:
-log σ²项防止方差σ²过小(崩溃为零)。-μ²项将均值μ推向零。- 综合效果是让每个输入对应的潜在分布像一个以
μ为中心、半径为σ的“气泡”,并且所有气泡都聚集在原点附*,避免重叠。
下图展示了损失函数中各项对潜在空间“气泡”的影响:

损失函数各项的作用
实战:用 PyTorch 实现 VAE
现在,让我们通过代码来具体实现一个VAE。我们将使用MNIST数据集。
以下是实现的关键步骤:
1. 定义编码器和解码器网络
编码器将784维的输入映射到 2 * latent_dim 维的输出(latent_dim 维是均值,latent_dim 维是对数方差)。
解码器将 latent_dim 维的潜在变量映射回784维的重建图像。




2. 重参数化技巧
这是实现的关键。我们根据编码器输出的 μ 和 log σ² 采样得到 Z。
3. 定义损失函数并训练
组合重建损失和KL散度,使用优化器进行训练。
以下是训练过程中损失下降和重建效果改善的示意图:

训练过程中的损失下降

训练不同阶段的重建效果对比(从左到右:原始图像,epoch 0, epoch 1, epoch 2, epoch 3)
VAE 作为生成模型



训练好VAE后,解码器本身就是一个生成模型。我们可以从标准正态分布 N(0, I) 中直接采样一个随机向量 Z,然后输入解码器,它就能生成一张新的、合理的图像。




下图展示了从随机潜在变量生成图像的过程:



从随机潜在变量生成图像




此外,VAE的连续潜在空间允许我们在不同数字之间进行*滑插值。例如,在潜在空间中,从数字“3”对应的点线性插值到数字“8”对应的点,解码器会产生一系列从“3”渐变到“8”的合理图像。



下图展示了在潜在空间中进行插值的效果:



潜在空间插值(从数字5到数字4)




潜在空间插值(从数字3到数字8)





可视化潜在空间
我们可以将测试集中所有图像经过编码器得到的均值 μ 投影到二维*面进行可视化。随着训练的进行,不同类别的数字会在潜在空间中逐渐形成分离的簇。


下图展示了训练不同阶段潜在空间的结构变化:






潜在空间可视化(epoch 0)




潜在空间可视化(epoch 5)



潜在空间可视化(epoch 10)

可以看到,未经训练时,各类别的点混杂在一起。训练后,相同数字的点聚集在一起,不同数字的簇彼此分离,并且整体分布接*原点。
总结
本节课中我们一起学习了变分自编码器。
我们从经典自编码器出发,指出了其潜在空间不连续、不适合生成的局限性。然后,我们引入了变分的思想,让编码器输出分布参数而非确定值,并通过重参数化技巧实现可训练的随机采样。
VAE的损失函数包含重建损失和KL散度两部分,前者保证重建质量,后者规范潜在空间的结构,使其连续、*滑且接*标准正态分布。这使得VAE的解码器成为一个强大的生成模型,能够从随机噪声中生成新数据,并在潜在空间中进行有意义的插值。
通过实战,我们实现了VAE,并在MNIST数据集上验证了其重建、生成和插值能力。最后,我们可视化了潜在空间的结构,直观地理解了VAE如何组织数据表示。

VAE是连接自编码器和生成式模型的重要桥梁,其核心思想对后续如扩散模型等更复杂的生成模型也有深远影响。


课程 P16:第9周 – 讲座:组稀疏性、世界模型与生成对抗网络 🧠
在本节课中,我们将学习基于能量的模型、组稀疏性、世界模型以及生成对抗网络的核心概念。我们将从稀疏编码和自动编码器开始,逐步深入到如何构建和训练能够预测世界状态变化的模型,并探讨生成对抗网络作为一种特殊的对比学习方法。


概述 📋
本节课内容分为三个主要部分。首先,我们将回顾稀疏编码,并介绍一种结合了稀疏自动编码器和分类器训练的架构。接着,我们将探讨组稀疏性和结构性稀疏性,这是一种通过分组正则化来学习具有不变性特征的方法。最后,我们将讨论如何构建世界模型来预测未来状态,并简要介绍生成对抗网络的基本原理。


稀疏自动编码器与分类器 🧩
上一节我们介绍了稀疏编码。本节中,我们来看看如何将其与分类器结合,构建一个多任务学习系统。
这个架构的核心思想是训练一个神经网络同时完成三件事:
- 重建输入数据。
- 使编码(潜在特征)具有稀疏性。
- 根据编码预测数据类别。
以下是该架构的示意图:

具体来说,系统包含一个编码器和一个解码器。编码器将输入 x 转换为稀疏特征向量 z。解码器尝试从 z 重建 x。同时,一个简单的线性分类器被附加在特征向量 z 上,用于预测类别标签。
整个系统的训练目标是同时最小化三个损失项:
- 重建损失:最小化输入
x与重建输出之间的*方误差。公式为:L_recon = ||x - decode(z)||^2 - 稀疏性损失:在特征向量
z上施加 L1 正则化,使其稀疏。公式为:L_sparse = λ * ||z||_1 - 分类损失:最小化分类器的预测误差(如交叉熵损失)。
这种方法的优点在于,通过强制系统寻找既能重建输入又能用于分类的特征,可以避免学习到退化的、无信息量的特征,从而起到正则化的作用。当有标签的训练数据不足时,这是一种有效的训练神经网络的方法。


组稀疏性与结构性稀疏性 🔗
上一节我们介绍了基础的稀疏性。本节中,我们来看看一种更高级的稀疏性形式——组稀疏性,它能够引导系统学习具有结构不变性的特征。
组稀疏性的核心思想不是对单个特征进行独立的稀疏约束,而是将特征分组,并对整个组的激活进行约束。这促使相似的特征被分组激活,从而学习到对某些变换(如旋转、尺度)具有不变性的特征。



组稀疏正则化

假设我们的稀疏特征向量为 z。我们将其分量划分为若干组(组可以重叠或不重叠)。对于第 j 组,我们计算组内特征的 L2 范数:
||z_{G_j}||_2 = sqrt( sum_{i in G_j} z_i^2 )
总的组稀疏正则化项是所有组的 L2 范数之和:
L_group_sparse = sum_j ||z_{G_j}||_2
与 L1 正则化倾向于关闭单个特征不同,组 L2 正则化倾向于关闭整个组。如果某些特征非常相似且倾向于同时被激活,系统就会将它们分到同一组,因为这是最小化激活组数的最佳方式。
应用与可视化
在实践中,我们可以将特征组织在某种拓扑结构中(例如 2D 网格),然后定义滑动窗口作为组。例如,在一个 16x16 的特征网格上,定义大小为 6x6、步长为 3 的滑动窗口作为重叠的组。
通过这种方式训练出的解码器权重(即特征本身)在拓扑空间中会呈现出缓慢变化的模式,例如类似“风车”或“漩涡”的结构。这与哺乳动物初级视觉皮层中观察到的“方位功能图”非常相似,其中对相似方向敏感的神经元在空间上是邻*的。
这种方法可以被视为一种“特征池化”,但它是在特征维度上进行的,而非空间维度。它可以被用来预训练神经网络:先以无监督方式训练一个具有组稀疏性的卷积自动编码器,然后丢弃解码器,将编码器作为特征提取器用于后续的有监督任务。
世界模型与预测控制 🌍
上一节我们讨论了如何从静态数据中学习特征。本节中,我们来看看如何构建一个能够预测动态世界未来状态的模型,即“世界模型”。


一个自主智能体系统通常包含四个部分:
- 感知模块:观察世界,并计算当前状态的表示
s_t。 - 世界模型(前向模型):给定当前状态
s_t、采取的动作a_t以及一个代表未知信息的潜在变量z_t,预测下一个状态s_{t+1}。 - 成本函数:评估状态或轨迹的“好坏”。
- 策略:根据当前状态决定采取什么动作。
模型预测控制
如果我们有一个训练好的、可微分的前向模型和成本函数,我们就可以进行模型预测控制。其过程是:
- 从当前状态
s_t开始。 - 在脑海中(使用前向模型)模拟未来多步,尝试不同的动作序列
[a_t, a_{t+1}, ...]。 - 计算每个模拟轨迹的总成本。
- 通过梯度下降等优化方法,找出能最小化总成本的最优动作序列。
- 执行序列中的第一个动作
a_t。 - 观察到新的状态
s_{t+1}后,重复整个过程。
这属于 System 2 类型的思考,即需要 deliberate planning(深思熟虑的规划)。
训练世界模型
训练前向模型本身就是一个预测问题,其架构与我们之前讨论的基于能量的模型类似:
- 输入:当前状态
s_t(可视为x)和动作a_t。 - 输出:对下一个状态
s_{t+1}(可视为y)的预测。 - 潜在变量:
z_t,用于捕捉影响未来状态但无法从当前状态直接观测到的信息(如其他智能体的意图、随机噪声)。
为了防止模型作弊(例如,忽略 s_t 和 a_t,仅从 z_t 重建 s_{t+1}),我们必须限制潜在变量 z_t 的信息容量。这可以通过之前提到的技巧实现,例如:
- 对
z_t添加噪声(如变分自编码器 VAE 的方法)。 - 使用一个编码器来预测
z_t,并约束其输出分布。 - 在训练时,随机将一半样本的
z_t设为零,迫使模型必须利用s_t和a_t的信息。
策略网络:从 System 2 到 System 1

模型预测控制虽然强大,但计算量较大。我们可以训练一个策略网络来直接学习从状态 s_t 到最优动作 a_t 的映射。这个策略网络可以通过在模拟环境中,将模型预测控制优化得到的动作序列作为目标,通过监督学习进行训练。

一旦策略网络训练完成,智能体就可以像 System 1 一样快速反应,无需每一步都进行耗时的内部模拟。这类似于人类从新手(需要思考每一步)到专家(凭直觉快速反应)的学习过程。



生成对抗网络 ⚔️
最后,我们简要介绍生成对抗网络,它可以被视为对比学习的一种特殊形式。
在基于能量的模型框架中,我们希望数据点处的能量低,非数据点处的能量高。对比方法通过主动生成“坏样本”(对比点)并推高其能量来实现这一点。
GAN 的核心思想是:使用一个神经网络(生成器 G)来智能地生成这些对比样本。
- 判别器 D:即我们的能量函数
E(y)或f(y)。它试图给真实数据y低能量(高分数),给生成数据y_bar高能量(低分数)。 - 生成器 G:它从一个随机噪声
z生成样本y_bar = G(z)。它试图生成能以假乱真、欺骗判别器的样本,即让f(G(z))尽可能低(能量低,分数高)。
训练过程是一个两人博弈(极小极大游戏):
- 训练判别器:最大化其区分真实数据和生成数据的能力。
max_D [ log(D(y)) + log(1 - D(G(z))) ] - 训练生成器:最小化其被判别器识破的概率(或等价地,最大化判别器对生成数据的误判)。
min_G [ log(1 - D(G(z))) ](原始形式,或使用其他损失如-log(D(G(z))))
原始的 GAN 公式存在训练不稳定和模式崩溃等问题。后续的改进(如 WGAN)通过约束判别器函数的*滑性(Lipschitz 连续性)来获得更稳定、更有用的能量函数(判别器),使得生成样本的质量和多样性得到提升。
总结 🎯
本节课中我们一起学习了:
- 多任务稀疏自动编码器:结合重建、稀疏性和分类损失,可以作为一种有效的正则化方法,尤其在标签数据稀缺时。
- 组稀疏性:通过对特征分组进行正则化,可以引导模型学习具有结构性和不变性的特征表示,与生物视觉系统有有趣的对应。
- 世界模型:构建可微分的前向模型来预测环境动态,是实现高级规划和快速反应的基础。我们讨论了模型预测控制、如何训练前向模型,以及如何从中提炼出快速的策略网络。
- 生成对抗网络:作为一种对比学习方法,它通过生成器与判别器的对抗博弈来学习数据分布,其核心可置于基于能量的模型框架下理解。

这些概念为理解自监督学习、无监督特征学习以及构建自主智能体提供了重要的理论基础和工具。



课程 P17:基于能量的生成对抗网络 (GANs) 教程 🧠
在本节课中,我们将要学习生成对抗网络 (GANs) 的核心概念,特别是其基于能量的视角。我们将了解GANs如何通过一个生成器和一个判别器(或成本网络)的对抗过程来学习生成数据,并探讨其工作原理、训练过程以及面临的挑战。
概述 📋
生成对抗网络是一种强大的无监督学习生成模型。它通过学习输入数据的概率分布,能够生成与原始数据相似的新样本。在本教程中,我们将深入探讨GANs的架构、训练动态以及如何避免常见的陷阱。
1. 生成模型与无监督学习
上一节我们介绍了课程目标,本节中我们来看看生成模型的基础。生成模型属于无监督学习领域,其目标是学习数据样本的概率分布。例如,自编码器中的解码器就可以被视为一种生成模型。
生成模型必须能够生成遵循特定分布的数据,就像原始输入数据一样。在没有标签的情况下,我们通过观察数据本身来学习其内在结构。
2. GANs 的基本架构
理解了生成模型的概念后,本节我们来剖析GANs的架构。一个典型的GAN由两个主要部分组成:生成器 (Generator) 和判别器 (Discriminator),在基于能量的视角下,判别器也常被称为成本网络 (Cost Network)。
以下是GAN的基本工作流程:
- 生成器 (G):接收一个从简单分布(如正态分布)中采样的随机噪声向量
z,并将其映射到数据空间,生成一个假样本x_hat = G(z)。 - 判别器/成本网络 (D/C):接收一个样本(可以是真实样本
x或生成样本x_hat),并输出一个标量值。这个值可以理解为样本是“真实”的概率(判别器视角),或是样本的“能量/成本”(能量模型视角)。成本越高,样本看起来越不真实。

与变分自编码器(VAE)从数据点开始编码到潜在空间再解码不同,GAN直接从潜在空间采样,通过生成器生成数据。
3. 对抗训练过程

了解了架构后,本节我们来看看这两个网络是如何通过对抗过程进行训练的。训练目标是一个极小极大博弈(Minimax Game):
- 判别器的目标:正确区分真实数据和生成数据。对于真实数据,输出高置信度(低成本);对于生成数据,输出低置信度(高成本)。
- 生成器的目标:迷惑判别器,使其生成的样本被判别器误认为是真实数据(即,让判别器对生成样本也输出低成本)。

训练步骤通常交替进行:
- 固定生成器,训练判别器:用一批真实数据和一批生成数据训练判别器,使其分辨能力更强。
- 固定判别器,训练生成器:利用判别器提供的梯度信息来更新生成器,使其生成更逼真的数据。

这个过程可以类比为:
- 生成器:伪造货币的造假者(意大利人)。
- 判别器:鉴别货币真伪的专家(德国人)。
- 梯度:间谍反馈的信息,告诉造假者他们的假币在哪里露出了破绽,从而指导他们改进技术。

4. 损失函数与梯度流
上一节我们形象地理解了对抗过程,本节中我们从数学和代码角度看看其具体实现。关键在于损失函数的设计和梯度的反向传播。
对于判别器(成本网络),其损失函数鼓励它对真实样本输出低值,对生成样本输出高值。一个简单的实现是使用均方误差(MSE Loss):
- 目标:真实样本
x对应输出0,生成样本x_hat对应输出m(例如m=10)。 - 损失函数:
L_D = MSE(D(x), 0) + MSE(D(G(z)), m)
对于生成器,其损失直接来自于判别器对生成样本的输出。生成器的目标是最小化这个输出值:
- 损失函数:
L_G = D(G(z)) - 这意味着生成器利用判别器提供的梯度
d(D(G(z)))/dG来更新自身参数,使得D(G(z))变小。
# 伪代码示意训练循环
for epoch in range(num_epochs):
# 1. 训练判别器
real_data = get_real_batch()
noise = sample_noise()
fake_data = generator(noise).detach() # 截断梯度,不更新生成器
d_real = discriminator(real_data)
d_fake = discriminator(fake_data)
# 判别器损失:真样本趋*1,假样本趋*0
d_loss = binary_cross_entropy(d_real, ones) + binary_cross_entropy(d_fake, zeros)
d_loss.backward()
optimizer_D.step()
# 2. 训练生成器
noise = sample_noise()
fake_data = generator(noise)
d_fake = discriminator(fake_data)
# 生成器损失:让判别器对假样本的输出趋*1(即判别器被骗)
g_loss = binary_cross_entropy(d_fake, ones)
g_loss.backward()
optimizer_G.step()
关键点:在训练判别器时,我们detach()生成器的输出,防止梯度传播到生成器。在训练生成器时,我们使用判别器对生成数据的输出作为损失,但将标签设为“真实”(1),从而引导生成器产生能骗过判别器的数据。






5. GANs 的挑战与陷阱
训练GAN并非易事,本节我们探讨几个常见的挑战。
以下是GAN训练中面临的主要问题:
- 判别器饱和:如果判别器训练得太好,对生成样本的输出会很快饱和(例如非常接*0),导致梯度消失(
d(L_G)/dG ≈ 0)。生成器因无法获得有效的梯度而停止学习。 - 模式崩溃:生成器可能发现某个特定样本(或一小类样本)能有效欺骗判别器,于是开始只生成这个样本,而忽略了数据分布的其他模式(多样性)。生成器将所有输入
z都映射到同一个输出x。 - 训练不稳定:生成器和判别器处于动态博弈中。判别器的决策边界在不断变化,生成器的目标也在移动。这可能导致训练振荡,难以收敛到一个*衡点。
- 评估困难:没有完美的客观指标来衡量生成样本的质量和多样性。常用的方法如初始分数依赖于另一个在图像数据上预训练的分类网络。

上图为模式崩溃的示意图,生成的数据点(蓝)坍塌到少数几个点,缺乏多样性。
为了解决梯度消失问题,一个改进是使用基于能量的模型或使用重构误差作为成本。例如,训练一个自编码器仅能很好地重构真实数据。对于远离真实数据流形的生成样本,其重构误差会很大,这个误差就可以作为连续、非饱和的成本信号来训练生成器。
6. 代码实例解析

理论需要实践来巩固,本节我们简要查看一个使用PyTorch实现的DCGAN代码片段,理解其中的关键部分。
# 生成器网络示例 (DCGAN)
class Generator(nn.Module):
def __init__(self, nz, ngf, nc):
super(Generator, self).__init__()
self.main = nn.Sequential(
# 输入是Z,进入卷积
nn.ConvTranspose2d(nz, ngf * 8, 4, 1, 0, bias=False),
nn.BatchNorm2d(ngf * 8),
nn.ReLU(True),
# 上采样层...
nn.ConvTranspose2d(ngf, nc, 4, 2, 1, bias=False),
nn.Tanh() # 输出范围[-1, 1]
)
def forward(self, input):
return self.main(input)
# 判别器网络示例
class Discriminator(nn.Module):
def __init__(self, nc, ndf):
super(Discriminator, self).__init__()
self.main = nn.Sequential(
# 输入是 (nc) x 64 x 64
nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
nn.LeakyReLU(0.2, inplace=True), # 使用LeakyReLU防止梯度消失
# 下采样层...
nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
nn.Sigmoid() # 输出一个概率值
)
def forward(self, input):
return self.main(input)
代码要点:
- 生成器使用转置卷积进行上采样,最终使用
Tanh激活将像素值规范到[-1, 1]。 - 判别器使用普通卷积进行下采样,使用
LeakyReLU而非普通ReLU来避免负值区域的梯度完全为零,这对梯度流动至关重要。 - 训练循环遵循我们之前描述的交替步骤,注意梯度清零、计算和参数更新的顺序。
总结 🎯

本节课中我们一起学习了生成对抗网络的核心原理。我们从生成模型的概念出发,详细介绍了GAN中生成器与判别器的对抗架构及其训练过程。我们探讨了GAN训练中的关键挑战,如梯度消失和模式崩溃,并简要分析了实践代码。

GAN提供了一种无需显式定义似然函数就能学习复杂数据分布的强大框架。理解其基于能量的视角和对抗训练的动态本质,是掌握现代生成模型的关键一步。尽管训练存在挑战,但GAN在图像生成、风格迁移等领域取得了令人瞩目的成果。



课程P18:计算机视觉中的自监督学习 👁️📚
概述
在本节课中,我们将学习计算机视觉领域的自监督学习。自监督学习是一种利用数据本身的结构来生成监督信号,从而学习有效数据表示的方法。我们将探讨其动机、核心方法、评估方式以及最新的研究进展。
1. 为什么需要自监督学习?🤔
上一节我们介绍了课程概述,本节中我们来看看为什么需要自监督学习。

传统的监督学习在计算机视觉中取得了巨大成功,例如在ImageNet上进行图像分类预训练。这种方法学习到的表示对于下游任务(如目标检测)非常有用,即使下游任务的标签数据有限。

然而,这种方法依赖于收集大量带标签的图像。例如,ImageNet数据集包含约1400万张图片和22,000个概念,其标注工作耗费了大约22人年。这凸显了获取大规模精确标注数据的困难和昂贵。
相比之下,互联网上存在海量的无标签图像数据。如果能够利用这些数据本身来学习表示,就可以减少对人工标注的依赖。自监督学习正是为此而生,它旨在从数据中自动生成监督信号。
2. 什么是自监督学习?🔍
上一节我们探讨了自监督学习的动机,本节我们来正式定义它。
自监督学习可以被视为一种无监督学习。其核心思想是:通过预测数据的一部分来自数据的另一部分,从而学习表示。
更具体地说:
- 监督学习:给定输入(如图像),我们有明确的目标(如“这是一只猫”)。
- 无监督学习:只有输入,没有明确的目标。
- 自监督学习:标签来自数据本身的共现部分或属性。我们设计一个“代理任务”,其目标是从数据中自动生成。

一个经典的例子是自然语言处理中的掩码语言模型(如BERT):给定一个不完整的句子(如“猫坐在____上”),模型需要预测被掩码的单词(如“垫子”)。这个预测任务的目标(即被掩码的单词)完全来自数据本身。


3. 视觉中的代理任务 🎯

上一节我们定义了自监督学习,本节我们来看看在计算机视觉中如何设计具体的“代理任务”。


代理任务本身并非我们最终关心的任务(如图像分类),而是我们用来学习良好数据表示的“借口”。以下是几种经典的视觉代理任务:


以下是几种经典的视觉代理任务:
-
相对位置预测:从图像中采样两个补丁,让网络预测第二个补丁相对于第一个补丁的位置(例如,右上、左下等)。这迫使网络理解图像的局部结构和内容。
- 公式/代码描述:任务可形式化为一个8分类问题(8个可能的方向)。
-
图像旋转预测:将图像旋转0°、90°、180°或270°,然后让网络预测所应用的旋转角度。这要求网络理解图像中物体的常规朝向和场景布局。
- 公式/代码描述:任务可形式化为一个4分类问题。
-
图像着色:给定一张灰度图像,让网络预测其颜色。这要求网络识别物体和场景的语义信息(例如,天空是蓝色的,草地是绿色的)。
- 公式/代码描述:最初被构建为分类(预测颜色bin)或回归(预测颜色值)问题。
-
上下文编码:随机掩码图像的一部分,然后根据周围上下文来预测被掩码的内容。这类似于NLP中的掩码语言模型。
4. 视频与多模态中的代理任务 🎬🔊
上一节我们介绍了基于图像的代理任务,本节我们将视野扩展到视频和多模态数据。
视频数据具有天然的时序结构,这为设计代理任务提供了丰富的信息。


- 帧序验证:从视频中采样三帧,判断它们是以正确的时间顺序排列,还是被打乱了顺序。为了完成这个任务,网络需要关注视频中移动的部分(通常是主体),从而学习到与动作、姿态相关的特征。
多模态数据(如视频和对应的音频)也提供了强大的自监督信号。
- 视听对应:判断一段视频剪辑和一段音频剪辑是否来自同一个视频源。为了判断声音是否来自画面,网络需要识别画面中可能发声的物体(如吉他、鼓),从而学习到物体的视觉特征。
5. 从代理任务到对比学习 🔄
上一节我们看到了各种创意十足的代理任务,本节我们来思考如何系统化地设计更好的学习方法。
早期的代理任务(如预测旋转、拼图)存在一个共同点:它们都是协变的。网络需要预测施加在输入上的具体变换(是哪种旋转?是哪种排列?)。这可能导致网络最后一层的表示过于专注于解决这个特定的代理任务,而对下游语义任务有用的不变性信息可能被丢失。
理想的视觉表示应该具有两种属性:
- 将语义相似的样本在特征空间中聚集在一起。
- 对不重要的变换(如颜色抖动、空间裁剪)保持不变。


这引导我们走向对比学习。对比学习的核心思想是:拉*相关样本(“正样本对”)在特征空间中的距离,同时推远不相关样本(“负样本对”)的距离。
关键问题在于如何定义“相关”和“不相关”。在自监督设定下,正样本对通常来自:
- 同一张图像的不同数据增强视图(如不同的裁剪、颜色抖动)。
- 同一视频中时间上接*的帧。
- 匹配的视频和音频片段。
负样本则通常来自不同的图像或视频。
6. 前沿方法示例:聚类与不变性 🚀
上一节我们引入了对比学习的框架,本节我们通过两个具体的前沿工作来深入理解。
方法一:深度聚类
核心思想:迭代地进行聚类和拟合。
- 聚类步骤:用一个网络(可以是任何预训练网络)提取所有图像的特征,然后对这些特征进行K-means聚类,为每张图像分配一个伪聚类标签。
- 拟合步骤:训练一个新的网络,目标仅仅是预测第一步中生成的伪聚类标签。
为什么有效:聚类步骤剥离了原始网络中可能与下游任务无关的细节噪声,只保留了“哪些图像应该聚在一起”的核心分组信息。新网络学习预测这些分组,从而获得更泛化的表示。


方法二:PIRL(代理任务不变表示学习)
核心思想:不再让网络预测具体的变换(如拼图的排列方式),而是让网络学习一个对变换不变的表示。
- 给定一张图像
I及其经过变换(如拼图)的版本I_T。 - 分别提取它们的特征
f(I)和g(I_T)。 - 训练目标不是预测变换
T,而是让f(I)和g(I_T)在特征空间中尽可能相似(同时与其他图像的特征不相似)。
为什么有效:它直接优化了表示的不变性,迫使网络忽略无关的变换,专注于捕捉图像中更本质的、语义上的内容。实验表明,这种方法学习到的表示在各个层(包括最后一层)都保持了良好的语义性,在下游任务上表现优异。

7. 评估与总结 📊
如何评估自监督学习方法?
评估通常分为两种方式:
- 线性评估:冻结预训练好的特征提取器,只在其提取的特征上训练一个简单的线性分类器。这直接测试了特征本身的质量。
- 微调评估:将预训练模型作为初始化,在下游任务上微调所有网络参数。这测试了预训练作为起点的好坏。




重要的是要在多种不同的下游任务(如图像分类、目标检测、表面法线估计、姿态估计)上进行评估,以全面衡量表示的泛化能力。


总结
在本节课中,我们一起学习了:
- 动机:人工标注数据的局限性催生了利用数据自身结构进行学习的自监督方法。
- 核心思想:通过设计“代理任务”,从数据中自动生成监督信号来学习表示。
- 经典方法:包括相对位置预测、旋转预测、着色、上下文编码等图像代理任务,以及利用时序和多模态信息的视频、音频任务。
- 演进方向:从解决具体的、协变的代理任务,发展到通过对比学习框架来学习不变的、聚类友好的表示。深度聚类和PIRL等工作代表了这一方向上的进展。
- 关键认识:一个好的视觉表示应该能够将语义相似的样本聚集起来,并对不重要的视觉变换保持不变。

自监督学习正在减少机器学习对大量标注数据的依赖,为从更丰富、更原始的数据中学习通用表示开辟了道路。


课程 P19:19. 第10周 – 实践:卡车倒车入库 🚛
在本节课中,我们将学习如何设计一个非线性控制器,通过自我学习的方式,控制一辆带有拖车的卡车从任意初始位置倒车进入装卸码头。这是一个经典的、具有挑战性的控制问题,我们将通过训练神经网络来解决它。
概述与目标


我们的目标是:给定卡车和拖车的初始位置(包括坐标和角度),训练一个神经网络控制器,使其能够输出一系列方向盘转向角,引导卡车倒车,直到拖车的尾部与码头*行并紧贴码头。



核心状态变量:
卡车和拖车的状态可以用6个变量描述:
- 卡车角度
θ_cab:卡车相对于x轴的角度。 - 卡车铰接点坐标
(x_cab, y_cab):卡车后部与拖车连接处的坐标。 - 拖车尾部坐标
(x_trailer, y_trailer):拖车尾部的坐标。 - 拖车角度
θ_trailer:拖车相对于x轴的角度。
最终目标状态:
- 拖车尾部坐标
(x_trailer, y_trailer)应尽可能接*码头坐标(x_dock, y_dock)。 - 拖车角度
θ_trailer应为0,即拖车与码头*行(正交)。
系统动力学与挑战
上一节我们介绍了问题的目标,本节中我们来看看系统的运动学模型以及控制它的难点。

卡车-拖车系统的运动是高度非线性的。当我们尝试倒车时,微小的转向角变化可能导致拖车产生复杂、难以预测的摆动。手动设计一个能处理所有可能初始位置的控制律非常困难。
运动学模型(简化表示):
系统的下一个状态 s_{k+1} 取决于当前状态 s_k 和当前转向角 φ_k。这可以用一个函数 F 来描述:
s_{k+1} = F(s_k, φ_k)
其中,s_k = [x_cab, y_cab, θ_cab, x_trailer, y_trailer, θ_trailer], φ_k 是方向盘的转向角。
解决方案架构:双网络训练法


面对非线性控制难题,论文提出了一种分两阶段训练神经网络的解决方案。
第一阶段:训练模拟器网络 🧠

首先,我们需要一个能准确预测系统动力学的模型。即使我们有解析的运动学方程,训练一个“模拟器”网络也更具通用性。这对于那些没有精确数学模型、只有观测数据的复杂系统(如交通流中的车辆)尤其重要。


训练方法:
我们让卡车在随机初始状态和随机转向角下运行,收集大量的 (当前状态, 转向角) -> 下一状态 数据对。然后,我们训练一个神经网络来拟合这个映射关系。



以下是训练模拟器的核心步骤:


- 生成数据:随机初始化卡车状态,并施加随机转向角,根据真实运动学方程计算下一状态,形成数据对
(s_k, φ_k) -> s_{k+1}。 - 构建网络:输入是7维(6维状态 + 1维转向角),经过隐藏层(如45个单元),输出是6维的预测下一状态。
- 定义损失:使用均方误差(MSE)损失函数,最小化预测状态与真实下一状态之间的差异。
Loss = MSE(s_{k+1}^{predicted}, s_{k+1}^{true}) - 训练:通过反向传播更新模拟器网络的权重,直到其能准确预测系统动态。




第二阶段:训练控制器网络 🎮

在拥有一个训练好的、可微分的模拟器网络后,我们就可以训练控制器网络了。控制器的任务是:观察当前状态,输出一个转向角。
训练方法(时间展开与反向传播):
我们将控制器网络和固定权重的模拟器网络连接起来,形成一个“展开”的计算图。
- 前向传播:从一个随机初始状态
s_0开始。- 控制器
C根据s_0输出转向角φ_0。 - 模拟器
T根据(s_0, φ_0)预测下一状态s_1。 - 将
s_1输入控制器C,得到φ_1,再输入模拟器T得到s_2。 - 重复此过程,直到满足终止条件(如撞到边界、达到最大步数或成功倒车入库),得到一条状态轨迹
s_0, s_1, ..., s_K。
- 控制器
- 计算最终损失:损失函数基于轨迹的最终状态
s_K。- 我们希望
(x_trailer_K, y_trailer_K)接*(x_dock, y_dock)。 - 我们希望
θ_trailer_K接* 0。 - 损失函数
L_final可以定义为这些差异的加权和。
- 我们希望
- 反向传播:计算
L_final相对于控制器网络参数θ_C的梯度。由于模拟器网络T是固定的且可微,梯度可以通过整个展开的计算图(从s_K一直回溯到s_0)进行反向传播。这实质上是随时间反向传播(BPTT)的一种形式。 - 更新控制器:使用计算出的梯度更新控制器网络的权重
θ_C,使其输出的转向角序列能引导系统到达期望的最终状态。
关键点:控制器网络的深度是可变的,它取决于每次模拟运行的步数 K。我们是在用一个固定目标(最终状态)来训练一个处理可变长度输入序列的网络。
实践演示与代码要点
让我们回到实践环节,看看代码中如何实现这些概念。





1. 环境设置与交互


首先,我们初始化一个卡车-拖车模拟环境。你可以手动输入转向角(-45度到+45度之间),并观察卡车如何运动,直观感受控制的难度。




# 伪代码示例:初始化并单步模拟
env = TruckTrailerEnv()
state = env.reset() # 获取初始状态 s0
steering_angle = 15 # 手动指定转向角,例如15度
next_state, done = env.step(steering_angle) # 应用动作,得到下一状态
env.render() # 可视化


2. 训练模拟器网络





以下是训练模拟器网络的核心代码结构:
# 伪代码示例:训练模拟器
simulator_net = SimulatorNet(input_dim=7, hidden_dim=45, output_dim=6)
optimizer = torch.optim.Adam(simulator_net.parameters())

for episode in range(num_episodes):
state = env.random_state()
for step in range(steps_per_episode):
steering = random.uniform(-45, 45)
next_state_true = env.true_dynamics(state, steering) # 根据真实模型计算
# 网络预测
input_vec = torch.cat([state, steering])
next_state_pred = simulator_net(input_vec)
# 计算损失并更新
loss = F.mse_loss(next_state_pred, next_state_true)
optimizer.zero_grad()
loss.backward()
optimizer.step()
state = next_state_true
3. 训练控制器网络(概念)
训练控制器需要将控制器和模拟器连接起来:
# 伪代码示例:训练控制器(概念性流程)
controller_net = ControllerNet(input_dim=6, hidden_dim=25, output_dim=1)
simulator_net.eval() # 冻结模拟器权重
optimizer = torch.optim.Adam(controller_net.parameters())
for episode in range(num_controller_episodes):
states = []
state = env.random_state()
done = False
step = 0
# 前向传播:展开轨迹
while not done and step < max_steps:
steering = controller_net(state) # 控制器输出
# 使用训练好的模拟器预测下一状态
input_vec = torch.cat([state, steering])
state = simulator_net(input_vec)
states.append(state)
step += 1
# 检查是否到达终止条件(如接*码头)
done = check_termination(state)
# 计算最终状态的损失
final_state = states[-1]
loss = final_loss(final_state, dock_target) # 计算与目标码头的差距
# 反向传播通过整个轨迹更新控制器
optimizer.zero_grad()
loss.backward() # 梯度会通过模拟器回溯到控制器
optimizer.step()



总结与延伸

本节课中,我们一起学习了一个经典的神经网络控制案例——卡车倒车入库。
核心要点总结:
- 问题:控制带有拖车的卡车倒车入库是一个复杂的非线性控制问题。
- 方法:采用两阶段训练法。
- 第一阶段:训练一个神经网络模拟器来学习系统的动力学模型。即使有解析模型,这种方法也更具通用性。
- 第二阶段:将控制器网络与固定模拟器网络连接并展开,通过随时间反向传播(BPTT),利用最终状态与目标状态的误差来训练控制器网络。这是一个用固定目标训练可变深度网络的巧妙应用。
- 关键:模拟器网络的可微性是整个训练流程能够进行梯度反向传播的基础。

延伸思考与练习:
- 尝试在本课程提供的代码基础上,实现控制器网络的训练部分。
- 思考如何将这种方法应用于其他具有复杂动力学的系统(如机器人、无人机)。
- 对比这种方法与强化学习(如策略梯度)在解决此类问题上的异同。

通过动手实现,你将能更深入地理解如何利用神经网络来学习和控制复杂的动态系统。
深度学习迷你课程 P2:分类、线性代数与可视化 📊
在本节课中,我们将学习分类任务的基本概念,探索线性代数在神经网络中的作用,并通过可视化理解数据在空间中的变换过程。我们将从分类问题入手,逐步理解如何使用线性变换和非线性激活函数来操作数据点,最终实现分类目标。

协议与课程目标 🤝

每次课程开始时,我们之间都会有一个小协议。我在这里是为了与您交流。如果您不明白我在说什么,请随时打断我,我会再次解释。这通常是我的责任,因为我可能没有充分考虑您的背景,或者跳过了一些步骤。如果您能帮助我理解您的困惑,我就能更好地进行教学。如果您对我的问题或笑话没有反应,我就无法知道您是否理解了内容。请尽量保持清醒,因为接下来的内容很重要。

什么是分类? 🐱🐶


上一节我们提到了神经网络的一些应用,其中之一就是分类。现在,我们来简要概述一下什么是分类。
分类是什么?假设我拍摄了一些物体的照片。例如,我用一台百万像素相机拍照,图像将具有RGB三个通道。如果图像是1000x1000像素,那么总共就有100万像素。每个像素有RGB三个值,所以整个图像可以看作是一个具有300万个维度的数据点。
在这个巨大的300万维空间中,每个图像都对应一个点。例如,一张狗的照片对应空间中的一个点,一张猫的照片对应另一个点。在理想情况下,我们希望所有狗的照片点聚集在空间的一个区域,所有猫的照片点聚集在另一个区域。
核心概念:图像可以表示为一个高维向量。例如,一个 1000x1000 的RGB图像可以扁*化为一个长度为 3,000,000 的向量。



# 假设 image 是一个 1000x1000x3 的数组
vector = image.flatten() # 形状变为 (3000000,)


线性代数:移动空间中的点 📐
上一节我们提到,分类的目标是将不同类别的点“移动”到空间的不同区域。那么,我们如何移动这些点呢?这就要用到线性代数。

您可能知道,线性变换包括缩放、旋转、反射、剪切和*移等操作。这些操作可以通过矩阵乘法来实现。
以下是几种基本的线性变换:
- 旋转:改变点的方向,但不改变与原点的距离。
- 缩放:沿特定方向拉伸或压缩点。
- 剪切:使点沿某个方向发生倾斜。
- 反射:将点映射到关于某条线或*面的对称位置。
- *移:将点整体移动一段距离。严格来说,*移不是线性变换(因为它不保持原点不变),而是仿射变换的一部分。
核心概念:一个线性变换可以用一个矩阵 W 表示。对向量 x 进行变换就是计算 Wx。仿射变换则在此基础上加上了*移向量 b,即 Wx + b。
import torch
W = torch.randn(5, 2) # 一个 5x2 的变换矩阵
b = torch.randn(5) # 一个*移向量
x = torch.randn(2) # 一个二维输入点
y = torch.matmul(W, x) + b # 仿射变换结果
通过组合这些变换,我们可以将复杂的点云(比如交织在一起的猫和狗的图像点)拉伸、旋转,使它们变得更容易被区分。



可视化:神经网络如何变换空间 🌀


理论之后,我们来看看实践。神经网络本质上就是学习一系列这样的变换,将输入数据映射到一个新的空间,使得不同类别的数据变得线性可分。
假设我们有一个包含五个螺旋分支的数据集,每个分支代表一个类别。数据点最初在二维空间中交织在一起。
网络的目标是学习一个变换,将这些点映射到一个新空间,使得属于同一螺旋分支的点聚集在一起,而不同分支的点则被分开。

我们构建一个简单的神经网络:
- 一个线性层(仿射变换),将2维输入映射到100维。
- 一个非线性激活函数(如ReLU)。
- 另一个线性层,将100维映射到2维(以便我们可视化)。


核心概念:深度神经网络是多个“线性变换+非线性激活函数”模块的堆叠。公式可以表示为:y = f(W_n ... f(W_2 f(W_1 x + b_1) + b_2) ... + b_n),其中 f 是非线性函数。

在训练开始时,网络是随机初始化的,其变换是随机的,无法很好地区分数据。通过训练(我们将在后续课程学习),网络调整其参数(矩阵 W 和向量 b),使得最终的变换能够实现我们的分类目标。




可视化显示,随着训练进行,网络逐渐将交织的螺旋“拉开”并“铺*”,最终在输出层,不同颜色的点(代表不同类别)变得清晰可分。




实践入门:使用PyTorch进行基本操作 ⚙️
上一节我们看到了神经网络变换空间的能力,本节我们将动手实践,学习如何使用PyTorch进行基本的张量操作。
首先,我们需要设置环境。PyTorch是一个强大的深度学习库。如果您的计算机有GPU,可以利用它来加速计算。


核心概念:使用 torch.cuda.is_available() 检查GPU,并使用 .to(device) 将模型和数据移动到相应的设备(CPU或GPU)上。


import torch
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f‘Using device: {device}’)
让我们创建一个简单的点云并进行变换:

-
创建数据:生成一个包含1000个点的二维数据集,这些点服从标准正态分布。
n_points = 1000 X = torch.randn(n_points, 2) # 设计矩阵,形状为 [1000, 2]

-
定义模型:创建一个简单的“模型”,它实际上只是一个线性变换(矩阵乘法)。
model = torch.nn.Sequential( torch.nn.Linear(2, 2, bias=False) # 一个2x2的线性层,无偏置(即无*移) ).to(device) # 初始化权重矩阵(这里我们手动设置一个例子) model[0].weight.data = torch.tensor([[2.0, 0.0], [0.0, 0.5]]) # 缩放变换


- 应用变换并可视化:将数据点通过模型变换,并绘制结果,观察矩阵乘法对点云形状的影响(例如,从圆形变为椭圆形)。
通过改变权重矩阵,您可以观察到旋转、缩放、剪切等效果。这就是神经网络中单层所做的事情。


结合非线性:构建真正的网络层 🧱




单一的线性变换能力有限,只能实现上述的线性操作。为了获得强大的表达能力,我们需要引入非线性激活函数,并将多个这样的“线性+非线性”层堆叠起来。




以下是一个包含非线性的简单网络示例:

model = torch.nn.Sequential(
torch.nn.Linear(2, 5), # 第一层:2维 -> 5维
torch.nn.ReLU(), # 非线性激活函数:将所有负值置零
torch.nn.Linear(5, 2) # 第二层:5维 -> 2维
).to(device)


核心概念:ReLU(Rectified Linear Unit)函数定义为 f(x) = max(0, x)。它引入了网络所需的非线性,使得网络可以学习复杂的、非线性的决策边界。
当我们使用随机初始化的参数将输入数据通过这个网络时,即使没有经过训练,我们也能看到输出空间的点云形状发生了显著变化,变得更加复杂和有结构,这与单一的线性变换效果截然不同。



总结与展望 🎯



本节课我们一起学习了以下核心内容:
- 分类任务:将高维数据(如图像)映射到类别标签的问题,可以看作是在高维空间中分离不同类别点集的任务。
- 线性代数基础:线性变换(旋转、缩放、剪切、反射)和*移(仿射变换)是移动和扭曲空间中点云的基本工具,通过矩阵 W 和向量 b 参数化。
- 神经网络可视化:神经网络通过习得一系列变换,将复杂交织的数据在最终层变得线性可分。我们通过螺旋数据集的动画直观地理解了这一过程。
- PyTorch入门:我们学习了如何设置PyTorch环境、创建张量、构建简单的线性模型和非线性网络,并观察它们对数据产生的初始变换效果。

目前,我们的网络是随机初始化的,其变换是任意的。在下一堂课中,我们将学习关键的一步:如何通过训练来“操纵”这些变换,即调整网络中的权重矩阵和偏置向量,使它们从任意的初始状态演变成能够完成特定分类目标的最终状态。这就是“学习”或“训练”的过程。



课程 P20:激活函数与损失函数详解 🧠
在本节课中,我们将学习 PyTorch 中常用的激活函数和损失函数。我们将了解它们的基本原理、适用场景以及如何在实际模型中选择和使用它们。通过本课程,你将能够更好地理解神经网络中非线性变换和优化目标的作用。
激活函数回顾 🔄
上一节我们介绍了课程的主要内容,本节中我们来看看激活函数。激活函数为神经网络引入了非线性,使其能够学习复杂模式。PyTorch 中定义了许多激活函数,它们大多源自各种研究论文,作者声称特定函数在其问题上表现更好。


以下是几种常见的激活函数及其变体:
- ReLU (Rectified Linear Unit): 非常标准的激活函数,公式为
f(x) = max(0, x)。 - Leaky ReLU: 允许负输入区域有一个小的正斜率,公式为
f(x) = max(ax, x),其中a是一个小的正常数。 - PReLU (Parametric ReLU): Leaky ReLU 的推广,其中负区域的斜率
a是可学习的参数。 - RReLU (Randomized ReLU): 训练时负区域的斜率是随机采样的。
- ELU (Exponential Linear Unit): 在负区域使用指数函数,公式为
f(x) = x if x > 0 else a*(exp(x)-1)。
PReLU 等函数通过允许负区域传播梯度,解决了 ReLU 在负输入时梯度为零的问题,这对于训练非常“瘦”的网络或某些生成对抗网络(GAN)的生成器尤为重要。
具有尺度参数的激活函数 ⚖️
上一节我们讨论了单拐点函数,本节中我们来看看那些输出受输入信号幅度影响的函数,它们具有内置的尺度参数。
- Softplus: 可视为 ReLU 的可微版本,公式为
f(x) = (1/β) * log(1 + exp(βx))。参数β控制拐点的锐利程度,β越大越接* ReLU。 - Sigmoid / Logistic 函数: *滑的 S 形函数,将输入映射到 (0, 1) 区间,公式为
f(x) = 1 / (1 + exp(-x))。常用于循环神经网络(RNN)的门控机制。 - Tanh (双曲正切): 与 Sigmoid 类似,但输出范围在 (-1, 1) 之间,具有*似零均值,有助于后续权重的更快收敛。
- Swish: 由 Google 提出,公式为
f(x) = x * sigmoid(βx)。它饱和速度比 Tanh 慢,缓解了梯度消失问题。 - Hard Tanh: 基本上是夹在 (-1, 1) 之间的线性斜坡。



对于深度网络,单拐点函数(如 ReLU 族)通常比 Sigmoid 这类双饱和函数表现更好,部分原因在于其尺度等变性(输入乘以一个因子,输出也乘以相同因子,函数形状不变)。而 Sigmoid/Tanh 的饱和特性在深层网络中容易导致梯度消失。批量归一化(BatchNorm)等技术通过固定输入的尺度,部分缓解了这个问题。
其他特殊用途的非线性函数 🛠️
除了作为神经元的激活函数,还有一些非线性函数用于特定任务。
- Soft Shrinkage: 主要用于稀疏编码等任务,将输入值向零收缩一个常数 λ,公式为
f(x) = sign(x) * max(|x| - λ, 0)。可以看作是 L1 正则化目标梯度下降的一步。 - Hard Shrinkage: 将所有绝对值小于 λ 的输入直接置零。
- LogSoftmax: 通常不单独作为激活函数,而是与负对数似然损失结合,用于多分类问题的输出层,数值上更稳定。
- Softmin: 与 Softmax 类似,但将输入视为惩罚(负能量)而非分数。
Softmax 在注意力机制(如 Transformer)中是必需的,因为它能将一组分数归一化为概率分布,迫使模型集中注意力。其中的温度参数 β 控制分布的“硬度”,β 越大分布越尖锐(接* one-hot)。训练初期可使用较小的 β(软分布)以获得*滑梯度,后期增大 β(退火)以获得更确定的决策,这在混合专家系统(MoE)中很有用。


损失函数概览 📉
上一节我们介绍了各种非线性变换,本节中我们来看看如何定义模型的优化目标,即损失函数。PyTorch 提供了丰富的损失函数。
以下是几种基础的回归和分类损失:
- 均方误差 (MSE):
L = (y_pred - y_true)^2的均值。最小化 MSE 会使预测值接*目标值的*均值,在图像预测中可能导致模糊。 - L1 损失:
L = |y_pred - y_true|的均值。最小化 L1 损失会使预测值接*目标值的中位数,对异常值更鲁棒,可能产生更清晰的图像预测。 - Smooth L1 损失/Huber 损失: 在零点附*使用二次函数,远处使用线性函数,*衡了 MSE 和 L1 的优点。
- 负对数似然损失 (NLLLoss): 用于分类,直接最大化正确类别的分数(或最小化其负对数)。
- 交叉熵损失 (CrossEntropyLoss): 通常指 LogSoftmax + NLLLoss 的合并版本。这是多分类最常用的损失,数值稳定。其本质是缩小模型预测分布与真实 one-hot 分布之间的 KL 散度。
- 二元交叉熵损失 (BCELoss): 用于二分类,输入需经过 Sigmoid 处理。
- 带 Logits 的二元交叉熵损失 (BCEWithLogitsLoss): 合并了 Sigmoid 和 BCELoss,数值上更稳定。
在类别不*衡问题中,不建议直接使用损失函数的类别权重参数,更好的做法是过采样稀有类别,使每个批次的类别频率均衡,以确保网络能为稀有类别学习到好的特征。训练后期可再用真实分布对输出层偏置进行微调。


边际损失与结构化损失 🎯


对于排序、度量学习或结构化预测任务,我们需要特殊的损失函数。
- 边际排序损失 (MarginRankingLoss): 要求一对输入中第一个比第二个大至少一个边际值
margin。公式为L = max(0, -y * (x1 - x2) + margin),其中y指示期望的排序方向。 - 三元组损失 (TripletMarginLoss): 用于度量学习。给定锚点样本
a、正样本p和负样本n,损失函数为L = max(0, d(a, p) - d(a, n) + margin)。它拉*a和p的距离,推远a和n的距离。 - 余弦嵌入损失 (CosineEmbeddingLoss): 用于训练 Siamese 网络,基于余弦相似度。它鼓励相似样本的余弦相似度接* 1,不相似样本的余弦相似度小于某个边际
margin。 - 连接主义时间分类损失 (CTCLoss): 用于序列标注任务(如语音识别),其中输入和输出序列长度可变且未对齐。它通过动态规划求最佳对齐路径,并计算损失,是可微的。
这些边际损失是构建能量基础模型(Energy-Based Models)的基础。一个好的损失函数应确保正确答案的能量低于错误答案的能量,且至少有一个边际差。例如,合页损失(Hinge Loss)及其变体就符合这一原则。


能量基础模型与损失函数设计 ⚡
更一般地,我们可以在能量基础模型(EBM)的框架下理解损失函数。在 EBM 中,模型输出一个能量函数 E(W, Y, X),低能量对应好答案。
损失函数 L 是能量函数 E 和训练集 S 的泛函。通过最小化 L 来学习参数 W。设计 L 的关键是:它必须使正确样本的能量降低,同时使(至少某些)错误样本的能量升高。
- 能量损失 (Energy Loss): 仅最小化正确答案的能量
E(W, Y_i, X_i)。这可能导致能量面整体塌缩(所有输出能量都低),除非模型结构有特殊约束。 - 负对数似然损失 (NLL): 在 EBM 框架下表现为
L = E(W, Y_i, X_i) + (1/β) log(∑_y exp(-βE(W, y, X_i)))。第一项压低正确答案能量,第二项(对数配分函数)抬高所有答案的能量。 - 感知器损失 (Perceptron Loss):
L = E(W, Y_i, X_i) - min_y E(W, y, X_i)。仅保证正确答案能量不高于最小能量,但允许所有能量相等,不适合非线性模型。 - 合页损失 (Hinge Loss):
L = max(0, E(W, Y_i, X_i) - min_{y≠Y_i} E(W, y, X_i) + m)。确保正确答案能量比最冒犯的错误答案能量至少低一个边际m,是良好的损失函数。 - *方*方损失 (Square-Square Loss): 类似合页损失,但使用*方项。
对于连续或高维的 Y,如何选择“最冒犯的错误答案” Ȳ 是一个挑战,这引出了对比学习(Contrastive Learning)中的各种负采样策略。
总结 🎓
本节课中我们一起学习了 PyTorch 中的核心组件:激活函数和损失函数。
- 激活函数为网络引入非线性。我们学习了 ReLU 族、Sigmoid/Tanh 族以及 Swish 等函数,理解了它们的特性(如单调性、尺度敏感性)和适用场景(如深度网络偏好单拐点函数,注意力机制需要 Softmax)。
- 损失函数定义了模型的优化目标。我们回顾了 MSE、L1、交叉熵等标准损失,并深入了解了边际损失(如三元组损失)和结构化损失(如 CTCLoss)在排序、度量学习和序列预测中的应用。
- 最后,我们从能量基础模型的视角统一审视了损失函数的设计哲学,即一个好的损失应同时压低正确答案的能量并抬高错误答案的能量。

掌握这些函数的工作原理和适用条件,将帮助你更有效地构建、调试和优化神经网络模型。




课程 P21:不确定性下的预测与策略学习 (PPUU) 🚗🤖
在本节课中,我们将学习一个名为“不确定性正则化的模型预测策略学习”的完整系统,该系统用于在密集交通中实现自动驾驶。我们将从构建世界模型开始,然后利用该模型来训练一个安全的驾驶策略,最后探讨如何评估策略的性能。
概述 📋

本节课的核心目标是理解并构建一个能够在不确定的、多智能体环境中(如高速公路)安全驾驶的智能体。我们将分三步走:
- 学习并模仿真实世界:构建一个能够预测其他车辆未来行为的模型。
- 在模型中训练策略:利用学到的世界模型,通过“思考”而非“实际尝试”来训练驾驶策略。
- 评估策略:将训练好的策略放回真实或模拟环境中进行测试。
我们将使用一个从真实高速公路数据中提取的大规模数据集,其中包含车辆的位置、速度和周围环境的图像表示。

第一部分:学习世界模型——预测给定历史和行动的未来 🌍➡️🔮
上一节我们概述了整体框架,本节中我们来看看如何具体构建世界模型。世界模型的目标是:给定当前状态(自身位置、速度、周围环境图像)和要执行的动作,预测出下一时刻的状态。

状态与动作定义
- 状态
s_t: 在时间步t,状态由三部分组成:- 位置向量
p_t:车辆自身的 (x, y) 坐标。 - 速度向量
v_t:车辆自身的 (vx, vy) 速度分量。 - 上下文图像
I_t:一个合成的RGB图像,用于表示周围环境。- 蓝色通道:代表智能体自身(位于图像中心)。
- 红色通道:代表车道线。
- 绿色通道:代表周围的其他车辆。
- 位置向量
- 动作
a_t: 车辆的控制信号,包括纵向加速度(加速/刹车)和横向加速度(转向)。这些动作可以从观测到的专家驾驶数据中,通过逆向运动学模型计算得出。
基础预测模型及其问题

一个直观的想法是训练一个确定性神经网络作为预测器。该网络以历史状态序列 s_1:t 和当前动作 a_t 为输入,直接输出对下一状态 s_{t+1} 的预测,并通过最小化均方误差 (MSE) 进行训练。


公式表示:
预测状态 = 预测器网络(历史状态序列, 当前动作)
损失函数 = MSE(预测状态, 真实下一状态)

然而,这种方法存在一个根本性问题:未来是多模态的。从同一个历史状态出发,由于其他车辆行为的不确定性,可能存在多种合理的未来。MSE损失会迫使网络输出所有可能未来的“*均”,导致预测结果模糊且不准确(例如,预测出的车辆位置是多个可能位置的模糊重叠)。


解决方案:引入潜变量


为了解决多模态问题,我们引入一个低维的潜变量 z_t。这个潜变量旨在捕捉那些无法从过去历史中推断出来的、关于未来的不确定性信息。
改进后的模型结构如下:
- 预测器: 接收历史状态和动作,输出一个关于未来的“基础”隐藏表示
f_pred。 - 潜变量扩展器: 将采样的潜变量
z_t扩展成一个与f_pred维度相同的表示f_exp。 - 求和与解码: 将
f_pred和f_exp相加,然后通过一个解码器网络,得到清晰的下一状态预测s_{t+1}。
核心思想:f_pred 负责预测可确定的部分(如自身动力学),而 f_exp 负责根据 z_t 来修正预测,以匹配某一种特定的未来可能。
训练与“作弊”问题
在训练时,为了得到能产生准确预测的 z_t,我们引入一个编码器。编码器接收真实的未来状态 s_{t+1},并输出潜变量 z_t 的分布(均值和方差)。这样,我们可以通过采样 z_t 来使预测完全匹配真实未来。
但这带来了“作弊”问题:网络可能学会将本应由动作 a_t 解释的信息(例如,自身转向导致视野旋转)编码到潜变量 z_t 中,从而降低了对动作输入的依赖。这在实际使用时(无法获知真实未来)是致命的。

正则化与解决方案









为了解决“作弊”问题,我们采用两种正则化技术:
- 变分约束: 我们强制编码器输出的潜变量分布
q(z_t | s_{t+1})接*一个简单的先验分布p(z_t)(如标准正态分布)。通过引入KL散度损失项来实现。
公式:损失 = MSE(预测状态, 真实状态) + β * KL( q(z_t | s_{t+1}) || p(z_t) ) - 潜变量丢弃: 在训练过程中,我们随机地用从先验分布
p(z_t)中采样的随机噪声,替换掉由编码器产生的z_t。这迫使预测器的主干网络必须学会依赖动作输入a_t来做出合理的预测,因为潜变量提供的“作弊”信息并不总是可靠。





在测试/使用阶段,我们不再有真实未来,因此直接从先验分布 p(z_t) 中采样潜变量,输入到训练好的模型中进行多步迭代预测,从而生成多样化的、合理的未来场景。




第二部分:利用世界模型训练驾驶策略 🧠🎮


上一节我们成功构建了一个能生成多样化未来预测的世界模型,本节中我们来看看如何利用这个“模拟器”来训练驾驶策略,而无需在真实世界中冒险。




策略训练框架


我们的策略(一个神经网络)以当前状态 s_t 为输入,输出控制动作 a_t。训练过程在“想象”中展开:
- 从初始状态
s_0开始。 - 策略网络根据当前状态
s_t输出动作a_t。 - 将状态
s_t和动作a_t,连同从先验分布采样的潜变量z_t,一起输入世界模型。 - 世界模型预测出下一个状态
s_{t+1}。 - 计算在状态
s_{t+1}下的任务成本。 - 将
s_{t+1}作为新的当前状态,重复步骤2-5,展开一个固定时间长度的轨迹。 - 计算整条轨迹上的累计成本,并通过时间反向传播 (Backpropagation Through Time, BPTT) 来更新策略网络的参数,目标是最小化累计成本。








成本函数设计


成本函数引导策略学习安全驾驶。主要包括两部分:
- 车道成本: 鼓励车辆保持在车道中心。用一个在车道中心最低、越偏离越高的势能函数表示。
- 接*度成本: 惩罚与其他车辆过于接*。同样用一个势能函数表示,其纵向范围随自身速度增加而扩大,以便在高速时看得更远。
最终成本是这两个势能函数与周围车辆占据栅格(绿色通道)逐元素相乘后的最大值,这是一个可微的操作。


朴素方法的问题与改进


如果只最小化上述任务成本,策略很容易找到“捷径”——例如,驶出道路或预测所有车辆消失(生成全黑图像),从而使成本归零。这显然不是我们想要的,因为策略的行为已经脱离了真实数据分布(流形)。





以下是两种改进方法:
- 专家正则化: 在损失函数中加入一项,鼓励策略输出的动作与数据集中观察到的专家动作相似。这是一种基于模型的模仿学习。
- 模型不确定性正则化(核心贡献): 这是更有效的方法。其思想是:当策略尝试的动作远离训练数据分布时,世界模型对其结果的预测会变得不确定(即,不同随机初始化或使用Dropout的模型副本会给出差异很大的预测)。我们可以利用这种预测的方差作为额外的正则项。
最终损失公式:总损失 = 任务成本 + λ * 预测方差
策略在训练时,会同时被优化以降低任务成本和降低其动作所导致的世界模型预测不确定性。这意味着策略被鼓励采取那些让未来更容易预测(即更安全、更常见)的动作。


第三部分:策略评估与总结 🏁📊


上一节我们介绍了训练策略的两种方法,本节我们来看看最终的性能评估以及对本课程的总结。

策略评估
将训练好的策略在保留的测试集数据中进行评估。我们让策略控制的车辆(蓝色)替换掉原始数据中的某辆车(黄色),并在模拟环境中运行。评估的关键是看蓝色车辆能否在复杂的、动态的交通环境中生存下来,并完成驾驶任务,即使它的轨迹可能与原始专家轨迹完全不同。
实验表明,结合了模型不确定性正则化的方法能够成功地学习到安全的驾驶策略,在密集车流中实现超车、跟车、避让等行为,且事故率显著降低。
课程总结
在本节课中,我们一起学习并构建了一个完整的“不确定性下的预测与策略学习”系统:
- 世界建模: 我们使用变分潜变量模型来学习一个能生成多模态未来预测的世界模型,并通过KL散度正则化和潜变量丢弃技术解决了信息泄露问题。
- 策略学习: 我们在学习到的世界模型内部,通过最小化任务成本和模型预测不确定性来训练驾驶策略,实现了安全、高效的“思想实验”式训练。
- 核心创新: 将模型预测的不确定性本身作为一个可微的正则项,引导策略探索安全且可预测的行为空间。
这个方法避免了在真实世界中试错的高风险,充分利用了大规模观测数据,并通过学习模型内部的不确定性来智能地正则化策略行为,为在复杂、不确定环境中实现可靠的自主系统提供了一个强有力的框架。
相关资源:
- 论文标题: Model Predictive Policy Learning with Uncertainty Regularization for Driving in Dense Traffic
- 代码库: 可在GitHub上获取
- 项目网站: 包含更多演示和细节

希望这节课能帮助你理解如何将深度学习模型用于复杂的决策与控制问题。


课程 P22:深度学习在自然语言处理中的应用 🧠
在本节课中,我们将学习深度学习在自然语言处理领域的关键技术和模型。我们将从语言建模的基础概念开始,逐步深入到Transformer架构、序列到序列模型以及自监督学习等核心内容。课程旨在为初学者提供一个清晰、全面的概览。


语言建模 📖
上一节我们介绍了课程的整体框架,本节中我们来看看什么是语言建模。语言建模本质上是一个密度估计问题,它为所有可能的字符串分配概率,目标是让流利的英语句子获得更高的概率。
由于可能的句子数量呈指数级增长,我们无法直接预测所有分类。最广泛使用的方法是利用链式法则将联合分布分解。具体来说,我们预测第一个单词,然后在给定第一个单词的条件下预测第二个单词,依此类推。
公式表示:
P(句子) = P(单词1) * P(单词2 | 单词1) * P(单词3 | 单词1, 单词2) * ...
这样,我们将密度估计问题转化为一系列分类问题:给定一段文本,预测下一个单词是什么。
以下是构建语言模型的基本步骤:
- 将文本输入神经网络。
- 神经网络将上下文映射为一个向量表示。
- 该向量与一个大型词向量矩阵(包含模型已知的所有单词)进行点积运算,计算相似度。
- 通过Softmax函数得到下一个单词的概率分布。
- 模型通过最大似然估计进行训练。


注意:在实际操作中,我们通常不直接处理整个单词,而是处理子词(subword)甚至字符,但建模技术保持不变。

上下文编码器:从CNN、RNN到Transformer 🔄
上一节我们介绍了语言建模的基本框架,本节中我们来看看如何构建上下文编码器。早期,人们主要使用卷积神经网络和循环神经网络。
卷积神经网络
CNN编码了语言的归纳偏置,即无论短语出现在什么位置,都应以相同的方式解释。典型的CNN语言模型首先将每个单词映射为一个向量(通过嵌入矩阵查找),然后应用多层一维卷积和非线性变换,最终得到一个代表上下文并用于预测下一个单词的向量。
优点: 模型非常快,适合处理大量训练数据。
缺点: 感受野有限,只能基于固定窗口内的前文进行预测,难以建模长距离依赖。
循环神经网络
RNN,特别是LSTM,是几年前最流行的方法。其核心思想是在每个时间步保持一个状态,该状态结合了之前的历史信息和当前输入的单词,并不断迭代更新。
优点: 至少在原理上,可以对无限长的上下文进行建模。
缺点:
- 信息瓶颈: 需要将整个文档历史压缩到单个向量中。
- 梯度消失: 过去单词的影响会随时间步呈指数级衰减,难以学习长期依赖。
- 训练缓慢: 由于顺序计算的性质,无法充分利用GPU的并行能力。

Transformer:当前的解决方案
Transformer是当前最先进的NLP模型。它于2017年通过论文《Attention Is All You Need》引入,彻底改变了该领域。
一个Transformer块包含两个主要子层:
- 多头自注意力机制: 这是Transformer的核心,允许模型直接关注输入序列中的任何位置。
- 前馈神经网络: 一个具有表达力的多层感知机。


此外,还有残差连接和层归一化,这些技术对于稳定训练深度模型至关重要。
为什么使用多头注意力而不是单头?
预测下一个单词需要同时关注上下文中的多种不同信息。多头注意力允许模型并行地关注来自不同表示子空间的信息。例如,一个头可能关注句法信息(如之前的形容词),另一个头可能关注语义信息(如主题)。
为什么需要掩码?
在训练语言模型时,我们使用自回归分解,即单词只能基于之前的单词进行预测。为了防止模型在训练时“偷看”未来的单词,我们使用上三角掩码矩阵(将未来位置的注意力分数设为负无穷大),确保每个位置只关注它之前的输出。
位置编码:
自注意力机制本身对输入顺序不敏感。为了注入序列的顺序信息,我们在输入词向量中添加了位置编码向量。这可以是学习得到的嵌入,也可以是使用正弦和余弦函数生成的固定编码。


Transformer的优势:
- 强大的建模能力: 每个单词可以直接访问所有先前单词的表示,避免了RNN的信息压缩瓶颈和梯度消失问题。
- 高度并行化: 自注意力操作可以同时计算所有时间步,充分利用现代硬件。
- 优秀的扩展性: 模型性能随着参数规模和数据量的增加而持续提升。
局限性: 自注意力的计算复杂度是序列长度的二次方,这限制了其直接处理超长文档(如整本书)的能力。目前有大量研究致力于开发高效的Transformer变体来解决这个问题。

解码与生成:从贪婪搜索到Top-k采样 🎲

上一节我们了解了如何训练一个强大的语言模型,本节中我们来看看如何从训练好的模型中生成文本。我们不仅想要模型给出概率,还想让它创造出像样的句子。

简单地找到全局概率最高的序列是计算不可行的。常见的解码策略包括:
贪婪解码
每一步都选择当前概率最高的单词。这种方法不能保证得到全局最优序列,且无法回溯。
集束搜索
集束搜索尝试跟踪一个大小为k的最佳假设列表。在每一步,它为列表中的每个假设扩展所有可能的下一单词,然后只保留总得分最高的k个新假设。
算法步骤:
- 初始化集束,包含一个空的起始假设。
- 对于每个生成步骤:
- 为当前集束中的每个假设计算下一个单词的概率分布。
- 从所有扩展假设中选出总对数概率最高的k个。
- 用这k个新假设更新集束。
- 当假设生成结束标记(如
<EOS>)时,将其移出集束并放入“已完成”列表。 - 重复步骤2-3,直到生成了足够数量的完整假设或达到最大长度。
- 从“已完成”列表中选择得分最高的假设作为输出。
存在的问题:
- 暴露偏差: 模型在训练时只见过真实的前缀,而在集束搜索时可能会遇到自己生成的错误前缀,导致性能下降。
- 退化与重复: 集束搜索有时会陷入重复循环或生成通用、乏味的文本。
采样与Top-k采样
另一种思路是从模型的概率分布中随机采样下一个单词,这能增加多样性。但纯随机采样容易产生不连贯的文本。
Top-k采样是一种改进方法:在每一步,只从概率最高的k个候选词中采样。这既保持了随机性带来的多样性,又避免了选择长尾分布中那些奇怪的低概率词。

许多令人印象深刻的文本生成示例(如GPT-2的新闻文章)都使用了类似Top-k采样的技术。这是一种经验上有效但理论不尽完美的解码方法。

序列到序列模型与条件生成 🌉

上一节我们讨论了无条件语言模型的解码,本节中我们来看看更有用的条件语言模型,即序列到序列模型。这类模型接收一个输入序列(如法语句子),并生成一个输出序列(如英语翻译)。

早期的Seq2Seq模型使用编码器-解码器架构,编码器将输入压缩为一个“思想向量”,解码器再用它来初始化并生成输出。这种架构存在信息瓶颈。
现代的Seq2Seq模型使用Transformer编码器-解码器架构:
- 编码器堆栈: 处理输入序列,其自注意力层可以查看输入中的所有单词(双向)。
- 解码器堆栈: 生成输出序列。除了自注意力层(需使用掩码确保自回归属性),还增加了一个交叉注意力层,用于关注编码器的最终输出。这使得输出中的每个单词都能直接与输入中的所有单词交互。
利用单语数据:反向翻译
对于像机器翻译这样的任务,高质量的*行语料(双语对照文本)是稀缺的。反向翻译是一种利用大量单语数据的技术。

步骤:
- 训练一个反向翻译模型(如英语->德语)。
- 用这个模型将大量的单语英语文本翻译成德语,得到“合成”的*行语料(可能包含噪音的德语句子 + 干净的英语句子)。
- 用这些合成数据,连同原有的真实*行语料,一起训练正向翻译模型(德语->英语)。

这种方法可以显著提升翻译质量,尤其是在低资源语言对上。
自监督学习:从Word2Vec到BERT 🎯
上一节我们介绍了需要*行数据的条件生成任务,本节中我们来看看如何利用海量的无标签文本进行自监督学习。这是*年来NLP取得突破的关键。
自监督学习的核心思想是:从数据本身构造监督信号。在NLP中,这意味着设计一个预训练任务,让模型通过完成这个任务来学习语言的通用知识。
Word2Vec:上下文无关的词嵌入
Word2Vec通过“完形填空”任务学习词向量:给定一个句子和其中一个被掩码的词,模型根据周围上下文预测该词。这使语义相*的词在向量空间中位置接*。
局限性: 每个词只有一个静态向量,无法根据上下文区分多义词。
ELMo:基于双向LSTM的上下文词嵌入
ELMo分别训练一个前向和一个后向的语言模型,然后将两个方向的表示拼接起来作为词的最终表示。这样,词的表示就依赖于其上下文了。


局限性: 表示是左右语境表示的浅层拼接,缺乏深层次交互。
BERT:基于Transformer的双向编码器
BERT采用了掩码语言模型作为预训练任务:随机掩码输入句子中15%的单词,然后让模型预测这些被掩码的词。关键的是,BERT使用Transformer编码器,使得每个被掩码的词在预测时都能看到句子中所有其他单词(双向上下文),实现了深层次的交互。
微调: 预训练后,只需在BERT的顶部添加一个简单的输出层(如用于分类的线性层),就可以在下游任务(如情感分析、问答)上进行微调。这消除了为每个任务设计特定架构的需要。
核心优势: BERT及其后续模型(如RoBERTa, T5)证明了,使用一个简单的预训练目标(如掩码预测),配合巨大的模型容量和海量数据,可以让模型学到极其丰富的语言知识,并在众多NLP基准测试上达到接*人类的水*。

总结与展望 🚀
在本节课中,我们一起学习了深度学习在NLP中的核心进展。
我们从语言建模这一基础任务出发,探讨了如何构建上下文表示,并回顾了从CNN、RNN到Transformer的演进历程。Transformer凭借其强大的多头自注意力机制和并行计算能力,已成为当前的主流架构。
接着,我们学习了如何从训练好的模型中进行解码与生成,比较了贪婪搜索、集束搜索和Top-k采样等策略。然后,我们将模型扩展到了序列到序列的条件生成任务,并介绍了利用单语数据的反向翻译技术。
最后,我们深入探讨了推动这一切进步的引擎:自监督学习。从Word2Vec到BERT,我们看到了如何通过设计巧妙的预训练任务,让模型从海量无标签文本中学习通用语言表示,再通过微调轻松适应各种下游任务。
尽管取得了巨大成功,NLP领域仍面临许多开放性问题,例如:如何将外部知识或常识有效地整合到模型中?如何建模超长文档?如何让一个模型解决多种任务而无需针对每个任务单独微调?如何实现真正的少样本或零样本学习?
未来的研究将继续探索这些方向,而Transformer架构和自监督学习范式无疑将是这一探索旅程中的重要基石。




🧠 P23:注意力机制与Transformer架构详解
在本节课中,我们将学习深度学习中的核心概念——注意力机制,特别是其在革命性的Transformer架构中的应用。我们将从基础的注意力概念讲起,逐步深入到自注意力、多头注意力,并最终理解完整的编码器-解码器Transformer结构。
🔍 第一部分:注意力机制基础
上一节我们介绍了课程概述,本节中我们来看看注意力机制的基本思想。
注意力机制的核心思想是:对于一个给定的输入集合,我们想要计算一个“隐藏表示”,这个表示是该集合中所有元素的线性组合。
假设我们有一组输入向量 X = {x1, x2, ..., xT},其中每个 xi 属于 R^n。我们希望得到的隐藏表示 h 是这些向量的加权和:


公式:h = X * a
其中:
X是一个n x T的矩阵,它的列就是我们的输入向量x1, x2, ..., xT。a是一个T维的权重向量,它决定了每个输入向量对最终输出的贡献程度。
那么,权重向量 a 是如何确定的呢?这引出了两种主要的注意力类型。
以下是两种主要的注意力类型:
- 硬注意力:约束权重向量
a是一个独热编码向量(即只有一个元素为1,其余为0)。这意味着h将完全等于集合中的某一个特定向量xi。 - 软注意力:约束权重向量
a的所有元素求和为1(通常通过Softmax函数实现)。这意味着h是所有输入向量的一个“软性”混合。
在深度学习中,我们主要使用软注意力,因为它处处可微,便于通过梯度下降进行训练。
🗝️ 第二部分:查询、键与值(QKV)模型
上一节我们介绍了基础的注意力形式,本节中我们来看看如何通过引入可学习的参数,将注意力机制泛化为一个更强大的“键值对查询”系统。


我们可以将注意力机制类比于一个键值对存储系统(例如字典或搜索引擎):
- 查询:你提出的问题(例如:“如何做千层面?”)。
- 键:存储系统中所有条目的标题或标识(例如:所有食谱的标题)。
- 值:存储系统中条目的实际内容(例如:每个食谱的具体做法)。
在注意力机制中,我们通过可学习的权重矩阵,从原始输入 x 派生出查询、键和值:

公式:
q = W_q * x(查询)k = W_k * x(键)v = W_v * x(值)

这里,W_q, W_k, W_v 是需要通过训练学习的参数矩阵。这使得模型可以学习如何为不同的任务生成更有意义的表示。


注意力权重的计算过程如下:
- 计算查询
q与所有键k_i的相似度(通常使用点积):score_i = q · k_i。 - 将所有相似度分数通过Softmax函数归一化,得到权重向量
a:a = softmax(scores)。 - 最终的输出是值
v_i的加权和:output = sum(a_i * v_i)。




一个重要的实现细节:在计算点积 q · k 后,我们通常会除以键向量维度 d_k 的*方根(sqrt(d_k))。这是因为当维度很高时,点积的结果可能变得非常大,导致Softmax函数的梯度非常小。进行缩放可以稳定训练。












🤖 第三部分:自注意力、多头注意力与Transformer




上一节我们建立了QKV模型,本节中我们来看看如何将其应用于序列,并构建出强大的Transformer模型。





自注意力
当查询、键、值都来自同一组输入时,这种注意力机制称为自注意力。它允许序列中的每个元素与序列中的所有其他元素进行交互,从而捕捉序列内部的依赖关系,无论它们之间的距离有多远。


多头注意力
为了让模型能够同时关注来自不同表示子空间的信息,我们使用多头注意力。其做法是:
- 使用多组不同的
W_q,W_k,W_v矩阵(即多个“头”),并行地计算多次注意力。 - 将每个头产生的输出拼接起来。
- 通过一个最终的线性投影层
W_o将拼接后的结果映射回期望的维度。




公式:MultiHead(Q, K, V) = Concat(head_1, ..., head_h) * W_o
其中,head_i = Attention(Q * W_q^i, K * W_k^i, V * W_v^i)



Transformer 架构
Transformer是一个完全基于注意力机制的序列到序列模型架构,主要由编码器和解码器堆叠而成。
以下是Transformer编码器层的主要组件:
- 多头自注意力层:输入序列通过自注意力机制进行交互。
- 残差连接与层归一化:将注意力层的输出与原始输入相加(残差连接),然后进行层归一化。这有助于梯度流动和训练稳定性。
- 前馈网络:对归一化后的每个位置独立应用一个全连接前馈网络(本质上是两个线性变换夹着一个ReLU激活函数)。
- 再次的残差连接与层归一化。
编码器由多个这样的层堆叠而成。
Transformer解码器层在结构上与编码器类似,但包含两个注意力子层:
- 带掩码的多头自注意力层:确保在生成当前位置的输出时,只能“看到”之前已生成的输出,而不能看到未来的信息(通过掩码实现)。
- 多头交叉注意力层:其查询来自解码器上一层的输出,而键和值来自编码器的最终输出。这允许解码器在生成每个词时,有选择地关注输入序列的不同部分。
- 前馈网络,同样伴随着残差连接和层归一化。
编码器和解码器都可以堆叠多层,以构建更强大的模型。
📝 总结与要点






本节课中我们一起学习了注意力机制和Transformer的核心原理。







核心要点总结:
- 注意力本质:通过计算输入元素的加权和来生成输出,权重由查询与键的相似度决定。
- QKV模型:将输入映射为查询、键、值,使注意力机制可学习且更灵活。
- 自注意力:让序列内部元素相互关注,有效捕捉长程依赖。
- 多头注意力:并行运行多个注意力头,捕捉不同方面的信息。
- Transformer:基于纯注意力构建的编码器-解码器架构,摒弃了循环,实现了高度并行化,成为现代NLP的基石。
- 关键技巧:残差连接、层归一化和缩放点积是稳定训练Transformer的关键。

Transformer的强大之处在于其完全并行化的计算方式(避免了RNN的顺序性)以及对序列中任意距离依赖关系的直接建模能力。虽然其注意力矩阵的大小会随序列长度*方增长(O(T^2)),这限制了处理超长序列的能力,但它仍然是当前深度学习领域最具影响力的架构之一。


📚 课程P24:图卷积网络(GCNs)教程
在本节课中,我们将学习图卷积网络(GCNs)的核心概念。我们将从传统的卷积架构开始,逐步扩展到图结构数据,并介绍两种主要的图卷积方法:谱图卷积和空间图卷积。课程内容力求简单直白,适合初学者理解。
🏁 概述:从传统卷积到图卷积
卷积神经网络(CNNs)是计算机视觉、语音处理和自然语言处理等领域的突破性技术。它们通过利用数据的局部性、*稳性和层次性等假设,有效地解决了高维学习问题。然而,这些传统卷积操作依赖于数据的规则网格结构(如图像的像素网格)。当面对社交网络、大脑连接图或分子结构等非欧几里得数据时,我们需要将卷积的概念扩展到图结构上。
本节我们将回顾传统卷积,并引出在图结构上定义卷积所面临的挑战。


🖼️ 传统卷积架构回顾
我们都知道,卷积神经网络是计算机视觉领域的重大突破。例如,在ImageNet竞赛中,使用卷积和池化操作的网络将图像分类误差降低了一半,这标志着从手工设计特征到特征学习的范式转变。卷积网络在语音处理和自然语言处理中也取得了显著成功。
卷积网络之所以强大,是因为它们能够为图像数据找到最佳的表示形式来解决特定问题。当然,我们尚未完全理解卷积网络的所有奥秘,这本身也开辟了许多研究领域。
当我们使用卷积网络时,主要基于以下假设:
- 数据是合成的:图像、视频、语音等数据由局部模式构成。
- 局部感受野:网络中的神经元只连接到上一层的局部区域,而非全部神经元。
- *稳特性:相似的模式在数据域中共享(例如,图像中不同位置的黄色斑块是相似的)。
- 层次组合:低级特征组合形成中级特征,中级特征再组合形成更高级的抽象特征。
卷积网络的第一部分负责提取这些层次化特征,第二部分则用于解决具体的任务(如分类、推荐),这构成了一个端到端的学习系统。


📊 理解数据域:从网格到图
在传统设置中,数据域具有规则的网格结构:
- 图像/视频:二维像素网格,每个节点(像素)具有特征(如RGB值)。
- 自然语言处理:一维单词序列,每个节点(单词)具有特征(如词嵌入)。
- 语音:一维时间序列,每个节点(时间点)具有特征(如气压值)。
这种规则的网格结构使得我们可以精确定义数学操作(如卷积和池化),并且计算高效。
然而,许多新兴数据并不具备这种规则结构:
- 社交网络:用户作为节点,好友关系作为边。连接模式不构成网格。
- 神经科学:大脑的不同区域(节点)通过神经纤维(边)连接,形成复杂的图结构。
- 量子化学:分子中的原子(节点)通过化学键(边)连接,结构取决于具体的分子。
所有这些问题的共同点是,它们都可以用图来建模。一个图由三个基本实体定义:
- 顶点集合 V:包含 n 个节点。
- 边集合 E:描述节点之间的连接。
- 邻接矩阵 A:一个 n×n 矩阵,记录节点之间的连接强度。
此外,每个节点可以具有一个 d_v 维的特征向量,每条边可以具有一个 d_e 维的特征向量,整个图也可以具有一个 d_g 维的全局特征向量。
🔍 核心挑战:如何将卷积扩展到图?
现在,关键问题是如何将卷积操作扩展到图结构上。我们首先回顾卷积的两种经典定义。
定义一:模板匹配(相关运算)
在计算机视觉中,卷积通常被视为模板匹配。数学上,对于图像域 Ω 中的位置 i,卷积输出为:
h_{i}^{l+1} = ∑_{j∈Ω} w_j · h_{i-j}^{l}
其中 w 是卷积核(模板),h^l 是第 l 层的特征。在实际操作中,我们通常使用相关运算(将减号改为加号),因为学习过程中翻转核不影响结果。


这种定义的关键在于:
- 局部性:核具有紧凑的支持(如3x3),只对中心节点的邻居求和。
- 顺序重要性:在规则网格上,节点的排序是固定的(例如,索引 j3 总是对应模板的右上角)。这使得匹配具有明确的意义。
然而,在图结构上直接应用模板匹配面临两个主要问题:
- 节点顺序缺失:图上没有预定义的节点顺序(如“左上角”)。索引是任意的,因此直接匹配节点特征向量没有意义。
- 邻居数量可变:模板中的节点数可能与图中某个节点的邻居数量不匹配。
定义二:卷积定理
另一种定义利用卷积定理:两个函数卷积的傅里叶变换等于它们各自傅里叶变换的点积。
F(w * h) = F(w) ⊙ F(h)
对于规则网格(如图像),我们可以利用快速傅里叶变换(FFT)高效计算,复杂度为 O(n log n)。
问题在于:我们能否将傅里叶变换的定义扩展到图上?关键在于如何定义图上的傅里叶变换并使其计算高效。
基于这两种定义,我们发展出了两大类图神经网络:空间图卷积网络(基于模板匹配思想)和谱图卷积网络(基于卷积定理思想)。
📈 谱图卷积网络
谱方法的核心是利用图的谱(拉普拉斯矩阵的特征值)理论来定义卷积。我们通过四个步骤来构建谱卷积。
步骤一:定义图拉普拉斯算子
图拉普拉斯矩阵是谱理论的核心算子。对于具有 n 个顶点的图,我们通常使用归一化拉普拉斯矩阵:
L = I - D^{-1/2} A D^{-1/2}
其中 A 是邻接矩阵,D 是度矩阵(对角矩阵,D_{ii} = ∑_j A_{ij})。拉普拉斯矩阵可以衡量图上函数的*滑度。
步骤二:定义傅里叶基


我们对拉普拉斯矩阵进行特征分解:
L = Φ Λ Φ^T
其中 Λ 是由特征值 λ_1, ..., λ_n 构成的对角矩阵,Φ 的每一列是对应的特征向量 φ_1, ..., φ_n。这些特征向量被称为图的傅里叶基,它们是正交的。
步骤三:定义傅里叶变换
对于定义在图顶点上的函数(信号)h(一个 n 维向量),其图傅里叶变换定义为将 h 投影到傅里叶基上:
ĥ = Φ^T h
逆傅里叶变换则为:
h = Φ ĥ
这本质上只是矩阵与向量的乘法。
步骤四:利用卷积定理
根据卷积定理,图上两个函数 w 和 h 的卷积可以定义为:
w * h = Φ ( (Φ^T w) ⊙ (Φ^T h) ) = Φ ( diag(ŵ) Φ^T h )
我们可以将 diag(ŵ) 视为一个谱滤波器,它是特征值 λ 的函数。因此,谱卷积操作就是用一个谱滤波器 g_θ(Λ) 来调制信号的傅里叶系数:
w * h = Φ g_θ(Λ) Φ^T h
存在的问题:直接计算需要完整的特征分解,复杂度为 O(n²),对于大型图不可行。此外,学到的滤波器不一定在空间上是局部化的。
🛠️ 实用的谱GCN:ChebNet与GCN
为了解决计算复杂度和局部性问题,研究者提出了改进方案。
ChebNet:用多项式逼*谱滤波器
ChebNet的核心思想是用一个 K 阶多项式来逼*谱滤波器 g_θ(Λ):
g_θ(Λ) ≈ ∑_{k=0}^{K} θ_k T_k(Λ̃)
其中 T_k 是切比雪夫多项式,Λ̃ 是缩放后的特征值矩阵。这样做的好处是:
- 局部性:K 阶多项式滤波器在空间上具有 K-跳的局部支持。
- 线性复杂度:卷积操作可以递归计算,无需特征分解,复杂度为 O(K|E|),对于稀疏图是线性的。
- 参数共享:只需要学习 K+1 个参数
θ_k。
卷积操作变为:
w * h ≈ ∑_{k=0}^{K} θ_k T_k(L̃) h
其中 L̃ 是缩放后的拉普拉斯矩阵。T_k(L̃) h 可以通过递归关系高效计算。
GCN:一阶*似
著名的图卷积网络(GCN)可以看作是 ChebNet 的一阶*似(K=1),并进一步简化后得到的模型。其每一层的传播规则为:
H^{(l+1)} = σ( D^{-1/2} Ã D^{-1/2} H^{(l)} W^{(l)} )
其中 Ã = A + I 是加了自连接的邻接矩阵,W^{(l)} 是可学习的权重矩阵,σ 是非线性激活函数。这个操作本质上是对每个节点,将其自身特征与邻居特征的*均值进行混合后变换。
谱GCN的特点:
- 各向同性:滤波器在所有方向上是相同的(因为拉普拉斯矩阵是各向同性的)。
- 高效:计算复杂度与边数成线性关系。
- 无需节点顺序:对节点的排列具有不变性。


🧩 空间图卷积网络
空间方法直接从模板匹配的思想出发,试图在图结构上定义局部聚合操作。

核心思想:邻居聚合
由于图上没有固定的节点顺序,我们不能直接使用一个固定的模板向量与每个邻居匹配。最简单的解决方案是:让所有邻居共享同一个可学习的变换,然后对变换后的邻居特征进行聚合(如求和、*均)。
最基础的空间GCN层操作可以表示为:
h_i^{(l+1)} = σ( W^{(l)} · AGGREGATE( { h_j^{(l)} : j ∈ N(i) } ) )
其中 N(i) 是节点 i 的邻居集合,AGGREGATE 是聚合函数(如均值、求和、最大值),W^{(l)} 是可学习的权重矩阵。
各向同性与各向异性空间GCN
- 各向同性GCN:如上式所示,所有邻居使用相同的权重
W进行处理。GCN、GraphSAGE等属于此类。它们计算效率高,但假设所有邻居的重要性相同。 - 各向异性GCN:考虑到不同邻居的重要性可能不同,我们希望对邻居进行差异化处理。这可以通过引入注意力机制或利用边特征来实现。
- 图注意力网络(GAT):为每个邻居
j计算一个注意力系数α_{ij},然后进行加权聚合:
h_i^{(l+1)} = σ( ∑_{j∈N(i)} α_{ij} W^{(l)} h_j^{(l)} )
其中α_{ij}通过一个可学习的注意力机制计算,通常基于节点i和j的特征。 - 门控机制与边特征:显式地利用边特征
e_{ij}来调制信息传递,例如:
h_i^{(l+1)} = σ( ∑_{j∈N(i)} η(e_{ij}) ⊙ W^{(l)} h_j^{(l)} )
其中η是一个基于边特征的可学习函数,⊙是逐元素乘法。
- 图注意力网络(GAT):为每个邻居
有趣的联系:标准的Transformer模型可以看作是在一个全连接图上运行的空间图注意力网络,其中每个节点(如单词)都与图中所有其他节点相连,注意力权重由查询-键机制计算。
🧪 图神经网络基准与通用流程
为了公*评估不同GNN模型的性能,我们需要超越小型数据集的标准基准。一个完整的图神经网络处理流程通常包含三层:
- 输入/嵌入层:将原始的节点特征和边特征通过线性层或嵌入层映射到低维向量
H^{(0)}和E^{(0)}。 - 图神经网络层堆叠:堆叠 L 层GNN层(如GCN、GAT、ChebNet等),每一层都更新节点和边的表示:
H^{(l)}, E^{(l)} = GNNLayer( H^{(l-1)}, E^{(l-1)}, A ) - 任务层:根据下游任务类型,对最终的图表示进行处理:
- 图级任务(分类/回归):先通过全局池化(如均值池化、求和池化)得到整个图的表示向量,然后输入MLP。
- 节点级任务:直接将每个节点的最终表示输入MLP。
- 边级任务:将一条边两端节点的表示拼接(或其它操作)后输入MLP。
🎯 总结
在本节课中,我们一起学习了图卷积网络(GCNs)的核心知识。我们从传统卷积网络及其假设出发,认识到将其扩展到图结构数据的重要性与挑战。
我们详细探讨了两种主要的扩展思路:
- 谱图卷积:基于卷积定理和图拉普拉斯算子的谱分解来定义卷积。ChebNet和GCN是其中高效且实用的代表,它们通过多项式逼*避免了昂贵的特征分解,实现了线性复杂度,但通常是各向同性的。
- 空间图卷积:基于邻居聚合的思想来模拟模板匹配。从简单的各向同性聚合(如GCN)发展到更精细的各向异性聚合(如GAT),后者能够区分不同邻居的重要性。
我们还了解了图神经网络的通用处理流程和基准测试的重要性。图神经网络为社交网络分析、药物发现、推荐系统等众多领域提供了强大的工具,是一个充满活力且快速发展的研究领域。理解其基本原理是探索更高级应用和研究的坚实基础。


课程 P25:图卷积神经网络 (GCN) 实践教程 🧠
在本节课中,我们将学习图卷积神经网络 (GCN) 的核心概念,并将其与之前学过的自注意力机制联系起来。我们将通过一个具体的代码示例,学习如何实现一个门控图卷积网络 (Gated GCN) 来对不同类型的图结构进行分类。
概述
图卷积网络是处理图结构数据的一种强大神经网络架构。它利用图中节点之间的连接关系(邻接矩阵的稀疏性)来聚合信息,这与自注意力机制处理集合数据的思想有相似之处,但增加了结构先验。
上一节我们介绍了自注意力机制,本节中我们来看看如何将类似的思想应用于图结构数据。
从自注意力到图卷积
在自注意力中,我们有一组输入向量 ( X = {x_1, x_2, ..., x_T} )。每个位置的隐藏表示 ( h_i ) 是所有输入向量的线性组合:
[
h_i = \sum_{j=1}^{T} \alpha_{ij} x_j
]


其中,系数 ( \alpha_{ij} ) 通过查询-键机制计算得出,并且通常满足 ( \sum_j \alpha_{ij} = 1 )。这可以写成矩阵乘法形式 ( H = A X ),其中 ( A ) 是注意力权重矩阵。
在图卷积网络中,我们同样有一组顶点(节点)( V ),每个顶点 ( v_i ) 有一个特征向量 ( x_i )。关键区别在于,图中节点之间存在明确的连接关系,由邻接矩阵 ( A ) 定义。在基础的图卷积中,对于节点 ( v_i ),其新的表示 ( h_i ) 是其所有邻居节点特征的聚合:
[
h_i = \frac{1}{d_i} \sum_{j \in \mathcal{N}(i)} x_j
]
这里,( \mathcal{N}(i) ) 是节点 ( i ) 的邻居集合,( d_i ) 是其度数(邻居数量)。这可以看作是注意力权重 ( \alpha_{ij} = 1/d_i )(如果 ( j ) 是 ( i ) 的邻居)的一个特例。邻接矩阵 ( A ) 在这里充当了一个“硬”注意力机制,它预先定义了哪些节点之间应该交互。
为了引入可学习的参数并考虑节点自身特征,我们通常会加入自连接和线性变换:
[
h_i = \sigma \left( W \cdot \left( \frac{1}{d_i} \sum_{j \in \mathcal{N}(i) \cup {i}} x_j \right) \right)
]
其中 ( W ) 是可学习的权重矩阵,( \sigma ) 是非线性激活函数。
门控图卷积网络 (Gated GCN)

我们将要实现的是一种更复杂的变体:残差门控图卷积网络。它不仅考虑节点特征,还考虑了边的特征,并使用一个门控机制来调制来自不同邻居的信息流。


以下是该模型的核心方程:





- 节点更新:对于节点 ( v ),其新表示 ( h_v ) 是自身特征和邻居特征的加权和。
[
h_v = f\left( A x_v + \sum_{u \in \mathcal{N}(v)} \eta_{vu} \cdot B x_u \right)
]
其中 ( A, B ) 是可学习的权重矩阵,( f ) 是激活函数(如 ReLU),( \eta_{vu} ) 是门控权重。






- 边表示与门控:边 ( e_{vu} ) 的表示 ( e_{vu} ) 由其两端节点特征计算得出,并用于计算门控 ( \eta_{vu} )。
[
e_{vu} = C e_{vu}^{(input)} + D x_v + E x_u
]
[
\eta_{vu} = \frac{\sigma(e_{vu})}{\sum_{k \in \mathcal{N}(v)} \sigma(e_{vk})}
]
这里 ( C, D, E ) 是可学习的权重矩阵,( \sigma ) 是 sigmoid 函数。门控 ( \eta_{vu} ) 是一个归一化的权重,决定了来自邻居 ( u ) 的信息对节点 ( v ) 的重要性。




- 边更新:边的表示也会随着网络层数更新,通常包含一个残差连接。
[
e_{vu}^{(new)} = e_{vu}^{(input)} + \text{ReLU}(e_{vu})
]



- 残差连接:为了防止梯度消失和网络退化,通常在节点和边的更新中加入残差连接。
[
h_v^{(l+1)} = h_v^{(l)} + \text{ReLU}(\text{LayerNorm}(h_v^{(new)}))
]
代码实践:图分类任务

现在,让我们通过代码来具体实现一个门控 GCN,并用于一个图分类任务。我们将使用 DGL (Deep Graph Library) 和 PyTorch。
1. 环境设置与数据准备
首先,我们导入必要的库并创建一个迷你图分类数据集。




import os
os.environ['DGLBACKEND'] = 'pytorch'
import dgl
import torch
import torch.nn as nn
import torch.nn.functional as F
import networkx as nx
import matplotlib.pyplot as plt




# 创建一个迷你图分类数据集
from dgl.data import MiniGCDataset
dataset = MiniGCDataset(num_graphs=100, min_num_v=10, max_num_v=20)


这个数据集包含多种图结构(如环形、星形、车轮形等),我们的任务是训练一个模型来区分这些图类型。


2. 定义 Gated GCN 层
以下是 Gated GCN 层的 PyTorch 模块实现。



class GatedGCNLayer(nn.Module):
def __init__(self, input_dim, output_dim):
super().__init__()
self.A = nn.Linear(input_dim, output_dim) # 用于自身节点变换
self.B = nn.Linear(input_dim, output_dim) # 用于邻居节点变换
self.C = nn.Linear(input_dim, output_dim) # 用于边变换
self.D = nn.Linear(input_dim, output_dim) # 用于边计算中的源节点
self.E = nn.Linear(input_dim, output_dim) # 用于边计算中的目标节点
self.bn_node = nn.BatchNorm1d(output_dim)
self.bn_edge = nn.BatchNorm1d(output_dim)
def message_func(self, edges):
# 计算边的表示 ej
Bj = self.B(edges.src['h']) # B * x_j
e = self.C(edges.data['e']) + self.D(edges.dst['h']) + self.E(edges.src['h'])
# 存储边的表示用于后续门控计算
edges.data['e'] = e
return {'Bj': Bj, 'e': e}
def reduce_func(self, nodes):
# 聚合所有传入的消息
Ax = self.A(nodes.data['h']) # A * x_i
Bj = nodes.mailbox['Bj'] # 来自各邻居的 B*x_j
e = nodes.mailbox['e'] # 来自各边的表示
# 计算门控权重 eta (sigma(e) / sum(sigma(e)))
sigma = torch.sigmoid(e)
sum_sigma = torch.sum(sigma, dim=1, keepdim=True)
eta = sigma / (sum_sigma + 1e-6)
# 用门控权重加权邻居信息
weighted_Bj = eta * Bj
h = Ax + torch.sum(weighted_Bj, dim=1)
# 应用批归一化和非线性激活
h = F.relu(self.bn_node(h))
e = F.relu(self.bn_edge(e.view(-1, e.size(-1)))).view(e.size())
return {'h': h, 'e_agg': e}
def forward(self, g, h, e):
with g.local_scope():
g.ndata['h'] = h
g.edata['e'] = e
# 消息传递与聚合
g.update_all(self.message_func, self.reduce_func)
h = g.ndata['h']
e = g.edata['e_agg'].view(e.shape) # 获取更新后的边表示
# 残差连接
h = h + F.relu(self.bn_node(self.A(h)))
e = e + F.relu(self.bn_edge(self.C(e)))
return h, e

3. 构建完整的 Gated GCN 模型
我们将堆叠多个 Gated GCN 层,并在最后使用一个多层感知机 (MLP) 对整个图进行分类。



class GatedGCN(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, num_layers):
super().__init__()
self.embed = nn.Linear(input_dim, hidden_dim)
self.layers = nn.ModuleList([
GatedGCNLayer(hidden_dim, hidden_dim) for _ in range(num_layers)
])
# 读出函数:对所有节点特征取*均,然后通过MLP得到图级别表示
self.readout = nn.Sequential(
nn.Linear(hidden_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, output_dim)
)
def forward(self, g, h, e):
h = self.embed(h)
for layer in self.layers:
h, e = layer(g, h, e)
with g.local_scope():
g.ndata['h'] = h
# 图读出:*均所有节点特征
hg = dgl.mean_nodes(g, 'h')
return self.readout(hg)



4. 训练与评估循环


以下是标准的训练和评估函数。
def train(model, optimizer, g, h, e, labels):
model.train()
logits = model(g, h, e)
loss = F.cross_entropy(logits, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss.item()
def evaluate(model, g, h, e, labels):
model.eval()
with torch.no_grad():
logits = model(g, h, e)
_, predicted = torch.max(logits, 1)
correct = (predicted == labels).sum().item()
return correct / labels.size(0)





# 初始化模型、优化器
model = GatedGCN(input_dim=1, hidden_dim=64, output_dim=8, num_layers=3)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)




# 假设 dataloader 能提供批处理后的图数据 (batched_graph), 节点特征 h, 边特征 e, 标签 labels
for epoch in range(50):
total_loss = 0
for batched_graph, h, e, labels in train_dataloader:
loss = train(model, optimizer, batched_graph, h, e, labels)
total_loss += loss
train_acc = evaluate(model, ...) # 在训练集上评估
test_acc = evaluate(model, ...) # 在测试集上评估
print(f'Epoch {epoch}, Loss: {total_loss:.4f}, Train Acc: {train_acc:.4f}, Test Acc: {test_acc:.4f}')




核心概念总结

本节课中我们一起学习了:
- 图卷积网络 (GCN) 的本质:它是一种利用图结构稀疏性(邻接矩阵)的神经网络,通过对邻居节点特征进行聚合来更新节点表示。可以看作是具有预定义稀疏注意力模式的自注意力机制。
- 与自注意力的联系与区别:
- 联系:两者都通过对一组输入元素(节点/词元)进行加权聚合来计算新的表示。
- 区别:自注意力的注意力权重 ( A ) 是通过数据动态计算(如
softmax(QK^T))的完全矩阵。而 GCN 中的“注意力”权重由给定的图结构(邻接矩阵 ( A )) 预先定义,通常是二元的或简单的归一化形式(如 ( D^{-1}A ))。GCN 利用了领域特定的稀疏性先验。
- 门控图卷积网络 (Gated GCN):一种高级变体,它引入了边特征和一个可学习的门控机制。门控权重由边的表示计算,用于调制来自不同邻居的信息流,使得模型能够更灵活地决定哪些邻居信息更重要。
- 实践应用:我们实现了一个 Gated GCN 模型,用于图分类任务。该任务要求模型根据图的整体结构(即节点之间的连接方式)来判断其所属类别。代码展示了如何使用 DGL 库定义消息传递函数、实现节点与边的更新,以及如何构建包含残差连接和批归一化的完整网络。

通过本课程,你应该理解了图神经网络的基本思想,并能够将其与熟悉的序列模型(如 Transformer)进行类比,同时也掌握了使用现代图深度学习库进行开发的基本流程。


课程 P26:第14周 - 结构化预测 🧩

在本节课中,我们将要学习结构化预测的基本概念。结构化预测是机器学习中的一个重要领域,它涉及预测那些本身具有复杂结构的输出,例如句子、序列或图。我们将从历史背景入手,通过一个简单的语音识别例子来理解其核心思想,并探讨如何利用能量模型和动态规划进行高效的推理与训练。
历史背景与动机
上一节我们介绍了结构化预测的目标。本节中,我们来看看它的历史起源。
结构化预测并非一个新问题。早在20世纪90年代初,研究者们就开始探索将神经网络与判别式训练结合,用于解决需要输出序列的任务,例如语音识别。
一个早期的代表性工作是Javier de la Cruz II在1991年提出的四词语音识别模型。该模型使用了一个时域卷积神经网络来处理语音信号,其输出是一系列特征向量。由于不同人说话速度不同,系统需要将输入的特征向量序列与预存的单词模板序列进行对齐,这个过程被称为动态时间规整。
动态时间规整详解
上一节提到了动态时间规整。本节中我们来详细看看它是如何工作的。
假设我们有一个语音信号,它被切分成一系列短时片段,并转换为特征向量。同时,我们为每个待识别的单词(例如数字0-9)预先存储了一个模板,它也是一个特征向量序列。
核心问题在于:输入序列的长度和模板序列的长度可能不同。动态时间规整的目标是找到一种最优的“弯曲”方式,将输入序列映射到模板序列上,使得两者的总体差异最小。
我们可以将两个序列的特征向量排列成一个矩阵。矩阵中的每个元素 (i, j) 代表输入序列第 i 个向量与模板序列第 j 个向量之间的距离(例如欧氏距离)。
寻找最优对齐路径就转化为在这个矩阵中,从左上角到右下角找一条路径,使得路径上所有点的距离之和最小。这条路径允许水*、垂直和对角线移动,分别对应着重复、跳过和匹配操作。
公式表示: 设输入序列为 X = [x1, x2, ..., xT],模板序列为 Y = [y1, y2, ..., yS]。距离矩阵 D 中 D[i,j] = dist(xi, yj)。动态时间规整寻找路径 P 以最小化总代价:
总代价 = Σ_{(i,j) in P} D[i,j]
作为能量模型的结构化预测
理解了序列对齐后,我们可以将结构化预测问题形式化为一个更通用的框架:能量模型。
在能量模型中,系统为每一个可能的输入 X 和输出结构 Y 的配对赋予一个标量值 E(X, Y),称为能量。我们的目标是找到使能量最低的输出 Y。
在语音识别的例子里:
X是输入的语音特征序列。Y是输出的单词类别(例如,“三”)。- 计算
E(X, Y)的过程包含了动态时间规整:即找到最优对齐路径Z(一个潜在变量),并计算该路径下的累积距离。因此,能量可以写为:
E(X, Y) = min_{Z in 对齐路径} [ Σ_{对齐点} 距离(x_i, y_j) ] - 推理时,我们计算所有候选
Y(所有单词)的能量,并选择能量最低的那个作为预测结果。
因子图与高效推理


当输出结构更复杂时(例如一个句子中的多个词),直接枚举所有可能组合在计算上是不可行的。幸运的是,许多问题的能量函数可以分解为局部项之和,这可以通过因子图来表示。
考虑一个简单的因子图模型,其能量是几个局部能量项的和,每项只依赖于输出变量的一小部分(例如,相邻的两个词)。这种分解特性使得我们可以利用动态规划等算法进行高效的最优解搜索,而无需遍历指数级数量的所有组合。
一个经典的高效算法是将搜索过程转化为在一个篱笆网络中寻找最短路径的问题。网络中的每个节点代表某个变量在某个时刻的取值状态,每条边代表一个局部能量项的代价。寻找全局能量最小的配置,等价于在这个网络中寻找从起点到终点的最短路径。
代码概念描述(伪代码):
# 假设我们有一个篱笆网络,nodes[t][k] 表示第t步第k个状态的累积最小能量
dp = initialize_with_infinity()
dp[start_node] = 0
for step in range(1, total_steps):
for current_state in states_at_step[step]:
# 从前一步的所有可能状态转移过来
for prev_state in states_at_step[step-1]:
cost = local_energy(prev_state, current_state, input_X)
dp[current_state] = min(dp[current_state], dp[prev_state] + cost)
# 回溯找到最优路径
best_path = backtrack(dp)
模型的训练
上一节我们讨论了如何进行推理。本节中我们来看看如何训练这样的模型。
训练的目标是调整模型的参数(例如,神经网络权重、模板向量),使得:
- 对于训练样本
(X, Y*)(Y*是正确答案),其能量E(X, Y*)尽可能低。 - 对于其他错误的输出
Y(Y ≠ Y*),其能量E(X, Y)应该比正确答案的能量更高。
这本质上是一个判别式训练准则。训练过程通常涉及以下步骤:
- 前向传播:对于给定的
X和Y*,运行推理算法(如动态时间规整或篱笆网络搜索),找到使能量最小的潜在变量配置Z*,并计算出E(X, Y*)。 - 对比采样:通常也需要为一些错误的
Y计算能量(或估计其下界)。 - 损失计算与反向传播:定义一个损失函数(例如,间隔损失),它惩罚正确答案能量过高或错误答案能量过低。然后通过反向传播算法,计算损失函数对模型所有可训练参数的梯度。
- 参数更新:使用梯度下降法更新参数。
关键点在于,即使推理过程涉及离散搜索(如动态规划),训练时的梯度仍然可以通过这些算法“传播”回来,调整那些连续可微的参数(如定义距离的神经网络)。












本节课中我们一起学习了结构化预测的核心思想。我们从语音识别中的动态时间规整这一具体实例出发,理解了将序列对齐问题形式化为能量最小化过程。进而,我们看到了如何利用能量模型和因子图的分解特性,通过动态规划等算法高效解决复杂的结构化预测问题。最后,我们探讨了如何通过判别式训练准则来训练这类模型,即使推理包含离散步骤,梯度仍能通过系统进行反向传播。结构化预测为处理序列、树、图等复杂输出提供了强大的理论框架和实用工具。
深度学习课程 P27:过拟合与正则化 🧠
在本节课中,我们将学习深度学习中的两个核心概念:过拟合与正则化。我们将探讨它们之间的联系,理解为什么过拟合会发生,以及如何使用正则化技术来缓解过拟合,从而提升模型的泛化能力。


过拟合现象 🔍
上一节我们介绍了课程概述,本节中我们来看看什么是过拟合。
过拟合是指模型在训练数据上表现很好,但在未见过的测试数据上表现较差的现象。这通常发生在模型过于复杂,以至于“记住”了训练数据中的噪声和细节,而非学习数据背后的通用规律。
以下是三种拟合情况的对比:
- 欠拟合:模型复杂度低于数据复杂度。例如,试图用一条直线去拟合抛物线形状的数据点,效果不佳。
- 恰当拟合:模型复杂度与数据复杂度匹配。模型能够很好地捕捉数据的主要趋势。
- 过拟合:模型复杂度高于数据复杂度。模型不仅学习了数据的真实规律,还拟合了数据中的随机噪声,导致在训练集上误差为零,但泛化能力差。
一个关键问题是:如果数据点来自一个完美的抛物线,即使模型能力很强,它也应该学出一个*滑的抛物线。为什么过拟合模型会产生“抖动”的曲线呢?原因在于现实数据总存在噪声。为了完美地穿过每一个带有轻微偏移的训练点,模型不得不产生复杂、古怪的函数形状。
在深度学习中,我们使用的模型通常参数过多、能力过强,它们甚至能够完美记忆带有随机标签的数据。这表明我们的模型是过参数化的。
为何需要过参数化模型? 🤔
既然过参数化会导致过拟合,为什么我们还要使用这样的模型呢?主要有两个原因:
- 优化更容易:在高维、过参数化的空间中,损失函数的景观通常更*滑,更容易找到好的解,训练过程更稳定。
- 便于调试:在开始正式训练前,一个重要的调试步骤是检查模型是否具备基本的学习能力。我们可以用一小批随机数据(甚至是随机标签)来训练模型几个周期。如果模型能够快速地将训练误差降到接*零(即“记住”这批数据),说明模型结构和训练流程在技术上是可行的。这被称为“记忆化测试”。
此外,在参数空间中,存在许多不同的参数配置(例如,通过置换神经网络某一层的神经元顺序)可以映射到完全相同的输入输出函数。这意味着在庞大的参数空间中,存在大量功能等价的解。我们倾向于过参数化,是因为它提供了更多容易到达的“好解”的路径。
正则化:对抗过拟合的武器 🛡️
我们已经了解了过拟合及其原因,那么如何解决它呢?本节我们将介绍正则化的核心思想。
正则化是一类用于防止过拟合、提高模型泛化能力的技术。它的核心思想是为模型添加一些约束或先验知识,限制其学习能力,使其更倾向于学习简单、*滑的模式,而不是复杂的噪声。
可以从几个角度理解正则化:
- 参数空间视角:为模型参数引入先验分布。例如,假设参数应该较小(服从高斯分布)或稀疏(服从拉普拉斯分布)。这相当于在训练时,不仅要求模型拟合数据,还要求参数符合我们预设的分布。
- 公式:带有L2正则化的损失函数可写为:
L_total = L_data + λ * ||w||^2,其中λ是控制正则化强度的超参数。
- 公式:带有L2正则化的损失函数可写为:
- 函数空间视角:对模型可以表示的函数集合进行限制。正则化使得模型只能学习那些不那么“极端”或“振荡”的函数,例如更*滑的函数。
- 算法修改视角:任何旨在降低泛化误差而非训练误差的算法修改,都可以视为正则化。这是深度学习中最实用的定义。
用一个比喻来理解不同强度的正则化:
- 无正则化:模型可以自由学习,可能产生复杂、带尖刺的边界(对应过拟合)。
- 弱正则化:对模型能力稍加限制,边界变得稍*滑一些。
- 强正则化:严格限制模型能力,可能迫使模型学习过于简单的模式(如将复杂边界*滑为圆形),可能导致欠拟合。
我们的目标是找到合适的正则化强度,在欠拟合和过拟合之间取得最佳*衡,达到“恰当拟合”的状态。
总结 📚












本节课中我们一起学习了:
- 过拟合:模型过度复杂,完美拟合训练数据(包括噪声),导致泛化性能下降。
- 过参数化的价值:尽管可能导致过拟合,但过参数化模型更易于优化和调试。
- 正则化:通过为参数添加先验、限制函数空间或修改学习算法,来约束模型复杂度,从而对抗过拟合、提升泛化能力的核心技术。






















理解过拟合与正则化是构建高效、鲁棒深度学习模型的关键。在实践中,我们需要根据具体任务和数据,灵活选择和调整正则化方法及其强度。

课程 P28:基于能量的隐变量模型推断 🔍


在本节课中,我们将学习如何对基于能量的隐变量模型进行推断。我们将从一个具体的椭圆数据生成示例入手,理解为什么传统的向量到向量映射神经网络无法处理此类数据,并介绍如何使用基于能量的隐变量模型来解决这个问题。课程将重点讲解能量函数、解码器以及如何利用模型进行推断,而不涉及模型的训练过程。
数据生成与问题引入
上一节我们概述了课程目标。本节中,我们来看看我们将要处理的数据及其带来的挑战。
我们的数据点 y 由两个分量组成:y1 和 y2。它们由以下公式生成:
y1 = ρ1(x) * cos(θ) + ε1
y2 = ρ2(x) * sin(θ) + ε2
其中:
x是从均匀分布中采样的输入。θ是从[0, 2π)均匀分布中采样的角度。ε1和ε2是从均值为0、标准差为1/20的正态分布中采样的噪声。ρ是一个将一维输入x映射到二维空间的函数,其具体形式为:
其中ρ1(x) = (α * x + β * (1 - x)) * exp(x) ρ2(x) = (β * x + α * (1 - x)) * exp(x)α = 1.5,β = 2。
这个生成过程会产生一个形状像“喇叭”的数据流形,其横截面是椭圆,且椭圆的大小随 x 指数增长。
这里的关键问题是:对于单个固定的输入 x,存在无数个可能的输出 y(构成一个椭圆)。同样,给定一个 y1 的值,通常对应两个 y2 的值。因此,传统的、学习单一确定性映射的前馈神经网络无法有效处理这种“一对多”的关系。
为了简化问题,便于教学,我们将进行以下处理:
- 固定输入
x = 0。这使得指数项exp(x) = 1,并且ρ函数简化为常数:ρ1 = 2,ρ2 = 1.5。 - 我们的数据 y 简化为从以下分布中采样24个点:
这形成了一个长轴半径为2、短轴半径为1.5的椭圆(加上少量噪声)。y1 = 2 * cos(θ) + ε1 y2 = 1.5 * sin(θ) + ε2θ和ε对我们而言是未知的隐变量。
模型架构:解码器与能量函数
上一节我们明确了要处理的数据。本节中,我们来看看解决此问题的模型架构。
我们假设已经获得了一个预训练好的模型。该模型的核心是一个解码器(Decoder) g(z),它接收一个隐变量(Latent Variable) z 作为输入,并输出一个重构数据点 ỹ。
ỹ = g(z) = [g1(z), g2(z)]^T
在我们的简化设定中,解码器被设计为对数据生成过程的一个良好猜测:
g1(z) = w1 * cos(z)
g2(z) = w2 * sin(z)
这里,w1 和 w2 是模型的可学习参数(在本推断练习中,它们已被固定为某个值)。cos(z) 和 sin(z) 是我们根据先验知识引入的。通过让 z 在 [0, 2π) 范围内变化,g(z) 的输出会描绘出一个椭圆。
模型的另一个核心组件是能量函数(Energy Function) E(y, z)。它衡量一个给定的真实数据点 y 与一个由隐变量 z 通过解码器生成的重构点 ỹ 之间的“不兼容性”或距离。能量越低,表示 y 和 ỹ 越兼容。
我们使用*方欧几里得距离来定义能量:
E(y, z) = (y1 - g1(z))^2 + (y2 - g2(z))^2
对于我们的24个训练数据点 Y = {y^(1), y^(2), ..., y^(24)},每个点都有其对应的能量函数 E(y^(i), z)。
推断过程:最小化能量
上一节我们介绍了模型的核心组件。本节中,我们来看看如何利用这个模型进行推断。
推断的核心任务是:对于一个给定的数据点 y(可以是训练集中的点,也可以是一个新点),找到最能解释它的隐变量 z。这通过最小化能量函数来实现。
以下是推断的基本步骤:
- 固定数据点:选择一个你想要分析的数据点 y。
- 优化隐变量:针对该 y,寻找使得能量
E(y, z)最小的z值。这通常通过梯度下降等优化算法完成:z* = argmin_z E(y, z) - 获取重构与能量:将找到的最优隐变量
z*输入解码器,得到最优重构 ỹ* =g(z*)。此时的最小能量值E(y, z*)反映了该数据点 y 与模型认为的“合理”数据流形(即解码器能生成的所有点)的匹配程度。能量越低,说明 y 越可能来自该模型描述的分布。
对于训练集中的每个点,我们都可以进行上述过程,从而得到24个最优隐变量 z* 和对应的最小能量。
总结
本节课中,我们一起学习了基于能量的隐变量模型的推断方法。
我们从具有内在多模态特性的椭圆数据生成示例出发,理解了传统神经网络的局限性。接着,我们介绍了由解码器和能量函数构成的模型架构,其中解码器将隐变量映射到数据空间,能量函数则度量真实数据与重构数据之间的差异。最后,我们详细讲解了推断的核心流程:通过最小化能量函数,为给定数据点找到最匹配的隐变量解释。


















通过本课,你掌握了如何利用一个预训练的基于能量的隐变量模型,来评估数据点与模型信念的兼容性,这是理解和使用此类生成模型的关键一步。


课程 P29:第15周 - 实践课 B - 训练隐变量能量模型 (EBM) 🧠
在本节课中,我们将学习如何训练隐变量能量模型。我们将从回顾上节课的“零温极限”自由能开始,然后引入一个更*滑的“松弛”版本的自由能,并理解其物理意义和计算方法。

回顾:零温极限自由能 🥶

上一节我们介绍了能量模型中的推理问题,即如何找到给定观测变量 y 时,隐变量 z 的最优值,并计算相应的能量 E 和自由能 F。
我们计算了所谓的“零温极限”自由能 F∞(y)。在我们的例子中,y 是一个二维向量,因此 F∞(y) 是一个标量场,可以想象成一个二维区域上的高度图。
- 紫色区域表示自由能
F∞ = 0。 - 绿色区域表示自由能
F∞ = 1。 - 黄色区域表示自由能
F∞ > 1。
这个自由能本质上是给定点 y 到模型流形(一个椭圆)的欧几里得距离的*方。因此,位于流形上的点距离为零,自由能也为零。随着点远离流形,自由能呈二次增长。
观察图像,我们注意到在椭圆内部的中心区域,自由能形成了一个“峰值”,而不是*滑的谷底。本节课,我们将学习如何松弛这个零温极限自由能,使其更加*滑,消除这些局部极小值(峰值)。
为了更清楚地观察,我们来看一个截面图(取 y1 = 0)。这个截面显示,在流形外部,自由能呈抛物线形上升;而在流形内部中心,自由能形成了一个高峰。
引入松弛自由能:温度的作用 🌡️
本节中,我们来看看如何通过引入“温度”的概念来*滑自由能景观。
我们已知的自由能 F∞ 是能量 E(y, z) 在 z 上的最小值:
F∞(y) = min_z E(y, z)
现在,我们引入一个参数化的松弛版本自由能 F(y; β):
F(y; β) = -1/β * log( ∫ exp(-β E(y, z)) dz )
核心概念解析:
β(Beta): 称为逆温度,β = 1/(k_B T),其中k_B是玻尔兹曼常数,T是温度。- 当温度
T极高时(如太阳表面),β → 0。 - 当温度
T极低时(如绝对零度),β → +∞。
- 当温度
- 零温极限: 当
β → ∞(极冷)时,F(y; β)就退化为我们之前的F∞(y),即取最小值。 - 高温极限: 当
β → 0(极热)时,F(y; β)趋*于能量E在z上的*均值。
这个松弛自由能 F(y; β) 被称为能量的 “软最小值”。与直接取最小值(硬最小值)不同,它考虑了所有可能 z 值的能量贡献,但通过指数加权让低能量区域贡献更大。
计算松弛自由能:离散化方法 🧮
为了实际计算积分 ∫ exp(-β E(y, z)) dz,我们采用简单的离散化方法。
我们将连续的积分*似为离散的和:
F(y; β) ≈ -1/β * log( ∑_i exp(-β E(y, z_i)) Δz )
在我们的例子中,隐变量 z 的定义域是 [0, 2π],因此积分测度 dz 对应总长度 2π。离散化时,Δz 是采样间隔。
让我们对一个具体点 y’ = [2, 3](图中右侧的绿叉)计算其松弛自由能。我们设定 β = 1。
以下是计算过程的直观展示:
- 对于流形(椭圆)上的每个点(由不同的
z生成),计算其到目标点y’的距离*方,即能量E。 - 计算
exp(-β * E),即exp(-距离*方)。距离越*,此项值越大。 - 将所有
exp(-β * E)的值求和(并考虑Δz),然后取对数并乘以-1/β,得到最终的松弛自由能F。
通过这种计算,原来在零温下只考虑最*点的“硬”距离,现在被“软化”为考虑所有点的加权*均距离,从而*滑了自由能曲面。
总结 📚



本节课中我们一起学习了:
- 回顾了零温自由能
F∞,它计算点到模型流形的最小*方距离,但可能产生非*滑的景观(如内部峰值)。 - 引入了松弛自由能
F(y; β),它通过逆温度参数β引入物理中的“温度”概念,将硬最小值操作转化为软最小值操作。 - 理解了其物理意义:
β → ∞对应零温极限,恢复为硬最小值;β → 0对应高温极限,趋*于*均值。 - 掌握了计算方法:通过离散化积分,我们可以实际计算给定观测
y时的松弛自由能,这为后续训练能量模型提供了更*滑、更易优化的目标函数。

















这种松弛技术对于训练复杂的隐变量能量模型至关重要,因为它能帮助优化过程避免陷入不良的局部极小值。



课程P3:随机梯度下降与反向传播 🧠
在本节课中,我们将学习深度学习中的两个核心优化算法:随机梯度下降 和 反向传播。我们将从参数化模型的基本概念开始,逐步深入到梯度计算、链式法则的应用,以及如何通过模块化的视角来理解神经网络的前向与反向传播过程。课程内容旨在让初学者能够清晰地掌握这些关键概念及其实现原理。
参数化模型与监督学习范式 📊
上一节我们介绍了课程概述,本节中我们来看看机器学习的基础——参数化模型。
参数化模型本质上是一个函数。这个函数依赖于两个参数:输入 和 可训练参数。在训练过程中,参数在所有样本间是共享的,但每个训练样本的输入是不同的。
在深度学习中,参数通常被隐式地存储在模型内部。一个典型的监督学习系统可以表示为一个计算图。
以下是监督学习系统的基本组成部分:
- 参数化模型 (G):一个确定性函数,接收输入
X和参数W,产生预测输出Y_bar。 - 成本函数 (C):一个标量函数,比较模型的预测输出
Y_bar和真实目标Y,计算出一个损失值。 - 样本损失 (L):对于单个样本
(X, Y),其损失L(X, Y, W)等于成本函数C的输出。 - 总损失 (J):在整个训练集
S上,总损失是所有样本损失的*均值,公式为:
J(W) = (1/|S|) * Σ_{(X,Y)∈S} L(X, Y, W)
机器学习的核心任务就是找到一组参数 W,使得总损失 J(W) 最小化。


基于梯度的优化方法 📉
上一节我们定义了要优化的目标函数,本节中我们来看看如何通过基于梯度的方法来最小化它。
基于梯度的优化方法假设我们可以相对容易地计算目标函数的梯度。梯度下降是最基本的方法,其思想是沿着函数最陡的下降方向(负梯度方向)迭代更新参数,以找到最小值。
梯度下降的更新规则可以用以下公式表示:
W_new = W_old - η * ∇J(W_old)
其中 η 是学习率(步长),∇J(W) 是总损失 J 关于参数 W 的梯度。
然而,计算整个训练集上的梯度(批量梯度下降)在数据集很大时计算代价高昂。因此,我们更常用随机梯度下降。
以下是随机梯度下降的核心思想:
- 随机梯度下降:每次迭代时,我们不计算所有样本的*均梯度,而是随机抽取一个样本
(X_p, Y_p),计算该样本的损失L关于参数的梯度,并沿此方向更新参数。更新公式为:
W_new = W_old - η * ∇_W L(X_p, Y_p, W_old) - 小批量梯度下降:在实际应用中,更常见的是使用一小批样本(一个批次)的*均梯度来更新参数。这样做既减少了单样本更新带来的噪声和振荡,又能利用现代硬件(如GPU)的并行计算能力,提高效率。
- 与强化学习的区别:在监督学习中,成本函数
C是可微的,我们可以直接计算梯度。而在强化学习中,奖励信号(类似于成本)通常是一个不可微的黑盒,只能通过采样来估计,这使得训练效率较低。
传统神经网络结构 🧱
上一节我们介绍了优化算法,本节中我们来看看这些算法要优化的对象——神经网络的结构。


传统的神经网络由交替的线性层和非线性激活函数堆叠而成。
一个两层的神经网络(通常称为三层网络,包括输入层、隐藏层和输出层)的前向传播过程如下:
- 第一层(线性):输入向量
X乘以权重矩阵W0,得到加权和向量S1。公式为:S1 = W0 * X。 - 第一层(非线性):将
S1的每个分量通过一个非线性激活函数h(如ReLU),得到激活值Z1。公式为:Z1 = h(S1)。 - 第二层(线性):
Z1乘以权重矩阵W1,得到S2。公式为:S2 = W1 * Z1。 - 第二层(非线性):将
S2通过激活函数,得到最终输出Z2(或Y_bar)。
如果没有非线性激活函数,多个线性层的组合等价于单个线性层,网络就无法学习复杂的模式。因此,非线性激活函数至关重要。
在PyTorch等框架中,我们可以用面向对象的方式简洁地定义网络:
class SimpleNet(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super().__init__()
self.linear1 = nn.Linear(input_size, hidden_size)
self.linear2 = nn.Linear(hidden_size, output_size)
self.relu = nn.ReLU()
def forward(self, x):
x = x.view(-1) # 展*输入
s1 = self.linear1(x)
z1 = self.relu(s1)
s2 = self.linear2(z1)
return s2
反向传播与链式法则 🔄


上一节我们了解了网络如何前向计算输出,本节中我们来看看如何高效地计算损失函数关于所有参数的梯度,即反向传播。
反向传播的核心是链式法则。考虑一个简单的复合函数 C = C(Z),Z = h(S)。根据链式法则,C 关于 S 的导数为:
dC/dS = (dC/dZ) * (dZ/dS) = (dC/dZ) * h'(S)
在神经网络中,我们将这个原理应用于每一层。假设我们已知损失 C 关于第 k+1 层输入 Z_{k+1} 的梯度 dC/dZ_{k+1},那么我们可以计算:
C关于第k层参数W_k的梯度:dC/dW_k = (dC/dZ_{k+1}) * (dZ_{k+1}/dW_k)C关于第k层输入Z_k的梯度(用于继续反向传播):dC/dZ_k = (dC/dZ_{k+1}) * (dZ_{k+1}/dZ_k)
其中,dZ_{k+1}/dW_k 和 dZ_{k+1}/dZ_k 是模块 F_k(执行 Z_{k+1} = F_k(Z_k, W_k) 操作)的雅可比矩阵。


从计算图的角度看,反向传播构建了一个与原始前向计算图结构相似但信号反向流动的图,用于计算梯度。现代深度学习框架(如PyTorch的Autograd)可以自动完成这一过程。


常见模块的反向传播 🧩
上一节我们介绍了反向传播的一般原理,本节中我们具体看看几种常见计算模块的梯度传播方式。
理解基本模块的梯度传播有助于我们设计新的网络层或调试模型。
以下是几种关键模块的行为:
- 复制模块:将单个输入
X复制到多个输出(如Y1=X,Y2=X)。在反向传播时,流向该输入的总梯度是来自所有输出分支的梯度之和。dC/dX = dC/dY1 + dC/dY2。 - 求和模块:将多个输入(如
X1,X2)相加得到一个输出Y = X1 + X2。反向传播时,输出梯度被复制并流向每一个输入。dC/dX1 = dC/dY,dC/dX2 = dC/dY。 - 最大值模块:输出是输入中的最大值,例如
Y = max(X1, X2)。这就像一个开关,只将梯度传递给前向传播时被选中的那个输入,其他输入的梯度为零。 - Softmax模块:将一组分数转换为一个概率分布。
Y_i = exp(X_i) / Σ_j exp(X_j)。其反向传播计算稍复杂,是一个很好的练习。它通常用于多分类网络的最后一层。 - Sigmoid模块:
σ(s) = 1 / (1 + exp(-s))。当其输入s的绝对值很大时,输出会饱和(接*0或1),导致梯度σ'(s)变得非常小,这被称为“梯度消失”问题。因此,在深层网络中,Sigmoid的使用已减少,转而更多使用ReLU及其变体。
实践技巧与总结 🛠️


上一节我们剖析了核心计算模块,本节中我们将以一些重要的实践技巧作为课程的结尾。
成功的深度学习实践不仅需要理解算法,还需要掌握一系列工程化技巧来使模型有效训练。
以下是一些关键的训练技巧:
- 输入归一化:对输入数据的每个特征进行归一化,使其均值为0,方差为1。这有助于稳定训练,加速收敛。例如,对于图像数据,可以计算所有训练图片每个通道(R, G, B)的均值和标准差,然后用它们来归一化每张图片。
- 权重初始化:不能将权重初始化为零或过大过小的随机值。常用的方法是“Xavier初始化”或“He初始化”,其目标是使每一层输出的方差与输入的方差大致相同。例如,使用均值为0,方差为
1 / fan_in的正态分布来初始化权重,其中fan_in是该层的输入数量。 - 学习率调整:使用学习率衰减策略,例如随着训练轮次增加逐步降低学习率,有助于模型在后期精细调整。更高级的优化器(如Adam)会自动调整每个参数的学习率。
- 正则化:
- L2正则化/权重衰减:在更新权重时,除了减去梯度项,还会将权重本身乘以一个略小于1的因子
(1 - λ),这等价于在损失函数中增加了权重的L2范数惩罚项,倾向于让权重向量变小。 - Dropout:在训练过程中,随机将神经网络中一部分神经元的输出置零。这可以防止网络对某些特定的神经元产生过度的依赖,是一种有效的正则化手段,能提升模型的泛化能力。
- L2正则化/权重衰减:在更新权重时,除了减去梯度项,还会将权重本身乘以一个略小于1的因子
- 使用合适的损失函数:对于分类问题,使用交叉熵损失配合Softmax或LogSoftmax,通常比使用均方误差损失效果更好,能缓解梯度饱和问题。


总结 📝
本节课中我们一起学习了深度学习的两个基石:随机梯度下降 和 反向传播。
我们从参数化模型和监督学习的基本框架出发,定义了需要最小化的损失函数。接着,我们探讨了如何使用基于梯度的方法进行优化,重点介绍了随机梯度下降及其变体小批量梯度下降的原理与优势。
然后,我们深入研究了传统神经网络的结构,理解了线性变换与非线性激活函数交替堆叠的本质。为了高效计算梯度,我们详细阐述了反向传播算法,其核心是利用链式法则将损失梯度从输出层逐层反向传播至输入层,并解释了如何将其视为在计算图上进行梯度传递的过程。
我们还具体分析了复制、求和、最大值、Softmax、Sigmoid等常见模块在反向传播中的行为。最后,我们总结了一系列至关重要的实践技巧,包括输入归一化、权重初始化、学习率调整、正则化方法等,这些技巧对于成功训练深度学习模型不可或缺。

掌握这些核心概念和技巧,是您进一步探索更复杂神经网络架构和应用的基础。

课程 P30:矩阵乘法、信号与卷积 🧮➡️📡➡️🌀
在本节课中,我们将学习矩阵乘法的不同视角,并将其应用于信号处理,最终引出卷积的概念。课程将从线性代数的回顾开始,逐步扩展到信号处理,最后通过Python代码进行实践。

线性代数回顾 📚
在神经网络中,我们通常可以这样表示一个隐藏层:
h = f(z)
其中,h 是隐藏层向量,f 是一个非线性激活函数,z 是输入的线性变换结果。
z 可以表示为矩阵 A 与输入向量 x 的乘积:
z = A x
假设 x 的维度是 n,z 的维度是 m,那么矩阵 A 的维度就是 m × n。这是因为矩阵的行数对应输出维度 m,列数对应输入维度 n。
我们可以将矩阵 A 展开为:
A = [ a11, a12, ..., a1n
a21, a22, ..., a2n
...
am1, am2, ..., amn ]
当矩阵 A 乘以列向量 x 时,通常的运算方式是“行乘以列”。结果向量 z 的每个元素 z_i 是矩阵 A 的第 i 行与向量 x 的点积。
因此,我们可以将矩阵 A 视为由行向量 a_i 组成:
A = [ a1; a2; ...; am ]
那么,乘法运算可以写为:
z = [ a1·x; a2·x; ...; am·x ]
每个 z_i 都是一个标量。
几何解释与模板匹配 🧩
现在,让我们深入理解每个元素 z_i = a_i · x 的含义。以二维空间为例(n=2),这个点积可以展开为:
z_i = a_i1 * x1 + a_i2 * x2
从几何角度看,点积 a_i · x 等于两个向量模长的乘积乘以它们之间夹角的余弦值:
a_i · x = ||a_i|| * ||x|| * cos(θ)
其中,θ 是向量 a_i 和 x 之间的夹角。
这个公式揭示了点积的核心意义:
- 当两个向量方向相同时(θ=0),cos(0)=1,点积值最大(正值)。
- 当两个向量垂直时(θ=90°),cos(90°)=0,点积为零。
- 当两个向量方向相反时(θ=180°),cos(180°)=-1,点积值最小(负值)。
因此,在矩阵乘法中,每一行向量 a_i 都可以看作一个“模板”或“核(kernel)”。 计算 a_i · x 的过程,实质上是在衡量输入向量 x 与模板 a_i 的匹配程度。这被称为模板匹配(Template Matching)。
列视角:线性组合 🔢
除了行视角,矩阵乘法还有另一个重要的解释:列视角。
回顾 z = A x 的运算,结果 z 也可以看作是矩阵 A 的各列向量的线性组合,组合系数就是输入向量 x 的各个元素。
具体来说,设矩阵 A 的列向量为 A1, A2, ..., An,那么:
z = x1 * A1 + x2 * A2 + ... + xn * An
这意味着,输出向量 z 是输入向量 x 的每个分量,对其对应的矩阵列向量进行缩放后,再相加的结果。
上一节我们回顾了矩阵乘法的两种核心视角:行视角(模板匹配)和列视角(线性组合)。接下来,我们将把这些概念应用到信号处理中。
将矩阵乘法应用于信号 📡
现在,我们考虑输入数据是信号的情况。假设我们有一个离散信号,表示为一个序列 x[0], x[1], ..., x[N-1]。
在传统的全连接层中,一个神经元(对应矩阵的一行)会与整个输入信号的所有点做点积。这要求矩阵 A 的列数 n 等于信号长度 N。
然而,对于像音频、图像这样的自然信号,相邻的数据点之间通常具有强相关性(例如,图像中相邻的像素颜色相似)。全连接层忽略了这种局部相关性,并且参数量巨大(m × N)。
为了更有效地处理信号,我们可以利用其局部特性。这就引出了局部连接的思想:让一个神经元(或滤波器)只与输入信号的一小段局部区域进行连接和计算。
从局部连接到卷积 🌀
假设我们设计一个小的“核(Kernel)” w,其长度为 K(例如 K=3)。我们让这个核在输入信号 x 上滑动。
在每一个位置 t,我们计算核 w 与信号局部片段 x[t : t+K-1] 的点积,得到一个输出值 y[t]:
y[t] = w[0]x[t] + w[1]x[t+1] + ... + w[K-1]*x[t+K-1]
这个操作就是一维离散卷积(Discrete Convolution) 的严格定义(在深度学习中通常指互相关)。
通过让同一个核 w 滑过整个信号,我们生成了输出信号 y。这个过程极大地减少了参数量(我们只需要学习 K 个参数,而不是 N 个),并且强制模型学习输入信号的局部特征。
卷积的本质是共享参数的局部模板匹配。 同一个核(模板)被重复用于检测输入信号中不同位置是否出现相同的局部模式。
扩展到多维与多个核 🧠
上述概念可以轻松扩展到多维信号(如图像,使用2D卷积)和使用多个核的情况。每个不同的核 w_i 学习检测一种不同的局部特征(如边缘、纹理)。所有核的输出堆叠起来,就形成了下一层的输入特征图。
上一节我们介绍了卷积如何作为共享参数的局部模板匹配来处理信号。现在,让我们通过代码来直观地理解这些操作。
使用 PyTorch 进行交互式实践 🐍
以下是使用 PyTorch 演示矩阵乘法和卷积操作的示例代码。
首先,我们模拟一个简单的矩阵乘法(全连接层):
import torch
# 定义输入信号 (长度为5)
x = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0])
print("输入信号 x:", x)
# 定义一个全连接层 (3个神经元,每个接收全部5个输入)
# 权重矩阵 A 的维度为 3x5
A = torch.tensor([[0.1, 0.2, 0.3, 0.4, 0.5],
[0.5, 0.4, 0.3, 0.2, 0.1],
[-0.1, 0.2, -0.3, 0.4, -0.5]])
print("\n权重矩阵 A (3个模板):\n", A)
# 矩阵乘法:模板匹配视角
z = torch.matmul(A, x) # 或者 A @ x
print("\n全连接层输出 z = A @ x:")
print("z[0] = A[0]·x =", z[0].item())
print("z[1] = A[1]·x =", z[1].item())
print("z[2] = A[2]·x =", z[2].item())
print("这衡量了信号 x 与每个模板行 A[i] 的匹配程度。")
接下来,我们演示卷积操作:
# 使用一维卷积实现局部连接和参数共享
print("\n" + "="*50)
print("一维卷积演示")
# 将输入 reshape 为适合卷积的格式: (batch_size, channels, length)
# 这里 batch_size=1, channel=1, signal_length=5
x_conv = x.view(1, 1, -1)
print("输入信号 x (卷积格式):", x_conv)
# 定义一个1D卷积核 (滤波器)
# 这里使用一个简单的边缘检测核 [-1, 0, 1]
conv_kernel = torch.tensor([[[-1.0, 0.0, 1.0]]]) # 格式: (out_channels, in_channels, kernel_size)
print("卷积核 w (边缘检测器):", conv_kernel)
# 执行卷积操作 (PyTorch 的 conv1d 实现互相关)
import torch.nn.functional as F
y = F.conv1d(x_conv, conv_kernel, padding=1) # padding=1 保持输出长度
print("\n卷积输出 y:")
print("原始信号 x:", x.tolist())
print("卷积核 w: [-1, 0, 1]")
print("输出 y (*似导数):", y.squeeze().tolist())
print("\n解释:")
print("y[0] ≈ -1*x[0] + 0*x[1] + 1*x[2] = ", -1*x[0] + 1*x[2])
print("这检测了信号在位置 t 附*的‘变化’或‘边缘’。")
print("同一个核 w 滑过整个信号,实现了参数共享。")
总结 🎯
在本节课中,我们一起学习了:
-
矩阵乘法的双重视角:
- 行视角(模板匹配):将矩阵的每一行视为一个模板,输出是输入向量与每个模板的匹配得分(点积)。
- 列视角(线性组合):将输出视为矩阵各列向量的线性组合,系数由输入向量提供。
-
信号处理与局部性:自然信号(如音频、图像)具有局部相关性。全连接层参数量大且忽略局部结构。
-
卷积的引出:为了解决上述问题,我们引入卷积操作。它本质上是共享参数的局部模板匹配。
- 使用一个小的核(Kernel) 作为局部模板。
- 核在输入信号上滑动,在每个局部位置计算点积,生成输出。
- 这显著减少了参数量,并让模型专注于学习有意义的局部特征。



- 实践验证:通过 PyTorch 代码,我们直观地对比了全连接层的全局模板匹配与卷积层的局部、参数共享的模板匹配。




核心在于理解:卷积是矩阵乘法在利用信号局部性和*移不变性特性后的一种高效、特化的形式。它是深度学习处理图像、语音等空间或时序数据的基石。
课程 P31:有监督与自监督迁移学习(使用 PyTorch Lightning)🚀
在本节课中,我们将学习迁移学习的概念,并分别在有监督和自监督的背景下,使用 PyTorch Lightning 和 PyTorch Lightning Bolts 库进行实践。迁移学习在数据不足或计算资源有限时非常有用,它能帮助我们利用预训练模型来加速新任务的训练过程。

概述 📋
迁移学习是指利用在一个任务上训练好的模型,来帮助解决另一个相关任务的技术。本节课将重点介绍两种迁移学习方式:有监督迁移学习和自监督迁移学习。我们将使用 PyTorch Lightning 来组织代码,并使用 PyTorch Lightning Bolts 库中提供的预训练模型和工具。
工具介绍 🛠️


在开始之前,我们先介绍两个核心工具:PyTorch Lightning 和 PyTorch Lightning Bolts。



PyTorch Lightning 是 PyTorch 的一个轻量级封装,旨在简化高性能研究代码的编写。它自动处理了多 GPU 训练、TPU 训练等复杂操作,让你能更专注于模型构建本身。


PyTorch Lightning Bolts 是一个研究工具包,它提供了许多最新的、经过测试和文档化的模型实现。当你开始一个新项目时,可以在这里寻找合适的基线模型,而无需花费数月时间从头实现。


如果你想了解更多关于这两个工具的信息,可以访问以下资源:
- PyTorch Lightning: https://www.pytorchlightning.ai
- PyTorch Lightning Bolts: 在 PyTorch Lightning 官网可以找到相关链接。
何时进行微调?🤔
在决定是否使用迁移学习时,你可以问自己几个问题。

以下是帮助你决策的关键因素:
- 数据量:如果你拥有大量数据,可能不需要微调预训练模型,可以直接从头训练。
- 时间和算力:即使数据量大,如果训练成本过高,使用预训练模型仍然是更好的选择。
- 数据分布匹配度:如果数据量小,你需要找到一个与你的数据分布相匹配的预训练模型。例如,在 ImageNet(包含自然物体图像)上训练的模型,可能不适用于医学 X 光图像分析。

迁移学习通常包含两部分:一个在其它任务上预训练的模型,以及你为了新任务而添加的顶层结构。然后你可以在自己的数据上对这个组合进行微调。

有监督迁移学习实践 🏁


上一节我们讨论了迁移学习的适用场景,本节中我们来看看最常见的情况:有监督迁移学习。我们假设你有一个小规模图像数据集和有限的算力。
我们将使用在 ImageNet 上预训练的 ResNet-50 模型。ImageNet 包含约1000个类别,每个类别约有1000张图像,总计约一百万张图像,这为模型提供了关于自然图像的强大先验知识。
首先,我们需要安装并导入必要的库,然后加载预训练的 ResNet-50 模型。
import torch
import torchvision
from torchvision import models, transforms
# 加载预训练的 ResNet-50 模型
model = models.resnet50(pretrained=True)



接下来,我们准备数据集。这里使用 CIFAR-10 数据集作为示例,它包含10个类别的32x32像素小图像。虽然尺寸与 ImageNet 不同,但由于卷积神经网络的特性,这通常可以工作。


# 定义数据转换
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 归一化到[-1, 1]
])

# 加载 CIFAR-10 数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32, shuffle=True)


由于 ResNet-50 原本是为 ImageNet 的1000个类别设计的,而 CIFAR-10 只有10个类别,因此我们需要修改模型的最后一层全连接层。



# 获取原模型最后一层输入特征数
num_ftrs = model.fc.in_features
# 替换全连接层,输出为10个类别
model.fc = torch.nn.Linear(num_ftrs, 10)


现在,如果我们直接用这个修改后的模型进行预测,效果会很差,因为新替换的全连接层是随机初始化的。因此,我们需要对这个模型进行微调。

微调有两种主要策略:
- 特征提取器:冻结预训练模型的所有层,只训练新添加的顶层(如我们刚替换的全连接层)。这相当于用预训练模型提取特征,然后用一个简单的分类器(如线性层、SVM)基于这些特征进行分类。
- 整体微调:解冻全部或部分预训练模型的层,与新层一起训练。通常,学习率需要设置得更低,以避免破坏预训练好的权重。

在本次实践中,我们将采用第二种策略,使用 PyTorch Lightning 来组织训练流程。

自监督迁移学习探索 🔬
上一节我们实践了有监督迁移学习,本节中我们来看看另一种前沿方法:自监督迁移学习。
与有监督学习需要大量标注数据不同,自监督学习让模型从无标签数据中自行学习有用的特征表示。这种表示可能比有监督学习获得的表示更具通用性,因为它不是针对特定分类任务优化的,因此在分割、检测等下游任务上可能迁移得更好。


目前有多种自监督学习方法,如 AMD、MoCo、CPC、SimCLR、BYOL 和 SwAV 等。其中 SwAV 是较新的方法。我们将使用 PyTorch Lightning Bolts 库中提供的 SwAV 预训练模型。
自监督迁移学习的流程与有监督类似:加载预训练模型,根据新任务修改输出层,然后进行微调。关键区别在于,预训练模型是通过自监督任务学习到的,其学到的特征可能对数据分布的变化更鲁棒。




总结 🎯





















本节课我们一起学习了迁移学习的核心概念与实践。





















我们首先了解了何时应该考虑使用迁移学习,关键因素包括数据量、计算资源以及预训练任务与目标任务的数据分布匹配度。





















接着,我们深入实践了有监督迁移学习,使用在 ImageNet 上预训练的 ResNet-50 模型,通过修改并微调最后一层,使其适应 CIFAR-10 数据集。我们介绍了两种微调策略:将预训练模型作为固定的特征提取器,或对整个模型进行微调。





















最后,我们探讨了自监督迁移学习这一新兴领域。自监督模型通过无标签数据学习通用特征,可能在下游任务中提供更好的迁移性能,并介绍了如何使用 PyTorch Lightning Bolts 库中的相关模型。





















希望本教程能帮助你理解并开始在实际项目中应用迁移学习技术。
P4:第2周 - 实验:训练神经网络 🧠

在本节课中,我们将学习如何训练一个神经网络来解决分类问题。我们将从理解问题开始,逐步构建网络架构,定义损失函数,并最终通过反向传播算法来优化网络参数。课程内容将结合理论解释和代码实践,确保初学者能够跟上。


概述与准备工作 📋
首先,我们有一个网站,您可以在我的GitHub仓库中找到链接。该网站包含了上一堂课的总结和之前的实验内容。您有责任在上课前仔细阅读这些总结。否则,如果我们花前15分钟回顾上次内容,那么留给新内容的时间将不足15分钟。周二的课程只有50分钟,我们将从一个问题开始。

从分类问题开始 🐱🐶

我想在猫和狗的图片之间进行分类。如果这是我的猫的图片,而我的狗的图片在附*,我们该如何首先将它们分开?
矩阵乘法的角色
矩阵乘法可以实现旋转、反射、缩放和剪切变换。标量之所以被称为“标量”,是因为它可以缩放向量。我们可以随时考虑一个矩阵,通过归一化使其行列式为1,然后实际上有一个标量在改变尺寸。我通常将矩阵视为旋转,所以我们是在任何维度的空间中对物体进行旋转。神经网络中的另一种操作是“挤压”。神经网络本质上就是旋转和挤压的重复组合。
构建神经网络架构 🏗️
上一节我们介绍了矩阵变换的基本概念,本节中我们来看看如何将这些概念组织成一个神经网络。

数据表示

我们的数据存在于*面上。例如,我们有一个包含三个分支的螺旋状数据集,每个点位于R²空间(即二维*面)。这些点带有颜色标签,代表三个不同的类别。您可以使用Matplotlib、Python和NumPy来绘制此图。
在数学上,我们有:
- 输入数据 X:一个矩阵,大小为
M x n,其中M是样本数量,n是特征维度(本例中 n=2)。 - 标签 C:最初,每个样本的标签是一个标量,例如1、2、3。但这种表示引入了无意义的顺序(例如,第1类并不比第2类“小”)。
为了避免顺序问题,我们使用 独热编码。对于有 K 个类别的问题,我们将每个标签 C_i 转换为一个长度为 K 的向量,其中只有对应类别索引的位置为1,其余为0。
公式:如果第 i 个样本属于第 k 类,则其标签向量 y_i 满足:y_i[k] = 1 且 ||y_i||_0 = 1。
将所有样本的独热编码标签堆叠起来,我们得到标签矩阵 Y,大小为 M x K。
网络结构
一个典型的全连接神经网络结构如下(自底向上绘制):
- 输入层(底部):接收数据
X。 - 隐藏层:对输入进行仿射变换(矩阵乘法
W1 * X + b1),然后应用一个逐元素的非线性激活函数F(如ReLU)。输出是隐藏向量H。 - 输出层:对隐藏层输出再进行一次仿射变换(
W2 * H + b2),然后应用另一个非线性函数G(对于分类,通常是Softmax)。输出是预测向量ŷ。

代码示意(伪代码):
H = F(W1 * X + b1) # 第一层:旋转 + 挤压
ŷ = G(W2 * H + b2) # 第二层:再次旋转 + 挤压(归一化)
因此,神经网络就是一系列“旋转”(仿射变换)和“挤压”(非线性激活)的堆叠。
激活函数与损失函数 ⚙️
上一节我们搭建了网络的结构,本节中我们来看看赋予网络“学习能力”的关键组件:激活函数和损失函数。
常见的非线性激活函数
以下是几种常用的激活函数:
- ReLU(修正线性单元):
f(x) = max(0, x)。它将所有负输入置零,保持正输入不变。 - Sigmoid:
σ(x) = 1 / (1 + exp(-x))。它将输入压缩到(0, 1)区间。 - Tanh(双曲正切):
tanh(x)。它是Sigmoid的缩放版,输出范围在(-1, 1)。 - Softmax:常用于输出层,将一组实数转换为概率分布。对于向量
z,其第i个分量为:softmax(z)_i = exp(z_i) / Σ_j exp(z_j)。
损失函数:交叉熵
对于分类任务,我们使用交叉熵损失(或称负对数似然)来衡量网络预测 ŷ 与真实标签 y 之间的差距。
公式:对于单个样本,损失为 L = -log(ŷ_c),其中 c 是该样本的真实类别索引。
解释:
- 如果网络对正确类别的预测概率
ŷ_c接*1,则-log(ŷ_c)接*0,惩罚很小。 - 如果网络预测概率
ŷ_c接*0,则-log(ŷ_c)会变得非常大,给予很大惩罚。 - 这迫使网络学习输出高置信度的正确预测。
整个训练集的总损失 J 是所有样本损失的*均值,它是网络所有参数(记作 Θ,包含所有权重 W 和偏置 b)的函数:J(Θ)。
训练网络:梯度下降与反向传播 📉


我们已经定义了网络和损失函数,本节中我们来看看如何通过优化损失函数来训练网络。
梯度下降
训练目标是找到一组参数 Θ,使损失 J(Θ) 最小化。我们从随机初始化参数 Θ₀ 开始。
- 计算当前参数下的损失
J(Θ₀)。 - 计算损失函数相对于每个参数的梯度
∇J(Θ₀)。梯度指向损失增长最快的方向。 - 我们希望损失降低,因此向梯度的反方向更新参数:
Θ_new = Θ_old - α * ∇J(Θ),其中α是学习率,控制步长。 - 重复步骤1-3,直到损失收敛。
反向传播





梯度下降的关键是计算梯度 ∇J(Θ)。对于深层网络,手动计算导数非常复杂。反向传播算法高效地完成了这项工作。其核心是链式法则:
- 前向传播:输入数据通过网络,计算每一层的输出,直到最终损失。
- 反向传播:从损失开始,反向计算损失对每一层参数的梯度。
- 先计算损失对输出层输入的梯度。
- 将这个梯度作为“误差信号”,反向穿过网络,利用链式法则逐层计算损失对每一层权重和输入的梯度。





核心思想:反向传播将最终损失的“责任”分配给网络中的每一个参数。



代码实践与观察 🖥️





理论部分已经介绍完毕,本节中我们通过代码实践来观察神经网络的训练过程和行为。





实验设置






我们使用Python和PyTorch框架。首先,我们生成或加载螺旋数据集(三个类别)。然后,我们定义不同的网络模型进行对比。






线性模型(基准)




我们首先训练一个简单的线性模型(只有一层仿射变换,无隐藏层)。代码如下所示:





import torch.nn as nn
model_linear = nn.Sequential(
nn.Linear(2, 3) # 从2维输入直接映射到3维输出(三个类别)
)
结果:线性模型只能产生直线(或超*面)作为决策边界。对于螺旋状这种非线性可分的数据,它的准确率很低(大约33%,即随机猜测水*),损失下降有限。





简单的神经网络



接下来,我们添加一个隐藏层和非线性激活函数(如ReLU)。




model_nn = nn.Sequential(
nn.Linear(2, 100), # 隐藏层:2维 -> 100维
nn.ReLU(), # 非线性激活
nn.Linear(100, 3) # 输出层:100维 -> 3维
)
训练过程:
- 定义优化器(如SGD或Adam)和损失函数(交叉熵损失)。
- 循环以下步骤:
- 前向传播计算预测
ŷ。 - 计算损失
loss = criterion(ŷ, y_true)。 - 优化器梯度清零:
optimizer.zero_grad()。 - 反向传播计算梯度:
loss.backward()。 - 更新参数:
optimizer.step()。
- 前向传播计算预测





结果:带有非线性激活的神经网络能够学习复杂的非线性决策边界,准确率显著提升,损失也能有效下降。




不同激活函数的比较



我们可以比较使用 ReLU 和 Tanh 的网络:
- ReLU:输出是分段线性的。在训练数据范围之外,网络会保持最后一个线性段的斜率进行外推。
- Tanh:输出是*滑的。在训练数据范围之外,函数会饱和(趋*于-1或1)。


这种差异在回归任务中尤其重要,因为它影响了模型对未知区域预测的“信心”或不确定性。一种估计不确定性的方法是训练多个不同初始化的网络,并计算它们预测的方差。


总结与要点 🎯


本节课中我们一起学习了如何训练一个神经网络:
- 问题定义:将分类问题转化为数据(
X)和独热编码标签(Y)。 - 网络架构:神经网络是仿射变换(旋转)和非线性激活(挤压)的堆叠。
- 激活函数:如ReLU、Sigmoid、Tanh、Softmax,它们引入了非线性,使网络能够拟合复杂函数。
- 损失函数:对于分类,使用交叉熵损失来衡量预测与真实的差距。
- 训练原理:通过梯度下降法最小化损失。反向传播算法高效计算网络所有参数的梯度。
- 代码实践:我们观察到线性模型对非线性数据失效,而简单的神经网络就能取得很好效果。不同激活函数会导致模型在训练区域内外有不同的行为。


关键启示:神经网络的强大能力源于其多层非线性变换的组合,使其能够从数据中学习极其复杂的模式和表示。

卷积神经网络教程 P5:第3周 - 卷积神经网络 🧠

在本节课中,我们将要学习卷积神经网络的基本概念、工作原理及其在模式识别中的应用。我们将从网络结构的基础开始,逐步深入到卷积操作、权重共享、池化等核心机制,并探讨其背后的生物学灵感。
网络结构与维度
上一节我们介绍了神经网络的基本概念,本节中我们来看看一个具体网络的层与维度。
在这种情况下,我们的网络通常在左侧有一个输入。

您在底部或左侧输入的内容在我的幻灯片中为粉红色。


然后我们有多少个激活多少个隐藏层。

您算出那里有四个隐藏层,因此整个网络有多少层。

在这里有六个权利,因为我们有四个隐藏。


再加上一个输入加上一个输出层,所以在这种情况下,我每个人有两个神经元。

正确的层,这意味着什么?矩阵的维数是多少。


我们在这里使用2 x 2,那么2 x 2矩阵的作用是什么。

你知道这个问题的答案吗?


分享和反思梦幻般的权利,因此我们限制了网络的执行。
如果允许FIR,我们在飞机上第一次看到的所有操作。
我们可以将隐藏层设为一百个神经元,哇,好吧,我们可以轻松地。
会影响我们的幻想,现在我们正在看电影是什么,我看到。
太棒了,曼达洛人现在太酷了,好吧,好这堂课有多好。
甚至被记录好了,我们不知道好了,请给我几秒钟,所以我们去这里了。

听好吧,所以我们从这个网络开始,我们有了这个。

中间层,我们强迫它们是二维的,这样所有。

转换被强制在飞机上,所以这就是网络。
对我们的飞机进行操作,将其折叠在这些文件夹中的特定区域上。
折叠非常非常突然。

因为所有转换都正确执行2d层,所以这。


培训真的让我付出了很多努力,因为优化是。

每当我将一百个神经元放入隐藏层中时。


培训非常容易,这真的很费力。

你必须告诉我为什么现在还不知道答案会更好吗。



知道期中答案,这样您就可以注意哪些问题了。

到电表右边,这是网络的最终输出,也是。

这些层是2D层,因此我的最后一层没有非线性。

是最终的分类区域,所以让我们看一下这是哪一层。

第一层仿射变换,因此看起来像是3D旋转,但。
这是不对的,只是旋转反射缩放和剪切。
然后这是什么部分啊,现在正在发生什么。
您会看到我们所拥有的。
就像视频部分正在扼杀网络的所有负面影响一样。
对不起。
这个空间的所有负面影响是第二个仿射。
转换,然后在这里再次应用areevo。
您会看到所有负子空间已被删除,它们已设置为。
零,那么我们继续进行第三个仿射变换。
很多次之后,您将拥有现在的重做层。
杀死所有三个象限之一,而只有一个象限。
每次都生存下来,然后我们进行第四次仿射变换。
顺其自然。
因为再次考虑到我们限制了所有变换。
要生活在这个空间中,它确实需要伸展,你知道要使用所有。
它可以再次写入的力量,这是倒数第二,然后是最后一个。
转换是最后一个,然后我们最终线性地到达。
终于在这里我们可以看到每个可分离区域。

在每个组件中分开,所以我们有。
旋转我们现在像风一样挤压,然后我们有了旋转反射。
因为行列式为负1,然后我们又有了最终的偏差。
再次使Areeda整流线性单元的正部分旋转。
翻转,因为我们旋转时负数又是负1行列式缩放。
再思考一遍,然后最后的偏见,这是第二次罚款。
转变,那么我们在这里又是积极的一部分,我们是第三层。
或旋转反射缩放,然后我们就可以进行SVD分解了。
应该知道您应该知道的权利,然后最后是翻译。
再来一次第三次谜语,然后我们进行第四层或旋转反射。
因为行列式为负,再次假设另一个旋转图标a。
经过反射和偏置后的小旋转终于读了哦,然后我们有了最后一个。
第五层,所以旋转缩放我们没有反射,因为。
行列式在这种情况下再加上一次反射,因为。
终端是负的,最后是最终的偏见,所以这是。

该网络是由几层的厚度构成的。

每层只有两个神经元的神经元正在执行。
分类任务,所有这些转换都被限制在。
可以住在飞机上。
所以这真的很难训练。
你能弄清楚为什么真的很难训练发生了什么。
如果我对五层之一的偏见使我的观点偏离了。
恰好在右上象限中,如果您将拥有四个偏差之一。
我最初的位置离右上象限不远,那么视频将是。
完全杀死一切,一切都崩溃为零好。
所以你不能做更多或任何事情。
因此,如果您只是将其设置为。
比不局限于每个神经元要胖一点。
的隐藏层,那么它更容易训练,或者您可以将。
两者很好,所以您可以拥有一个。
脂肪较少的网络,但是您会有一些隐藏层。
所以这几乎是一个问题,所以肥胖是你有多少神经元。

助手隐藏层没问题,所以问题是我们如何确定。
网络的结构或配置正确如何设计网络。
答案将是N将要传授的内容。
这个学期是正确的,所以保持保持保持它就像保持你的注意力高。
因为你知道那就是我们要在这里教的东西,这是一个好问题。
对,没有像许多实验这样的数学规则。
经验证据。
您知道很多人尝试不同的配置。
我们发现了一些实际上可以正常工作的东西,我们将再次成为。
在以下课程中涵盖这些体系结构的其他问题。
害羞不行,所以我想我们可以切换第二部分的类型。

上课好,所以今天我们要谈论商业网。

因此,我将从与商业网有关的内容入手,而不仅仅是。
这是转换神经网络参数的想法,所以在这里我们。
有一个我们以前看过的图,除了稍微扭一下。
我们在这里看到的是,我们知道X和WW的G是参数。
X是您知道的输入。
可以对输出进行预测,然后。
到成本函数中,我们之前已经看到过,但是这里的转折是。
权向量而不是要优化的参数是。
实际上它本身可能是其他一些函数的输出参数。
如果此函数不是参数化函数。
或者是分支明智函数。
但唯一的输入是您可以确定的另一个参数。
我们在这里所做的是使该神经网络的权重成为。
有人通过函数列出了一些基本参数。
您很快就意识到,如果您回来,核心小组就可以在那里工作。
通过G函数传播渐变。

以获取渐变。
关于白色参数实现的任何目标函数。
可以在此处通过H函数继续传播以获取具有。
尊重你。
所以最终你会知道传播这样的事情。
在审核时,您要乘以。
关于参数的目标函数,然后由。
相对于其自身参数的H函数。
这样您就可以得到产品。
的两个雅各布人中的一个,这就是你从后面传播你得到的。
不必为此而在pi火炬中做任何事情。
您定义网络,这是发生的旧日期。
现在当然W是函数U的函数U。

W将是U的变化乘以H的雅可比矩阵。
转置,所以这是您在此处进行有效更改的一种方式。
在W中,您无需更新W而实际上在约会。
您就是OData。
乘以H的雅可比行列式,你知道我们在这里有移调,不是。
与之相反。
存在一个*方矩阵,该矩阵为nw x n W,即。
W的维数*方好,所以这里的矩阵。
行,因为W具有分量,则列数是。
U的组成部分,然后这个家伙当然反过来了。
所以它是n乘以nW,所以当你可能使产品做那些。
两个矩阵,您将获得NW采访矩阵,然后相乘。
通过这个NW向量得到一个NW向量,这是您需要的。
可以确定权重。
所以这是转换。
参数空间,您知道可以使用此方法的多种方式。
使用它的特定方式是当H称为a时,您知道我们在说什么。
关于上周,这是一个白色的连接器,所以想象一下。
H所做的是,它占用U的一个分量并将其复制多次。
这样您就可以在G函数上复制相同的值和相同的权重。
G函数我们多次使用相同的值。
所以说这将。
看起来像这样,让我们假设您是二维u1 u2,然后W是四个。
维度,但w1和w2等于u1且W 3 w 4等于u2,所以。
基本上,您只有两个自由参数,并且在更改一个时。
您的组件可以非常简单地同时更改W的两个组件。
方式,这被称为权重共享,当两个权重被强制为。
等于他们实际上负担得起,它们实际上等于瞬时参数。
它既控制着共享的地方,又是很多东西的基础。
您知道商业网等等的想法,但是但是,您可以。
认为这是U的H的非常非常简单的形式,因此您无需再做。
从某种意义上说,什么都可以,只要您拥有共享。
明确地带有一个在回程中进行白色连接的模块。
当成分向后传播时,将梯度相加,因此。
例如。
某些成本函数相对于u 1的梯度将为。
梯度的总和,以使关于W 1和W 2的消耗和。
类似地,关于u 2的梯度将是。
关于W 3和W 4好的,这只是通过传播的效果。
两个白色连接器,好的,这是此参数转换的更一般的视图。
这里有些人称超级网络,所以超级网络是一个网络。

其中一个网络的权重被计算为另一个网络的输出。

网络好,所以您有一个网络H来查看它自己的输入。
参数U并计算第二个网络的权重,所以。
这样做的好处是这些值为其命名,这个想法很古老。
回到80年代,人们使用所谓的乘法互动或。
具有信号PI单元的三向网络,它们基本上就是这个主意。
可能是它稍微有点笼统的概括。
动态地在X的G中动态定义的函数。
W,因为W实际上是输入和其他一些参数的复杂函数。
当您在做什么时,这是特别有趣的架构。
您对X所做的事情正在以某种方式对其进行转换,因此您可以想到。
W是该转换的参数,因此Y将是。
X和XI的变换版本基本上意味着函数H。
计算该转换还可以,但欢迎在几周后回到该转换。
想要提及这一点,因为它基本上是对。
这项权利,您只需再多一根从X到H的导线,这就是您的方式。
获得那些超级网络,好的。
所以我们显示的是您知道可以有一个参数的想法。
在另一个网络中控制多个多个项目的50个参数。
有用的原因是。
如果您想检测输入中的主题,并且想要。
不管同伴在哪里都可以检测到这个图案,所以假设您有一个。
输入序列,但可能会很酷。
假设这种情况是矢量的序列序列。
有一个网络,需要三个连续三个真空的集合。
对X和W的网络G进行向量化,并尝试检测特定的基序。
这三个向量中的一个,也许这是我不知道的功耗。
电力消耗。
有时您知道您可能想要成为。
能够检测到像斑点或趋势之类的东西,也许是你。
知道某种时间序列的金融工具,也许是。
语音信号,并且您要检测包含三种声音的特定声音。
定义该语音的音频内容种类的向量。
信号。
因此您希望能够检测到是否是语音信号。
在语音识别过程中,您需要检测特定的Sonya。
可能想检测出您知道元音P正确的声音P。
无论它出现在序列中的何处,您都希望您知道一些检测器。
发出声音P时触发。
因此它应该具有。
探测器,您可以在右侧滑过,无论此图案在哪里。
发生检测到,所以您需要的是一些太阳网络并确定优先级。
您知道的功能您可以拥有该功能的多个副本。
应用于输入的各个区域,它们都具有相同的权重,但是。
您想训练整个系统并训练到n,例如,让我们说。
谈论在这里开始更复杂的场景,让我们看看。

要发音的关键字,以便系统收听声音和。
想要检测某个特定关键字的唤醒词是否一直。
发音正确,所以这是Alexa正确的。
而您说Alexa并退出会唤醒它。
发出正确的声音,所以您想要拥有的是。
某种网络在声音上占据一扇窗户。
然后不断。

您知道在后台进行某种检测,但是您希望能够。

检测您是否知道声音在正在播放的帧中的任何位置。
看了谁的话我应该说,这样你就可以拥有一个网络。


这样,您知道复制的探测器都共享。
相同的权重,然后输出,您知道分数是否。
已经检测到某些东西可以进入最大功能了,那就是。
输出以及您对这样的系统进行训练的方式,您知道您会遇到很多问题。
样本中的一些例子是关键词已经发音和一堆。
带有关键字的音频样本未发音,然后训练2。
当技术规模在此框架中某处出现时,类分类器将打开。
当不是,但没有人告诉您单词lxl在。
可以对系统进行训练的窗口,因为对于。
劳动者喜欢看音频信号,然后很好地告诉您。
这是。
这个词是Alexa被发音,他们唯一知道的是你。
在几秒钟内知道单词已经发音。
可以的某处,所以您想应用这样的网络。
复制的检测器您不知道确切的位置,但是您需要运行。
这个最大值。
您想训练系统以知道自己想返回。
向其传播渐变,以便它学会检测您是否知道Alexa。
醒来的单词出现了。
所以发生的就是你有多个。
在此网络的此示例中,复制了五个副本,并且它们全部。
共享相同的权重。
因为只有一个权重向量正在发送它。
值是同一网络的五个不同实例,因此我们向后传播。
通过传播到网络的五个副本。
您将获得五个。
弧度,因此这些梯度会累加成参数。
在饼图和其他深度运行框架中实现的奇怪方式。
这是在单个参数中完成此累加或梯度。
隐含地,这是为什么要进行反向项目或。
您必须将梯度零添加,因为其中存在一些隐式。
当您进行反向传播时可以累积渐变,所以这是另一个。
这种情况很有用,这才是真正的动力。
首先是条件网背后的问题。
训练系统识别形状与位置是否独立。
形状是否发生以及输入中是否存在该形状的变形。
所以这是一种非常简单的卷积网络。
用手训练不好,是手工设计的。
明确地将场景与基因区分开来,所以您可以在。
输入图像,您知道分辨率很低,这个方程是什么。
由此可见,C的点数为n。
结束,您可以想象为此设计一个探测器,而这些探测器。
拐角,所以如果您有端点检测器或检测到。
线段的末端和角落检测器。
这是C,这是D。
无论您在何处结束。
它是一个C,所以这里有一个超过C的示例,您使用了第一个检测器,因此。
黑色和白色的小东西,他会在顶部吗?
这是它检测段的结尾以及这种方式。
这里代表的是这里的黑色像素,所以认为这是一些。
样板好吧,您要使用该样板,您要。
在输入图像上滑动它,然后将模板与。

放在下方的小图像。
是否与您的方式相匹配。
将要确定它们是否匹配是您要做的。
产品,因此您将把那些以白色像素表示的东西视为加值。
一或减一说加格雷格减一减白代表你会。
将这些像素也视为黑色加一,白色减一。
当您使用该模板计算小窗口的点积时。
它们是相似的,您将获得很大的正值。
你会得到一个零或负值或更小的值。
所以您在这里带上那个小探测器,然后用。
我们知道的第二个窗口的第一个窗口等您每移动一个像素。
每个位置的时间。
您会回想结果,而您得到的是。
这个权利,所以这里是灰度表示。
匹配,实际上是由形成的向量之间的点积。
这些值以及vac和对应位置上的补丁。
输入,因此此处的图片与该图片的尺寸大致相同-边框。
效果,并且您会看到有一个每当输出黑暗时会有一个。
匹配,因此当您在此处看到匹配项时,因为此终结点检测器在此处。
匹配您知道的端点,您在此处在底部看到了某种匹配。
而另一种价值观并不那么黑暗。
如果将阈值设置为阈值,则现在不那么想要。
如果小于零,则输出为加一,如果大约为特殊零。
您可以将这些地图正确地设置到该阈值,但是您得到了什么。
你知道这个小家伙在两个终点都检测到了比赛。
看好了。
所以现在如果你拿这张地图,你会看到我总结一下,只添加所有。
您获得的正数是通过阈值,即。
您的C探测器不是一个很好的C探测器它不是一个很好的探测器。
除了C的那些特定示例。
也许这些将起作用,对于D来说。
现在就足够了。
与这里的其他检测器类似,旨在检测D的角。
对,所以这个家伙在这里,当您在输入上滑动时,此检测器将。
将检测到左上角,而那个家伙将检测到右下角。
一旦达到阈值,您将获得这两张地图的拐角处。
检测到,然后您可以将其加总,检测器将立即打开。
您在这里看到的是一个很好的例子,因为现在检测到。
是*移不变性,所以如果我在这里取相同的输入D并移了一个。
几个像素,我再次运行该检测器,它将。
在这里的任何地方都可以检测到主题,输出将被转移,所以这是。
称为等价转移,因此该网络的输出等于。
移位。
这意味着如果我移位输入,输出将移位,但是。
否则不变,等价不变性就是。
将其转移到输出中将完全不变,但在这里。
修改它只是进入了与输入相同的方式,所以如果我总结一下。
在未来的Mads中激活活动,无论在哪里。
他们发生了,如果我只是这样,我的ID检测器仍将继续激活。
计算总和,所以这是一种手工制作的模式识别器。
它使用。
用户本地特征检测器,然后总结他们的活动以及。
你得到的是不变的检测,好吧。
这实际上是一种非常经典的方法。
建立某些类型的模式识别系统。
年,但这里的花招当然很重要,当然很有趣。
将是要学习那些您知道的模板,我们可以知道。
我们将其视为一个神经网络,然后向其传播,然后学习。
模板作为神经网络的权重,您毕竟知道我们正在使用它们来做。
该产品是加权总和,




课程 P6:第3周 - 实践课 - 自然信号特性与卷积神经网络 🧠
在本节课中,我们将学习自然信号(如图像、音频)的核心特性,并理解卷积神经网络(CNN)如何利用这些特性来高效地处理数据。我们将从信号的基本属性开始,逐步过渡到CNN的设计原理和实际应用。
自然信号的三个核心特性 📡

上一节我们介绍了课程的主题,本节中我们来看看什么是自然信号以及它们的关键特性。自然信号,如声音和图像,通常表现出三种主要属性:*稳性、局部性和组合性。
*稳性

*稳性意味着信号中相同的模式会在一维(如时间)或二维(如空间)上重复出现。例如,一段音频中的特定音调或一张图像中的纹理可能会在信号的不同位置反复出现。
局部性

局部性是指信号中某一点的信息主要与其邻*点的信息相关,而与远处点的信息关系不大。例如,图像中一个像素的颜色,更可能与其周围像素的颜色相似,而不是与图像另一端的像素颜色相关。
组合性

组合性是指复杂的结构由更简单、更小的部分组合而成。例如,一张人脸图像可以由眼睛、鼻子、嘴巴等局部特征组合而成;一个句子可以由单词组合而成。
从全连接网络到卷积神经网络 🔄


上一节我们了解了自然信号的特性,本节中我们来看看传统的全连接神经网络在处理这类信号时面临的问题,以及卷积神经网络如何提供解决方案。
在全连接网络中,每个神经元都与上一层的所有神经元相连。对于一个像图像这样的高维输入(例如 256x256 像素),这意味着需要学习海量的连接权重,计算成本极高,且容易过拟合。
然而,根据自然信号的局部性特性,一个神经元其实无需关注整个输入,只需关注输入的一个小局部区域即可。因此,我们可以删除那些连接遥远区域的权重,使网络连接变得稀疏。这大大减少了需要计算的参数数量。
根据自然信号的*稳性特性,相同的模式可能出现在输入的不同区域。因此,我们无需为每个局部区域都学习一套独立的权重。相反,我们可以让网络的不同部分共享同一套权重(即卷积核)。这意味着我们只需学习一小部分参数,就能让这些参数在输入的所有位置上检测相同的模式。
结合稀疏连接和参数共享,就构成了卷积操作的核心思想。这使得CNN具有以下优势:
- 参数效率高:需要训练的参数更少。
- 训练更快,泛化更好:由于参数共享,用于训练每个权重的数据更多。
- *移不变性:无论模式出现在输入中的哪个位置,都能被相同的卷积核检测到。
卷积网络的实际架构与操作 🏗️
上一节我们介绍了卷积的核心思想,本节中我们来看看卷积神经网络在实际中是如何构建和工作的。
一个典型的用于图像处理的CNN架构会堆叠多个卷积层。随着网络层次的加深,每一层“看到”的输入范围(即感受野)会越来越大,从而能够组合低层特征,形成更高层、更抽象的特征表示。
以下是CNN中常见的组件:
- 卷积层:执行核心的卷积操作。其关键超参数包括卷积核大小(如3x3)、步长和填充。
- 填充:在输入边缘添加零值,以控制输出特征图的空间尺寸。
- 为什么常用奇数大小的卷积核?:奇数核(如3x3, 5x5)有明确的中心点,便于对称填充,以保持空间尺寸。
- 非线性激活函数:如ReLU,被添加在卷积层之后,为网络引入非线性,使其能够学习更复杂的模式。
- 池化层:用于对特征图进行下采样,减少空间尺寸和计算量,同时提供一定的*移不变性。常见操作有最大池化和*均池化。
- 归一化层:如批归一化,用于稳定和加速训练过程。
- 残差连接:允许信息绕过某些层直接向前传播,有助于缓解深层网络中的梯度消失问题,使训练极深的网络成为可能。
在整个网络中,信息流可以概括为:将空间信息(图像的高度和宽度)逐步转化为特征信息(通道的深度)。早期的层关注低级特征(如边缘、纹理),后期的层则组合这些特征形成高级概念(如物体部件)。
实践对比:全连接网络 vs 卷积神经网络 ⚖️
理论需要实践验证。本节中我们通过一个简单的实验来直观对比两种网络架构的性能差异。


我们使用MNIST手写数字数据集进行实验,并确保两种网络(全连接网络和卷积神经网络)具有相同数量级的参数。
-
正常图像训练:
- 在全连接网络上,模型需要学习图像中任意两个像素点之间可能的关系,其中很多长程依赖是冗余的。
- 在卷积神经网络上,模型专注于学习局部像素之间的关系。
- 实验结果:两者性能相*(例如,全连接网络达到87%准确率),但CNN的参数利用效率更高。
-
像素打乱的图像训练:
- 我们对所有输入图像的像素进行相同的随机打乱,但保持标签不变。这彻底破坏了图像的空间局部性和*稳性。
- 再次用两种网络进行训练。
- 实验结果:全连接网络的性能下降不多,因为它原本就学习所有像素对之间的关系,打乱只是改变了像素的索引顺序。而卷积神经网络的性能显著下降,因为它所依赖的局部空间模式已被破坏。




这个实验清晰地表明:卷积神经网络的有效性严重依赖于输入数据具有局部性和*稳性。当这些先验假设成立时,CNN是高效且强大的工具;当这些假设被破坏时,其优势将不复存在。




总结 📝



本节课我们一起学习了自然信号的三个核心特性(*稳性、局部性、组合性),并深入探讨了卷积神经网络如何利用稀疏连接和参数共享来高效处理具备这些特性的数据。我们还了解了CNN的标准架构组件,并通过实践对比,验证了CNN的设计与数据特性之间的紧密联系。理解这些基本原理,是有效应用和设计卷积神经网络的关键。

课程P7:第四周实践课 - 聆听卷积 🔊🎵


在本节课中,我们将学习卷积运算的直观理解,并将其应用于音频信号处理。我们将从线性代数的基本概念回顾开始,逐步过渡到卷积神经网络(CNN)的核心思想,并通过一个实际的音频分析例子来展示卷积如何帮助我们识别信号中的模式。
线性代数回顾 📐
上一节我们介绍了神经网络的基本结构。本节中,我们来看看构成这些网络基础的线性代数运算。
一个简单的神经网络隐藏层可以表示为:
h = f(z)
其中,z 是线性(仿射)变换的输出。我们可以将 z 写为矩阵乘法:
z = A * x
在这里,x 是一个属于 R^n 的输入向量,A 是一个 m x n 的矩阵。这个矩阵乘法的输出 z 是一个属于 R^m 的向量。

理解矩阵乘法
我们可以用两种方式来理解矩阵 A 乘以向量 x。
第一种方式:点积视角
矩阵 A 的每一行与输入向量 x 进行点积运算。具体来说,输出的第 i 个元素 z_i 是 A 的第 i 行向量 a_i^T 与 x 的点积:
z_i = a_i^T · x = ||a_i|| * ||x|| * cos(θ)
其中 θ 是向量 a_i 和 x 之间的夹角。这意味着,点积本质上衡量了两个向量的对齐程度。当两个向量方向相同时(夹角为0),余弦值为1,点积最大;方向相反时(夹角为180°),余弦值为-1,点积最小(负值最大)。在神经网络中,经过激活函数(如ReLU)后,我们只关心正向的匹配。
第二种方式:列组合视角
我们也可以将矩阵乘法视为矩阵各列的加权和:
z = A[:,1] * x_1 + A[:,2] * x_2 + ... + A[:,n] * x_n
这里,输出 z 是矩阵 A 的每一列,以输入向量 x 的对应元素为权重,进行线性组合的结果。这可以理解为用一组基向量(矩阵的列)来表示输入信号 x。
从全连接到卷积 🔄

理解了全连接层(矩阵乘法)后,我们来看看如何将其扩展到卷积运算。

假设我们有一个很长的输入信号(例如一段音频),使用全连接层意味着需要一个非常宽的巨大矩阵,参数数量极多。然而,对于像音频、图像这样的数据,我们通常有两个合理的假设:
- 局部性:信号中有意义的模式通常只存在于局部区域(例如,音频中某个音符的特征只由一小段波形决定)。
- *稳性:相同的模式可能会在信号的不同位置重复出现(例如,同一个音符可能在乐曲中多次响起)。
基于这些假设,我们可以对全连接矩阵施加约束,从而得到卷积层:
- 稀疏性:每个输出神经元只与输入的一小部分局部区域连接,而不是全部输入。这在矩阵中表现为大量的零。
- 参数共享:我们在输入的不同位置使用相同的权重集(即卷积核)。这在矩阵中表现为重复的、相同的权重块。
因此,卷积运算可以看作是一个具有稀疏性和参数共享特性的特殊矩阵乘法。它极大地减少了参数量,并更有效地捕捉数据的局部特征。
实战:用卷积聆听音频 🎹

现在,让我们将理论付诸实践,看看卷积如何帮助我们分析音频信号。



我们的目标是:给定一段混合了多个音符的音频,识别出在特定时间播放了哪些音符。



1. 音频信号与频谱图


一段音频波形本身很难直接解读其包含的音高(频率)。为了分析频率成分,我们使用频谱图。频谱图是通过短时傅里叶变换得到的,它显示了信号频率随时间的变化情况,是一种“局部化的傅里叶变换”。




在我们的例子中,我们生成了一段包含四个不同频率(例如800Hz, 1200Hz, 1600Hz等)音符的音频。在频谱图上,这些音符会表现为在不同时间出现的亮线(能量峰值)。



2. 卷积作为模式匹配器





如何自动检测这些音符呢?我们可以将每个想要检测的音符(例如一个纯音)预先制作成一小段音频模板,作为我们的卷积核。



然后,我们使用每个卷积核在整个输入音频信号上进行卷积操作。回顾点积的视角:卷积操作计算了输入信号的每个局部片段与卷积核(模板)的余弦相似度。

- 当输入信号的某个局部片段与卷积核代表的音符高度匹配时,它们的波形对齐得很好,点积(卷积输出)会产生一个很大的正值。
- 当不匹配时,输出值则很小。

因此,卷积的输出信号中的峰值,就对应了输入音频中与卷积核匹配的音符出现的位置。




3. 结果可视化



通过用多个不同频率的卷积核(对应不同音符)对输入音频进行卷积,我们可以得到一系列输出信号。将这些结果与原始音频的频谱图并列显示,可以清晰地看到:
- 每个卷积核的输出峰值,精确地对应了频谱图中该频率成分出现的时间段。
- 例如,对应1600Hz的卷积核,只会在音频播放1600Hz音符的时间段产生高响应。
这直观地证明了,卷积层能够有效地在时域信号中检测其训练过的、或预设的特定模式。




信号的维度 📊



在结束之前,我们简要回顾一下不同类型数据的维度,这对于理解卷积神经网络的结构很重要。



以下是不同信号的维度定义:




- 一维信号:例如音频。定义域是时间索引的集合(如
{1, 2, ..., T})。每个时间点对应一个(单声道)或多个(立体声)通道的幅值。 - 二维信号:例如图像。定义域是高度和宽度索引的笛卡尔积(如
{1,...,H} x {1,...,W})。每个像素位置对应一个(灰度)或多个(RGB彩色)通道的强度值。 - 三维信号:例如视频或高光谱图像。定义域可以是
空间高度 x 空间宽度 x 时间,或者是空间高度 x 空间宽度 x 光谱通道。这增加了时间或光谱维度。
卷积操作可以相应地设计为一维卷积(用于音频、文本)、二维卷积(用于图像)或三维卷积(用于视频)。
总结 🎯
本节课中我们一起学习了:
- 线性代数中矩阵乘法的两种直观解释:点积对齐度量的行视角和基向量线性组合的列视角。
- 通过引入局部性和*稳性假设,将全连接层推广到卷积层,其本质是稀疏且参数共享的矩阵乘法。
- 通过一个音频分析实例,展示了卷积如何作为强大的模式匹配工具,通过计算局部片段与卷积核的相似度来检测信号中的特定特征。
- 区分了不同数据类型的维度(一维、二维、三维),这决定了卷积运算的应用形式。

卷积是深度学习中处理具有局部相关性和*移不变性数据的核心操作,理解其背后的直观原理和数学本质至关重要。



课程P8:优化方法在神经网络中的应用 🚀
在本节课中,我们将学习优化方法,特别是梯度下降及其变体,以及它们在训练神经网络中的核心作用。优化是机器学习的基石,它帮助我们调整网络参数以最小化损失函数,从而提升模型性能。
概述 📋
优化是机器学习和某些方面的核心。今天要谈论的方法将每天都用在您的角色中,潜在地作为应用科学家、研究科学家或数据科学家。我将专注于这些方法的应用,特别是而不是背后的理论,部分原因是我们还没有完全理解所有这些方法。所以我来这里并说这就是它为什么起作用的原因,我会简化一些事情,但是我可以告诉您如何使用它们,我们如何知道它们在某些情况下会起作用,以及最好的方法是用来训练您的神经网络。
梯度下降法 📉


上一节我们介绍了优化的核心概念,本节中我们来看看最基础的优化方法——梯度下降法。
向您介绍优化主题,我需要从世界上最差的梯度下降方法开始,我将在稍后解释。这是最糟糕的方法,但首先我们将使用最通用的方法。
现在制定优化,您将要考虑的问题将会具有比这更多的结构,但是从符号上来说这非常有用,以这种方式开始。所以我们现在讨论的是函数f。优化程序的属性将在f上采用其他结构,但在我们的神经网络中练习结构基本上不遵循假设人们没有做出任何假设。
练习,我将从通用F开始,即使我们已经将其视为连续且可区分的。自从神经网络进入错误假设的领域,如今,大多数人在实践中都无法区分,而是有一个等效的子差速器,您基本上可以将其插入所有这些公式,如果您不顾一切,就没有理论来支持它,应该可以工作。
所以这里显示了梯度下降的方法,这是一个迭代方法。因此您从点k等于零开始,并在每一步都更新点。这里我们将使用W来表示当前的迭代,是神经网络点的标准命名法,将是一些重量的大集合,每层一个重量张量,但符号我们将整个东西压缩成一个向量,您可以想像一下,通过将所有向量重塑为所有张量两个向量并将它们连接在一起。
这种方法是我们要做的非常简单,就是遵循负梯度的方向。它的原理很简单,所以让我给你一个图,也许这将有助于解释为什么遵循负梯度方向是一个好主意。所以我们对我们的功能知之甚少,更好的是,当我们优化要查看的功能时,这是一个高层次的想法,景观局部优化景观。所以通过优化景观我是指我们网络中所有可能的权重的域,现在我们不知道是什么,如果我们在您的网络上使用任何特定的权重,我们将不知道是否会发生,它会更好地完成我们尝试将其培训到的任务,或更糟糕的是。


但我们确实知道,局部是当前为ad的点以及渐变和此渐变提供一些有关我们可以沿该方向行驶的信息,可能会改善我们网络的性能,或者在这种情况下降低价值。在此设置中,我们的功能最小化,最小化功能本质上就是在网络中进行培训,因此最小化损失将使您在分类任务中表现最佳,或您正在尝试做的任何事情。
因为我们只看世界在本地,此渐变基本上是我们拥有的最佳信息,您可以认为这是从一个山谷开始,在那里您开始某个可怕的地方,例如,山顶上的小指部分,您旅行从那里下来,在每个点上,您都遵循您附*的方向最遗憾的是最陡峭的下降,实际上是对百分比进行分级的方法,有时被称为最速下降法。该方向将如果您在本地移动仅一个假设我之前提到的这种*滑度是无穷小,实际上在实践中是不正确的,但我们将假设*滑度这一小步长只会使梯度变化很小,因此,您乘车的方向至少是一个好的方向小步骤。
我们基本上只是沿着这条路走,采取更大的步骤,因为我们可以穿越风景,直到到达底部的山谷是我们的功能最小化。现在有一个我们可以针对一些问题类别说些什么,我将使用我们可以做的最简单的问题类,因为这是我唯一的事情真的可以在一张幻灯片上完成任何数学运算,所以请多多包涵。此类是二次的,因此对于二次优化问题,我们实际上只基于渐变就知道很多,所以首先渐变离开整个空间的一半,现在用绿色来说明这一点线,所以我们在那一点线在绿线附*开始,知道解决方案不能在空间的其余部分,并且从您的网络,但它仍然是我们要遵循的真正好的指导方针。
负梯度的方向可能在其他地方有更好的解决方案空间,但要找到它们比仅仅寻找最好的要困难得多,我们所处位置附*的解决方案,所以我们正在努力寻找最佳解决方案我们附*的解决方案。您可以想象这是地球上有许多丘陵和山谷,我们不希望知道关于地球另一边的一座山的东西,但我们当然可以寻找我们目前所在的山下的山谷。
实际上,您可以在这里将这些功能表示为这些功能地形图这与您使用的地形图相同,熟悉来自地球的地球,这些环代表山,现在这里的环代表下降,所以这是底部我们在这里显示的山谷不是那里中心的山顶,所以我们梯度消除了整个可能空间的一半,这非常合理,然后去寻找这个负梯度,因为它是正交于这条在空间后会切断的线,您可以看到我已经得到了正交的指示,你在那里的小拉正方形。
所以,花费梯度下降的梯度属性在很大程度上取决于这些二次问题的问题结构实际上是相对容易地描述将要发生的事情,所以我将给您一个这里有一些概述,我会花几分钟,因为它是非常有趣。我希望你们中有一定背景的人线性代数可以遵循此推导,但我们将考虑二次优化问题现在该问题在灰色框中表示。
在顶部,您可以看到这是一个二次数,其中a是一个正定矩阵,我们可以处理更广泛的Quadra二次方程,这可能但是在正定情况和光栅下分析最简单,当然,用w- b和u作为在二次方的情况下,这个问题的形式是闭合的。时代B现在我们要做的是采取绿色框中显示的步骤,我们只是将其插入到距解的距离中,所以这个wk减去1减去W星离解决方案还有一段距离,所以我们想看看随着时间的变化,这个想法是,如果随着时间的推移我们越来越接*解决方案,该方法就是收敛。
因此我们从与解决方案的距离开始,将其插入现在,通过重新整理一点点更新的价值,我们可以将这些术语组合在一起,并且我们可以将B写为反所以我们可以拉或者我们可以将W星拉到里面的支架里面,然后我们得到这个表达式,它是矩阵乘以先前距离解矩阵乘以以前的距离解,现在我们不知道关于这个二次方在哪个方向上变化最大的任何事情,但是我们不能仅仅通过采用矩阵作为范数,到解的距离在这里这个范数在底部,这是现在当您考虑矩阵规范时的底线。


很明显,您将拥有一个表达式,其中该矩阵的特征值将是1减去mu gamma或1减去L伽玛。现在我得到这个的方式是我只看什么是极限本征a的值,我们称它们为mu和L,然后将它们插入表达式。我们可以看到这种组合的极限特征值是什么矩阵I减去gamma a,您现在具有此绝对值,您可以优化并获得二次方的最佳学习率,但是最佳学习率在实践中并不可靠,您可能不会想要使用它。
以便可以使用的更简单的值是L ll上的1特征值,这使您的收敛速度为1减去缩短解决方案每一步的距离。
随机梯度下降法 🔄
上一节我们分析了二次问题下的梯度下降,本节中我们来看看在实际神经网络训练中更常用的随机梯度下降法。
我们将每天都在练习中训练神经网络,因此,投放优化实际上并没有什么不同,我们要做的是用随机替换梯度下降步骤中的梯度。现在在神经网络中可以*似梯度,在这里,通过随机逼*来精确表示的是损失的梯度对于单个数据点单个实例,您可能要调用它,所以我得到了在这里的记号中,这个函数L是一天的损失数据点由AI索引,我们通常将其写在优化文献作为函数fi,我将使用此表示法,但您应该将fi想象成一个单一实例I和此处的损失。
我正在使用有监督的学习设置,其中有我标记为YI的数据点,所以他们指出XI标签YI函数的全部损失显示在顶部,这是所有这些F的总和。现在让我给您更多解释对于我们在这里所做的事情,我们将其随机放置在渐变中梯度,这是一个Luisi*似值,这通常是在随机优化设置,因此我们具有渐变和在我们的设置中,它的期望值等于整个梯度,因此您可以想到随机梯度下降步骤为完全梯度步骤期望。
现在这实际上并不是查看它的最佳方法,因为有一个发生的事情远不止于梯度下降和噪声,所以让我给你一点细节,但是首先我让任何人问任何问题,在我继续之前先在这里。
关于这个,但是是的,所以你是对的,所以使用整个数据集来计算梯度就是我所说的梯度下降。梯度下降在机器学习中我们现在几乎总是使用小批量生产,因此人们可能会在使用“梯度下降”之类的名称时,他们真的在谈论随机梯度下降以及您提到的内容是绝对正确的。
所以训练神经网络有些困难,使用非常大的批量,这在一定程度上可以理解,我会在下一张幻灯片上进行实际解释。让我让我进入您的首先指出问题的答案,实际上是这里的第三点随机梯度下降中的噪声引起这种现象,称为退火,并且该图直接它的右边说明了这种现象,因此您的网络培训风景很多,有很多小地方不是好的极小值出现在通往好的极小值的路径上的极小值。
很多人都赞成的理论是SGD渐变中产生的噪声实际上有助于优化器跳跃在这些坏的最小值上,理论上这些坏的最小值很小空间,所以他们很容易越过,我们是一个很好的最小值,导致您自己网络上的良好性能更大,更难跳过,所以,这个回答你的问题是的。


所以除了退火的观点之外,还有实际上还有其他一些原因,因此我们在我们从每个术语梯度和使用随机梯度获得的信息让我们在很多情况下利用这种冗余,几百个示例几乎与在完整数据上计算出的梯度一样好设置,通常便宜几千倍,具体取决于您的问题,就是鉴于以下原因,很难提出令人信服的理由使用梯度下降随机梯度下降的成功,这就是为什么厌恶的梯度说这是我们最好的错过之一,但梯度下降是最坏的情况之一。
实际上,早期的相关性很明显,这个令人反感的梯度可以关联到高达0.999的系数那些早期阶段的真实梯度的相关系数优化,所以我想简要谈一谈您需要了解的内容,我认为Jana已经简要提及了这一点,但实际上我们不使用随机梯度下降中的单个实例我们如何使用小批量实例,所以我只是在这里使用一些符号,但是每个人都使用迷你批处理使用不同的符号,因此您不应该过于依赖记法。
但基本上每个步骤中我都会在这里将其称为B,I为步长,您基本上使用迷你批处理中的梯度,这是数据的子集,而不是现在,几乎每个人都将使用此mini随机选择统一批处理,有些人使用替换采样,有些人使用时没有替换采样,但是,为此,差异并不重要,您可以使用小型配料有很多优点,因此实际上有一些很好的推动作用理论上的理由不是任何批次,但实际的原因是,这些实际原因中的绝大多数是我们进行的计算氨训练时,可能会以1%的效率利用我们的硬件。
网络,如果我们尝试使用单个实例,我们将获得最大收益批量有效使用数百种硬件的高效利用,例如,如果您正在训练典型的imagenet数据集,例如,您不要使用小于64的批量来获得良好的效率,可以降到32,但另一个重要的应用是分布式培训和,正如之前提到的,这确实是一件大事,最*能够训练imagenet天说,通常需要两天时间训练,不久前,只花了一个小时就花了一个星期的时间,他们这样做的方法是使用非常大的微型批次大量批次中,您需要使用一些技巧才能使其正常工作,您可能不会讲授入门课程,所以我鼓励您检查该纸,如果您有兴趣将它作为图像网小时忘了我不记得的脸书作者,目前的第一作者作为旁注,在某些情况下,您需要进行完整的批次优化,不要在其中使用梯度下降,这种情况我不能强调得足够多,即使有完整的批次数据是迄今为止最有效的方法,即插即用,您无需考虑它的积累,即l-bfgs,经过50年的优化研究,效果非常好,火炬的实现非常好,但是SyFy的实现会导致一些过滤15年前编写的代码,这几乎是防弹的,因为它们就是那些,所以这是一个好问题。
传统上,您确实需要使用完整的数据集,现在管焊炬实现实际上支持使用微型电池有点灰色地带,因为实际上没有理论支持,这可能会很好地解决您的问题,也可能无法解决问题,值得尝试,我的意思是您想为每个梯度使用整个数据集评估,或者更有可能,因为很少有人要这样做,可能更有可能您正在解决其他一些优化问题,不是在您的网络中训练,而是可能与一些辅助问题有关,您需要解决没有此数据点结构的优化问题,不是夏天不是数据点的总和,希望这是另一个,是的,哦。
是的,这个问题很年轻,建议我们使用小批量等于我们数据集中的类数的大小,为什么是问题是合理的答案是我们想要任何向量代表整个数据集,通常每个类都相当在属性上与其他类不同,因此使用小批量实际上每个类*均包含一个实例,可以显式执行该操作,尽管不必要,通过具有大约等于该大小的尺寸,我们可以假定它具有食物梯度的结构,因此您可以捕捉到很多相关性,全梯度显示的数据,这是一个很好的指南,尤其是当您在不受硬件过多约束的CPU上进行培训,当对CPU批处理量进行能量培训时,此处的效率对于硬件利用率取决于问题,我总是会推荐迷你批处理我认为不值得尝试以1号为起点,赚取小额收益也许值得探索,是的,还有另一个问题是在退火示例中,所以问题是为什么丢失风景如此动摇,这实际上是非常非常实际法则的逼真描述猛击了神经网络,令人难以置信的是,他们有很多丘陵和山谷,这是现在我们正在积极研究的东西是,有很多很好的极小值,所以山丘和山谷知道这一点是因为您的网络具有与之相关的组合方面,可以通过将所有权重转移到周围来收割电流表,使其成为神经网络,您可以从事您的工作,您将知道它的输出是否完全相同,所有这些权重都在移动,您正在执行的任务是什么,基本上对应于参数空间中的其他位置,因此,鉴于这些可能的重新排列方式呈指数级增长,获得与网络相同的权重,这就是这些峰值的令人难以置信的尖峰数,现在的原因为什么这些局部极小值仍然活跃研究,所以我不确定我能给你一个很好的答案,但他们在实践中绝对可以观察到,我可以说的是,它们似乎少了一个问题我们非常,像最先进的网络一样,所以这些本地最小值被认为是15年前的大问题,但此刻人们基本上从未遭受打击在实践中,当使用某种推荐参数时,例如,当您使用非常大的批次时,您可能会遇到这些问题,甚至清楚地表明,使用大批量生产时性能不佳甚至这些较大的最小值可归因于这些局部最小值,所以对正在进行的研究是的问题是您无法真正看到这种局部结构,因为我们处在这个百万维的空间中,这不是一个好方法看到它,是的,我不知道人们是否已经探索过,我不是熟悉相关论文,但我敢打赌有人看过它,所以您可能想要对Google询问,是的,因此神经网络设计中的许多进步都有实际上在很多方面都减少了这种颠簸,所以这是为什么它不再被认为是一个大问题的原因,在过去被认为是一个大问题还有其他问题,是的,很难看到,但是您可以做某些事情,我们可以峰谷肯定较小,并且通过重新缩放某些部分的神经网络可以放大某些方向的曲率,可以拉伸和压缩特定创新残差的方向提到的连接很容易看到它们*滑损失实际上您可以在两点之间画两条线空间,您会看到沿这条线发生的事情,这实际上是我们最好的方式有一个可视化的百万维空间,所以我把他变成了一个维您会发现,这两点之间的关系要好得多使用这些剩余连接时选择的任何两点,我都会在讲课中谈论所有关于闪避的问题,是的,如果希望我能无需再次询问即可回答该问题,但我们会看到,感谢任何其他问题,是的,所以l-bfgs很棒的方法,优化研究人员的星座,我们仍在使用SGD,60年代或更早时发明的方法仍是最先进的技术,但是。
动量法 ⚡


上一节我们探讨了随机梯度下降,本节中我们来看看能显著提升训练效率的动量法。


实际上是几年后的一些创新,但是有一些创新自sed发明以来的一项创新,其中一项创新是我稍后再谈,所以动力十足。当您使用随机时,应该几乎总是使用梯度下降,值得详细介绍一下,您经常会调整动量参数和网络,这是有助于了解调优过程中的实际操作,因此,动量的问题很容易被误解,这可以解释实际上存在三种不同的动量写作方式,看起来完全不同,但结果却是相当的,我只想之所以介绍这两种方式,是因为第三种方式不是众所周知,而是,实际上,我认为查看它的正确方法是我不谈论我的在这里进行研究,我们将讨论它如何在您将要使用的软件包。


这里的第一种形式是实际实现的,在Python和您将在此处使用的其他软件中,我们维护两个变量现在您会看到许多使用不同符号的论文,这里P是在物理学中用于动量的符号,通常也将其用作在讨论sed与动量时的动量变量,所以我将关注那个约定,所以我们现在不必再进行单个迭代,而必须使用Eretz P和W,并在每一步都更新它们,这是一个非常简单的更新,因此,P更新涉及添加到旧P,而不是完全添加到旧P,P我们对旧的P进行阻尼,我们通过将其乘以一个常数来减小它,比一个更糟,所以减少旧的P,在这里我使用beta帽子作为常数,因此在实践中可能会有0.9的少量阻尼和,我们添加了新的梯度。


所以P是这种累积的梯度缓冲区,您可以想到新的梯度在何处可以达到全价,而过去的梯度又在哪里在每一步都减少了一定的系数,通常为0.9减小,因此缓冲区往往是某种梯度的连续求和,基本上,我们只是将其修改为custer梯度两步下降,使用这个P而不是负梯度而不是梯度对不起,自从两行公式以来,在更新中使用P而不是最好以下面的第二种形式来理解,等价的情况下,您需要对地图的beta进行较小的转换,所以它不是,两种方法之间的beta完全相同,但实际上是相同的,因为在实践中,这些基本相同,直到获得罗马化和,我认为这部电影可能更清晰,这种形式称为随机重球方法,这里我们的更新仍然包括渐变,但我们也加上我们现在所走的过去方向的倍数副本,这意味着我们在这里实际上在做什么,所以实际上并不难进行可视化,我将使用一种经过提炼的可视化出版物,您可以在那里看到底部的衣服,但我对此非常不同意,他们在该文档中讨论的内容,但我喜欢可视化,因此,让我们使用had,我将在后面解释为什么我不同意一些问题,但这是非常简单,所以您可以将动量视为物理过程,而我提到你们中那些已经完成物理入门课程的人涵盖了这一点,所以动量是事物在不断移动中的特性,如果您熟悉牛顿法则,目前正在朝着正确的方向发展,法律规定事物要朝着前进的方向前进,这是动量,当您进行物理映射时,梯度有点像,推动你识字的力量,以此类推,它在每个点上都推着沉重的球,而不是使戏剧性左侧显示的每一步的行进方向都发生了变化图,而不是进行这些巨大的改变,我们将做一个适度的变化,所以当我们意识到自己走错了路时方向,我们有点掉头,而不是踩下手刹,摆弄它在很多实际应用中,问题会给您带来很大的改善,所以在这里您可以看到您,到解决方案结束时更接*解决方案,并且振荡更少,您会看到这种振荡,因此,如果您使用梯度下降型方法,所以我们在这里谈论梯度之上的动量,在可视化中下降,您将得到这种振荡,这只是梯度下降的特性,如果不修改,就无法摆脱它,方法,我们对他们来说在某种程度上可以减轻这种振荡。




这里的另一种可视化效果将使您对如何这个beta参数控制事物的系现在更大,如果等于零,则返回零,您会在梯度下降中分散注意力,必须小于1,否则Met在开始时一切都会炸毁,包括过去的渐变,随着时间的流逝越来越重,在零到一之间,典型值范围从0.25到小像0.99,所以在实践中您可以接*一个,发生的是,较小的值会导致您更快地改变方向,所以在您可以在左侧看到带有小Beta的

课程P9:一维多通道卷积与自动求导 🧠
在本节课中,我们将学习一维信号的多通道卷积操作,并深入理解PyTorch框架中的自动求导机制。我们将从卷积的矩阵表示开始,逐步过渡到多通道情况,最后通过代码实践来掌握自动求导的原理与应用。

卷积的矩阵表示回顾 📊
上一节我们介绍了卷积可以通过矩阵乘法来表示。让我们在这里画出来。

我们上次看到,可以写一个矩阵。矩阵中的每一列扮演着相似的角色,我们通常将其设计为与输入向量相乘。
当我们用这个矩阵乘以输入向量时,你会看到两种不同类型的等效表示形式。你能看清楚吗?
该乘积的输出类似于第一行乘以这个列向量,然后我再次缩小它们。这应该是大小相同的,否则你无法将它们相乘。
因此,我们有这个等等,直到最后一个,这将是我的最后一个向量。我们已经看到,这里的每个对象对我来说都是标量积。
但它们代表什么呢?我们怎么称呼它?我上次向你展示的一些标量积的演示涉及三角函数,对吧?所以这些都是投影。
从几何角度谈论,或者你可以将其视为修正的归一化余弦值。所以这基本上将是我对一个内核的输入信号的投影。

我的输入信号到内核的投影。因此,对此也有另一种解释,另一种看待这种情况的方式。这基本上是我们第一列矩阵A乘以向量X的第一个元素。
是的,所以返回第一个元素,然后你进行第二次调用,乘以向量X的第二个元素,直到到达最后一列,乘以最后一个元素。假设这是长度N,这是M乘以N。所以高度将再次是朝向我们的维度,矩阵的宽度是我们来自的维度。
第二部分是下面的内容。因此,我们在这里不使用这个矩阵。相反,因为我们要进行卷积,因为我们想利用稀疏性、数据的*稳性和组合性。我们在这里仍然使用相同的矩阵,也许对吧?我们在这里使用同一个人,但随后我们要使用的内核在整个信号上反复使用相同的权重。
对,因此在这种情况下,此矩阵的宽度不再是N,因为这里将是K,这将是正确的内核大小。所以这里,我要绘制更薄的矩阵,而这个矩阵将是小写K的高度。也许我们仍然可以称它为好吧,所以让我们在这里说,例如,有几个内核。
让我拥有我的卷积核,然后我就有其他非绿色的。让我更改,让我们放粉红色,以便你拥有一个。那么你可能拥有绿色的权利,以此类推。那么我们如何正确使用这些内核呢?
现在,我们基本上可以通过堆叠并移动来使用这些内核,对吧?所以我们从这里得到第一个内核,然后你基本上会得到你在这里的第一个家伙,然后你转移它,移动它,依此类推,直到获得整个矩阵。然后我们将其设为0。
这里,这里是0。这只是回顾。然后你有一个用于蓝色的。现在你在这里做了魔术,只需复制,我就粘贴。现在你还可以做彩色看神奇的魔术,而我们有了粉红色的。那么你拥有最后一个权利,我可以做同样的复制吗?是的,我可以做得很棒。
所以你不能在纸上复制和粘贴。好颜色,最后一个浅绿色好吧。所以我们复制。现在有多少个矩阵?我们有多少层?不算像屏幕上的字母和K或M这样的数字是什么?
通常,你只是在猜测自己不应该在猜测。告诉我正确的答案。我认为这是一次面试,我正在培训你。所以我们有多少张特征图,正确?所以这里的这张和我的M一样多,是这个初始事物的行数,在这里好吗?而是这个小内核的宽度在这里好吧好。那是什么?这个矩阵的高度是多少?
你确定?再试一次。我听不到并减去K加一。好。最后是这个东西的输出是正确的,所以输出将是一个向量的高度将是相同的,为N减K加1。那么应该是正确的,但是这个厚度是多少?最终向量M对,所以这个东西在这里和M对一样厚。所以这是我们上次离开的地方,正确。
但是后来有人问我,然后我意识到,我们这里有尽可能多的不同颜色。例如在这种情况下,如果我只是想确保我们了解发生了什么事。现在第一件事在这里,第二件事在这里,我拥有第三件事。在这种情况下可以。所以上次他们问我是否有人最后问我,所以我们在这种情况下最终会如何进行卷积?
在这里,因为在这里我们假设我的卷积核长度,比如说三个,但是它们只是一个小向量。所以有人告诉我,那你从这里开始怎么办?因为现在我们有一个厚度,然后才开始在这里。到目前为止,只有N个元素对的向量是你要遵循的。
更快,因为我们已经看到了我正在审查的这些内容,但是你是和我在一起直到现在是否是好的?太好了。让我们看看我们如何继续前进。
那么事情就是告诉你,现在实际上是在假设我们从那个长为N的向量开始。是的,但是在这种情况下,这也意味着我们拥有一些看起来像这样的东西。所以你基本上这里是1,这也是1,所以我们只有,例如一个单声道信号,这是正确的N高度。

所以,假设现在我们正在使用立体声系统,那将会是什么?我在这里的域。所以你知道我的X可以看作是从域是渠道的数量。所以这个家伙在这里是的,X是一个维度和某处。所以这是Ω是什么?星期二上的课的第二次Ω不是一组实数,没有其他人。
尝试我们正在使用计算机,时间轴是你有多少样本。有一个样本编号样本编号2或样本编号3,所以你基本上是一个自然空间的子集。所以这将是像0, 1, 2这样的东西在集合上将是N的子集,所以它不是。如果你有时间连续域,我们的R将是你在此看到的。
到目前为止,我刚刚向你展示的情况是输入通道数,因为这将是我的X对,这是我的输入。所以在这种情况下,到目前为止我们在这里展示的只是使用一个通道,意味着我们有一个单声道音频。让我们现在假设七个。假设这个家伙是两个,这样你就会谈论立体声音频信号正确。
好吧,让我们看看这些东西是如何变化的。好吧,在这种情况下,我让我想是的,我该如何画画?我会画正确的画。很少抱怨,如果你不关注,那么你到目前为止是否关注?因为,我看着我的*板电脑,我看不到你的对,所以你应该抱怨。
没什么意义的事,否则就变得无聊了。一直在看着你,对,对,对,好,我很无聊。谢谢。好吧,所以我们在这里有这个信号,对。然后在这种情况下,我们有一些厚度,这个家伙的厚度正确。所以在这种情况下,这个将是C。如果是立体声信号,你只剩下两个通道。
没错,这一直保持下去。所以如果我愿意,我们的内核喜欢对这个信号进行卷积,所以你有不同的相同。如果我想进行一次大卷积,则可以选择正确的卷积核等等。卷积我不是在说两个反卷积,因为它们仍在使用域名,这是第一权利。因此这实际上很重要。
所以如果我问你这是什么类型的信号,基本上你必须在这里查看这个数字,所以我们正在谈论一个是一维域权的一维信号。1D域还可以。所以我们仍在使用一维信号,但在这种情况下,你知道你有两个每点的值。那么我们将使用哪种内核?所以我只是在这种情况下绘制它,我们将使用类似的东西。
所以我,要画这个家伙,假设我在这里有K,那将是我的内核的宽度。但是在这种情况下,我要有一些厚度在这里。所以基本上你可以在这里应用这个东西,然后你就可以第二行和第三行,以此类推。所以你可能仍然喜欢这里M个内核,但在这种情况下,你还必须具有一定的厚度,必须与输入大小匹配正确。
因此这里的厚度必须与输入大小匹配。所以让我向你展示如何应用卷积,以便在这里得到这些切片之一,然后你将在这里应用。好吧,然后你就这样下去了。好吧,所以只要你应用这些,就可以在此执行此人的内积。有了这些,你得到的实际上是一个一个的标量。
所以,每当我在左侧使用这个橙色的东西时,我会做一个点积与这个产品的标量积,我只是得到一个标量。所以这实际上是我的1D卷积。1D卷积意味着它以这种方式下降。这只是为什么将其称为1D,但我们将其的每个元素相乘。卷积核让这个家伙在这里第二排,这个家伙好吗?你看到你把它们全部相乘,将它们全部相加,然后你得到了第一个输出在这里好。
所以每当我进行乘法运算时,我都会得到第一个输出在这里。我继续向下滑动该内核,然后你将获得第二个输出,第三个输出,第四个,依此类推,直到结束为止。然后,发生然后发生,我将要拾取不同的内核返回它。假设我得到第三个,好吧,让我们得到第二个,我得到一个第二个,我执行相同的操作,你将来到这里。
实际上让我们像矩阵一样,你走下去,直到你走到最后一个,这将是正确的结局。空内核将以这种方式下降,你将在这里得到最后一个。好的,是的,没有令人困惑的清除方式。所以这是我在是的,因为它是所有这些值之间的点积,基本上将信号的这一部分投影到该内核上,因此你将喜欢看贡献是什么,就像这部分的对齐方式一样,信号到这个特定的子空间。
好吧,这就是卷积。到目前为止,当你有一个频道时,我可以使用。频道现在我们有多个频道。好吧,哦,是的,一秒钟一秒,顶部一一秒,底部一秒。因此你实际上在这里丢失了第一行,而在这里丢失了最后一行。因此在这种情况下结束输出将是N减去三加一,所以你输了在上面两个一好。


在这种情况下,如果你实际上在底部输了两个,在卷积中心做一个中心。通常在每次进行卷积运算时,都会在结尾处开始,内核维数减去一。如果你将像这样的手,你有一个三核,在这里得到第一个是匹配的,然后你切换一个,然后切换到右侧,这样就可以打架了。
让我们告诉父母两个正确的。所以你有五个信号,有两个内核,你有一个1, 2, 3和4,所以我们从5开始,你最终为四个。因为如果使用内核大小为三,则得到一、二和三。所以如果使用内核大小,则转到三好。所以你可以随时尝试执行此操作。



好吧,现在我将向你展示这些内核的尺寸以及带有PyTorch的输出好吗?是否好吧好先生。是的,是的,我的意思是再放大一点。




在PyTorch中实现卷积 🛠️



现在我们可以去进行转换、激活、激活,然后我们有辩论是的PyTorch正确的Python深度学习。所以在这里我们可以运行IPython。如果我按Ctrl+L清除屏幕,我们可以导入torch,那么我可以从torch导入nn。



所以现在我们可以看到例如名为“让我们设置我的卷积层将等于nn.Conv1d。然后我可以一直走下去,直到得到这个,再说是,再说我有不知道如何使用此功能,我只问了一个问号,然后按Enter。我现在要在这里查看文档。

这样在这种情况下,第一项将是输入通道,然后是输出通道。那我就在街角叹息好了。例如,我们要在这里输入通道有一个立体声信号,所以我们把两个通道。我们说的是M,假设我们有16个内核,所以这是我要使用的内核数量。然后让我们计算一下我们的内核大小,我在这里使用的相同,所以让K或内核大小等于3好吧。



所以在这里,我将定义我的第一个卷积对象。所以如果我打印该对象,你会看到我们有一个Conv1d,它是从两个通道开始的立体声到16个声道,意味着我使用了16个小规模的内核,大小是3,然后步长也是1好的。在这种情况下,我要检查将成为我的卷积权重,权重的大小是多少?我们有多少权重?



我们有16个权重,所以内核的维度是(16, 2, 3)。好的,这是什么?Janis对,所以我有16个卷积核,每个的厚度都很高,然后长度是3,很合理,因为你将要应用这些整个信号中有16个信号。所以让我现在发出信号等于torch.randn,叹了口气我不知道是说64我也是。


不得不说我有一批1号,所以我有一个虚拟维度,所以我只有一个信号,然后就是64。我们说有两个通道,对,所以我有一个信号,一个例子有两个通道,有64个样本。所以这是我的X。卷积偏置大小正确的16,因为你有一个偏置项。好的,所以会发生什么?我们对X的卷积输出你好,所以我仍然有一个样本正确。

多少个通道?16。信号的长度是多少?好,64-3+1=62。修复它好吧,棒极了。好吧,如果我要使用与内核大小的卷积5,现在我该怎么办呢?我还不能大喊听到你60好吧,你正在追赶好吧。


因此,让我们现在尝试使用具有2D卷积的高光谱图像。好吧,现在我要编码了,这里的卷积将成为我的这种情况是正确的,或者再次成为Conv2d。我不知道如何用它,所以我问一个问号,然后在这里输入通道输出通道,步长、填充好的。

所以我将把输入尝试输入通道,所以它是一个具有20个*面的高光谱图像。所以在这种情况下,请输入20对,因为你要从20个光谱带开始。那么我们要输入通道的输出数量,我们说在这种情况下将再次使用16。我将输入内核大小,因为我打算使用ok,所以我们实际定义让我们实际定义我的首先发信号。
所以我的X会成为torch.randn,然后说一个例如20个高度的通道,我猜6128可以很好地固定在64个通道上,然后128好的,这将是我的输入数据。所以我的卷积现在可能是这样,所以我有20输入16的通道我们将要使用的卷积核,然后将在这种情况下指定内核大小,让我们使用类似三个的东西乘以五就可以了。
那么输出将是多少?任何人是否什么否20 Janice是输入数据的通道,所以你这里有多少个内核?16就在那里。我们有16个内核,其中有20个通道,以便它们可以覆盖输入3乘5对,像个短裤一样,是的,很短,但很大。所以这是什么?我会在卷积后得到一个(16, 60, 124)大小的输出。

比方说,我想实际加回我想去的头,我可以添加一些填充,所以这里,如果你不记得那个步长,我将再次步长1。只需输入问号就能知道语法,然后如何现在我应该加倍努力在y方向上迈出大步,我应该在y方向上添加很多填充,因为它将在顶部一个在底部一个然后在x方向然后好。


你正在追随幻想。所以现在如果我只运行这一行,你想初始大小还可以。所以现在你同时拥有1D和2D的问题是卷积核的维数和维数信号的符号。再次,我重复什么是集合的维数?二维数据的谨慎使用,再次正确,所以将需要四个尺寸在执行二维卷积时存储内核的集合。

一个是将会大步向前,因此,如果你不知道这是如何工作的,只需将问号,然后在这里告诉你,这样大步就可以告诉你,如果你是第一个内核,则每次内核都大步向前移动。表示你仅是批次大小,因此PyTorch希望你始终使用批次,意思是你仅使用一项权利就有多少信号,以便我们期望。

如果你发送将要输入张量的输入矢量,第三维度将要破裂并抱怨,所以我们还有一段时间。





自动求导机制 🤖

进入第二部分,好的第二部分将会是一直在为第一个作业权计算一些衍生权,所以在完成家庭作业之后,也许你必须要做的是计算这一点,好吧,应该在笑,这是个玩笑。太棒了,这就是你可以在90年代写回去计算。


我想在接下来的内容中将介绍LSDM的梯度,下一节课。所以他们如何仍然必须做这些事情是对的。尽管如此,我们还是可以使用Python来自动计算这些梯度。这样我们就可以检查这些自动梯度的工作原理。好吧好吧,我们现在要转到笔记本电脑的第三号,是看不见的。让我看看是否可以突出显示,现在更糟糕了第三,自动称呼黄莺,好吧,我走。






全屏显示,所以我们的教程将在这里,只需创建我的张量,它也具有这些所需的梯度。在这种情况下等于true我的意思是问torch,请跟踪所有梯度,计算确实使张量竞争,从而我们可以执行计算偏导数可以。


在这种情况下,我的Y为X就是X就是一二三四Y就是X减去第二个好吧。所以现在我们可以注意到grad_fn函数在这里,让我们看看这些东西是什么。在那里,看到哦,这是一个向后退的含义。这意味着Y已经由执行X与-之间减去的模块生成。

你有X减2,因此如果你检查谁很好地产生了Y,就会有一个减法模块好。那么X的梯度函数现在是什么?应该回答哦好吧为什么没有?因为他们应该在那儿写Alfredo产生了所有的权利。


没事的,还好吧,所以让我们实际上把鼻子在我们里面,我们实际上可以访问你拥有的第一个元素积累为什么是积累我不知道我忘了,但是如果你进去那里,你会看到初始向量我们使用的初始张量是一二三四,在这个计算图中,你还可以找到原始张量。


对,所以现在让我们获取Z值,内部将是我的Y*方乘以三,然后我计算*均值a就是Z的*均值。所以如果我计算这个东西的*方,我乘以三,取*均值。所以这是*方乘以3,然后是*均值,所以你可以如果你不相信我,请尝试一下,让我们看一下这东西的样子。


我将在这里推广所有这些计算顺序,因此我们从二乘二矩阵得出这个人在这里要买什么-这个X是谁。好吧,你跟随它凉爽,然后我们减去-对,然后我们再乘以Y两次,这就是为什么要进行乘法运算以获得相同的结果。减法就是将X减2乘以自身的Whyatt,你还有另一个乘法那可以乘以三,然后你将最后的均值向后移动,因为此Y为绿色,因为它表示不。


好,是的,谢谢你笑了,所以我算回道具吧。背景做什么这条线做什么?我想听听大家知道我们已经计算出正确的梯度,那么反向传播是你计算梯度的方式我们如何训练网络。梯度不正确或亚伦昨天回话,反向传播是用于完全计算梯度的不同的东西好吗,请分开存放不要合并。

他们每个人都过了一会,不是,他们看不到我,这两件事一直存在撞到一个糊状的想法,是不是很痛苦,她会计算正确的梯度,所以猜猜我们现在正在计算一些梯度,所以我们在你的页面上,这将是什么?这是*均水*,所以这是1/4对,所有这些红眼睛的总和还可以。


那我从1变到4好了,那我说我要去的是什么?等于3y我就对了,是的,没问题,不好,然后这个等于3乘以X减去2*方,所以它属于什么?至,R属于哪里,R是对的,所以它是一个标量。

好的,现在我们可以在dX上计算da了,这是多少东西?将有1/4个论坛出现在这里,然后你就知道了,让我们拥有这个关于X的第i个元素一个,好吧,我们要有这个里面是我有三个我为什么*方,它是三个X减右边的两个正方形,所以这三个出现在此处两个也下降,那么你乘以Xi减2到现在应该是正确的了。


好吧,所以我的X是这里的元素,实际上,我也要计算这一点,所以这一步消失了,确实,这是Xi减去3的1.5倍,- 2 - 3,好数学好吧好吧谢谢你好吧。那我将来要做什么?X实际上是我在这里直接写转置的,所以对于第一个元素,你有一个1.5倍,所以1.5减3,你得到1减1.5对。


第二个是3减3,你得到0瑞安,这是3减3。也许我应该把所有事情都写对,这样你实际上就可以跟随。1.5减3现在你有3减3以下你有4点5减3,然后最后一个将是6减3,这将等于减1点5,0 1点5


浙公网安备 33010602011771号