(CVPR 2020 Oral)最新Scene Graph Generation開源框架與一些碎碎念
https://zhuanlan.zhihu.com/p/109657521
最新最完善的場景圖生成Scene Graph Generation (SGG)代碼框架介紹,以及關於場景圖生成我們真正應該關心的是什么
前言:
2019上半年跌跌撞撞地搞了很多亂七八糟的東西但都沒work,尤其讓我酸的是我上半年沒做work的一個VQA的idea居然在同年ICCV看到一篇極其相似的文章,雖然對方取巧用了BERT硬是提了一點才中的,但真的沒產出的時候看着別人發paper太酸了。話雖如此,取巧用idea以外的trick發paper還是不值得學習的。同年下半年看了很久的《The Book of Why》來尋找靈感,最后到了臨近CVPR deadline,還是回歸了自己的老本行場景圖生成,投稿了一篇《Unbiased Scene Graph Generation from Biased Training》,並幸運的以(SA,SA,WA)的分數中了今年的CVPR 2020 Oral。結合我之前對SGG領域的了解,我認為目前SGG領域內關於不同message passing模型設計帶來的提升已經趨於飽和,且這個研究方向目前來看已經愈發沒有意義,因為由於自然存在以及數據標注中的bias和長尾效應(long-tail effect), 所謂的模型優化已經漸漸變成了更好的擬合數據集的bias,而非提取真正有意義的relationships。在此基礎上,我在該工作中主要做了如下兩件事:
1)延續我去年在VCTree(CVPR 2019)中提出的mean Recall@K,設計了一個unbias的inference算法(注意不是training方法哦~),並希望讓更多的人關注真正有意義的SGG,而不是去擬合數據集刷指標;
2)由於之前通用的SGG框架neural-motifs已經落后於時代,我設計了個新的代碼框架(已於Github開源)。
不僅結合了最新的maskrnn-benchmark用於底層物體檢測,同時集成了目前最全的metrics包括Recall,Mean Recall,No Graph Constraint Recall, Zero Shot Recall等,同時代碼中為各種指標的evaluation統一了接口,希望后續有更多的研究者們可以設計出更有價值的metrics,從而使SGG領域不至於再只關注一個biased指標Recall而淪為灌水聖地。
框架說明:
如果在過去兩年做過SGG的同學應該都或多或少知道neural-motifs代碼框架,雖然Rowan Zellers本身是我非常崇拜的一位大神,但是他neural-motifs代碼里的各種炫技操作和神乎其神的trick,經常讓我非常頭大,更不要說糟糕的文檔和說明了。當然,真正讓我下定決心要寫個新的Codebase的主要原因還是因為neural-motifs的底層Faster R-CNN已經過於落后,畢竟SGG里物體的檢測和識別也是非常重要的,甚至有時候比預測relationship更加重要。為了便於大多數有物體檢測背景的同學follow,我挑選了去年最當紅,哦不,最為可靠的facebookresearch/maskrcnn-benchmark框架作為基礎,在其基礎上搭建了我的Scene-Graph-Benchmark.pytorch。該代碼不僅兼容了maskrcnn-benchmark所支持的所有detector模型,且得益於facebookresearch優秀的代碼功底,更大大增加了SGG部分的可讀性和可操作性(BoxList類的設計簡直是人類工程智慧的結晶,崇拜,從早年的faster-rcnn框架過來的我熱淚盈眶)。目前我們框架提供的各種baseline模型,有着當之無愧的State-of-The-Art SGCls和SGGen結果(如下圖,PredCls我還需要花時間調一下)。由於復現版本的VCTree為了簡便省略了原文的Hybrid Learning,同時SGGen/SGCls/PredCls超參也做了統一,而非各自最優,所以此處的PredCls低於VCTree原文。
傳送門:
https://link.zhihu.com/?target=https%3A//github.com/KaihuaTang/Scene-Graph-Benchmark.pytorch
Recall@K for Iterative Message Passing(IMP), Neural Motifs, Transformer,VCTree
- Faster R-CNN預訓練
該項目的Faster R-CNN預訓練部分基本完全采用maskrcnn-benchmark的源代碼(雖然我們又增加了attribute_head的實現,但還沒開始正式使用),僅就數據集做了更換,換成SGG的VisualGenome數據集。因此后續研究者可以完全參考maskrcnn-benchmark的代碼設計新的detector,並運用於SGG中。下面是一個訓練命令行樣例:
CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --master_port 10001 --nproc_per_node=4 tools/detector_pretrain_net.py --config-file "configs/e2e_relation_detector_X_101_32_8_FPN_1x.yaml" SOLVER.IMS_PER_BATCH 8 TEST.IMS_PER_BATCH 4 DTYPE "float16" SOLVER.MAX_ITER 50000 SOLVER.STEPS "(30000, 45000)" SOLVER.VAL_PERIOD 2000 SOLVER.CHECKPOINT_PERIOD 2000 MODEL.RELATION_ON False OUTPUT_DIR /home/kaihua/checkpoints/pretrained_faster_rcnn SOLVER.PRE_VAL False
主要參數文件可以參考"configs/e2e_relation_detector_X_101_32_8_FPN_1x.yaml"。注意所有config文件的default設置在"maskrcnn_benchmark/config/defaults.py"中,而優先級如下,具體每個參數的意義可以參考我Github項目中的解釋。
命令行參數 ===》(覆蓋)===》xxx.yaml參數 ===》(覆蓋)===》defaults.py參數
當然因為detector的訓練費時費力費卡,而且考慮到公平起見以往大部分SGG的工作都會follow一個固定的預訓練模型,即neural-motifs項目所給的預訓練detector模型。我這里也release了一個我訓練好的ResNeXt-101-32x8模型。考慮到國內同學可能下載不便,我這里給一個百度網盤的鏈接(提取碼:gsfn):
https://link.zhihu.com/?target=https%3A//pan.baidu.com/s/1MDFgqluIe_LKhi7MP2Pjqw
值得一提的是,我們的Faster R-CNN其實還支持了attribute_head,即物體的屬性,但為了公平比較,我目前還沒有在發表的文章中加入對應實驗,歡迎大家完善這部分工作。
- SGG as RoI_Head
關於最主要的SGG部分,我將其設計為了一個roi_head,參考其他roi_heads,如box_head的設計,我將主要代碼集中於"maskrcnn_benchmark/ modeling/ roi_heads/ relation_head"目錄下,結構如下:
relation_head代碼結構
所以,如果想要設計自己的新模型,大部分情況下,只需要修改 "roi_relation_predictor.py" 以及增加相應的"model_xxx.py"和"utils_xxx.py"足矣。目前我們框架所提供的模型有:
-
Neural-MOTIFS: https://arxiv.org/abs/1711.06640
-
Iterative-Message-Passing: https://arxiv.org/abs/1701.02426
-
VCTree: https://arxiv.org/abs/1812.01880
-
Transformer (由史佳欣實現,沒有發表單獨的paper)
-
Causal-TDE:https://arxiv.org/abs/2002.11949
-
- MOTIFS: https://arxiv.org/abs/1711.06640
- VCTree: https://arxiv.org/abs/1812.01880
- VTransE: https://arxiv.org/abs/1702.08319
他們可以通過"MODEL.ROI_RELATION_HEAD.PREDICTOR"參數進行選擇。因為最后一個Causal-TDE是我今年CVPR2020提出的一個特殊的Inference方式,而非具體的模型,所以它測試了三種模型。我們會在下文無偏見的場景圖生成中具體的介紹這個方法。
- 所有指標簡介
作為基礎知識,我先簡單介紹下SGG的三種設定: 1) Predicate Classification (PredCls): 給定所有ground-truth的物體類別和bounding boxes,求這張圖的場景圖。2)Scene Graph Classification (SGCls): 給定所有ground-truth物體的bounding boxes,求場景圖(需要預測物體類別)3) Scene Graph Detection/Generation (SGDet/SGGen) :只給圖片,自己跑detection檢測物體,最后預測場景圖。
然后關於指標,之前的SGG最大的問題之一,就是過度依賴單一指標Recall@K,而這個指標因為不區分各個類別的貢獻加上VisualGenome本身的長尾效應,很容易被過擬合。這導致了近年大多數SGG的文章,只是在過擬合的道路上越走越遠,而非真正生成了更有意義的場景圖。於是我在該框架中整合了已知的所有指標,也希望后續的工作可以report更多有意義的指標來分析算法有優缺。下圖是我代碼的一個結果輸出樣例。
項目在測試/驗證時的輸出格式
所有本框架支持的指標有:
- Recall@K (R@K): 這是最早的也是最廣為接受的指標,由盧老師在https://arxiv.org/abs/1608.00187中提出。因為VisualGenome數據集的ground-truth中對relationship的標注並不完整,所以簡單的正確率並不能很好的反映SGG的效果。盧老師用了檢索的指標Recall,把SGG看成檢索問題,不僅要求識別准確,也要求能更好的剔除無關系的物體對。
- No Graph Constraint Recall@K (ngR@K):這個指標最早由Pixel2Graph使用,由Neural-MOTIFS命名。這個指標的目的在於,傳統的Recall計算里,一對物體只能有一個relation參與最終的排序,但ngR@K允許一對物體的所有relation都能參與排序。這也非常有道理,比如 human(0.9) - riding (0.6) - horse (0.9):total score=0.9x0.6x0.9,可能這對物體還有另一個relation:human(0.9) - on (0.3) - horse (0.9):total score=0.9x0.3x0.9。后者雖然分數比riding低,但也是一種可能的情況。ngR@K的結果往往大大高於單純的R@K。
- Mean Recall@K (mR@K): 這個指標由我的VCTree和另外一個同學的KERN在2019年的CVPR中同時提出,不過我並沒有作為VCTree的主要貢獻,只在補充材料中完整展示了結果表。由於VisualGenome數據集的長尾效應,傳統Recall往往只要學會幾個主要的relation類比如on,near等,即便完全忽視大部分類別也可以取得很好的結果。這當然不是我們想看到的,所以mean Recall做了一件很簡單的事,把所有謂語類別的Recall單獨計算,然后求均值,這樣所有類別就一樣重要了。模型的驅動也從學會盡可能多個relation(有大量簡單relation的重復)變成學會盡可能多種類的relation。
- Zero Shot Recall@K (zR@K):在早期的視覺關系識別中,人們也使用了Zero Shot Recall指標,但在SGG中又漸漸被人忽視了,我們在這又重新增加了這個指標,因為它可以很好的展示SGG的拓展能力。Zero Shot Recall指的並不是從來沒見過的relation,而只是在training中沒見過的主語-謂語-賓語的三元組組合,所有單獨的object和relation類別還是都見過的,不然就沒法學了。
- Top@K Accuracy (A@K):這個指標來自於某個之前研究者對PredCls和SGCls的誤解,並不建議大家report到文章中,這里列出來是希望大家以后別犯這個錯。該同學在PredCls和SGCls中不僅給了所有object的bounding box,還給了主語-賓語所有pair的組合,所以這就完全不是一個Recall的檢索了,而是給定兩個物體,來判斷他們relation的正確率。
- Sentence-to-Graph Retrieval (S2G):最后是我在Causal-TDE中提出的ground-truth caption到SG檢索,它可以看成一個理想的下游任務,可以看作一個VQA:問指定圖片的SG符不符合給定描述。他的意義在於,他完全摒棄了visual feature,只使用符號化的SG。他可以測試檢測出的SG是否可以用來完整地豐富地表示原圖(潛台詞:從而支持符號化的推理)。由於這需要額外的訓練過程,所以並不能直接在SGG的val/test里輸出。
當然現有的metrics也許仍然不夠完善,如果有更好的能體現SG效果或有助於分析SGG的指標,我的代碼框架也支持自定義指標,本項目的所有指標都通過如下類實現,可以添加至"maskrcnn_benchmark/data/datasets/evaluation/vg/sgg_eval.py"並在同目錄下的"vg_eval.py"中調用。
class SceneGraphEvaluation(ABC):
def __init__(self, result_dict):
super().__init__()
self.result_dict = result_dict
@abstractmethod
def register_container(self, mode):
print("Register Result Container")
pass
@abstractmethod
def generate_print_string(self, mode):
print("Generate Print String")
pass
# Don't forget the calculation function
# def calculate_recall()
# return
- 常見誤區
在從事SGG研究的這兩年里,我從看的paper,代碼,和自己當reviewer審的paper里,發現兩個常見的誤區,這會導致結果異常的高。我假設這些作者是出於無心,所以在此澄清。
- 不加區分No Graph Constraint Recall@K (ngR@K)和Recall@K (R@K)。除了早期最開始提出ngR的Pixel2Graph外,我還看到過一些人,直接將自己ngR的結果和別人單純Recall的結果作比較,聲稱自己是state-of-the-art的結果。這是非常不公平的。他們常見的症狀有:1)沒有明顯理由的情況下PredCls的Recall@100大於75%,2)文中沒有明確提及ngR和R的區別。目前比較合理的推測是,在VisualGenome數據集中,傳統Recall@100在Predcls上的上限應當在70%左右。因為數據集的不完整性,其實很多預測都是正確的,只是數據集里沒標,所以這造成了傳統Recall永遠無法達到理論的100%。那么如果自稱PredCls的Recall@100大於75%的,多半其實是用了ngRecall。
- 在PredCls和SGCls中,使用Top@K Accuracy (A@K)來報告成Recall,這又是另一個誤區。因為PredCls和SGCls中給定的只是所有object的bounding box,而非具體的主語-賓語的pair信息。一旦給定了pair信息,那么其實就沒有recall的ranking了,只是純粹的accuracy。這個誤區最初發現於contrastive loss中。我花了小半個月才發現為什么他的PredCls和SGCls結果這么好。它的症狀也很簡單,PredCls和SGCls中的Recall@50, Recall@100結果一摸一樣,只有Recall@20稍低。因為沒有了ranking,top50和100也就沒區別了,沒有圖片有多於50個的ground-truth relationships。
- 額外的話
- 對於一些模型,打開或關閉 "MODEL.ROI_RELATION_HEAD.POOLING_ALL_LEVELS" 這個設置會很大地影響relation的預測,比如如果在VCTree的Predcls中關閉這個設置,就可以提升結果,但在對應的SGCls和SGGen中卻不行。上文的VCTree結果,我為了統一都打開了這個設定。
- 對於一些模型(不是全部),使用Learning to Count Object中提出的一種fusion方法,可以顯著地提升結果。這種fusion算法的公式為:\(f(x_1,x_2)=ReLU(x_1+x_2)-(x_1-x_2)^2\) 。使用方法是,在 "roi_relation_predictors.py" 中,將主語賓語特征的混合方式改為這個公式,目前項目中使用的為簡單的torch.cat((head_rep, tail_rep), dim=-1)。
- 另外更不要說一些hidden_dim的參數了。我想表達的是,因為時間的限制(還有卡的),我其實沒有對這個項目做過多的調參,我寫的一些超參我也沒有完整測試。如果有打比賽經驗的同學,通過修改參數達到更好的結果我一定不會驚訝。
無偏見的場景圖生成(和一些不成熟的想法)
其實在我接觸SGG以來最大的困惑不是SGG做的不好,而是現在的SGG如果單看Recall明明已經非常好了,但為什么仍沒有得到廣泛的應用。換句話說,SGG畫的餅這么香為什么沒人吃,雖然也有一些下游任務中有人利用SGG發了paper,但都沒有成為主流。我的想法是relationship是一個非常主觀且很依賴語境的標簽,這不同於簡單的物體分類和識別,在后者中對就是對錯就是錯。而我的理解里單純的deep learning的本質是memorize所有數據集,並做一些簡單的歸納匯總(一定程度的generalize),這是一個非常passive的過程,所以在簡單的分類識別等passive的任務上效果最為顯著。比作人類的話,這可以看作人下意識情況下做的一些工作(我們從來不知道自己是怎么識別出蘋果的,識別的時候純粹是下意識反應,也不會去思考為什么是蘋果)。最新的NLP中的研究也發現,進入Transformer時代后,很多簡單的問答,機器已經可以做的非常擬人了。但如果稍微在問題中加入一個簡單的1+1之類的需要思考的過程,模型瞬間給出很多滑稽的結果。所以現在的deep learning學出來的模型,更像一個被動的只靠直覺驅使的人。而SGG和任何需要Reasoning的任務比如VQA,都更加主動和主觀。比如SGG中到底是(human, on, horse)還是(human, riding, horse),取決於語境是關注“what's on horse?(空間關系)”還是“what is the man doing?(具體動作)”,不能簡單地說誰對誰做(如果直接歸為多類別問題的話,又會因為同義詞太多而無法得到足夠好的標注)。我不認為現在的大部分推理(Reasoning)的文章,真正做到了推理(雖然我們組就是做這個的),加了很多attention也只是更好的擬合了數據集而已。因為給定同一份輸入,一個確定的網絡(假設不包含隨機sample等操作)永遠只會得到一個結果,是一個passive的反饋。而我們最新的CVPR 2020的文章“Unbiased Scene Graph Generation from Biased Training”就嘗試通過對因果圖的干預,使同一份輸入,同一個網絡,得到不同的輸出,用於不同的目的。類似於人對於同一個問題,可以在腦海里思考各種可能選擇,做出權衡。我覺得這可能是未來真正解決Reasoning的一個可能方向,用一個deep model擬合對世界的認知(知識),但真正的推理在於對這些知識的應用和分析,即對模型中各個節點進行不同的干預后的觀察。當然這種干預用什么去驅使,RL?Sample?還有待考慮(這怕不是要靠意識本身)。
扯遠了,回到SGG中。雖然我們還是無法解決用什么標准去評價一個更主觀更有意義的SGG,但肯定不是目前最主流的Recall@K,在這種指標下即便模型只學出了on/near/wear/has等少數幾種relationship,模型的Recall@K依然非常高,這種場景圖對下游reasoning任務的幫助非常有限,簡單的來說沒有引入足夠多的信息量,那么一個最簡單的辦法,就是讓relationship盡可能地diverse,學到盡可能多種類的relationship就有更多信息量了。所以去年CVPR 2019,我在VCTree這篇文章里提了個mean Recall的指標用於評價一個無偏見的場景圖,上文也作了介紹。
- KERN與VCTree(CVPR 2019)
其實同年,有兩篇工作同時提出了一摸一樣的mean Recall的指標,名字都一樣。除了我們的VCTree還有一篇叫KERN的工作。這兩篇工作首次開始關注,怎么衡量和解決無偏見的場景圖。且這兩篇給出的solutions都和網絡的message-passing的結構有關。傳統的方法往往通過attention來體現這種結構,這樣也就將結構和feature一起end-to-end地訓練了,就非常容易造成網絡的過擬合,即產生結果完全biased到一些大類中:on/near。那么我們來看看VCTree和KERN給的兩種解決方案。
1)KERN使用了一個基於統計的message-passing structure,這個結構是一個hard structure無法學習(基於他們的代碼,我發現這個結構matrix被設為requires_grad = False)。既然結構都不學了,所以也就不存在結構上的過擬合。
2)VCTree則用一個獨立的分支來學習structure,並確保feature learning的分支和structure learning的分支沒有/中斷反向傳播。這也就保證了,結構和feature不會共同過擬合到一起。下圖是我對VCTree核心idea的一個概覽圖。
VCTree結構分支與feature分支概覽圖
到了今年的CVPR,我們做了一個更大膽的構想。我們將常見的MOTIFS和VCTree等模型歸納為了如下的因果圖。因果圖中每個節點為一些關鍵變量,而其中的有向邊就是對各種網絡forward運算的簡化,僅體現了一種因果上的決定關系。
左側為網絡概述,右側為對應的因果圖
然后,我們分析了這些模型中偏見到底來自於哪里。我們認為,SGG中的偏見主要來源於下圖右下角的那種場景,即不看具體的兩個物體的狀態(feature),單純通過兩個物體的label和一個union box的環境信息,就盲猜這兩個物體是什么relationship。因為VisualGenome數據集的bias和長尾效應,偏偏這種盲猜不僅更容易學習還大部分情況下都是對的。這就導致了模型不再關注物體具體的狀態(feature)而直接take了盲猜的biased shortcut(畢竟deep learning永遠優先收斂到各種shortcut上)。導致的結果就是,具體的visual feature不再重要,也就預測不出真正有意義的finegrain的relationships了。因為更finegrain的relation出現太少,而且很容易錯,所以干脆把所有復雜的sitting on/standing on/riding全預測成on。
然后就產生了下圖C子圖中的非常沒有意義的scene graph,偏偏Recall還挺好。因為標注員本身就傾向於標這些簡單的relationships。(標注員標注時候的狀態基本就像我上文說的passive反饋狀態,所以標出來的relationship都趨同,沒有現實中真正結合語境時的那種靈活。一些Reasoning的任務,比如VQA,的標注也有類似問題。這些思維上比較active的任務,如果要求標注員自己列舉各種語境下不同的結果(SGG)或標注的同時給出腦海里思考的過程(VQA),又顯得代價太高昂而不現實)。
於是為了迫使模型預測更有意義的relationship,我們用了一個causal inference中的概念,即Total Direct Effect(TDE)來取代單純的網絡log-likelihood。Effect的概念可以理解為醫學上為了確定一個葯真正的療效,我們除了觀測患者服葯后的表現(傳統模型的預測),還要減去這個葯的安慰劑效應(通過控制變量/反事實干預得到的bias)。這就體現在了下圖中。我們的Unbiased TDE是一個除去了盲猜帶來的“安慰劑效應”后的真正的relationship可能性。
雖然簡單,這樣的方法卻取得了非常有效的效果,從下圖的各個predicate的單獨Recall結果就可以看出。注意!有的人或許會問,為什么不一開始就刪除模型中biased shortcut分支,只用feature預測relationship。這就好比去擬合Y=aX+b,想要得到一個無bias:b的模型,我們不能單純的就用Y=aX去擬合數據,而是要先構建出Y=aX+b擬合完了再刪去b。我們實驗部分也證明了單純去掉biased shortcut分支,並不會直接得到最好的unbiased SGG。另外,還有一些額外的使用TDE的trick,可以去我的Github上看對應的Tips and Tricks。
總結和展望
最后我想在總結里再嘮叨嘮叨場景圖的應用,我一直覺得場景圖是visual reasoning的基礎,但同時現在的SGG還遠不足以支撐visual reasoning。即便我提出的Unbiased SGG也還有很長的路要走。而具體怎么使用SG又是另一個問題了。
我們腦子里想事情的時候,往往都是先構建事物之間的相互關系,然后思考某些事物處於特定狀態下對其他相關聯事物的影響,然后關聯事物的關聯事物的影響。。。。這里我指的既可以是Unbiased SGG中提到的因果圖,也可以是將SG運用於Visual Reasoning任務中后的SG。但重要的一點是,我覺得真正的Reasoning肯定不再是簡單的用模型跑個one-time feedforwad。而是類似於:(1):干預1->觀察1->Output1, 干預2->觀察2->Output2, .... (2):->比較分析。
絮絮叨叨地說了一堆,最后都說的有點亂了。很多東西我也只是粗淺的不成熟的想法,有待更多的實驗和研究去驗證。畢竟不知道對不對,不知道work不work,不知道會發生什么,不知道結果為什么是這樣,才是科研的常態。希望大家多多嘗試些更有意思的想法,在state-of-the-art每天更新的時代,提升零點幾個點也許遠沒有嘗試些新的可能性,或者對一些現象做出有參考價值的分析來得有意義,哪怕performance不是最高也不要氣餒。說起來我的Causal TDE還降低了傳統指標Recall幾個點呢。