Source code for pygod.nn.done

import math
import torch
from torch import nn
from torch_geometric.nn import MLP
from torch_geometric.utils import to_dense_adj

from .conv import NeighDiff


[docs]class DONEBase(nn.Module): """ Deep Outlier Aware Attributed Network Embedding DONE consists of an attribute autoencoder and a structure autoencoder. It estimates five losses to optimize the model, including an attribute proximity loss, an attribute homophily loss, a structure proximity loss, a structure homophily loss, and a combination loss. It calculates three outlier scores, and averages them as an overall scores. This model is transductive only. See :cite:`bandyopadhyay2020outlier` for details. Parameters ---------- x_dim : int Input dimension of attribute. s_dim : int Input dimension of structure. hid_dim : int, optional Hidden dimension of model. Default: ``64``. num_layers : int, optional Total number of layers in model. A half (floor) of the layers are for the encoder, the other half (ceil) of the layers are for decoders. Default: ``4``. dropout : float, optional Dropout rate. Default: ``0.``. weight_decay : float, optional Weight decay (L2 penalty). Default: ``0.``. act : callable activation function or None, optional Activation function if not None. Default: ``torch.nn.functional.relu``. w1 : float, optional Weight of structure proximity loss. Default: ``0.2``. w2 : float, optional Weight of structure homophily loss. Default: ``0.2``. w3 : float, optional Weight of attribute proximity loss. Default: ``0.2``. w4 : float, optional Weight of attribute homophily loss. Default: ``0.2``. w5 : float, optional Weight of combination loss. Default: ``0.2``. **kwargs Other parameters for the backbone. """ def __init__(self, x_dim, s_dim, hid_dim=64, num_layers=4, dropout=0., act=torch.nn.functional.relu, w1=0.2, w2=0.2, w3=0.2, w4=0.2, w5=0.2, **kwargs): super(DONEBase, self).__init__() self.w1 = w1 self.w2 = w2 self.w3 = w3 self.w4 = w4 self.w5 = w5 # split the number of layers for the encoder and decoders assert num_layers >= 2, \ "Number of layers must be greater than or equal to 2." encoder_layers = math.floor(num_layers / 2) decoder_layers = math.ceil(num_layers / 2) self.attr_encoder = MLP(in_channels=x_dim, hidden_channels=hid_dim, out_channels=hid_dim, num_layers=encoder_layers, dropout=dropout, act=act, **kwargs) self.attr_decoder = MLP(in_channels=hid_dim, hidden_channels=hid_dim, out_channels=x_dim, num_layers=decoder_layers, dropout=dropout, act=act, **kwargs) self.struct_encoder = MLP(in_channels=s_dim, hidden_channels=hid_dim, out_channels=hid_dim, num_layers=encoder_layers, dropout=dropout, act=act, **kwargs) self.struct_decoder = MLP(in_channels=hid_dim, hidden_channels=hid_dim, out_channels=s_dim, num_layers=decoder_layers, dropout=dropout, act=act, **kwargs) self.neigh_diff = NeighDiff() self.emb = None
[docs] def forward(self, x, s, edge_index): """ Forward computation. Parameters ---------- x : torch.Tensor Input attribute embeddings. s : torch.Tensor Input structure embeddings. edge_index : torch.Tensor Edge index. Returns ------- x_ : torch.Tensor Reconstructed attribute embeddings. s_ : torch.Tensor Reconstructed structure embeddings. h_a : torch.Tensor Attribute hidden embeddings. h_s : torch.Tensor Structure hidden embeddings. dna : torch.Tensor Attribute neighbor distance. dns : torch.Tensor Structure neighbor distance. """ h_a = self.attr_encoder(x) x_ = self.attr_decoder(h_a) dna = self.neigh_diff(h_a, edge_index).squeeze() h_s = self.struct_encoder(s) s_ = self.struct_decoder(h_s) dns = self.neigh_diff(h_s, edge_index).squeeze() self.emb = (h_a, h_s) return x_, s_, h_a, h_s, dna, dns
[docs] def loss_func(self, x, x_, s, s_, h_a, h_s, dna, dns): """ Loss function for DONE. Parameters ---------- x : torch.Tensor Input attribute embeddings. x_ : torch.Tensor Reconstructed attribute embeddings. s : torch.Tensor Input structure embeddings. s_ : torch.Tensor Reconstructed structure embeddings. h_a : torch.Tensor Attribute hidden embeddings. h_s : torch.Tensor Structure hidden embeddings. dna : torch.Tensor Attribute neighbor distance. dns : torch.Tensor Structure neighbor distance. Returns ------- loss : torch.Tensor Loss value. oa : torch.Tensor Attribute outlier scores. os : torch.Tensor Structure outlier scores. oc : torch.Tensor Combined outlier scores. """ # equation 9 is based on the official implementation, and it # is slightly different from the paper dx = torch.sum(torch.pow(x - x_, 2), 1) tmp = self.w3 * dx + self.w4 * dna oa = tmp / torch.sum(tmp) # equation 8 is based on the official implementation, and it # is slightly different from the paper ds = torch.sum(torch.pow(s - s_, 2), 1) tmp = self.w1 * ds + self.w2 * dns os = tmp / torch.sum(tmp) # equation 10 dc = torch.sum(torch.pow(h_a - h_s, 2), 1) oc = dc / torch.sum(dc) # equation 4 loss_prox_a = torch.mean(torch.log(torch.pow(oa, -1)) * dx) # equation 5 loss_hom_a = torch.mean(torch.log(torch.pow(oa, -1)) * dna) # equation 2 loss_prox_s = torch.mean(torch.log(torch.pow(os, -1)) * ds) # equation 3 loss_hom_s = torch.mean(torch.log(torch.pow(os, -1)) * dns) # equation 6 loss_c = torch.mean(torch.log(torch.pow(oc, -1)) * dc) # equation 7 loss = self.w3 * loss_prox_a + \ self.w4 * loss_hom_a + \ self.w1 * loss_prox_s + \ self.w2 * loss_hom_s + \ self.w5 * loss_c return loss, oa, os, oc
[docs] @staticmethod def process_graph(data): """ Obtain the dense adjacency matrix of the graph. Parameters ---------- data : torch_geometric.data.Data Input graph. """ data.s = to_dense_adj(data.edge_index)[0]