Skip to content

Commit

Permalink
Merge pull request #146 from XAITK/update-to-v0.8.0
Browse files Browse the repository at this point in the history
Update to v0.8.0.
  • Loading branch information
bjrichardwebster authored May 23, 2024
2 parents 50bf858 + a8c4012 commit 6d788a9
Show file tree
Hide file tree
Showing 22 changed files with 526 additions and 239 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci-unittests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8","3.9","3.10","3.11"]
python-version: ["3.8","3.9","3.10","3.11","3.12"]
# Extras for included, optional plugin support (space-separated lists)
opt-extra: [
"", # no extras
Expand Down Expand Up @@ -128,7 +128,7 @@ jobs:
run: poetry run pytest

- name: CodeCov report submission
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
Expand Down
7 changes: 6 additions & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
version: 2
formats:
- pdf
build:
os: ubuntu-22.04
tools:
python: "3.8"

python:
version: 3.8
install:
- requirements: docs/readthedocs-reqs.txt
- method: pip
path: .

sphinx:
builder: html
configuration: docs/conf.py
11 changes: 8 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
FROM python:3.8 AS xaitk_base

# install poetry and configure to install to pip environment
RUN pip install poetry \
&& poetry config virtualenvs.create false
ENV PATH=/root/.local/bin:${PATH}
RUN (curl -sSL 'https://install.python-poetry.org' | python3 -) \
&& poetry config virtualenvs.create false

WORKDIR /xaitk

Expand All @@ -11,5 +12,9 @@ COPY poetry.lock pyproject.toml /xaitk/
RUN poetry install --no-root

# install package
COPY . /xaitk/
COPY docs /xaitk/docs/
COPY scripts /xaitk/scripts/
COPY tests /xaitk/tests/
COPY xaitk_saliency /xaitk/xaitk_saliency
COPY .flake8 .mypy.ini CONTRIBUTING.md LICENSE.txt README.md /xaitk/
RUN poetry install
3 changes: 2 additions & 1 deletion docs/readthedocs-reqs.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Additional requirements for building our documentation in read-the-docs build
sphinx-prompt
sphinx-prompt==1.3.0
sphinx-rtd-theme==1.2.2
1 change: 1 addition & 0 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Release Notes
release_notes/v0.6.0
release_notes/v0.6.1
release_notes/v0.7.0
release_notes/v0.8.0
33 changes: 33 additions & 0 deletions docs/release_notes/v0.8.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
v0.8.0
======

Updated dependencies to support Python 3.12 along with a few other smaller bug fixes.

Updates / New Features
----------------------

CI/CD

* Changed CodeCov version from v3 to v4.

* Add python 3.12 to test matrix

Docker

* Update Dockerfile install of poetry, and make separate & specific directory
copies.

Utils

* Updated logging format of occlusion masking benchmark utility.

Fixes
-----

Tests

* Fix various floating point equality comparisons to not use exact match.

* Fix random number usage from numpy to use `np.random.default_rng`.

* Fix error being masked in `test_sal_on_coco_dets`
457 changes: 332 additions & 125 deletions poetry.lock

Large diffs are not rendered by default.

25 changes: 19 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ name = "xaitk_saliency"
# REMEMBER: `distutils.version.*Version` types can be used to compare versions
# from strings like this.
# This package prefers to use the strict numbering standard when possible.
version = "0.7.0"
version = "0.8.0"
description = """\
Visual saliency map generation interfaces and baseline implementations \
for explainable AI."""
Expand All @@ -29,22 +29,33 @@ classifiers = [
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
]

[tool.poetry.dependencies]
python = "^3.8"
numpy = ">=1.22"
numpy = [
{version = ">=1.22,<1.26", python = ">=3.8,<3.12"},
{version = ">=1.26", python = ">=3.12"}
]
scikit-image = [
# Hinge because minimum support 0.20.0 for py3.11
{ version = ">=0.18.1", python = "<3.11" },
{ version = ">=0.20.0", python = ">=3.11" }
{ version = ">=0.20.0", python = ">=3.11, <3.12" },
{ version = ">=0.22.0", python = ">=3.12" }
]
scikit-learn = [
{version = ">=1.2, <1.4", python = ">=3.8,<3.12"},
{version = ">=1.4", python = ">=3.12"}
]
scikit-learn = ">=1.2"
smqtk-classifier = ">=0.17.0"
smqtk-core = ">=0.18.0"
smqtk-descriptors = ">=0.16.0"
smqtk-detection = ">=0.19.0"
scipy = ">=1.8.1"
scipy = [
{version = ">=1.8.1,<1.9", python = ">=3.8,<3.11"},
{version = ">=1.9", python = "^3.11"}
]
click = ">=8.0.3"
setuptools = "*"
# Optionals for "example" extra
Expand All @@ -56,10 +67,12 @@ torchvision = {version = ">=0.10.0", optional = true}
tqdm = { version = ">=4.45.0", optional = true }
# Optionals for "tools" extra"
kwcoco = { version = ">=0.2.18", optional = true}
pyyaml = {version = ">=6.0.1", optional = true, python = ">=3.12"}
shapely = {version = ">=2.0.2", optional = true, python = ">=3.12"}

[tool.poetry.extras]
example_deps = [ "jupyter", "matplotlib", "papermill", "torch", "torchvision", "tqdm" ]
tools = [ "kwcoco", "matplotlib" ]
tools = [ "kwcoco", "matplotlib", "pyyaml", "shapely" ]

[tool.poetry.dev-dependencies]
# CI
Expand Down
16 changes: 8 additions & 8 deletions tests/impls/gen_classifier_conf_sal/test_occlusion_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ def test_1class_scores(self) -> None:
Test basic scoring with a single class for broadcasting sanity check.
"""
impl = OcclusionScoring()
np.random.seed(2)
rng = np.random.default_rng(2)
# Three Perturbation masks of height and width 10px for 1 class
image_confs_1_class_ = np.random.rand(1)
pertb_confs_1_class_ = np.random.rand(3, 1)
mask_confs_1_class_ = np.random.randint(low=0, high=2, size=(3, 10, 10), dtype='int')
image_confs_1_class_ = rng.standard_normal((1))
pertb_confs_1_class_ = rng.standard_normal((3, 1))
mask_confs_1_class_ = rng.integers(low=0, high=2, size=(3, 10, 10), dtype='int')

sal = impl.generate(image_confs_1_class_, pertb_confs_1_class_, mask_confs_1_class_)
assert sal.shape == (1, 10, 10)
Expand All @@ -85,10 +85,10 @@ def test_20class_scores(self) -> None:
Test scoring for 20 classes.
"""
impl = OcclusionScoring()
np.random.seed(2)
rng = np.random.default_rng(2)
# Three Perturbation masks of height and width 10px for 20 classes
image_confs_1_class_ = np.random.rand(20)
pertb_confs_1_class_ = np.random.rand(3, 20)
mask_confs_1_class_ = np.random.randint(low=0, high=2, size=(3, 10, 10), dtype='int')
image_confs_1_class_ = rng.standard_normal((20))
pertb_confs_1_class_ = rng.standard_normal((3, 20))
mask_confs_1_class_ = rng.integers(low=0, high=2, size=(3, 10, 10), dtype='int')
sal = impl.generate(image_confs_1_class_, pertb_confs_1_class_, mask_confs_1_class_)
assert sal.shape == (20, 10, 10)
2 changes: 1 addition & 1 deletion tests/impls/gen_classifier_conf_sal/test_rise_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_configuration(self) -> None:
"""
inst = RISEScoring(p1=0.747)
for inst_i in configuration_test_helper(inst):
assert inst_i.p1 == 0.747
assert np.allclose(inst_i.p1, 0.747)

def test_bad_alignment(self) -> None:
"""
Expand Down
40 changes: 20 additions & 20 deletions tests/impls/gen_descriptor_sim_sal/test_similarity_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ def test_generate_mismatch_ref_descriptors(self) -> None:
Test that we appropriately error when the input reference descriptors
are not the same dimensionality.
"""
np.random.seed(0)
test_ref_descr = np.random.rand(16)
test_query_descrs = np.random.rand(5, 15) # Different than above
test_pert_descrs = np.random.rand(32, 16)
test_masks = np.random.rand(32, 16, 16)
rng = np.random.default_rng(seed=0)
test_ref_descr = rng.standard_normal(16)
test_query_descrs = rng.standard_normal((5, 15)) # Different than above
test_pert_descrs = rng.standard_normal((32, 16))
test_masks = rng.standard_normal((32, 16, 16))

impl = SimilarityScoring()

Expand All @@ -76,11 +76,11 @@ def test_generate_mismatched_perturbed(self) -> None:
Test that we appropriately error when the input perturbation
descriptors and mask arrays are not equal in first-dimension length.
"""
np.random.seed(0)
test_ref_descr = np.random.rand(16)
test_query_descrs = np.random.rand(1, 16)
test_pert_descrs = np.random.rand(32, 16)
test_masks = np.random.rand(30, 16, 16) # Different than above
rng = np.random.default_rng(seed=0)
test_ref_descr = rng.standard_normal(16)
test_query_descrs = rng.standard_normal((1, 16))
test_pert_descrs = rng.standard_normal((32, 16))
test_masks = rng.standard_normal((30, 16, 16)) # Different than above

impl = SimilarityScoring()

Expand All @@ -96,11 +96,11 @@ def test_1_featurevec(self) -> None:
Test basic scoring with a single feature for broadcasting sanity check.
"""
impl = SimilarityScoring()
np.random.seed(2)
ref_feat = np.random.rand(2048)
query_feats = np.random.rand(2, 2048)
pertb_feats = np.random.rand(3, 2048)
pertb_mask = np.random.randint(low=0, high=2, size=(3, 10, 10), dtype='int')
rng = np.random.default_rng(2)
ref_feat = rng.standard_normal(2048)
query_feats = rng.standard_normal((2, 2048))
pertb_feats = rng.standard_normal((3, 2048))
pertb_mask = rng.integers(low=0, high=2, size=(3, 10, 10), dtype='int')
sal = impl.generate(ref_feat, query_feats, pertb_feats, pertb_mask)
assert sal.shape == (2, 10, 10)

Expand All @@ -123,10 +123,10 @@ def test_512_featdim(self) -> None:
Test scoring for features of 512 dims.
"""
impl = SimilarityScoring()
np.random.seed(2)
ref_feat = np.random.rand(512)
query_feats = np.random.rand(1, 512)
pertb_feats = np.random.rand(15, 512)
pertb_mask = np.random.randint(low=0, high=2, size=(15, 10, 10), dtype='int')
rng = np.random.default_rng(2)
ref_feat = rng.standard_normal(512)
query_feats = rng.standard_normal((1, 512))
pertb_feats = rng.standard_normal((15, 512))
pertb_mask = rng.integers(low=0, high=2, size=(15, 10, 10), dtype='int')
sal = impl.generate(ref_feat, query_feats, pertb_feats, pertb_mask)
assert sal.shape == (1, 10, 10)
21 changes: 12 additions & 9 deletions tests/impls/gen_detector_prop_sal/test_drise_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ def test_shape_sanity(self) -> None:
Test basic scoring with a single feature for broadcasting sanity check.
"""
impl = DRISEScoring()
ref_dets = np.random.rand(2, 7)
pert_dets = np.random.rand(10, 3, 7)
pert_mask = np.random.randint(
rng = np.random.default_rng(seed=0)
ref_dets = rng.standard_normal((2, 7))
pert_dets = rng.standard_normal((10, 3, 7))
pert_mask = rng.integers(
low=0, high=2, size=(10, 15, 25), dtype='int')
sal = impl.generate(ref_dets, pert_dets, pert_mask)
assert sal.shape == (2, 15, 25)
Expand All @@ -52,9 +53,10 @@ def test_mask_detections_size_mismatch(self) -> None:
Test size mismatch between perturbed detections and masks.
"""
impl = DRISEScoring()
ref_dets = np.random.rand(2, 7)
pert_dets = np.random.rand(9, 3, 7) # ONE LESS than pert mask mat.
pert_mask = np.random.randint(
rng = np.random.default_rng(seed=0)
ref_dets = rng.standard_normal((2, 7))
pert_dets = rng.standard_normal((9, 3, 7)) # ONE LESS than pert mask mat.
pert_mask = rng.integers(
low=0, high=2, size=(10, 15, 25), dtype='int')
with pytest.raises(
ValueError,
Expand All @@ -68,9 +70,10 @@ def test_detections_classes_mismatch(self) -> None:
Test mismatch in number of classes between perturbed and reference detections.
"""
impl = DRISEScoring()
ref_dets = np.random.rand(2, 8) # ONE MORE than pert dets mat.
pert_dets = np.random.rand(10, 3, 7)
pert_mask = np.random.randint(
rng = np.random.default_rng(seed=0)
ref_dets = rng.standard_normal((2, 8)) # ONE MORE than pert dets mat.
pert_dets = rng.standard_normal((10, 3, 7))
pert_mask = rng.integers(
low=0, high=2, size=(10, 15, 25), dtype='int')
with pytest.raises(
ValueError,
Expand Down
8 changes: 4 additions & 4 deletions tests/impls/gen_image_classifier_blackbox_sal/test_rise.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ def test_configuration(self) -> None:
assert isinstance(inst_g, RISEScoring)
assert inst_p.n == 444
assert inst_p.s == 33
assert inst_p.p1 == .22
assert np.allclose(inst_p.p1, .22)
assert inst_p.seed == 42
assert inst_p.threads == 99
assert inst_g.p1 == .22
assert np.allclose(inst_g.p1, .22)
assert inst_i.get_config()['debiased'] is True

def test_configuration_not_debiased(self) -> None:
Expand All @@ -58,8 +58,8 @@ def test_configuration_not_debiased(self) -> None:
inst_g = inst_i._po._generator
assert isinstance(inst_p, RISEGrid)
assert isinstance(inst_g, RISEScoring)
assert inst_p.p1 == .22
assert inst_g.p1 == 0.0
assert np.allclose(inst_p.p1, .22)
assert np.allclose(inst_g.p1, 0.0)
assert inst_i.get_config()['debiased'] is False

def test_generation_rgb(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/impls/gen_object_detector_blackbox_sal/test_drise.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_configuration(self) -> None:
assert isinstance(inst_g, DRISEScoring)
assert inst_p.n == 123
assert inst_p.s == 8
assert inst_p.p1 == 0.73
assert np.allclose(inst_p.p1, .73)
assert inst_p.seed == 98
assert inst_p.threads == 5
assert inst_g.get_config() == {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_configuration(self) -> None:
assert isinstance(inst_g, DRISEScoring)
assert inst_p.n == 55
assert inst_p.s == (15, 8)
assert inst_p.p1 == 0.34
assert np.allclose(inst_p.p1, 0.34)
assert inst_p.seed == 7
assert inst_p.threads == 2
assert inst_g.get_config() == {}
Expand Down
Loading

0 comments on commit 6d788a9

Please sign in to comment.