鍵點(diǎn)數(shù)據(jù)集深度解析:從數(shù)據(jù)探查到模型訓(xùn)練全流程)
簡(jiǎn)介人體姿態(tài)估計(jì)是計(jì)算機(jī)視覺(jué)領(lǐng)域的核心任務(wù)其目標(biāo)是從圖像或視頻中精準(zhǔn)定位人體關(guān)節(jié)位置。其技術(shù)原理通常基于深度學(xué)習(xí)模型通過(guò)回歸關(guān)鍵點(diǎn)坐標(biāo)或預(yù)測(cè)熱力圖來(lái)實(shí)現(xiàn)。這項(xiàng)技術(shù)的核心價(jià)值在于將抽象的坐標(biāo)點(diǎn)轉(zhuǎn)化為對(duì)人體姿態(tài)、行為乃至意圖的數(shù)字化理解是構(gòu)建智能感知系統(tǒng)的重要基石。在工程實(shí)踐中一個(gè)高質(zhì)量、標(biāo)注規(guī)范的骨骼關(guān)鍵點(diǎn)數(shù)據(jù)集是算法成功的決定性因素。此類數(shù)據(jù)集廣泛應(yīng)用于健身與運(yùn)動(dòng)分析、人機(jī)交互與虛擬現(xiàn)實(shí)、安防監(jiān)控以及動(dòng)畫制作等多個(gè)高價(jià)值場(chǎng)景。本文將以一個(gè)典型的人體骨骼關(guān)鍵點(diǎn)檢測(cè)數(shù)據(jù)集為例系統(tǒng)闡述從數(shù)據(jù)解壓、結(jié)構(gòu)解析、質(zhì)量評(píng)估到數(shù)據(jù)預(yù)處理、增強(qiáng)策略定制以及模型訓(xùn)練準(zhǔn)備的完整工程化流程為相關(guān)領(lǐng)域的開發(fā)者和研究者提供一套可復(fù)用的實(shí)戰(zhàn)方法論。1. 項(xiàng)目概述一份骨骼關(guān)鍵點(diǎn)數(shù)據(jù)集的深度解構(gòu)最近在整理硬盤里的陳年資料翻到了一個(gè)名為“人體骨骼關(guān)鍵點(diǎn)檢測(cè)數(shù)據(jù)集_20251123_004453.zip”的文件包。這名字一看就是典型的“項(xiàng)目產(chǎn)物”帶著時(shí)間戳透著一種“做完實(shí)驗(yàn)隨手一存準(zhǔn)備日后復(fù)盤”的意味。對(duì)于從事計(jì)算機(jī)視覺(jué)特別是人體姿態(tài)估計(jì)、動(dòng)作識(shí)別或者人機(jī)交互方向的朋友來(lái)說(shuō)這類數(shù)據(jù)集就是我們的“彈藥庫(kù)”。今天我就以這個(gè)數(shù)據(jù)集為引子和大家深入聊聊當(dāng)我們拿到一個(gè)這樣的“裸數(shù)據(jù)包”時(shí)應(yīng)該如何去理解它、評(píng)估它并最終讓它為我們的模型訓(xùn)練服務(wù)。這不僅僅是解壓文件那么簡(jiǎn)單而是一個(gè)從數(shù)據(jù)認(rèn)知到工程實(shí)踐的全流程。一個(gè)優(yōu)秀的數(shù)據(jù)集是算法成功的基石。但“優(yōu)秀”二字往往隱藏在文件的命名規(guī)則、標(biāo)注格式、數(shù)據(jù)分布這些細(xì)節(jié)里。這個(gè)數(shù)據(jù)集標(biāo)題已經(jīng)透露了幾個(gè)關(guān)鍵信息核心任務(wù)是“人體骨骼關(guān)鍵點(diǎn)檢測(cè)”文件格式是“.zip”壓縮包并且有一個(gè)精確到秒的生成時(shí)間“20251123_004453”。我們將圍繞這些線索一步步拆解看看如何像偵探一樣從零散的圖片和標(biāo)注文件中還原出數(shù)據(jù)集的完整面貌和應(yīng)用潛力。2. 數(shù)據(jù)集核心價(jià)值與典型應(yīng)用場(chǎng)景解析2.1 骨骼關(guān)鍵點(diǎn)檢測(cè)的任務(wù)本質(zhì)人體骨骼關(guān)鍵點(diǎn)檢測(cè)通俗講就是從一張圖片或一段視頻中精準(zhǔn)地定位出人體關(guān)節(jié)的位置比如頭頂、鼻子、左右肩、左右肘、左右腕、左右髖、左右膝、左右踝等。這些點(diǎn)連起來(lái)就構(gòu)成了人體的骨骼框架。這項(xiàng)技術(shù)是許多高級(jí)應(yīng)用的“前哨站”。它的價(jià)值不在于畫出幾個(gè)點(diǎn)而在于將這些抽象的坐標(biāo)點(diǎn)轉(zhuǎn)化為對(duì)人體姿態(tài)、行為乃至意圖的理解。2.2 核心應(yīng)用場(chǎng)景深度剖析基于骨骼關(guān)鍵點(diǎn)數(shù)據(jù)我們可以解鎖非常豐富的應(yīng)用場(chǎng)景這也是此類數(shù)據(jù)集備受追捧的原因。2.2.1 健身與運(yùn)動(dòng)分析這是目前非常火熱的落地方向。通過(guò)攝像頭捕捉用戶的運(yùn)動(dòng)姿態(tài)實(shí)時(shí)計(jì)算出關(guān)節(jié)角度、動(dòng)作幅度和運(yùn)動(dòng)軌跡。例如在智能健身鏡或健身APP中系統(tǒng)可以判斷深蹲時(shí)膝蓋是否超過(guò)腳尖、瑜伽動(dòng)作是否標(biāo)準(zhǔn)、高爾夫揮桿的姿勢(shì)是否合理。數(shù)據(jù)集的質(zhì)量直接決定了分析的準(zhǔn)確性。一個(gè)包含各種體型、穿著和光照條件下健身動(dòng)作的數(shù)據(jù)集其價(jià)值遠(yuǎn)超一個(gè)只在實(shí)驗(yàn)室白背景下采集的簡(jiǎn)單數(shù)據(jù)集。2.2.2 人機(jī)交互與虛擬現(xiàn)實(shí)讓機(jī)器理解人的動(dòng)作是實(shí)現(xiàn)自然交互的關(guān)鍵。比如通過(guò)手勢(shì)控制智能電視、隔空操作PPT或者在VR游戲中玩家的每一個(gè)彎腰、跳躍都能被精準(zhǔn)映射到虛擬角色上。這類應(yīng)用對(duì)檢測(cè)的實(shí)時(shí)性和魯棒性要求極高數(shù)據(jù)集需要包含大量快速運(yùn)動(dòng)、肢體遮擋如手在身體前方以及復(fù)雜背景下的樣本。2.2.3 安防與異常行為識(shí)別在公共場(chǎng)所通過(guò)分析行人的行走姿態(tài)、奔跑、摔倒、打架等骨骼關(guān)鍵點(diǎn)序列可以及時(shí)發(fā)現(xiàn)異常情況并預(yù)警。例如識(shí)別老人摔倒的“突然倒地”姿態(tài)模式。這要求數(shù)據(jù)集不僅有關(guān)鍵點(diǎn)還要有連續(xù)的時(shí)間序列視頻幀并且標(biāo)注了各類異常行為標(biāo)簽。2.2.4 動(dòng)畫與游戲制作傳統(tǒng)動(dòng)畫制作中動(dòng)作捕捉需要演員穿著專業(yè)設(shè)備在特定場(chǎng)地完成。而基于視覺(jué)的動(dòng)捕技術(shù)僅用普通攝像頭就能驅(qū)動(dòng)數(shù)字角色大大降低了成本。相關(guān)數(shù)據(jù)集需要非常高精度的關(guān)鍵點(diǎn)標(biāo)注通常包含更多細(xì)節(jié)關(guān)節(jié)點(diǎn)如手指關(guān)節(jié)并且動(dòng)作范圍要覆蓋常見(jiàn)的行走、奔跑、跳躍、舞蹈等。拿到“人體骨骼關(guān)鍵點(diǎn)檢測(cè)數(shù)據(jù)集_20251123_004453.zip”時(shí)我們首先要思考它可能服務(wù)于以上哪個(gè)或哪些場(chǎng)景這決定了我們后續(xù)評(píng)估數(shù)據(jù)集的側(cè)重點(diǎn)。3. 數(shù)據(jù)集的初步探查與結(jié)構(gòu)解析3.1 文件解壓與目錄結(jié)構(gòu)觀察第一步永遠(yuǎn)是解壓。解壓后一個(gè)清晰、規(guī)范的目錄結(jié)構(gòu)是好數(shù)據(jù)集的第一個(gè)標(biāo)志。通常我們會(huì)看到類似以下的布局HumanSkeletonDataset_20251123/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ ├── 000002.jpg │ │ └── ... │ └── val/ │ ├── 000501.jpg │ └── ... ├── annotations/ │ ├── train.json │ ├── val.json │ └── keypoint_definitions.txt └── README.mdimages/: 存放所有圖像文件通常按訓(xùn)練集train、驗(yàn)證集val甚至測(cè)試集test分開放置。這體現(xiàn)了數(shù)據(jù)劃分的規(guī)范性。annotations/: 存放標(biāo)注文件。這是核心中的核心。標(biāo)注可能以JSON、XML如PASCAL VOC格式或TXT格式存在。README.md: 一個(gè)優(yōu)秀的數(shù)據(jù)集必備的“說(shuō)明書”。它會(huì)說(shuō)明數(shù)據(jù)來(lái)源、標(biāo)注規(guī)范、關(guān)鍵點(diǎn)定義、許可證等信息。如果這個(gè)文件缺失或過(guò)于簡(jiǎn)陋我們就要花更多功夫去“猜”。實(shí)操心得解壓后第一件事不是急著寫代碼而是用眼睛看。數(shù)一圖片大概有多少?gòu)埧纯磮D片的尺寸是否統(tǒng)一打開幾張圖片看看內(nèi)容人物場(chǎng)景、清晰度、光照。然后立刻尋找README文件。如果找不到就去annotations文件夾里用文本編輯器打開一個(gè)標(biāo)注文件嘗試?yán)斫馄浣Y(jié)構(gòu)。3.2 標(biāo)注格式深度解讀骨骼關(guān)鍵點(diǎn)的標(biāo)注格式有多種主流標(biāo)準(zhǔn)識(shí)別格式是正確使用數(shù)據(jù)的前提。3.2.1 COCO Keypoints 格式這是目前最流行的格式之一源自MS COCO數(shù)據(jù)集。其標(biāo)注JSON文件結(jié)構(gòu)復(fù)雜但信息完整。{ info: {...}, licenses: [...], images: [ {id: 1, file_name: 000001.jpg, height: 480, width: 640, ...}, ... ], annotations: [ { id: 1, image_id: 1, category_id: 1, keypoints: [x1, y1, v1, x2, y2, v2, ...], num_keypoints: 17, area: 3672.56, bbox: [x, y, width, height], iscrowd: 0 }, ... ], categories: [ { id: 1, name: person, supercategory: person, keypoints: [nose, left_eye, ..., right_ankle], skeleton: [[16, 14], [14, 12], ...] // 關(guān)節(jié)點(diǎn)連接關(guān)系 } ] }keypoints列表按順序存儲(chǔ)每個(gè)關(guān)鍵點(diǎn)的[x坐標(biāo), y坐標(biāo), 可見(jiàn)性v]。可見(jiàn)性v通常為2已標(biāo)注且可見(jiàn)1已標(biāo)注但被遮擋0未標(biāo)注。bbox人物的檢測(cè)框?qū)τ趦呻A段姿態(tài)估計(jì)模型非常重要。num_keypoints該人物實(shí)例中已標(biāo)注的關(guān)鍵點(diǎn)數(shù)量。skeleton定義了哪些關(guān)鍵點(diǎn)之間可以連線用于可視化。3.2.2 MPII Human Pose 格式另一個(gè)經(jīng)典數(shù)據(jù)集MPII的格式常見(jiàn)于學(xué)術(shù)研究。它通常為每個(gè)圖像提供一個(gè)獨(dú)立的MAT文件或整合在一個(gè)MAT文件中包含豐富的元信息如活動(dòng)標(biāo)簽、軀干尺寸、縮放因子等更適合進(jìn)行2.5D或3D姿態(tài)分析的研究。3.2.3 自定義簡(jiǎn)單格式有些項(xiàng)目自用的數(shù)據(jù)集可能采用更簡(jiǎn)單的格式比如每張圖片對(duì)應(yīng)一個(gè)同名的TXT文件里面每一行記錄一個(gè)關(guān)鍵點(diǎn)的(x, y)坐標(biāo)和類別ID。排查技巧實(shí)錄如果標(biāo)注文件是JSON先用json.load()讀入Python打印它的頂層鍵keys()。如果是COCO格式你一定會(huì)看到images,annotations,categories這幾個(gè)鍵。然后打印第一個(gè)annotation條目查看keypoints數(shù)組的長(zhǎng)度。如果是17*351那很可能就是標(biāo)準(zhǔn)的COCO 17關(guān)鍵點(diǎn)格式。這一步的快速判斷能節(jié)省大量時(shí)間。4. 數(shù)據(jù)集質(zhì)量評(píng)估與清洗實(shí)戰(zhàn)4.1 關(guān)鍵質(zhì)量維度分析確定了格式接下來(lái)就要評(píng)估數(shù)據(jù)集的“成色”。主要從以下幾個(gè)維度入手?jǐn)?shù)據(jù)量級(jí)與劃分訓(xùn)練集、驗(yàn)證集分別有多少?gòu)垐D片、多少個(gè)標(biāo)注的人體實(shí)例通常一個(gè)能訓(xùn)練穩(wěn)健模型的數(shù)據(jù)集訓(xùn)練實(shí)例數(shù)應(yīng)在萬(wàn)級(jí)以上。劃分比例是否合理常見(jiàn)如8:2或9:1標(biāo)注完整性是否存在大量v0未標(biāo)注的關(guān)鍵點(diǎn)計(jì)算所有實(shí)例的平均num_keypoints。如果這個(gè)數(shù)字遠(yuǎn)小于總關(guān)鍵點(diǎn)數(shù)如17說(shuō)明標(biāo)注缺失嚴(yán)重可能需要清洗或采用能處理部分標(biāo)注的損失函數(shù)。標(biāo)注準(zhǔn)確性需要人工抽樣檢查。隨機(jī)選取幾十張圖片將標(biāo)注的關(guān)鍵點(diǎn)可視化在原圖上觀察點(diǎn)是否準(zhǔn)確落在關(guān)節(jié)處。常見(jiàn)的標(biāo)注錯(cuò)誤包括點(diǎn)標(biāo)偏、左右混淆左肩標(biāo)成右肩、嚴(yán)重遮擋時(shí)胡亂猜測(cè)。數(shù)據(jù)多樣性場(chǎng)景多樣性室內(nèi)、室外、街道、健身房、辦公室等。人物多樣性不同年齡、體型、身高、穿著緊身衣、寬松衣、裙子。姿態(tài)多樣性常見(jiàn)站、坐、走、跑以及各種運(yùn)動(dòng)、舞蹈等復(fù)雜姿態(tài)。挑戰(zhàn)性因素遮擋人物被物體或其他人物遮擋、光照變化逆光、暗光、運(yùn)動(dòng)模糊、多人密集場(chǎng)景。定義一致性關(guān)鍵點(diǎn)的解剖學(xué)定義是否清晰且一致例如“左髖”是指大腿骨與骨盆連接處的中心點(diǎn)這個(gè)定義在所有標(biāo)注員中是否統(tǒng)一keypoint_definitions.txt文件或categories中的keypoints列表就是標(biāo)準(zhǔn)。4.2 自動(dòng)化評(píng)估腳本編寫我們可以編寫Python腳本進(jìn)行快速量化評(píng)估。以下是一個(gè)基于COCO格式的評(píng)估示例import json from collections import Counter import matplotlib.pyplot as plt # 加載標(biāo)注文件 with open(‘a(chǎn)nnotations/train.json‘, ‘r‘) as f: coco_data json.load(f) # 1. 統(tǒng)計(jì)基礎(chǔ)信息 num_images len(coco_data[‘images‘]) num_annotations len(coco_data[‘a(chǎn)nnotations‘]) print(f“圖像數(shù)量 {num_images}“) print(f“人體實(shí)例數(shù)量 {num_annotations}“) # 2. 分析關(guān)鍵點(diǎn)可見(jiàn)性 all_keypoints [] missing_keypoints_per_person [] for ann in coco_data[‘a(chǎn)nnotations‘]: kps ann[‘keypoints‘] # kps是[x1,y1,v1, x2,y2,v2, ...]的扁平列表 visibility [kps[i2] for i in range(0, len(kps), 3)] # 取出所有v值 all_keypoints.extend(visibility) missing_count visibility.count(0) # 統(tǒng)計(jì)未標(biāo)注點(diǎn) missing_keypoints_per_person.append(missing_count) # 統(tǒng)計(jì)可見(jiàn)性分布 vis_counter Counter(all_keypoints) print(f“關(guān)鍵點(diǎn)可見(jiàn)性分布 {vis_counter}“) # v2:可見(jiàn) v1:遮擋 v0:缺失 # 3. 統(tǒng)計(jì)每人的標(biāo)注關(guān)鍵點(diǎn)數(shù)量 avg_keypoints sum([ann[‘num_keypoints‘] for ann in coco_data[‘a(chǎn)nnotations‘]]) / num_annotations print(f“平均每人標(biāo)注關(guān)鍵點(diǎn)數(shù) {avg_keypoints:.2f}“) # 4. 可視化缺失情況分布 plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.hist(missing_keypoints_per_person, binsrange(0, 18, 1), edgecolor‘black‘) plt.xlabel(‘每人缺失關(guān)鍵點(diǎn)數(shù)量‘) plt.ylabel(‘頻數(shù)‘) plt.title(‘缺失關(guān)鍵點(diǎn)分布‘) plt.subplot(1, 2, 2) plt.boxplot([ann[‘a(chǎn)rea‘] for ann in coco_data[‘a(chǎn)nnotations‘]]) plt.ylabel(‘人體框面積 (像素)‘) plt.title(‘人體尺寸分布‘) plt.tight_layout() plt.show()這個(gè)腳本能快速給出數(shù)據(jù)集的宏觀健康狀況。注意事項(xiàng)評(píng)估時(shí)一定要區(qū)分“驗(yàn)證集”和“測(cè)試集”。驗(yàn)證集用于訓(xùn)練時(shí)調(diào)參和監(jiān)控過(guò)擬合我們可以隨意查看和分析。但真正的“測(cè)試集”在學(xué)術(shù)上應(yīng)該只用于最終評(píng)估其標(biāo)注通常是不可見(jiàn)的只有圖片或者即使有標(biāo)注在模型開發(fā)過(guò)程中也應(yīng)“盲用”以避免無(wú)意中在測(cè)試集上過(guò)擬合。檢查你的數(shù)據(jù)集劃分是否包含了獨(dú)立的測(cè)試集。5. 數(shù)據(jù)預(yù)處理與增強(qiáng)策略定制5.1 數(shù)據(jù)讀取與解析管道搭建在模型訓(xùn)練前需要構(gòu)建一個(gè)高效的數(shù)據(jù)加載管道DataLoader。以PyTorch為例我們需要自定義一個(gè)Dataset類。import torch from torch.utils.data import Dataset, DataLoader from PIL import Image import json import cv2 import numpy as np class CocoKeypointsDataset(Dataset): def __init__(self, annotation_path, img_dir, transformNone): with open(annotation_path, ‘r‘) as f: self.coco json.load(f) self.img_dir img_dir self.transform transform # 創(chuàng)建圖像ID到標(biāo)注列表的映射加速查找 self.img_id_to_anns {} for ann in self.coco[‘a(chǎn)nnotations‘]: img_id ann[‘image_id‘] if img_id not in self.img_id_to_anns: self.img_id_to_anns[img_id] [] self.img_id_to_anns[img_id].append(ann) # 創(chuàng)建圖像ID到圖像信息的映射 self.img_id_to_info {img[‘id‘]: img for img in self.coco[‘images‘]} def __len__(self): return len(self.coco[‘images‘]) def __getitem__(self, idx): img_info self.coco[‘images‘][idx] img_id img_info[‘id‘] img_path os.path.join(self.img_dir, img_info[‘file_name‘]) # 讀取圖像 image Image.open(img_path).convert(‘RGB‘) original_size image.size # (width, height) # 獲取該圖像對(duì)應(yīng)的所有人體標(biāo)注 anns self.img_id_to_anns.get(img_id, []) # 準(zhǔn)備目標(biāo)這里以單個(gè)主要人物為例實(shí)際可能需要處理多人 # 我們?nèi)∶娣e最大的人體實(shí)例假設(shè)每圖一人或關(guān)注主要人物 if anns: main_ann max(anns, keylambda x: x[‘a(chǎn)rea‘]) keypoints np.array(main_ann[‘keypoints‘]).reshape(-1, 3) # (17, 3) bbox main_ann[‘bbox‘] # [x, y, width, height] # 將bbox轉(zhuǎn)換為 [x1, y1, x2, y2] 格式 bbox [bbox[0], bbox[1], bbox[0]bbox[2], bbox[1]bbox[3]] else: # 如果沒(méi)有標(biāo)注可以返回空或進(jìn)行特殊處理 keypoints np.zeros((17, 3)) bbox [0, 0, original_size[0], original_size[1]] sample { ‘image‘: image, ‘keypoints‘: keypoints, # (17, 3) ‘bbox‘: bbox, ‘image_id‘: img_id } if self.transform: sample self.transform(sample) return sample這個(gè)Dataset類完成了最基礎(chǔ)的讀取工作返回圖像、關(guān)鍵點(diǎn)坐標(biāo)和邊界框。5.2 針對(duì)姿態(tài)估計(jì)的數(shù)據(jù)增強(qiáng)策略數(shù)據(jù)增強(qiáng)是提升模型泛化能力的關(guān)鍵但對(duì)于關(guān)鍵點(diǎn)檢測(cè)增強(qiáng)必須考慮空間幾何一致性。隨機(jī)水平翻轉(zhuǎn)這是最常用且有效的增強(qiáng)。翻轉(zhuǎn)圖像時(shí)關(guān)鍵點(diǎn)坐標(biāo)和邊界框也要相應(yīng)翻轉(zhuǎn)并且必須交換左右成對(duì)的關(guān)鍵點(diǎn)索引如左肩和右肩。如果關(guān)鍵點(diǎn)順序是固定的如COCO順序需要在代碼中預(yù)定義一個(gè)左右對(duì)稱映射關(guān)系進(jìn)行交換。隨機(jī)旋轉(zhuǎn)與縮放在合理范圍內(nèi)如旋轉(zhuǎn)±30度縮放0.75~1.25進(jìn)行仿射變換。變換后關(guān)鍵點(diǎn)坐標(biāo)需要通過(guò)相同的變換矩陣進(jìn)行計(jì)算。顏色抖動(dòng)調(diào)整亮度、對(duì)比度、飽和度和色調(diào)這對(duì)關(guān)鍵點(diǎn)位置無(wú)影響可以增強(qiáng)模型對(duì)光照變化的魯棒性。CutOut/RandomErasing隨機(jī)遮擋圖像的一小塊矩形區(qū)域可以模擬部分遮擋迫使模型不過(guò)度依賴局部上下文。MixUp 或 Mosaic更高級(jí)的增強(qiáng)將多張圖像混合能極大地增加數(shù)據(jù)的復(fù)雜性和多樣性但對(duì)數(shù)據(jù)加載管道的要求更高。實(shí)操心得在實(shí)現(xiàn)增強(qiáng)時(shí)我強(qiáng)烈建議使用albumentations庫(kù)。它專門為計(jì)算機(jī)視覺(jué)任務(wù)設(shè)計(jì)對(duì)關(guān)鍵點(diǎn)、邊界框的支持非常友好而且速度快。下面是一個(gè)增強(qiáng)管道的示例import albumentations as A from albumentations.pytorch import ToTensorV2 def get_train_transform(): return A.Compose([ A.HorizontalFlip(p0.5), A.Rotate(limit30, p0.5, border_modecv2.BORDER_CONSTANT, value0), A.RandomScale(scale_limit0.25, p0.5), # 縮放 A.PadIfNeeded(min_height512, min_width512, border_modecv2.BORDER_CONSTANT, value0), A.RandomCrop(height512, width512), A.ColorJitter(brightness0.2, contrast0.2, saturation0.2, hue0.1, p0.5), A.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ToTensorV2(), ], keypoint_paramsA.KeypointParams(format‘xyv‘, remove_invisibleFalse)) # 注意格式匹配 # 在Dataset的__getitem__中應(yīng)用 if self.transform: # 將關(guān)鍵點(diǎn)從 (17, 3) 轉(zhuǎn)換為albumentations需要的列表格式 [(x1,y1,v1), ...] kps_list [tuple(kp) for kp in keypoints] transformed self.transform(imagenp.array(image), keypointskps_list, bboxes[bbox]) image transformed[‘image‘] keypoints np.array(transformed[‘keypoints‘]).reshape(-1, 3) # bbox 可能也需要處理...使用albumentations可以確保圖像和標(biāo)注的變換是同步且正確的避免了自己實(shí)現(xiàn)變換矩陣時(shí)容易出現(xiàn)的錯(cuò)誤。6. 模型訓(xùn)練準(zhǔn)備與標(biāo)簽生成6.1 從坐標(biāo)到熱圖標(biāo)簽的生成邏輯大多數(shù)現(xiàn)代姿態(tài)估計(jì)模型如HRNet HigherHRNet SimpleBaseline并不直接回歸關(guān)鍵點(diǎn)的 (x, y) 坐標(biāo)而是預(yù)測(cè)一個(gè)“熱圖”Heatmap。對(duì)于每個(gè)關(guān)鍵點(diǎn)類型生成一個(gè)和輸入圖像尺寸成比例如下采樣4倍或8倍的二維矩陣。在關(guān)鍵點(diǎn)坐標(biāo)對(duì)應(yīng)的位置放置一個(gè)以該點(diǎn)為中心的高斯核熱圖該處的值最高如1.0并向四周衰減。模型的任務(wù)就是學(xué)習(xí)預(yù)測(cè)出這些高斯熱圖。為什么用熱圖而不是直接回歸坐標(biāo)學(xué)習(xí)更簡(jiǎn)單回歸精確的坐標(biāo)值是一個(gè)困難的回歸問(wèn)題而熱圖將問(wèn)題轉(zhuǎn)化為在特征圖上尋找峰值更符合卷積網(wǎng)絡(luò)提取空間特征的優(yōu)勢(shì)。提供空間不確定性高斯核的方差sigma可以調(diào)節(jié)。對(duì)于難以標(biāo)注的模糊點(diǎn)或遮擋點(diǎn)可以使用更大的sigma讓標(biāo)簽更“軟”傳遞一種不確定性信息。處理多人更自然通過(guò)熱圖不同人的同一類關(guān)鍵點(diǎn)會(huì)在圖上形成多個(gè)峰值便于區(qū)分。生成熱圖標(biāo)簽的代碼示例def generate_heatmap(keypoints, output_size, sigma2): “”“ keypoints: (num_kps, 3) [x, y, visibility] output_size: (H, W) 輸出熱圖尺寸 sigma: 高斯核標(biāo)準(zhǔn)差 ”“” num_kps keypoints.shape[0] heatmaps np.zeros((num_kps, output_size[0], output_size[1]), dtypenp.float32) for i in range(num_kps): x, y, v keypoints[i] if v 2: # 如果關(guān)鍵點(diǎn)不可見(jiàn)或未標(biāo)注熱圖全為0 continue # 將原圖坐標(biāo)映射到輸出特征圖坐標(biāo) x int(x * output_size[1] / original_img_width) y int(y * output_size[0] / original_img_height) # 生成二維高斯分布 # 這里使用一個(gè)更高效的方法先創(chuàng)建坐標(biāo)網(wǎng)格 # 實(shí)際實(shí)現(xiàn)中為了效率常使用更優(yōu)化的方式例如利用廣播機(jī)制 # 以下為示意代碼 xx, yy np.meshgrid(np.arange(output_size[1]), np.arange(output_size[0])) d2 (xx - x)**2 (yy - y)**2 exponent d2 / (2 * sigma * sigma) heatmap np.exp(-exponent) heatmap[heatmap 0.01] 0 # 閾值化減少計(jì)算量 heatmaps[i] heatmap return heatmaps # (17, H, W)在實(shí)際訓(xùn)練中這個(gè)生成過(guò)程會(huì)集成到數(shù)據(jù)加載管道里。sigma是一個(gè)重要超參數(shù)通常設(shè)置為output_stride / 6左右output_stride是網(wǎng)絡(luò)下采樣倍數(shù)需要根據(jù)任務(wù)調(diào)整。6.2 損失函數(shù)的選擇與權(quán)衡對(duì)于熱圖預(yù)測(cè)最常用的損失函數(shù)是均方誤差MSE Loss或帶權(quán)重的MSE。因?yàn)闊釄D上大部分區(qū)域都是0背景只有關(guān)鍵點(diǎn)附近有小區(qū)域是非零值這會(huì)導(dǎo)致正負(fù)樣本極度不平衡。常見(jiàn)的改進(jìn)是使用MSELoss結(jié)合焦點(diǎn)損失Focal Loss的思想或者直接使用自適應(yīng)加權(quán)MSE給正樣本區(qū)域高斯核區(qū)域更高的權(quán)重。以帶權(quán)重的MSE為例import torch.nn as nn import torch.nn.functional as F class KeypointMSELoss(nn.Module): def __init__(self, use_target_weightFalse): super().__init__() self.criterion nn.MSELoss(reduction‘mean‘) self.use_target_weight use_target_weight # 是否對(duì)每個(gè)關(guān)鍵點(diǎn)使用不同的權(quán)重 def forward(self, output, target, target_weightNone): “”“ output: (B, K, H, W) 網(wǎng)絡(luò)預(yù)測(cè)的熱圖 target: (B, K, H, W) 真實(shí)熱圖 target_weight: (B, K, 1) 每個(gè)關(guān)鍵點(diǎn)的權(quán)重根據(jù)可見(jiàn)性等計(jì)算 ”“” batch_size output.shape[0] num_keypoints output.shape[1] # 計(jì)算每個(gè)關(guān)鍵點(diǎn)、每個(gè)樣本的損失 losses [] for i in range(num_keypoints): pred_i output[:, i].reshape(batch_size, -1) # (B, H*W) gt_i target[:, i].reshape(batch_size, -1) if self.use_target_weight and target_weight is not None: # 例如對(duì)v0缺失的關(guān)鍵點(diǎn)權(quán)重設(shè)為0不參與損失計(jì)算 weight target_weight[:, i].unsqueeze(-1) # (B, 1) loss_i self.criterion(pred_i * weight, gt_i * weight) else: loss_i self.criterion(pred_i, gt_i) losses.append(loss_i) # 對(duì)所有關(guān)鍵點(diǎn)的損失取平均 total_loss sum(losses) / num_keypoints return total_loss對(duì)于存在大量遮擋或標(biāo)注不全的數(shù)據(jù)集合理利用target_weight至關(guān)重要。我們可以將可見(jiàn)性v為0的關(guān)鍵點(diǎn)權(quán)重設(shè)為0v為1遮擋的權(quán)重設(shè)為0.5v為2可見(jiàn)的權(quán)重設(shè)為1.0這樣模型就不會(huì)強(qiáng)行去學(xué)習(xí)那些根本沒(méi)有標(biāo)注信息的位置。7. 訓(xùn)練流程中的關(guān)鍵技巧與問(wèn)題排查7.1 學(xué)習(xí)率策略與優(yōu)化器選擇姿態(tài)估計(jì)模型通常較大如HRNet-W48訓(xùn)練需要謹(jǐn)慎。AdamW 優(yōu)化器目前是很多工作的首選它結(jié)合了Adam的自適應(yīng)學(xué)習(xí)率和權(quán)重衰減。初始學(xué)習(xí)率可以設(shè)得小一些例如3e-4或1e-3。學(xué)習(xí)率調(diào)度策略推薦使用余弦退火Cosine Annealing或帶熱重啟的余弦退火Cosine Annealing with Warm Restarts。這能讓學(xué)習(xí)率平滑下降并在后期進(jìn)行小幅“重啟”有助于模型跳出局部最優(yōu)。PyTorch中調(diào)用torch.optim.lr_scheduler.CosineAnnealingLR或CosineAnnealingWarmRestarts非常方便。注意事項(xiàng)在訓(xùn)練初期前幾個(gè)epoch可以使用線性熱身Linear Warmup策略將學(xué)習(xí)率從0逐漸增加到初始值。這能穩(wěn)定訓(xùn)練防止初期梯度爆炸。許多開源代碼庫(kù)如MMPose都內(nèi)置了這個(gè)功能。7.2 多尺度訓(xùn)練與測(cè)試為了提升模型對(duì)不同分辨率人物的檢測(cè)能力多尺度訓(xùn)練是標(biāo)準(zhǔn)操作。在數(shù)據(jù)加載時(shí)隨機(jī)將輸入圖像縮放到一個(gè)尺寸范圍內(nèi)如[256, 288, 320, 352, 384, 416, 448, 480, 512]中的某個(gè)尺寸。同時(shí)保持輸入圖像的長(zhǎng)寬比通過(guò)填充Padding到正方形。在測(cè)試推理時(shí)通常采用多尺度測(cè)試和翻轉(zhuǎn)測(cè)試。即將同一張圖像縮放到多個(gè)尺度如[256, 384, 512]并分別進(jìn)行水平翻轉(zhuǎn)將所有預(yù)測(cè)結(jié)果進(jìn)行平均或取最大值能顯著提升最終精度AP但會(huì)成倍增加計(jì)算時(shí)間。在工程部署時(shí)需要權(quán)衡精度和速度。7.3 常見(jiàn)訓(xùn)練問(wèn)題與排查表在訓(xùn)練你自己的模型時(shí)很可能會(huì)遇到以下問(wèn)題。這里提供一個(gè)快速排查指南問(wèn)題現(xiàn)象可能原因排查與解決思路Loss不下降或震蕩劇烈學(xué)習(xí)率過(guò)高。數(shù)據(jù)標(biāo)注噪聲太大。數(shù)據(jù)增強(qiáng)過(guò)于激進(jìn)導(dǎo)致標(biāo)簽“失真”。1. 大幅降低學(xué)習(xí)率如降到1e-4嘗試。2. 可視化一批訓(xùn)練數(shù)據(jù)檢查增強(qiáng)后的圖像和關(guān)鍵點(diǎn)是否還合理。3. 關(guān)閉所有數(shù)據(jù)增強(qiáng)用原始數(shù)據(jù)訓(xùn)練幾輪看Loss是否正常下降。模型預(yù)測(cè)所有關(guān)鍵點(diǎn)都在圖像中心標(biāo)簽處理錯(cuò)誤導(dǎo)致熱圖全為0或中心有固定模式。損失函數(shù)權(quán)重失衡背景主導(dǎo)。1. 檢查熱圖生成函數(shù)確保高斯核中心坐標(biāo)計(jì)算正確。2. 可視化生成的熱圖標(biāo)簽看高斯斑點(diǎn)是否出現(xiàn)在正確位置。3. 在損失函數(shù)中增加正樣本區(qū)域的權(quán)重。驗(yàn)證集精度遠(yuǎn)低于訓(xùn)練集嚴(yán)重過(guò)擬合。訓(xùn)練集和驗(yàn)證集數(shù)據(jù)分布差異大。1. 增加數(shù)據(jù)增強(qiáng)特別是CutOut, MixUp。2. 使用更強(qiáng)的正則化如Dropout, Weight Decay。3. 檢查驗(yàn)證集標(biāo)注質(zhì)量是否比訓(xùn)練集難很多某些關(guān)鍵點(diǎn)如手腕、腳踝精度始終很低這些關(guān)鍵點(diǎn)在數(shù)據(jù)集中本身被遮擋多、標(biāo)注少或模糊。模型容量不足或感受野不夠大。1. 統(tǒng)計(jì)數(shù)據(jù)集中各關(guān)鍵點(diǎn)的可見(jiàn)性比例對(duì)低可見(jiàn)性關(guān)鍵點(diǎn)使用更高的損失權(quán)重。2. 考慮使用注意力機(jī)制或非局部網(wǎng)絡(luò)模塊增強(qiáng)模型對(duì)長(zhǎng)距離依賴的建模能力。3. 嘗試更大的backbone或更高分辨率的特征圖。訓(xùn)練速度非常慢輸入圖像尺寸過(guò)大。數(shù)據(jù)加載管道是瓶頸未使用多進(jìn)程。模型太大。1. 適當(dāng)減小輸入尺寸如從512x512降到384x384。2. 在DataLoader中設(shè)置num_workers為CPU核心數(shù)如8并啟用pin_memoryTrue。3. 使用混合精度訓(xùn)練AMP可以大幅加速并減少顯存占用。實(shí)操心得訓(xùn)練初期我習(xí)慣先在一個(gè)非常小的子集比如100張圖上過(guò)擬合。如果模型能在這個(gè)小數(shù)據(jù)集上快速達(dá)到接近0的訓(xùn)練損失說(shuō)明整個(gè)數(shù)據(jù)管道、模型前向傳播、損失計(jì)算、反向傳播的流程基本是正確的。然后再放到全量數(shù)據(jù)上訓(xùn)練這樣能盡早排除代碼層面的低級(jí)錯(cuò)誤。8. 模型評(píng)估與指標(biāo)解讀模型訓(xùn)練完成后我們需要用驗(yàn)證集或測(cè)試集進(jìn)行定量評(píng)估。骨骼關(guān)鍵點(diǎn)檢測(cè)最核心的評(píng)估指標(biāo)是OKSObject Keypoint Similarity基礎(chǔ)上的APAverage Precision和ARAverage Recall。8.1 OKS關(guān)鍵點(diǎn)相似度OKS類似于目標(biāo)檢測(cè)中的IoU它衡量預(yù)測(cè)關(guān)鍵點(diǎn)與真實(shí)關(guān)鍵點(diǎn)的相似程度。計(jì)算公式為OKS Σ_i [exp(-d_i^2 / (2 * s^2 * κ_i^2)) * δ(v_i 0)] / Σ_i [δ(v_i 0)]d_i第i個(gè)關(guān)鍵點(diǎn)預(yù)測(cè)坐標(biāo)與真實(shí)坐標(biāo)的歐氏距離。s人物尺度的平方根sqrt(area)面積越大允許的誤差范圍也越大。κ_i第i個(gè)關(guān)鍵點(diǎn)的歸一化常數(shù)反映該關(guān)鍵點(diǎn)標(biāo)注的難易程度如眼睛比髖部更容易標(biāo)。這個(gè)值通常由數(shù)據(jù)集提供方根據(jù)標(biāo)注者的一致性計(jì)算得出。δ(v_i 0)指示函數(shù)當(dāng)真實(shí)關(guān)鍵點(diǎn)可見(jiàn)v0時(shí)為1否則為0。OKS值在0到1之間越接近1表示預(yù)測(cè)越準(zhǔn)確。8.2 AP與AR基于OKS我們可以設(shè)定一個(gè)閾值如0.5, 0.75。對(duì)于一個(gè)預(yù)測(cè)的人體實(shí)例如果其與某個(gè)真實(shí)實(shí)例的OKS大于閾值則認(rèn)為該預(yù)測(cè)是正確匹配True Positive。然后像目標(biāo)檢測(cè)一樣計(jì)算不同置信度下的 Precision-Recall 曲線。AP (Average Precision)通常指OKS閾值設(shè)為0.5時(shí)的平均精度AP0.5或者更常用的在多個(gè)OKS閾值如0.5, 0.55, 0.6, ..., 0.9, 0.95上取平均記為AP有時(shí)叫AP^0.5:0.95 COCO的主要指標(biāo)。AR (Average Recall)在每張圖片中限定最多檢測(cè)K個(gè)人如K20的情況下計(jì)算的平均召回率。在COCO數(shù)據(jù)集的評(píng)估中你會(huì)看到諸如AP,AP0.5,AP0.75,AP (medium),AP (large),AR等指標(biāo)。對(duì)于你自己的數(shù)據(jù)集如果標(biāo)注格式與COCO兼容可以直接使用官方的pycocotools庫(kù)進(jìn)行評(píng)估這是最權(quán)威的方式。排查技巧實(shí)錄如果評(píng)估時(shí)AP異常低比如低于0.1首先不要懷疑模型而是檢查評(píng)估代碼和預(yù)測(cè)結(jié)果的格式。確保你生成的預(yù)測(cè)結(jié)果JSON文件完全符合COCO評(píng)估API要求的格式。一個(gè)常見(jiàn)的錯(cuò)誤是坐標(biāo)未歸一化或歸一化錯(cuò)了尺度應(yīng)該是相對(duì)于原圖而不是輸入網(wǎng)絡(luò)的縮放后圖像。另一個(gè)錯(cuò)誤是關(guān)鍵點(diǎn)順序與數(shù)據(jù)集的定義不匹配。務(wù)必仔細(xì)對(duì)照categories中的keypoints列表順序。本文還有配套的精品資源點(diǎn)擊獲取