深度學習原理與PyTorch實戰(zhàn)(下篇)_第1頁
深度學習原理與PyTorch實戰(zhàn)(下篇)_第2頁
深度學習原理與PyTorch實戰(zhàn)(下篇)_第3頁
深度學習原理與PyTorch實戰(zhàn)(下篇)_第4頁
深度學習原理與PyTorch實戰(zhàn)(下篇)_第5頁
已閱讀5頁,還剩261頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

深度學習原理與PyTorch實戰(zhàn)(下篇)目錄\h第8章人工智能造假術——圖像生成與對抗學習\h8.1反卷積與圖像生成\h8.1.1卷積神經(jīng)網(wǎng)絡回顧\h8.1.2反卷積運算\h8.1.3反池化過程\h8.1.4反卷積與分數(shù)步伐\h8.1.5輸出圖像尺寸公式\h8.1.6批正則化技術\h8.2圖像生成實驗1——最小均方誤差模型\h8.2.1模型思路\h8.2.2代碼實現(xiàn)\h8.2.3運行結果\h8.3圖像生成實驗2——生成器—識別器模型\h8.3.1生成器—識別器模型的實現(xiàn)\h8.3.2對抗樣本\h8.4圖像生成實驗3——GAN\h8.4.1GAN的總體架構\h8.4.2程序實現(xiàn)\h8.4.3結果展示\h8.5小結\h8.6Q&A\h8.7擴展閱讀\h第9章詞匯的星空——神經(jīng)語言模型與Word2Vec\h9.1詞向量技術介紹\h9.1.1初識詞向量\h9.1.2傳統(tǒng)編碼方式\h9.2NPLM:神經(jīng)概率語言模型\h9.2.1NPLM的基本思想\h9.2.2NPLM的運作過程詳解\h9.2.3讀取NPLM中的詞向量\h9.2.4NPLM的編碼實現(xiàn)\h9.2.5運行結果\h9.2.6NPLM的總結與局限\h9.3Word2Vec\h9.3.1CBOW模型和Skip-gram模型的結構\h9.3.2層次歸一化指數(shù)函數(shù)\h9.3.3負采樣\h9.3.4總結及分析\h9.4Word2Vec的應用\h9.4.1在自己的語料庫上訓練Word2Vec詞向量\h9.4.2調用現(xiàn)成的詞向量\h9.4.3女人-男人=皇后-國王\h9.4.4使用向量的空間位置進行詞對詞翻譯\h9.4.5Word2Vec小結\h9.5小結\h9.6Q&A\h第10章深度網(wǎng)絡LSTM作曲機——序列生成模型\h10.1序列生成問題\h10.2RNN與LSTM\h10.2.1RNN\h10.2.2LSTM\h10.3簡單01序列的學習問題\h10.3.1RNN的序列學習\h10.3.2LSTM的序列學習\h10.4LSTM作曲機\h10.4.1MIDI文件\h10.4.2數(shù)據(jù)準備\h10.4.3模型結構\h10.4.4代碼實現(xiàn)\h10.5小結\h10.6Q&A\h10.7擴展閱讀\h第11章神經(jīng)機器翻譯機——端到端機器翻譯\h11.1機器翻譯簡介\h11.1.1基于規(guī)則的機器翻譯技術\h11.1.2統(tǒng)計機器翻譯\h11.1.3神經(jīng)機器翻譯\h11.1.4關于Zero-shot翻譯\h11.2編碼—解碼模型\h11.2.1編碼—解碼模型總體架構\h11.2.2編碼器\h11.2.3解碼器\h11.2.4損失函數(shù)\h11.2.5編碼—解碼模型歸納\h11.2.6編碼—解碼模型的效果\h11.3注意力機制\h11.3.1神經(jīng)機器翻譯中的注意力\h11.3.2注意力網(wǎng)絡\h11.4更多改進\h11.4.1GRU的結構\h11.4.2雙向GRU的應用\h11.5神經(jīng)機器翻譯機的編碼實現(xiàn)\h11.5.1神經(jīng)網(wǎng)絡的構建\h11.5.2神經(jīng)網(wǎng)絡的訓練\h11.5.3測試神經(jīng)機器翻譯機\h11.5.4結果展示\h11.6更多改進\h11.6.1集束搜索算法\h11.6.2BLEU:對翻譯結果的評估方法\h11.6.3對編碼—解碼模型的改進\h11.7廣義的翻譯\h11.7.1廣義翻譯機\h11.7.2廣義翻譯的應用場景\h11.8Q&A\h第12章更強的機器翻譯模型——Transformer\h12.1Transformer概述\h12.1.1編碼—解碼模型回顧\h12.1.2Transformer全景概覽\h12.1.3神奇的自注意力\h12.2Atoken旅行記\h12.2.1奇怪的序號牌\h12.2.2分身之門\h12.2.3新朋友\h12.3Transformer部件詳解\h12.3.1詞嵌入與位置嵌入\h12.3.2自注意力模塊計算詳解\h12.3.3自注意力層的矩陣計算\h12.3.4殘差連接與層歸一化\h12.3.5逐點計算的前向網(wǎng)絡層\h12.3.6解碼器中的自注意力\h12.3.7解碼器的輸出層\h12.4動手訓練一個Transformer翻譯模型\h12.4.1翻譯模型中輸入單位的粒度\h12.4.2模型定義\h12.4.3模型訓練\h12.4.4Transformer相關開源庫\h12.5小結\h第13章學習跨任務的語言知識——預訓練語言模型\h13.1語言模型簡要回顧\h13.2預訓練Transformer詳解\h13.2.1深入了解GPT\h13.2.2深入了解BERT\h13.2.3模型微調\h13.2.4模型表現(xiàn)\h13.3單句分類:BERT句子分類實戰(zhàn)\h13.4后BERT時代\h13.5小結\h第14章人體姿態(tài)識別——圖網(wǎng)絡模型\h14.1圖網(wǎng)絡及圖論基礎\h14.1.1圖的基本概念\h14.1.2什么是圖網(wǎng)絡\h14.1.3圖網(wǎng)絡的基本任務和應用場景\h14.2圖卷積網(wǎng)絡\h14.2.1GCN的工作原理\h14.2.2打開GCN的黑箱\h14.2.3從社團劃分任務來理解GCN\h14.3實戰(zhàn):使用GCN識別人體姿態(tài)\h14.3.1數(shù)據(jù)來源與預處理\h14.3.2代碼實現(xiàn)\h14.4小結\h第15章AI游戲高手——深度強化學習\h15.1強化學習簡介\h15.1.1強化學習的要素\h15.1.2強化學習的應用場景\h15.1.3強化學習的分類\h15.2深度Q學習算法\h15.2.1Q學習算法\h15.2.2DQN算法\h15.2.3DQN在雅達利游戲上的表現(xiàn)\h15.3DQN玩FlappyBird的PyTorch實現(xiàn)\h15.3.1FlappyBird的PyGame實現(xiàn)\h15.3.2DQN的PyTorch實現(xiàn)\h15.4小結\h15.5通用人工智能還有多遠\h15.6Q&A第8章人工智能造假術——圖像生成與對抗學習通過前面幾章的學習,我們已經(jīng)領略了深度學習技術的強大,特別是卷積神經(jīng)網(wǎng)絡是如何在圖像識別、遷移學習、圖像風格遷移等任務上一展身手的。近年來,深度學習的關注焦點又轉移到了生成模型上。生成模型是指由神經(jīng)網(wǎng)絡再造出圖像、文本或音頻。我們當然希望機器偽造的圖像、文本和聲音越逼真越好,因此,我們稱之為:人工智能造假術。人工智能造假術在最近幾年發(fā)展迅猛。下面我們通過一些例子,看看目前的人工智能造假已經(jīng)發(fā)展到了什么程度。我們知道,給真實圖像打上馬賽克很容易,還原卻很難。然而生成模型卻可以完成這件“不可能”的事。圖8.1所示就是從馬賽克圖像恢復原始圖像的一個例子。圖8.1針對人臉的超分辨率技術(圖片來源:ShizhanZhu,SifeiLiu,ChenChangeLoy,etal.DeepCascadedBi-NetworkforFaceHallucination,2017.)雖然與真實的圖像相比還存在差異,但這種效果已經(jīng)相當清晰了。這種技術叫作超分辨率重建,即將分辨率很低的圖轉變?yōu)榉直媛矢叩膱D。另外,計算機還可以自動補全殘缺的圖像,如圖8.2所示。圖8.2將缺失的部分圖像補全(圖片來源:ChaoYang,XinLu,ZheLin,etal.High-ResolutionImageInpaintingusingMulti-ScaleNeuralPatchSynthesis,2017.)這種曾出現(xiàn)在科幻片中的技術目前已經(jīng)實現(xiàn),經(jīng)過大量圖像的訓練,圖像生成神經(jīng)網(wǎng)絡就可以做到這一點。FaceApp是另一款由俄羅斯人開發(fā)的應用,開發(fā)者將各種各樣的變臉技術全部安置到這款手機應用中,給用戶帶來了無窮的樂趣。如圖8.3所示,它可以將一張無表情的臉變成很自然的笑臉,也可以讓你提前看到衰老的樣子,甚至可以讓你過一把返老還童的癮,還能夠為你虛擬化妝,讓你瞬間變換一種風格。圖8.3FaceApp的變臉游戲(圖片來自FaceApp)而看到下面這項技術,不知道各位漫畫設計師是該高興還是該為自己的職業(yè)前途而擔憂呢。通過PainterChainer這款技術,人工智能可以自動給漫畫上色,而且顏色的配置是由人類設計師引導的,如圖8.4所示。圖8.4給二次元畫作自動上色(圖片來自petalicapaint網(wǎng)站)下面這項“絕技”似乎更有用途,這就是根據(jù)文字描述生成相關的圖像,如圖8.5所示。圖8.5根據(jù)文字生成符合描述的圖像(圖片來源:ScottReed,ZeynepAkata,XinchenYan,etal.GenerativeAdversarialTexttoImageSynthesis.arXiv:1605.05396v2.)下面的圖像都是根據(jù)上面的文字描述生成的。未來我們只需要描述想要的圖,計算機就能夠自動生成這樣的圖像。所有這些應用全部用到了一項關鍵性技術:圖像生成。這一任務可以描述為:當我們給機器輸入一個向量(可以代表一串文字、一張圖像或一個標簽)時,機器就能輸出一張符合我們要求的圖像。本章我們就來詳細講解圖像生成技術。首先,我們會介紹基于反卷積神經(jīng)網(wǎng)絡的圖像生成技術;其次,會引入GAN(generativeadversialnetwork,生成對抗網(wǎng)絡)。GAN在最近幾年發(fā)展非常迅猛,它不僅能夠在實際的圖像生成中大放異彩,而且已經(jīng)形成了一種新型的機器學習方式。在理論介紹完畢之后,我們會以生成逼真的手寫數(shù)字圖像為任務,詳細介紹如何動手搭建一個圖像生成系統(tǒng)。從基于均方誤差的生成器,到生成器—識別器,再到生成器—判別器,我們會一步步帶領讀者走向成功,并解決一系列實際問題。8.1反卷積與圖像生成事實上,生成模型在深度學習中具有相當漫長的發(fā)展歷史。早在2006年,辛頓在《科學》雜志上提出的第一個深度神經(jīng)網(wǎng)絡就是生成模型。受限玻爾茲曼機(RestrictedBoltzmannMachine)、自動編碼器(AutoEncoder)、變分自編碼器(VariationalAutoEncoder)、反卷積神經(jīng)網(wǎng)絡(De-convolutionalNeuralNetwork)等都是生成模型,都可以用在圖像生成任務上。本章我們主要介紹一種特殊的生成模型——基于反卷積的圖像生成技術。之所以重點介紹這個模型,是因為它是專為圖像生成而打造的,而且與我們反復介紹的卷積神經(jīng)網(wǎng)絡密切相關——反卷積技術可以看作卷積技術的鏡像。如圖8.6所示,總體來看,基于卷積神經(jīng)網(wǎng)絡的識別和預測模型是一個從大尺度圖像逐漸變換到小尺度圖像,最后到一個標簽的數(shù)據(jù)加工過程;而反卷積神經(jīng)網(wǎng)絡生成模型是從一個向量到小尺度圖像,再逐漸轉化成大尺度圖像的過程。二者剛好形成了鏡面對稱的結構。事實上,這種鏡像關系可以更加具體而微妙,我們可以給每個卷積運算找到鏡像的反卷積運算。圖8.6卷積神經(jīng)網(wǎng)絡與反卷積神經(jīng)網(wǎng)絡架構示意圖(圖片來源:HyeonwooNoh,SeunghoonHong,BohyungHan.LearningDeconvolutionNetworkforSemanticSegmentation,2015.)8.1.1卷積神經(jīng)網(wǎng)絡回顧在介紹反卷積神經(jīng)網(wǎng)絡之前,我們先來簡單回顧一下卷積神經(jīng)網(wǎng)絡的架構和計算過程。卷積神經(jīng)網(wǎng)絡是具有特殊結構的前饋神經(jīng)網(wǎng)絡,輸入一張圖像,輸出一個分類標簽或一組預測數(shù)值。如圖8.7所示,每一個立方體是一系列神經(jīng)元排列成立方體的形狀,每一個切片是一個特征圖,每一個特征圖對應一個卷積核,而每一個卷積核可以對上一層的輸入進行卷積運算。圖8.7執(zhí)行手寫數(shù)字識別任務的卷積神經(jīng)網(wǎng)絡網(wǎng)絡交替進行兩種操作:卷積和池化。卷積相當于用一系列不同的模板去匹配圖像中的不同區(qū)域,從而抽取出模式。池化相當于對原始輸入進行大尺度的抽象和簡化,從而使圖像越來越小,以便得到更大尺度的信息。在整個卷積神經(jīng)網(wǎng)絡的計算過程中,最重要的運算莫過于卷積運算了。它是將一個卷積核與輸入圖像上的所有區(qū)域進行乘積加和的運算過程。如圖8.8所示,左邊圖像是原始輸入,中間的黑白方塊就是某一個卷積層對應的卷積核,卷積核的尺寸是固定的(在實驗前就已確定),而卷積核中的每一個元素的具體數(shù)值是在實驗中通過反向傳播算法不斷調節(jié)、計算出來的(一開始只要進行隨機初始化即可)。圖8.8卷積核與特征圖在計算的過程中,卷積核會從左到右、從上到下依次滑動所有與其大小相等的圖像小塊區(qū)域,對每一個小塊做乘積與加和運算,并將運算結果放置到右側,最后得到的特征圖就是一步卷積運算的輸出。8.1.2反卷積運算反卷積(deconvolution)運算可以看作卷積運算的鏡像,每一個卷積運算都有一個對應的反卷積運算。我們以圖8.9所示的卷積核為例,看看它所對應的反卷積應該如何運算。首先,需要搞清楚,反卷積運算與卷積運算一樣,它的輸入輸出一般也都是圖像。只不過,在卷積運算中,輸出圖像通常會比輸入圖像小,而在反卷積運算中,輸出圖像通常會比輸入圖像大。如何得到這個新的輸出圖像呢?圖8.9卷積核的矩陣表示第一步,將卷積運算的卷積核轉換為反卷積運算的卷積核,它是如圖8.10所示的矩陣。圖8.10圖8.9對應的反卷積核注意觀察就會發(fā)現(xiàn),這個反卷積的卷積核,相對于原卷積核來說是進行了“上下顛倒、左右翻轉”的操作。更一般地,只要卷積核是一個方形的矩陣,我們就需要沿著矩陣的水平中心軸上下翻轉,然后再沿著矩陣的豎直對稱軸左右翻轉,如圖8.11所示。圖8.11卷積核的翻轉以得到反卷積核第二步,將反卷積對應的輸入圖像用0在兩邊補齊成更大的圖像,使得用反卷積的卷積核作用到這張補齊的圖像后,得到的輸出圖像是一張與卷積運算的輸入圖像同等大小的圖像。我們知道,對于卷積運算來說,輸入圖像尺寸為,卷積核窗口大小為,往外對稱地填補個空白格,輸出就是大小的圖像。所以,要想輸出一張大小的圖像,而輸入圖像大小是,那么我們的填補大小就應該為。例如,在這個例子中,由于反卷積的輸入圖像是3×3大小,輸出是5×5大小,那么就需要在四周向外補充兩排全0的元素。第三步,用反卷積的卷積核與填充后的輸入圖像做卷積,得到反卷積核的卷積運算結果。計算過程如圖8.12所示。圖8.12反卷積運算結果示例值得注意的是,反卷積并不是卷積的逆運算,即如果把卷積的結果再輸入反卷積,并不能得到與輸入圖像一模一樣的圖像。然而,值得肯定的是,反卷積運算也是一種卷積運算,只不過它通常能夠通過補齊元素的方式對一張小的輸入圖像進行卷積得到一張大的圖像,這就是它的特別之處。事實上,在反卷積神經(jīng)網(wǎng)絡的訓練和運算過程中,我們就是把反卷積當作卷積來看待的,只不過卷積往往能夠讓圖像越變越小,而反卷積可以讓圖像越變越大。讀者讀到這里可能會覺得很奇怪,既然反卷積就是一種卷積,只不過它會把圖越變越大,那么,為什么我們還要單獨提出“反卷積”操作呢?從卷積的卷積核到反卷積的卷積核為何用如此怪異的手段來轉化?這要追溯到神經(jīng)網(wǎng)絡訓練的基本方法(即反向傳播算法)才能找到答案。事實上,反卷積的定義是根據(jù)反向傳播算法給出的。我們知道,與神經(jīng)網(wǎng)絡的前饋過程類似,誤差反傳過程就是將誤差值(或者叫梯度值)沿著網(wǎng)絡反向傳播。其中,每一時刻每一個神經(jīng)元上都會有一個誤差值。那么,在卷積神經(jīng)網(wǎng)絡中,既然所有的特征圖上排布的都是神經(jīng)元,它們自然都有自己的誤差,那么這些誤差就會組成一張圖像,我們稱之為誤差圖。于是:·在反傳誤差時,需要從層將誤差反傳回第層,從而得到第層的誤差值;·可以從數(shù)學上證明,在反傳誤差時,第層的誤差圖就是用這層的卷積核的翻轉(即反卷積的卷積核)在第層的誤差圖上做卷積得到的。這個從高層的誤差圖計算得到低層誤差圖的運算就叫作反卷積運算。8.1.3反池化過程討論完卷積之后,下面我們來考慮池化。在反卷積神經(jīng)網(wǎng)絡中,是不是也要定義一種反池化運算呢?答案是肯定的。反池化有許多方法,下面介紹的是其中一種。在介紹這個方法之前,我們首先要擴展一下卷積運算,為其增加一個叫步伐(striding)的參數(shù)。通過這個參數(shù)可以廢除卷積神經(jīng)網(wǎng)絡中的池化運算,因為卷積加池化的整體效果非常接近步伐大于1的卷積效果。步伐參數(shù)就是卷積核在輸入圖像上滑動做卷積的過程中,每一步所跳躍的格點數(shù)量。在一般的卷積運算中,這個間隔是1,即每做完一次內積運算,窗口就會往右或往下平移一格。但是,當步伐為2的時候,卷積核窗口就會每間隔2個像素移動一步,就相當于卷積加池化了,如圖8.13所示。圖8.13步伐為2時的卷積運算[圖片來自GitHub上的conv_arithmetic(vdumoulin)]顯然,步伐越大,卷積運算的每一步跳躍就越大,得到的輸出特征圖就會越小,而且中間被跳躍過的那些像素直接被忽略了,如圖8.14所示。一般而言,假如輸入圖像尺寸是,卷積窗口大小是,填充數(shù)為,步伐為,那么輸出的特征圖大小為,其中中括號表示向下取整。圖8.14步伐大于1的卷積效果等同于卷積加池化所以,當單純使用步伐大于1的卷積運算時,就可以達到卷積加池化的效果了。于是,我們便可以利用這種方式把一層卷積和池化合并為一個卷積運算。這種合并不僅簡化了流程,而且還使反卷積的定義更直接了。8.1.4反卷積與分數(shù)步伐前面講過,每一個卷積運算都可以對應一個反卷積運算,我們只需要將卷積核進行上下、左右翻轉就可以得到反卷積對應的卷積核了。那么,當我們把步伐考慮進來后,步伐大于1這種情形又應該怎樣做反卷積呢?既然當步伐大于1的時候卷積會忽略一些格點,那么在做反卷積的時候,我們就要填補一些格點,這樣就可以與卷積的過程形成鏡像了。這種通過填補格點做反卷積的過程稱為分數(shù)步伐(fractionalstriding)。如果卷積的步伐是2,那么相應的反卷積就被定義為步伐為1/2的卷積。具體應該怎么做呢?如圖8.15所示,假如我們要對一張2×2的圖像做1/2步長的卷積運算,那么需要進行以下操作?!ぴ谌我?個格點之間插入1個空白:這里體現(xiàn)了分數(shù)步伐(一般地,當卷積的步伐為的時候,任意兩個像素之間加入個空白格);·在擴充的圖上做卷積,此時卷積的步伐striding值為1。圖8.15步伐大于1時的反卷積運算[圖片來自GitHub上的conv_arithmetic(vdumoulin)]之后便可以完成反卷積了。所以,反卷積神經(jīng)網(wǎng)絡是徹頭徹尾全部是卷積的網(wǎng)絡。我們還需要注意一點細節(jié):在PyTorch中,反卷積運算是要調用nn.ConvTranspose2d()函數(shù)的。當我們執(zhí)行1/2步伐的卷積運算的時候,應將步伐值仍設為整數(shù)值,即nn.ConvTranspose2d()對應的參數(shù)仍設為striding=2。這樣,我們就同時完成了反卷積和反池化的過程。8.1.5輸出圖像尺寸公式借助卷積和反卷積,我們可以將圖像進行各種尺度的變換。在設計卷積神經(jīng)網(wǎng)絡或反卷積神經(jīng)網(wǎng)絡的時候,我們不必關心卷積和反卷積如何計算,只需要關心每一步輸入輸出的張量大?。ㄌ貏e是輸入和輸出圖像的尺寸)就可以了。實際上,這些圖像的尺寸早已被這些卷積、反卷積運算中的參數(shù)所決定了。所以,了解卷積、反卷積中各種參數(shù)與輸出圖像尺寸關系十分重要。下面給出卷積和反卷積參數(shù)與輸出圖像尺寸之間關系的數(shù)學表達式。首先來看卷積運算,假設輸入圖像的尺寸為的方形圖像,卷積核窗口大小為,卷積間隔為,四周填充方格個數(shù)為,那么,一次卷積運算得到的輸出圖像大小就是:再來看反卷積運算。假設輸入圖像的尺寸為,卷積核窗口大小為,卷積間隔為1/,輸入端四周填充方格個數(shù)為,輸出端圖像四周填充方格個數(shù)為,那么,在這張圖上進行反卷積得到的圖像大小就是:根據(jù)這兩個公式以及每一次卷積或反卷積運算的輸出圖像的尺寸,我們就可以反推出每一步卷積或反卷積的參數(shù)是多少。了解了每一次反卷積的操作,我們就很容易擴展到多個反卷積層,把它們按照一定的次序拼接起來,就可以做成一個圖像生成器,如圖8.16所示。圖8.16多層反卷積神經(jīng)網(wǎng)絡最左側的輸入為一個100維的隨機向量,把這個向量輸入到一層層的反卷積神經(jīng)網(wǎng)絡中,就能從無到有地生成一張圖像了。8.1.6批正則化技術生成問題比識別問題難度大得多,因為它要“無中生有”地產生符合要求的豐富信息。在實際操作過程中,生成網(wǎng)絡非常難以訓練。人們開發(fā)了很多方法來解決這一問題。其中,批正則化(batchnormalization,通常簡稱為BatchNorm)就是一個常用的方法。我們知道,對于一個深度網(wǎng)絡來說,反復迭代很容易讓輸出結果發(fā)生很大的數(shù)值漂變,產生比較大的方差。特別是在生成網(wǎng)絡中,由于網(wǎng)絡本身就是在放大信息,所以很容易讓輸出數(shù)值產生大的漂變。而通過在每一層網(wǎng)絡加入一個BatchNorm操作,就能將這一層的輸出值限定在給定的范圍內,從而避免其波動過大。BatchNorm的工作原理很簡單,如圖8.17所示。圖8.17BatchNorm原理示意圖它是加在每一層神經(jīng)網(wǎng)絡非線性激活函數(shù)之前的一層運算節(jié)點,所進行的運算就是將輸入的數(shù)值進行歸一化處理,然后映射到我們想要的范圍,即:其中,、是學習的參數(shù),它們會在反向傳播算法執(zhí)行時自動更新,和則分別是輸入數(shù)值在一個批次中的均值和方差,即:這里是一個批次的大小。所以,無論輸入數(shù)據(jù)的變化范圍有多么大,BatchNorm操作后的輸出結果始終會局限在這個范圍內,而、又是可以學習的,因此既限定了神經(jīng)網(wǎng)絡輸出數(shù)值的變化范圍,又不失靈活性。在實踐中,人們發(fā)現(xiàn)在生成器中加入BatchNorm操作的效果非常明顯。有時候,如果沒有BatchNorm,生成的圖像會漆黑一片,但是加上以后會立即顯示出正常的圖像。8.2圖像生成實驗1——最小均方誤差模型在講解了反卷積原理之后,我們用它來實現(xiàn)一個真正的圖像生成器。這個實驗的任務是輸出一張逼真的手寫數(shù)字圖像。要完成這個任務并非看上去那么簡單。接下來,我們將陸續(xù)介紹一系列方法,每一種方法都會比前一種方法更好,也會遇到新的問題,最終我們將通過GAN方法來實現(xiàn)一個手寫數(shù)字圖像生成器。8.2.1模型思路我們首先想到的做法是搭建一個反卷積神經(jīng)網(wǎng)絡,它接受的輸入信息是一個單獨的數(shù)字,輸出則是一張這個數(shù)字所對應的手寫數(shù)字圖像。我們搭建的網(wǎng)絡的整體架構如圖8.18所示。圖8.18手寫數(shù)字生成的最小平方誤差模型數(shù)字輸入網(wǎng)絡后首先被擴充為一個100維的向量(每個維度都是同一個數(shù)字),之后經(jīng)過第一層反卷積的作用變成一個尺寸為(128,5,5)的張量,之后是尺寸為(64,13,13)的張量,最后是一個單通道的28×28大小的圖像。當我們訓練這個網(wǎng)絡的時候,需要給它成對的數(shù)字標簽和對應的手寫數(shù)字圖像,只不過標簽作為輸入,圖像作為輸出提供監(jiān)督信息。損失函數(shù)可以直接用生成圖像與真實圖像之間的差異來衡量,最直接的差異函數(shù)就是每個像素點色彩值的均方誤差,因此我們稱這個模型為最小均方誤差模型。8.2.2代碼實現(xiàn)接下來,我們用PyTorch來實現(xiàn)這個思路。首先,導入所有要用到的包:#導入需要的包,請保證torchvision已經(jīng)在環(huán)境中安裝好

#在Windows中需要單獨安裝torchvision包,在命令行運行pipinstalltorchvision即可

importtorch

importtorch.nnasnn

importtorch.optimasoptim

importtorch.nn.functionalasF

importtorchvision.datasetsasdsets

importtorchvision.transformsastransforms

importtorchvision.utilsasvutil

importmatplotlib.pyplotasplt

importnumpyasnp

importos

%matplotlibinline

接下來,定義一些全局變量,并加載訓練模型所需要的數(shù)據(jù):#定義超參數(shù)

image_size=28#輸出圖像尺寸

input_dim=100#輸入給生成器的向量維度,增加維度可以提高生成器輸出樣本的多樣性

num_channels=1#圖像通道數(shù)

num_features=64#生成器中的卷積核數(shù)量

batch_size=64#批次大小

#如果系統(tǒng)中有GPU,則用GPU完成張量的計算

use_cuda=torch.cuda.is_available()#定義一個布爾型變量,標志當前的GPU是否可用

#如果當前GPU可用,則優(yōu)先在GPU上進行張量計算

dtype=torch.cuda.FloatTensorifuse_cudaelsetorch.FloatTensor

itype=torch.cuda.LongTensorifuse_cudaelsetorch.LongTensor

#加載MINIST數(shù)據(jù),如果沒有下載過,就會在當前路徑下新建/data子目錄,并把文件存放其中

#MNIST數(shù)據(jù)是torchvision包自帶的,可以直接調用

#在調用自己的數(shù)據(jù)時,可以用torchvision.datasets.ImageFolder或者

#torch.utils.data.TensorDataset來加載

train_dataset=dsets.MNIST(root='./data',#文件存放路徑

train=True,#提取訓練集

#將圖像轉化為張量,在加載數(shù)據(jù)時就可以對圖像做預處理

transform=transforms.ToTensor(),

download=True)#當找不到文件的時候,自動下載

#加載測試集

test_dataset=dsets.MNIST(root='./data',

train=False,

transform=transforms.ToTensor())

#訓練集的加載器,自動將數(shù)據(jù)分割成batch,順序隨機打亂

train_loader=torch.utils.data.DataLoader(dataset=train_dataset,

batch_size=batch_size,

shuffle=True)

'''我們希望將測試數(shù)據(jù)分成兩部分,一部分作為校驗數(shù)據(jù),一部分作為測試數(shù)據(jù)。

校驗數(shù)據(jù)用于檢測模型是否過擬合,并調整參數(shù),測試數(shù)據(jù)用于檢驗整個模型的工作'''

#首先定義下標數(shù)組indices,它相當于對所有test_dataset中數(shù)據(jù)的編碼

#然后定義下標indices_val表示校驗數(shù)據(jù)的下標,indices_test表示測試數(shù)據(jù)的下標

indices=range(len(test_dataset))

indices_val=indices[:5000]

indices_test=indices[5000:]

#根據(jù)下標構造兩個數(shù)據(jù)集的SubsetRandomSampler采樣器,它會對下標進行采樣

sampler_val=torch.utils.data.sampler.SubsetRandomSampler(indices_val)

sampler_test=torch.utils.data.sampler.SubsetRandomSampler(indices_test)

#根據(jù)兩個采樣器定義加載器

#注意將sampler_val和sampler_test分別賦值給validation_loader和test_loader

validation_loader=torch.utils.data.DataLoader(dataset=test_dataset,

batch_size=batch_size,

sampler=sampler_val

)

test_loader=torch.utils.data.DataLoader(dataset=test_dataset,

batch_size=batch_size,

sampler=sampler_test

)

之后,定義新的網(wǎng)絡類來實現(xiàn)反卷積的功能:classModelG(nn.Module):

def__init__(self):

super(ModelG,self).__init__()

self.model=nn.Sequential()#model為一個內嵌的序列化的神經(jīng)網(wǎng)絡模型

#利用add_module增加一個反卷積層,輸入為input_dim維,輸出為2*num_features維

#窗口大小為5,padding=0

#輸入圖像大小為1,輸出圖像大小為W'=(W-1)S-2P+K+P'=(1-1)*2-2*0+5+0=3,5*5

self.model.add_module('deconv1',nn.ConvTranspose2d(input_dim,num_features*2,5,2,0,bias=False))

#增加一個batchnorm層

self.model.add_module('bnorm1',nn.BatchNorm2d(num_features*2))

#增加非線性層

self.model.add_module('relu1',nn.ReLU(True))

#增加第二層反卷積層,輸入為2*num_features維,輸出為_features維,窗口大小為5,padding=0

#輸入圖像大小為5,輸出圖像大小為W'=(W-1)S-2P+K+P'=(5-1)*2-2*0+5+0=13,13*13

self.model.add_module('deconv2',nn.ConvTranspose2d(num_features*2,num_features,5,2,0,

bias=False))

#增加一個batchnorm層

self.model.add_module('bnorm2',nn.BatchNorm2d(num_features))

#增加非線性層

self.model.add_module('relu2',nn.ReLU(True))

#增加第二層反卷積層,輸入為2*num_features維,輸出為num_features維,窗口大小為4,padding=0

#輸入圖像大小為13,輸出圖像大小為W'=(W-1)S-2P+K+P'=(13-1)*2-2*0+4+0=28,28*28

self.model.add_module('deconv3',nn.ConvTranspose2d(num_features,num_channels,4,2,0,bias=False))

self.model.add_module('sigmoid',nn.Sigmoid())

defforward(self,input):

output=input

#遍歷網(wǎng)絡的所有層,一層層輸出信息

forname,moduleind_children():

output=module(output)

#輸出一張28像素×28像素的圖像

returnoutput

defweight_init(m):

#模型參數(shù)初始化

#默認的初始化參數(shù)卷積核的權重均值大約是0,方差在10^{-2}左右

#BatchNorm層的權重均值大約是0.5,方差在0.2左右

#使用如下初始化方式可以讓方差更小,收斂更快

class_name=m.__class__.__name__

ifclass_name.find('conv')!=-1:

m.weight.data.normal_(0,0.02)

ifclass_name.find('norm')!=-1:

m.weight.data.normal_(1.0,0.02)

defmake_show(img):

#將張量變成可以顯示的圖像

img=img.data.expand(batch_size,3,image_size,image_size)

returnimg

defimshow(inp,title=None):

#在屏幕上繪制圖像

"""ImshowforTensor."""

ifinp.size()[0]>1:

inp=inp.numpy().transpose((1,2,0))

else:

inp=inp[0].numpy()

mvalue=np.amin(inp)

maxvalue=np.amax(inp)

ifmaxvalue>mvalue:

inp=(inp-mvalue)/(maxvalue-mvalue)

plt.imshow(inp)

iftitleisnotNone:

plt.title(title)

plt.pause(0.001)#稍微暫停,打印圖像

值得注意的是,我們可以調用PyTorch自帶的函數(shù)nn.ConvTranspose2d()來完成反卷積的操作。另外,在forward()方法中,我們并未顯式編碼每一層運算,而是直接循環(huán)遍歷當前神經(jīng)網(wǎng)絡中所有的子模塊,并利用module(output)的方式自動執(zhí)行這些子模塊,將結果保存到output這個張量中。這一操作展示了PyTorch在拼裝神經(jīng)網(wǎng)絡運算組件過程中的靈活性。此外,保證權重在開始時的多樣與豐富,也有利于生成圖像的多樣化。最后,我們用下面的代碼來進行訓練:#訓練模型

print('Initialized!')

#定義生成器模型

net=ModelG()

#加載到GPU

net=net.cuda()ifuse_cudaelsenet

#目標函數(shù)采用最小均方誤差

criterion=nn.MSELoss()

#定義優(yōu)化器

optimizer=optim.SGD(net.parameters(),lr=0.0001,momentum=0.9)

#隨機選擇生成0~9的數(shù)字,用于每個周期打印并查看結果

samples=np.random.choice(10,batch_size)

samples=torch.from_numpy(samples).type(dtype)

#開始訓練

step=0#計數(shù)經(jīng)歷了多少時間步

num_epochs=100#總的訓練周期

record=[]

forepochinrange(num_epochs):

train_loss=[]

#加載數(shù)據(jù)批次

#注意數(shù)據(jù)中的data(圖像)轉化為了要預測的target,

#數(shù)據(jù)中的target(標簽)則轉化成了輸入網(wǎng)絡的數(shù)據(jù)

forbatch_idx,(data,target)inenumerate(train_loader):

#data為一批圖像,target為一批標簽

target,data=data.clone().detach().requires_grad_(True),target.clone().detach()

#將數(shù)據(jù)加載到GPU中

ifuse_cuda:

target,data=target.cuda(),data.cuda()

#將輸入的數(shù)字標簽轉化為生成器net能夠接受的(batch_size,input_dim,1,1)維張量

data=data.type(dtype)

data=data.resize(data.size()[0],1,1,1)

data=data.expand(data.size()[0],input_dim,1,1)

#給網(wǎng)絡模型做標記,標志模型正在訓練集上訓練

#這種區(qū)分主要是為了打開/關閉net的training標志

net.train()

output=net(data)#神經(jīng)網(wǎng)絡完成一次前饋的計算過程,得到預測輸出output

loss=criterion(output,target)#將output與標簽target比較,計算誤差

optimizer.zero_grad()#清空梯度

loss.backward()#反向傳播

optimizer.step()#一步隨機梯度下降算法

step+=1

#記錄損失函數(shù)值

ifuse_cuda:

loss=loss.cpu()

train_loss.append(loss.data.numpy())

ifstep%100==0:#每隔100個batch執(zhí)行一次打印操作

net.eval()#給網(wǎng)絡模型做標記,標志模型在校驗集上運行

val_loss=[]#記錄校驗集上的準確率的容器

#開始在校驗集上進行循環(huán),計算校驗集上的準確率

idx=0

for(data,target)invalidation_loader:

target,data=data.clone().detach().requires_grad_(True),target.clone().detach()

idx+=1

ifuse_cuda:

target,data=target.cuda(),data.cuda()

data=data.type(dtype)

data=data.resize(data.size()[0],1,1,1)

data=data.expand(data.size()[0],input_dim,1,1)

output=net(data)#完成一次前饋計算過程,得到訓練后的模型net在校驗集上的表現(xiàn)

loss=criterion(output,target)#比較output與標簽target,計算誤差

ifuse_cuda:

loss=loss.cpu()

val_loss.append(loss.data.numpy())

#打印誤差等數(shù)值,其中準確率為本訓練周期epoch開始后到目前批的準確率的平均值

print('訓練周期:{}[{}/{}({:.0f}%)]\t,訓練數(shù)據(jù)Loss:{:.6f}\t,校驗數(shù)據(jù)Loss:{:.6f}'.format(

epoch,batch_idx*batch_size,len(train_loader.dataset),

100.*batch_idx/len(train_loader),np.mean(train_loss),np.mean(val_loss)))

record.append([np.mean(train_loss),np.mean(val_loss)])

#產生一組圖像,保存到temp1文件夾(需要事先建立),檢測生成器當前的效果

#改變輸入數(shù)字圖像的尺寸,適應于生成器網(wǎng)絡

withtorch.no_grad():

samples.resize_(batch_size,1,1,1)

samples=samples.data.expand(batch_size,input_dim,1,1)

samples=samples.cuda()ifuse_cudaelsesamples#加載到GPU

fake_u=net(samples)#用原始網(wǎng)絡作為輸入,得到偽造的圖像數(shù)據(jù)

fake_u=fake_u.cpu()ifuse_cudaelsefake_u

img=make_show(fake_u)#將張量轉化成可繪制的圖像

os.makedirs('temp1',exist_ok=True)

vutil.save_image(img,'temp1/fake%s.png'%(epoch))#保存生成的圖像

8.2.3運行結果接下來,我們看看這個模型的運行結果。訓練誤差曲線如圖8.19所示。圖8.19圖像生成器最小均方誤差模型的訓練曲線我們訓練了100輪(epoch),兩條誤差曲線都在下降,說明模型一直都在學習新東西。校驗曲線一直在測試曲線下面,說明模型一直處于欠擬合狀態(tài)。如果持續(xù)不斷地向它輸入數(shù)據(jù),它還能持續(xù)地下降,但是下降得已經(jīng)越來越不明顯了。而當我們實際看輸出圖像時,卻發(fā)現(xiàn)生成結果很不樂觀,如圖8.20所示。圖8.20最小均方誤差模型的生成結果每張圖像的上方是輸入網(wǎng)絡的真實數(shù)字,我們看到除了1、3、9等數(shù)字以外,其他圖像都很模糊。為什么會這樣呢?不難發(fā)現(xiàn),最小化均方誤差會讓模型在每一個輸入數(shù)字下學習到一個平均的手寫數(shù)字圖像。由于同一個數(shù)字的手寫數(shù)字圖像可能差異很大,這就導致學出來的平均數(shù)字非常模糊。而且,即便是同一個數(shù)字的兩張不同的手寫數(shù)字圖像,在經(jīng)過均方誤差的計算后,也會存在相當大的誤差。也許等待更長的訓練時間,數(shù)字可能更清晰,但我們想嘗試更高效的設計。8.3圖像生成實驗2——生成器—識別器模型既然我們之前實現(xiàn)過一個效果相當不錯的手寫數(shù)字識別器,那么能不能通過使識別器成為正確數(shù)字的判斷標準,來改善MSE損失函數(shù)導致的過于平均的情況?也就是將生成器的結果輸入識別器,讓它來矯正生成器。我們的新網(wǎng)絡架構如圖8.21所示。圖8.21生成器—識別器網(wǎng)絡模型我們將識別器最后的交叉熵損失函數(shù)放進來,既訓練識別器,又訓練生成器。使用PyTorch的動態(tài)計算圖就可以非常方便地操作。8.3.1生成器—識別器模型的實現(xiàn)生成器代碼沒有任何變化,識別器的代碼在第5章已經(jīng)給出,因此這里就不再詳細展示了。為了減少訓練周期,我們使用了固定值遷移學習技術,即將訓練好的手寫數(shù)字識別器的網(wǎng)絡直接遷移過來。從文件中加載識別器的代碼如下:#定義待遷移的網(wǎng)絡框架,所有的神經(jīng)網(wǎng)絡模塊包括:Conv2d、MaxPool2d,

#Linear等模塊不需要重新定義,會自動加載

#但是網(wǎng)絡的forward功能沒有辦法自動實現(xiàn),需要重寫

#一般地,加載網(wǎng)絡只加載網(wǎng)絡的屬性,不加載方法

depth=[4,8]

classConvNet(nn.Module):

def__init__(self):

super(ConvNet,self).__init__()

defforward(self,x):

x=F.relu(self.conv1(x))

x=self.pool(x)

x=F.relu(self.conv2(x))

x=self.pool(x)

#將三維的張量全部轉換成一維的張量

x=x.view(-1,image_size//4*image_size//4*depth[1])

x=F.relu(self.fc1(x))#全連接,激活函數(shù)

x=F.dropout(x,training=self.training)#默認以0.5的概率對該層進行dropout

x=self.fc2(x)#全連接,激活函數(shù)

x=F.log_softmax(x)#log_softmax可以理解為概率對數(shù)值

returnx

defretrieve_features(self,x):

#該函數(shù)專門用于提取卷積神經(jīng)網(wǎng)絡的特征圖,

#返回feature_map1、feature_map2為前兩層卷積層的特征圖

feature_map1=F.relu(self.conv1(x))#完成第一層卷積

x=self.pool(feature_map1)#完成第一層池化

feature_map2=F.relu(self.conv2(x))#第二層卷積,兩層特征圖都存儲到了feature_map1,

#feature_map2中

return(feature_map1,feature_map2)

defrightness(predictions,labels):

"""計算預測錯誤率的函數(shù),其中predictions是模型給出的一組預測結果,batch_size行num_classes列的矩陣,labels是數(shù)據(jù)之中的正確答案"""

pred=torch.max(predictions.data,1)[1]#對于任意一行(一個樣本)的輸出值的第1個維度求最大,

#得到每一行的最大元素的下標

rights=pred.eq(labels.data.view_as(pred)).sum()#將下標與labels中包含的類別進行比較,

#并累計得到比較正確的數(shù)量

returnrights,len(labels)#返回正確的數(shù)量和這一次比較的元素數(shù)量

netR=torch.load('minst_conv_checkpoint')#讀取硬盤上的minst_conv_checkpoint文件

netR=netR.cuda()ifuse_cudaelsenetR#加載到GPU中

forparainnetR.parameters():

para.requires_grad=False#將識別器的權重設置為固定值

新的訓練代碼如下:#開始訓練

print('Initialized!')

netG=ModelG()#新建一個生成器

netG=netG.cuda()ifuse_cudaelsenetG#加載到GPU中

netG.apply(weight_init)#初始化參數(shù)

criterion=nn.CrossEntropyLoss()#用交叉熵作為損失函數(shù)

optimizer=optim.SGD(netG.parameters(),lr=0.001,momentum=0.9)#定義優(yōu)化器

#隨機選擇batch_size個數(shù)字,用來生成數(shù)字圖像

samples=np.random.choice(10,batch_size)

samples=torch.from_numpy(samples).type(dtype)

num_epochs=100#總訓練周期

statistics=[]#數(shù)據(jù)記載器

forepochinrange(num_epochs):

train_loss=[]

train_rights=[]

#加載數(shù)據(jù)

forbatch_idx,(data,target)inenumerate(train_loader):

#注意圖像和標簽互換了

#data為一批標簽,target為一批圖像

target,data=data.clone().detach().requires_grad_(True),target.clone().detach()

ifuse_cuda:

target,data=target.cuda(),data.cuda()

#復制標簽變量放到label中

label=data.clone()

data=data.type(dtype)

#改變張量大小以適應生成器網(wǎng)絡

data=data.resize(data.size()[0],1,1,1)

data=data.expand(data.size()[0],input_dim,1,1)

netG.train()#給網(wǎng)絡模型做標記,標識模型正在訓練集上訓練

netR.train()

output1=netG(data)#神經(jīng)網(wǎng)絡完成一次前饋的計算過程,得到預測輸出output

output=netR(output1)#用識別器網(wǎng)絡來做分類

loss=criterion(output,label)#將output與標簽target比較,計算誤差

optimizer.zero_grad()#清空梯度

loss.backward()#反向傳播

optimizer.step()#一步隨機梯度下降算法

step+=1

ifuse_cuda:

loss=loss.cpu()

train_loss.append(loss.data.numpy())

right=rightness(output,label)#計算準確率所需數(shù)值,返回數(shù)值為(正確樣例數(shù),總樣本數(shù))

train_rights.append(right)#將計算結果裝到列表容器train_rights中

ifstep%100==0:#每間隔100個batch執(zhí)行一次打印操作

netG.eval()#給網(wǎng)絡模型做標記,標識模型正在校驗集上運行

netR.eval()

val_loss=[]#記錄校驗集上的準確率的容器

val_rights=[]

#開始在校驗集上進行循環(huán),計算校驗集上的準確率

for(data,target)invalidation_loader:

#注意target是圖像,data是標簽

target,data=data.clone().detach().requires_grad_(True),target.clone().detach()

ifuse_cuda:

target,data=target.cuda(),data.cuda()

label=data.clone()

data=data.type(dtype)

#改變張量大小以適應生成器網(wǎng)絡

data=data.resize(data.size()[0],1,1,1)

data=data.expand(data.size()[0],input_dim,1,1)

output1=netG(data)#神經(jīng)網(wǎng)絡完成一次前饋的計算過程,得到預測輸出output

output=netR(output1)#利用識別器來識別

loss=criterion(output,label)#將output與標簽target比較,計算誤差

ifuse_cuda:

loss=loss.cpu()

val_loss.append(loss.data.numpy())

#計算準確率所需數(shù)值,返回數(shù)值為(正確樣例數(shù),總樣本數(shù))

right=rightness(output,label)

val_rights.append(right)

#分別計算使用過的測試集,以及全部校驗集上模型的表現(xiàn):分類準確率

#train_r為一個二元組,分別記錄經(jīng)歷過的所有訓練集中分類正確的數(shù)量和該集合中總的樣本數(shù)

#train_r[0]/train_r[1]是訓練集上的分類準確率,val_r[0]/val_r[1]是校驗集上的分類準確率

train_r=(sum([tup[0]fortupintrain_rights]),sum([tup[1]fortupintrain_rights]))

#val_r為一個二元組,分別記錄校驗集中分類正確的數(shù)量和該集合中總的樣本數(shù)

val_r=(sum([tup[0]fortupinval_rights]),sum([tup[1]fortupinval_rights]))

print(('訓練周期:{}[{}/{}({:.0f}%)]\t,訓練數(shù)據(jù)Loss:{:.6f},準確率:{:.2f}%\t,'

'校驗數(shù)據(jù)Loss:'+'{:.6f},準確率:{:.2f}%').format(epoch,batch_idx*batch_size,

len(train_loader.dataset),

100.*batch_idx/len(train_loader),

np.mean(train_loss),

100.*train_r[0]/train_r[1],

np.mean(val_loss),

100.*val_r[0]/val_r[1]))

#記錄中間的數(shù)據(jù)

statistics.append({'loss':np.mean(train_loss),'train':100.*train_r[0]/train_r[1],

'valid':100.*val_r[0]/val_r[1]})

#產生一組圖像并保存到temp1文件夾(需要事先建立),檢測生成器當前的效果

withtorch.no_grad():

samples.resize_(batch_size,1,1,1)

samples=samples.data.expand(batch_size,input_dim,1,1)

samples=samples.cuda()ifuse_cudaelseinputs

fake_u=netG(samples)

fake_u=fake_u.cpu()ifuse_cudaelsefake_u

img=make_show(fake_u)

os.makedirs('temp1',exist_ok=True)

vutil.save_image(img,'temp1/fake%s.png'%(epoch))

這里的關鍵代碼在于output1=netG(data)和output=netR(output1)這兩句,也就是將生成器的結果交給了識別器,識別器最后給出預測的數(shù)字。使用如此簡單的方式就可以將兩個神經(jīng)網(wǎng)絡首尾連接起來,這都是拜PyTorch的動態(tài)計算圖所賜。那么結果如何呢?8.3.2對抗樣本我們先來看看訓練Loss的情況,如圖8.22所示。圖8.22生成器—識別器的識別曲線可以看到,不管是訓練集還是校驗集,錯誤率都降到了相當?shù)偷乃剑踔劣袝r可以達到0%。接下來再看看生成圖像的結果,如圖8.23所示。圖8.23生成器—識別器的生成圖像結果結果簡直“慘不忍睹”!數(shù)字3~9幾乎都在不同程度上糊成了一團,只有0和2勉強像回事兒,顯然這并不是我們想要的結果。那么這個生成器—識別器的問題出在哪里?這樣的圖像是如何被準確率高達100%的識別器判別為正常數(shù)字的呢?為了檢驗程序,我們將這些雜亂無章的數(shù)字重新輸入識別器中,一個看似不可思議的事實是,這樣糊成一團的圖像經(jīng)過識別器的分類計算后,真的能被識別成為正常的手寫數(shù)字,而且還與輸入的標簽一模一樣!看來,恰恰是識別器誤把生成器產生的模糊圖像判斷為正確的數(shù)字,從而導致傳遞給生成器的誤差變小甚至變沒了,因此生成器的效果很差。要想弄明白識別器為什么會犯這樣的錯誤,我們首先看看圖像被輸進識別器后,經(jīng)過卷積和池化后的特征圖是什么樣的。圖8.24是輸進識別器后的第一層特征圖對比。上面一排是生成器生成圖像輸入識別器中產生的特征圖(最左邊的圖為生成器偽造的9,輸入給了識別器),下面一排是正常的數(shù)字圖像輸入識別器產生的特征圖(最左邊的圖為真實的手寫數(shù)字9,輸入給了識別器)。圖8.24輸入對抗樣本和正常樣本后的第一層特征圖可以看到,兩張圖像的第一層特征圖區(qū)別非常大,下面一排都是肉眼可辨的清晰的數(shù)字,而上面一排則是雜亂無章的一團。接下來我們看第二層特征圖。由于第二層有8個卷積核,所以有8個特征圖,我們分別把偽造的9和真實的9所對應的特征圖都畫出來,如圖8.25所示。圖8.25輸入對抗樣本和正常樣本后的第二層特征圖意外的事情發(fā)生了,在第二層的8張?zhí)卣鲌D中,居然有很多部分非常相似。這就不難理解為什么生成器生成的圖像會令識別器混淆了,因為識別器是根據(jù)第二層特征圖做出最后判斷的,生成器生成的圖像騙過了識別器的“眼睛”。一張是混亂不堪的偽造圖像,一張是清晰的手寫數(shù)字圖像,兩張圖區(qū)別如此之大,為什么在輸入同樣的識別器后竟然會在第二層特征圖中得到相似的結果呢?事實上,導致識別器產生某個數(shù)字(如9)的輸出所對應的輸入圖像不止一張,而是對應了非常大的空間。然而,在我們用標準的手寫數(shù)字圖像進行訓練的時候,實際上只關注了一類比較靠近手寫數(shù)字圖像的區(qū)域

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論