Kuvaajan huomioverkot hupun alla

Lähdesolmu: 769288

kuvaa huomioverkostoja

Graafiset hermoverkot (GNN) ovat nousseet vakiotyökalupakiksi oppiaksesi kaaviotiedoista. GNN: t pystyvät ajamaan parannuksia ongelmiin liittyvillä ongelmilla eri aloilla, kuten sisällön suositteleminen tai huumeiden löytäminen. Toisin kuin muuntyyppiset tiedot, kuten kuvat, oppiminen kaaviotiedoista vaatii erityisiä menetelmiä. Määritelty Michael Bronstein:

[..] nämä menetelmät perustuvat jonkinlaiseen viestin muotoon, joka välittää kaavion, jolloin eri solmut voivat vaihtaa tietoja.

Erityisten tehtävien suorittamiseksi kaavioilla (solmujen luokittelu, linkkien ennustus jne.) GNN-kerros laskee solmun ja reunan esitykset ns. rekursiivinen naapuruston diffuusio (Tai viesti kulkee). Tämän periaatteen mukaan kukin kaaviosolmu vastaanottaa ja yhdistää ominaisuuksia naapureiltaan edustamaan paikallista kuvaajan rakennetta: erityyppiset GNN-kerrokset suorittavat erilaisia ​​aggregaatiostrategioita.

GNN-kerroksen yksinkertaisimmat formulaatiot, kuten Graph Convolutional Networks (GCNs) tai GraphSage, suorittavat isotrooppisen aggregaation, jossa kukin naapuri osallistuu yhtä lailla päivittämään keskussolmun esityksen. Tämä blogiviesti esittelee minisarjan (2 artikkelia), joka on omistettu Graph Attention Networks (GAT) -analyyseille, jotka määrittelevät anisotropiaoperaation rekursiivisessa naapuruusdiffuusiossa. Anisotropia-paradigmaa hyödyntämällä oppimiskykyä parannetaan tarkkailumekanismilla, joka antaa jokaisen naapurin panokselle eri merkityksen.

Jos olet täysin uusi GNN-verkoissa ja niihin liittyvissä käsitteissä, kehotan sinua lukemaan seuraavan esittelyartikkelin: Graafisten hermoverkkojen rakennuspalikoiden ymmärtäminen (intro).

Jos tästä perusteellisesta opetussisällöstä on hyötyä sinulle, tilaa AI-tutkimuksen postituslista hälytys, kun julkaisemme uutta materiaalia. 

GCN vs GAT - Matematiikan lämmittely

Lämmittely perustuu Deep Graph Libraryn ilmoittamiin GAT-yksityiskohtiin verkkosivusto.

Ennen kuin ymmärrämme GAT-kerroksen käyttäytymisen, kerrotaan uudelleen matematiikka GCN-kerroksen suorittaman aggregaation takana.

GCN-kerros - yhdistämistoiminto
  • on solmun yhden hypyn naapureiden joukko i. Tämä solmu voitaisiin myös sisällyttää naapureihin lisäämällä itsesilmukka.
  • on graafin rakenteeseen perustuva normalisointivakio, joka määrittää isotrooppisen keskimääräisen laskennan.
  • σ on aktivaatiofunktio, joka tuo epälineaarisuuden muunnokseen.
  • on ominaisuuksien muunnokseen hyväksytty oppittavien parametrien painematriisi.

GAT-kerros laajentaa GCN-kerroksen perusaggregaatiofunktiota ja antaa jokaiselle reunalle erilaisen tärkeyden huomiokertoimien kautta.

GAT-kerroksen yhtälöt
  • Yhtälö (1) on alemman kerroksen upotuksen lineaarinen muunnos Hei, ja W on sen oppittava painomatriisi. Tämä muunnos auttaa saavuttamaan riittävän ilmaisuvoiman muuntaa syöttöominaisuudet korkean tason ja tiheiksi ominaisuuksiksi.
  • Yhtälö (2) laskee pareittain normalisoimattoman huomiopisteen kahden naapurin välillä. Täällä se yhdistää ensin z kahden solmun upottaminen, missä || tarkoittaa ketjutusta. Sitten se vie tällaisen ketjutuksen pistetuloksen ja oppittavan painovektorin a. Loppujen lopuksi pistetuotteen tulokseen lisätään LeakyReLU. Huomiopisteet osoittavat naapurisolmun merkityksen sanomanvälityskehyksessä.
  • Yhtälö (3) käyttää softmaxia normalisoimaan huomiopisteet kunkin solmun saapuvilla reunoilla. Softmax koodaa edellisen vaiheen lähdön todennäköisyysjakaumassa. Tämän seurauksena huomiopisteet ovat paljon vertailukelpoisempia eri solmuissa.
  • Yhtälö (4) on samanlainen kuin GCN-aggregaatti (katso yhtälö osan alussa). Naapureiden upotukset kootaan yhteen huomiopisteiden mukaan skaalattuna. Tämän skaalausprosessin tärkein seuraus on oppia eri panos kustakin naapurisolmusta.

NumPy-toteutus

Ensimmäinen vaihe on valmistaa ainesosat (matriisit) edustamaan yksinkertaista käyrää ja suorittamaan lineaarinen muunnos.

NumPy-koodi

print('nn----- One-hot vector representation of nodes. Shape(n,n)n')
X = np.eye(5, 5)
n = X.shape[0]
np.random.shuffle(X)
print(X) print('nn----- Embedding dimensionn')
emb = 3
print(emb) print('nn----- Weight Matrix. Shape(emb, n)n')
W = np.random.uniform(-np.sqrt(1. / emb), np.sqrt(1. / emb), (emb, n))
print(W) print('nn----- Adjacency Matrix (undirected graph). Shape(n,n)n')
A = np.random.randint(2, size=(n, n))
np.fill_diagonal(A, 1) A = (A + A.T)
A[A > 1] = 1
print(A)

ulostulo

----- One-hot vector representation of nodes. Shape(n,n)
[[0 0 1 0 0] # node 1 [0 1 0 0 0] # node 2 [0 0 0 0 1] [1 0 0 0 0] [0 0 0 1 0]]
----- Embedding dimension
3
----- Weight Matrix. Shape(emb, n)
[[-0.4294049 0.57624235 -0.3047382 -0.11941829 -0.12942953] [ 0.19600584 0.5029172 0.3998854 -0.21561317 0.02834577] [-0.06529497 -0.31225734 0.03973776 0.47800217 -0.04941563]]
----- Adjacency Matrix (undirected graph). Shape(n,n)
[[1 1 1 0 1] [1 1 1 1 1] [1 1 1 1 0] [0 1 1 1 1] [1 1 0 1 1]]

Ensimmäinen matriisi määrittelee solmujen yhden kuuman koodatun esityksen (solmu 1 on lihavoitu). Sitten määritellään painomatriisi hyödyntäen määriteltyä upotusulottuvuutta. Olen korostanut kolmannen sarakevektorin W koska kuten pian näette, tämä vektori määrittelee solmun 1 päivitetyn esityksen (1-arvo alustetaan 3. sijaintiin). Voimme suorittaa lineaarisen muunnoksen saavuttaaksemme riittävän ilmaisuvoiman solmuominaisuuksille alkaen näistä ainesosista. Tämän vaiheen tarkoituksena on muuttaa (yhden koodauksen koodatut) tulo-ominaisuudet matalaksi ja tiheäksi esitykseksi.

GAT-kerros (yhtälö 1)

NumPy-koodi

# equation (1)
print('nn----- Linear Transformation. Shape(n, emb)n')
z1 = X.dot(W.T)
print(z1)

ulostulo

----- Linear Transformation. Shape(n, emb) [[-0.3047382 0.3998854 0.03973776] [ 0.57624235 0.5029172 -0.31225734] [-0.12942953 0.02834577 -0.04941563] [-0.4294049 0.19600584 -0.06529497] [-0.11941829 -0.21561317 0.47800217]]

Seuraava toimenpide on ottaa käyttöön oman huomion kertoimet jokaiselle reunalle. Yhdistämme lähdesolmun ja kohdesolmun edustuksen reunojen esittämiseksi. Tämän ketjutusprosessin mahdollistaa vierekkäisyysmatriisi A, joka määrittelee kaavion kaikkien solmujen väliset suhteet.

GAT-kerros (yhtälö 2)

NumPy-koodi

# equation (2) - First part
print('nn----- Concat hidden features to represent edges. Shape(len(emb.concat(emb)), number of edges)n')
edge_coords = np.where(A==1)
h_src_nodes = z1[edge_coords[0]]
h_dst_nodes = z1[edge_coords[1]]
z2 = np.concatenate((h_src_nodes, h_dst_nodes), axis=1)

ulostulo

----- Concat hidden features to represent edges. Shape(len(emb.concat(emb)), number of edges) [[-0.3047382 0.3998854 0.03973776 -0.3047382 0.3998854 0.03973776] [-0.3047382 0.3998854 0.03973776 0.57624235 0.5029172 -0.31225734] [-0.3047382 0.3998854 0.03973776 -0.12942953 0.02834577 -0.04941563] [-0.3047382 0.3998854 0.03973776 -0.11941829 -0.21561317 0.47800217] [ 0.57624235 0.5029172 -0.31225734 -0.3047382 0.3998854 0.03973776] [ 0.57624235 0.5029172 -0.31225734 0.57624235 0.5029172 -0.31225734] [ 0.57624235 0.5029172 -0.31225734 -0.12942953 0.02834577 -0.04941563] [ 0.57624235 0.5029172 -0.31225734 -0.4294049 0.19600584 -0.06529497] [ 0.57624235 0.5029172 -0.31225734 -0.11941829 -0.21561317 0.47800217] [-0.12942953 0.02834577 -0.04941563 -0.3047382 0.3998854 0.03973776] [-0.12942953 0.02834577 -0.04941563 0.57624235 0.5029172 -0.31225734] [-0.12942953 0.02834577 -0.04941563 -0.12942953 0.02834577 -0.04941563] [-0.12942953 0.02834577 -0.04941563 -0.4294049 0.19600584 -0.06529497] [-0.4294049 0.19600584 -0.06529497 0.57624235 0.5029172 -0.31225734] [-0.4294049 0.19600584 -0.06529497 -0.12942953 0.02834577 -0.04941563] [-0.4294049 0.19600584 -0.06529497 -0.4294049 0.19600584 -0.06529497] [-0.4294049 0.19600584 -0.06529497 -0.11941829 -0.21561317 0.47800217] [-0.11941829 -0.21561317 0.47800217 -0.3047382 0.3998854 0.03973776] [-0.11941829 -0.21561317 0.47800217 0.57624235 0.5029172 -0.31225734] [-0.11941829 -0.21561317 0.47800217 -0.4294049 0.19600584 -0.06529497] [-0.11941829 -0.21561317 0.47800217 -0.11941829 -0.21561317 0.47800217]]

Edellisessä lohkossa olen korostanut 4 riviä, jotka edustavat solmuun 4 liitettyjä 1 sisäistä reunaa. Kunkin rivin 3 ensimmäistä elementtiä määrittävät solmun 1 naapureiden upotuksen, kun taas kunkin rivin 3 muuta elementtiä määritetään itse solmu 1 (kuten huomaat, ensimmäinen rivi koodaa itsesilmukan).

Tämän toimenpiteen jälkeen voimme ottaa huomioon tarkkailukertoimet ja kertoa ne reunaesityksestä, joka johtuu ketjutusprosessista. Lopuksi Leaky Relu -toimintoa käytetään tämän tuotteen tulokseen.

NumPy-koodi

# equation (2) - Second part
print('nn----- Attention coefficients. Shape(1, len(emb.concat(emb)))n')
att = np.random.rand(1, z2.shape[1])
print(att) print('nn----- Edge representations combined with the attention coefficients. Shape(1, number of edges)n')
z2_att = z2.dot(att.T)
print(z2_att) print('nn----- Leaky Relu. Shape(1, number of edges)')
e = leaky_relu(z2_att)
print(e)

ulostulo

----- Attention coefficients. Shape(1, len(emb.concat(emb))) [[0.09834683 0.42110763 0.95788953 0.53316528 0.69187711 0.31551563]] ----- Edge representations combined with the attention coefficients. Shape(1, number of edges) [[ 0.30322275] [ 0.73315639] [ 0.11150219] [ 0.11445879] [ 0.09607946] [ 0.52601309] [-0.0956411 ] [-0.14458757] [-0.0926845 ] [ 0.07860653] [ 0.50854017] [-0.11311402] [-0.16206049] [ 0.53443082] [-0.08722337] [-0.13616985] [-0.08426678] [ 0.48206613] [ 0.91199976] [ 0.2413991 ] [ 0.29330217]] ----- Leaky Relu. Shape(1, number of edges)
[[ 3.03222751e-01] [ 7.33156386e-01] [ 1.11502195e-01] [ 1.14458791e-01] [ 9.60794571e-02] [ 5.26013092e-01] [-9.56410988e-04] [-1.44587571e-03] [-9.26845030e-04] [ 7.86065337e-02] [ 5.08540169e-01] [-1.13114022e-03] [-1.62060495e-03] [ 5.34430817e-01] [-8.72233739e-04] [-1.36169846e-03] [-8.42667781e-04] [ 4.82066128e-01] [ 9.11999763e-01] [ 2.41399100e-01] [ 2.93302168e-01]]

Tämän prosessin lopussa saavutimme eri pisteet kaavion jokaiselle reunalle. Yläosassa olen korostanut ensimmäiseen reunaan liittyvän kertoimen kehitystä. Sitten, jotta kerroin olisi helposti vertailukelpoinen eri solmujen välillä, softmax-funktiota käytetään kaikkien naapureiden panoksiin jokaisessa kohdesolmussa.

GAT-kerros (yhtälö 3)

NumPy-koodi

# equation (3)
print('nn----- Edge scores as matrix. Shape(n,n)n')
e_matr = np.zeros(A.shape)
e_matr[edge_coords[0], edge_coords[1]] = e.reshape(-1,)
print(e_matr) print('nn----- For each node, normalize the edge (or neighbor) contributions using softmaxn')
alpha0 = softmax(e_matr[:,0][e_matr[:,0] != 0]) alpha1 = softmax(e_matr[:,1][e_matr[:,1] != 0])
alpha2 = softmax(e_matr[:,2][e_matr[:,2] != 0])
alpha3 = softmax(e_matr[:,3][e_matr[:,3] != 0])
alpha4 = softmax(e_matr[:,4][e_matr[:,4] != 0])
alpha = np.concatenate((alpha0, alpha1, alpha2, alpha3, alpha4))
print(alpha) print('nn----- Normalized edge score matrix. Shape(n,n)n')
A_scaled = np.zeros(A.shape)
A_scaled[edge_coords[0], edge_coords[1]] = alpha.reshape(-1,)
print(A_scaled)

ulostulo

----- Edge scores as matrix. Shape(n,n) [[ 3.03222751e-01 7.33156386e-01 1.11502195e-01 0.00000000e+00 1.14458791e-01] [ 9.60794571e-02 5.26013092e-01 -9.56410988e-04 -1.44587571e-03 -9.26845030e-04] [ 7.86065337e-02 5.08540169e-01 -1.13114022e-03 -1.62060495e-03 0.00000000e+00] [ 0.00000000e+00 5.34430817e-01 -8.72233739e-04 -1.36169846e-03 -8.42667781e-04] [ 4.82066128e-01 9.11999763e-01 0.00000000e+00 2.41399100e-01 2.93302168e-01]] ----- For each node, normalize the edge (or neighbor) contributions using softmax [0.26263543 0.21349717 0.20979916 0.31406823 0.21610715 0.17567419 0.1726313 0.1771592 0.25842816 0.27167844 0.24278118 0.24273876 0.24280162 0.23393014 0.23388927 0.23394984 0.29823075 0.25138555 0.22399017 0.22400903 0.30061525] ----- Normalized edge score matrix. Shape(n,n) [[0.26263543 0.21349717 0.20979916 0. 0.31406823] [0.21610715 0.17567419 0.1726313 0.1771592 0.25842816] [0.27167844 0.24278118 0.24273876 0.24280162 0. ] [0. 0.23393014 0.23388927 0.23394984 0.29823075] [0.25138555 0.22399017 0. 0.22400903 0.30061525]]

Tulkitaksemme viimeisen normalisoitujen reunapisteiden määrittävän matriisin merkityksen, kerrotaan uudelleen vierekkäisyysmatriisin sisältö.

----- Adjacency Matrix (undirected graph). Shape(n,n) [[1 1 1 0 1] [1 1 1 1 1] [1 1 1 1 0] [0 1 1 1 1] [1 1 0 1 1]]

Kuten näette, sen sijaan, että meillä olisi 1 arvo reunojen määrittelemiseksi, skaalattiin uudelleen jokaisen naapurin osuus. Viimeinen vaihe on laskea naapuruston aggregaatti: naapureiden upotukset sisällytetään kohdesolmuun, skaalattuna huomiopisteiden avulla.

GAT-kerros (yhtälö 4)

NumPy-koodi

# equation (4)
print('nnNeighborhood aggregation (GCN) scaled with attention scores (GAT). Shape(n, emb)n')
ND_GAT = A_scaled.dot(z1)
print(ND_GAT)

ulostulo

Neighborhood aggregation (GCN) scaled with attention scores (GAT). Shape(n, emb) [[-0.02166863 0.15062515 0.08352843] [-0.09390287 0.15866476 0.05716299] [-0.07856777 0.28521023 -0.09286313] [-0.03154513 0.10583032 0.04267501] [-0.07962369 0.19226439 0.069115 ]]

Seuraavat vaiheet

Tulevassa artikkelissa kuvaan Multi-Head GAT -kerroksen taustalla olevia mekanismeja, ja näemme joitain sovelluksia linkin ennustustehtävälle.

Viitteet

  • Koodin juokseva versio on saatavana seuraavassa muistikirja. Löydät myös DGL-toteutuksen, josta on hyötyä tarkistettaessa toteutuksen oikeellisuus.
  • Alkuperäinen paperi Graph Attention Networks -sivustolta Petar Veličkovićilta, Guillem Cucurullilta, Arantxa Casanovalta, Adriana Romerolta, Pietro Liòlta, Yoshua Bengioiselta saatavilla arXiv.
  • Ehdotan myös aiheen syvällistä selitystä videosta Aleksa Gordić.

Tämä artikkeli julkaistiin alunperin Kohti datatieteitä ja julkaistu uudelleen TOPBOTS: lle tekijän luvalla.

Nauti tästä artikkelista? Tilaa lisää AI-päivityksiä.

Ilmoitamme sinulle, kun julkaisemme lisää teknistä koulutusta.

Lähde: https://www.topbots.com/graph-attention-networks-under-the-hood/

Aikaleima:

Lisää aiheesta TOPBOTIT