【Make YOLO Great Again】栏目专注于从更实战,更深刻的角度解析YOLOv1-v7这个CV领域举足轻重的算法系列,并给出其在业务侧,竞赛侧以及研究侧的延伸思考。欢迎大家一起交流学习,分享宝贵的ideas与思考~
大家好,我是Rocky。
近年来YOLO系列层出不穷,更新不断,已经到v7版本。Rocky认为不能简单用版本高低来评判一个系列的效果好坏,YOLOv1-v7不同版本各有特色,在不同场景,不同上下游环境,不同资源支持的情况下,如何从容选择使用哪个版本,甚至使用哪个特定部分,都需要我们对YOLOv1-v7有一个全面的认识。
故Rocky将YOLO系列每个版本都表示成下图中的五个部分,逐一进行解析,并将每个部分带入业务侧,竞赛侧,研究侧进行延伸思考,探索更多可能性。
【Make YOLO Great Again】YOLOv1-v7全系列大解析(Neck篇)已经发布,大家可按需取用~
而本文将聚焦于Head侧的分享,希望能让江湖中的英雄豪杰获益,也希望大家提出宝贵的建议与观点,让这个栏目更加繁荣。
So,enjoy(与本文的BGM一起食用更佳哦):
YOLOv1-v7论文&&代码大放送
YOLO系列中Head结构的由来以及作用
YOLOv1 Head侧解析
YOLOv2 Head侧解析
YOLOv3 Head侧解析
YOLOv4 Head侧解析
YOLOv5 Head侧解析
YOLOx Head侧解析
YOLOv6 Head侧解析
10.YOLOv7 Head侧解析
YOLOv1论文名以及论文地址:You Only Look Once:
Unified, Real-Time Object Detection
YOLOv1开源代码:YOLOv1-Darkent
YOLOv2论文名以及论文地址:YOLO9000:
Better, Faster, Stronger
YOLOv2开源代码:YOLOv2-Darkent
YOLOv3论文名以及论文地址:YOLOv3: An Incremental Improvement
YOLOv3开源代码:YOLOv3-PyTorch
YOLOv4论文名以及论文地址:YOLOv4: Optimal Speed and Accuracy of Object Detection
YOLOv4开源代码:YOLOv4-Darkent
YOLOv5论文名以及论文地址:无
YOLOv5开源代码:YOLOv5-PyTorch
YOLOx论文名以及论文地址:YOLOX: Exceeding YOLO Series in 2021
YOLOx开源代码:YOLOx-PyTorch
YOLOv6官方讲解:YOLOv6:又快又准的目标检测框架开源啦
YOLOv6开源代码:YOLOv6-PyTorch
YOLOv7论文名以及论文地址:YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object
detectors
YOLOv7开源代码:Official YOLOv7-PyTorch
YOLO系列中的Head侧主要包含了Head检测头,损失函数部分以及Head侧的优化策略。
Head检测头体现了YOLO系列“简洁美”的思想,与two-stage检测算法相比,YOLO取消了RPN模块,设计了特征提取网络+检测头的end-to-end整体逻辑,其对工程的友好特性让工业界顿时开满“YOLO花”。
YOLO系列的损失函数部分可谓是目标检测领域中的“掌上明珠”,其在业务侧,竞赛侧和研究侧都有很强的迁移价值。
【Rocky的延伸思考】
YOLOv1作为YOLO系列的开山鼻祖,其Head侧整体逻辑比较直观,并且对后续的版本影响深远。
YOLOv1中,图片被划分为 7 × 7 7\times7 7×7 的网格(grid cell),每个网络在Head侧进行独立检测。
YOLOv1在Inference过程中并不是把每个单独的网格作为输入,网格只是用于物体ground truth中心点位置的分配,如果一个物体的ground truth中心点坐标在一个grid cell中,那么就认为这个grid cell就是包含这个物体,这个物体的预测就由该grid cell负责。而不是对图片进行切片,并不会让网格的视野受限且只有局部特征。
YOLOv1的输出是一个 7 × 7 × 30 7\times7\times30 7×7×30 的张量, 7 × 7 7\times7 7×7 表示把输入图片划分成 7 × 7 7\times7 7×7 的网格,每一个网格的通道维度等于 30 = ( 2 × 5 + 20 ) 30=(2\times5+20) 30=(2×5+20),代表YOLOv1中每个网格能预测2个框,每个框能预测5个参数 ( x , y , w , h , C ) (x,y,w,h,C) (x,y,w,h,C) 再加上20个种类。
把上述内容转换成通用公式就是网格一共是 S × S S \times S S×S 个,每个网格产生 B B B 个检测框,每个检测框会经过网络最终得到相应的bounding box。最终会得到 S × S × B S\times S\times B S×S×B 个bounding box,每个bounding box都包含5个预测值,分别是bounding box的中心坐标 x , y x,y x,y,bounding box的宽高 w , h w,h w,h 和置信度 C C C。其中 C C C 代表网格中box能与物体的取得的最大IOU值。
铺垫了这么多变量表示,到这里终于可以引出对工业界产生深远影响的YOLOv1的损失函数,YOLO系列的后续版本的损失函数都是从这个最初的形式优化而来。
$$\begin{aligned}
Loss = \lambda_{coord} \sum{S2}{i=0}\sum{B}_{j=0}I{obj}{ij}\lbrack(x_i - \hat{x}_i)^2 + (y_i - \hat{y}_i)^2\rbrack \
乍一看YOLOv1的损失函数十分复杂,don’t worry,接下来Rocky将进行详细分析。
整体上来看,YOLOv1的损失函数可以分为检测框的回归损失,置信度误差损失以及分类误差损失。
公式中第一行和第二行代表了检测框的中心点和宽高的回归损失,其中 I i j o b j I^{obj}_{ij} Iijobj 表示第 i i i 个网格的第 j j j 个box是否去预测这个物体,即这个box与物体的ground truth box的IOU值和其他box相比是否是最大的。如果是,那么 I i j o b j = 1 I^{obj}_{ij} = 1 Iijobj=1,否则 I i j o b j = 0 I^{obj}_{ij} = 0 Iijobj=0,而YOLOv1中每个网格只有2个box,还是比较简单的。值得注意的是宽高回归损失中使用了开根号的操作,这是考虑到了小目标与大目标对应的检测框会存在差异,并消除这个差异。不开根号时,损失函数往往更倾向于调整尺寸比较大的检测框。例如,12个像素点的偏差,对于 888 × 888 888\times888 888×888 的检测框几乎没有影响,因为此时的IOU值还是很大,但是对于 28 × 48 28\times48 28×48 的小检测框影响就很大。
公式中第三行和第四行代表了置信度误差损失,分别是含物体的置信度误差损失和不含物体的置信度误差损失。当网格中含有物体时, I i j o b j = 1 , I i j n o o b j = 0 I^{obj}_{ij} = 1, I^{noobj}_{ij} = 0 Iijobj=1,Iijnoobj=0,并且置信度ground truth值 C ^ i = 1 \hat{C}_i = 1 C^i=1;当网格中不含物体时, I i j o b j = 0 , I i j n o o b j = 1 I^{obj}_{ij} = 0, I^{noobj}_{ij} = 1 Iijobj=0,Iijnoobj=1,并且置信度ground truth值 C ^ i = 0 \hat{C}_i = 0 C^i=0。包含物体的预测置信度 C i C_i Ci 为网格中box与物体ground truth box能取到的最大的IOU值,这很好理解,计算逻辑也直接明了。但是不包含物体的置信度误差损失究竟是怎么回事?don’t worry,不包含物体的置信度误差损失包含两部分,一部分是包含物体的网格中的两个box中不负责预测的那个box,另外一部分是不包含物体的网格中的box,让他们都往0回归吧!
目标检测中存在一个常见问题,那就是类别不均衡问题,YOLOv1中也不例外。在一张图像中物体往往只占一小部分,大部分还是背景为主。故在置信度误差损失中设置了 λ c o o r d = 5 \lambda_{coord} = 5 λcoord=5 和 λ n o o b j = 0.5 \lambda_{noobj} = 0.5 λnoobj=0.5 来平衡含物体的置信度误差损失和不含物体的置信度误差损失两者的权重,让模型更加重视含物体的置信度误差损失。
公式中第五行代表了分类误差损失,只有当 I i j o b j = 1 I^{obj}_{ij} = 1 Iijobj=1 时才会进行计算。
到这里,YOLOv1损失函数的解析就告一段落了。接下来我们看一下YOLOv1Head侧的优化策略:
YOLOv1的缺陷:
【Rocky的延伸思考】
YOLOv2的Head侧在YOLOv1的基础上进行了网络结构和损失函数的改进,并且大名鼎鼎的anchor box也在此引入。
YOLOv2在YOLOv1的基础上去掉了最后的全连接层,采用了卷积和anchor boxes来预测检测框。由于使用卷积对特征图进行下采样会使很多细粒度特征(Fine-Grained Features)的损失,导致小物体的识别效果不佳。故在YOLOv2Head侧中引入了passthrough layer结构,将特征图一分为四,并进行concat操作,保存了珍贵的细粒度特征。
刚才提到了YOLOv2使用卷积和anchor box来输出检测框,那么到底anchor box机制是怎么样的呢?
YOLOv1中每个网格预测两个检测框,并让最合适的检测框向ground truth框进行回归修正。在YOLOv2中,Head侧不对检测框的宽高进行直接硬回归,而是将检测框与Anchor框的偏差(offset)进行回归,并且每个网格指定 n n n 个anchor box。在训练时,只有最接近ground truth的检测框进行损失的计算。在引入anchor box后,mAP由69.5下降至69.2,原因在于每个网格预测的物体变多之后,召回率大幅上升,准确率有所下降,总体mAP略有下降。
在引入anchor box之后,又使用了Dimension Clusters操作,使得anchor box的宽高由聚类算法产生。没错,就是K-means算法~~(K-NN算法)~~。使用K-means算法获得anchor box的具体细节可以在我之前的文章【三年面试五年模拟】算法工程师的独孤九剑秘籍(第七式)中取用。YOLOv2Head侧输出的feature map大小为 13 × 13 13\times13 13×13,每个grid cell设置了 5 5 5 个anchor box预测得到 5 5 5 个检测框,一共有 13 × 13 × 5 = 845 13\times13\times5=845 13×13×5=845 个检测框,与YOLOv1相比大大提高目标的定位准确率。
优化了anchor box预设置后,YOLOv2设计了Direct location prediction操作来支持检测框与Anchor框的偏差(offset)回归逻辑。与YOLOv1相比,YOLOv2中每个检测框输出5个偏差参数 ( t x , t y , t w , t h , t o ) (t_x,t_y,t_w,t_h,t_o) (tx,ty,tw,th,to),为了将预测框的中心点约束在当前grid cell中,使用sigmoid函数 σ ( ⋅ ) \sigma(·) σ(⋅) 将 t x 和 t y t_x和t_y tx和ty 归一化处理,将值约束在 [ 0 , 1 ] [0,1] [0,1] 之间,这使得模型训练更稳定。
其中 P w P_w Pw 和 P h P_h Ph 代表anchor box的宽高, c x c_x cx 和 c y c_y cy 代表grid cell左上角相对于feature map左上角的距离。
讲完了网络结构的改进和anchor box,接下来就是损失函数的改进了:
$$\begin{aligned}
Loss = \lambda_{coord} \sum{S2}{i=0}\sum{B}_{j=0}I{obj}{ij}(2 - \hat{w}\times \hat{h})[(x_i - \hat{x}_i)^2 + (y_i - \hat{y}_i)^2]\
可以看出,在计算检测框的回归损失时,YOLOv2去掉了开根号操作,进行直接计算。但是根据ground truth的大小对权重系数进行修正: ( 2 − w ^ × h ^ ) (2 - \hat{w}\times \hat{h}) (2−w^×h^)(这里 w ^ \hat{w} w^ 和 h ^ \hat{h} h^ 都归一化到 [ 0 , 1 ] [0,1] [0,1]),这样对于尺度较小的预测框其权重系数会更大一些,可以放大误差,起到和YOLOv1计算平方根相似的效果。
在训练前期(iter < 12800),YOLOv2还会进行 0.01 ∑ i = 0 S 2 ∑ j = 0 B I i j n o o b j [ ( p x − x i ) 2 + ( p y − y i ) 2 + ( p w − w i ) 2 + ( p h − h i ) 2 ] 0.01 \sum^{S^2}_{i=0}\sum^{B}_{j=0}I^{noobj}_{ij}[(p_x - x_i)^2 + (p_y - y_i)^2 + (p_w - w_i)^2 + (p_h - h_i)^2] 0.01∑i=0S2∑j=0BIijnoobj[(px−xi)2+(py−yi)2+(pw−wi)2+(ph−hi)2] 的计算,表示对anchor boxes和检测框进行坐标回归,促进网络学习到anchor的形状。
【Rocky的延伸思考】
YOLOv3Head侧在YOLOv2的基础上引入了多尺度检测逻辑和多标签分类思想,优化了损失函数。
YOLOv3在Neck侧的基础上顺势而为融合了3个尺度,在多个尺度的融合特征图上分别独立做检测。再将Anchor Box由5个增加至9个,每个尺度下分配3个Anchor Box,最终对于小目标的检测效果提升明显。并且多尺度+9anchor box让YOLOv3的整体检测性能达到了一个比较从容的level。
再说多标签分类思想,我大受震撼。首先什么是多标签分类呢?我们先对几种常见的分类逻辑做一个对比:
YOLOv3将YOLOv2的单标签分类改进为多标签分类,Head侧将用于单标签分类的Softmax分类器改成多个独立的用于多标签分类的Logistic分类器,取消了类别之间的互斥,可以使网络更加灵活。YOLOv2使用Softmax分类器,认为一个检测框只属于一个类别,每个检测框分配到概率最大的类别。但实际场景中一个检测框可能含有多个物体或者有重叠的类别标签。Logistic分类器主要用到Sigmoid函数,可以将输入约束在0到1的范围内,当一张图像经过特征提取后的某一检测框类别置信度经过sigmoid函数约束后如果大于设定的阈值,就表示该检测框负责的物体属于该类别。
YOLOv3的损失函数在YOLOv2的基础上进行了改进:
$$\begin{aligned}
Loss = \lambda_{coord} \sum{S2}{i=0}\sum{B}_{j=0}I{obj}{ij}(2 - \hat{w}\times \hat{h})[(x_i - \hat{x}_i)^2 + (y_i - \hat{y}_i)^2]\
YOLOv3中置信度误差损失和分类误差损失都使用交叉熵来表示。
【Rocky的延伸思考】
YOLOv4的Head侧沿用了YOLOv3的整体架构,并引入了CIOU Loss和DIOU NMS来提升Head侧的整体性能。
在YOLOv3中,使用MSE(均方误差)损失函数对检测框的中心点以及宽高坐标进行优化。在MSE损失函数的逻辑中,将检测框的中心点和宽高坐标作为独立的变量对待,但是实际上他们之间是有关联的。所以一个直观的解决方案是使用IOU损失代替MSE损失。
YOLOv4论文中依次提到了IOU Loss,GIOU Loss,DIOU Loss以及CIOU Loss。其中IOU Loss是其他Loss的基石,也最为简单,公式如下:
L I O U = 1 − I O U ( A , B ) = 1 − A ∩ B A ∪ B L_{IOU} = 1 - IOU(A,B) = 1 - \frac{A \cap B}{A \cup B} LIOU=1−IOU(A,B)=1−A∪BA∩B
其中 A A A 代表检测框, B B B 代表ground truth, I O U IOU IOU 代表两者的交并比。
IOU Loss思想简洁明了,但存在两个问题:
而GIOU Loss能改进这些问题,其在检测框与ground truth之外映射一个最小外接矩形来缓解IOU Loss存在的问题,其公式和图解如下:
L G I O U = 1 − G I O U = 1 − I O U ( A , B ) + ∣ C − A ∪ B ∣ ∣ C ∣ L_{GIOU} = 1 - GIOU = 1 - IOU(A,B) + \frac{|C - A \cup B|}{|C|} LGIOU=1−GIOU=1−IOU(A,B)+∣C∣∣C−A∪B∣
其中 C C C 代表最小外接矩形,最后一项代表使用差集来进行约束与惩罚。
但出现上图右侧的情况时,GIOU Loss被打回原形。
DIOU Loss在GIOU Loss基础上提出了中心点距离的概念,来改进GIOU Loss被打回原形的问题。
对ground truth和检测框的中心点进行度量,引入位置信息的同时加快了损失函数的收敛,其公式如下:
L D I O U = 1 − D I O U = 1 − I O U ( A , B ) + D 2 2 D 1 2 L_{DIOU} = 1 - DIOU = 1 - IOU(A,B) + \frac{D_2^2}{D_1^2} LDIOU=1−DIOU=1−IOU(A,B)+D12D22
但是DIOU Loss仍存在检测框长宽比带来的新问题:
最后,就是YOLOv4中使用的CIOU Loss登场了。CIOU Loss在DIOU Loss的基础上考虑了检测框长宽比的因素,将检测框回归损失的三大逻辑:重叠面积、中心点距离,长宽比进行了有效整合,其公式如下:
L C I O U = 1 − C I O U = 1 − I O U ( A , B ) + D 2 2 D 1 2 + v 2 ( 1 − I O U ( A , B ) ) + v L_{CIOU} = 1 - CIOU = 1 - IOU(A,B) + \frac{D_2^2}{D_1^2} + \frac{v^2}{(1 - IOU(A,B)) + v} LCIOU=1−CIOU=1−IOU(A,B)+D12D22+(1−IOU(A,B))+vv2
其中 v v v 代表了长宽比一致性的参数:
v = 4 π 2 ( a r c t a n w g t h g t − a r c t a n w h ) 2 v = \frac{4}{\pi^2}(arctan\frac{w^{gt}}{h^{gt}} - arctan\frac{w}{h})^2 v=π24(arctanhgtwgt−arctanhw)2
在使用了CIOU Loss之后,YOLOv4的整体损失函数如下所示:
$$\begin{aligned}
Loss = CIOULoss + \sum{S2}{i=0}\sum{B}_{j=0}I{obj}{ij}[C_i\log(C_i) + (1-\hat{C}_i)\log(1-\hat{C}_i)]\
介绍完YOLOv4的CIOU Loss以及整体损失函数,接下来我们来介绍YOLOv4对NMS操作的优化。
YOLOv4改进了YOLOv1-v3中使用的传统NMS操作,使用了DIOU思想计算“IOU”值,进一步优化了后处理效果。CIOU思想在训练中有很大作用,但是在Inference过程中,并没有ground truth的信息,所以使用DIOU足矣,且能减少计算量。
【Rocky的延伸思考】
YOLOv5的Head侧在YOLOv4的基础上引入了Auto Learning Bounding Box Anchors(自适应anchor box)和邻域正负样本分配策略。
YOLOv5的anchor box是自适应于训练数据的,会根据不同的训练数据自动学习适配相应的anchor box。代码中具体的对应函数是check_anchor函数。
由于增加高质量正样本检测框可以显著加速收敛,故YOLOv5设计了相应的邻域正负样本分配策略,其主要流程如下:
比起yolov4中一个ground truth只能匹配一个正样本,YOLOv5能够在多个grid cell中都分配到正样本,有助于训练加速和正负样本平衡。
【Rocky的延伸思考】
YOLOx的Head侧在YOLOv5的基础上在网络结构中引入了Decoupled Head,并使用anchor-free思想和SimOTA正负样本分配策略进行损失函数的计算与优化。
YOLOx使用了三个Decoupled Head(解耦头),分别聚焦cls(分类信息),reg(检测框信息)和IOU(置信度信息)。常规的检测头在特征的表达与学习能力上比起Decoupled Head有所欠缺,并且Decoupled Head模块能加快模型的收敛速度。
除此之外,YOLOx还使用anchor-free思想,比起YOLO系列中常规的anchor-based,在Head侧可以减少约 2 3 \frac{2}{3} 32 的参数。比起anchor-based方法使用先验知识设计anchor尺寸,anchor-free思想将感受野作为“anchor”信息。上述三个Decoupled Head中最上面的分支对应着大anchor框,中间的分支对应着中等anchor框最下面的分支对应着小anchor框。最后的输出将这个三个分支融合成一个 85 × 8400 85 \times 8400 85×8400 的特征向量。
接下来就是介绍YOLOx的正负样本分配策略了,我们知道目标检测场景一张图像中往往负样本占绝大多数,而正样本只是少数。为了获得更多高质量的正样本,YOLOx中设计了样本初筛+SimOTA逻辑。
在样本初筛中,有两种方法来筛选正样本:
经过初筛之后,再使用SimOTA进行精细化筛选。其主要流程如下:
YOLOv5的正负样本分配策略是基于邻域匹配,并通过跨网格匹配策略增加正样本数量,从而使得网络快速收敛,但是该方法属于静态分配方法,并不会随着网络训练的过程而调整。YOLOx使用的SimOTA能够算法动态分配正样本,进一步提高检测精度。而且比起OTA由于使用了Sinkhorn-Knopp算法导致训练时间加长,SimOTA算法使用Top-K近似策略来得到样本最佳匹配,大大加快了训练速度。
【Rocky的延伸思考】
YOLOv6的Head侧和YOLOx一样使用Anchor-free逻辑和SimOTA标签分配策略,并在其基础上改进了Decoupled Head(解耦检测头)结构,在损失函数中引入了SIoU边界框回归损失。
YOLOv6依然采用了Decoupled Head结构,并对其进行了精简设计。YOLOX的检测头虽然提升了检测精度,但一定程度上增加了网络延时。YOLOv6采用Hybrid Channels策略重新设计了一个更高效的Decoupled Head结构,在维持精度的同时降低了延时,缓解了Decoupled Head中 3 × 3 3\times 3 3×3 卷积带来的额外延时开销。
为了进一步提升回归精度,YOLOv6使用了SIoU检测框回归损失函数来优化网络的学习过程。
YOLOv4中的CIoU Loss虽然考虑到检测框与ground truth之间的重叠面积、中心点距离,长宽比这三大因素,但是依然缺少了对检测框与ground truth之间方向的匹配性的考虑。SIoU Loss通过引入了所需回归之间的向量角度,重新定义了距离损失,有效降低了回归的自由度,加快网络收敛,进一步提升了回归精度。
SIOU Loss论文地址:SIoU Loss: More Powerful Learning for Bounding Box Regression
【Rocky的延伸思考】
YOLOv7的Head侧使用了和YOLOv5一样的损失函数,引入RepVGG style改造了Head网络结构,并使用了辅助头(auxiliary Head)训练以及相应的正负样本匹配策略。
RepVGG style在训练过程中可以通过多路分支提升性能,推理可以通过结构重新参数化实现推理速度的加快。
Rocky之前也对RepVGG style思想进行迁移性实验,发现RepVGG style在不同模型中的兼容性并不是很强,往往需要针对当前的模型和场景进行大量调参才能展现效果。
YOLOv7在Head侧引入了辅助头(auxiliary Head)进行训练。正常网络训练如上图(a)所示,而用辅助头参与训练时,将对模型的训练进行深度监督,如上图(b)所示。将辅助头和检测头的损失进行融合,相当于在网络高层进行局部的模型ensemble操作,提升模型的整体性能。
而YOLOv7的正负样本分配策略正是围绕着检测头(lead head)与auxiliary Head进行设计,其主要是将YOLOv5和YOLOx的正负样本分配策略相结合:
使用YOLOv5的正负样本分配策略分配正样本。
使用YOLOx的正负样本分配策略确定正样本。
YOLOv7的正负样本分配策略相较于yolov5,加入了loss aware,利用当前模型的表现,能够实时精筛;而较于只使用YOLOX中的SimOTA算法,能够提供更精确的先验知识。
上图(d)中,lead head和auxiliary head使用一样的正负样本匹配策略,通过让浅层的auxiliary head学习到lead head已经获得的特征,让lead head更能专注于学习尚未学习到的剩余特征。
而上图(e)中,在使用lead head和auxiliary head一起优化模型的时候,auxiliary head的正样本是较为“粗糙的“,主要是通过放宽正样本分配过程的约束来获得更多的正样本。lead head中的一个anchor如果匹配上ground truth,则分配3个正样本,而同样的情况下auxiliary head分配5个。lead head中将top10个样本IOU求和取整,而auxiliary head中取top20。auxiliary head的学习能力不如lead head强,为了避免丢失需要学习的信息,将重点优化auxiliary head的召回率。而lead head可以从高recall的结果中筛选出高精度的结果作为最终输出。lead head和auxiliary head的损失函数权重设置为 4 : 1 4:1 4:1。
【Rocky的延伸思考】
Rocky将算法高价值面试知识点即“三年面试五年模拟”之独孤九剑秘籍前六式进行汇总梳理成汇总篇,并制作成pdf版本,大家可在公众号后台 【精华干货】菜单或者回复关键词“三年面试五年模拟” 进行取用。由于“三年面试五年模拟”之独孤九剑秘籍pdf版本是Rocky在工作之余进行整理总结,难免有疏漏与错误之处,欢迎大家对可优化的部分进行指正,Rocky将在后续的优化迭代版本中及时更正。
Rocky一直在运营技术交流群(WeThinkIn-技术交流群),这个群的初心主要聚焦于技术话题的讨论与学习,包括但不限于CV算法,算法,开发,IT技术以及工作经验等。群里有很多人工智能行业的大牛,欢迎大家入群一起学习交流~(请添加小助手微信Jarvis8866,拉你进群~)