
簡介以FACS理論為基礎(chǔ)AU_Recognition-master 是面向情感計算與計算機視覺研究者的面部表情單元識別工具可對人臉眼部、嘴部等局部區(qū)域的肌肉動作單元進(jìn)行檢測與強度分析彌補傳統(tǒng)表情識別只能區(qū)分喜悅、悲傷等整體情緒的粒度不足。資源共28個文件壓縮包僅1.54MB核心為8個Python腳本涵蓋AlexNet、ResNet、Inception、VGG等模型實現(xiàn)及數(shù)據(jù)加載器另含2個.ddd模型文件9張.bmp與4張.png用于展示網(wǎng)絡(luò)結(jié)構(gòu)、預(yù)處理流程和識別效果1個ipynb提供交互式演示并配套Markdown說明、License等文檔。項目內(nèi)容覆蓋AU識別完整流程從面部圖像預(yù)處理、特征提取到動作單元回歸預(yù)測和結(jié)果可視化代碼結(jié)構(gòu)緊湊且目錄清晰便于快速復(fù)現(xiàn)實驗也適合在此基礎(chǔ)上調(diào)整模型或接入自有數(shù)據(jù)。目前已有540人學(xué)習(xí)可用于人臉表情分析入門基線搭建也可為中等以上開發(fā)者提供FACS相關(guān)模型設(shè)計參考。 我第一次在 GitHub 上刷到AU_Recognition-master這個倉庫時正被一個需求反復(fù)折磨公司要做一套攝像頭分析系統(tǒng)要求不僅能輸出“高興、難過、生氣”這幾種大表情還得把“真笑”和“假笑”區(qū)分開。普通表情識別模型當(dāng)場抓瞎——兩種表情的輸出標(biāo)簽都叫“高興”可判斷依據(jù)藏在眼輪匝肌、嘴角這些極細(xì)小的肌肉動作里。AU_Recognition這類面部表情單元識別工具就是解決這個問題的它不預(yù)測情感標(biāo)簽而是把一張臉拆成一組可量化的動作單元編號告訴你眉毛抬了多少、嘴角動了多少、眼睛周圍有沒有收縮。這篇文章我會從 FACS 基礎(chǔ)講起把 AU 識別的難點、數(shù)據(jù)準(zhǔn)備、模型設(shè)計、完整實操流程和真實部署中容易踩的坑一次說清楚。適合兩類人看一類是想復(fù)現(xiàn)開源項目做畢設(shè)或論文實驗的研究者另一類是正在選型情感計算/疲勞監(jiān)測方案的工程師。1. 為什么AU識別比“猜整臉表情”難一個量級1.1 從FACS說起微笑不是一個標(biāo)簽而是一組編號FACS面部動作編碼系統(tǒng)上世紀(jì)七十年代由 Paul Ekman 和 Wallace Friesen 提出核心思想很簡單不管人類面部表情多復(fù)雜都可以分解成有限個“動作單元”簡稱 AU。每個 AU 對應(yīng)一組特定肌肉的運動比如AU1眉毛內(nèi)側(cè)上抬AU2眉毛外側(cè)上抬AU4眉毛下壓、皺眉AU6臉頰上升/眼輪匝肌收縮也就是眼周出現(xiàn)笑紋AU12嘴角向外上方拉動AU17下巴上抬看出關(guān)鍵點了嗎一個發(fā)自內(nèi)心的“杜興式微笑”其實是 AU6 AU12 同時激發(fā)而禮貌性假笑往往只有 AU12眼周沒有動靜。普通表情分類把這兩者都?xì)w為“happy”但 AU 識別會老老實實地把[AU61, AU121]和[AU60, AU121]區(qū)分開。這種“原子級”的粒度是 AU 識別最大的價值。1.2 多標(biāo)簽、區(qū)域局部、類別不平衡AU任務(wù)的三重考驗我剛上手時犯過一個錯誤把 AU 識別當(dāng)成普通的圖像分類任務(wù)去做結(jié)果訓(xùn)練一堆 epoch 后平均 F1 慘不忍睹。后來才意識到AU 識別和表情分類有三個本質(zhì)差異。第一它是多標(biāo)簽分類不是多分類。一張臉可以同時出現(xiàn)多個 AU輸出是一個[0/1, 0/1, ...]向量而不是一個 softmax 后互斥的類別索引。這意味著分類頭、損失函數(shù)、評估指標(biāo)全都不一樣。第二AU 具有極強的區(qū)域局部性。AU4 的判別信息集中在眉毛和眉心AU12 集中在嘴角周圍AU17 集中在下巴附近。如果網(wǎng)絡(luò)用全局池化把 7×7 的特征圖壓成一維向量很多局部細(xì)節(jié)已經(jīng)丟了。這也是很多論文專門做“區(qū)域注意力”的根本原因。第三類別不平衡非常嚴(yán)重。公開數(shù)據(jù)集中 AU6、AU12 這類動作出現(xiàn)頻率高而 AU15、AU17 這類相對少見。直接拿 BCE 損失訓(xùn)練模型會學(xué)會“大部分預(yù)測為 0”因為這樣已經(jīng)能拿到很高的準(zhǔn)確率。這三重考驗決定了 AU 識別不能直接套用 ImageNet 分類的那套寫法從數(shù)據(jù)到模型都要專門設(shè)計。2. 數(shù)據(jù)準(zhǔn)備AU模型真正“吃”的是什么2.1 常見公開數(shù)據(jù)集差異與選擇AU_Recognition這類開源項目通常會給一個默認(rèn)數(shù)據(jù)集選項但跑之前你得先搞清楚各個數(shù)據(jù)集的脾氣。我列個表格方便對比數(shù)據(jù)集AU數(shù)量是否含強度標(biāo)注可靠性適用場景BP4D12個AU是0-5專家人工標(biāo)注較可靠通用AU識別訓(xùn)練/評估DISFA8個AU是0-5專家人工標(biāo)注較可靠強度估計、AU單元研究EmotioNet數(shù)百個AU部分自動標(biāo)注噪聲偏大大規(guī)模預(yù)訓(xùn)練CK離散表情觸發(fā)AU否專家標(biāo)注表情到AU的對照實驗我第一次做實驗直接用 BP4D因為它的視頻幀數(shù)多、AU 覆蓋較全而且標(biāo)注是幀級別的適合訓(xùn)練圖像模型。DISFA 也常見但它只標(biāo)注 8 個 AU如果想覆蓋 AU2、AU17 這類動作數(shù)據(jù)就不夠用。EmotioNet 數(shù)據(jù)量大但自動標(biāo)注的噪聲會讓模型學(xué)到不少錯誤關(guān)聯(lián)我通常只拿它做預(yù)訓(xùn)練再用 BP4D 精調(diào)。2.2 人臉檢測、對齊與多標(biāo)簽標(biāo)注的組織不管哪個數(shù)據(jù)集訓(xùn)練前必須先把人臉區(qū)域歸一化到統(tǒng)一尺度。標(biāo)準(zhǔn)流程是關(guān)鍵點檢測68 點或 106 點- 根據(jù)眼睛位置對齊到標(biāo)準(zhǔn)模板 - 裁剪出固定大小的人臉 - 歸一化像素。這一步做不好后面全白搭。你想想如果人臉在畫面里的位置、角度、尺度五花八門模型的一部分能力就要浪費在“找臉”上而不是用來學(xué) AU 特征。對齊之后一個訓(xùn)練樣本就變成了“人臉圖像 AU 標(biāo)簽向量”。例如某幀人臉同時有 AU1、AU4、AU12那么標(biāo)簽向量對應(yīng)位置就是 1其余為 0。存成一個 CSV 或 JSON 索引文件每個樣本一行配合圖像路徑訓(xùn)練時按索引讀取即可。兩個數(shù)據(jù)劃分細(xì)節(jié)值得注意。一要按被試者subject劃分訓(xùn)練/驗證/測試集不要讓同一個人的幀同時出現(xiàn)在訓(xùn)練集和測試集里否則模型很容易通過記憶“這個人長什么樣”來作弊驗證指標(biāo)會虛高。二要謹(jǐn)慎對待水平翻轉(zhuǎn)增強。大多數(shù)圖像任務(wù)里隨機翻轉(zhuǎn)是安全且有效的但 AU 是帶有方向語義的部分?jǐn)?shù)據(jù)集中存在左右不對稱的標(biāo)注一旦翻轉(zhuǎn)AU 編號對應(yīng)的左右側(cè)就反了。我的習(xí)慣是先驗證數(shù)據(jù)集的 AU 定義是否雙側(cè)對稱不確定的時候?qū)幙刹蛔龇D(zhuǎn)或者只做小概率翻轉(zhuǎn)。3. 模型結(jié)構(gòu)全局特征之外為什么大家都在做局部注意力3.1 為什么不能把表情識別模型改改就直接用這是新手最容易踩的坑。拿一個訓(xùn)練好的人臉表情分類模型把最后的 softmax 分類頭換成 sigmoid 多標(biāo)簽頭看似能跑實際效果很一般。原因回到第一節(jié)說的區(qū)域局部性。普通表情分類模型經(jīng)過多層卷積和全局平均池化后空間信息已經(jīng)被壓扁了模型只關(guān)心“整體上這張臉像不像高興”。而 AU 識別需要知道“眉間這塊區(qū)域有沒有向下擠壓”“嘴角那一小塊有沒有向外上拉”。全局特征丟失了這些“在哪里”的信息所以直接把分類模型改成多標(biāo)簽輸出通常只能達(dá)到勉強能看的水平F1 距離論文里的 SOTA 差一大截。3.2 主流方案的演變ROI、熱圖注意力、圖關(guān)系建模要解決局部性問題學(xué)術(shù)界和開源項目里大致有三類方案按實現(xiàn)復(fù)雜度遞增第一類是ROI 區(qū)域裁剪。利用人臉關(guān)鍵點把眉毛、眼睛、嘴角、下巴等區(qū)域分別裁剪成 patch每個 patch 單獨過特征提取器再拼接或加權(quán)融合。實現(xiàn)最簡單也最直觀既然 AU4 看眉毛那就把眉毛區(qū)域給它。第二類是關(guān)鍵點熱圖注意力。典型代表是 JAA-Net它在網(wǎng)絡(luò)里同時預(yù)測人臉關(guān)鍵點熱圖并用熱圖作為注意力權(quán)重去調(diào)制 AU 特征。相當(dāng)于讓網(wǎng)絡(luò)自己學(xué)會“該往哪里看”比硬裁剪更靈活。AU_Recognition這類項目里如果用了類似結(jié)構(gòu)訓(xùn)練時一般會要求同時提供關(guān)鍵點標(biāo)注或者預(yù)訓(xùn)練的 landmark 檢測結(jié)果。第三類是圖神經(jīng)網(wǎng)絡(luò)/Transformer 關(guān)系建模。AU 之間不是相互獨立的AU6 和 AU12 經(jīng)常共同出現(xiàn)AU1 和 AU4 同時出現(xiàn)的概率很低。EAC-Net 這類工作把 AU 當(dāng)作圖上的節(jié)點用圖注意力學(xué)習(xí)它們之間的共現(xiàn)關(guān)系Transformer 方案則把每個局部 patch 當(dāng)作 token用自注意力挖掘區(qū)域之間的關(guān)聯(lián)。效果通常更好但對數(shù)據(jù)和算力的要求也更高。選型建議很直接如果你是自己做實驗先跑通 ROI 裁剪或者熱圖注意力的版本核心是把“局部特征”這件事做對等 baseline 穩(wěn)了再上 GNN 或 Transformer 刷點。一上來就懟大模型很容易在數(shù)據(jù)處理細(xì)節(jié)上翻車。3.3 損失函數(shù)與評估指標(biāo)調(diào)F1而不是調(diào)準(zhǔn)確率訓(xùn)練時損失函數(shù)一般用BCEWithLogitsLoss也就是把 sigmoid 和 BCE 合并在一起計算數(shù)值上更穩(wěn)定。類別不平衡怎么辦兩個常用手段# 為每個AU設(shè)置不同正樣本權(quán)重 pos_weight torch.tensor([negative_count[i] / positive_count[i] for i in range(num_aus)]) criterion nn.BCEWithLogitsLoss(pos_weightpos_weight)另一個思路是 Focal Loss它對難分類樣本給更大梯度權(quán)重也能緩解不平衡。實際中我更喜歡先用 pos_weight參數(shù)好調(diào)、效果穩(wěn)定。評估指標(biāo)千萬別用“準(zhǔn)確率”。想想看10 個 AU 全為 0 的樣本占多數(shù)模型全預(yù)測 0 也能有 80% 準(zhǔn)確率看起來很好實際上什么 AU 都沒識別出來。正確做法是看每個 AU 的 F1再算平均 F1通常叫 Avg F1有時候也看所有正樣本上的總 F1。F1 能同時懲罰漏檢和誤檢對不平衡任務(wù)公平得多。4. 跑通AU_Recognition-master的完整實操流程4.1 環(huán)境準(zhǔn)備與項目結(jié)構(gòu)下載AU_Recognition-master之后先別急著 python train。把依賴裝好再說。這個項目通常是 PyTorch 系我建議的環(huán)境組合是Python 3.8 或 3.9PyTorch 1.10帶 CUDA 11.xopencv-python、numpy、pandas、tqdm、matplotlib人臉檢測/關(guān)鍵點庫dlib 或 face-alignment部分版本用 MTCNN/RetinaFacegit clone https://github.com/你的地址/AU_Recognition-master.git cd AU_Recognition-master pip install -r requirements.txt項目結(jié)構(gòu)通常長這樣data/放數(shù)據(jù)索引與預(yù)處理腳本models/放網(wǎng)絡(luò)結(jié)構(gòu)定義utils/放評估指標(biāo)和可視化函數(shù)根目錄下有train.py、test.py、inference.py。不同人的組織習(xí)慣不一樣但整體鏈路大差不差。4.2 從數(shù)據(jù)預(yù)處理到訓(xùn)練一條線這是我最建議你認(rèn)真看的部分。整個流程可以拆成四步下載并整理數(shù)據(jù)集。以 BP4D 為例下載后把視頻抽幀或者直接用官方提供的幀圖生成索引 CSV。每一行至少包含image_path和au_label兩列。離線預(yù)處理或在線預(yù)處理。我的經(jīng)驗是人臉檢測和關(guān)鍵點對齊很耗時如果每輪 epoch 都重新做一次訓(xùn)練效率極低。建議先把所有人臉檢測、對齊、裁剪好保存成 numpy 文件或圖片緩存訓(xùn)練時只讀緩存。缺點是占磁盤優(yōu)點是快。啟動訓(xùn)練。典型命令類似python train.py --dataset BP4D --backbone resnet50 --batch-size 32 --epochs 50 --gpu 0訓(xùn)練過程中重點盯兩個東西每個 epoch 的驗證集平均 F1以及各個 AU 的 F1 是否出現(xiàn)“大部分 AU 不工作”的現(xiàn)象。如果 F1 長期在低位徘徊先查數(shù)據(jù)對齊結(jié)果而不是換網(wǎng)絡(luò)。 4.保存 checkpoint 與評估。訓(xùn)練完用驗證集上 Avg F1 最高的模型跑test.py拿到每個 AU 的 F1、平均 F1 和混淆矩陣方便寫報告用。4.3 單張圖片和實時視頻推理訓(xùn)練完之后的推理鏈路很清晰檢測人臉 - 關(guān)鍵點對齊 - 裁剪 - 模型前向 - sigmoid 得到概率 - 閾值化成 0/1。偽代碼如下model.eval() with torch.no_grad(): out model(aligned_face) # shape: [1, num_aus] probs torch.sigmoid(out).cpu().numpy()[0] aus [i 1 for i, p in enumerate(probs) if p thresholds[i]] print(檢測到AU, aus)這里的thresholds很重要后面第五節(jié)會細(xì)說。實時視頻推理時不要每幀都重新檢測人臉那樣 CPU 占用會非常高。視頻流里用跟蹤器鎖定人臉位置每隔幾十幀重新檢測一次中間的幀直接用上一幀的人臉框做對齊裁剪速度能快很多。5. 部署踩坑記錄從數(shù)據(jù)集下載到真實場景泛化5.1 數(shù)據(jù)集格式“方言”太多先統(tǒng)一標(biāo)注再訓(xùn)練BP4D、DISFA、EmotioNet 這三個數(shù)據(jù)集的 AU 編號并不完全一致有的標(biāo) 12 個 AU有的標(biāo) 8 個 AU標(biāo)簽文件格式更是五花八門。我第一次訓(xùn)練時就吃過虧腳本默認(rèn)讀 CSVDISFA 給的是文檔行格式解析直接報錯。排查鏈路是這樣的先打印第一條數(shù)據(jù)對照原數(shù)據(jù)集文檔確認(rèn)字段含義再檢查 AU 映射表把數(shù)據(jù)集自帶的編號映射到項目模型統(tǒng)一的 0-based 索引最后做一個簡單的數(shù)據(jù)完整性校驗看看正樣本數(shù)量是否符合預(yù)期。建議所有數(shù)據(jù)集解析完都統(tǒng)一輸出成同一種格式比如image_path,au_idx,value的長表或標(biāo)準(zhǔn)多列 CSV后面訓(xùn)練腳本只認(rèn)這一種格式省心很多。5.2 高F1的模型一到攝像頭就“翻車”這是最讓我頭疼的一個坑。BP4D 驗證集上 Avg F1 明明還行一到公司實際攝像頭畫面里就頻繁誤報畫面稍微暗一點或者人臉側(cè)一點AU4皺眉就開始亂觸發(fā)。原因仔細(xì)想想也很明白BP4D 是實驗室場景正面、光照均勻、表情做作實際攝像頭畫面是自然光照、有側(cè)臉、有遮擋、表情強度也小得多。這是典型的分布偏移。我的處理方式有三招先把所有 AU 的判定閾值在驗證集上做網(wǎng)格搜索不要用默認(rèn) 0.5。類別不平衡時0.5 往往不是最優(yōu)閾值對正樣本稀少的 AU閾值要降到 0.3 甚至 0.2 才合理。再用時序平滑。單幀預(yù)測噪聲大但 AU 在幾百毫秒內(nèi)是連續(xù)的。用一個滑動窗口對概率做均值或者 EMA 平滑比單幀硬判穩(wěn)得多。最后做數(shù)據(jù)增強。給訓(xùn)練數(shù)據(jù)加光照抖動、小角度旋轉(zhuǎn)、部分遮擋random erasing能在一定程度上模擬真實場景。5.3 資源受限下的訓(xùn)練技巧如果只有一張消費級顯卡顯存裝不下大 batch不要直接投降。我常用的三個手段第一是梯度累積代碼里用一個小 batch 多次前向把梯度累積起來再更新參數(shù)等效于增大 batch size只是訓(xùn)練時間變長。第二是混合精度訓(xùn)練PyTorch 自帶 AMP顯存占用能少一半速度還更快。第三是降低輸入分辨率。AU 識別雖然依賴局部細(xì)節(jié)但也沒必要一開始就用 224×224。先用 160×160 把流程跑通再慢慢加分辨率。這三個手段組合起來一張 8G 顯存的卡也能訓(xùn)得動 ResNet50 級別的 AU 模型。6. 從研究到落地AU識別還能用在哪些地方6.1 疲勞駕駛、心理評估與醫(yī)療輔篩疲勞駕駛檢測是我比較看好的落地場景。通過 AU26/AU27下頜下拉、張嘴和眼部相關(guān)動作的持續(xù)時長可以判斷打哈欠、閉眼這些疲勞信號比單純用眼睛縱橫比判閉眼更魯棒。在心理評估方向研究里有不少工作把微笑時的 AU6 和 AU12 聯(lián)合出現(xiàn)作為“真實積極情緒”的指標(biāo)用于抑郁傾向輔助篩查或者人機交互中的情緒反饋。注意這不是診斷工具而是給專業(yè)人員提供一個客觀測量參考。工程上做這類系統(tǒng)時一定要先把數(shù)據(jù)隱私和倫理邊界想清楚別把“表情分析”包裝成“讀心術(shù)”。6.2 虛擬形象驅(qū)動與表情遷移現(xiàn)在很多 3D 虛擬人、直播數(shù)字人需要實時驅(qū)動面部表情。AU 作為中間表示很合適先識別用戶當(dāng)前的真實 AU 狀態(tài)再映射到虛擬形象的表情參數(shù)比直接遷移關(guān)鍵點坐標(biāo)更穩(wěn)定也不容易產(chǎn)生恐怖谷效應(yīng)。這套方案在換臉、表情重演類應(yīng)用里也能看到類似思路。6.3 我的落地建議先從單攝像頭正面場景切入我個人踩過不少坑之后的體會是AU 識別很強大但它不是萬能的。如果你想在真實產(chǎn)品里用最好先從一個相對理想的環(huán)境切入。固定攝像頭、人正對鏡頭一到兩米、光線可控這三個條件滿足后再做性能調(diào)優(yōu)成功率會高很多。另外在工程上別指望模型每幀輸出一次“精確無誤”的 AU 集合就萬事大吉合理的做法是把 AU 概率當(dāng)中間特征結(jié)合時序規(guī)則和業(yè)務(wù)邏輯一起判斷比如連續(xù) N 幀 AU6 持續(xù)激活才算一次真實微笑。最后再分享一個小技巧跑通項目之后先自己錄一段視頻把每一幀的 AU 預(yù)測可視化出來你馬上就能發(fā)現(xiàn)模型在哪種姿勢、哪種光照下會失常這比看多少論文里的指標(biāo)都管用。本文還有配套的精品資源點擊獲取