训练
数据集划分
training set
validation set 训练后,用来调试超参的,比如拿一半数据作为验证集
它的目的是在每个 Epoch 结束后,实时监控模型在未见数据上的表现,防止过拟合,并保存表现最好的那个权重(Best Weight),而不是最后一次迭代的权重。代码验证必须和训练绑定
test dataset 只会用一次的数据集,最后要用这个评价
有的时候会偷懒叫test set,但是实际上会被用来调试超参,实际是validation set
代码测试必须独立:测试是在整个训练完全结束后,拿着那个 Best Weight 去跑测试集。3D 图像在测试时通常不用 Crop,而是用滑动窗口推理来评估全脑图像,还要计算 Dice、Hausdorff 距离,甚至要把预测出的 .nii.gz 保存下来看可视化,这部分代码很长,塞进训练脚本里会极其臃肿。
k-交叉验证
注意是validation数据集的用法
- 在没有足够多数据时使用(这是常态)- 算法: - 将训练数据分割成 K 块 - For i = 1, ..., K - 使用第 i 块作为验证数据集,其余的作为训练集 - 报告 K 个验证集误差的平均- 常用:K = 5 或 10最后把k次的误差取平均,作为这次训练的误差,用这个误差评估这次的超参怎么样
正则
所有正则只在训练中使用
L2正则
这种正则因为更新参数的时候合并同类项后w乘一个小于一的数,也叫权重衰退
l2正则和weight decay其实是等价的
但是l2正则是在loss函数上加东西,weight decay是在优化器上做工作,在参数更新的时候乘以参数,当下用weight decay比较多,二者使用的地方不一样
-
计算梯度
-
时间 t 更新参数
- 通常 ,在深度学习中通常叫做权重衰退
T正则
在计算过程中,对得到的数据结果加入噪音,也叫随机丢弃
保证数据期望不变
dropout(x)的定义:
-
对 加入噪音得到 ,我们希望 (手写笔记:)
-
丢弃法对每个元素进行如下扰动
0 & \text{with probablity } p \\ \frac{x_i}{1-p} & \text{otherise} \end{cases} $$
上面的是dropout方法的定义也就是dropout(x) = x’
dropout的使用

全连接层构建的MLP(多层感知机 是没有卷积的结构)采用dropout,卷积层比如CNN不用这种方法
丢弃概率是一个超参数
精度
梯度爆炸/消失
用relu作为激活函数,因为链式求导法则,最后得出的梯度有部分是通过高阶乘法得来的,这会导致:
-数值溢出
-对学习率敏感
用sigmoid作为激活函数,因为sigmoid的导数在0附近才有明显值,因为 链式求导法则,会有多个sigmoid的导数相乘,每个sigmoid的导数不会太大,所以最终梯度总会很小,会导致:
-梯度变成0
-训练没有进展
-对底层尤为严重,训练深层神经网络可以只训练上方的层

总结:relu的部分组成梯度为1,相当于没限制,多层累乘会导致梯度爆炸;sigmoid的梯度比较小,多层累乘会导致梯度消失
浮点数精度
一般采用16位浮点数 float16
https://blog.csdn.net/leo0308/article/details/117398166 浮点数储存,不是等间隔精度,数值小精度高
float 32位,double64位
数值稳定性和权重初始化和激活函数的选取
权重初始化
思想:
把每层的输出和梯度当作一个随机变量,为了让模型稳定,我们希望设计一个网络能够满足:


和约束:输入的均值=输出的均值=0,输入的方差=输出的方差=1
输出梯度的均值=输入梯度的均值=0,输入的方差=输出的方差=1
推导出需要满足的两个条件:
Xavier 初始

目标是让梯度更新的整个过程数值稳定
在没有激活函数的前提下,第一个式子让每一层的输出(w · X_t-1)方差都相同且为1,第二个式子让每一层的梯度方差相同且为1
很多都是假设,只是为了初始化的时候能让数值尽量稳定
激活函数选取
为了让激活函数前后的值,激活函数前后的梯度:期望为0,方差不变。激活函数应该尽量接近f(x)=x

优化器
1. SGD (Stochastic Gradient Descent, 随机梯度下降)
-
时间: 1951年 (Robbins & Monro),但随反向传播算法在1980年代被广泛应用于神经网络。
-
核心思想:
最原始的“梯度下降”(GD) 需要计算整个数据集的梯度才能更新一次参数,这在数据量大时慢得无法接受。
SGD 的思想是:“看小批次,走一小步”。它每次只从数据集中随机抽取一小批 (mini-batch) 数据来估算梯度,并立即更新参数。
-
性能提升:
- 快: 更新频率极高,使训练在大数据集上成为可能。
- 跳出局部最优: 随机性(噪声)有助于“跳出”损失函数中的鞍点或较差的局部最小值。
-
主要问题:
- 震荡: 梯度估计的噪声很大,导致收敛过程非常“颠簸”(震荡),尤其是在遇到“峡谷”形(一维很陡,一维很平)的损失表面时。
- 调参困难: 对学习率 (Learning Rate) 非常敏感。
2. SGD with Momentum (带动量的SGD)
-
时间: 1964年 (Polyak),1986年被引入神经网络。
-
核心思想:
引入“动量”(Momentum) 概念,模拟物理学中的惯性。它不再只看当前的梯度,而是维护一个“速度”向量,这个速度是过去所有梯度的指数加权平均值。
- 更新方向 =
-
性能提升 (解决SGD的问题):
- 减少震荡: 如果当前梯度方向与“惯性”方向相反(如在峡谷中来回震荡),动量会使其减速。
- 加速收敛: 如果当前梯度方向与“惯性”方向一致(如在平坦的斜坡上),动量会使其加速。
- 更快地穿过平坦区域/鞍点。
-
主要问题:
仍然使用一个全局的、固定的学习率,没有“因材施教”。
(注: Nesterov Accelerated Gradient (NAG, 1983) 是 Momentum 的一个“更聪明”的变体,它会“预判”下一步的位置再计算梯度,通常收敛更快,但Momentum更常作为基础被提及。)
(进化分支: 从这里开始,优化器分化出了一个新思路:自适应学习率)
3. Adagrad (Adaptive Gradient Algorithm)
-
时间: 2011年 (Duchi et al.)
-
核心思想:
“对不同的参数使用不同的学习率”。它为每个参数维护一个历史梯度平方和。
- 更新时,用全局学习率除以“该参数历史梯度平方和的平方根”。
-
性能提升:
- 自适应学习率: 对于梯度一直很大的参数(例如常见的特征),其学习率会迅速下降。
- 利好稀疏数据: 对于梯度很小的参数(例如稀疏数据中不常出现的特征),其学习率会保持得较高,得到更多训练。非常适用于NLP或广告点击率预估 (CTR) 任务。
-
主要问题:
学习率单调递减。因为它累积了所有历史梯度的平方和,这个分母只会越来越大,导致学习率在训练后期变得过小,模型可能提前停止学习。
4. RMSprop (Root Mean Square Propagation)
-
时间: 2012年 (Geoff Hinton, 在Coursera课程中提出)
-
核心思想:
解决了 Adagrad 学习率消失的问题。它不再累积所有历史梯度,而是只计算最近一段时间的梯度平方的指数加权平均值。
- 它“忘记”了很久以前的梯度,只关注近期的梯度大小。
-
性能提升 (解决Adagrad的问题):
- 学习率不消失: 分母(近期梯度均方根)不会无限增大,允许模型在训练后期继续学习。
- 稳定: 在非凸问题(如深度网络)上表现比 Adagrad 更稳定。
(集大成者: 结合“动量”和“自适应学习率”)
5. Adam (Adaptive Moment Estimation)
-
时间: 2014年 (Kingma & Ba)
-
核心思想:
“动量” (Momentum) 和 “RMSprop” 的合体。
- 它像 Momentum 一样,维护一个梯度的指数加权平均值(称为“一阶矩估计”,)。
- 它像 RMSprop 一样,维护一个梯度平方的指数加权平均值(称为“二阶矩估计”,)。
-
性能提升:
- 两者兼得: 既有 Momentum 的快速收敛特性,又有 RMSprop 的自适应学习率特性。
- 鲁棒性强: 对超参数(如学习率)的初始选择相对不那么敏感。
- **成为默认选项:**深度学习领域“默认”优化器,它在大多数任务上都表现得又快又好。
6. AdamW (Adam with Weight Decay)
-
时间: 2017年 (Loshchilov & Hutter)
-
核心思想:
修复了 Adam 在L2正则化(权重衰减)上的一个BUG。
标准的 L2 正则化在 Adam 中效果不佳,因为它会被 Adam 的自适应分母缩放,导致大权重的衰减效果反而变弱了。
AdamW 将权重衰减 (Weight Decay) 与梯度更新解耦(分开处理),使其成为一个独立的步骤,恢复了 L2 正则化应有的效果。
-
性能提升 (解决Adam的问题):
- 更好的泛化能力: 通过正确实现权重衰减,AdamW 提供了更好的正则化效果,防止模型过拟合,泛化能力通常优于原始 Adam。
- 现代标配: 尤其是在训练大型模型(如 Transformers、BERT、GPT)时,AdamW 已基本取代 Adam 成为新的标准。
7. Warm up
黑科技,让学习率先变大再变小,transformer里就用了,总之比直接用adam从大到小效果好
大概是让RMSprop这种变化学习率在最开始学习平均梯度的时候不要一下迈步太快,学习到一些统计信息后再大部训练,可以用一个min(at , adam)的learning rate来干这事

类别不平衡问题
对于大部分为某一不重要种类的分类问题,如果不对损失函数进行加权,模型会“偷懒”。它会发现只要无脑预测所有像素都是“背景”,就能达到98%的准确率,导致损失函数变得很低。但这样的模型是完全无用的,因为它一个肿瘤像素都找不到。
中值频率平衡
因此,在计算loss时,要给不重要的类别很低的权重,重要类别很高的权重
freq = freq / np.sum(freq)#装有各类别出现次数的数组->各类别频率的数组#比如freq = [0.98, 0.005, 0.008, 0.002]。
weight = np.median(freq) / freq#中位数是 (0.005 + 0.008) / 2 = 0.0065'''weight[0] (背景): 0.0065 / 0.98 = 0.0066 (一个非常低的权重)weight[1] (类别1): 0.0065 / 0.005 = 1.3 (一个中等权重)weight[2] (类别2): 0.0065 / 0.008 = 0.8125 (一个中等权重)weight[3] (类别3): 0.0065 / 0.002 = 3.25 (一个非常高的权重)'''
# 这里的权重数组就是从预处理脚本中计算并复制过来的 weights = torch.tensor([0.00852772, 1.80163797, 0.69206569, 1.99316031], device=device)# 将权重传递给损失函数 criterion = nn.CrossEntropyLoss(weight=weights) if model.n_classes > 1 else nn.BCEWithLogitsLoss()分别计算出每个类别的频率
微调
固定底部的一些参数不参与更新->更强的正则
低层次的特征更加通用
高层次的特征跟数据集更相关
一般用更小的学习率,更少的数据迭代
数据处理
数据增强
在训练时加入噪声,测试的时候不做变换
翻转
上下翻转
左右翻转
切割
随机高宽比
随机大小
随机位置
颜色
调亮度
调色温
调饱和度
patch
把一张大的图切成小的、可能重叠的方块。每个epoch在使用该样本的时候随机切一块出来拿出训练。推理时整张图切片分别推理再用“滑动窗口进行拼接
目的:解决显存不足的问题,或者增加训练样本的数量。
缺点:推理的时候速度很慢,一张图要推理好多次小图再拼接
模型设计
性能分析
内存
1. 模型参数 (Parameters & Weights) —— 占比极小
- 内容:卷积核的权重W和偏置b
- 特点:模型一开始加载就占用的固定空间。
2. 优化器状态 (Optimizer States) —— 占比小,但不可忽视
- 内容:如果你用 Adam 优化器,它要存梯度的动量(Momentum)和方差。
- 公式:通常是模型参数量的 2-3 倍。
3. 输入数据 (Input Data) —— 占比一般
- 内容:原始的图像张量。
4. 中间激活值 (Intermediate Activations / Feature Maps) —— 真正的显存杀手
- 内容:网络每一层(卷积、激活、池化)输出的特征图。例:
- 为什么都要存?:因为在**反向传播(Backpropagation)**算梯度时,需要用到前向传播时的这些中间结果。除非你用
checkpointing技术重算,否则它们必须全程赖在显存里不走。
推理效率
由计算量和并行度共同决定
用单像素/体素的flops量比较就可以
3D是2D的卷积核倍,但是一般要用patch,这个很慢
1. 2D 卷积
- 任务:处理 155 张切片,每张大小 240 X 240。
- 单点计算量:计算输出特征图上的每一个像素,需要做
2. 3D 卷积
- 任务:处理 1 个体素块,大小 155 X 240 X 240。
- 单点计算量:计算输出特征图上的每一个体素,需要做
-
patch计算量:gpu吃不下3D图,要切成patch再推理再拼接
- 128 X 128 X 128的patch,50% Overlap,X轴240需要4个patch,Y轴4个,Z轴3个,共48个patch
-
总计算量
指标
性能指标
一个特征里——单个数据:LInear,一维:时序模型,二维:卷积,三维:3D卷积
卷积复杂度
浮点运算次数FLOPS(计算复杂度)
卷积核的长宽深通…,卷积核的个数,卷积核要移动的距离(输入的长宽深…)
假设输入特征图的尺寸为H,W,输入输出通道数都为C,卷积核的大小为n。
FLOPS≈输出H * W * C^2 * n^2
C^2指的是输入通道数和输出同样通道需要的卷积核数
参数量(空间复杂度)
卷积核的长宽深…,卷积核的个数
假设输入特征图的尺寸为H,W,输入输出通道数都为C,卷积核的大小为n。
参数量≈C^2 * n^2
卷积核的宽,高,深,个数(输出的深);输出的宽,高
并行度
一层向前传播同时有多少个单位可以同时计算,比如注意力层是O(n),CNN是O(1),即所有时间片共n个位置的注意力可以同时被计算
最长路径
在并行的前提下,至少多少次计算能得到可以出现能感受到全局的神经元(只有一个也行),比如cnn是O(n/k),RNN是O(n),注意力是O(1)
准确度/loss指标
Dice
Dice 系数是衡量两个集合 A(预测)和 B(真值)重叠度的谐波平均值。
Dice_loss = 1 - Dice
初期对于模型要求很高(重叠少的时候dice很小),梯度高,更新快
可以用输出对loss求导的大小来推导,某个metric对模型的要求(梯度大还是小,更新快不快):
IoU / Jaccard Index
IoU 是衡量两个集合交集与并集的比率。
IoU_loss = 1 - IoU
初期对于模型要求较小,但是后期要求很严
场景举例:
假设有一个大框 A 包含了一个小框 B(B 在 A 内部,且 B 的面积是 A 的一半)。
- IoU 会很低(因为并集很大),这符合直觉:大框包小框,位置不准,得扣分。(目标检测)
- Dice 会相对较高(因为它是平均值),它对尺寸差异不那么敏感。(语义分割)
- 类别不平衡:再目标检测中,锚框你只是在算两个矩形的重叠率。 既然没有“海量背景淹没前景”的问题,Dice “梯度大、抗不平衡”的优势就荡然无存了。
MSE
(y - ŷ)²
这玩意是做回归用的,预测的是一个数,和CE不一样,CE是预测分类的
CE
只关心真实标签对应的预测概率的负对数,多分类里面是在对logic做softmax算概率,不是算ce的过程
perplexity困惑度
用来衡量语言模型的好坏 : (1, ∞)
Π是负对数似然,n是对整句话的所有词的预测概率进行平均,不仅要在多个概率中预测单个词,还要把句子的每个预测做平均
困惑度就是交叉熵损失的指数形式,也就是e^Π。
BLEU
用来衡量机器翻译的好坏 : (0,1)
pn是预测中n- gram的精度,把不同gram的精度乘起来,n一般选四
例:ABCDEF 预测 ABBCD
p1= 4/5, p2 = 3/4, p3 = 1/3, p4 = 0
只懂匹配,不懂语义
经典模型
Normalization层
BN(batch normalization)
是一个全连接层或者卷积层的附加层,在全连接网络和卷积网络里都可以使用,一般不和dropout一起用
可以加速收敛,可以加一个更大的学习率,但是不怎么会改变模型精度
放在全连接或卷积层之后,激活函数之前!/放在全连接或卷积层输入之前
为了解决:
损失出现在最深层,数据在最浅层,深层训练的比较快(靠近损失梯度大),浅层的训练的比较慢(传来的梯度小)导致:
1.浅层训练慢,但是浅层一变,其他层要跟着变
2.深层要不断重新学习很多次,导致收敛变慢
所以需要数值稳定性,此方法提供了缩放,增加了数值稳定性。
但性能的提升实际上可能是在训练中加入了随机噪音
原理:
1.训练时向前传播时,在BN层停下,等待一个batch里的所有样本计算到该层,这对这个batch做归一
2.按特征/通道求平均:对于某个x(特征/通道),对每个x以外的所有维度(批量/批量,长,宽)求均值,标准差,然后对x进行Z-score归一化(\epsilon防止除0)
计算方式: 跨越整个 Batch N 以及空间维度 H, W,为每一个通道 C 计算独立的均值和方差。
适用场景: 标准的计算机视觉分类任务(如 ResNet)。
核心痛点: 极度依赖 Batch Size 的大小。当 Batch Size 太小(例如小于 8)时,计算出的统计量极不稳定,会导致模型性能崩塌。此外,它不适合处理长度可变的序列数据(如 RNN)。
该层额外维持一个running_mean和一个running_var,用动量方法根据每个batch的mean和var更新,不是参数,是buffer,给以后用
在推理的时候和dropout一样必须关掉,一个样本一个输出,自己归一输出就是0了,关掉bn之后会用running_mean和一个running_var
Layer Normalization
为了解决 BN 依赖 Batch Size 和不适应序列数据的问题而生。
- 计算方式: 沿着通道维度
C和空间维度H, W计算,即为每一个样本N独立计算一个全局均值和方差。 - 适用场景: Transformer 架构(目前 LLM 的标配)、RNN,以及几乎所有的 NLP 任务。它在样本自身内部进行归一化,完全不依赖其他样本。
- layernorm就是对一个样本内不同的特征维度进行归一化,batchnorm是同一个batch
Instance Normalization
在batch大小比较小时替代BN使用
可以将它看作是极端化、精细化的 LN。
- 计算方式: 仅仅在空间维度
H, W上计算,即为每一个样本N的每一个通道C独立计算均值和方差。 - 适用场景: 生成对抗网络(GAN)、风格迁移。
- 工程价值: IN 能够直接过滤掉图像实例特定的对比度和亮度信息。在处理医疗图像时,不同设备或扫描批次产生的图像对比度往往有差异,IN 可以很好地消除这种个体风格的干扰,让网络更专注于组织结构的解剖特征。
Group Normalization (GN)
介于 LN 和 IN 之间的一种折中方案,由何恺明团队提出。
- 计算方式: 将通道
C划分为G个组(Group),在每一个样本N的每一个组内(跨越C/G个通道和H, W)计算均值和方差。 - 适用场景: 极其消耗显存的视觉任务(如目标检测、高分辨率图像处理)。特别是在进行 3D 医疗图像分割时,由于三维体积数据庞大,GPU 显存往往只能支撑 Batch Size 设为 1 或 2,此时传统的 BN 完全失效,而 GN 因为其计算不依赖 Batch 维度,成为了最优替代方案。
迁移学习

思想
1.选择一个模型,比如VGG19,固定参数
2.让content_image和style_image分别过模型,把某些层的输出当作特征拿出来
3.建立一个SynthesizedImg(nn.Module),初始化的时候随机一个nn.parameter作为预测的图像,forward()直接返回parameter。(没有x输入了,直接用一个module的参数当输入,迭代器直接更新参数就相当于更新预测的图像了)
4.用统计特征作为指标计算contentloss和styleloss,比如均方误差,协方差,tv指数。
RNN
序列模型任务种类

hidden(隐藏的)是现实存在的东西,可能没观察到
latent(潜在的)是现实不存在的东西,人为加的,比如类别标号
DL领域一般可以互换
RNN前
自(auto)回归,意思是用已知的模型预测同种类型的下一个模型(或其概率),比如分类就不是自回归(图片预测标号)
在先RNN时代,序列模型用马尔可夫过程预测

我们想得到p(xt|x1…xt-1)(在已知的情况下预测下一个某种数据出现的概率),接着在马尔可夫假设下,这个概率只和t之前tao个数据相关,这里再次假设”数据事件可以用f(x)表示,那么把代表某个事件的f(x1),f(x2)…作为MLP的输入来进行预测下一个事件的f(xt)。
RNN思想
一句话是一个样本,一句话有多个词xn,每个词是一个向量

把上一个观察(或者是第一个输入)和上一个隐变量一起更新本次隐变量,用本次隐变量获得本次输出
错位现象
y1实际上在输出的时候是x1预测的,比如x1=“我”x2=“爱”,y1=“爱”y2=“你”,同列的y要比x在前面
多层叠加
实际应用中把多层RNN在空间上叠起来使用,时间方向上长度由输入句子长短决定,不够就padding
空间方向不共享参数,时间方向共享参数(一层RNN一套参数)

梯度裁剪
T时间上的O(T)长度矩阵乘法链容易导致梯度爆炸
·如果梯度长度(矩阵L2大小)超过θ,则投影至θ:
GRU

哈达玛积:逐元素相乘
思想
对于单个时间额外引入R(重置门),Z(更新门)矩阵,大小等同隐状态,一维数组
计算候选隐状态时,重置门概率把历史隐状态的值做(0,1)的变换,代表当下的新理解
更新本次隐状态,更新门调整历史隐状态和当前隐状态的比例。输出和往后传都使用这个隐状态
效果
能比较好的解决梯度消失问题
更新门能允许新的隐状态直接把历史隐状态拿过来,不用过W,有点像加了系数的resnet的直接连接
对梯度爆炸无能为力,有大W就是会爆,得靠梯度裁剪
LSTM

相当于GRU的隐藏状态变成了两个
1.计算candidateMemory这一步在GRU有额外的重置门对ht-1的遗忘,但后面的inputGate代替(1-Z)弥补了这一点
2.forgetGate和inputGate融合历史记忆和候选记忆,在GRU中是单个更新门Z和1-Z的任务,这里两个系数分开了
3.GRU融合完记忆直接作为隐状态输出了,LSTM加入outputGate才输出隐状态,记忆则直接往后传
4.h输出给下一层,c只在时间上传递
深层循环神经网络
一个RNN或者LSTM里,堆叠多个隐藏层,最后再用一个线性层输出,在pytorch中用num_layers决定

双向RNN/LSTM
一次数据正着放,一次数据反着放(输出要再倒回来),然后两个不同rnn的h前后concat到一起(同一列,不加维度),特征倍增然后加一个输出
两个隐藏层互不干扰,输出由两个隐藏层共同经过线性层(softmax得到各个词的概率)得到。
双向RNN不好做推理,因为拿不到将来的信息。但是可以做翻译,语音识别等能拿到大部分信息的任务,对句子进行特征提取。

Seq2Seq
Encoder-Decoder架构思想

把RNN/LSTM的隐藏状态当作一个表示语义的向量,处理这个向量,用这个向量解出需要的东西。
编码器可以是一个RNN,可以是双向,因为一般能看到全局
解码器是一个RNN,因为要预测所以不能是双向
seq2seq思想

Encoder最后一层最后时间的state记为h0
decoder:
训练
1.初始数据在开头和结尾分别加上<bos>和<eos>,nn.embedding能直接通过索引号得到词向量,但是也要训练
2.把h0重复成decoder输入的长度,和label concatenate起来,得到input,送入rnn(预测时只能一步步做,把这个直接写在forward函数里,这样只用传入X和h0就能自动计算真正的输入)
3.每个时间步更新隐状态作为下一个时间步的隐状态,rnn自带此功能
4.rnn的output(output,state)取output出来,过一个linear层,再过一个softmax层(过了才能求loss的倒用来更新)(最大结果的索引,就是预测的索引,训练时只值来更新,最大值预测的索引是哪个不重要)
预测
1.在一个for循环中,不断地预测,在监测到output为<eos>时break
2.初始输入是<bos>,每个时间步把上一步的输出(linear层的输出就行)取最大的索引(预测词索引)当作下一步的输入X(预测时看不到decoder的全部输入)(X是在decoder的forward函数里embedding的,输入只给索引就行 )(X进到forward里也要和h0 concatenate)
3.推理时*<eos>是预测出来的,假如inference的时候一直没有预测到<eos>,要在截断长度break,避免死循环。*
统一长度
训练时,每个句子(样本)的长度可能不一样,但是又想用batch训练,如何把不同长的句子组成batch?
Packing
在词汇表里加入<pad>,然后 裁剪/用<pad>填充句子到”截断长度”,并且记录真实长度,[输入,输入长度,输出,输出长度],这样在for batch_data,len_data,batch_labels,len_labels in dataloader:的时候能直接对一个batch的数据同时处理
packing直接在进模型前就把pad删了,因为RNN的每个时间片是依次进行的,不删pad还会占用计算资源
Padding Mask
保留 <pad> 在矩阵里,但是在计算 Attention时,使用 Mask 矩阵。需要 [Data, Mask](不再强依赖 Lengths 列表,而是依赖由 Data的 <pad> 生成的 True/False 矩阵)
计算时带着 <pad> 一起算,但在 Softmax 之前,把 Mask 为 0 的位置的分数设为负无穷,这样 <pad> 的权重就变成了 0,不会影响其他词。
方便矩阵计算直接并行,然后用mask删除影响。
束搜索
decoder每次的输入都是上一个时间最好的预测结果,这是贪心。
k束搜索在每一个时间取k个最好的预测结果作为候选,控制了复杂度的同时给出了比贪心更优解的可能
Word2Vec
用onehot表示词没有语义效果,用词向量embedding可以表示语义
1.给每个词先用onehot[n]记录
2.隐藏层1,onehot[n]乘ID矩阵W_id[n,300]的到本词的向量ID[300]
3.生成训练数据,用大小为3的滑动窗口,给中间的词猜周边的词,例:The quick brown生成(quick, The),(quick, brown)。用(输入词ID, 目标词ID)的格式储存
4.隐藏层2,W_h[n,300]乘以ID[300]得到各个预测词的softmax
5.更新两个W矩阵,W_id拿出来作为embedding
VGG
拆解设计:
大的卷积核可以用多层小的卷积核替代,一个5X5的卷积核可以看作做两次3X3的卷积核的卷积,同时参数和计算量变小

NiN (NetWorkInNetWork)
在VGG思路的基础上
1.mlpconv层,实际等价于1X1卷积层等价于全连接层
2.Global Average Pooling, GAP,把最后的全连接层用全局池化层代替,也就是n X m X C直接池化成1 X 1 X C
FPN (Feature Pyramid Networks)
C5 ─────────→ P5 ↑ ↑ C4 ───────→ P4 ↑ ↑ C3 ──────→ P3 ↑ ↑ C2 ─────→ P2c2是已经经过1层backbone模型后的特征,把 backbone的特征一层层压缩下去,然后从最后一层开始,c5(=p5)先上采样和上一层特征concat起来,bottleneck融合,然后重复这个步骤,直道第一层收到了从最后一层传回来的特征。得到了一串不同分辨率的特征

resnet
残差设计:
思想
浅层比深层效果好,意味着至少要让深层网络的深层不发挥副作用
让深层的初始状态就是h(x)=x, identity mapping , 可以加快学习速度,比从0开始学习快
f(x)学习的是残差
h(x)= f(x) + x

结构
每个resnet结构里至少得有两层+relu,否则没有非线性变化,即wx+x和wx没有本质区别

优点
梯度传播的时候,“+x”直接连接的存在让梯度能传到到很深的层而不至于变为0,很深的层也能获得前面层的梯度
同时梯度始终比较大,训练速度快
bottom neck设计:
假如输入维度太深,可以先用1X1的卷积核先降维,过卷积后再用1X1升维

通道数对齐
一般提升通道数的同时要将长宽减半,也就是stride=2,来维持计算量不会变太大
使用C_out个1*1卷积核来让x的通道数对齐f(x)

FCN (Fully Convolutional Networks)
思想
第一个真正意义上的端到端像素级分割模型,它解决了类CNN的分类网络输出形状的限制
将 VGG 等经典分类网络中的最后几层全连接层替换成转置卷积层。这样网络无论输入多大的图像,输出都依然是一个空间特征图,而不是一个固定的向量。
FCN 的上采样是粗糙的。它虽然能恢复到原图大小,但丢失了大量在下采样过程中损失的精细空间信息
类似于没有跳跃连接的u-net
转置卷积
把小的输入的每个像素乘以卷积核,然后把重叠部分相加。能恢复到原图大小,但是不能恢复数值

直接在图像上看如上图的算法,假如用矩阵实现的话,实际上就是重新设计了一下W的形状
转置卷积的padding=1会让输出左右变小1
stride会让输出变大
转置卷积层可以用双线性插值初始化
构建W
把X拉平[m*2]
W拉成[m*2的大小],第一行把需要的地方填上W的数值,其他填0,每一行依次右移stride
U-net

上采样
只能恢复原来的大小,不能恢复原来的数值
1.使用Transposed Convolution,卷积实际执行为=矩阵乘法
2.先用双线性插值把图分辨率放大,然后用普通conv2d(可以解决方法1的棋盘格效应)
nnU-net
标准 U-Net + 自动化的工程实践(预处理/训练/推理)
自动化部分
nnU-Net 把预处理的人工经验全部写成了代码规则。当把数据喂给它时,它会执行:
- 提取数据集指纹 (Dataset Fingerprint): 自动分析数据:图像尺寸中位数是多少?体素间距是多少?灰度分布如何?
- 推导管道配置 (Pipeline Configuration): 根据指纹,计算出一套最佳超参数。
- 比如:BRATS 数据的体素间距不一致,它会自动决定如何重采样到标准各向同性。
- 比如:显存只有 11GB,它会自动计算最大的 Patch Size 和 Batch Size。
patch会导致推理速度显著变慢 需要考虑
优化部分
1.激活函数使用Leaky ReLU
2.归一化使用Instance Normalization,因为batchsize一般很小甚至是1
3.深层监督 ,不仅在最后一层计算 Loss,还在 Decoder 的中间层(不同分辨率下)计算 Loss,强迫网络在深层也学好特征
MobileNet
手机端常用,通道数一般很少,卷积的时候通道之间信息交流比较少
MobileNet 将3x3卷积拆分为两步:
1.深度卷积Depthwise :用一个3x3核,但只在自己的通道上卷,不串通道。
2.逐点卷积 :用一个1x1核,负责跨通道融合。
MobileNet V2 参考resnet设计了倒残差结构(Inverted Residuals):
1.1x1 升维: 把通道变多(两头小、中间大)
2.3x3 Depthwise: 在高维空间做廉价的深度卷积。
3.1x1 降维: 融合信息并输出。
MobileNet V3 没看论文
使用了 NAS (神经网络搜索) 自动设计,引入了 SE (Squeeze-and-Excitation) 注意力模块,修改了耗时的激活函数(h-swish)
对比 V2: 同样精度下,速度快 20-30%;同样速度下,精度更高。
ConvNeXt
GoogLeNet
inception块设计:
用四条并行的不同超参的卷积层和池化层来抽取不同的信息,然后再沿通道concatenate。模型参数小,计算复杂度低
模型:
每一段(stage)的inception通道数可能不同,每段之间通道数翻倍,长宽减半

GoogLeNet-V1 inception块

GoogLeNet-V3的inception块

Attention
有多种注意力分数算法方案,对应不同场景,注意力分数是用来加权的,原来的输入加上注意力得到新输入
seq2seq with attention

加性注意力分数
为了得到decoder中的某一个有全局视野的输出
用在RNN/LSTM/Seq2Seq中,两个W,一个v,需要训练,W矩阵可以帮助长度不等的k和q对齐长度然后相加
k:encoder中某一步的隐藏层状态,有原始序列中的每个位置的信息。
q:decoder里上一步输出的隐藏层的值是q(h大小),包含了翻译到现在位置句子的状态
v(不是value):把tanh后有长度的向量压缩成一个数字作为注意力分数的参数矩阵
1.分别计算q和每一时间步的k的注意力分数,过softmax得到注意力权重
2.用注意力权重加权求和”encoder的各隐藏状态”(这个才是value,加性方法key=value)得到Context Vector
3.相比普通seq2seq来说,decoder的输入变了(训练和预测都一样),原来输入词向量concatenate的是h0,现在concatenate Context Vector(而不是h0)
点积注意力分数
同样用在RNN/LSTM/Seq2Seq中,和加性差不多
当k和q等长时可以用,相当于把MLP换成了点积
自注意力
自注意力分数
前提:一个句子内部各个词希望获得自己的上下文信息
KQV
一个词需要其他词的信息,对于一个输入单词 X来说
Query:代表了自身的偏好。 我想查xx东西
Key:代表了X自身的属性特点(让别人用Q*K查询影响大小用的) 我声明是XX东西
Value:代表了对其他词的内涵(被查的时候用注意力权重乘以V加权求和) 我的内容
对于一个输入单词 X,计算其KQV,三个W矩阵在训练时学习且整个序列共用这三个矩阵
想得到一个词X的输出,我们需要该词X的 Q 和所有词的 K 和 V 来提供信息
注意力分数/注意力权重
1.X的Q扫描各个词的K,用Q · K得到:对于X来说,各个词的注意力分数(包括X自己)
2.注意力分数过softmax得到各个词对于X来说的注意力权重
3.X时间片的输出等于""各词的V”对于”X对各词的注意力权重的加权和”
d_k是向量Q的长度,防止注意力分数过大,不同分数softmax太极端,非0即1
位置编码
刚刚的KQV直接计算出的结果是不包含位置信息的,即使输入的词调换顺序,得到的结果还是一样的,所以要引入位置编码
位置编码没有改变注意力机制本身,只是改变了输入
X: (n,d) 输入的词向量矩阵
n: 序列长度,d: 向量维度
i: 第i个词(时间片),2j<第2j列>第2j列>,代表偶数列(向量维度列)

对于某个向量维度(列),不同时间片(行)获得不同的P值,P的矩阵和X同等大小
位置信息直观理解:
1.对于前面一些的列/特征/j 不同的位置/i会让p变化的比较快,后面的特征列的p随i变化较慢,有点像二进制编码,为了记录不同内容,第一位总是变得最快
2.位于i+δ处的位置编码可以通过i处的位置编码的线性投影表示:

投影矩阵跟i(位置)无关,也就是在特征列j确定的前提下,相邻的位置编码是直接能得到互相转换的关系的,所以说位置编码记录的是相对位置信息。
**人话:对于同一个特征,用序列的不同位置的编码,计算得到这两个位置的距离。**也就是说假如模型有了位置编码的信息,也就等价有了两个词相对位置的信息
最终该注意力层输入输入由原X变为X+P,不知道为啥加法能融入位置信息,但是确实是加法
带掩码的自注意力
模型在计算完kq后,进行 Softmax算v的权重之前,会把掩码矩阵 M 加到注意力分数上:
- 对于允许看到的位置(过去和现在),掩码矩阵 M 里的值是 0。加上 0 不改变原分数。
- 对于不允许看到的位置(未来),掩码矩阵 M 里的值是负无穷。
当执行 Softmax 计算时,由于e^{-\infty} = 0,那些被挡住的“未来词”的注意力权重就会被硬生生变成了绝对的 0。这意味着无论未来的词有多重要,当前词对它的注意力分配都是零,彻底切断了信息流。

Transformer
纯粹的基于自注意力机制,完全不要RNN

多头自注意力模块
一个句子内部各个词希望获得自己的上下文信息,重新得到一个有上下文信息的句子

1.在多头自注意力模块中,有一个可训练参数矩阵W_Q、W_K、W_V(Word_Vec, Word_Vec),k,q,v都是输入向量本身
2.多头自注意力模块(比如n头)的batch输入X(Batch_Size, Seq_Len, Word_Vec),和三个”二维的W”一次性算出W*X的结果,重复n遍。也就是每个头有三个注意力W参数。分别计算,互不干扰,不过在矩阵上可以并行。
3.把不同头的结果Concatenate一下 ,多出一个不同头的维度。
4.过一个FC层,把不同头的维度融合成一维,其他维不影响,最后得到和输入一样形状的输出,这个FC层包含在了多头注意力模块里了,没单独写出来
Encoder
add就是残差连接,x = x + Attention(x)
norm是layernorm,layernorm对一个样本内不同的特征维度进行归一化,batchnorm是同一个batch
这里的FC就是一个两层的全连接层,是对每一个时间片的向量分别做FC,时间片之间没有联系

Decoder

这张图少了addnorm
字面的,decoder由self-attention,cross-attention和add&norm组成,重复n次,每次都有一个masked attention/kv-cache和encoder的最后一层进行cross-attention,总之就是效果好,也可以改融合方式。这就是decoder的一个注意力模块
这张图上面部分是算互注意力的时候
下面是算自注意力的时候
Train
几乎和seq2seq一样
1.decoder的输入直接看正确答案,最前面加上<bos>,正确答案直接加上<eos>
2.但是算自注意力的时候要用掩码矩阵(Masked self-attention)来防止未来视,也就是即使有全部输入但是不给模型看,这样矩阵训练很快,免得一个个像推理一样训

inference
-
推理时初始只有<begin>也就是用while循环实现
-
算自注意力的时候某时间片的attention必须只计算当前时间片及以前所有时间片的信息,对比seq2seq预测新词只需要上个词的词向量。在decoder的自注意力部分,这里需要以前所有已预测的词的kv,所以引入kv-cache
-
算自注意力的时候
缓存机制:我们在显存中开辟一块空间,专门存放过去已经计算过的所有词的 K 和 V。
单步输入:预测第 t 个词时,不把前面所有词拼成句子输入,而只输入上一步预测出的那 1 个新词。
提取与拼接:这个新词经过 Linear 层得到自己的 Q_{new}, K_{new}, V_{new}。然后,模型去显存里把过去存好的历史 [K_1, K_2, … K_{t-1}] 拿出来,和 K_{new} 拼在一起。
计算并更新:用 Q_{new} 和拼接后的完整 K 计算注意力权重,再乘以拼接后的完整 V。计算完成后,把新得到的 K_{new}, V_{new} 追加存入 KV-Cache 显存中,供下一步使用。
-
没有q-cache,因为一个q算出来后直接被拿去算当前时间片的输出了,之后的时间片再也用不上这个q了,某个时间片只需要保留k和v让之后的q来取信息
ATvsNAT(non-autoregressive)

对于Decoder来说:
1.AT decoder并行比较差
2.NAT decoder输出平行化,比较快,但是需要其他规范长度的手段,比如
另外一个predictor吃encoder的输入,输出decoder的输入
Transformer变体
语音
1.只看10ms切成一个vector,这样语音也是向量序列
2.O(n方)的复杂度,太长了,所以truncate一下,只截一小段用transformer
图像 ViT

- 它首先把图像像切豆腐一样,切成 像素的 Patch。总共切出 个小块。
- 每个小块通过一个线性层(可以理解为对每个小块做一个全连接操作),展平并投影成一个长度为 768 的向量。此时张量形状变为 。
- 加上位置编码后,输入多头自注意力层(Self-Attention)。每个 Patch 会和其他所有 Patch 进行点积计算(),以此来理解全局的解剖结构。
- 可选:经过多层堆叠后,输出一个包含丰富全局语义的图像嵌入向量矩阵(Image Embedding) (这里假设输出通道从被压缩到了 256,宽和高是 ),就是16*16个块,一个块用一个向量表示,已经没有像素了
人话:切成方块,一个方块展平当成一个时间片
Bert
Bert的动机是预训练,也就是做一个抽取特征很好的网络出来,这样之后对不同的任务只需要改分类器。
Bert就是个没有decoder的transformer,只训练encoder部分,用的时候根据场景再设计decoder
Embedding层
Position embedding进行了改进,现在position embedding是可以自行学习的,不再是固定的三角函数position
加入了Segment embeddingf分割不同句子,并且用seg标签分隔输入

训练目标
encoder是双向的,输入能看到全部信息,但是标准语言模型要求单项。所以训练方案和平时不一样
1.带掩码的语言模型
训练目标由预测完整句子变成把输入遮起来一部分完型填空。
训练时,每次推理随机(比如15%)概率将输入一些词元换成<mask>之后再输入,让encoder模型输出预测mask
但是在这15%的<mask>里,有80%的概率确实变成mask,10%概率不变mask而是变换成另一个词向量,10%保留原有词向量
这样做是因为在微调阶段,真正推理的时候是没有mask的,不能在训练时让模型养成看到mask才推理的习惯
2.下一个句子预测
训练目标是让模型判断一个句子对中的两个句子是不是相邻的
在输出的<cls>位置预测这两个句子是否相邻

<cls>做二分类只是一个手段,我们推理的时候不在意二分类准不准,<cls> 在预训练时已经被打造成了一个完美的“全局句子特征提取器”,可以加点层用来做些其他东西。
mamba
https://blog.csdn.net/v_JULY_v/article/details/134923301
假设我们有一些输入信号,该信号首先乘以矩阵
上面第一步的结果,加上:上一个状态与矩阵相乘(矩阵描述了所有内部状态如何连接)的结果,用来更新状态state
然后,使用矩阵来将状态转换为输出
最后,再利用矩阵提供从输入到输出的直接信号,这通常也称为跳跃连接skip-connection
由于矩阵类似于跳跃连接,因此在没有跳跃连接的情况下,SSM 通常被视为不加D这条线的下图

这其实就是RNN,S4 (Structured State Space)
mamba把ABC换成了根据x变化的函数(D是跳跃链接,通用的)
SSD
目标检测算法
思想:
1.先生成一些锚框出来,每个锚框都算一个训练样本, 锚框分为两类:background和用IoC(交并比)评估的。
2.然后计算每个锚框(生成的)和边缘框(标注的)的IoC指数,选择最大的指标关联该锚框和边缘框,排除这一行和列并重复,直到所有边缘框被关联。
3.预测这个锚框到真实边缘框的偏移

预测
NMS非极大值抑制
1.依次选中非背景类的最大预测值
2.去掉所有其它“与它的IoU值”大于threshold的锚框,得到一个比较干净的输出
SAM
交互式分割(Interactive Segmentation)的概念:我们希望人工在其中一张切片少预测的区域点一个点(提供一个坐标)。系统接收到这个点后,模型能够“聪明地”理解医生的意图,自动把这 60 张切片上相关的缺损边缘全部重新计算并扩充好。2D手动纠错还好,3D纠错需要一个个改每个切片工作量还是有点大,所以要交互式分割。
SAM是个SAM 是“通才”,预训练好的,光能分辨边界,但是不知道人需要什么结果,做成交互式分割模型,点一下模型自动找相似区块,并且把记忆存到memory bank里
原版SAM2是给视频追踪目标用的,但是memory bank不仅可以理解为找某个物体的特征,还能理解为分割图像的方法。SAM 2 既然只要接收到“图像+掩码”就能生成记忆,并且能用这个记忆去指导下一张图的分割。

第一章:训练时 (Training Phase)
在医疗分割的 Baseline设置中,通常是将预训练好的 SAM 拿来,用我们的医疗数据集进行微调(Fine-tuning)。
1. 前向传播:张量在三个组件中的变形记
**A. 图像编码器 (Image Encoder) **
- 输入:一张图像张量 (通常会被复制成 3 通道以适配预训练权重)。
- 操作:image encoder部分使用的是ViT。
- 它首先把图像像切豆腐一样,切成 像素的 Patch。总共切出 个小块。
- 每个小块通过一个线性层(可以理解为对每个小块做一个全连接操作),展平并投影成一个长度为 768 的向量。此时张量形状变为 。
- 加上位置编码后,输入多头自注意力层。每个 Patch 会和其他所有 Patch 进行点积计算(),来理解全局的结构。
- 输出:经过多层堆叠后,输出一个包含丰富全局语义的图像嵌入向量矩阵(Image Embedding) (这里假设输出通道被压缩到了 256,宽和高是 )。
B. 提示编码器 (Prompt Encoder) —— 表达意图
- 输入:医生给定的一个坐标点,比如 ,以及它的标签(1代表前景)。意思就是人工可以直接告诉模型确定这个点是目标。
- 操作:将这个坐标进行位置编码,得到长256的向量(类似于transformer里用正余弦函数表征相对位置),再”直接加上”一个表示“这是正向提示(比如医生希望包含这个区域)”的可学习参数矩阵。前者之后可告诉某个图像块是否和医生的点位置相近,后者可以告诉是否和医生的点图像语义相近。
- 输出:一个提示嵌入向量(Prompt Embedding) ( 是提示点的数量,这里 )。256长才能作为Q查询image encoder的256维的输出。
- 这个编码器的参数都是预训练好的,只是用SAM就不用动,帮你把人工输入翻译给模型的。
C. 掩码解码器 (Mask Decoder) ——“双向交叉注意力”
- 输入:第一次image encoder的输出 和 提示嵌入 (只在第一次提取记忆的时候有提示嵌入,后续memory bank有内容后image encoder的输出只过memory attention)
- 第一步:提示指向图像 (Prompt-to-Image Attention)
- 此时,提示 作为 Query (),图像 作为 Key () 和 Value ()。
- 张量操作:计算 得到注意力分数矩阵。这个操作的物理意义是:“拿着给定的这个坐标点,去全图 的网格里扫描,看哪些位置的图像特征和这个点的意图最匹配”。经过 Softmax 后,把图像特征 加权求和,更新提示向量。
- 第二步:图像指向提示 (Image-to-Prompt Attention)
- 反过来,图像 作为 ,更新后的提示作为 和 。
- 张量操作:再次计算点积。物理意义是:“让图像中的每一个特征网格,去向提示点索要信息”。比如,图像中某个边缘网格本来不确定自己是不是左心室,但它计算发现自己离提示点很近(点积相似度高),于是它就把提示点的信息吸收过来,更新图像特征。
- 第三步:生成掩码 (Mask Prediction)
- 经过几次上述的双向更新后,取出最终的图像特征矩阵。
- 使用你笔记里提到的转置卷积 (Transposed Convolution),将 的特征图逐级放大 倍,恢复到 (甚至是原图大小 )。
- 最后经过一个 MLP(全连接层)输出预测张量 ,每个像素值代表该点是左心室的概率。
D. 记忆编码器 (Memory Encoder, 记为 )
思想:如果你给它一张图像 和对应的真实掩码 ,它会把这对信息压缩成一个高维的“记忆 Token” ,这个掩码其实是sam自己预测出来的,用来指导后续
把这个记忆 Token 理解为一种“超级提示向量”。以做到之后模型不需要医生给坐标点,它本身就包含了“目标长什么样、边界在哪”的浓缩特征。用这个去做双向注意力交叉掩码
Meg-sam说在医疗领域直接训练这个m会过拟合,换训练方法
优化一组伪观测数据 。当成真输入和真标签,但是他们其实是可以优化的参数,优化过后用他们生成一个记忆token m,这个m就是联邦中医院之间互相传输的数据,大概就2MB,这个才是真正的需要训练的东西,其他都是预制的
-
是一个参数化的“伪图像”张量。
-
是一个参数化的“伪掩码”张量。
-
训练记忆编码器
-
前向投影:在每次迭代开始时,把当前这组伪图像和伪掩码 ,送入冻结参数的记忆编码器 。
这一步确保了无论伪数据长多离谱,经过 投影出来的记忆向量 ,绝对是 SAM2 能够完美解析的“合法格式” 。
-
条件分割:拿到合法的 后,用它去条件化控制同样完全冻结的 SAM2 骨干网络 ,对真实的训练切片 进行预测 :
计算 Loss:将预测的掩码 与真实的医生标注 比较,计算 Dice Loss 和 BCE Loss 。
-
反向传播 (核心亮点):
开始计算梯度!梯度顺着计算图往回流:从 Loss 出发 穿过冻结的骨干网络 穿过冻结的记忆编码器 最终落在伪观测数据 的每一个像素点上!
-
-
使用训练好的伪数据编码记忆
特征提取:
- 伪图像 会通过 SAM2 共享的Image encoder,输出一个图像特征矩阵 。
掩码下采样 (Downsampling):
- 伪掩码 原本是 的高分辨率矩阵。它会经过几个步长为 的卷积层(Conv2d, stride=2)。每次卷积,长宽减半,通道数增加。
- 最终,它被压缩成与图像特征一模一样的形状:掩码特征 。
信息叠加 (Element-wise Addition):
- 现在,把图像特征和掩码特征在对应的位置上逐元素相加:。
- 加法不仅保留了图像该位置长什么样的特征(),还打上了“这里是我们需要关注的目标区域”的标记(),为后续的注意力点积做好了完美的分离准备。
生成记忆 Token:
-
相加后的矩阵再经过一个 的卷积层进行通道间的线性组合映射,最终输出的就是我们的记忆张量 (和image encoder输出同形状)。
-
存到memory bank (FIFO队列)里
随机化的伪数据 (x̂, ŷ) │ ▼
冻结的 Image Encoder- 冻结的 Memory Encoder │ ▼ 生成记忆 m │ │ ▼ 真实 CT x ──► 冻结的 SAM 2 │ ▲ │ │ └── 使用记忆 m │ ▼ 预测掩码 │ ▼ 与真实标注 y 比较 │ ▼ Loss │ ▼ 反向传播,只更新 (x̂, ŷ) │ └── 下一轮重新生成 m
也就是要训练个(x,y)出来,然后用这一对来作为memory,核心是合成一个能指导多种肿瘤的伪标签(x,y)出来,用它来合成memory
E.Memory Attention
第一次运行SAM数据直接穿过去,因为memory bank 是空的
后续Prompt Encoder停用,而输入在这里要将mi和imgencoder的输出做交叉注意力
第二章:推理时 (Inference Phase)
和训练没啥区别,停用了prompt encoder和memory encoder,用memorybank里的记忆进行到maskdecoder这一步就结束了
这里的真实工程开发场景,有一个极其重要的性能优化设计,这是它和传统 CNN(如 U-Net)最大的区别:
预计算与缓存 (Pre-compute & Cache) 机制
- U-Net 的痛点:在 U-Net 中,如果医生对预测的掩码不满意,想要手动点一下修改,整张图像必须从头到尾重新过一遍网络,包括极其耗时的深层卷积计算。
- SAM 的工程智慧:
- 重头在编码:图像编码器(ViT)非常庞大,计算 FLOPS 极高。但对于同一张切片 ,无论你后面给什么提示词,它的图像嵌入 是永远不变的。
- 一次计算,多次复用:当切片一载入系统,后台立刻只运行一次图像编码器,生成 ,并将其存入显存 (VRAM)。
- 极速交互:当医生在屏幕上点下一个点(生成提示)。系统只需要运行极其轻量级的“提示编码器”和“掩码解码器”。它们直接从显存中调取提前算好的 进行刚才提到的“双向交叉注意力”点积计算。
- 实际效果:这一步的计算量极小,耗时通常在 50 毫秒以内。医生点一下,瞬间就能看到新的分割结果,体验如丝般顺滑。
第一次运行SAM,人工点一次要分割的东西,模型学习到记忆向量m,存在Memory Bank(FIFO队列),之后
技术方案
copy mechanism
场景
1.有可能不是什么输入都能被训练到,比如输入”我叫ABC”,输出最好能学到直接使用ABC而不是去预测ABC作为输出。
2.给文章写摘要,可能需要从文章中抄
Guided attention
比如语音输入,合成,输出必须把输入每一个字都读出来
guided attention规范了输出的形式
Exposure bias
只学习正确的输入可能会导致验证时一步错步步错,所以训练时要适当给一些有错的输入
BLEU score训练时使用
BLEU不能微分,不好当loss,训练的时候不好用,还是用cross entropy,但是可以把BLEU score的loss当成RL的reward,把decoder当agent
推理的时候可以用,比较好
束搜索
decoder每次的输入都是上一个时间最好的预测结果,这是贪心。
k束搜索在每一个时间取k个最好的预测结果作为候选,控制了复杂度的同时给出了比贪心更优解的可能
贪心提供了一些随机性,在需要创造性的任务表现比较好,beam search在比较固定和需要全局视野的情况表现比较好
硬件
CPU
提升内存稳定性

因为缓存线读取机制和矩阵按行存储,所以按行读取矩阵比按列读取矩阵快。
伪共享
如果两个不同线程在操作不同的变量,但这两个变量恰好被分配在了同一条缓存线中。
当线程 A 修改了变量 ,整个 64 字节的缓存线会被标记为“脏”(Dirty)。
为了保持缓存一致性,当线程 B 想要读取变量 Y 时(即使 Y没有被 A 修改),由于X和Y都在同一条缓存线上,CPU 会强制将这条缓存线从 A 的核心同步(或失效)到 B 的核心。
这种不必要的同步会严重降低多核并行程序的性能。
利用多核
超线程不一定对计算密集型任务有用,即使一个核被超线程了四个,实际还是共用同样数量的寄存器,一个沾满其他的就不能用了
能并行的代码用并行版本
GPU

显存很贵,因为带宽比较大
GPU需要CPU调用从RAM通过PCIe总线把数据送到VRAM,但是VRAM和GPU间通信带宽很大,VRAM是直接焊在板子上的
并行 几千个线程,如果运算并行不太好,比如矩阵太小,效果不会很好
少用控制语句 GPU不太擅长ifelse这种,少用控制语句
通信少在GPU和CPU之间搬数据
ASIC
专门为特定领域定制的计算单元,一般用来代替GPU,同性能,便宜,但是不是非常通用
比如用了systolic array架构的谷歌的TPU,专门加强了矩阵运算用来代替GPU

总结

模型并行
将一个批量的计算切分到多个cpu上
数据并行
一个批量的数据分成n块,每个gpu拿到完整的参数计算一块数据的梯度
性能一般比较好

模型并行
将模型划分成n块,每个gpu拿到一部分的层,比如alexnet的双gpu优化
难优化一些,但是可以解决单gpu显存放不下
通道并行(数据+模型)
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时