Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

33 get rid of libmr dependency #34

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ jobs:
# install depedencies
python -m pip install .
# optional dependencies
python -m pip install cython numpy scikit-learn
python -m pip install libmr
python -m pip install scikit-learn
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
Expand Down
3 changes: 0 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,6 @@ The package can be installed via PyPI:

**Optional Dependencies**


* ``libmr`` for the OpenMax Detector [#OpenMax]_ . The library is currently broken and unlikely to be repaired.
You will have to install ``cython`` and ``libmr`` afterwards manually.
* ``scikit-learn`` for ViM
* ``gdown`` to download some datasets and model weights
* ``pandas`` for the `examples <https://pytorch-ood.readthedocs.io/en/latest/auto_examples/index.html>`_.
Expand Down
47 changes: 47 additions & 0 deletions src/pytorch_ood/detector/openmax/libnotmr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Using the weibull fitting implementation from
https://github.com/ashafaei/OD-test/blob/8252aace84e2ae1ab95067876985f62a1060aad6/methods/openmax.py


"""

import numpy as np
from scipy.stats import exponweib
import logging
import scipy.stats

log = logging.getLogger(__name__)


class LibNotMR(object):
"""
Instead of using LibMR (https://github.com/abhijitbendale/OSDN/tree/master/libMR) we implemented
the simple operations with Scipy. The output is checked against the original library for verification.
"""

def __init__(self, tailsize: int = 20):
self.tailsize = tailsize
self.min_val = None
self.translation = 10000 # this constant comes from the library.
# it only makes small numerical differences.
# we keep it for authenticity.
self.a = 1
self.loc = 0
self.c = None
self.scale = None

def fit_high(self, inputs: np.ndarray) -> None:
tailtofit = sorted(inputs)[-self.tailsize:]
self.min_val = np.min(tailtofit)
new_inputs = [i + self.translation - self.min_val for i in tailtofit]
params = exponweib.fit(new_inputs, floc=0, f0=1)
self.c = params[1]
self.scale = params[3]

def w_score(self, inputs: np.ndarray) -> np.ndarray:
new_inputs = inputs + self.translation - self.min_val
new_score = exponweib.cdf(new_inputs, a=self.a, c=self.c, loc=self.loc, scale=self.scale)
return new_score

def __str__(self):
return 'Weib: C=%.2f scale=%.2f min_val=%.2f' % (self.c, self.scale, self.min_val)
24 changes: 10 additions & 14 deletions src/pytorch_ood/detector/openmax/numpy.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
try:
import libmr
except ImportError as e:
print("You will have to install libmr manually to use OpenMax")
raise e

import logging
from typing import TypeVar

import numpy as np
import scipy.spatial.distance as distance
from .libnotmr import LibNotMR
Self = TypeVar("Self")

log = logging.getLogger(__name__)

Expand All @@ -22,11 +19,11 @@ class OpenMax(object):


.. warning:: This methods requires `libmr` to be installed, which is broken at the moment. You can only use it
by installing `cython` and `numpy`, and `libmr` manually afterwards.
by installing `cython` and `numpy`, and `libmr` manually afterward.

:param tailsize: length of the tail to fit the distribution to
:param alpha: number of class activations to revise
:param euclid_weight: weight for the euclidean distance.
:param euclid_weight: weight for the Euclidean distance.

:see Paper: `ArXiv <https://arxiv.org/abs/1511.06233>`__
:see Implementation: `GitHub <https://github.com/abhijitbendale/OSDN>`__
Expand All @@ -49,7 +46,7 @@ def _reset(self):
self.distributions = dict()
self.is_fitted = False

def fit(self, x: np.ndarray, y: np.ndarray) -> np.ndarray:
def fit(self, x: np.ndarray, y: np.ndarray) -> Self:
"""
Fit Openmax layer

Expand All @@ -74,11 +71,10 @@ def fit(self, x: np.ndarray, y: np.ndarray) -> np.ndarray:
pass
# calculate distances of all elements
dists = self._get_dists_to_center(clazz, x[idxs])
tailtofit = sorted(dists)[-self.tailsize :]
model = libmr.MR(alpha=self.alpha)
model.fit_high(tailtofit, len(tailtofit))
if not model.is_valid:
log.error(f"Fitting was invalid ({len(tailtofit)} instances for class {clazz})")

model = LibNotMR(tailsize=self.tailsize)
model.fit_high(dists)

self.distributions[clazz] = model
self.is_fitted = True
return self
Expand Down
4 changes: 2 additions & 2 deletions src/pytorch_ood/detector/openmax/torch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class OpenMax(Detector):
We use the activation of the *unknown* class as outlier score.

.. warning:: This methods requires ``libmr`` to be installed, which is broken at the moment. You can only use it
by installing ``cython`` and ``numpy``, and ``libmr`` manually afterwards.
by installing ``cython`` and ``numpy``, and ``libmr`` manually afterward.

:see Paper: `ArXiv <https://arxiv.org/abs/1511.06233>`__
:see Implementation: `GitHub <https://github.com/abhijitbendale/OSDN>`__
Expand All @@ -45,7 +45,7 @@ def __init__(
:param model: neural network, assumed to output logits
:param tailsize: length of the tail to fit the distribution to
:param alpha: number of class activations to revise
:param euclid_weight: weight for the euclidean distance.
:param euclid_weight: weight for the Euclidean distance.
"""
self.model = model

Expand Down
12 changes: 6 additions & 6 deletions tests/model/test_pretrained.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def test_wrn_pretrained(self, pretrain, n_classes):
y = model(x)
self.assertEqual(y.shape, (1, n_classes))

@for_examples(("cifar10-pixmix", 10))
def test_wrn_pretrained_widen4(self, pretrain, n_classes):
model = WideResNet(pretrained=pretrain, num_classes=n_classes, widen_factor=4)
x = torch.ones(size=(1, 3, 32, 32))
y = model(x)
self.assertEqual(y.shape, (1, n_classes))
# @for_examples(("cifar10-pixmix", 10))
# def test_wrn_pretrained_widen4(self, pretrain, n_classes):
# model = WideResNet(pretrained=pretrain, num_classes=n_classes, widen_factor=4)
# x = torch.ones(size=(1, 3, 32, 32))
# y = model(x)
# self.assertEqual(y.shape, (1, n_classes))