內(nèi)容同步發(fā)表于 知乎 與 科創(chuàng)論壇
前陣子讀了一篇關(guān)于三維重建的論文戒劫,是Facebook團(tuán)隊(duì)聯(lián)合華盛頓大學(xué)發(fā)表在CVPR2019上的。這一論文嘗試用神經(jīng)網(wǎng)絡(luò)去近似SDF函數(shù)(將在后面進(jìn)行簡(jiǎn)述),進(jìn)而重建三維模型。
在閱讀過程中我對(duì)其中的AutoDecoder概念產(chǎn)生了一定興趣智哀,特此將一些隨想記錄下來。
在談?wù)揂utoDecoder之前楚昭,我想先用最少的文字說說這個(gè)傳說中のDeepSDF如庭。
用最簡(jiǎn)單易懂的語言來說,對(duì)于一個(gè)已知的三維模型舒岸,SDF(Signed Distance Function)就是一個(gè)三維空間中的函數(shù)绅作,輸入一個(gè)坐標(biāo)點(diǎn),返回該點(diǎn)到三位模型表面的距離蛾派。同時(shí)這個(gè)距離是有符號(hào)的俄认,一般來說个少,對(duì)于模型外面的點(diǎn),距離為正眯杏,而模型內(nèi)部的點(diǎn)則為負(fù)夜焦。或者從觀察者的角度岂贩,如果點(diǎn)在表面面向觀察者的一側(cè)茫经,則為正,在背離觀察者的一側(cè)萎津,則為負(fù)卸伞。
引用DeepSDF論文官方的兔兔說明一下:
各個(gè)方法對(duì)于SDF函數(shù)的存儲(chǔ)往往是離散的,比如將三維空間是做一系列的“體素”(類比二維空間的“像素”)姜性,每個(gè)體素中存儲(chǔ)一個(gè)SDF值瞪慧,之后嘗試用體素中等于或接近零的部分重建一個(gè)三維表面。
而DeepSDF論文呢部念,則嘗試將SDF解析為一個(gè)連續(xù)的函數(shù)弃酌。雖然文章把這一點(diǎn)作為一個(gè)創(chuàng)新提出(包括將模型表面視作SDF回歸的決策邊界),其實(shí)我覺得這個(gè)很早應(yīng)該就有人想到了儡炼,因?yàn)楦旧蟻碇v妓湘,SDF本來就應(yīng)該是一個(gè)連續(xù)函數(shù)才對(duì),只是受制于各種原因乌询,我們無法為每個(gè)三維物體寫出這個(gè)連續(xù)函數(shù)的完整形式榜贴。這也就是DeepSDF的思路,用神經(jīng)網(wǎng)絡(luò)這個(gè)“萬能插值機(jī)”去近似這一函數(shù)妹田。
事實(shí)上我之前也想過這個(gè)方向唬党,也相信許多人嘗試過這個(gè)思路。不過就像大多數(shù)DeepLearning落地的應(yīng)用一樣鬼佣,真正需要做的工作在于如何科學(xué)地編碼輸入輸出驶拱,以及如何Encapsulate你自己的領(lǐng)域知識(shí)到神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)和訓(xùn)練過程當(dāng)中去。
文章一開始提出了一個(gè)顯而易見的結(jié)構(gòu)晶衷,就是直接拿個(gè)神經(jīng)網(wǎng)絡(luò)蓝纲,輸入坐標(biāo),輸出SDF值晌纫,然后對(duì)每一個(gè)三維模型單獨(dú)訓(xùn)練税迷,充分發(fā)揮“插值機(jī)”的原始作用。不過顯然锹漱,這種方法在現(xiàn)實(shí)中是很難應(yīng)用的箭养,因?yàn)閷?duì)于每個(gè)新模型都要訓(xùn)練一個(gè)新神經(jīng)網(wǎng)絡(luò),實(shí)在是太低效太不Generalizablism了凌蔬,那么自然地露懒,為了發(fā)揮深井網(wǎng)絡(luò)Generalizability的優(yōu)勢(shì)闯冷,團(tuán)隊(duì)自然而然想到了使用一個(gè)Latent Vector去表示三維模型的原始形態(tài),于是新的神經(jīng)網(wǎng)絡(luò)就變成了懈词,給定一個(gè)用來表示三維模型的Latent Vector蛇耀,附帶一個(gè)用以查詢的坐標(biāo)點(diǎn),返回這個(gè)坐標(biāo)點(diǎn)所在位置的SDF值坎弯。
具體的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)和LossFunction的設(shè)計(jì)就不詳述了纺涤,非常Straight forward,掃一眼論文就明白抠忘。
眾所周知(并不)獲得一個(gè)原始輸入數(shù)據(jù)的Latent Vector Representation撩炊,大家最喜歡的方法之一就是訓(xùn)練一個(gè)AutoEncoder。然而DeepSDF則使用了一個(gè)稍有不同的東西崎脉,他們叫做AutoDecoder拧咳,故名思義,就是一個(gè)不訓(xùn)練Encoder的Decoder囚灼。
這里我想吐個(gè)大嘈骆膝,他們的論文說了一大堆不要Encoder的理由,比如AutoEncoder大家一般都只用一半而把另一半另一半扔掉什么的灶体,說來說去沒說到點(diǎn)上阅签。強(qiáng)烈懷疑他們組提出使用AutoDecoder架構(gòu)的人和最后執(zhí)筆寫論文的人溝通不暢。我自己讀下來的感覺是蝎抽,AutoDecoder的選用本質(zhì)上是一個(gè)在FeedForward過程的速度政钟,和數(shù)據(jù)本身潛在的復(fù)雜度和變數(shù)之間的取舍(如果我的理解有錯(cuò),歡迎指正)
下面簡(jiǎn)單講一下這個(gè)傳說中の AutoDecoder:
如圖所示樟结,標(biāo)準(zhǔn)AutoEncoder的架構(gòu)大體上是养交,一個(gè)輸入,若干層(稱為Encoder)之后有一個(gè)關(guān)鍵的瓶頸層(Code)瓢宦,瓶頸層后面若干層(稱為Decoder)之后有一個(gè)和輸入層長(zhǎng)得一樣的輸出層层坠。訓(xùn)練的時(shí)候努力讓輸出等于(甚至優(yōu)于——比如修復(fù)類任務(wù))輸入,最后訓(xùn)練出來的瓶頸層刁笙,就是對(duì)輸入數(shù)據(jù)的一種更緊湊的壓縮,也叫做關(guān)于原始數(shù)據(jù)的Latent Vector谦趣。Latent Vector往往包含了原始數(shù)據(jù)最關(guān)鍵的那些信息疲吸,更加便于提取特征,等等等等前鹅。
而DeepSDF則提出摘悴,我們可以不要Encoder部分,而直接取用Decoder部分舰绘。
這就很TM詭異了蹂喻,如果我只有一個(gè)Decoder葱椭,我怎么知道哪個(gè)Latent Vector對(duì)應(yīng)原始輸入呢?
官方給出的示意圖長(zhǎng)這樣:
Hmm...不是很好懂口四,沒錯(cuò)孵运,不僅這圖不太好懂,論文里的描述也亂七八糟蔓彩,不過且慢治笨,容我再畫一張圖:
這張圖里,我們加入了一個(gè)額外的輸入赤嚼,稱為第0層旷赖,而 Code 層則是一個(gè)純 Linear 層,那么第 0 層對(duì) Code 層的權(quán)重
即是我們要的 Latent Vector更卒。
另外等孵,Decoder 不必是解碼成原始數(shù)據(jù)的樣子,而是只要解碼成目標(biāo)函數(shù)的輸出(比如SDF值)就好了蹂空。
那么對(duì)于DeepSDF來說俯萌,其結(jié)構(gòu)事實(shí)上可以理解成:
- 第 0 層是一個(gè)恒為1的輸入,
- Code 層和要查詢的
一起輸入到后面的深度神經(jīng)網(wǎng)絡(luò)
- 最后輸出SDF函數(shù)
而這個(gè)訓(xùn)練過程則是:
-
對(duì)每個(gè)模型
腌闯,初始化一個(gè)獨(dú)立的Latent Vector
(嗯绳瘟,原論文用
表示Latent Vector,不過為了避免和空間坐標(biāo)點(diǎn)
產(chǎn)生迷惑姿骏,我在這里用
代替了)
- 然后對(duì)于每個(gè)
樣本糖声,選擇該Vector作為到Code層的權(quán)重,即
分瘦,
- 再對(duì)整個(gè)神經(jīng)網(wǎng)絡(luò)(包含
)進(jìn)行訓(xùn)練蘸泻。
這樣子訓(xùn)練下來,每個(gè)訓(xùn)練樣本都漸漸就訓(xùn)練出了自己的Latent Vector嘲玫,而Decoder則學(xué)習(xí)到了對(duì)每個(gè)Latent Vector悦施,如何匹配其要查詢的對(duì)應(yīng)的sdf值。
可是既然這樣去团,每個(gè)訓(xùn)練數(shù)據(jù)都訓(xùn)練出了自己獨(dú)立的Latent Vector抡诞,那么面對(duì)新的數(shù)據(jù)(比如測(cè)試數(shù)據(jù)集)該怎么辦?論文里輕描淡寫了一個(gè)土陪,可是這個(gè)
是怎么做的呢昼汗?難道還是對(duì)每個(gè)新數(shù)據(jù)重新訓(xùn)練一番?鬼雀!
這里不得不吐槽一下顷窒,這個(gè)項(xiàng)目不僅論文抓不住重點(diǎn),代碼也寫得亂七八糟源哩,讀得人暈頭轉(zhuǎn)向鞋吉,重建的核心部分居然還存在# TODO: why is this needed
這樣的注釋鸦做。我準(zhǔn)備過一陣子專門針對(duì)機(jī)器學(xué)習(xí)界軟件工程基礎(chǔ)薄弱,代碼凌亂不堪的現(xiàn)象開一篇文章谓着。
于是帶著迷惑我又去看了一遍這個(gè)項(xiàng)目的源碼泼诱,然后發(fā)現(xiàn)還真的(差不多)是這樣 。漆魔。坷檩。
不過比起直接用作為輸入的那個(gè)版本,這個(gè)版本在面對(duì)新數(shù)據(jù)
的時(shí)候改抡,鎖定了整個(gè)Decoder矢炼,也就是說保持
的映射不變,而只訓(xùn)練新的
阿纤,大大減少了需要訓(xùn)練的參數(shù)數(shù)量句灌。并且因?yàn)镈ecoder經(jīng)過之前的訓(xùn)練,已經(jīng)包含了關(guān)于訓(xùn)練數(shù)據(jù)的先驗(yàn)知識(shí)欠拾,所以這次Latent Vector訓(xùn)練的收斂將會(huì)十分迅速胰锌。
其實(shí)這里面我有一個(gè)小小的疑惑,為什么不保留論文最開始的結(jié)構(gòu)(只輸入)藐窄,但是對(duì)于每個(gè)不同的模型资昧,獨(dú)立訓(xùn)練輸入層對(duì)第一個(gè)隱藏層的權(quán)重,這樣輸入層對(duì)第一個(gè)隱藏層的變換可以視作一個(gè)“Latent Function”(對(duì)應(yīng)“Latent Vector”的概念)荆忍,將
的值變換為該模型對(duì)應(yīng)的格带,隱空間下該點(diǎn)的特征。這樣一來刹枉,神經(jīng)網(wǎng)絡(luò)的參數(shù)數(shù)量能夠減少叽唱,并且也能夠更好地表達(dá)Latent Coding和空間坐標(biāo)之間的耦合關(guān)系。這一方面的比較和分析微宝,如果有時(shí)間棺亭,我也有可能會(huì)去做一做。
所以蟋软,答案是镶摘,AutoDecoder的結(jié)構(gòu)不能像AutoEncoder一樣面對(duì)新數(shù)據(jù)時(shí)只需要一個(gè)Forward過程,而是需要根據(jù)后面的Decoder中隱含的先驗(yàn)知識(shí)岳守,進(jìn)行少量的訓(xùn)練钉稍,以找到一個(gè)Latent Code出來。
那么問題來了棺耍,為什么要使用一個(gè)無法進(jìn)行實(shí)時(shí)推斷而是每次都要訓(xùn)練的AutoDecoder來代替原有的AutoEncoder呢?
一個(gè)不那么具備說服力(但是論文里似乎確實(shí)提出了)的理由是种樱,因?yàn)镈ecoder不必還原原始數(shù)據(jù)蒙袍,而可以是近似目標(biāo)函數(shù)俊卤,所以訓(xùn)練出來的Latent Vector,對(duì)于我們目標(biāo)函數(shù)所需要的信息會(huì)更加友好害幅。但是仔細(xì)想一想就會(huì)發(fā)現(xiàn)消恍,如果我們直接使用原始數(shù)據(jù)進(jìn)行輸入,構(gòu)建一個(gè)神經(jīng)網(wǎng)絡(luò)以现,然后在某一隱藏層拼接上查詢狠怨。那么訓(xùn)練之后,我們就可以得到一個(gè)在預(yù)測(cè)時(shí)只需要Forward過程而無需再訓(xùn)練的神經(jīng)網(wǎng)絡(luò)邑遏,而被拼接了
的那一層也就可以視作Latent Vector佣赖。
所以……用AutoDecoder的深層原因到底是什么?
于是我去費(fèi)勁巴拉地又讀了一遍他們的代碼记盒,著重看了看Reconstruction部分憎蛤,想辦法搞明白這個(gè)神經(jīng)網(wǎng)絡(luò)到底在做什么之后,謎題便漸漸解開了纪吮。
首先俩檬,這個(gè)模型在重建的時(shí)候到底在干什么?這個(gè)問題在我讀論文和代碼的時(shí)候一度困擾了我很久碾盟,尤其是在我看到這個(gè)預(yù)期輸出是SDF Value的神經(jīng)網(wǎng)絡(luò)在 Test - Evaluation 階段棚辽,依然在測(cè)試數(shù)據(jù)集中包含SDF Value進(jìn)行訓(xùn)練。進(jìn)一步捋順之后我反應(yīng)過來他們?cè)谏窠?jīng)網(wǎng)絡(luò)中的那一步Evaluation冰肴,實(shí)際上是在衡量這個(gè)神經(jīng)網(wǎng)絡(luò)能構(gòu)造出一個(gè)多好的Latent Vector來近似測(cè)試數(shù)據(jù)集中的SDF Value屈藐,而不是嘗試計(jì)算Reconstruct Error。而在真正的表面重建過程中嚼沿,這個(gè)收斂出來的Latent Vector將和任意輸入坐標(biāo)一起得出一個(gè)SDF Value估盘,進(jìn)而實(shí)現(xiàn)任意精度的重建。
那么問題就逐漸明朗了骡尽,DeepSDF在做的事情遣妥,其實(shí)是對(duì)于有限精度采樣的SDF數(shù)據(jù),在包含先驗(yàn)知識(shí)的Decoder的幫助下攀细,回歸出一個(gè)連續(xù)的SDF函數(shù)箫踩,從而實(shí)現(xiàn)將有窮精度的數(shù)據(jù)轉(zhuǎn)換成任意精度的查詢器,以便進(jìn)行精細(xì)重建谭贪。
返回來看這個(gè)AutoDecoder在測(cè)試數(shù)據(jù)上的訓(xùn)練過程(即尋找Latent Vector的過程)境钟,我們發(fā)現(xiàn),可以將一個(gè)模型的所有采樣點(diǎn)作為一個(gè)完整的epoch俭识,而不是把單個(gè)模型的全部采樣數(shù)據(jù)一股腦輸入進(jìn)去慨削。這么做的好處是什么呢?
- 如果是AutoEncoder,為了得到代表一整個(gè)模型的Latent Vector缚态,我們將不得不想辦法統(tǒng)一模型原始數(shù)據(jù)的分辨率(比如每三個(gè)神經(jīng)元代表一個(gè)采樣點(diǎn)磁椒,那么一個(gè)模型最多只能有
input_size / 3
個(gè)采樣點(diǎn))。而對(duì)于AutoDecoder玫芦,一個(gè)模型對(duì)應(yīng)的采樣數(shù)量只影響其epoch的大小浆熔,而不會(huì)影響單個(gè)輸入本身的尺寸,所以AutoDecoder的架構(gòu)支持對(duì)每個(gè)輸入模型有任意數(shù)量的采樣點(diǎn)桥帆。 - 另一個(gè)非常重要的是医增,對(duì)于模型的采樣點(diǎn),一個(gè)顯著的特征就是它是Orderless的老虫,即你交換任意兩個(gè)采樣點(diǎn)的順序叶骨,也不應(yīng)當(dāng)影響最終輸出的結(jié)果。傳統(tǒng)的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)并不是很善于解碼orderless vector的輸入张遭,但是如果我們不將整個(gè)模型的所有采樣點(diǎn)編碼為一個(gè)巨大的input vector邓萨,而是將每個(gè)采樣點(diǎn)作為一個(gè)input vector,整個(gè)模型的所有采樣點(diǎn)作為一個(gè)epoch去訓(xùn)練菊卷,那么我們就可以應(yīng)對(duì)這種orderless的特征了缔恳。
綜上,我們可以得到AutoDecoder結(jié)構(gòu)與AutoEncoder以及論文最開始提出的結(jié)構(gòu)(下面簡(jiǎn)稱xyz結(jié)構(gòu))進(jìn)行比較:
- AutoDecoder vs xyz結(jié)構(gòu):
- 對(duì)于新的模型洁闰,可以比xyz更快速地收斂成一個(gè)可靠的查詢器
- 經(jīng)過訓(xùn)練的Decoder包含了來自訓(xùn)練數(shù)據(jù)集的先驗(yàn)知識(shí)歉甚,使得對(duì)于SDF的回歸并不是單純地“插值”操作,而是考慮了模型本身附帶的意義而進(jìn)行的合理補(bǔ)全扑眉。
- AutoDecoder vs AutoEncoder
- AutoDecoder能夠處理任何尺寸的采樣數(shù)據(jù)纸泄,而不必像AutoEncoder那樣將一個(gè)模型的全部采樣點(diǎn)包含在一個(gè)定長(zhǎng)的input vector中
- AutoDecoder將單獨(dú)的Input Vector轉(zhuǎn)換成Epoch的操作,使得采樣的順序能夠被很好地解耦腰素,適合處理orderless的采樣數(shù)據(jù)
- 相比AutoEncoder來說聘裁,AutoDecoder面對(duì)每一個(gè)新數(shù)據(jù)時(shí)都需要一個(gè)訓(xùn)練過程來找到合適的Latent Vector,無法在需要相對(duì)實(shí)時(shí)的領(lǐng)域應(yīng)用
到這里弓千,AutoDecoder的具體合適的應(yīng)用場(chǎng)景衡便,可謂呼之即出了。
不過最后我想說一個(gè)自己的疑惑洋访,不知道大家怎么看待:
將單個(gè)模型的所有采樣數(shù)據(jù)作為一個(gè)Epoch而非一整個(gè)Input Vector去獲取Latent Vector的這一操作镣陕,是否因?yàn)樯窠?jīng)網(wǎng)絡(luò)的Independence Assumption而丟失了鄰接節(jié)點(diǎn)的互相影響呢?
鄙人不才姻政,沒想到足夠的支持或者否定呆抑,還希望大家討論點(diǎn)撥