# DeepLearning **Repository Path**: nicelusanjin/DeepLearning ## Basic Information - **Project Name**: DeepLearning - **Description**: 李沐,动手学深度学习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 1 - **Created**: 2025-05-04 - **Last Updated**: 2025-07-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: Notes ## README # Pytorch 在PyTorch中,`permute`和`view`都是用于改变张量形状的操作,但它们的功能和使用场景有所不同。 ### 1. `view`操作 - **功能**: `view`用于改变张量的形状(shape),但不改变张量的数据顺序。它要求张量在内存中是连续的(contiguous),否则会报错。 - **使用场景**: 当你需要改变张量的形状而不改变数据顺序时使用。 - **示例**: ``` import torch x = torch.arange(6) # tensor([0, 1, 2, 3, 4, 5]) y = x.view(2, 3) # tensor([[0, 1, 2], [3, 4, 5]]) ``` ### 2. `permute`操作 - **功能**: `permute`用于改变张量的维度顺序,可以重新排列张量的轴。它不要求张量在内存中是连续的。 - **使用场景**: 当你需要改变张量的维度顺序时使用。 - **示例**: ``` import torch x = torch.arange(6).view(2, 3) # tensor([[0, 1, 2], [3, 4, 5]]) y = x.permute(1, 0) # tensor([[0, 3], [1, 4], [2, 5]]) ``` ### 异同点总结 - **相同点**: - 两者都用于改变张量的形状或维度。 - **不同点**: - `view`仅改变形状,不改变数据顺序,且要求张量是连续的。 - `permute`改变维度顺序,可能改变数据顺序,不要求张量是连续的。 ### 注意事项 - 使用`view`前,确保张量是连续的,否则需先调用`contiguous()`方法。 - `permute`不会改变张量的数据存储顺序,但会改变访问顺序。 通过这些操作,你可以灵活调整张量的形状和维度,适应不同的计算需求。 - **`view`**: 更高效,但要求张量连续,适合对性能要求高的场景。 - **`reshape`**: 更灵活,自动处理非连续张量,适合通用场景。 - **连续存储**意味着张量的数据在内存中是按照逻辑顺序(即按照张量的形状和步长)连续排列的。 - **非连续存储**意味着数据在内存中不是按照逻辑顺序排列的,可能是由于某些操作(如转置、切片等)导致的。 ### 广播机制 - PyTorch支持广播(broadcasting),但需要满足广播规则: - 从**最后一个维度**开始,两个张量的维度大小必须相等,或者其中一个为1。 - 3X2, 2X2就不行 ### Sequential类的一种实现方式 ```python class MySequential(nn.Module): def __init__(self, *args): super().__init__() for idx, module in enumerate(args): # 这里,module是Module子类的一个实例。我们把它保存在'Module'类的成员 # 变量_modules中。_module的类型是OrderedDict self._modules[str(idx)] = module def forward(self, X): # OrderedDict保证了按照成员添加的顺序遍历它们 for block in self._modules.values(): X = block(X) return X ``` ### 若Sequential类中没有对应的功能 ### 自定义模块(模块之间可以嵌套,形成树结构) ```python class FixedHiddenMLP(nn.Module): def __init__(self): super().__init__() # 不计算梯度的随机权重参数。因此其在训练期间保持不变 self.rand_weight = torch.rand((20, 20), requires_grad=False) self.linear = nn.Linear(20, 20) def forward(self, X): X = self.linear(X) # 使用创建的常量参数以及relu和mm函数 X = F.relu(torch.mm(X, self.rand_weight) + 1) # 复用全连接层。这相当于两个全连接层共享参数 X = self.linear(X) # 控制流 while X.abs().sum() > 1: X /= 2 return X.sum() ``` ### 模型参数的保存与加载 将模型的参数存储为一个叫做“mlp.params”的文件 ```python torch.save(net.state_dict(), 'mlp.params') clone = MLP() #要先声明网络结构 clone.load_state_dict(torch.load("mlp.params")) clone.eval() Y_clone = clone(X) Y_clone == Y ``` ### nn.Flatten() 只保留两个维度,一个代表批量大小,一个代表特征拉平后的(通道数、长和宽等等) ### nn.AdaptiveAvgPool2d((1,1)) 经过 `nn.AdaptiveAvgPool2d((1,1))` 后,输出特征图的尺寸变为 `(C, 1, 1)`。 # 深度学习-李沐 ## 矩阵求导 ![](images/deriv1.png) ![](images/deriv2.png) ![](images/deriv3.png) ![](images/deriv3_5.png) ![](images/deriv4.png) ![](images/deriv5.png) ![](images/deriv6.png) ## 多层感知机MLP 相比于线性层有激活函数。 ![](images/MLP.png) nn.CrossEntryloss函数已经在计算的时候(在最后一层)自动添加softmax计算了,所以net里不需要最后再加softmax ## 模型选择+过拟合和欠拟合 (五个叛徒都穿蓝色衣服)相关性不一定有因果性 * 训练误差:模型在训练数据上的误差 * 泛化误差:模型在新数据上的误差 * 例子:根据模考成绩来预测未来考试分数 * 在过去的考试中表现很好(训练误差)不代表未来考试会好(泛化误差) * 学生A通过背书来在模考中拿到很好的成绩 * 学生B知道答案后面的原因 * 验证数据集:一个用来评估模型好坏的数据集 * 例如拿出50%的训练数据 * 不要跟训练数据混在一起(常犯错误) * 测试数据集:只用一次的数据集。例如 * 未来的考试 * 我出价的房子的实际成交价 * 用在Kaggle私有排行榜中的数据集 * 不用利用测试数据集的结果去调超参数(常犯错误) ### 没有足够多数据怎么办? K-则交叉验证 * 将训练数据分割成K块 * For i = 1, ..., K * 使用第i块作为验证数据集,其余的作文训练数据集 * 报告K个验证集误差的平均 * 常用:K=5或10 ### 过拟合和欠拟合 ![](images/fitting.png) ### 模型容量 * 拟合各种函数的能力 * 低容量的模型难以拟合训练数据 * 高容量的模型可以记住所有的训练数据 ![](images/fitting2.png) ### 估计模型容量 * 给定一个模型种类,将有两个主要因素 * 参数的个数 * 参数值的选择范围 数据复杂度: * 样本的个数 * 每个样本的元素个数 * 时间、空间结构 * 多样性 ## 权重衰退(过拟合解决方法) 通过限制参数值的选择范围来控制模型容量 使用均方范数作为硬性限制: 目标函数: $min \quad l(w, b)\quad subject \quad to \quad ||w||^2 <= \theta$ * 通常不限制偏移b(限不限制都差不多) * 小的$\theta$意味着更强的正则项 使用均方范数作为柔性限制 * 对于每个$\theta$,都可以找到$\lambda$使得之前的目标函数等价于下面: $$ min \quad l(w,b)+\frac{\lambda}{2}||w||^2 $$ 可以通过拉格朗日乘子来证明 * 超参数$\lambda$控制了正则项的重要程度 * $\lambda=0$:无作用 * $\lambda=\infin$, $w^*=0$ ![](images/weights_min.png) 简洁实现(在迭代器中使用): ```python trainer = torch.optim.SGD([ {"params":net[0].weight,'weight_decay': wd}, {"params":net[0].bias}], lr=lr) ``` ## 丢弃法 动机:一个好的模型需要对输入数据的扰动鲁棒 ![](images/dropout.png) * 通常将丢弃法作用在隐藏全连接层的输出上 丢弃法只在训练时(设置net.train())使用:他们影响模型参数的更新 在推理过程中(设置net.eval()),丢弃法直接返回输入。 总结: * 丢弃法将一些输出项随机置0来控制模型复杂度 * 常作用在多层感知机的隐藏层输出上 * 丢弃概率是控制模型复杂度的超参数 ## 数值稳定性 数值稳定性的常见问题:梯度爆炸和梯度消失 梯度爆炸的问题(激活函数为ReLU): * 值超出值域,对16位浮点数尤为严重 对学习率敏感: * 如果学习率太大->大参数值->更大的梯度 * 如果学习率太小->训练无进展 * 我们可能需要在训练过程中不断调整学习率 梯度消失的问题(激活函数为Sigmod) * 梯度值变成0 * 训练没有进展(不管如何选择学习率) * 对于底部层尤为严重 * 仅仅顶部层训练的较好 * 无法让神经网络更深 ### 如何让训练更加稳定 * 目标:让梯度值在合理的范围内 * 将乘法变加法(ResNet,LSTM) * 归一化(梯度归一化,梯度裁剪) * 合理的权重初始和激活函数 #### 让每一层的方差是一个常数 * 将每层的输出和梯度都看做随机变量 * 让它们的均值和方差都保持一致 权重初始化: * 在合理值区间里随机初始参数 * 训练开始的时候更容易有数值不稳定 * 远离最优解的地方损失函数表面可能很复杂 * 最优解附近表面会比较平 * 使用$N(0,0.01)$来初始可能对小网络没问题,但不能保证深度神经网络 检查常用的激活函数: tanh和ReLU没问题,sigmod需要变为4*sigmod(x)-2 ## 卷积层 两个原则 * 平移不变性 * 局部性 超参数 * 核矩阵的大小 * 输入通道个数 * 输出通道个数 可学习的参数 * 核矩阵的数值 * 偏移(可有可无) 训练参数复杂度 * $c_o \times c_i \times k^2$ ## 卷积层里的多输入多输出通道 多个输入通道 * 每个通道都有一个卷积核,结果是所有通道卷积结果的**和**。 多个输出通道 * 我们可以有多个三维卷积核(相当于四维),每个核生成一个输出通道 输入**X**:$c_i \times n_h \times n_w$ 核**W**:$c_0 \times c_i \times k_h \times k_w$ 输出**Y**:$c_0 \times m_h \times m_w$ * 每个输出通道可以识别特定模式 * 输入通道核识别并组合输入中的模式 ## 卷积层里的填充和步幅 填充 在输入周围添加额外的行/列。 填充$p_h$行和$p_w$列,输出形状为 $$ (n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1) $$ 通常取$p_h=k_h-1, p_w=k_w-1$ 步幅 步幅是指行/列的滑动步长 总结 * 填充和步幅是卷积层的超参数 * 填充在输入周围添加额外的行/列,来控制输出形状的减少量 * 步幅是每次滑动核窗口时的行/列的步长,可以成倍的减少输出形状 ## 池化层 为什么需要池化层? * 积对位置敏感 * 需要一定程度的平移不变性 * 照明,物体位置,比例,外观等等因图像而异 池化层类别: * 最大池化层:每个窗口中最强的模式信号 * 平均池化层:将最大池化层中的“最大”操作替换为“平均” 填充,步幅和多个通道: * 池化层与卷积层类似,都具有填充和步幅 * 没有可学习的参数 * 在每个输入通道应用池化层以获得相应的输出通道 * 输出通道数=输入通道数 总结: * 池化层返回窗口中的最大或平均值 * 缓解卷积层对位置的敏感性 * 同样有窗口大小、填充和步幅作为超参数 **深度学习框架中的步幅与池化窗口的大小相同** ## 经典卷积神经网络 LeNet * LeNet是早期成功的神经网络 * 先使用卷积层来学习图片空间信息 * 然后使用全连接层来转换到类别空间 ## 深度卷积神经网络 AlexNet ImageNet:469X387 * AlexNet是更大更深的LeNet,10x参数个数,260x计算复杂度 * 新加入了丢弃法,ReLu,最大池化层,和数据增强 * AlexNet赢下了2012ImageNet竞赛后,标志着新的一轮神经网络热潮的开始 ## 使用块的网络 VGG * VGG使用可重复使用的卷积块来构建深度卷积神经网络 * 不同的卷积块个数和超参数可以得到不同复杂度的变种 ## 网络中的网络NiN * NiN块使用卷积层加两个1X1卷积层 * 后者对每个像素增加了非线性性 * NiN使用全局平均池化层来替代VGG和AlexNet中的全连接层 * 不容易过拟合,更少的参数个数 ## 含并行连结的网络 GoogLeNet 4个路径从不同层面抽取信息,然后在输出通道维**合并** * Inception块用4条有不同超参数的卷积层和池化层的路来抽取不同的信息 * 它的一个主要优点是模型参数小,计算复杂度低 * GoogleNet使用了9个Inception块,是第一个达到上白层的网路。 ## 批量归一化(BN) * 损失出现在最后,后面的层训练较快 * 数据在最底部 * 底部的层训练较慢 * 底部层一变化,所有都得跟着变 * 最后的那些层需要重新学习多次 * 导致收敛变慢 * 我们可以在学习底部层的时候避免变化顶部层吗? * 固定小批量里面的均值和方差,然后再做额外的调整 $$ x_{i+1}= \gamma \frac{x_i-\mu_B}{\sigma_B}+\beta $$ * **可学习**的参数为$\gamma$和$\beta$ * 作用在 * 全连接层和卷积层输出上,激活函数前 * 全连接层和卷积层输入上 * 对于全连接层,作用在**特征维**(超参数)nn.BatchNorm2d(特征数) * 对于卷积层,作用在**通道维**(超参数)nn.BatchNorm1d(通道维) * 批量归一化固定小批量中的均值和方差,然后学习出适合的偏移和缩放 * 可以**加速收敛速度**,但**一般不改变模型精度** ## 残差网络 ResNet * $f(x)=x+g(x)$ * 高宽减半ResNet块(stride为2, 1x1的卷积核输出通道x2) * 后接多个高宽不变ResNet块 ## 单机多卡并行 * 数据并行:将小批量分成n块,每个GPU拿到完整参数计算一块数据的梯度 * 通常性能更好 * 模型并行:将模型分成n块,每个GPU拿到一块模型计算它的前向和方向结果 * 通常用于大模型到单GPU放不下 ## 数据增广 * 增加一个已有数据集,使得有更多的多样性,模型泛化性能更好 * 在语言里面加入各种不同的背景噪音 * 改变图片的颜色和形状 翻转 * 左右翻转 * 上下翻转(不总是可行) 切割 * 从图片中切割一块,然后变形到固定形状 * 随机高宽比 * 随机大小 * 随机位置 颜色 * 改变色调,饱和度,明亮度 ## 微调(Transfer迁移学习) 网络架构 * 一个神经网络一般可以分成两块 * 特征抽取将原始像素变成容易线性分割的特征 * 线性分类器来做分类 训练 * 是一个目标数据集上的正常训练任务,但使用更强的正则化 * 使用更小的学习率 * 使用更少的数据迭代 * 源数据集**远复杂于目标数据**,通常微调效果更好 重用分类器权重 * 源数据集可能也有目标数据中的部分标号 * 可以使用预训练好模型分类器中对应标号向量来做初始化 固定一些层 * 神经网络通常学习有层次的特征表示 * 低层次的特征更加通用 * 高层次的特征则跟数据集相关 * 可以固定底部一些层的参数,不参与更新 * 更强的正则 总结 * 微调通过使用在大数据上得到的预训练好的模型来初始化模型权重来完成提升精度 * 预训练模型质量很重要 * 微调通常速度更快、精度更高 ## 物体检测和数据集 边缘框 * 一个边缘框可以通过4个数字定义 目标检测数据集 * 每行表示一个物体 * 图片文件名,物体类别,边缘框 * COCO数据集 总结 * 物体检测识别图片里的多个物体的类别和位置 * 位置通常用边缘框表示 让我们读取一个小批量,并打印其中的图像和标签的形状。 图像的小批量的形状为(批量大小、通道数、高度、宽度),看起来很眼熟:它与我们之前图像分类任务中的相同。 标签的小批量的形状为(批量大小,m,5),其中m是数据集的任何图像中边界框可能出现的最大数量。 小批量计算虽然高效,但它要求每张图像含有相同数量的边界框,以便放在同一个批量中。 通常来说,图像可能拥有不同数量个边界框;因此,在达到m之前,边界框少于m的图像将被非法边界框填充。 这样,每个边界框的标签将被长度为5的数组表示。 数组中的第一个元素是边界框中对象的类别,其中-1表示用于填充的非法边界框。 数组的其余四个元素是边界框左上角和右下角的(x,y)坐标值(值域在0~1之间)。 对于香蕉数据集而言,由于每张图像上只有一个边界框,因此m=1。 ## 锚框 * 一类目标检测算法是基于锚框 * 提出多个被称为锚框的区域(边缘框) * 预测每个锚框里是否含有关注的物体 * 如果是,预测从这个锚框到真实边缘框的偏移 loU-交并比 * loU用来计算两个框之间的相似度 * 0表示无重叠,1表示重合 * Jacquard指数: $$ J(A,B)=\frac{|A\cap B|}{|A\cup B|} $$ 赋予锚框标号 * 每个锚框是一个训练样本(固定生成,比如以每个像素为中心生成多个锚框) * 将每个锚框,要么标注成背景,要么关联上一个真实边缘框 * 我们可能会生成大量的锚框 * 这个导致大量的负类样本 使用非极大值抑制(NMS)输出(删除相似框) * 每个锚框预测一个边缘框 * NMS可以合并相似的预测 * 选中非背景类的最大预测值 * 去掉所有其它和它loU值大于$\theta$的预测 * 重复上述过程,直到所有预测要么被选中,要么被去掉 总结 * 一类目标检测算法基于锚框来预测 * 首先生成大量锚框,并赋予标号,每个锚框作为一个样本进行训练 * 在预测时,使用NMS来去掉冗余的预测 * 锚框的质量很重要 ## 物体检测算法:R-CNN,SSD,YOLO 区域卷积神经网络 SSD: * SSD通过单神经网络来检测模型 * 以每个像素为中心的产生多个锚框 * 在多个段的输出上进行多尺度的检测 YOLO(你只看一次): * SSD中锚框大量重叠,因此浪费了很多计算 * YOLO将图片均匀分成$S\times S$个锚框 * 每个锚框预测$B$个边缘框 ## 语义分割 更加精细: * 语义分割将图片中的每个像素分类到对应的类别 语义分割和实例分割(其实就是标号的区别) ## 转置卷积 * 卷积不会增大输入的高宽,通常要么不变、要么减半 * 转置卷积则可以用来增大输入高宽 * 如果卷积将输入从$(h,w)$变成了$(h',w')$ * 同样超参数的转置卷积则从$(h',w')$变成$(h,w)$ ## 全连接卷积神经网络(FCN) * FCN是用深度神经网络来做语义分割的奠基性工作 * 它用转置卷积层来替换CNN最后的全连接层,从而可以实现每个像素的预测 ## 样式迁移 * 将样式图片中的样式迁移到内容图片上,得到合成图片 * 样式层选取:一部分靠前的(局部信息),一部分靠后的(全局信息) * 内容层选取:一部分相对靠后的(高层信息) ## 序列数据 * 音乐、语言、文本和视频都是连续的 * 标题“狗咬人”远没有“人咬狗”那么令人惊讶 * 大地震发生后,很可能会有几次较小的余震 * 人的互动是连续的,从网上吵架可以看出 * 预测明天的股价要比填补昨天遗失的股价更困难 统计工具 * 在时间$t$观察到$x_t$,那么得到$T$个不独立的随机变量 * $(x_1,...x_T)\sim p(X)$ * 使用条件概率展开 * $p(a,b)=p(a)p(b|a)=p(b)p(a|b)$ * $p(X)=p(x_1)\cdot p(x_2|x_1)\cdot ...p(x_T|x_1,...X_{T-1})$ 序列模型 * 对条件概率建模 $p(x_t|x_1,...x_{t-1})=p(x_t|f(x_1,...x_{t-1}))$ 对自身数据建模,也称自回归模型 方案A-马尔科夫假设 * 假设当前数据只跟$\tau$个过去数据相关 * $p(x_t|x_1,...x_{t-1})=p(x_t|x_{t-\tau},..,x_{t-1})=p(x_t|f(x_{t-\tau},...x_{t-1}))$ * 由于固定了长度,所以可以用MLP模型 方案B-潜变量模型 * 引入潜变量$h_t$来表示过去信息$h_t=f(x_1,...x_{t-1})$ * 这样$x_t=p(x_t|h_t)$ * 相当于有两个模型需要训练 * 一个是如何通过当前的x和h预测下一个h * 一个是如何通过当前的h和上一个x来预测下一个x * “记住”了历史信息,简化了模型,不用考虑特别长的历史了,和动态规划很像 总结 * 时序模型中,当前数据跟之前观察到的数据相关 * 自回归模型使用自身过去数据来预测未来 * 马尔科夫模型假设当前只跟最近少数数据相关,从而简化模型 * 潜变量模型使用潜变量来概括历史信息 ## 文本预处理 * 每个文本序列又被拆分成一个token列表(按词或者按字母) * 构建一个字典,通常也叫做词汇表(vocabulary) ,用来将字符串类型的标记映射到从0开始的数字索引中。 ## 语言模型 * 给定文本序列$x_1,...,x_T$,语言模型的目标是估计**联合概率$p(x_1,...,x_T)$** * 它的应用包括 * 做预训练模型(eg BERT,GPT-3) * 生成本文,给定前面几个词,不断的使用$x_t \sim p(x_t,..,x_{t-1})$来生成后续文本 * 判断多个序列中哪个更常见 使用计数来建模 * 假设序列长度为2,我们预测 * $p(x,x')=p(x)p(x'|x)=\frac{n(x,x')}{n}$ * 这里$n$是总词数,$n(x),n(x,x')$是单个单词和连续单词对的出现次数 N元语法 * 当序列很长时,因为文本量不够大,很可能$n(x_1,...,x_T)<= 1$ * 使用马尔科夫假设可以缓解这个问题 ## 循环神经网络 RNN 潜变量自回归模型 ![](images/RNN.png) * 使用潜变量$h_t$总结过去的信息 * $p(h_t|h_{t-1},x_{t})$ * 循环神经网络 * 更新隐藏状态:$h_t=\phi(W_{hh}h_{t-1} + W_{hx}x_{t} + b_h)$ * 输出:$o_t=\phi(W_{ho}h_t+b_o)$ * $W_{hh}h_{t-1}$存放了时序信息,去掉这一项就退化成MLP * 每个$h_t$代表RNN网络的一种状态,每次都会更新这种状态,也就是说横向代表时间而不是整个参数矩阵 * 序列标签是是原始序列的下一个词源,因为每次都是用当前的词预测下一个词 * 训练的是隐藏层的权重 * 预测是先将prefix个词输入进去,得到state,然后从这个state中依次预测每个词 * 输出是每个词出现的概率(经过了softmax),用交叉熵来评定训练效果好坏 困惑度 * **衡量一个语言模型的好坏**可以用平均交叉熵 $$ \pi = \frac{1}{n}\Sigma-logp(x_t|x_{t-1},...) $$ * 历史原因NLP使用困惑度$exp(\pi)$来衡量,1表示完美,无穷大是最差情况 梯度裁剪 * 如果梯度长度超过$\theta$,那么拖影回长度$\theta$ ## 门控循环单元(GRU) 关注一个序列 * 不是每个观察值都是同等重要 * 想只记住相关的观察需要: * 能关注的机制(更新门) * 能遗忘的机制(重置门) * 两种极端情况之间学习 * 不看xt,忽略掉它 * 只看xt,忽略前面所有状态 * R、Z的形状和H一样 ![](images/GRU.png) ## 长短期记忆网络(LSTM)(Long Short-Term Memory) * 忘记门:将值朝0减少 * 输入门:决定不是忽略掉输入数据 * 输出门:决定是不是使用隐状态 * 四种状态 * 可以忘记所有之前的状态 * 输入除了H还多了个C(记忆单元) ![](images/LSTM.png) ## 深层循环神经网络 * 深度循环神经网络使用多个隐藏层来获得更多的非线性性 ## 双向循环神经网络 * 双向循环神经网络通过反向更新的隐藏层来利用方向时间信息 * 通常用来对序列抽取特征、填空,而不是预测未来 ## 编码器-解码器架构 重新考察CNN: * 编码器:将输入编程成中间表达形式(特征) * 解码器:将中间表示解码成输出 一个模型被分为两块: * 编码器:将文本表示成向量 * 解码器:向量表示成输出 * 解码器有一个init_state函数,解码器用于预测 ![](images/Encoder&Decoder.png) ## 序列到序列学习(seq2seq) 机器翻译: * 给定一个源语言的句子,自动翻译成目标语言 * 这两个句子可以有不同的长度 Seq2Seq * 编码器是一个RNN,读取输入句子 * 可以是双向 * 解码器使用另外一个RNN来输出 编码器-解码器细节 * 编码器是没有输出的RNN * 编码器最后时间步的隐状态,用作解码器的初始隐状态 训练 * 训练时解码器使用目标句子作为输入 衡量生成序列的好坏的BLEU(预测好坏,并不是loss, loss仍然是交叉熵) * $p_n$是预测中所有n-gram的精度 * 惩罚过短的预测,长匹配有高权重 ## 束搜索 贪心搜索 * 在seq2seq中我们使用了贪心搜索来预测序列 * 将当前时刻预测概率最大的词输出 * 但贪心很可能不是最优的 穷举搜索(计算上不可行) 束搜索: * 保存最好的$k$个候选 * 在每个时刻,对每个候选新加一项($n$种可能),在$kn$个选项中选出最好的$k$个 束搜索在每次搜索时保存$k$个最好的候选 * $k=1$时是贪心搜索 * $k=n$时是穷举搜索 ## 注意力机制 心理学 * 动物需要在复杂环境下有效关注值得注意的点 * 心理学框架:人类根据随意线索和不随意线索选择注意点 卷积、全连接、池化层都只考虑不随意线索 注意力机制则显示的考虑随意线索 * 随意线索被称之为**查询**(query) * 每个输入时一个值(value)和**不随意线索**(key)的对 * 通过**注意力池化层**来有偏向性的选择某些输入 **非参数注意力池化层**(无可学习的参数) * 给定数据$(x_i,y_i), i=1,...,n$ * 平均池化层是最简单的方案:$f(x)=\frac{1}{n}\sum_{i} y_i$ * 更好地方案是60年代提出来的Nadaraya-Watson核回归(K表示x与xi之间的距离) $$ f(x)=\sum_{i=1}^{n}\frac{K(x-x_i)}{\sum_{j=1}^{n}K(x-x_j)}y_i $$ * 使用高斯核$K(u)=\frac{1}{\sqrt{2\pi}}exp(-\frac{u^2}{2})$ * 那么 $$ f(x)=\sum_{i=1}^{n}softmax(- \frac{1}{2}(x-x_i)^2)y_i $$ 参数化的注意力机制 * 在之前基础上引入可以学习的$w$ $$ f(x)=\sum_{i=1}^{n}softmax(- \frac{1}{2}((x-x_i)w)^2)y_i $$ 总结 * 心理学认为人通过随意线索和不随意线索选择注意点 * 注意力机制中,通过query(随意线索)和key(不随意线索)来有偏向性的选择输入 * 可以一般写作$f(x)=\sum_{i} \alpha(x,x_i)y_i$,这里$\alpha(x,x_i)$是注意力权重 * 早在60年代就有非参数的注意力机制 * 接下来我们会介绍多个不同的权重设计 ## 注意力分数 * 注意力分数是query和key的相似度,注意力权重是分数的softmax结果 ![](images/attention_score.png) * 两种常见的分数计算: * 将query和key合并起来进入一个单输出隐藏层的MLP ![](images/additive_attention.png) * 直接将query和key做内积 ![](images/Scaled_dot.png) ![](images/attention.png) valid_len是指key_value pair的有效长度 ## 使用注意力机制的Seq2Seq 动机 * 机器翻译中,每个生成的词可能相关于源句子中不同的词 * seq2seq模型中不能对此直接建模 加入注意力 * 原来解码器的输入上下文是编码器最后一个时刻的state,现在应该根据预测词的不同来故意的匹配之前的state * 编码器对每次词的输出作为key和value(它们等价) * 解码器RNN对上一个词的输出是query * 注意力的输出和下一个词的词嵌入合并进入RNN ![](images/Seq2Seq_with_attention.png) 总结 * Seq2Seq中通过隐状态在编码器和解码器中传递信息 * 注意力机制可以根据解码器RNN的输出来匹配到合适的编码器RNN的输出来更为有效的传递信息 ## 自注意力 * 自注意力池化层将$x_i$当作key,value,query来对序列抽取特征 * query和key,value来源相同 * 完全并行、最长序列为1、但对长序列计算复杂度高 位置编码 * 跟CNN/RNN不同,自注意力并没有记录位置信息 * 位置编码将位置信息注入到输入里 * 假设长度为n的序列是X=nxd,那么使用位置编码矩阵P=nxd来输出X+P作为自编码输入 * 位置编码在输入中加入位置信息,使得自注意力能够记忆位置信息 * 相对位置比绝对位置要好 ## Transformer * 基于编码器-解码器架构来处理序列对 * 跟使用注意力的seq2seq不同,Transformer是纯基于注意力 ![](images/transformer.png) ### 多头注意力 * 对同一key, value, query,希望抽取不同的信息 * 例如短距离关系和长距离关系 * 多头注意力使用h个独立的注意力池化 * 合并各个头(head)输出得到最终输出 ![](images/multi_head.png) ### 有掩码的多头注意力 * 解码器对序列中一个元素输出时,不应该考虑该元素之后的元素 * 可以通过掩码来实现(valid_len) * 也就是计算xi输出时,假装当前序列长度为i ### 基于位置的前馈网络 * 将输入形状由(b,n,d)变换成(bn,d) * 作用两个全连接层 * 输出形状由(bn,d)变化回(b,n,d) * 等价于两层核窗口为1的一维卷积层 ### 层归一化 * 批量归一化对每个特征/通道例元素进行归一化 * 不适合序列长度会变的NLP应用 * 层归一化对每个样本例的元素进行归一化 ### 信息传递 * 编码器中的输出y1,...,yn * 将其作为解码器中第i个Transformer块中多头注意力的key和value * 它的query来自目标序列 * 意味着编码器和解码器中块的个数和输出维度都是一样的 ### 预测 * 预测第t + 1个输出 * 解码器中输入前t个预测值 * 在自注意力中,前t个预测值作为key和value,第t个预测值还作为query ### 总结 * Transformer是一个纯使用注意力的编码-解码器 * 编码器和解码器都有n个transformer块 * 每个块里使用多头(自)注意力,基于位置的前馈网络,和层归一化 ## Bert * 基于微调的NLP模型 * 预训练的模型抽取了足够多的信息 * 新的任务只需要增加一个简单的输出层 ### Bert架构 * 只有编码器的Transformer * 两个版本: * Base:#blocks=12, hidden size=768, heads=12, parameters=110M * Large:#blocks=24, hidden size=1024, heads=12, parameter=340M ### 对输入的修改 * 每个样本是一个句子对 * 加入额外的片段嵌入 * 位置编码可学习 ![](images/bert.png) ### 预训练任务1:带掩码的语言模型 * Transformer的编码器是双向,标准语言模型要求单向 * 带掩码的语言模型每次随机(15%概率)将一些词元换成 * 因为微调任务中不出现 * 80%概率下,将选中的词元变成 * 10%概率下换成一个随机词元 * 10%概率下保持原有的词元 ### 预训练任务2:下一句子预测 * 预测一个句子对中两个句子是不是相邻 * 训练样本中: * 50%概率选择相邻句子对:this movie is greati like it * 50%概率选择随机句子对:this movie is greathello world * 将对应的输出放到一个全连接层来预测 ## 优化算法 * 小批量随机梯度下降 * 冲量法 * 使用平滑过的梯度对权重更新 ![](images/v.png) * Adam * t更大时,需要做修正 ![](images/Adam1.png) 另外引入s的目的是可能有些特征的值很大,有的很小,所以需要对梯度的各个维度值做重新调整 ![](images/Adam2.png) * Adam对梯度做平滑,且对梯度各个维度值做重新调整 ### Word Embedding embedding后的长度由用户决定,参数矩阵是训练过程中学出来的 ![](images/wordEmbedding1.png) ![](images/wordEmbedding2.png) # 李宏毅 ## 自注意力机制 输入的句子是一排向量,个数不确定。 * One-hot encoding:每个词相互独立,没有任何语义信息,维数过高 * Word-embedding:相同类别的词具有相似的词向量,维数适中 * Count-based:if two words wi and wj frequently co-occur, V(wi) and V(wj) would be close to each other * Prediction based: 通过神经网络来训练一个模型,它的输入是one-hot词向量,输出是下一个词的one-hot词向量,然后取第一层隐藏层作为word-embedding的结果。 输入个数: * 每个词向量对应一个label:判断每个词的词性(Sequence Labeling) * 只输出一个label:电影评论好坏(情感分析 ) * 模型自己决定输出多少个label:seq2seq(机器翻译) Sequence Labeling * 如何才能考虑到一个句子的所有词(上下文) * 计算b1:找出与a1相关的几个词向量(相关度可以用$\alpha$表示,注意力分数),相关度计算可以用Dot-product来算 * 可以同时计算b1,b2,b3,b4 * 可训练的参数只有Wq, Wk, Wv ![](images/self-attention.png) Multi-head Self-attention: * 有时可能需要不同的相关性,所以query可以分成多个head * 不同的head独立计算 Positional Encoding(位置信息) * 对每个位置提供一个位置向量ei,将其与ai相加代替原来的输入 ## Transformer 一种seq2seq的模型。 Input a sequence, output a sequence. The output length is determined by model. Encoder: 输入有多长,输出就有多长 ![](images/transformer_encoder1.png) ![](images/transformer_encoder2.png) Decoder: 1. 先从Encoder的输出中读入数据 2. 通过BOS(begin of sentence)输出第一个词 3. 每次根据前一个词的输入,加上之前所有词和encoder的输出,决定输出是什么 ![](images/transformer_decoder1.png) Self-attention->Masked Self-attention:不用考虑所有的词向量,只考虑当前和之前的词向量。 ![](images/masked_self_attention.png) Decoder要决定输出的句子长度。 需要引入END这个词向量,表示句子结束。 如何读入Encoder的输出数据?**Cross attention** ![](images/cross_attention.png) 一些优化技术: * Copy Mechanism(聊天机器人) * Guided Attention(语言识别) * Beam Search(束搜索)(也不一定就好,因为有时候需要加入一些随机性) * Optimizing Evaluation Metrics(训练时对每个词向量做交叉熵损失,验证时做整个句子的损失) * Scheduled Sampling(在训练时刻意加入一些错误的词向量,减少推理时一步错步步错的现象) ## Self-Supervised Learning(自监督学习) 不需要提供标签,将输入分成两部分,一部分作为标签,一部分作为模型输入,然后自行学习。 * Masking Input 先pre-train做填空题 ![](images/masking_input.png) * Next Sentence Prediction(效果不太好) 在根据不同的下游模型进行微调。 ![](images/fine-tune.png)