我們能比卷積神經網絡做得更好嗎

我們能比卷積神經網絡做得更好嗎

英國機器視覺會議(BMVC)大約兩週前在英國卡迪夫結束,是計算機視覺和模式識別領域的頂級會議之一,具有28%的競爭接受率。與其他人相比,這是一個小活動,所以你有足夠的時間在會議上走來走去,和論文講述者一對一的交流,我覺得這大有裨益。

我在會議上展示了一份關於分層多圖網絡圖像分類的工作,在林曉、穆罕默德·艾默爾(主頁)和我的博士顧問格雷厄姆·泰勒的監督下,我在SRI國際公司實習期間主要在上面工作。

在本文中,我們基本上試圖回答以下問題:“我們能比卷積神經網絡做得更好嗎?”。 在這裡,我討論這個問題,並通過結果支持我的論點。 我還將引導您使用PyTorch從PASCAL VOC 2012的整個管道前進,獲取單個圖像。

這篇文章的完整代碼在我的https://github.com/bknyaz/bmvc_2019上。應該很容易地對其進行調整,以對整個PASCAL數據集進行訓練和驗證。

那麼,為什麼我們要比ConvNets做得更好?他們在許多任務上都勝過人類嗎?

例如,您可以說圖像分類是解決的任務。 好吧,就ImageNet而言,是的。 但是,儘管ImageNet做出了巨大貢獻,但這是一個奇怪的任務。 您為什麼要區分數百種狗? 因此,結果是我們成功地建立了模型,但是無法區分稍微旋轉的狗和貓。 幸運的是,我們現在有了ImageNet-C和其他類似的基準,表明我們離解決它還很遙遠。

我們能比卷積神經網絡做得更好嗎

相關任務(例如對象檢測)中出現的另一個未解決的問題是在非常大的圖像(例如4000×3000)上進行訓練,例如Katharopoulos&Fleuret(ICML,2019)和Ramapuram等人解決了這個問題。 (BMVC,2019)。 多虧了後者,我現在知道如果海報的背景是黑色的,那麼很有可能來自Apple。 我也應該保留一些顏色!

因此,也許我們需要不同於卷積神經網絡的東西?也許我們應該從一開始就使用具有更好屬性的模型,而不是不斷修補其錯誤.

我們認為這種模型可以是圖神經網絡(GNN):一種可以從圖結構數據中學習的神經網絡。 GNN具有一些吸引人的屬性。 例如,與ConvNets相比,GNN本質上是旋轉和平移不變的,因為在圖形中根本沒有旋轉或平移的概念,即沒有左右,在某種意義上只有“鄰居”(Khasanova和Frossard, ICML,2017)。 因此,人們多年來一直試圖解決的使ConvNet更好地推廣到不同的輪換的問題可以通過GNN自動解決!

關於從大圖像中學習,如何從圖像中提取超像素並將低維輸入輸入到GNN而不是將下采樣(例如224×224)圖像輸入到ConvNet? 與雙線性插值相比,超像素似乎是對圖像進行下采樣的一種更好的方法,因為超像素通常通過保持對象之間的邊界來保留很多語義。 藉助ConvNet,我們無法直接從這種輸入中學習,但是,有一些很好的提議建議利用它們(Kwak等人,AAAI,2017)。

因此,GNN聽起來很棒!讓我們看看它在實踐中的表現。

但是不好了!基於(Kipf&Welling,ICLR,2017)的基準GNN在PASCAL上僅達到19.2%(平均平均精度或mAP),而在每一層中具有相同數量的層和濾波器的ConvNet為32.7%

我們提出了一些改進措施,最終擊敗了ConvNet!

1. 層次圖

在ConvNets中,圖像的層次結構是通過池化層隱式建模的。 在GNN中,您至少可以通過兩種方式實現這一目標。 首先,您可以使用類似於ConvNets的池化方法,但是對於圖而言,定義一種快速且良好的池化方法確實具有挑戰性。 相反,我們可以計算多個比例的超像素,並通過將它們與較大的父超像素對應來合併超像素。 但是,由於某些原因,這種合併在我們的案例中效果不佳(我仍然認為效果很好)。 因此,我們改為在輸入級別對層次結構建模。 特別是,我們將所有比例的超像素組合成一個集合,並基於語義分割中常用的基於聯合的交集(IoU)計算層次關係。

我們能比卷積神經網絡做得更好嗎

基於該原理,我在下面的代碼中構建了層次圖。 我還構建了空間圖的多尺度版本,但它僅編碼空間關係,而IoU應該更好地編碼分層關係。 例如,使用IoU,我們可以在遠程子節點之間創建快捷方式,即連接兩個空間上相距較遠但屬於同一父節點(例如汽車)的小超像素(例如車輪),如上圖所示。

實際上,層次圖將mAP提升到31.7%,使其比ConvNet僅低1%,而可訓練參數卻減少了4倍!如果僅使用空間多尺度圖,則結果將比本文中探討的要差得多。

def compute_iou_binary(seg1, seg2):
inters = float(np.count_nonzero(seg1 & seg2))
#區域可以預先計算
seg1_area = float(np.count_nonzero(seg1))
seg2_area = float(np.count_nonzero(seg2))
return inters / (seg1_area + seg2_area - inters)

def hierarchical_graph(masks_multiscale, n_sp_actual, knn_graph=32):
n_sp_total = np.sum(n_sp_actual)
A = np.zeros((n_sp_total, n_sp_total))
for level1, masks1 in enumerate(masks_multiscale):
for level2, masks2 in enumerate(masks_multiscale[level1+1:]):
for i, mask1 in enumerate(masks1):
for j, mask2 in enumerate(masks2):
A[np.sum(n_sp_actual[:level1], dtype=np.int) + i,
np.sum(n_sp_actual[:level2+level1+1], dtype=np.int) + j] = compute_iou_binary(mask1, mask2)
sparsify_graph(A, knn_graph)
return A + A.T

n_sp_actual = []
avg_values_multiscale, coord_multiscale, masks_multiscale = [], [], []

# Scales [1000, 300, 150, 75, 21, 7] ]在論文中
for i, (name, sp) in enumerate(zip(['children', 'parents', 'grandparents'], [1000, 300, 21])):
superpixels = slic(img, n_segments=sp)

n_sp_actual.append(len(np.unique(superpixels)))
avg_values_, coord_, masks_ = superpixel_features(img, superpixels)
avg_values_multiscale.append(avg_values_)
coord_multiscale.append(coord_)
masks_multiscale.append(masks_)

A_spatial_multiscale = spatial_graph(np.concatenate(coord_multiscale), img.shape[:2], knn_graph=knn_graph)
A_hier = hierarchical_graph(masks_multiscale, n_sp_actual, knn_graph=None)

很棒!我們還可以做些什麼來進一步改善結果?

2.易學的關係

到目前為止,如果我們可視化濾波器,它們將看起來非常原始(就像高斯一樣)。 有關更多詳細信息,請參見我的https://medium.com/@BorisAKnyazev/tutorial-on-graph-neural-networks-for-computer-vision-and-beyond-part-1-3d9fada3b80d。 我們想學習一些類似於ConvNets的邊緣檢測器,因為效果很好。 但是事實證明,使用GNN來學習它們非常困難。 為此,我們基本上需要根據座標之間的差異在超像素之間生成邊緣。 這樣,我們將使GNN能夠理解座標系(旋轉,平移)。 我們將使用在PyTorch中定義的2層神經網絡,如下所示:

pred_edge = nn.Sequential(nn.Linear(2, 32),
nn.ReLU(True),
nn.Linear(32, L))

其中L是預測邊數或濾波器數,例如下面的圖表中的4.我們限制濾波器僅根據 |(x₁,y₁) - (x₂,y₂)|之間的絕對差而不是原始值來學習邊緣,從而使濾波器變得對稱。 這限制了濾波器的容量,但是它仍然比我們的基準GCN使用的簡單高斯濾波器好得多。

在我的https://github.com/bknyaz/bmvc_2019/blob/master/bmvc_2019.ipynb中,我創建了一個LearnableGraph類,該類實現了在給定節點座標(或任何其他特徵)和空間圖的情況下預測邊緣的邏輯。 後者用於在每個節點周圍定義一個小的局部鄰域,以避免預測所有可能的節點對的邊緣,因為它昂貴且連接非常遠的超像素沒有多大意義。

下面,我將訓練有素的prededge函數可視化。 為此,我假設在其中應用卷積的索引為1的當前節點位於座標系(x₁,y₁)= 0的中心。 然後,我簡單地採樣其他節點的座標(x₂,y₂),並將其輸入給prededge。 顏色顯示邊緣的強度取決於與中心節點的距離。

我們能比卷積神經網絡做得更好嗎

學習到的圖也非常強大,但是計算量較大,如果我們生成非常稀疏的圖,則可以忽略不計。 32.3%的結果僅比ConvNet低0.4%,如果我們生成更多的過濾器,則可以輕鬆地改善它!

3. 多尺度GNN

現在,我們有了三個圖:空間圖,層次圖和學習圖。 具有空間或層次圖的單個圖卷積層僅允許特徵在“第一鄰居”內傳播。 在我們的例子中,鄰居是軟性定義的,因為我們使用高斯來定義層次圖的空間圖和IoU。 (Defferrard等。 NIPS(2016)提)出了一種多尺度(multihop)圖卷積算法,該算法將K-hop鄰域內的特徵聚合在一起並近似譜圖卷積。 有關此方法的詳細說明,請參見我的https://towardsdatascience.com/tutorial-on-graph-neural-networks-for-computer-vision-and-beyond-part-2-be6d71d70f49。 對於我們的空間圖,它實質上對應於使用多個不同寬度的高斯。 對於分層圖,我們可以通過這種方式在遠程子節點之間創建K-hop快捷方式。 對於學習的圖,此方法將創建可視化的學習過濾器的多個比例。

使用多尺度圖卷積,在我的GraphLayerMultiscale類中實現,結果證明是非常重要的,它使我們的性能比基準卷積神經網絡高出0.3%!

4. 以低成本改善關係類型的融合

到目前為止,為了從我們的三個圖中學習,我們使用了標準的級聯方法。 但是,這種方法有兩個問題。 首先,這種融合算子的可訓練參數的數量是線性的。 輸入和輸出要素的維數,比例(K)和關係類型的數量,因此,如果我們一次增加兩個或多個這些參數,它的確會快速增長。 其次,我們嘗試融合的關係類型可以具有非常不同的性質,並佔據流形的非常不同的子空間。 為了同時解決這兩個問題,我們提出了類似於(Knyazev等人,NeurIPS-W,2018)的可學習的預測。 通過這種方式,我們將線性相關性解耦,與串聯相比,參數數量減少了2-3倍。 此外,可學習的投影變換了多關係特徵,因此它們應占據流形的附近子空間,從而促進信息從一種關係傳播到另一種關係。

我們能比卷積神經網絡做得更好嗎

通過使用在下面的GraphLayerFusion類中實現的擬議融合方法,我們將ConvNet擊敗了達到了34.5%提升1.8%,而參數卻減少了2倍! 對於最初對圖像的空間結構一無所知的模型,除了以超像素編碼的信息外,還給人留下了深刻的印象。 探索其他融合方法(例如這種方法)以獲得更好的結果將很有趣。

class GraphLayerFusion(GraphLayerMultiscale):
def __init__(self,
in_features,
out_features,
K,
fusion='pc',
n_hidden=64,
bnorm=True,
activation=nn.ReLU(True),
n_relations=1):
super(GraphLayerFusion, self).__init__(in_features, out_features, K, bnorm, activation, n_relations)
self.fusion = fusion
if self.fusion == 'cp':
fc = [nn.Linear(in_features * K * n_relations, n_hidden),
nn.ReLU(True),
nn.Linear(n_hidden, out_features)]
else:
if self.fusion == 'pc':
fc = [nn.Linear(n_hidden * n_relations, out_features)]
elif self.fusion == 'sum':
fc = [nn.Linear(n_hidden, out_features)]
else:
raise NotImplementedError('cp, pc or sum is expected. Use GraphLayer for the baseline concatenation fusion')
self.proj = nn.ModuleList([nn.Sequential(nn.Linear(in_features * K, n_hidden), nn.Tanh())
for rel in range(n_relations)]) # projection layers followed by nonlinearity
if bnorm:
fc.append(BatchNorm1d_GNN(out_features))
if activation is not None:
fc.append(activation)
self.fc = nn.Sequential(*fc)

def relation_fusion(self, x, A):
B, N = x.shape[:2]
for rel in range(self.n_relations):
y = self.chebyshev_basis(A[:, :, :, rel], x, self.K).view(B, N, -1) # B,N,K,C

if self.fusion in ['pc', 'sum']:
y = self.proj[rel](y) # projection
if self.fusion == 'sum':
y_out = y if rel == 0 else y_out + y
continue
# for CP and PC
if rel == 0:
y_out = []
y_out.append(y)

y = self.fc(y_out if self.fusion == 'sum' else (torch.cat(y_out, 2))) # B,N,F
return y

結語

事實證明,有了多關係圖網絡和一些技巧,我們可以比卷積神經網絡做得更好!不幸的是,在改進GNN的過程中,我們逐漸失去了不變性。 例如,旋轉圖像後,超像素的形狀可能會發生變化,而我們用於節點特徵以改善模型的超像素座標也使其健壯性降低。。儘管如此,我們的工作只是邁向更好的圖像推理模型的一小步,並且我們證明了GNN可以為一個有希望的方向鋪平道路。有關實現的詳細信息,請參閱我在Github上的https://github.com/bknyaz/bmvc_2019。我還高度推薦Matthias Fey的碩士論文,其中包含與非常相關的主題相關的https://github.com/rusty1s/embedded_gcnn


分享到:


相關文章: