text to image(八):《Image Generation from Scene Graphs》
最近在翻閱文本生成圖像的相關工作,目前比較新的有突破性的工作是李飛飛工作團隊18年cvpr發表的《Image Generation from Scene Graphs》 。
?????? 論文地址:https://arxiv.org/abs/1804.01622
?????? 源碼地址: https://github.com/google/sg2im
?????? 這篇主要就是介紹該論文的工作,穿插對部分代碼的理解和講解。看完代碼以后拜服Justin Johnson大神,真厲害!
?
一、相關工作
?????? 先前已經有了很多文本生成圖像的方法,比較具有代表性的是StackGAN和StackGAN++(會在其它博客中給出介紹)。StackGAN存在的比較突出的問題是不能處理比較復雜的文本。比如句子為:A sheep by another sheep standing on the grass with sky above and a boat in the ocean by a tree behind the sheep.
?????? ?結果如下圖所示:
? ? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
左圖是StackGAN生成的結果,右圖是李飛飛小組提出的新方法的結果。右邊的效果明顯更好。
?
二、基本思想
?????不同于先前的方法,李飛飛小組提出可以使用場景圖作為中間媒介。即由原本的
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?文本----->圖像(也就是RNN+GAN的直接搭配)??
? ? ??轉化為
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 文本--→場景圖--→圖像。
? ? ??首先的問題是:什么是場景圖,場景圖怎么得到?
? ? ??場景圖是一種可以用來表示文本或者圖像結構的表述。如圖二所示,即為圖一所對應的文本的場景圖。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?????? 可以看到,場景圖將場景表示為有向圖,其中節點(紅色)是對象,邊(藍色)給出對象之間的關系。
? ? ? ?關于場景圖的獲取:一些數據集中提供了與圖像配套的場景圖,例如Visual Genome數據集。大部分場景圖的工作都是基于此數據集的。第二種方法是使用句子或者圖像直接生成場景圖。從句子中生成的場景圖的工作有:《Generating Semantically Precise Scene Graphs from Textual Descriptions for Improved Image Retrieval》。從圖像中生成場景圖的工作有:《Visual relationship detection with language priors》 、 《Pixels to graphs by associative embedding》 、《Scene graph generation by iterative message passing》、《On support relations and semantic scene graphs》。這里暫時不做詳細的介紹。
?????? ?獲得場景圖之后,后續的處理如下圖所示:
? ? ? ?首先將場景圖(Scene graph)輸入圖卷積網絡(Graph Convolution)獲得對象的嵌入向量(Object features)(嵌入向量是什么,后續有解釋)。獲得的嵌入向量輸入對象布局網絡。對象布局網絡預測對象的bounding boxes和segmentation masks。得到scene layout(場景布局)。將其輸入級聯細化網絡(Cascaded Refinement Network)得到最后的輸出圖像。
? ? ? ? 每個模塊的具體實現會在后面配合代碼進行介紹。這里給出嵌入向量的概念:我們使用不同的向量來表示不同的對象(詞匯),最直接的做法是使用one-hot的形式,但是當詞庫特別大,詞向量會十分冗長。而且兩個關系很接近的詞匯對應的向量(比如 “國王”和“王后”)之間的關系并不能通過one-hot的詞向量表示出來。
? ? ? ? 所以引入嵌入向量(詞嵌入)來表示不同對象。首先,它也是一個向量,但是長度比較短,而且是一個密集向量。其次,它可以表示關系相近的對象之間的關系(比如“國王”-“男人”+“女人”約等于“王后” ) 這種關系是通過距離表現出來的。比如“貓”和“狗”向量之間的距離比較近,但是“貓”和“天空”距離就比較遠。
三、數據集介紹
?????? 在介紹具體的模型結構前,我們先介紹一下實驗中用到的數據集。源碼中可選的數據集有兩個:Visual Genome數據集和COCO數據集。
? ? ?Visual Genome 數據集包括 7 個主要部分:
? ? ? 區域描述 --------對圖像的不同區域進行描述。這些區域是由邊框坐標限定的,區域之間允許有重復。數據集中平均對每一張圖片有 42 種區域描述。每一個描述都是一個短語包含著從 1 到 16 單詞長度,以描述這個區域。
? ? ? ?對象---------------平均每張圖片包含21個物體,每個物體周圍有一個邊框。
? ? ? 屬性---------------平均每張圖片有16個屬性。一個物體可以有0個或是更多的屬性。屬性可以是顏色(比如yellow),狀態(比如standing),等等。
? ? ? ?關系----------------“關系”將兩個物體關聯到一起。
? ? ? ?區域圖-------------將從區域描述中提取的物體、屬性、以及關系結合在一起。表述該區域
? ? ? ?場景圖-------------一整幅圖片中所有的物體、屬性、以及關系的表示
? ? ? 問答對-------------每張圖片都有兩類問答:基于整張圖片的隨意問答(freeform QAs),以及基于選定區域的區域問答(region-based QAs)。在本實驗中用不到,所以不做過多解釋。
? ? ? ? 此外,每一個對象、屬性、關系在WordNet中都有自己規范化的ID。
COCO的數據標注信息包括:
- 類別標志
- 類別數量區分
- 像素級的分割
?
? ? ? ?2014年版本的數據為例,一共有20G左右的圖片和500M左右的標簽文件。標簽文件標記了每個segmentation+bounding box的精確坐標,其精度均為小數點后兩位。一個目標的標簽示意如下:
{"segmentation":[[392.87, 275.77, 402.24, 284.2, 382.54, 342.36, 375.99, 356.43, 372.23, 357.37, 372.23, 397.7, 383.48, 419.27,407.87, 439.91, 427.57, 389.25, 447.26, 346.11, 447.26, 328.29, 468.84, 290.77,472.59, 266.38], [429.44,465.23, 453.83, 473.67, 636.73, 474.61, 636.73, 392.07, 571.07, 364.88, 546.69,363.0]], "area": 28458.996150000003, "iscrowd": 0,"image_id": 503837, "bbox": [372.23, 266.38, 264.5,208.23], "category_id": 4, "id": 151109}
?
?
四、模型結構
? ? ? ?依舊先上這張整體結構圖。;另外,下文提到的向量具體數值均是為了便于理解,并不完全與代碼運行后的實際數值相同。
圖卷積網絡(GCN)
? ? ? ?由Thomas Kpif于2017年在論文《Semi-supervised Classification with Graph Convolutional Networks》。Thomas Kpif的這篇論文屬于譜卷積,即將卷積網絡的濾波器與圖信號同時搬移到傅里葉域以后進行處理。
? ? ? ? 原論文沒看太懂,數學推導可以參https://blog.csdn.net/chensi1995/article/details/77232019值得注意的是,圖嵌入(graph embedding)、網絡嵌入(network embedding)、網絡表示學習(network representation learning),這三個概念從原理上來說其實表達的是同一件事,核心思想就是“通過深度學習技術將圖中的節點(或邊)映射為向量空間中的點,進而可以對向量空間中的點進行聚類、分類等處理”。圖卷積神經網絡就屬于圖嵌入技術的一種。
?????? 也就是說,圖卷積網絡的目的是把對象(節點)、關系(邊)映射為嵌入向量。
?????? 具體原理可以參考https://blog.csdn.net/tMb8Z9Vdm66wH68VX1/article/details/78705916。 為方便后續理解,這里給出部分內容的截圖。
Hl即為第l個隱含層,H0就是輸入層。
?
?
可以看到GCN可以有一到多層。
?
下面分析源碼:
? ? ? 首先讀取場景圖,在代碼中場景圖的表示是一個由若干字典組成的列表。一幅圖像可能有一到多個場景圖表述。
? ? ? 字典有兩個key: 'objects' 和'relationships' 。這里給出一個scene graph:{'objects': ['sky', 'grass', 'zebra'], 'relationships': [[0, 'above', 1], [2, 'standing on', 1]]}。對于擁有多個字典(場景圖)的列表(圖像),在列表中從前到后字典逐漸變復雜,對象數目和關系數目增多。在relationship的列表中,目標用索引表示。一幅圖像中有N個目標,則在所有的字典中,目標的索引從0到N-1。
? ? ? ?場景圖的處理是encode_scene_graphs函數。函數輸入是單個場景圖的字典或者包含了多個字典的列表,輸出是元組(objs, triples, obj_to_img)。之前講到在VG數據集中,所有的對象、屬性、關系都有規范化的索引,我們暫且稱之為詞庫索引。objs, triples, obj_to_img都是 列表,objs表示所有所有場景圖中出現的所有目標的詞庫索引,triples表示所有場景圖中每一張場景圖中目標之間的關系,obj_to_img與objs長度相同,用于標注哪些目標屬于哪些場景圖。比如:
? ? ?? objs:[11, 17, 130, 0, 11, 17, 129, 0]??????? 最后用0來代表'__image__'
? ? ?? triples:[[0, 4, 1], [2, 19, 1], [0, 0, 3], [1, 0, 3], [2, 0, 3], [4, 4, 5]]
? ? ? ?obj_to_img:[0, 0, 0, 0, 1, 1, 1, 1,…..N]?? 總共有N個場景圖
? ? ? ?相關代碼:
def encode_scene_graphs(self, scene_graphs):"""Encode one or more scene graphs using this model's vocabulary. Inputs tothis method are scene graphs represented as dictionaries like the following:{"objects": ["cat", "dog", "sky"],"relationships": [[0, "next to", 1],[0, "beneath", 2],[2, "above", 1],]}This scene graph has three relationshps: cat next to dog, cat beneath sky,and sky above dog.Inputs:- scene_graphs: A dictionary giving a single scene graph, or a list ofdictionaries giving a sequence of scene graphs.Returns a tuple of LongTensors (objs, triples, obj_to_img) that have thesame semantics as self.forward. The returned LongTensors will be on thesame device as the model parameters."""if isinstance(scene_graphs, dict):# We just got a single scene graph, so promote it to a listscene_graphs = [scene_graphs]objs, triples, obj_to_img = [], [], []obj_offset = 0for i, sg in enumerate(scene_graphs):#對于單幅圖像 有好幾個場景圖 每次進一個場景圖# Insert dummy __image__ object and __in_image__ relationshipssg['objects'].append('__image__')image_idx = len(sg['objects']) - 1for j in range(image_idx):sg['relationships'].append([j, '__in_image__', image_idx])#首先是對場景圖進行處理,除了原本的目標以外,加入新的目標 ,也就是總體的'__image__'#加入了新的目標,就需要更新關系列表 也就是 之間的每一個目標 都'__in_image__'在圖像里#舉例:sg['objects']:['sky', 'grass', 'sheep', '__image__']#sg['relationships']: [[0, 'above', 1], [2, 'standing on', 1], [0, #'__in_image__', 3], [1, '__in_image__', 3], [2, '__in_image__', 3]]#0 1 .....len(sg['objects']) - 2 分別是原圖中目標的索引 #image_idx = len(sg['objects']) - 1 是image的索引for obj in sg['objects']: obj_idx = self.vocab['object_name_to_idx'].get(obj, None) #獲取場景圖中目標對應的詞庫索引if obj_idx is None:raise ValueError('Object "%s" not in vocab' % obj)objs.append(obj_idx) #對應的詞庫索引 最后用0來代表'__image__' [11, 17, 130, 0, 11, 17, 129, 0]obj_to_img.append(i) #用于標注 對于當前的圖像 這是第幾個場景圖 [0, 0, 0, 0, 1, 1, 1, 1]for s, p, o in sg['relationships']:pred_idx = self.vocab['pred_name_to_idx'].get(p, None) #獲取關系對應的詞庫索引if pred_idx is None:raise ValueError('Relationship "%s" not in vocab' % p)triples.append([s + obj_offset, pred_idx, o + obj_offset])#描述目標之間的關系 不同場景圖的不同目標 用不同的索引標注#[[0, 4, 1], [2, 19, 1], [0, 0, 3], [1, 0, 3], [2, 0, 3], [4, 4, 5], [6, 19, 5], [4, 0, 7], [5, 0, 7], [6, 0, 7]]obj_offset += len(sg['objects'])device = next(self.parameters()).deviceobjs = torch.tensor(objs, dtype=torch.int64, device=device)#所有場景圖中出現的所有目標的詞庫索引triples = torch.tensor(triples, dtype=torch.int64, device=device) #所有場景圖中每一張場景圖中目標之間的關系obj_to_img = torch.tensor(obj_to_img, dtype=torch.int64, device=device)# 標注哪些目標屬于哪些場景圖return objs, triples, obj_to_img將對象和關系使用128維的詞嵌入表示,將其輸入圖卷積網絡,得到嵌入向量。
相關代碼:
obj_vecs = self.obj_embeddings(objs) #[42,128] 將每個目標用128維的嵌入向量表示 obj_vecs_orig = obj_vecs pred_vecs = self.pred_embeddings(p) #[63,128] 將每種關系也用128維的嵌入向量表示if isinstance(self.gconv, nn.Linear): #如果沒有設計場景圖卷積網絡 而是直接加了全連接層 obj_vecs = self.gconv(obj_vecs) #得到128維度的輸出向量 else:obj_vecs, pred_vecs = self.gconv(obj_vecs, pred_vecs, edges) #如果只有一層if self.gconv_net is not None:obj_vecs, pred_vecs = self.gconv_net(obj_vecs, pred_vecs, edges) #如果有多層 就把剛才那一層的輸出作為輸入#[42,128] [63,128] 這里42是對象的數量 63是關系的數量2、 對象布局網絡
對象布局網絡由兩部分組成,一部分是Mask regression network,一部分是Box regression network,如下圖所示:
?
?
(1)預測對象的bounding_box:
使用一個多層感知器來實現。包含有三層,實驗中輸入層是128的對象嵌入向量,隱藏層的維度取了512,輸出層是4維向量,對每個對象的嵌入向量,得到bouding_box的坐標。
代碼:
boxes_pred = self.box_net(obj_vecs)?? #[42,4]? 對于每個對象 預測它們的bounding_box
??????
(2)預測mask:
?????? 建立一個mask_net來預測mask。網絡的輸入是所有場景圖中所有對象的嵌入向量。例如一共由42個對象,輸入為[42,128,1,1],輸出為16*16的mask_pre[42,1,16,16],也就是每個對象的binary masks。網絡中主要進行了上采樣、卷積等操作。如下列代碼所示:
代碼:
masks_pred = None if self.mask_net is not None:mask_scores = self.mask_net(obj_vecs.view(O, -1, 1, 1))#輸入[42,128,1,1]輸出[42,1,16,16] #對于每個對象產生16*16的圖像 O是對象的總數目 masks_pred = mask_scores.squeeze(1).sigmoid() #[42,16,16]def _build_mask_net(self, num_objs, dim, mask_size): #將128維的圖像變為1維的maskoutput_dim = 1layers, cur_size = [], 1while cur_size < mask_size:layers.append(nn.Upsample(scale_factor=2, mode='nearest'))#上采樣圖像成原來的四倍(長寬2倍)layers.append(nn.BatchNorm2d(dim))layers.append(nn.Conv2d(dim, dim, kernel_size=3, padding=1))layers.append(nn.ReLU())cur_size *= 2if cur_size != mask_size:raise ValueError('Mask size must be a power of 2')layers.append(nn.Conv2d(dim, output_dim, kernel_size=1)) #最后的輸出圖像是單通道的layout return nn.Sequential(*layers)(3)bounding_box和mask結合生成圖像布局
作者設計了masks_to_layout網絡來實現這一功能。輸入是obj_vecs, layout_boxes, layout_masks, obj_to_img以及 想要產生的圖像布局的尺寸H, W。輸出是[N,D,H,W]的形式。
N是要生成的圖像batch大小,H,W是尺寸,D是通道數。具體結構原理沒看太懂。
?????? 如下列代碼所示:
layout = masks_to_layout(obj_vecs, layout_boxes, layout_masks,obj_to_img, H, W)#輸入: obj_vecs[42,128] layout_boxes[42,4] layout_masks[42,16,16]#obj_to_img[42] H:128 W:128 #layout:[7,128,128,128]?
3、級聯網絡
級聯網絡來自論文《Photographic image synthesis with cascaded refinement networks》。在得到layout([7,128,128,128])后,作者引入了layout_noise([7, 32, 128, 128]),與其結合成([7, 160, 128, 128])輸入級聯網絡,得到最終的三通道彩圖。
級聯網絡并沒有依靠生成對抗網絡(GAN)以訓練generator與discriminator network的方式來做image-to-image,而是采用了一種級聯精練網絡Cascaded Refinement Network (CRN)來實現逼真街景圖的生成。每個模塊接收兩個場景布局作為輸入,即下采樣到模塊的輸入分辨率和來自前一個模塊的輸出。這些輸入通道連接并傳遞給一對3*3卷積層; 然后在傳遞到下一個模塊之前使用最近鄰插值對輸出進行上采樣。 第一個模塊采用高斯噪聲z~ pz作為輸入,并且來自最后一個模塊的輸出被傳遞到兩個最終的卷積層以產生輸出圖像。
具體的代碼模塊比較多,這里就不再給出介紹。
代碼:
img = self.refinement_net(layout) #輸入[7, 160, 128, 128] 輸出[7, 3, 128, 128]至此,模型結構部分介紹完畢。
五、訓練
模型使用一對鑒別器網絡和訓練圖像生成網絡f來生成逼真的輸出圖像。 確保生成的圖像的整體外觀是真實的; 確保圖像中的每個對象看起來都是真實的;它的輸入是一個對象的像素,使用雙線性插值裁剪并重新縮放到固定大小。除了將每個對象分類為真實或虛假之外,還確保使用輔助分類器來識別每個對象,該分類器預測對象的類別。
我們聯合訓練圖像生成網絡f、鑒別器和。我們試圖最小化6個損失的加權和。
?
Box loss預測對象的位置信息,懲罰真實對象位置與預測位置的誤差。
Mask loss pixelwise cross-entropy使用像素交叉熵懲罰地面實況和預測掩模之間的差異; 不用于在Visual Genome上訓練的模型。
Pixel loss懲罰GT圖像與生成圖像之間的L1差異。
Image adversarial loss鼓勵生成的圖像整體看起來更真實。
Object adversarial loss 鼓勵生成的對象看起來更真實。
Auxiliarly classifier loss確保每一個對象都能夠被Dobj識別。
?
訓練部分的代碼涉及鑒別器和生成器,6種損失,比較復雜(再次膜拜大神),這里就不給出詳細的代碼理解,只寫一下訓練主函數的基本流程。
?
總結
以上是生活随笔為你收集整理的text to image(八):《Image Generation from Scene Graphs》的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片 360度旋转动画
- 下一篇: 分享一个边看视频就能边练口语的学习网站,