From 8ddc8d7ac2c2cc8da5c88558f720c6efc786c64e Mon Sep 17 00:00:00 2001 From: Nathan Michlo Date: Fri, 5 Feb 2021 01:22:54 +0200 Subject: [PATCH 1/4] update requirements --- requirements.txt | 72 ++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/requirements.txt b/requirements.txt index bfa60466..6059eafa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,58 +1,58 @@ -pip == 21.0 +pip>=21.0 # DATA SCIENCE & ML # ================= -numpy == 1.19.5 -# pandas == 1.2.1 +numpy==1.20.0 +# pandas==1.2.1 -# opencv-python == 4.5.1.48 +# opencv-python==4.5.1.48 -torch == 1.7.1 -torchvision == 0.8.2 -torch-optimizer == 0.1.0 -pytorch-lightning == 1.1.5 -# pytorch-lightning-bolts == 0.3.0 +torch==1.7.1 +torchvision==0.8.2 +torch-optimizer==0.1.0 +pytorch-lightning==1.1.7 +# pytorch-lightning-bolts==0.3.0 -# tensorflow == 2.4.1 -# tensorflow-gpu == 2.4.1 -# tensorboard == 2.4.1 +# tensorflow==2.4.1 +# tensorflow-gpu==2.4.1 +# tensorboard==2.4.1 -scipy == 1.6.0 -scikit-learn == 0.24.1 -# xgboost == 1.3.3 -# lightgbm == 3.1.1 +scipy==1.6.0 +scikit-learn==0.24.1 +# xgboost==1.3.3 +# lightgbm==3.1.1 # IMAGE AUGMENTATION # ================== -kornia == 0.4.1 # $ pip install git+https://github.com/kornia/kornia -# imgaug == 0.4.0 -# albumentations == 0.5.2 +kornia==0.4.1 # $ pip install git+https://github.com/kornia/kornia +# imgaug==0.4.0 +# albumentations==0.5.2 # INPUT / OUTPUT # ================ -# psutil == 5.8.0 -# pillow == 8.1.0 -# imageio == 2.9.0 -# moviepy == 1.0.3 -h5py >= 2.10.0 # as of tensorflow 2.4 it does not support h5py 3+ +# psutil==5.8.0 +# pillow==8.1.0 +# imageio==2.9.0 +# moviepy==1.0.3 +h5py>=2.10.0 # as of tensorflow 2.4 it does not support h5py 3+ # GRAPHING & DISPLAY # ================== -matplotlib == 3.3.3 -# plotly == 4.14.3 -# seaborn == 0.11.1 -# tqdm == 4.56.0 +matplotlib==3.3.4 +# plotly==4.14.3 +# seaborn==0.11.1 +# tqdm==4.56.0 -# streamlit == 0.75.0 -wandb == 0.10.15 +# streamlit==0.75.0 +wandb==0.10.17 # UTILITY # ======= -# pytest == 6.2.1 +# pytest==6.2.1 -hydra-core == 1.0.5 -hydra-colorlog == 1.0.0 -# hydra-joblib-launcher == 1.1.1 -hydra-submitit-launcher == 1.0.1 +hydra-core==1.0.6 +hydra-colorlog==1.0.0 +# hydra-joblib-launcher==1.1.1 +hydra-submitit-launcher==1.1.0 -# submitit == 1.1.5 +# submitit==1.1.5 From 369210df6a0aab3d54fd52739a52b0784ba6c5cd Mon Sep 17 00:00:00 2001 From: Nathan Michlo Date: Fri, 5 Feb 2021 02:29:58 +0200 Subject: [PATCH 2/4] tests for doc examples --- disent/util/__init__.py | 26 ++++++++++++++++++ docs/examples/overview_framework_adagvae.py | 3 ++- docs/examples/overview_framework_ae.py | 3 ++- docs/examples/overview_framework_betavae.py | 3 ++- docs/examples/overview_metrics.py | 7 ++--- requirements-test.txt | 3 +++ requirements.txt | 2 +- tests/test_docs_examples.py | 30 +++++++++++++++++++++ 8 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 requirements-test.txt create mode 100644 tests/test_docs_examples.py diff --git a/disent/util/__init__.py b/disent/util/__init__.py index eaf2a6dc..cd1bfcf1 100644 --- a/disent/util/__init__.py +++ b/disent/util/__init__.py @@ -1,5 +1,6 @@ import functools import logging +import os import time from dataclasses import dataclass @@ -19,6 +20,31 @@ # ========================================================================= # +def is_test_run(): + """ + This is used internally to test some scripts. There is no need + to use this function in your own scripts. + """ + return bool(os.environ.get('DISENT_TEST_RUN', False)) + + +def test_run_int(integer: int, div=100): + """ + This is used to test some scripts, by dividing the input number. + There is no need to use this function in your own scripts. + """ + return ((integer + div - 1) // div) if is_test_run() else integer + + +def _set_test_run(): + os.environ['DISENT_TEST_RUN'] = 'True' + + +# ========================================================================= # +# seeds # +# ========================================================================= # + + def seed(long=777): """ https://pytorch.org/docs/stable/notes/randomness.html diff --git a/docs/examples/overview_framework_adagvae.py b/docs/examples/overview_framework_adagvae.py index 4baca007..5ce64766 100644 --- a/docs/examples/overview_framework_adagvae.py +++ b/docs/examples/overview_framework_adagvae.py @@ -6,6 +6,7 @@ from disent.frameworks.vae.weaklysupervised import AdaVae from disent.model.ae import EncoderConv64, DecoderConv64, GaussianAutoEncoder from disent.transform import ToStandardisedTensor +from disent.util import is_test_run data: GroundTruthData = XYSquaresData() dataset: Dataset = GroundTruthDatasetOrigWeakPairs(data, transform=ToStandardisedTensor()) @@ -20,5 +21,5 @@ cfg=AdaVae.cfg(beta=4, average_mode='gvae', symmetric_kl=False) ) -trainer = pl.Trainer(logger=False, checkpoint_callback=False) +trainer = pl.Trainer(logger=False, checkpoint_callback=False, fast_dev_run=is_test_run()) trainer.fit(module, dataloader) \ No newline at end of file diff --git a/docs/examples/overview_framework_ae.py b/docs/examples/overview_framework_ae.py index ad612b5b..75f71e62 100644 --- a/docs/examples/overview_framework_ae.py +++ b/docs/examples/overview_framework_ae.py @@ -6,6 +6,7 @@ from disent.frameworks.ae.unsupervised import AE from disent.model.ae import EncoderConv64, DecoderConv64, AutoEncoder from disent.transform import ToStandardisedTensor +from disent.util import is_test_run data: GroundTruthData = XYSquaresData() dataset: Dataset = GroundTruthDataset(data, transform=ToStandardisedTensor()) @@ -20,5 +21,5 @@ cfg=AE.cfg() ) -trainer = pl.Trainer(logger=False, checkpoint_callback=False) +trainer = pl.Trainer(logger=False, checkpoint_callback=False, max_steps=1, fast_dev_run=is_test_run()) trainer.fit(module, dataloader) \ No newline at end of file diff --git a/docs/examples/overview_framework_betavae.py b/docs/examples/overview_framework_betavae.py index 1aded024..797443cd 100644 --- a/docs/examples/overview_framework_betavae.py +++ b/docs/examples/overview_framework_betavae.py @@ -6,6 +6,7 @@ from disent.frameworks.vae.unsupervised import BetaVae from disent.model.ae import EncoderConv64, DecoderConv64, GaussianAutoEncoder from disent.transform import ToStandardisedTensor +from disent.util import is_test_run data: GroundTruthData = XYSquaresData() dataset: Dataset = GroundTruthDataset(data, transform=ToStandardisedTensor()) @@ -20,5 +21,5 @@ cfg=BetaVae.cfg(beta=4) ) -trainer = pl.Trainer(logger=False, checkpoint_callback=False) +trainer = pl.Trainer(logger=False, checkpoint_callback=False, max_steps=1, fast_dev_run=is_test_run()) trainer.fit(module, dataloader) \ No newline at end of file diff --git a/docs/examples/overview_metrics.py b/docs/examples/overview_metrics.py index fe8dff22..6adc0f1c 100644 --- a/docs/examples/overview_metrics.py +++ b/docs/examples/overview_metrics.py @@ -7,6 +7,7 @@ from disent.metrics import metric_dci, metric_mig from disent.model.ae import EncoderConv64, DecoderConv64, GaussianAutoEncoder from disent.transform import ToStandardisedTensor +from disent.util import is_test_run, test_run_int data = XYObjectData() dataset = GroundTruthDataset(data, transform=ToStandardisedTensor()) @@ -23,7 +24,7 @@ def make_vae(beta): ) def train(module): - trainer = pl.Trainer(logger=False, checkpoint_callback=False, max_steps=256) + trainer = pl.Trainer(logger=False, checkpoint_callback=False, max_steps=256, fast_dev_run=is_test_run()) trainer.fit(module, dataloader) # we cannot guarantee which device the representation is on @@ -31,8 +32,8 @@ def train(module): # evaluate return { - **metric_dci(dataset, get_repr, num_train=1000, num_test=500, boost_mode='sklearn'), - **metric_mig(dataset, get_repr, num_train=2000), + **metric_dci(dataset, get_repr, num_train=10 if is_test_run() else 1000, num_test=5 if is_test_run() else 500, boost_mode='sklearn'), + **metric_mig(dataset, get_repr, num_train=20 if is_test_run() else 2000), } a_results = train(make_vae(beta=4)) diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 00000000..952d3ab7 --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,3 @@ + +pytest == 6.2.2 +pytest-cov == 2.11.1 diff --git a/requirements.txt b/requirements.txt index 6059eafa..fd565365 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ pip>=21.0 # DATA SCIENCE & ML # ================= -numpy==1.20.0 +numpy==1.19.5 # pandas==1.2.1 # opencv-python==4.5.1.48 diff --git a/tests/test_docs_examples.py b/tests/test_docs_examples.py new file mode 100644 index 00000000..bc8d7fbd --- /dev/null +++ b/tests/test_docs_examples.py @@ -0,0 +1,30 @@ +import sys +from contextlib import contextmanager +import pytest +import os +from glob import glob +from disent.util import _set_test_run + + +@contextmanager +def no_stdout(): + old_stdout = sys.stdout + sys.stdout = open(os.devnull, 'w') + yield + sys.stdout = old_stdout + + +ROOT_DIR = os.path.abspath(__file__ + '/../..') + + +@pytest.mark.parametrize("module", [ + os.path.relpath(path, ROOT_DIR).replace('/', '.')[:-3] + for path in glob(os.path.join(ROOT_DIR, 'docs/examples/**.py')) +]) +def test_docs_examples(capsys, module): + # make sure everything doesnt take 5 years to run + _set_test_run() + # run all the files in the examples folder + import importlib + with no_stdout(): + importlib.import_module(module) From 0d437a8b6ae25473a765a8ca9b57506bec1b9659 Mon Sep 17 00:00:00 2001 From: Nathan Michlo Date: Fri, 5 Feb 2021 03:19:21 +0200 Subject: [PATCH 3/4] github actions + setup.py --- .github/workflows/python-publish.yml | 34 ++++++++++++++++++++ .github/workflows/python-test.yml | 46 ++++++++++++++++++++++++++++ MANIFEST.in | 1 + setup.py | 33 ++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 .github/workflows/python-publish.yml create mode 100644 .github/workflows/python-test.yml create mode 100644 MANIFEST.in create mode 100644 setup.py diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 00000000..0c238706 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,34 @@ +# This workflow will upload a Python Package +# using Twine when a release is created + +name: publish + +on: + release: + types: [created] + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools wheel twine + + - name: Build and publish + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + run: | + python3 setup.py sdist bdist_wheel + python3 -m twine upload dist/* diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml new file mode 100644 index 00000000..9e925ad1 --- /dev/null +++ b/.github/workflows/python-test.yml @@ -0,0 +1,46 @@ +# This workflow will install Python dependencies, +# then run tests over a variety of Python versions. + +name: test + +on: + push: + branches: [ main, dev ] + tags: [ '*' ] + pull_request: + branches: [ main, dev ] + +jobs: + build: + + strategy: + matrix: + os: [ubuntu-latest] # [ubuntu-latest, windows-latest, macos-latest] + python-version: [3.8] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install -r requirements-test.txt + python3 -m pip install -r requirements.txt + + - name: Test with pytest + run: | + python3 -m pytest --cov=disent tests/ + + - uses: codecov/codecov-action@v1 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: true + # codecov automatically merges all generated files + # if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.9 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..f9bd1455 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include requirements.txt diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..ce98080e --- /dev/null +++ b/setup.py @@ -0,0 +1,33 @@ +import setuptools + + +with open("README.md", "r", encoding="utf-8") as file: + long_description = file.read() + +with open('requirements.txt', 'r') as f: + install_requires = (req[0] for req in map(lambda x: x.split('#'), f.readlines())) + install_requires = [req for req in map(str.strip, install_requires) if req] + + +setuptools.setup( + name="disent", + author="Nathan Juraj Michlo", + author_email="NathanJMichlo@gmail.com", + + version="0.0.1.dev1", + python_requires="==3.8", + packages=setuptools.find_packages(), + + install_requires=install_requires, + + url="https://github.com/nmichlo/eunomia", + description="Vae disentanglement framework built with pytorch lightning.", + long_description=long_description, + long_description_content_type="text/markdown", + + classifiers=[ + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Intended Audience :: Science/Research", + ], +) From dd610a55017848194193b742c2fc7280f393acee Mon Sep 17 00:00:00 2001 From: Nathan Michlo Date: Fri, 5 Feb 2021 04:34:13 +0200 Subject: [PATCH 4/4] move tests --- .github/workflows/python-test.yml | 6 ++---- disent/frameworks/_in_progress/__test__msp.py | 19 ------------------- pytest.ini | 4 ---- .../test_state_space.py | 4 ++-- 4 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 disent/frameworks/_in_progress/__test__msp.py delete mode 100644 pytest.ini rename disent/data/util/__test__state_space.py => tests/test_state_space.py (86%) diff --git a/.github/workflows/python-test.yml b/.github/workflows/python-test.yml index 9e925ad1..d755793e 100644 --- a/.github/workflows/python-test.yml +++ b/.github/workflows/python-test.yml @@ -12,14 +12,12 @@ on: jobs: build: - + runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] # [ubuntu-latest, windows-latest, macos-latest] python-version: [3.8] - runs-on: ${{ matrix.os }} - steps: - uses: actions/checkout@v2 @@ -27,7 +25,7 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - + - name: Install dependencies run: | python3 -m pip install --upgrade pip diff --git a/disent/frameworks/_in_progress/__test__msp.py b/disent/frameworks/_in_progress/__test__msp.py deleted file mode 100644 index 08181aa1..00000000 --- a/disent/frameworks/_in_progress/__test__msp.py +++ /dev/null @@ -1,19 +0,0 @@ -import torch -from disent.frameworks._in_progress.msp import MatrixSubspaceProjection - - -def test_msp_orthonormal(): - batch_size, z_size, y_size = 128, 6, 6 - msp = MatrixSubspaceProjection(y_size=y_size, z_size=z_size, init_mode='ortho') - - # new random 'activation' from encoder - z = torch.randn((batch_size, z_size)) - - # reset all labels in the label space - labels = torch.as_tensor([0, 0, 0, 0, 0, 0]) - apply_mask = torch.as_tensor([True, True, True, True, True, True]) - z_mutated = msp.mutated_z(z, labels, apply_mask) - - # assert latent space is also zero after mutating due to 'ortho' init - # typically this is achieved with loss on the M matrix - assert torch.allclose(z_mutated, torch.zeros_like(z_mutated), atol=1e-05) diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 100a343d..00000000 --- a/pytest.ini +++ /dev/null @@ -1,4 +0,0 @@ - -[pytest] -testpaths = disent -python_files = __test__*.py \ No newline at end of file diff --git a/disent/data/util/__test__state_space.py b/tests/test_state_space.py similarity index 86% rename from disent/data/util/__test__state_space.py rename to tests/test_state_space.py index cd82f1da..3c8565e8 100644 --- a/disent/data/util/__test__state_space.py +++ b/tests/test_state_space.py @@ -45,6 +45,6 @@ def test_new_functions(): s = StateSpace([2, 4, 6]) print(np.max([s.sample_factors((2, 2), factor_indices=[2, 1, 2, 2]) for i in range(100)], axis=0)) print(np.max([s.sample_missing_factors([[1, 1], [2, 2]], known_factor_indices=[0, 2]) for i in range(100)], axis=0)) - print(np.min([s.resample_radius([[0, 1, 2], [0, 0, 0]], resample_radius=1, distinct=True) for i in range(1000)], axis=0).tolist()) - print(np.max([s.resample_radius([[0, 1, 2], [0, 0, 0]], resample_radius=1, distinct=True) for i in range(1000)], axis=0).tolist()) + # print(np.min([s.resample_radius([[0, 1, 2], [0, 0, 0]], resample_radius=1, distinct=True) for i in range(1000)], axis=0).tolist()) + # print(np.max([s.resample_radius([[0, 1, 2], [0, 0, 0]], resample_radius=1, distinct=True) for i in range(1000)], axis=0).tolist())