Пример #1
0
    "DB100K",
    "OpenEA",
    "Countries",
    "WD50KT",
    "Wikidata5M",
    "PharmKG8k",
    "PharmKG",
    # Utilities
    "dataset_resolver",
    "get_dataset",
    "has_dataset",
]

logger = logging.getLogger(__name__)

dataset_resolver = ClassResolver.from_entrypoint(group="pykeen.datasets",
                                                 base=Dataset)
if not dataset_resolver.lookup_dict:
    raise RuntimeError(
        dedent("""\
    Datasets have been loaded with entrypoints since PyKEEN v1.0.5, which is now a
    very old version of PyKEEN.

    If you simply use `python3 -m pip install --upgrade pykeen`, the entrypoints will
    not be reloaded. Instead, please reinstall PyKEEN using the following commands:

    $ python3 -m pip uninstall pykeen
    $ python3 -m pip install pykeen

    If you are on Kaggle or Google Colab, please follow these instructions:
    https://pykeen.readthedocs.io/en/stable/installation.html#google-colab-and-kaggle-users
Пример #2
0
    "DistMultInteraction",
    "DistMAInteraction",
    "ERMLPInteraction",
    "ERMLPEInteraction",
    "HolEInteraction",
    "KG2EInteraction",
    "MuREInteraction",
    "NTNInteraction",
    "PairREInteraction",
    "ProjEInteraction",
    "RESCALInteraction",
    "RotatEInteraction",
    "SimplEInteraction",
    "SEInteraction",
    "TorusEInteraction",
    "TransDInteraction",
    "TransEInteraction",
    "TransFInteraction",
    "TransHInteraction",
    "TransRInteraction",
    "TripleREInteraction",
    "TuckerInteraction",
    "UMInteraction",
]

representation_resolver: ClassResolver[
    Representation] = ClassResolver.from_subclasses(
        base=Representation,
        default=Embedding,
    )
Пример #3
0
        x_e: Optional[torch.FloatTensor] = None,
    ) -> torch.FloatTensor:  # noqa: D102
        if message is None or x_e is None:
            raise ValueError(
                f"{self.__class__.__name__} requires message and x_e.")

        # view for heads
        message_ = message.view(message.shape[0], self.num_heads, -1)
        # compute attention coefficients, shape: (num_edges, num_heads)
        alpha = self.activation(
            torch.einsum(
                "ihd,hd->ih",
                torch.cat(
                    [
                        message_,
                        x_e[target].view(target.shape[0], self.num_heads, -1),
                    ],
                    dim=-1,
                ),
                self.weight,
            ))
        # TODO we can use scatter_softmax from torch_scatter directly, kept this if we can rewrite it w/o scatter
        alpha = softmax(alpha, index=target, num_nodes=x_e.shape[0], dim=0)
        alpha = self.dropout(alpha)
        return (message_ * alpha.view(-1, self.num_heads, 1)).view(
            -1, self.num_heads * self.attention_dim)


edge_weight_resolver = ClassResolver.from_subclasses(
    base=EdgeWeighting, default=SymmetricEdgeWeighting)
Пример #4
0
        if self.stopper.should_evaluate(epoch):
            # TODO how to pass inductive mode
            if self.stopper.should_stop(epoch):
                self.training_loop._should_stop = True
            # Since the model is also used within the stopper, its graph and cache have to be cleared
            self.model._free_graph_and_cache()
            # When the stopper obtained a new best epoch, this model has to be saved for reconstruction
        if self.stopper.best_epoch != self.last_best_epoch and self.best_epoch_model_file_path is not None:
            self.training_loop._save_state(
                path=self.best_epoch_model_file_path,
                triples_factory=self.triples_factory)
            self.last_best_epoch = epoch


callback_resolver: ClassResolver[
    TrainingCallback] = ClassResolver.from_subclasses(base=TrainingCallback, )

#: A hint for constructing a :class:`MultiTrainingCallback`
TrainingCallbackHint = OneOrSequence[HintOrType[TrainingCallback]]
TrainingCallbackKwargsHint = OneOrSequence[OptionalKwargs]


class MultiTrainingCallback(TrainingCallback):
    """A wrapper for calling multiple training callbacks together."""

    #: A collection of callbacks
    callbacks: List[TrainingCallback]

    def __init__(
        self,
        callbacks: TrainingCallbackHint = None,
Пример #5
0
There are two other major considerations when randomly sampling negative triples: the random sampling
strategy and the filtering of positive triples. A full guide on negative sampling with the SLCWA can be
found in :mod:`pykeen.sampling`. The following chart from [ali2020a]_ demonstrates the different potential
triples considered in LCWA vs. sLCWA based on the given true triples (in red):

.. image:: ../img/training_approaches.png
  :alt: Troubleshooting Image 2
"""  # noqa:E501

from class_resolver import ClassResolver

from .callbacks import TrainingCallback  # noqa: F401
from .lcwa import LCWATrainingLoop  # noqa: F401
from .slcwa import SLCWATrainingLoop  # noqa: F401
from .training_loop import NonFiniteLossError, TrainingLoop  # noqa: F401

__all__ = [
    "TrainingLoop",
    "SLCWATrainingLoop",
    "LCWATrainingLoop",
    "NonFiniteLossError",
    "training_loop_resolver",
    "TrainingCallback",
]

training_loop_resolver = ClassResolver.from_subclasses(
    base=TrainingLoop,  # type: ignore
    default=SLCWATrainingLoop,
)
Пример #6
0
            x=x,
            source=target,
            target=source,
            edge_type=edge_type,
            edge_weights=edge_weights,
            accumulator=y,
        )
        if self.bias is not None:
            y = y + self.bias
        # activation
        if self.activation is not None:
            y = self.activation(y)
        return y


decomposition_resolver = ClassResolver.from_subclasses(
    base=Decomposition, default=BasesDecomposition)


class RGCNRepresentation(Representation):
    r"""Entity representations enriched by R-GCN.

    The GCN employed by the entity encoder is adapted to include typed edges.
    The forward pass of the GCN is defined by:

     .. math::

        \textbf{e}_{i}^{l+1} = \sigma \left( \sum_{r \in \mathcal{R}}\sum_{j\in \mathcal{N}_{i}^{r}}
        \frac{1}{c_{i,r}} \textbf{W}_{r}^{l} \textbf{e}_{j}^{l} + \textbf{W}_{0}^{l} \textbf{e}_{i}^{l}\right)

    where $\mathcal{N}_{i}^{r}$ is the set of neighbors of node $i$ that are connected to
    $i$ by relation $r$, $c_{i,r}$ is a fixed normalization constant (but it can also be introduced as an additional
Пример #7
0
    def __init__(
        self,
        regularizers: Iterable[Regularizer],
        total_weight: float = 1.0,
        apply_only_once: bool = False,
    ):
        super().__init__(weight=total_weight, apply_only_once=apply_only_once)
        self.regularizers = nn.ModuleList(regularizers)
        for r in self.regularizers:
            if isinstance(r, NoRegularizer):
                raise TypeError("Can not combine a no-op regularizer")
        self.register_buffer(
            name="normalization_factor",
            tensor=torch.as_tensor(
                sum(r.weight for r in self.regularizers),
            ).reciprocal(),
        )

    @property
    def normalize(self):  # noqa: D102
        return any(r.normalize for r in self.regularizers)

    def forward(self, x: torch.FloatTensor) -> torch.FloatTensor:  # noqa: D102
        return self.normalization_factor * sum(r.weight * r.forward(x) for r in self.regularizers)


regularizer_resolver: ClassResolver[Regularizer] = ClassResolver.from_subclasses(
    base=Regularizer,
    default=NoRegularizer,
)
Пример #8
0
    increasing = False

    def __call__(self, ranks: np.ndarray, num_candidates: Optional[np.ndarray] = None) -> float:  # noqa: D102
        return super().__call__(ranks=ranks) / self.expected_value(num_candidates=num_candidates)


@parse_docdata
class AdjustedArithmeticMeanRankIndex(ArithmeticMeanRank):
    """The adjusted arithmetic mean rank index (AMRI).

    ---
    link: https://arxiv.org/abs/2002.06914
    description: The re-indexed adjusted mean rank (AAMR)
    """

    name = "Adjusted Arithmetic Mean Rank Index (AAMRI)"
    value_range = ValueRange(lower=-1, lower_inclusive=True, upper=1, upper_inclusive=True)
    synonyms = ("adjusted_mean_rank_index", "amri", "aamri")
    increasing = True
    supported_rank_types = (RANK_REALISTIC,)
    needs_candidates = True

    def __call__(self, ranks: np.ndarray, num_candidates: Optional[np.ndarray] = None) -> float:  # noqa: D102
        return 1.0 - (super().__call__(ranks=ranks) - 1.0) / (self.expected_value(num_candidates=num_candidates) - 1.0)


rank_based_metric_resolver: ClassResolver[RankBasedMetric] = ClassResolver.from_subclasses(
    base=RankBasedMetric,
    default=InverseHarmonicMeanRank,  # mrr
)
Пример #9
0
    def __call__(
        self,
        edge_index: numpy.ndarray,
        known_anchors: Optional[numpy.ndarray] = None,
    ) -> numpy.ndarray:  # noqa: D102
        anchors = known_anchors or None
        for selection in self.selections:
            anchors = selection(edge_index=edge_index, known_anchors=anchors)
        return anchors


anchor_selection_resolver: ClassResolver[
    AnchorSelection] = ClassResolver.from_subclasses(
        base=AnchorSelection,
        default=DegreeAnchorSelection,
        skip={SingleSelection},
    )


class AnchorSearcher:
    """A method for finding the closest anchors."""
    @abstractmethod
    def __call__(self, edge_index: numpy.ndarray, anchors: numpy.ndarray,
                 k: int) -> numpy.ndarray:
        """
        Find the $k$ closest anchor nodes for each entity.

        :param edge_index: shape: (2, m)
            the edge index
        :param anchors: shape: (a,)
Пример #10
0
    "NeptuneResultTracker",
    "WANDBResultTracker",
    "JSONResultTracker",
    "CSVResultTracker",
    "PythonResultTracker",
    "TensorBoardResultTracker",
    "ConsoleResultTracker",
    # Utilities
    "tracker_resolver",
    "TrackerHint",
    "resolve_result_trackers",
]

tracker_resolver: ClassResolver[ResultTracker] = ClassResolver.from_subclasses(
    base=ResultTracker,
    default=ResultTracker,
    skip={FileResultTracker, MultiResultTracker},
)


def resolve_result_trackers(
    result_tracker: OneOrManyHintOrType[ResultTracker] = None,
    result_tracker_kwargs: OneOrManyOptionalKwargs = None,
) -> MultiResultTracker:
    """Resolve and compose result trackers.

    :param result_tracker: Either none (will result in a Python result tracker),
        a single tracker (as either a class, instance, or string for class name), or a list
        of trackers (as either a class, instance, or string for class name
    :param result_tracker_kwargs: Either none (will use all defaults), a single dictionary
        (will be used for all trackers), or a list of dictionaries with the same length
Пример #11
0
# -*- coding: utf-8 -*-
"""Evaluation."""

from class_resolver import ClassResolver

from .classification_evaluator import ClassificationEvaluator, ClassificationMetricResults
from .evaluator import Evaluator, MetricResults, evaluate
from .rank_based_evaluator import RankBasedEvaluator, RankBasedMetricResults

__all__ = [
    "evaluate",
    "Evaluator",
    "MetricResults",
    "RankBasedEvaluator",
    "RankBasedMetricResults",
    "ClassificationEvaluator",
    "ClassificationMetricResults",
    "evaluator_resolver",
    "metric_resolver",
]

evaluator_resolver: ClassResolver[Evaluator] = ClassResolver.from_subclasses(
    base=Evaluator,
    default=RankBasedEvaluator,
)

metric_resolver: ClassResolver[MetricResults] = ClassResolver.from_subclasses(
    MetricResults)
Пример #12
0
    def forward(self, a: torch.FloatTensor, b: torch.FloatTensor) -> torch.FloatTensor:  # noqa: D102
        return self.__class__.func(a, b)


class SubtractionCompositionModule(FunctionalCompositionModule):
    """Composition by element-wise subtraction."""

    func = torch.sub


class MultiplicationCompositionModule(FunctionalCompositionModule):
    """Composition by element-wise multiplication."""

    func = torch.mul


class CircularCorrelationCompositionModule(FunctionalCompositionModule):
    """Composition by circular correlation via :func:`pykeen.nn.functional.circular_correlation`."""

    func = circular_correlation


composition_resolver = ClassResolver.from_subclasses(
    CompositionModule,
    default=MultiplicationCompositionModule,
    skip={
        FunctionalCompositionModule,
    },
)
Пример #13
0
    from pykeen.pipeline import pipeline

    results = pipeline(
        dataset='YAGO3-10',
        model='PairRE',
        training_loop='sLCWA',
        negative_sampler='bernoulli',
    )
"""  # noqa

from class_resolver import ClassResolver

from .basic_negative_sampler import BasicNegativeSampler
from .bernoulli_negative_sampler import BernoulliNegativeSampler
from .negative_sampler import NegativeSampler
from .pseudo_type import PseudoTypedNegativeSampler

__all__ = [
    "NegativeSampler",
    "BasicNegativeSampler",
    "BernoulliNegativeSampler",
    "PseudoTypedNegativeSampler",
    # Utils
    "negative_sampler_resolver",
]

negative_sampler_resolver = ClassResolver.from_subclasses(
    NegativeSampler,
    default=BasicNegativeSampler,
)
Пример #14
0
    "TuckER",
    "UM",
    # Inductive Models
    "InductiveNodePiece",
    "InductiveNodePieceGNN",
    # Evaluation-only models
    "SoftInverseTripleBaseline",
    "MarginalDistributionBaseline",
    # Utils
    "model_resolver",
    "make_model",
    "make_model_cls",
]

model_resolver = ClassResolver.from_subclasses(
    base=Model,
    skip={
        # Abstract Models
        _NewAbstractModel,
        # We might be able to relax this later
        ERModel,
        LiteralModel,
        # baseline models behave differently
        EvaluationOnlyModel,
        *get_subclasses(EvaluationOnlyModel),
        # Old style models should never be looked up
        _OldAbstractModel,
        EntityRelationEmbeddingModel,
    },
)
Пример #15
0
...     loss='marginranking',
...     loss_kwargs=dict(margin=1),
...     training_loop='slcwa',
...     training_kwargs=dict(num_epochs=100, batch_size=128),
...     negative_sampler='basic',
...     negative_sampler_kwargs=dict(num_negs_per_pos=1),
...     evaluator_kwargs=dict(filtered=True),
...     evaluation_kwargs=dict(batch_size=128),
...     stopper='early',
...     stopper_kwargs=dict(frequency=5, patience=2, relative_delta=0.002),
... )
"""

from class_resolver import ClassResolver

from .early_stopping import EarlyStopper, StopperCallback  # noqa: F401
from .stopper import NopStopper, Stopper

__all__ = [
    "Stopper",
    "NopStopper",
    "EarlyStopper",
    # Utils
    "stopper_resolver",
]

stopper_resolver = ClassResolver.from_subclasses(
    Stopper,
    default=NopStopper,
)
Пример #16
0
    lower=-1.0,
    upper=1.0,
    description="A balanced measure applicable even with class imbalance",
    link="https://en.wikipedia.org/wiki/Phi_coefficient",
)

# TODO there's something wrong with this, so add it later
# classifier_annotator.higher(
#     rmc.pr_auc_score,
#     name="AUC-PR",
#     description="Area Under the Precision-Recall Curve",
#     link="https://rexmex.readthedocs.io/en/latest/modules/root.html#rexmex.metrics.classification.pr_auc_score",
# )

classification_metric_resolver: ClassResolver[ClassificationMetric] = ClassResolver(
    list(classifier_annotator.metrics.values()),
    base=ClassificationMetric,
)


def _check():
    """Check that all functions in the classification module are annotated."""
    for func in rmc.__dict__.values():
        if not inspect.isfunction(func):
            continue
        parameters = inspect.signature(func).parameters
        if "y_true" not in parameters or "y_score" not in parameters:
            continue
        if func in EXCLUDE:
            if func in classifier_annotator.metrics:
                raise ValueError(f"should not include {func.__name__}")
            continue