본 글은 자율주행하트 공개 계정의 승인을 받아 재인쇄되었습니다.
자체 개발 칩을 생산하는 몇몇 주요 제조업체를 제외하면 대부분의 자율주행 회사는 TensorRT와 분리될 수 없는 NVIDIA 칩을 사용할 것이라고 믿습니다. TensorRT는 다양한 NVIDIA GPU 하드웨어 플랫폼에서 실행되는 C++ 추론 프레임워크입니다. Pytorch, TF 또는 기타 프레임워크를 사용하여 훈련한 모델은 먼저 onnx 형식으로 변환한 다음 TensorRT 형식으로 변환한 다음 TensorRT 추론 엔진을 사용하여 모델을 실행함으로써 NVIDIA GPU에서 이 모델을 실행하는 속도를 향상시킬 수 있습니다. .
일반적으로 onnx와 TensorRT는 상대적으로 고정된 모델(모든 수준, 단일 분기 등의 고정된 입력 및 출력 형식 포함)만 지원하고 가장 바깥쪽 동적 입력만 지원합니다(onnx 내보내기는 Dynamic_axes 매개변수를 설정하여 결정할 수 있음) 차원의 동적 변화를 허용하기 위해) 그러나 인식 알고리즘의 최전선에서 활동하는 친구들은 중요한 개발 추세가 표적 탐지, 표적 추적, 궤도 예측, 의사 결정 계획 등을 포괄할 수 있는 end-2-end라는 것을 알게 될 것입니다. 구동 링크이며 전면 및 후면 프레임과 밀접한 관련이 있는 타이밍 모델이어야 합니다. 엔드투엔드 표적 탐지 및 표적 추적을 달성하는 MUTR3D 모델을 대표적인 예로 사용할 수 있습니다(모델 소개는 참조). :)
MOTR/MUTR3D에서는 진정한 엔드투엔드 다중 객체 추적을 달성하기 위한 라벨 할당 메커니즘의 이론과 예를 자세히 설명합니다. 자세한 내용을 보려면 링크를 클릭하십시오: https://zhuanlan.zhihu.com/p/609123786
이 모델을 TensorRT 형식으로 변환하고 정밀 정렬을 달성하는 경우, 심지어 fp16 정밀 정렬도 일련의 동적 요소에 직면할 수 있습니다. 예를 들어, 여러 if-else 분기, 하위 네트워크 입력 형태의 동적 변경, 동적 처리가 필요한 기타 작업 및 연산자 등
Pictures
MUTR3D 아키텍처 전체 프로세스에 많은 세부 사항이 포함되므로 상황이 다릅니다. 전체 네트워크의 참고 자료를 보거나 Google에서 검색해도 플러그 앤 플레이 솔루션을 찾기가 어렵습니다. 한 달 이상의 지속적인 분할과 실험을 통해 하나씩 해결할 수 있습니다. 그리고 블로거의 연습(TensorRT에 대한 이전 경험은 많지 않았고 그 성질을 이해하지 못했습니다). 저는 많은 두뇌를 사용했고 많은 함정을 밟았습니다. 마침내 성공적으로 변환하고 fp32/fp16 정밀 정렬을 달성했습니다. , 단순 표적 탐지에 비해 지연 증가가 매우 작았습니다. 여기에 간단한 요약을 하여 모두에게 참고가 되고자 합니다. (네, 그동안 리뷰를 쓰다 드디어 실습에 대해 글을 쓰게 되었습니다!)
우선 MUTR3D의 데이터 형식이 꽤 특별합니다. , 그리고 모든 예제가 사용됩니다. 이는 각 쿼리가 더 많은 정보에 바인딩되고 더 쉬운 일대일 액세스를 위해 인스턴스로 패키징되기 때문입니다. 그러나 배포의 경우 입력과 출력은 텐서만 가능하므로 인스턴스 데이터는 반드시 필요합니다. 먼저 디스어셈블하면 여러 개의 텐서 변수가 되며, 현재 프레임의 쿼리 및 기타 변수가 모델에 생성되므로 이전 프레임에 보관된 쿼리 및 기타 변수만 입력하고 이 둘을 모델에 연결하면 됩니다. .
입력 선주문 프레임 쿼리 및 기타 변수의 경우 중요한 문제는 형태가 불확실하다는 것입니다. 이는 MUTR3D가 이전 프레임에서 대상을 감지한 쿼리만 유지하기 때문입니다. 이 문제는 상대적으로 해결하기 쉽습니다. 가장 간단한 방법은 패딩, 즉 고정된 크기로 패딩하는 것입니다. 쿼리의 경우 패딩에 모두 0을 사용할 수 있습니다. 적절한 숫자는 자체 데이터를 기반으로 한 실험을 통해 결정될 수 있습니다. 너무 적으면 목표를 쉽게 놓치고, 너무 많으면 공간을 낭비하게 됩니다. onnx의 Dynamic_axes 파라미터는 동적 입력을 구현할 수 있지만 후속 변환기에서 계산한 크기를 포함하므로 문제가 있을 수 있습니다. 아직 시도해보지 않았지만 독자들은 시도해 볼 수 있습니다
특수 연산자를 사용하지 않으면 패딩 후 ONNX 및 TensorRT로 성공적으로 변환할 수 있습니다. 실제로 이런 상황이 발생해야 하지만 이는 이 기사의 범위를 벗어납니다. 예를 들어, MUTR3D에서는 프레임 간 참조점을 이동할 때 torch.linalg.inv 연산자를 사용하여 의사 역행렬을 찾는 기능이 지원되지 않습니다. 지원되지 않는 연산자를 발견하면 교체를 시도해야 합니다. 작동하지 않으면 모델 외부에서만 사용할 수 있습니다. 숙련된 사람도 자신의 연산자를 작성할 수 있습니다. 하지만 이 단계는 모델의 전처리 및 후처리에 배치될 수 있기 때문에 모델 외부로 이동하기로 결정했습니다. 자체 연산자를 작성하는 것이 더 어려울 것입니다
성공적인 변환이 모든 것이 잘 된다는 것을 의미하지는 않습니다. 대답은 종종 '아니요'입니다. 정확도 차이가 매우 크다는 것을 알 수 있습니다. 이는 모델에 모듈이 많기 때문입니다. 먼저 첫 번째 이유에 대해 이야기해 보겠습니다. Transformer의 self-attention 단계에서는 여러 쿼리 간의 정보 상호작용이 발생합니다. 그러나 원본 모델은 대상이 한 번 감지된 쿼리(모델에서는 활성 쿼리라고 함)만 유지하며 이러한 쿼리만 현재 프레임의 쿼리와 상호 작용해야 합니다. 그리고 이제는 유효하지 않은 쿼리가 많이 채워지기 때문에 모든 쿼리가 함께 상호 작용하면 필연적으로 결과에 영향을 미치게 됩니다
이 문제에 대한 해결책은 DN-DETR[1]에서 영감을 얻었는데, 이는 nn.MultiheadAttention의 'attn_mask' 매개변수에 해당하는 attention_mask를 사용하는 것입니다. 그 기능은 처음에는 정보 상호 작용이 필요하지 않은 쿼리를 차단하는 것입니다. 이는 NLP에서 모든 문장이 현재 요구 사항을 정확히 충족하는 일관되지 않은 길이로 설정되어 있기 때문입니다. True는 차단해야 하는 쿼리를 나타내고 False는 유효한 쿼리를 나타냅니다.
attention 마스크 다이어그램 attention_mask를 계산하는 로직이 조금 복잡하기 때문에 TensorRT를 연산하고 변환할 때 새로운 문제가 발생할 수 있으므로 모델 외부에서 계산하여 모델에 입력 변수로 입력한 후 전달해야 합니다. 다음은 샘플 코드입니다.
data['attn_masks'] = attn_masks_init.clone().to(device)data['attn_masks'][active_prev_num:max_num, :] = Truedata['attn_masks'][:, active_prev_num:max_num] = True[1]DN-DETR: Accelerate DETR Training by Introducing Query DeNoising4. 패딩이 QIM에 미치는 영향QIM은 MUTR3D에 있습니다. 변환기에 의한 쿼리 출력에 대한 후처리 모듈은 주로 세 단계로 나뉩니다. 즉, obj_idxs >= 0인지 여부(훈련 단계에서 무작위 삭제도 포함)를 기준으로 현재 프레임에서 대상의 쿼리를 감지하고 fp 쿼리를 무작위로 추가하는 것입니다(포함되지 않음). 추론 단계) 두 번째 단계는 쿼리 업데이트, 즉 쿼리 출력 값의 self-attention, ffn 및 쿼리 입력 값을 포함하여 첫 번째 단계에서 필터링된 쿼리에 대한 업데이트가 이루어지며, 세 번째 단계는 업데이트된 쿼리를 재생성된 초기 쿼리와 다음 프레임의 입력으로 연결하는 것입니다. 포인트 3에서 언급한 문제가 두 번째 단계에서도 여전히 존재한다는 것을 알 수 있습니다. 쿼리 간에는 모든 상호 작용을 수행하지만 활성 쿼리 간에는 정보 상호 작용만 수행합니다. 따라서 여기에서는 주의 마스크가 다시 사용됩니다. QIM 모듈은 선택 사항이지만 실험에 따르면 모델의 정확도를 높이는 데 도움이 됩니다. QIM을 사용하려면 현재 프레임의 감지 결과를 모델 외부에서 알 수 없기 때문에 이 주의 마스크를 모델에서 계산해야 합니다. 텐서RT의 구문 제한으로 인해 많은 작업이 변환에 실패하거나 무엇을 얻지 못할 것입니다. 결과적으로, 많은 실험 끝에 결론은 인덱스 슬라이스의 직접 할당(포인트 3의 예제 코드와 유사)이 일반적으로 지원되지 않는다는 것입니다. , 어텐션 마스크의 bool 유형을 float 유형으로 변환해야 하며, 마지막으로 어텐션 마스크를 사용하기 전에 다시 bool 유형으로 변환해야 합니다. 다음은 예제 코드입니다.
obj_mask = (obj_idxs >= 0).float()attn_mask = torch.matmul(obj_mask.unsqueeze(-1), obj_mask.unsqueeze(0)).bool()attn_mask = ~attn_mask
mask = (~attention_mask[-1]).float()track_scores = track_scores * mask
需要重新写的内容是:赋值的值必须是一个,不能是多个。例如,当我更新新出现的目标时,我不会统一赋值为某个ID,而是需要为每个目标赋予连续递增的ID。我想到的解决办法是先统一赋值为一个比较大且不可能出现的数字,比如1000,以避免与之前的ID重复,然后在后续处理中将1000替换为唯一且连续递增的数字。(我真是个天才)
如果要进行递增操作(+=1),只能使用简单的掩码,即不能涉及复杂的逻辑计算。例如,对disappear_time的更新,本来需要同时判断obj_idx >= 0且track_scores = 0这个条件。虽然看似不合理,但经过分析发现,即使将obj_idx=-1的非目标的disappear_time递增,因为后续这些目标并不会被选入,所以对整体逻辑影响不大
综上,最后的动态更新track_id示例代码如下,在后处理环节要记得替换obj_idx为1000的数值.:
def update_trackid(self, track_scores, disappear_time, obj_idxs):disappear_time[track_scores >= 0.4] = 0obj_idxs[(obj_idxs == -1) & (track_scores >= 0.4)] = 1000disappear_time[track_scores 5] = -1
至此模型部分的处理就全部结束了,是不是比较崩溃,但是没办法,部署端到端模型肯定比一般模型要复杂很多.模型最后会输出固定shape的结果,还需要在后处理阶段根据obj_idx是否>0判断需要保留到下一帧的query,再根据track_scores是否>filter score thresh判断当前最终的输出结果.总体来看,需要在模型外进行的操作只有三步:帧间移动reference_points,对输入query进行padding,对输出结果进行过滤和转换格式,基本上实现了端到端的目标检测+目标跟踪.
需要重新写的内容是:以上六点的操作顺序需要说明一下。我在这里按照问题分类来写,实际上可能的顺序是1->2->3->5->6->4,因为第五点和第六点是使用QIM的前提,它们之间也存在依赖关系。另外一个问题是我没有使用memory bank,即时序融合的模块,因为经过实验发现这个模块的提升效果并不明显,而且对于端到端跟踪机制来说,已经天然地使用了时序融合(因为直接将前序帧的查询信息带到下一帧),所以时序融合并不是非常必要
好了,现在我们可以对比TensorRT的推理结果和PyTorch的推理结果,会发现在FP32精度下可以实现精度对齐,非常棒!但是,如果需要转换为FP16(可以大幅降低部署时延),第一次推理会发现结果完全变成None(再次崩溃)。导致FP16结果为None一般都是因为出现数据溢出,即数值大小超限(FP16最大支持范围是-65504~+65504)。如果你的代码使用了一些特殊的操作,或者你的数据天然数值较大,例如内外参、姿态等数据很可能超限,一般可以通过缩放等方式解决。这里再说一下和我以上6点相关的一个原因:
7.使用attention_mask导致的fp16结果为none的问题
这个问题非常隐蔽,因为问题隐藏在torch.nn.MultiheadAttention源码中,具体在torch.nn.functional.py文件中,有以下几句:
if attn_mask is not None and attn_mask.dtype == torch.bool:new_attn_mask = torch.zeros_like(attn_mask, dtype=q.dtype)new_attn_mask.masked_fill_(attn_mask, float("-inf"))attn_mask = new_attn_mask
可以看到,这一步操作是对attn_mask中值为True的元素用float("-inf")填充,这也是attention mask的原理所在,也就是值为1的位置会被替换成负无穷,这样在后续的softmax操作中,这个位置的输入会被加上负无穷,输出的结果就可以忽略不记,不会对其他位置的输出产生影响.大家也能看出来了,这个float("-inf")是fp32精度,肯定超过fp16支持的范围了,所以导致结果为none.我在这里把它替换为fp16支持的下限,即-65504,转fp16就正常了,虽然说一般不要修改源码,但这个确实没办法.不要问我怎么知道这么隐蔽的问题的,因为不是我一个人想到的.但如果使用attention_mask之前仔细研究了原理,想到也不难.
好的,以下是我在端到端模型部署方面的全部经验分享,我保证这不是标题党。由于我对tensorRT的接触时间不长,所以可能有些描述不准确的地方
需要进行改写的内容是:原文链接:https://mp.weixin.qq.com/s/EcmNH2to2vXBsdnNvpo0xw
위 내용은 실제 배포: 엔드투엔드 탐지 및 추적을 위한 동적 순차 네트워크의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!