估計(jì)文獻(xiàn)閱讀與復(fù)現(xiàn):從PNP到深度回歸的完整避坑指南)
簡介頭部姿態(tài)估計(jì)是計(jì)算機(jī)視覺與人工智能領(lǐng)域的重要研究方向常用于虛擬現(xiàn)實(shí)、自動(dòng)駕駛、智能監(jiān)控等場景。壓縮包收錄5篇2017—2018年的代表性文獻(xiàn)覆蓋深度學(xué)習(xí)、特征提取與3D幾何模型等主流方法適合算法工程師與科研人員了解前沿進(jìn)展。壓縮包內(nèi)共5個(gè)文件以4篇PDF學(xué)術(shù)論文為主另附1個(gè)XML文件整體大小13.11MB。PDF文獻(xiàn)介紹基于卷積神經(jīng)網(wǎng)絡(luò)的自適應(yīng)梯度訓(xùn)練、細(xì)粒度頭部姿態(tài)回歸、四元數(shù)姿態(tài)表示及三維對(duì)齊中的姿態(tài)輔助方法XML對(duì)應(yīng)元數(shù)據(jù)便于檢索。閱讀后能理解關(guān)鍵點(diǎn)檢測、無關(guān)鍵點(diǎn)估計(jì)、旋轉(zhuǎn)矩陣與四元數(shù)建模、評(píng)估標(biāo)準(zhǔn)等內(nèi)容并獲得從數(shù)據(jù)預(yù)處理到模型訓(xùn)練與結(jié)果分析的研究路徑。目前已有351人學(xué)習(xí)下載對(duì)想把握頭部姿態(tài)估計(jì)技術(shù)演進(jìn)或設(shè)計(jì)實(shí)驗(yàn)方案的讀者頗具參考價(jià)值。1. 頭部姿態(tài)估計(jì)文獻(xiàn)該怎么讀先建立坐標(biāo)系再談精度做駕駛員分心檢測時(shí)攝像頭裝在儀表盤上方系統(tǒng)必須在幾十毫秒內(nèi)判斷司機(jī)有沒有看路做線上課堂注意力分析時(shí)要區(qū)分學(xué)生是低頭記筆記還是趴著睡覺。這兩類需求的底層技術(shù)都是頭部姿態(tài)估計(jì)Head Pose Estimation——從一張人臉圖像解算出頭部的三軸旋轉(zhuǎn)角 yaw、pitch、roll。這個(gè)方向的文獻(xiàn)十年里漲了近一個(gè)數(shù)量級(jí)從傳統(tǒng)幾何方法到深度回歸不斷翻新但新手翻開論文大多卡在同一個(gè)地方數(shù)據(jù)集協(xié)議、坐標(biāo)定義、評(píng)估指標(biāo)各寫各的讀十篇也串不起一條完整的技術(shù)線。這篇筆記按我實(shí)際調(diào)研和復(fù)現(xiàn)經(jīng)驗(yàn)把文獻(xiàn)怎么讀、方案怎么選、代碼怎么跑、坑在哪里一次講透。2. 頭部姿態(tài)估計(jì)的技術(shù)演進(jìn)路線從 PNP 幾何求解到深度學(xué)習(xí)回歸2.1 傳統(tǒng)幾何路線人臉關(guān)鍵點(diǎn) PNP 求解早期文獻(xiàn)的主體思路不是“直接回歸角度”而是兩步走先用檢測器定位人臉關(guān)鍵點(diǎn)眼睛、鼻子、嘴角再利用 2D-3D 對(duì)應(yīng)關(guān)系求旋轉(zhuǎn)角。這里有個(gè)繞不開的核心算法叫 PNPPerspective-n-Point解決的問題是已知一組 3D 點(diǎn)的世界坐標(biāo)、對(duì)應(yīng) 2D 點(diǎn)的圖像坐標(biāo)、相機(jī)內(nèi)參求相機(jī)相對(duì)于物體的旋轉(zhuǎn)矩陣和平移向量。放到頭部姿態(tài)場景里3D 點(diǎn)來自一顆通用的人臉模板模型2D 點(diǎn)來自關(guān)鍵點(diǎn)檢測器輸出旋轉(zhuǎn)矩陣就是頭部的朝向。我最早跑通這個(gè)流程時(shí)用的就是 OpenCV 自帶的cv2.solvePnP五個(gè)基準(zhǔn)點(diǎn)就能出角。代碼骨架如下import cv2 import numpy as np # 3D 人臉基準(zhǔn)點(diǎn)左右眼外眼角、鼻尖、左右嘴角單位毫米 model_points np.array([ [-70.0, -70.0, -70.0], # 左眼外眼角 [70.0, -70.0, -70.0], # 右眼外眼角 [0.0, 0.0, -100.0], # 鼻尖 [-50.0, 50.0, -80.0], # 左嘴角 [50.0, 50.0, -80.0] # 右嘴角 ], dtypenp.float64) # 相機(jī)內(nèi)參焦距取圖像最長邊主點(diǎn)取圖像中心經(jīng)驗(yàn)值做法 focal_length max(frame.shape[0], frame.shape[1]) center (frame.shape[1] / 2, frame.shape[0] / 2) camera_matrix np.array([ [focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1] ], dtypenp.float64) dist_coeffs np.zeros((4, 1)) # image_points 是檢測器給出的 5 個(gè)關(guān)鍵點(diǎn)像素坐標(biāo) success, rvec, tvec cv2.solvePnP( model_points, image_points, camera_matrix, dist_coeffs ) rmat, _ cv2.Rodrigues(rvec)這段代碼里最容易被忽略的是dist_coeffs如果是普通網(wǎng)絡(luò)攝像頭畸變系數(shù)通常設(shè)全零也能用但如果你用的是廣角攝像頭或者從視頻里截幀畸變不校正會(huì)讓角度偏 2 到 3 度。另一個(gè)關(guān)鍵是 3D 模板的選擇五個(gè)點(diǎn)是簡配版論文里常見的是用 68 點(diǎn)或 49 點(diǎn)模板做 PNP點(diǎn)數(shù)越多對(duì)關(guān)鍵點(diǎn)定位誤差的魯棒性越好但計(jì)算量也上去了。這條路線到今天仍有一席之地因?yàn)樗懔﹂_銷極低、不需要訓(xùn)練數(shù)據(jù)適合嵌入式設(shè)備做粗估計(jì)。但它的上限也被關(guān)鍵點(diǎn)檢測器卡死頭部大角度轉(zhuǎn)動(dòng)時(shí)側(cè)臉的可見關(guān)鍵點(diǎn)少了一半PNP 會(huì)出現(xiàn)退化輸出角度開始抖動(dòng)。2.2 深度學(xué)習(xí)路線從分類到熱圖回歸的三步走深度學(xué)習(xí)文獻(xiàn)把這個(gè)問題重新定義為輸入一張人臉圖像直接輸出三個(gè)角度值。這個(gè)方向的演進(jìn)可以拆成三步。第一代做法是角度分類把 yaw 按 15 度一個(gè) bin 分成若干類用分類網(wǎng)絡(luò)去預(yù)測區(qū)間好處是訓(xùn)練穩(wěn)定壞處是角度精度被 bin 大小限制邊界樣本還會(huì)一跳一跳的。第二代是做直接回歸用全連接層輸出三個(gè)浮點(diǎn)數(shù)配合 MSE 損失網(wǎng)絡(luò)結(jié)構(gòu)簡單但角度和圖像特征之間是高度非線性的映射純回歸在中大角度上經(jīng)常偏。第三代是熱圖回歸和多任務(wù)學(xué)習(xí)。比如 FSA-Net 的做法是先預(yù)測人臉關(guān)鍵點(diǎn)的熱圖再從熱圖的分布特征里回歸角度本質(zhì)上是把 2D 關(guān)鍵點(diǎn)的結(jié)構(gòu)化信息和角度估計(jì)結(jié)合起來WHENet 則把分類和回歸做了加權(quán)融合專門解決大角度漂移。讀這代文獻(xiàn)時(shí)你會(huì)發(fā)現(xiàn)大家都在強(qiáng)調(diào)“歐拉角不是好的回歸目標(biāo)”——因?yàn)?yaw 接近正負(fù) 90 度時(shí)pitch 和 roll 的耦合會(huì)讓損失函數(shù)不穩(wěn)定。近年來的 6DRepNet 干脆放棄歐拉角改回歸旋轉(zhuǎn)矩陣的 6 維表示就是為了繞開周期性的角度 wrap 問題。2.3 選型判斷什么場景用哪條路線從業(yè)者選路線我之前踩過幾次坑總結(jié)下來就一句話先看部署環(huán)境再看數(shù)據(jù)規(guī)模。如果你只有 CPU 或端側(cè)芯片且角度精度要求不高傳統(tǒng)關(guān)鍵點(diǎn)加 PNP 仍然是性價(jià)比最高的方案一個(gè)輕量關(guān)鍵點(diǎn)檢測模型加幾十行幾何代碼就能交付。如果你有 GPU 且有大量標(biāo)注數(shù)據(jù)或者需要高精度和大角度覆蓋深度學(xué)習(xí)是唯一出路。還有一條中間路線是“深度回歸頭加傳統(tǒng)前置”人臉檢測后直接把關(guān)鍵點(diǎn)檢測和 PNP 串起來用輕量網(wǎng)絡(luò)替代整套回歸目前在車艙監(jiān)控里很常見。文獻(xiàn)閱讀的優(yōu)先級(jí)也和這些場景強(qiáng)相關(guān)不要一上來就啃最新的旋轉(zhuǎn)矩陣論文先把 PNP 基線和輕量回歸模型跑通你才知道后續(xù)論文的提升到底體現(xiàn)在什么工況下。3. 讀文獻(xiàn)前先搞懂的數(shù)據(jù)集與評(píng)估指標(biāo)300W-LP、AFLW2000 與 MAE 的度量3.1 主流數(shù)據(jù)集辨析誰在訓(xùn)練、誰在測試頭部姿態(tài)估計(jì)的數(shù)據(jù)集并不算多但臟水很深。做文獻(xiàn)調(diào)研時(shí)我建議你重點(diǎn)記住下面這張表里的四個(gè)數(shù)據(jù)集它們幾乎覆蓋了所有論文會(huì)用到的訓(xùn)練和測試組合數(shù)據(jù)集圖像來源標(biāo)注方式常見用途300W-LP真實(shí)照片合成擴(kuò)充3DMM擬合的稠密對(duì)齊訓(xùn)練集主力AFLW2000真實(shí)照片3D標(biāo)注測試集BIWIKinect深度圖深度圖配準(zhǔn)深度模態(tài)、跨模態(tài)驗(yàn)證CMU Panoptic多相機(jī)采集多視角重建極端的連續(xù)角度變化300W-LP 幾乎是所有深度學(xué)習(xí)的訓(xùn)練起點(diǎn)它把真實(shí)人臉做隨機(jī)旋轉(zhuǎn)再合成出不同姿態(tài)的樣本所以叫 Large Pose。但它的標(biāo)注不是人工逐張標(biāo)角度而是通過 3DMM 模型的投影參數(shù)反推的這意味著角度質(zhì)量高度依賴擬合時(shí)的臉型模板。AFLW2000 就是從 AFLW 里挑出 2000 張帶全臉關(guān)鍵點(diǎn)的照片角度標(biāo)注質(zhì)量更好所以幾乎每篇論文都會(huì)拿它當(dāng)測試集。BIWI 是深度相機(jī)拍的有準(zhǔn)確的頭部位姿真值但它的圖像是灰度加深度直接拿 RGB 模型去測會(huì)吃虧。CMU Panoptic 樣本量大常用于驗(yàn)證極端姿態(tài)下的魯棒性。閱讀文獻(xiàn)時(shí)如果訓(xùn)練集和測試集都用 300W-LP 自己 split這種數(shù)值要打問號(hào)??蓮?fù)現(xiàn)性較好的論文通常遵守“300W-LP 訓(xùn)練、AFLW2000 測試”的協(xié)議少數(shù)還會(huì)加 BIWI 做跨庫驗(yàn)證。3.2 評(píng)估指標(biāo)的三個(gè)坑同源測試虛高、坐標(biāo)系不一致、分辨率敏感評(píng)估指標(biāo)幾乎清一色用 MAEMean Absolute Error即 yaw/pitch/roll 三個(gè)方向上預(yù)測值與真值差的絕對(duì)值的平均值。但同一個(gè) MAE 數(shù)字背后可能完全不可比。第一個(gè)坑是訓(xùn)練集和測試集是否同源。我在復(fù)現(xiàn)某篇論文時(shí)發(fā)現(xiàn)它報(bào)的 AFLW2000 誤差在 3 度以內(nèi)同協(xié)議復(fù)現(xiàn)出來卻是 5 度多排查到最后發(fā)現(xiàn)它訓(xùn)練時(shí)混入了 AFLW 的原始圖這相當(dāng)于變相把測試分布見過一遍。第二個(gè)坑是坐標(biāo)系定義。有的論文把 yaw 正方向定義為向右轉(zhuǎn)頭有的定義成向左如果你拿它的模型輸出直接對(duì)比另一個(gè)模型的輸出角度正負(fù)號(hào)一顛倒MAE 會(huì)大得離譜。第三個(gè)坑是輸入圖像的分辨率和人臉裁剪 margin。同一個(gè)模型人臉框外擴(kuò) 10% 和緊貼臉部AFLW2000 上的 MAE 能差 1.5 度以上。所以讀文獻(xiàn)時(shí)不要只抄 MAE 表格要把下面幾個(gè)信息一并記下來人臉框用的是檢測器還是數(shù)據(jù)集自帶標(biāo)注、圖像 resize 到多少、裁剪時(shí)有沒有外擴(kuò) margin、角度坐標(biāo)系是圖像坐標(biāo)系還是相機(jī)坐標(biāo)系。這四個(gè)信息缺一個(gè)你對(duì)這個(gè)數(shù)字的信任度就要下調(diào)一檔。3.3 視角與精準(zhǔn)度的衡量難例分析比平均誤差更值得看只盯著平均 MAE 會(huì)錯(cuò)過很多東西。我后來養(yǎng)成了一個(gè)習(xí)慣每次復(fù)現(xiàn)完一個(gè)模型把預(yù)測結(jié)果按真值角度分箱比如 0 到 30 度、30 到 60 度、60 度以上各算一個(gè) MAE。你會(huì)發(fā)現(xiàn)很多論文的平均值好看是因?yàn)?0 到 30 度的樣本占大頭那些樣本本來就好預(yù)測而 60 度以上大姿態(tài)樣本的誤差可能已經(jīng)超過 15 度。真實(shí)車載或課堂上大姿態(tài)恰恰是系統(tǒng)最容易漏報(bào)的場景。凡是論文里給了角度分箱誤差表的可信度比只給總 MAE 的高很多。4. 從論文到代碼復(fù)現(xiàn)一篇頭部姿態(tài)估計(jì)文獻(xiàn)的最小流程4.1 入門復(fù)現(xiàn)選哪篇從開源、依賴、硬件三個(gè)維度篩復(fù)現(xiàn)這個(gè)方向的文獻(xiàn)第一步不是選最新最強(qiáng)的模型而是選最容易跑通的基線。我一般按三個(gè)條件篩一是有官方或可信第三方開源代碼二是依賴庫不超過 PyTorch 加 OpenCV三是能在單張消費(fèi)級(jí) GPU 上完成訓(xùn)練。三個(gè)條件同時(shí)滿足的話FSA-Net 是首選WHENet 次之兩者都有 PyTorch 實(shí)現(xiàn)數(shù)據(jù)預(yù)處理不復(fù)雜訓(xùn)練好的權(quán)重也容易拿到。6DRepNet 精度高但依賴更多建議放在第二輪。選 FSA-Net 還有一個(gè)好處它的網(wǎng)絡(luò)結(jié)構(gòu)是“特征聚合”加“角度回歸”的混合設(shè)計(jì)讀懂它就能順帶理解一大票后續(xù)論文的模塊動(dòng)機(jī)。復(fù)現(xiàn)過程按“推理腳手動(dòng)起來”和“訓(xùn)練流程完整跑通”兩步走第一步目的是驗(yàn)證環(huán)境和預(yù)訓(xùn)練權(quán)重第二步目的是驗(yàn)證數(shù)據(jù)協(xié)議。4.2 跑通最小推理腳本關(guān)鍵點(diǎn)提取與角度計(jì)算我先給一個(gè)不需要訓(xùn)練的最小可跑腳本用 MediaPipe 提取 5 個(gè)關(guān)鍵點(diǎn)再走 PNP 出角度。這段代碼本身就是一條完整的基線能幫你快速理解“關(guān)鍵點(diǎn)加幾何”路線的全流程import cv2 import numpy as np import mediapipe as mp # MediaPipe FaceMesh 關(guān)鍵點(diǎn)索引左右眼外眼角、鼻尖、左右嘴角 IDX {left_eye: 33, right_eye: 263, nose_tip: 1, left_mouth: 61, right_mouth: 291} model_points np.array([ [-70.0, -70.0, -70.0], [70.0, -70.0, -70.0], [0.0, 0.0, -100.0], [-50.0, 50.0, -80.0], [50.0, 50.0, -80.0] ], dtypenp.float64) mp_face_mesh mp.solutions.face_mesh face_mesh mp_face_mesh.FaceMesh(static_image_modeTrue, max_num_faces1) def estimate_head_pose(image): h, w image.shape[:2] rgb cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results face_mesh.process(rgb) if not results.multi_face_landmarks: return None lm results.multi_face_landmarks[0].landmark image_points np.array([ [lm[IDX[left_eye]].x * w, lm[IDX[left_eye]].y * h], [lm[IDX[right_eye]].x * w, lm[IDX[right_eye]].y * h], [lm[IDX[nose_tip]].x * w, lm[IDX[nose_tip]].y * h], [lm[IDX[left_mouth]].x * w, lm[IDX[left_mouth]].y * h], [lm[IDX[right_mouth]].x * w, lm[IDX[right_mouth]].y * h], ], dtypenp.float64) focal_length max(h, w) center (w / 2, h / 2) camera_matrix np.array([ [focal_length, 0, center[0]], [0, focal_length, center[1]], [0, 0, 1] ], dtypenp.float64) success, rvec, tvec cv2.solvePnP( model_points, image_points, camera_matrix, np.zeros((4, 1)) ) rmat, _ cv2.Rodrigues(rvec) # 從旋轉(zhuǎn)矩陣提取歐拉角注意不同文獻(xiàn)的行列順序和正負(fù)方向 yaw np.degrees(np.arctan2(rmat[0, 2], rmat[0, 0])) pitch np.degrees(np.arctan2(-rmat[1, 2], np.sqrt(rmat[1, 0] ** 2 rmat[1, 1] ** 2))) roll np.degrees(np.arctan2(rmat[1, 0], rmat[0, 0])) return yaw, pitch, roll這段代碼里的幾個(gè)參數(shù)要重點(diǎn)說。第一focal_length取圖像最大邊是經(jīng)驗(yàn)做法嚴(yán)格的相機(jī)標(biāo)定需要棋盤格但工程上如果你不追求絕對(duì)角度這個(gè)值夠用它影響的是角度的縮放比例而非相對(duì)趨勢。第二MediaPipe 的坐標(biāo)是歸一化到 [0,1] 的必須乘回圖像寬高。第三歐拉角的提取方式有至少兩種定義我給的公式對(duì)應(yīng)的旋轉(zhuǎn)順序是 R Rz * Ry * Rx如果你換一種順序yaw 和 roll 會(huì)互相串位。碰到別人的代碼先確認(rèn)旋轉(zhuǎn)矩陣到歐拉角的對(duì)應(yīng)關(guān)系再比數(shù)字。4.3 訓(xùn)練一個(gè)輕量回歸模型數(shù)據(jù)加載、損失與評(píng)估跑通推理腳本之后下一步是用深度學(xué)習(xí)復(fù)現(xiàn)論文里的訓(xùn)練流程。下面這段代碼展示的是最小模型加訓(xùn)練循環(huán)的關(guān)鍵骨架對(duì)應(yīng)的是“圖像特征直接回歸三軸角”的路線import torch import torch.nn as nn from torch.utils.data import Dataset # 簡單回歸頭backbone 輸出 512 維特征映射到 3 個(gè)角度 class PoseRegressor(nn.Module): def __init__(self, backbone_out512): super().__init__() self.fc nn.Sequential( nn.Linear(backbone_out, 128), nn.ReLU(), nn.Dropout(0.3), nn.Linear(128, 3) ) def forward(self, x): return self.fc(x) # 角度是連續(xù)量常見損失函數(shù)是 Smooth-L1對(duì)離群點(diǎn)魯棒 criterion nn.SmoothL1Loss() # 訓(xùn)練時(shí)每個(gè) batch 的數(shù)據(jù)增強(qiáng)很關(guān)鍵隨機(jī)左右翻轉(zhuǎn)會(huì)翻轉(zhuǎn) yaw 的符號(hào) # 如果不做符號(hào)處理模型會(huì)學(xué)到錯(cuò)誤的左右對(duì)稱映射這段代碼告訴你兩件事。第一角度的回歸頭通常不復(fù)雜真正的區(qū)別在 backbone 和訓(xùn)練數(shù)據(jù)。第二數(shù)據(jù)增強(qiáng)里的隨機(jī)翻轉(zhuǎn)是一個(gè)隱形坑圖像水平翻轉(zhuǎn)后yaw 的正負(fù)號(hào)要翻轉(zhuǎn)pitch 和 roll 的符號(hào)也要相應(yīng)處理很多復(fù)現(xiàn)者漏了這一步導(dǎo)致模型誤差在左右轉(zhuǎn)頭方向不對(duì)稱。損失函數(shù)層面少數(shù)文獻(xiàn)會(huì)用分類加回歸的多任務(wù)損失比如 yaw 先分箱做分類、再做箱內(nèi)回歸修正復(fù)現(xiàn)時(shí)注意這種混合損失有兩個(gè)分支不能只取其中一個(gè)分支參與反向傳播。評(píng)估時(shí)最標(biāo)準(zhǔn)的做法是這樣def compute_mae(preds, gts): preds np.array(preds) gts np.array(gts) return np.mean(np.abs(preds - gts), axis0) # [yaw_mae, pitch_mae, roll_mae]注意這里返回的是三個(gè)方向各自的 MAE論文里經(jīng)常寫“均值 3.5 度”你復(fù)現(xiàn)時(shí)要確認(rèn)它到底是指三個(gè)方向 MAE 的平均還是 yaw 單項(xiàng)。一字之差對(duì)比時(shí)誤差可能被放大三倍。5. 頭部姿態(tài)估計(jì)文獻(xiàn)復(fù)現(xiàn)避坑指南五個(gè)常見翻車點(diǎn)5.1 換掉人臉檢測器精度憑空跌了三五度現(xiàn)象用同一個(gè)姿態(tài)模型原論文用 MTCNN 做人臉檢測我換成 RetinaFace評(píng)估出來的 MAE 明顯變大。原因姿態(tài)估計(jì)模型看到的輸入圖像是“人臉檢測框裁剪后 resize 的圖像”不同檢測器返回的框位置、大小、是否有 background margin 都不一樣。模型在訓(xùn)練時(shí)見過的是訓(xùn)練集檢測器的裁剪統(tǒng)計(jì)結(jié)果你把裁剪分布換了等價(jià)于輸入分布漂移。解決復(fù)現(xiàn)文獻(xiàn)時(shí)先嚴(yán)格沿用論文里寫的人臉檢測器和裁剪參數(shù)包括 margin 比例。確認(rèn)模型跑通后再單獨(dú)測檢測器替換的影響不要同時(shí)換檢測器和圖像預(yù)處理兩個(gè)變量。5.2 同源測試指標(biāo)虛高跨庫測試立刻現(xiàn)形現(xiàn)象模型在 300W-LP 的隨機(jī)劃分測試集上表現(xiàn)優(yōu)秀但換成 AFLW2000 或?qū)嶋H采集視頻后誤差幾乎翻倍。原因300W-LP 的劃分方式如果隨機(jī)打亂而非按身份切分同一個(gè)人的不同姿態(tài)幀會(huì)同時(shí)出現(xiàn)在訓(xùn)練和測試?yán)锬P陀涀×藗€(gè)人特征這不叫姿態(tài)估計(jì)叫身份記憶。解決優(yōu)先選擇在跨庫協(xié)議下報(bào)數(shù)字的論文或者自己復(fù)現(xiàn)時(shí)額外加一組 AFLW2000 做測試。結(jié)果報(bào)告里同時(shí)給同源和跨庫兩套數(shù)字這對(duì)判斷模型泛化能力最有說服力。5.3 坐標(biāo)系定義不一致正負(fù)號(hào)莫名相反現(xiàn)象兩個(gè)模型單獨(dú)測都正常把它們的結(jié)果放在同一個(gè)坐標(biāo)系里比較時(shí)yaw 的數(shù)值分布整體反號(hào)。原因不同文獻(xiàn)對(duì)旋轉(zhuǎn)正方向的定義不同。有的按右手定則yaw 為正表示順時(shí)針轉(zhuǎn)從上往下看有的則相反。圖像坐標(biāo)系下 x 軸向右相機(jī)坐標(biāo)系下 x 軸向左兩種定義直接導(dǎo)致正負(fù)號(hào)翻轉(zhuǎn)。解決統(tǒng)一用一個(gè)坐標(biāo)系定義我一般把“頭向右偏轉(zhuǎn)”記為 yaw 正方向這樣符合大多數(shù)車載場景的直覺。對(duì)比之前先拿幾個(gè)已知角度的合成圖測試兩個(gè)模型輸出的符號(hào)一致性。5.4 輸入分辨率預(yù)處理不統(tǒng)一結(jié)果忽高忽低現(xiàn)象模型在 224x224 輸入下誤差正常改成 112x112 后誤差明顯變大或者反過來。原因人臉姿態(tài)估計(jì)的特征對(duì)關(guān)鍵點(diǎn)和輪廓細(xì)節(jié)敏感分辨率直接影響可用的結(jié)構(gòu)信息。有些論文的 backbone 是在 ImageNet 上預(yù)訓(xùn)練的其內(nèi)部的 BN 統(tǒng)計(jì)量跟輸入分辨率強(qiáng)相關(guān)一旦換了輸入尺寸BN 層的均值和方差就不匹配了。解決訓(xùn)練和推理保持同一分辨率。如果不得已要降分辨率把 backbone 的 BN 層重算統(tǒng)計(jì)量或者在目標(biāo)分辨率下做短暫微調(diào)。讀論文時(shí)留意它的輸入尺寸2020 年后多數(shù)工作在 224x224但 300W-LP 原始人臉圖小強(qiáng)行放大也會(huì)引入插值噪聲這是一個(gè)雙向坑。5.5 三維標(biāo)注與二維投影之間脫節(jié)現(xiàn)象訓(xùn)練數(shù)據(jù)集的標(biāo)注是三維旋轉(zhuǎn)矩陣但代碼加載后轉(zhuǎn)成歐拉角時(shí)旋轉(zhuǎn)順序?qū)戝e(cuò)導(dǎo)致訓(xùn)練目標(biāo)本身就有問題模型一直不收斂。原因旋轉(zhuǎn)矩陣轉(zhuǎn)歐拉角的順序有無數(shù)種組合例如“先 yaw 再 pitch 再 roll”和“先 roll 再 pitch 再 yaw”得到的結(jié)果完全不同。數(shù)據(jù)集標(biāo)注里給的是矩陣或四元數(shù)復(fù)現(xiàn)者用通用代碼轉(zhuǎn)歐拉角沒有確認(rèn)論文里歐拉角的應(yīng)用順序目標(biāo)變量就錯(cuò)了。解決先用數(shù)據(jù)集的已知樣例比如角度為 0、90 度的合成圖畫出旋轉(zhuǎn)矩陣到歐拉角的換算流程再核對(duì)訓(xùn)練加載器。最穩(wěn)妥的辦法是直接把網(wǎng)絡(luò)輸出改為旋轉(zhuǎn)矩陣表示用矩陣距離作為損失回避歐拉角順序歧義。6. 進(jìn)階驗(yàn)證技巧用跨庫對(duì)比和消融實(shí)驗(yàn)篩文獻(xiàn)6.1 快速跨庫驗(yàn)證讓新舊模型在同一批圖里跑讀完一篇新論文與其逐一復(fù)現(xiàn)訓(xùn)練不如先做一次“模型對(duì)拍”。把現(xiàn)有的兩三個(gè)開源權(quán)重模型和新論文開源權(quán)重放在同一批圖像上跑這批圖要包含三個(gè)來源AFLW2000 的隨機(jī) 500 張、你自己用手機(jī)拍的照片、從網(wǎng)上下載的多人物合照。寫個(gè)小腳本統(tǒng)一輸出它們的 yaw/pitch/roll 分布直方圖你會(huì)發(fā)現(xiàn)分布形態(tài)能直觀暴露問題某個(gè)模型在大角度段出現(xiàn)斷崖另一個(gè)模型的小角度抖動(dòng)明顯。這一步不需要真值標(biāo)注純看分布合理性十分鐘就能篩掉一批明顯有問題的權(quán)重。6.2 讀消融實(shí)驗(yàn)時(shí)只看相對(duì)提升不聽絕對(duì)數(shù)字消融實(shí)驗(yàn)的表格信息量比主實(shí)驗(yàn)大得多但很多人把它當(dāng)裝飾跳過。我現(xiàn)在的習(xí)慣是把消融表里每一行的誤差變化折算成相對(duì)主模型的百分比再看哪些模塊貢獻(xiàn)最大。如果某個(gè)模塊拿掉后誤差只漲 0.1 度但代碼復(fù)雜度增加 30%這個(gè)設(shè)計(jì)在實(shí)際落地時(shí)要慎重考慮。另外如果一篇論文的消融實(shí)驗(yàn)只報(bào)了自己數(shù)據(jù)集的同源測試沒有跨庫數(shù)字它聲稱的“有效”就只在他自己的數(shù)據(jù)分布上有效。這個(gè)習(xí)慣幫我避過很多次“論文很美好、落地很骨感”的坑。最典型的翻車記憶是我接到過一個(gè)需求是把某個(gè)開源項(xiàng)目部署到低算力設(shè)備上初步測試平均誤差比原論文低了我差點(diǎn)就直接交付后來用跨庫分布圖一看原來模型在小角度段已經(jīng)退化得不成樣子平均數(shù)的好看單純是因?yàn)闇y試視頻里人臉角度集中在正前方。從那以后我凡是接觸新的姿態(tài)估計(jì)方案第一件事就是畫誤差分布曲線而不是看一眼 MAE 就收工。希望這篇筆記也能幫你在讀頭部姿態(tài)估計(jì)文獻(xiàn)時(shí)少走幾條彎路。本文還有配套的精品資源點(diǎn)擊獲取