Browse Source

change basic order

tags/v0.3.1
Frozenmad 4 years ago
parent
commit
d0396c8bd2
9 changed files with 133 additions and 95 deletions
  1. +11
    -10
      autogl/solver/__init__.py
  2. +14
    -14
      autogl/solver/base.py
  3. +1
    -1
      autogl/solver/classfier/__init__.py
  4. +2
    -2
      autogl/solver/classfier/base.py
  5. +45
    -22
      autogl/solver/classfier/graph_classifier.py
  6. +0
    -0
      autogl/solver/classfier/link_predictor.py
  7. +60
    -33
      autogl/solver/classfier/node_classifier.py
  8. +0
    -0
      autogl/solver/dgl/__init__.py
  9. +0
    -13
      autogl/solver/pyg/__init__.py

+ 11
- 10
autogl/solver/__init__.py View File

@@ -1,12 +1,13 @@
import importlib
import sys
from ..backend import DependentBackend
"""
Auto solver for various graph tasks
"""

# load corresponding backend of subclass
def _load_subclass_backend(backend):
sub_module = importlib.import_module(f'.{backend.get_backend_name()}', __name__)
this = sys.modules[__name__]
for api, obj in sub_module.__dict__.items():
setattr(this, api, obj)
from .classifier import AutoGraphClassifier, AutoNodeClassifier, AutoLinkPredictor
from ..utils import LeaderBoard

_load_subclass_backend(DependentBackend)
__all__ = [
"AutoNodeClassifier",
"AutoGraphClassifier",
"AutoLinkPredictor",
"LeaderBoard",
]

autogl/solver/pyg/base.py → autogl/solver/base.py View File

@@ -4,20 +4,20 @@ Solver base class
Provide some standard solver interface.
"""

from typing import Any, Tuple
from typing import Any, Iterable, Tuple
from copy import deepcopy

import torch

from ...module.feature import FEATURE_DICT
from ...module.hpo import HPO_DICT
from ...module.model import MODEL_DICT
from ...module.nas.algorithm import NAS_ALGO_DICT
from ...module.nas.estimator import NAS_ESTIMATOR_DICT
from ...module.nas.space import NAS_SPACE_DICT
from ...module import BaseFeature, BaseHPOptimizer, BaseTrainer
from ..utils import LeaderBoard
from ...utils import get_logger
from ..module.feature import FEATURE_DICT
from ..module.hpo import HPO_DICT
from ..module.model import MODEL_DICT
from ..module.nas.algorithm import NAS_ALGO_DICT
from ..module.nas.estimator import NAS_ESTIMATOR_DICT
from ..module.nas.space import NAS_SPACE_DICT
from ..module import BaseFeature, BaseHPOptimizer, BaseTrainer
from .utils import LeaderBoard
from ..utils import get_logger

LOGGER = get_logger("BaseSolver")

@@ -175,7 +175,7 @@ class BaseSolver:
self.feature_module = None
elif isinstance(feature_module, (BaseFeature, str)):
self.feature_module = get_feature(feature_module)
elif isinstance(feature_module, list):
elif isinstance(feature_module, Iterable):
self.feature_module = get_feature(feature_module[0])
for feature_engineer in feature_module[1:]:
self.feature_module &= get_feature(feature_engineer)
@@ -306,15 +306,15 @@ class BaseSolver:

nas_algorithms = (
nas_algorithms
if isinstance(nas_algorithms, (list, tuple))
if isinstance(nas_algorithms, Iterable)
else [nas_algorithms]
)
nas_spaces = (
nas_spaces if isinstance(nas_spaces, (list, tuple)) else [nas_spaces]
nas_spaces if isinstance(nas_spaces, Iterable) else [nas_spaces]
)
nas_estimators = (
nas_estimators
if isinstance(nas_estimators, (list, tuple))
if isinstance(nas_estimators, Iterable)
else [nas_estimators]
)


autogl/solver/pyg/classifier/__init__.py → autogl/solver/classfier/__init__.py View File

@@ -2,7 +2,7 @@
Auto classifier for classification problems.
"""

from .base import BaseClassifier
from ..base import BaseClassifier
from .graph_classifier import AutoGraphClassifier
from .node_classifier import AutoNodeClassifier
from .link_predictor import AutoLinkPredictor

autogl/solver/pyg/classifier/base.py → autogl/solver/classfier/base.py View File

@@ -4,8 +4,8 @@ Base solver for classification problems

from typing import Any
from ..base import BaseSolver
from ....module.ensemble import ENSEMBLE_DICT
from ....module import BaseEnsembler
from ...module.ensemble import ENSEMBLE_DICT
from ...module import BaseEnsembler


class BaseClassifier(BaseSolver):

autogl/solver/pyg/classifier/graph_classifier.py → autogl/solver/classfier/graph_classifier.py View File

@@ -11,16 +11,17 @@ import numpy as np
import yaml

from .base import BaseClassifier
from ....module.feature import FEATURE_DICT
from ....module.model import BaseModel, MODEL_DICT
from ....module.train import TRAINER_DICT, get_feval, BaseGraphClassificationTrainer
from ...module.feature import FEATURE_DICT
from ...module.model import BaseModel, MODEL_DICT
from ...module.train import TRAINER_DICT, get_feval, BaseGraphClassificationTrainer
from ..base import _initialize_single_model, _parse_hp_space
from ...utils import LeaderBoard, set_seed
from ....datasets import utils
from ...utils import get_logger
from ..utils import LeaderBoard, set_seed
from ...datasets import utils
from ..utils import get_logger
from ...backend import DependentBackend

LOGGER = get_logger("GraphClassifier")
__backend = DependentBackend.get_backend_name()

class AutoGraphClassifier(BaseClassifier):
"""
@@ -239,7 +240,7 @@ class AutoGraphClassifier(BaseClassifier):

Parameters
----------
dataset: torch_geometric.data.dataset.Dataset
dataset: autogl.data.dataset
The multi-graph dataset needed to fit on.

time_limit: int
@@ -300,10 +301,17 @@ class AutoGraphClassifier(BaseClassifier):

# set up the dataset
if train_split is None and val_split is None:
assert hasattr(dataset, "train_split") and hasattr(dataset, "val_split"), (
"The dataset has no default train/val split! "
"Please manually pass train and val ratio."
)
# Currently, there are no much implementation difference between pyg and dgl on solver
# We can use way of hotfix to judge
if __backend == 'pyg':
assert hasattr(dataset, "train_split") and hasattr(dataset, "val_split"), (
"The dataset has no default train/val split! "
"Please manually pass train and val ratio."
)
elif __backend == 'dgl':
# no available solutions here.
# TODO: we cannot judge whether the graph dataset has train/val/test split on dgl.
pass
LOGGER.info("Use the default train/val/test ratio in given dataset")
# if hasattr(dataset.train_split, "n_splits"):
# cross_validation = True
@@ -327,17 +335,29 @@ class AutoGraphClassifier(BaseClassifier):
dataset = self.feature_module.transform(dataset, inplace=inplace)

self.dataset = dataset
assert dataset[0].x is not None, (
"Does not support fit on non node-feature dataset!"
" Please add node features to dataset or specify feature engineers that generate"
" node features."
)
# check whether the dataset has features.
# currently we only support graph classification with features.
if __backend == 'pyg':
assert dataset[0].x is not None, (
"Does not support fit on non node-feature dataset!"
" Please add node features to dataset or specify feature engineers that generate"
" node features."
)
elif __backend == 'dgl':
assert 'feat' in dataset[0].ndata['feat'], (
"Does not support fit on non node-feature dataset!"
" Please add node features to dataset or specify feature engineers that generate"
" node features."
)

# initialize graph networks
self._init_graph_module(
self.gml,
num_features=dataset.num_node_features,
num_classes=dataset.num_classes,
# TODO: what should we use to get feature dimension?
num_features=dataset.num_node_features if __backend == 'pyg' else dataset[0].ndata['feat'].size(-1),
num_classes=dataset.num_classes if __backend == 'pyg' else dataset.nclasses,
feval=evaluator_list,
device=self.runtime_device,
loss="cross_entropy" if not hasattr(dataset, "loss") else dataset.loss,
@@ -410,7 +430,10 @@ class AutoGraphClassifier(BaseClassifier):
if self.ensemble_module is not None:
performance = self.ensemble_module.fit(
result_valid,
dataset.data.y[dataset.val_index].cpu().detach().numpy(),
# TODO: get validation set of graphs
dataset.data.y[dataset.val_index].cpu().detach().numpy()
if __backend == 'pyg' else
dataset.labels[dataset.val_index].cpu().detach().numpy(),
names,
evaluator_list,
n_classes=dataset.num_classes,
@@ -519,7 +542,7 @@ class AutoGraphClassifier(BaseClassifier):

Parameters
----------
dataset: torch_geometric.data.dataset.Dataset or None
dataset: autogl.data.Dataset or None
The dataset needed to predict. If ``None``, will use the processed dataset
passed to ``fit()`` instead. Default ``None``.

@@ -629,7 +652,7 @@ class AutoGraphClassifier(BaseClassifier):

Parameters
----------
dataset: torch_geometric.data.dataset.Dataset or None
dataset: autogl.data.Dataset or None
The dataset needed to predict. If ``None``, will use the processed dataset passed
to ``fit()`` instead. Default ``None``.


autogl/solver/pyg/classifier/link_predictor.py → autogl/solver/classfier/link_predictor.py View File


autogl/solver/pyg/classifier/node_classifier.py → autogl/solver/classfier/node_classifier.py View File

@@ -13,21 +13,20 @@ import yaml

from .base import BaseClassifier
from ..base import _parse_hp_space, _initialize_single_model
from ....module.feature import FEATURE_DICT
from ....module.model import MODEL_DICT, BaseModel
from ....module.train import TRAINER_DICT, BaseNodeClassificationTrainer
from ....module.train import get_feval
from ....module.nas.space import NAS_SPACE_DICT
from ....module.nas.algorithm import NAS_ALGO_DICT
from ....module.nas.estimator import NAS_ESTIMATOR_DICT, BaseEstimator
from ...utils import LeaderBoard, set_seed
from ....datasets import utils
from ....utils import get_logger

from torch_geometric.nn import GATConv, GCNConv
from ...module.feature import FEATURE_DICT
from ...module.model import MODEL_DICT, BaseModel
from ...module.train import TRAINER_DICT, BaseNodeClassificationTrainer
from ...module.train import get_feval
from ...module.nas.space import NAS_SPACE_DICT
from ...module.nas.algorithm import NAS_ALGO_DICT
from ...module.nas.estimator import NAS_ESTIMATOR_DICT, BaseEstimator
from ..utils import LeaderBoard, set_seed
from ...datasets import utils
from ...utils import get_logger
from ...backend import DependentBackend

LOGGER = get_logger("NodeClassifier")
__backend = DependentBackend.get_backend_name()

class AutoNodeClassifier(BaseClassifier):
"""
@@ -241,7 +240,7 @@ class AutoNodeClassifier(BaseClassifier):

Parameters
----------
dataset: torch_geometric.data.dataset.Dataset
dataset: autogl.data.Dataset
The dataset needed to fit on. This dataset must have only one graph.

time_limit: int
@@ -306,7 +305,10 @@ class AutoNodeClassifier(BaseClassifier):

# set up the dataset
if train_split is not None and val_split is not None:
size = dataset.data.x.shape[0]
if __backend == 'pyg':
size = dataset.data.x.shape[0]
else:
size = dataset.graphs[0].num_nodes()
if balanced:
train_split = (
train_split if train_split > 1 else int(train_split * size)
@@ -325,12 +327,18 @@ class AutoNodeClassifier(BaseClassifier):
dataset, train_ratio=train_split, val_ratio=val_split
)
else:
assert hasattr(dataset.data, "train_mask") and hasattr(
dataset.data, "val_mask"
), (
"The dataset has no default train/val split! Please manually pass "
"train and val ratio."
)
if __backend == 'pyg':
assert hasattr(dataset.data, "train_mask") and hasattr(
dataset.data, "val_mask"
), (
"The dataset has no default train/val split! Please manually pass "
"train and val ratio."
)
elif __backend == 'dgl':
assert "train_mask" in dataset[0].ndata and "val_mask" in dataset[0].ndata, (
"The dataset has no default train/val split! Please manually pass "
"train and val ratio."
)
LOGGER.info("Use the default train/val/test ratio in given dataset")

# feature engineering
@@ -338,26 +346,40 @@ class AutoNodeClassifier(BaseClassifier):
dataset = self.feature_module.fit_transform(dataset, inplace=inplace)

self.dataset = dataset
assert self.dataset[0].x is not None, (
"Does not support fit on non node-feature dataset!"
" Please add node features to dataset or specify feature engineers that generate"
" node features."
)

# check whether the dataset has features.
# currently we only support graph classification with features.
if __backend == 'pyg':
assert dataset[0].x is not None, (
"Does not support fit on non node-feature dataset!"
" Please add node features to dataset or specify feature engineers that generate"
" node features."
)
elif __backend == 'dgl':
# TODO: how can we get features?
assert 'feat' in dataset[0].ndata['feat'], (
"Does not support fit on non node-feature dataset!"
" Please add node features to dataset or specify feature engineers that generate"
" node features."
)

# initialize graph networks
self._init_graph_module(
self.gml,
num_features=self.dataset[0].x.shape[1],
num_classes=dataset.num_classes,
# TODO: how can we get num_features?
num_features=self.dataset[0].x.shape[1] if __backend == 'pyg' else self.dataset[0].ndata['feat'].size(-1),
num_classes=self.dataset.num_classes,
feval=evaluator_list,
device=self.runtime_device,
loss="nll_loss" if not hasattr(dataset, "loss") else dataset.loss,
loss="nll_loss" if not hasattr(dataset, "loss") else self.dataset.loss,
)

if self.nas_algorithms is not None:
# perform neural architecture search
self._init_nas_module(
num_features=self.dataset[0].x.shape[1],
# TODO: how can we get num_features?
num_features=self.dataset[0].x.shape[1] if __backend == 'pyg' else self.dataset[0].ndata['feat'].size(-1),
num_classes=self.dataset.num_classes,
feval=evaluator_list,
device=self.runtime_device,
@@ -385,7 +407,8 @@ class AutoNodeClassifier(BaseClassifier):
if isinstance(train_name, str):
trainer = TRAINER_DICT[train_name](
model=model,
num_features=self.dataset[0].x.shape[1],
# TODO: how can we get num_features?
num_features=self.dataset[0].x.shape[1] if __backend == 'pyg' else self.dataset[0].ndata['feat'].size(-1),
num_classes=self.dataset.num_classes,
loss="nll_loss"
if not hasattr(dataset, "loss")
@@ -398,8 +421,9 @@ class AutoNodeClassifier(BaseClassifier):
trainer = train_name
trainer.model = model
trainer.update_parameters(
# TODO: how can we get num_features?
num_features=self.dataset[0].x.shape[1] if __backend == 'pyg' else self.dataset[0].ndata['feat'].size(-1),
num_classes=self.dataset.num_classes,
num_features=self.dataset[0].x.shape[1],
loss="nll_loss"
if not hasattr(dataset, "loss")
else dataset.loss,
@@ -444,7 +468,10 @@ class AutoNodeClassifier(BaseClassifier):
if self.ensemble_module is not None:
performance = self.ensemble_module.fit(
result_valid,
self.dataset[0].y[self.dataset[0].val_mask].cpu().numpy(),
#
self.dataset[0].y[self.dataset[0].val_mask].cpu().numpy()
if __backend == 'pyg' else
self.dataset[0].ndata['label'][self.dataset[0].ndata['val_mask']].cpu().numpy(),
names,
evaluator_list,
n_classes=dataset.num_classes,

+ 0
- 0
autogl/solver/dgl/__init__.py View File


+ 0
- 13
autogl/solver/pyg/__init__.py View File

@@ -1,13 +0,0 @@
"""
Auto solver for various graph tasks
"""

from .classifier import AutoGraphClassifier, AutoNodeClassifier, AutoLinkPredictor
from ..utils import LeaderBoard

__all__ = [
"AutoNodeClassifier",
"AutoGraphClassifier",
"AutoLinkPredictor",
"LeaderBoard",
]

Loading…
Cancel
Save