dmlc--dgl
e590feeb62
* GAT * Fix mistake * Fix * hotfix * Fix * Fix * Fix * Fix * Fix * Fix * Fix * Update * Update * Update * Fix style * Hotfix * Hotfix * Hotfix * Fix * Fix * Update * CI trial * Update * Update * Update
175 行
6.3 KiB
Python
175 行
6.3 KiB
Python
# pylint: disable=C0111, C0103, C0200
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.nn.functional as F
|
|
import dgl
|
|
from dgl import BatchedDGLGraph
|
|
|
|
from .gnn import GCNLayer, GATLayer
|
|
from ...nn.pytorch import WeightAndSum
|
|
|
|
class MLPBinaryClassifier(nn.Module):
|
|
"""MLP for soft binary classification over multiple tasks from molecule representations.
|
|
|
|
Parameters
|
|
----------
|
|
in_feats : int
|
|
Number of input molecular graph features
|
|
hidden_feats : int
|
|
Number of molecular graph features in hidden layers
|
|
n_tasks : int
|
|
Number of tasks, also output size
|
|
dropout : float
|
|
The probability for dropout. Default to be 0., i.e. no
|
|
dropout is performed.
|
|
"""
|
|
def __init__(self, in_feats, hidden_feats, n_tasks, dropout=0.):
|
|
super(MLPBinaryClassifier, self).__init__()
|
|
|
|
self.predict = nn.Sequential(
|
|
nn.Dropout(dropout),
|
|
nn.Linear(in_feats, hidden_feats),
|
|
nn.ReLU(),
|
|
nn.BatchNorm1d(hidden_feats),
|
|
nn.Linear(hidden_feats, n_tasks)
|
|
)
|
|
|
|
def forward(self, h):
|
|
"""Perform soft binary classification over multiple tasks
|
|
|
|
Parameters
|
|
----------
|
|
h : FloatTensor of shape (B, M3)
|
|
* B is the number of molecules in a batch
|
|
* M3 is the input molecule feature size, must match in_feats in initialization
|
|
|
|
Returns
|
|
-------
|
|
FloatTensor of shape (B, n_tasks)
|
|
"""
|
|
return self.predict(h)
|
|
|
|
class BaseGNNClassifier(nn.Module):
|
|
"""GCN based predictor for multitask prediction on molecular graphs
|
|
We assume each task requires to perform a binary classification.
|
|
|
|
Parameters
|
|
----------
|
|
gnn_out_feats : int
|
|
Number of atom representation features after using GNN
|
|
n_tasks : int
|
|
Number of prediction tasks
|
|
classifier_hidden_feats : int
|
|
Number of molecular graph features in hidden layers of the MLP Classifier
|
|
dropout : float
|
|
The probability for dropout. Default to be 0., i.e. no
|
|
dropout is performed.
|
|
"""
|
|
def __init__(self, gnn_out_feats, n_tasks, classifier_hidden_feats=128, dropout=0.):
|
|
super(BaseGNNClassifier, self).__init__()
|
|
self.gnn_layers = nn.ModuleList()
|
|
|
|
self.weighted_sum_readout = WeightAndSum(gnn_out_feats)
|
|
self.g_feats = 2 * gnn_out_feats
|
|
self.soft_classifier = MLPBinaryClassifier(
|
|
self.g_feats, classifier_hidden_feats, n_tasks, dropout)
|
|
|
|
def forward(self, bg, feats):
|
|
"""Multi-task prediction for a batch of molecules
|
|
|
|
Parameters
|
|
----------
|
|
bg : BatchedDGLGraph
|
|
B Batched DGLGraphs for processing multiple molecules in parallel
|
|
feats : FloatTensor of shape (N, M0)
|
|
Initial features for all atoms in the batch of molecules
|
|
|
|
Returns
|
|
-------
|
|
FloatTensor of shape (B, n_tasks)
|
|
Soft prediction for all tasks on the batch of molecules
|
|
"""
|
|
# Update atom features with GNNs
|
|
for gnn in self.gnn_layers:
|
|
feats = gnn(bg, feats)
|
|
|
|
# Compute molecule features from atom features
|
|
h_g_sum = self.weighted_sum_readout(bg, feats)
|
|
|
|
with bg.local_scope():
|
|
bg.ndata['h'] = feats
|
|
h_g_max = dgl.max_nodes(bg, 'h')
|
|
|
|
if not isinstance(bg, BatchedDGLGraph):
|
|
h_g_sum = h_g_sum.unsqueeze(0)
|
|
h_g_max = h_g_max.unsqueeze(0)
|
|
h_g = torch.cat([h_g_sum, h_g_max], dim=1)
|
|
|
|
# Multi-task prediction
|
|
return self.soft_classifier(h_g)
|
|
|
|
class GCNClassifier(BaseGNNClassifier):
|
|
"""GCN based predictor for multitask prediction on molecular graphs
|
|
We assume each task requires to perform a binary classification.
|
|
|
|
Parameters
|
|
----------
|
|
in_feats : int
|
|
Number of input atom features
|
|
gcn_hidden_feats : list of int
|
|
gcn_hidden_feats[i] gives the number of output atom features
|
|
in the i+1-th gcn layer
|
|
n_tasks : int
|
|
Number of prediction tasks
|
|
classifier_hidden_feats : int
|
|
Number of molecular graph features in hidden layers of the MLP Classifier
|
|
dropout : float
|
|
The probability for dropout. Default to be 0., i.e. no
|
|
dropout is performed.
|
|
"""
|
|
def __init__(self, in_feats, gcn_hidden_feats, n_tasks,
|
|
classifier_hidden_feats=128, dropout=0.):
|
|
super(GCNClassifier, self).__init__(gnn_out_feats=gcn_hidden_feats[-1],
|
|
n_tasks=n_tasks,
|
|
classifier_hidden_feats=classifier_hidden_feats,
|
|
dropout=dropout)
|
|
|
|
for i in range(len(gcn_hidden_feats)):
|
|
out_feats = gcn_hidden_feats[i]
|
|
self.gnn_layers.append(GCNLayer(in_feats, out_feats))
|
|
in_feats = out_feats
|
|
|
|
class GATClassifier(BaseGNNClassifier):
|
|
"""GAT based predictor for multitask prediction on molecular graphs.
|
|
We assume each task requires to perform a binary classification.
|
|
|
|
Parameters
|
|
----------
|
|
in_feats : int
|
|
Number of input atom features
|
|
"""
|
|
def __init__(self, in_feats, gat_hidden_feats, num_heads,
|
|
n_tasks, classifier_hidden_feats=128, dropout=0):
|
|
super(GATClassifier, self).__init__(gnn_out_feats=gat_hidden_feats[-1],
|
|
n_tasks=n_tasks,
|
|
classifier_hidden_feats=classifier_hidden_feats,
|
|
dropout=dropout)
|
|
assert len(gat_hidden_feats) == len(num_heads), \
|
|
'Got gat_hidden_feats with length {:d} and num_heads with length {:d}, ' \
|
|
'expect them to be the same.'.format(len(gat_hidden_feats), len(num_heads))
|
|
num_layers = len(num_heads)
|
|
for l in range(num_layers):
|
|
if l > 0:
|
|
in_feats = gat_hidden_feats[l - 1] * num_heads[l - 1]
|
|
|
|
if l == num_layers - 1:
|
|
agg_mode = 'mean'
|
|
agg_act = None
|
|
else:
|
|
agg_mode = 'flatten'
|
|
agg_act = F.elu
|
|
|
|
self.gnn_layers.append(GATLayer(in_feats, gat_hidden_feats[l], num_heads[l],
|
|
feat_drop=dropout, attn_drop=dropout,
|
|
agg_mode=agg_mode, activation=agg_act))
|