作者 | chen212 编辑 | 计算机视觉算法随笔
点击下方卡片,关注“自动驾驶之心”公众号
ADAS巨卷干货,即可获取
点击进入→自动驾驶之心【求职交流】技术交流群
本文只做学术分享,如有侵权,联系删文
全文篇幅较长, 建议mark后预留充足时间, 辨证学习+思考!
一、python
二、C++
三、linux
四、pytorch
五、DL 基础
六、模型部署/压缩
七、传统视觉
八、项目思维, 模型优化
九、智力/code题
一、python
import 加不加点
加点是绝对导入, c2 import 了c1, c3 再来import c2的话, 也可顺利使用到c1
不加点, 则只是相对导入, c3 import c2, 会出现找不到c1的问题.
2. is更严格, 检查value和地址是否一致, ==仅检查value是否一致
3. 装饰器:
def logger(func):
def wrapper(*args, **kw):
print('主人, 我准备开始执行: {} 函数了:'.format(func.__name__))
func(*args, **kw)
print('主人, 我执行完啦.')
return wrapper
@logger
def func(a,b):
print("{}+{}={}".format(a,b,a+b))
func(100, 20)
装饰器: 不改变源函数的情况下添加功能, 场景: 插入日志, 计时.
迭代器: __iter__(), __next__()两个方法: 返回对象本身, 返回下一个对象. (迭代器就是每个元素依次计算出,有前后顺序. 下一个值依赖上一个值)
生成器: 特殊的迭代器, 使用yield关键字一个个"吐"出元素
4. return 和 yield
yield 针对list等循环数据, 可依次一一返回数据;
return只能在循环结束后一次性返回所有结果. (类似py2中的xrange和range, xrange是一个个的生成, range是一口气生成. python3合并了这俩统一为range, 内存更友好了.)
5. map(): 参数1: 函数or数据类型, 可对参数2做对应的函数运算or数据类型变换. py2的map()直接返回(可迭代)结果, py3则返回一个迭代器.(得手动list一下才可看到具体结果.)
6. transpose(): 矩阵转置; reshape(): 打散再重排, 重排规则可指定(最内维度开始还是最外维度开始)
7. python常见数据结构: list, tuple, set(), dict(), numpy的Ndarray, Matrix, pandas的DataFrame. dict按value排序: sorted(dict.items(), reverse=True, key=lambda x:x[1]) [reverse=True降序, False升序]
8. * 星号:
1. * 单星号: 任意长度的可迭代数据
2. ** 双星号: 函数传参; 合并俩字典
9. python深浅拷贝: 都会生成一个看起来相同的对象, 区别: 拷贝出的对象的地址, 是否和原址一样. 体现于, 有两层地址时,
浅拷贝: 最外层地址变了, 内层的可变对象(地址不变但值会变)的地址没变(内层新旧会一起变) 深拷贝: 两层地址均会变, 则不出现新旧一起变.
10. Python的内存管理: 引用计数; 垃圾回收; 内存池
1. 内存池: (预先申请一块空间,有内存需求则往这里使用,不够了再申请新的块. 避免零碎的内存申请需求.)
2. 垃圾回收: 回收无用的内存空间(没对象指向的内存, 视为无用的内存空间). python 解释器来做这个事. 具体做法:
1. 引用计数 为0了就是无对象指向了, 就是垃圾, 回收这块内存!
2. 标记清除: 内存快占满时, python会暂停所有程序, 然后从0开始对所有内存中的数据扫码打标记, 并一次性删除被标记上的数据.
3. 分代回收: 新, 青, 老
新创建的对象都放在新, so创建前都会查新满没满; (满了就会开始启动垃圾回收, 新里面剩下没被回收的, 就放入青. 青到老的维护也是一样)
11. python 的 print加不加括号
python3加, 因为print是一个内置函数,有多个参数
python2不加, 因为print是一个语法结构
12. python list的底层: 是一个数组, 其中存放着PyObject类型的指针
13. python new 和 init 区别:
1. new: 实例创建前, 调用, 返回此实例对象.
2. init: 实例创建后, 调用, 给一些初始值做初始化.
14. 下划线:
1. _a: 前单下划线: 保护内容, 仅允许内部使用or子类继承, from xx import xx时不可被导入
2. __a: 前双下划线: 较之1更严格, 连子类都不可继承, 私有仅本类使用.
3. __a__: 前双下划线: 为python的特殊方法
4. a_: 后单下划线: 避免和python关键词冲突, 无其他含义.
15. lambda x: 2*x+1
16. global 关键字修饰 全局变量
17. python sort函数实现原理: Timsort
1. 先找到list已排好序的块
2. 合并这些块
18. callable: 检查是否可被调用
19. 正则表达式:re.match(pattern, string, flags = 0)
20. GIL全局锁: cpython的特性, 保证一个进程(processing)中同一时刻只有一个线程在执行(避免多个线程间数据干扰, 垃圾回收等带来执行错误.)
线程释放GIL:
1. 线程进入IO操作之前会主动释放, 所以io密集型任务适合用多线程
2. 解释器不间断的运行了1000字节码(py2)或 15毫秒(py3) 后会释放GIL
21. 多线程/多进程:
1. 线程threading cpu io密集场景下使用. m个cpu, 线程数可: m+1、2m、io操作很多的话可10m
2. 进程: from multiprocessing import Process 计算/cpu密集型场景
python多线程怎么占满核? (不可实现, 需要多进程,且尽量每个cpu分一个进程)
线程, 进程, 怎么通信?
1. 多进程通信: queue()进程间做通信, pipe()单个进程内通信,两端分别负责,读写.
2. 多线程通信: 一般线程池写个循环, 然后.start()开启各个线程.
通信内容包括: 锁: lock(), rlock(); .event()设置线程等待; .main_thread()返回主线程
二、C++
1. const, #define宏定义
const定义常量有数据类型, #define则无.
数据类型安全检查只对const有用, define无法查数据可能引发错误.
2. malloc和new
new创建对象, 无需指定内存大小, 返回对象的指针, 对象类型明确.
malloc创建对象, 要指定内存大小, 返回void *, 后续还需自行转换类型.
3. 虚函数 和 纯虚函数 virtual
纯虚函数: 没有被实现, 只是被定义好了
虚函数: 当基类指针指向一个子类对象a, 同时基类中也有a(父子中出现重名).
基类声明了虚函数,则调用子的a, 否则调用基的a. [基中声明virtual,则子中的同名对象可被基使用.] 虚继承: 为了实现多重继承 (不常用)
4. 结构体与类的区别: class与struct
struct默认权限为 public
class默认权限为 private
5. C++多线程(thread)通信: 共享内存, 互斥锁, 死锁,
共享内存出现在: 多线程间涉及共同操作某数据, 则需对数据做"保护". so引入"互斥锁": lock(), unlock 死锁: 出现>=2个互斥量, 均在等待释放而无法工作.
6. 全局变量: 用extern修饰(也可不用), 定义在'{}'外面
7. 内存泄露: 该delete的变量用完后没del, 导致泄露.
可去检测程序运行时内存耗用情况, 出现长期占用或耗用贼大, 则debug排查是否泄露.
8. 构造/ 析构函数:
构造函数: 定义对象后自动执行, 无需用户调用, 无返回类型, 用户也可自行重写构造函数并传参进来; 初始化顺序: 基类 -> 成员类 -> 派生类
析构函数: 完成对象生命周期结束后的, 清理工作. 用户未定义则系统自动实现, 用户定义了则可自行加一些别的功能(如输出一些信息); 初始化顺序: 派生 -> 成员 -> 基类
析构函数 不是必须是 虚函数: 父的析构定义为虚,则父删除某资源子也会删除. 否则父中删除子不变化. 构造函数可以是私有的.
10. static
修饰局部变量时, 出函数体变量仍有效;
声明函数, 则此函数不可被其他文件引用;
修饰全部变量: 则是为了让重名但来自不同文件的变量不共享冲突. (类似修饰函数,static后在其他文件中就不可见了.)
11. 引用和指针:
引用: 不可为空, 一定要初始化(因为它是某个对象的别名), 不可变
指针: 可变, 可为空, 本身是个对象有自己的地址, 它的内容是: 某一对象的内存位置.
智能指针: 自动在对象生命周期结束后, 释放内存. (防止内存管理出错,泄露之类)
一个智指例子: shared_ptr 引用型共享指针
1. 指向内存内的一块资源, 不同指针可指向同一份资源
2. 内部使用引用计数机制, 引用为0则析构函数释放本块资源.(这就完成自动del了)
12. vector和数组的区别, vector扩容?
初始化时: 数组长度需指定,后续不可变, vector无需指定,后续长度可变
名字: 数组名表示数组的首地址,不仅是名字还是个指针, vector的名就只是个名
STL中vector的: 插入, 读取. 时间复杂度,实现原理
1. 连续空间存储, 支持指针访问; 两个迭代器:start, finish 指向首尾 (end_of_storage指向当前可使用空间的最尾部)
2. 插入: push_back 另配置一块空间(原空间大小*2), 然后将原内容拷贝(从0index开始). 再放置新元素, 并随后拷贝插入点后的原元素. 完成后再释放原vector的空间. (so对vector的任何引起空间重配操作, 均会使得指向原vector的迭代器失效) (动态数组也是这样完成插入元素的)
3. 删除: pop_back() 销毁最后一个元素, 不会做内存释放
13. 数组与链表
数组内存连续, 链表内存不连续(存当前节点和下一节点信息)
数组优于链表的点:
1. 省内存,不用每个元素存下一元素的信息;
2. 访问快且可随机访问, 因为是连续内存存放, 根据首地址+index计算就可访问. O(1)
(链表则需要从0开始, 逐步依次走到想要访问的位置)
链表较之数组的优点:
1. 插入删除快, 只涉及修改节点一处的信息变化. (但数组, 插入的话,插入点后的数组都需要移动内存位置,删除也是. 除非修改位置在最尾部才也比较快)
2. 内存利用更灵活. 数组插入,需要重新申请原内存*2的空间, 这较容易带来内存不足. 链表零散灵活的放,不会出现这个隐患.
14. 矩阵怎么在内存中保存
1. txt: vector push和fout.open
2. opencv(.xml): cv::Mat矩阵
15. 内存对齐
以4为倍数内存开始读写 (计算机以字节为单位, 分4 8 16 32等, so需要以4为单位对齐)
对齐规则:
1.基本类型的对齐值就是其sizeof值;
2.结构体的对齐值是其成员的最大对齐值;
3.编译器可以设置一个最大对齐值, 实际对齐值是该类型的对齐值与默认对齐值取最小
16. 散列表(哈希表)的实现原理 (python的dict()的底层也是散列表, 平均查找时复:O(n))
每个结构体包含: key, value, next(指向下一发生散列冲突的记录)
17. 虚拟地址和物理内存
物理内存: 有限, 全部进程都放物理上会不够, 故引出虚拟空间(虚拟的,用户把部分内容放虚拟空间, 使用则需排序调度到物理上)
虚拟地址: 防止一个进程可直接访问另一进程地址空间. 每个进程有4G大小虚拟地址空间, 均是从0编址, 程序直接操作的是虚拟地址空间, so即使相同编址指向的内容也不一样. 保护到了.
18. 栈(先进后出)
栈支持动态扩容 (用支持扩容的数据实现, 栈满时则申请更大的数据, 并做数据搬移)
19. C++的面向对象: 封装, 继承, 多态
1. 多态:允许不同类的对象对同一消息做出响应 (发送消息=函数调用)
通过判断所引用对象的实际类型, 选择调用哪个方法.
2. 多态存在的三个必要条件:
1. 有继承 (有父子关系); 2. 有重写(父子中同函数名实现不同功能, 参数相同); 3. 父类引用指向子类对象(父的参数给到子中的函数)
20. 重载 和 重写
1. 重写出现在父子间, 重载出现在同一个类内,方法名相同但参数不同(个数顺序类型均可不同)
2. 重写要求: 重写方法的修饰范围大于被重写的范围; 重载没这个要求
21. gcc编译过程:
1. 预处理生成.i文件
2. .i转为汇编语言生成.s
3. 汇编编程机器代码, 生成.o
4. .o生成可执行文件 .exe之类的.
三、linux
1. nohup python3 __main__.py >log.log 2>&1 & [后台挂命令并写入log]
2. 查看CPU使用率: atop
3. 显示磁盘使用情况: df -lh
四、pytorch
1. 点乘: a * b or torch.mul(a,b)
2. 矩阵乘法: torch.mm(a, b) 带batch维度的矩阵乘法: (b*m*n) * (b*n*p) -> (b*m*p): torch.bmm(a, b)
3. torch.nn.SyncBatchNorm多卡BN同步(单张卡的batch size很小时, 使用SBN起到扩大网络batch size作用)
4. 初始化:
torch.nn.init.kaiming_normal_ : conv
torch.nn.init.constant_ : bias值 or bn,focal loss之类的需学参数是alpah, beta, gamma
torch.nn.init.xavier_normal_(layer.weight) : 线性层
5. 分布式训练:
model = nn.DataParallel(model) 单机多卡, 主卡算梯度再同步, 主卡负载高;
nn.DistributedDataParallel: 支持单机多卡, 多机多卡: 每个gpu独立进程算梯度, distributed.init_process_group 做各进程间通信
7. 转置: torch.t(tensor) or tensor.T
8. 多进程读数据:
torch.utils.data.DataLoader(dataset,batch_size=,shuffle=,sampler=,batch_sampler)
sampler,batch_sampler: 默认都是None, 生成一系列index. 可自定义sample函数来读取数据并生成index.
dataset完成: 根据index读数据和标签;
DataLoader完成: 可迭代数据装载
9. detach(): 梯度截断,且返回一个无梯度新变量
detach_(): 截断且直接改变本结点(带_下划线的都是原地修改操作)
.data: 类似引用, 获取数据value
10. nn.functional/nn.Module:
nn.functional无需实例化, 创建无参数的layer: 如relu pooling之类的
nn.Module需要学参数的话得手动加: params=xxx, need_grad=True
nn.Module: 继承nn.Module父类, 创建有参数的layer: 如conv. 自动实现forward()
11. torch.Tensor 和 torch.tensor 区别
1. torch.Tensor(data)是类, tensor是ta的实例 (可创建一个空tensor, torch.tensor()不可会报错)
2. torch.tensor(data)是函数, 可把data转为指定的dype类型(or遵从data的类型). (torch.FloatTensor、torch.LongTensor、torch.DoubleTensor等)
12. torch.no_grad 和 required_grad=False 区别
1. torch.no_grad: 是一个上下文管理器,禁止梯度计算. 用在网络推断,减少计算内存
2. required_grad=False: 用来冻结部分网络层不参与梯度/参数更新 (一个是推理时不计算, 一个是训练时不更新)
13. squeeze()删除维度1, unsqueeze()添加维度1.
14. model.train()开启训练, forward() backward()都是自动的
15. PyTorch显存: 参数, 梯度, 网络中间结果, 优化器状态 (显存占用量约为参数量x4)
1. torch.cuda.memory_allocated() 所有tensor占用的显存和
2. torch.cuda.max_memory_allocated() 调用函数起来达到的最大显存占用字节数
第一次优化器状态, 会增加x2显存开销
五、DL 基础:
1. ROI Align: 原图的box整数坐标下采样n倍后变float, 取value则只能坐标取整, 取整后映射回原图位置就偏了. so(双)线性插值, 获取float位置处的value, 解决坐标往回映射的偏移问题.
2. 小目标检测的改进:
1. FPN多尺度, 深浅特征融合
2. 不同大小的目标自适应的选择在哪一层feature map检出(cvpr18的PAN结构, 深至浅融合+浅至深融合, 然后各种concat到一起, 自适应让网络挑选)
3.扩大输入尺寸
4. 多一些全局信息, 加入attention. 让网络能有更多的上下文, 这对大小目标出现遮挡的场景, 也有比较好的目标检出效果. (DETR中做了可视化展示.)
3. 全连接层fc和Conv层的区别: fc输入尺寸不可变, conv可变; 如cls/seg的最后一层数=类别+1, 用conv比fc方便.
4. fl: = -alpha*(1-pt)^gamma * log(pt); CE = -log(pt)
5. softmax = e^xi / sum(e^xj)j=1~k
6. CE交叉熵可作为损失函数的原因: CE是KL散度的一部分, 而KL可量化两个分布相似度.
7. relu为啥比sigmoid好:
1. sigmoid的导数使得网络的梯度容易陷入: 消失和饱和,使训练困难. relu导数则不会.
2. relu计算简单快, 且可给网络带来一定参数稀疏性(负值直接置0,又快又稀疏)
leaky relu和relu:
relu: 小于0的值直接置0, 会导致部分神经元在训练中被"杀死", 但这个特点也达到: 特征稀疏的作用.
leaky relu: 负数值scale下: -10 变 -5. 没有绝对的好坏, 这俩激活函数, 根据场景而定.
8. 1x1卷积的作用:
1. 升降维(channel变化)
2. 增加非线性
3. 可做为一种残差连接方式
9. 上采样有哪些方法?
1. 各种插值(如双线性,近邻,三线性等)
2. 转置卷积(即:反卷积)
3. 空格处填0或复制同样的值(反pooling操作)
转置卷积棋盘格效应: 像素不平滑不连续, 一块块的像棋盘. (kernel不被stride整除,出现棋盘) why: 当stride>1, 反卷积会在图像间隙中补0. 举例kernel=3, 则连续两次卷积,实际计算的是: 2个0+1个原像素; 1个0+2个原像素. 插入0且0的个数不等, 带来了"明暗"不稳定效果, 也即棋盘效应.
怎么缓解棋盘效应?
1. kernel size设计为能被stride整除
2. 补0还是有点粗糙, 可先对原图做(双邻三)线性插值, 然后再遵循1.的尺寸设计, 效果会好很多!
下采样: pooling和卷积均可. (设置好stride就行了.)
10. 欠拟合表现: 模型不收敛, 验证集效果差. (优化办法: 网络加深, 优化器, 初始化, 激活函数等调整)
11. 轻量网络:
1. mobilev1: 深度可分离卷积; 分辨率scale; channel scale
2. shufflenetv2: 提倡cint=cout; 适当使用分组卷积; 提高网络并行度; 合并多个"逐元素"计算
12. 缓解过拟合: (train loss小但val loss大)
1. L1会使得一些w趋近1实现特征稀疏; L2会使一些w value变小(参数太大太敏感)
2. 剪枝网络结构(net太大参数太多, 数据不够训)
3. 补充数据, 加强数据增强
4. Dropout, early stop
13. 稀疏卷积: 对输入输出不为空的数据建立位置哈希表和RuleBook(类似im2col), 只对有效数据计算卷积减少计算量!
14. 陷入局部最优如何解决? (关于怎么判断, 大概就是换一批同分布但不同的数据,模型结果不稳定)
1. 继续训, 看能否跳出(对应的合理设置lr)
2. 重新开启训练, 注意调整lr策略, 优化器, 初始化方式, 激活函数等.
15. batch size过大会怎样?
容易陷入局部最优; 小batch or batch=1可认为是人为加入噪声, 使模型走出鞍点在更大范围内寻找收敛点. 建议: 训练早期大batch size加速收敛, 训练后期小batch引入噪声, (即做纯SGD慢慢把error磨低)
16. 常用数据增强:
1. 几何变换: 平移, 旋转, 翻转, 剪裁, 缩放, 扭曲形变 等
2. 颜色变换: 颜色空间转换, 亮度调整, 模糊(随机加噪), 灰度化 等
3. 一些针对性的, 如目标检测中: cutout, mixup, 随机mask遮挡擦除 等
4. Color Jitter扰动: 对比度, 亮度, 饱和度, 色度 等 调整
5. 项目中: 先用base的aug方法训模型, 然后针对fail case(太小,太大,光照暗亮,形态特殊等原因导致未检出)性设计aug方法
6. 还有些auto aug的库可使用, 但一般项目不会直接用, 训行业大模型时候可实验使用试试
17. 样本/类别不平衡:
1. 一个batch内, 做类别均等采样(保证每个类别都采样到, 每个类别数量多少可自行设计.量不够的类做sample重采样)
2. 对loss weight做权重设置
3. focal loss, OHEM之类的
18. Map计算方法: mean average precision
1. 基于每个recall计算最大的precision, 取所有p的均值, 即ap
19. AUC: 给一个正例,一个负例. 模型预测值 Ptp > Pfp 的可能性.
20. 优化器:
1. SGD: batch(minbatch)内样本的梯度, 结合学习率, 更新权重值
2. SGD+动量: 在SGD的基础上, 不仅仅看当前梯度, 还考虑历史更新方向.
v = alpha*v + beta*diff; wi = wj+v [diff当前梯度, v记录历史更新方向]
3. Adam: 自适应调整学习率. 考虑梯度的一阶二阶信息.
4. AdamW: 理论上: AdamW=Adam+Weight Decay (搭配大点的weight decay效果不错. ssa中甚至开到了0.01, 0.05)
21. loss nan 处理:
先查数据是否有问题; 再code bug,网络层写错啥之类的; 再是训练方式:初始化方式不当,bn没加,学习率过大,损失函数不当等; 最后: 可能除以了0, 考虑是网络过程中出现了0值, 定位到0去改;
22. 7*7卷积 3个3*3卷积:
1. 7x7大卷积, 可作为大图的特征提取conv, 常在网络的第一层使用. 感受野一下子就上来了. (ConvNeXt, resnet的第一层)
2. 多几个3x3可近似7x7, 计算量更少(中间的激活次数还更多增强非线性)
23. 目标检测:
1. two-stage效果比one-stage好的原因:
1. 一定程度在RPN中, 先解决了部分样本不平衡问题(正负的个数, 难度差异)
2. 有点cascade的意思, RPN筛选了一遍好的proposal, 然后还ROI pooling, 再做cls类别细分和reg. (cls细分相对one-stage也会精度更好些的.)
3. two的小目标效果会更好(one的input size开太大容易OOM吃不住, 这直接就影响了小目标的精度. 把输入放大是有助于小目标检出!)
2. 回归函数为啥是 L1 smooth, L1,L2差在哪?
1. L2差在, gt和pred>1时, 平方一下loss太大容易爆炸
2. L1 smooth较之L1的优势是: gt和pred的误差以1为分界点, <1则平方(让梯度足够小,慢慢小步优化), >1则L1(绝对值差, 不像L2平方下爆炸).
24. Transformer:
1. attention如何计算: kq三个向量做"相关性"计算, 再加权到v上.
2. self attention除以根号k: 为了scale!!! :
1. 内积值可能很大, 不normalize的话, 计算量大, 内存占用大, 且有溢出风险;
2. 内积值太大送给softmax,可能导致梯度很小.
softmax的梯度函数: (a: S(xi)*(1-S(xi) or b: (-S(xi)*S(xj))). 先看a, xi太大了, S(xi)趋近于1, 则a值趋近0; 再看b, 当各xi,j间方差很大, 则xi与xj要么一个0要么一个1, b值仍会趋于0. 除以sqrt(dk), 把内积方差值化为1, 就可解决上面俩"隐患".
3. 用 LN(layer norm):
因为样本长度不一致, 用BN需要把各个句子padding到size一致, 添加太多冗余信息.
另外, 一个句子在内部做统计量抓取就可了, 单条句子本身就有独立性和分布特性, 无需跨越不用样本(尤其样本间差异很大时更没必要)
4. swin transformer:
1. 在局部小window内做attention, 计算量从图像分辨率的平方倍优化至线性倍;
2. 使用shifted层级结构+patch merging, 实现多层级多感受野目的(这也是为分割,检测等密集型预测任务做的关键性优化)
5. transformer编码顺序信息:
用sin, cosin实现每个句子中词index等价线性, 不受句子长度影响. 4个词的句子和10个词的句子, index=2处的编码力度一致.
6. transformer一般用什么优化器:
建议选择Adamw(搭配大点的learning rate decay), Adam, 毕竟数据需求大且显存要求高. 理论上: AdamW=Adam+Weight Decay
六、模型部署/压缩
1. 算子融合(计算图优化)
1. chong重参处理:
1. 为何可重参:
1. 卷积的同质性: 一个系数乘在卷积核里, 和卷积后再乘, 作用一样.
2. 相加性: 两个卷积分别处理后再相加, == 卷积核相加, 再做一次conv计算即可)
2. RepVGG (使得部署结构简化且加速, 降低硬件支持需求.)
将并联的带BN的3x3conv,1x1conv,残差结构(可看做1x1的Depthwise conv), 转换为一个3x3conv.
3. 其他重参处理:
1. 多个conv后的结果在维度上concat, 转为: 直接多个卷积的weight,bais concat, 再做一次conv计算即可.
2. average-pooling 转为: conv, conv value为 1/nxn. n是wind大小. 计算更快.
3. 1x1Conv+1x3Conv+3x1Conv(并联)->Conv. 利用conv的相加性.
4. conv和bn融合: cbr -> (cb)r 内存使用和推理速度都好.
2. 低精度推理
一般backward才会涉及高精度防止太小的梯度被抹掉. 推理阶段只forward不back,so可做低精度处理, 加速且省显存, 牺牲一点点精度.
3. 优化卷积/矩阵计算:
1. 工程上:
1. im2col 转化为矩阵乘法然后可继续优化矩阵运算(如gemm)
2. Winograd算法(卷积计算中某些值是一样的在重复计算, so可在conv前计算一次缓存起来, 砍掉冗余计算: https://zhuanlan.zhihu.com/p/74567600)
2. 设计结构上:
1. 多个小卷积代替一个大卷积 (3个3x3 -> 1个7x7)
2. 深度可分离卷积 (转为depth-wise和point-wise)
计算量: depth: cin x h x w x k1 x k2, point: cin x cout x 1x1 x h x w
normal conv: cin x cout x h x w x k1 x k2
3. 1x3 + 3x1 代替3x3. (参数减少但可能带来一些性能下降)
4. 浮点数在计算机中的表示方式: M x 2^E (M: 尾数; E: 阶码)
1. float32位: 1符号 + 8E + 23M
2. double64位: 1符号 + 11E + 52M
5. 量化: 线性量化: 均匀(线性)量化; 非线性量化: 如log量化
对参数x, 做 线性 or log 映射截断 (截掉过多的浮点位)
6. 知识蒸馏:
让student的输出p和teacher的输出q近似, 仅更新student的参数.
loss = CE(y, p) + alpha * CE(p, q)
温度系数T(loss中使用):
知识蒸馏提出softmax-T: exp(zi/T) / sum(expzj/T)
T=1为softmax, T接近0,分布近似one-hot, T越大分布越平缓,起保留相似信息作用.
T怎么取:
T的作用有点类似标签软化, sharpness的one-hot到平滑些的分布(类似label smooth)
训好的teacher预测值q是偏硬的, 加上T则可软化,从而提供更多信息给student(最大值仍指向真类别, 其他维度上值小点,可告诉网络此sample与一些非真类别有相似处) [so想要平滑程度越大, T取值就越大. 蒸馏过程中对T逐渐降温(值减小)到最终T=1]
7. 剪枝:
1. 砍 全连接层的冗余参数
2. 去除 value趋于0的神经元
3. 神经元的梯度过小, 也可被剪
一般: 训好大模型, 剪枝, 微调后, 再让slim model train一会数据, 适应下.
七、传统视觉:
1. 均值/高斯 滤波: nxn转为: (1xn) * (nx1), 计算量由 HWnn 降低为 2nHW.
2. 透射变换, 仿射变换:
1. 仿射: 旋转, 平移, 缩放 等处理.
2. 透射: 2D矩阵 转 3D空间效果, 全景拼接, 等处理.
区别: 仿射是透视的子集, 放射后平行的仍平行, 透射则不一定.
3. 图片去水印: 一般白色阴影水印(且像素值稳定,和背景有融合), 设置像素阈值命中替换value即可.
4. 图像降噪方法: 滤波(均值,中值,高斯,双边)
5. 边缘检测算子: Roberts, Sobel(差分,平滑两部分)
6. 图像锐化: 使模糊图像变清晰. 可用Sobel, Laplace算子等实现
7. canny边缘检测: 一阶偏导计算梯度的幅值和方向, 再用双阈值筛选把边缘连起来.
八、项目思维, 模型优化:
1. 竞品调研(确定大方向, 熟悉这个事已有的解决方案是怎样的: 分割检测做? 已有方案的速度精度等)
2. 数据调研(收集数据, 最好是线上的(or客户的真实数据), 和项目场景一致的开源数据也可; 准备label)
3. 论文调研(看经典的基础论文先了解这个领域, 再追新论文看是否可用到项目上)
4. 项目分析:
1. 分析数据的[统计信息], 根据信息设计数据前处理(检测里anchor和gt匹配, 项目的各类别train数据比例)
2. 分析bad case: 光照条件下效果差, 运动条件下效果差, 目标不全情况下效果差等. 根据具体原因, 做数据优化增强.
5. 数据有噪声:
1. 用类似label smooth这样的loss,减轻对噪声的过拟合;
2. symmetric cross entropy 对称交叉熵, 有对称性的函数可抗噪干扰
九、智力/code题:
1. 25匹5赛道,top3 or top5: 7 or 8
https://zhuanlan.zhihu.com/p/362775051
2. 二维矩阵01连通域个数
3. code.txt[1] 常考code和题集, 【方便给个start啊】
Links:
[1]https://github.com/jiachen0212/hope_better_job.git
① 全网独家视频课程
BEV感知、毫米波雷达视觉融合、多传感器标定、多传感器融合、3D目标检测、目标跟踪、Occupancy、cuda与TensorRT模型部署、协同感知、语义分割、自动驾驶仿真、传感器部署、决策规划、轨迹预测等多个方向学习视频(扫码免费学习)
视频官网:www.zdjszx.com② 国内首个自动驾驶学习社区
近2000人的交流社区,涉及30+自动驾驶技术栈学习路线,想要了解更多自动驾驶感知(2D检测、分割、2D/3D车道线、BEV感知、3D目标检测、Occupancy、多传感器融合、多传感器标定、目标跟踪、光流估计)、自动驾驶定位建图(SLAM、高精地图、局部在线地图)、自动驾驶规划控制/轨迹预测等领域技术方案、AI模型部署落地实战、行业动态、岗位发布,欢迎扫描下方二维码,加入自动驾驶之心知识星球,这是一个真正有干货的地方,与领域大佬交流入门、学习、工作、跳槽上的各类难题,日常分享论文+代码+视频,期待交流!
③【自动驾驶之心】技术交流群
自动驾驶之心是首个自动驾驶开发者社区,聚焦目标检测、语义分割、全景分割、实例分割、关键点检测、车道线、目标跟踪、3D目标检测、BEV感知、Occupancy、多传感器融合、大模型、SLAM、光流估计、深度估计、轨迹预测、高精地图、NeRF、规划控制、模型部署落地、自动驾驶仿真测试、产品经理、硬件配置、AI求职交流等方向。扫码添加汽车人助理微信邀请入群,备注:学校/公司+方向+昵称(快速入群方式)