dual-vade 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dual_vade-0.1.0/CITATION.cff +18 -0
- dual_vade-0.1.0/LICENSE +21 -0
- dual_vade-0.1.0/MANIFEST.in +19 -0
- dual_vade-0.1.0/PKG-INFO +117 -0
- dual_vade-0.1.0/README.md +101 -0
- dual_vade-0.1.0/docs/BUNDLE_FORMAT.md +31 -0
- dual_vade-0.1.0/docs/RELEASING.md +113 -0
- dual_vade-0.1.0/docs/TRAINING.md +104 -0
- dual_vade-0.1.0/docs/source_provenance.json +22 -0
- dual_vade-0.1.0/examples/train_local.py +37 -0
- dual_vade-0.1.0/pyproject.toml +21 -0
- dual_vade-0.1.0/release-approval.json +25 -0
- dual_vade-0.1.0/requirements-release.txt +9 -0
- dual_vade-0.1.0/setup.cfg +4 -0
- dual_vade-0.1.0/src/dual_vade/__init__.py +5 -0
- dual_vade-0.1.0/src/dual_vade/_models/__init__.py +1 -0
- dual_vade-0.1.0/src/dual_vade/_models/fullfreq.py +272 -0
- dual_vade-0.1.0/src/dual_vade/_models/lowfreq.py +275 -0
- dual_vade-0.1.0/src/dual_vade/_models/polarfreq.py +494 -0
- dual_vade-0.1.0/src/dual_vade/_models/reim.py +338 -0
- dual_vade-0.1.0/src/dual_vade/inference.py +296 -0
- dual_vade-0.1.0/src/dual_vade/training.py +145 -0
- dual_vade-0.1.0/src/dual_vade.egg-info/PKG-INFO +117 -0
- dual_vade-0.1.0/src/dual_vade.egg-info/SOURCES.txt +26 -0
- dual_vade-0.1.0/src/dual_vade.egg-info/dependency_links.txt +1 -0
- dual_vade-0.1.0/src/dual_vade.egg-info/requires.txt +3 -0
- dual_vade-0.1.0/src/dual_vade.egg-info/top_level.txt +1 -0
- dual_vade-0.1.0/tools/check_release.py +209 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "Please cite this software when using the package."
|
|
3
|
+
type: software
|
|
4
|
+
title: "Dual-VaDE: unsupervised neural image clustering"
|
|
5
|
+
version: 0.1.0
|
|
6
|
+
authors:
|
|
7
|
+
- family-names: Zheng
|
|
8
|
+
given-names: Wugeng
|
|
9
|
+
repository-code: "https://github.com/ZhengWugeng/dual-vade"
|
|
10
|
+
license: MIT
|
|
11
|
+
abstract: >-
|
|
12
|
+
Unsupervised image clustering with VaDE, Freq-VaDE, and Dual-VaDE.
|
|
13
|
+
Train neural networks on user-provided images, fit a post-hoc GMM,
|
|
14
|
+
and save complete local models for later inference.
|
|
15
|
+
keywords:
|
|
16
|
+
- clustering
|
|
17
|
+
- variational autoencoder
|
|
18
|
+
- unsupervised learning
|
dual_vade-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Wugeng Zheng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
include README.md pyproject.toml MANIFEST.in CITATION.cff LICENSE
|
|
2
|
+
include release-approval.json requirements-release.txt
|
|
3
|
+
recursive-include src *.py
|
|
4
|
+
include examples/train_local.py
|
|
5
|
+
include tools/check_release.py
|
|
6
|
+
include docs/BUNDLE_FORMAT.md docs/TRAINING.md docs/source_provenance.json
|
|
7
|
+
include docs/RELEASING.md
|
|
8
|
+
prune model_bundles
|
|
9
|
+
prune research_inputs
|
|
10
|
+
prune .git
|
|
11
|
+
prune .github
|
|
12
|
+
prune .codex
|
|
13
|
+
prune build
|
|
14
|
+
prune dist
|
|
15
|
+
global-exclude __pycache__ *.py[cod]
|
|
16
|
+
global-exclude *.pth *.pt *.npz *.npy *.joblib *.pkl
|
|
17
|
+
# No data, model assets, previews, private credentials, or mailbox files.
|
|
18
|
+
global-exclude *.nii *.nii.gz *.dcm *.png *.jpg *.jpeg *.eml
|
|
19
|
+
global-exclude .env .env.* .pypirc *.pem *.key
|
dual_vade-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dual-vade
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Unsupervised image clustering with trainable VaDE, Freq-VaDE, and Dual-VaDE
|
|
5
|
+
Author: Wugeng Zheng
|
|
6
|
+
Maintainer: Wugeng Zheng
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Repository, https://github.com/ZhengWugeng/dual-vade
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: numpy>=1.24
|
|
13
|
+
Requires-Dist: torch>=2.6
|
|
14
|
+
Requires-Dist: scikit-learn>=1.3
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# Dual-VaDE
|
|
18
|
+
|
|
19
|
+
Unsupervised neural image clustering with VaDE, Freq-VaDE, and Dual-VaDE.
|
|
20
|
+
Author and maintainer: Wugeng Zheng. Code license: MIT.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
python -m pip install dual-vade
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Requires Python 3.10 or newer. Runtime dependencies are NumPy, PyTorch, and
|
|
29
|
+
scikit-learn. For GPU use, install a PyTorch build compatible with your GPU and
|
|
30
|
+
driver before installing this package. Use `device="cpu"` for CPU operation.
|
|
31
|
+
|
|
32
|
+
Give the model your prepared images, train without labels, save the complete
|
|
33
|
+
model, and reload it for inference on new images. There is no required dataset,
|
|
34
|
+
official pretrained model, model registry, or automatic model download.
|
|
35
|
+
The interface encapsulates neural training and GMM fitting; it does not claim
|
|
36
|
+
source-code or weight secrecy.
|
|
37
|
+
|
|
38
|
+
## Train, cluster, and save
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from dual_vade import DualVaDE
|
|
42
|
+
|
|
43
|
+
# images: a float array shaped (N, 128, 128), with values in [0, 1].
|
|
44
|
+
# No ground-truth labels are passed to fit.
|
|
45
|
+
model = DualVaDE(n_clusters=3, device="cuda")
|
|
46
|
+
model.fit(images, epochs=200, verbose=True)
|
|
47
|
+
labels = model.predict(images)
|
|
48
|
+
model.save("my_model")
|
|
49
|
+
|
|
50
|
+
# Later, with no training data or further fitting:
|
|
51
|
+
restored = DualVaDE.load("my_model", device="cpu")
|
|
52
|
+
new_labels = restored.predict(new_images)
|
|
53
|
+
probabilities = restored.predict_proba(new_images)
|
|
54
|
+
embeddings = restored.transform(new_images)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`fit` trains the neural encoders, decoder, and native mixture prior, then fits a
|
|
58
|
+
separate diagonal GMM to the final training posterior means. It is not a GMM-only
|
|
59
|
+
refit. A new constructor starts from random neural weights. Calling `fit` again,
|
|
60
|
+
or after loading your own saved model, continues those weights with a new
|
|
61
|
+
optimizer and final GMM. `fit` returns `self`; `predict` returns cluster IDs.
|
|
62
|
+
The user chooses `n_clusters`; the package does not select it automatically or
|
|
63
|
+
assign semantic names to clusters.
|
|
64
|
+
|
|
65
|
+
## Model choices
|
|
66
|
+
|
|
67
|
+
- `VaDE(n_clusters=3, ...)`: spatial-only.
|
|
68
|
+
- `FreqVaDE(n_clusters=3, spectral="reim", ...)`: frequency-only.
|
|
69
|
+
- `DualVaDE(n_clusters=3, spectral="reim", ...)`: spatial and frequency branches.
|
|
70
|
+
|
|
71
|
+
The four spectral choices are `fullfreq`, `lowfreq`, `polarfreq`, and `reim`.
|
|
72
|
+
ReIm is the default for FreqVaDE and DualVaDE. The old internal FullCplx source
|
|
73
|
+
is named PolarFreq publicly. Together with VaDE, this provides nine training
|
|
74
|
+
configurations. The preserved network implementations are unchanged.
|
|
75
|
+
|
|
76
|
+
## Input requirements
|
|
77
|
+
|
|
78
|
+
Supply finite, real-valued grayscale image arrays shaped `(N,H,W)` or
|
|
79
|
+
`(N,1,H,W)`, already scaled to `[0,1]`. All images must match the constructor's
|
|
80
|
+
`image_size`: default 128, or a power of two at least 64. Training requires at
|
|
81
|
+
least `max(2, n_clusters)` images; practical training usually needs more data.
|
|
82
|
+
|
|
83
|
+
The same format applies to medical or non-medical images; no specific dataset is
|
|
84
|
+
required. RGB images, 3D volumes, and file-directory paths are not direct inputs.
|
|
85
|
+
The package does not silently resize, normalize, register, or convert raw images.
|
|
86
|
+
Use the same preparation at training and prediction time. Train only on your
|
|
87
|
+
training split when evaluating held-out generalization.
|
|
88
|
+
|
|
89
|
+
`examples/train_local.py` reads your prepared `.npy` array or a `.npz` archive
|
|
90
|
+
containing `images`. See `docs/TRAINING.md` for the API, training recipe, and
|
|
91
|
+
lifecycle, and `docs/BUNDLE_FORMAT.md` for saved model contents.
|
|
92
|
+
|
|
93
|
+
## Saved models and scope
|
|
94
|
+
|
|
95
|
+
`save("my_model")` creates a new directory containing `config.json`,
|
|
96
|
+
`model_state.pth`, `clusterer.npz`, and `manifest.json`. A network `.pth` alone
|
|
97
|
+
cannot restore the full clustering pipeline. Loading and prediction never train
|
|
98
|
+
or fit a GMM. The package does not upload your images or saved models.
|
|
99
|
+
|
|
100
|
+
The MIT license covers this software, not rights to anyone's input data. This
|
|
101
|
+
release distributes neither datasets nor official pretrained weights/GMMs.
|
|
102
|
+
Users are responsible for the permissions applicable to their data and any
|
|
103
|
+
models they choose to share. Outputs are cluster IDs, not clinical diagnoses.
|
|
104
|
+
|
|
105
|
+
## Validation
|
|
106
|
+
|
|
107
|
+
The installed code-only rc3 wheel passed HPG job `46757892` on RTX PRO 6000.
|
|
108
|
+
All nine configurations trained on generated non-medical grayscale images,
|
|
109
|
+
without external data or pretrained assets. Neural parameter updates, final GMM,
|
|
110
|
+
held-out inference, saved-model GPU/CPU reload, CPU training, continued training,
|
|
111
|
+
and the CLI example passed. See `docs/TRAINING_VALIDATION_CODE_ONLY.json`.
|
|
112
|
+
The published TestPyPI wheel then passed the same nine-configuration checks in
|
|
113
|
+
job `46761432`, installed into a fresh package venv with the validated HPG
|
|
114
|
+
scientific dependencies. Its origin and API hash were verified, with no source
|
|
115
|
+
checkout used for package imports. See `docs/TESTPYPI_INSTALL_VALIDATION.json`.
|
|
116
|
+
Short functional checks do not establish clustering quality on a user's dataset.
|
|
117
|
+
Historical results are retained in `docs/VALIDATION_STATUS.md`.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Dual-VaDE
|
|
2
|
+
|
|
3
|
+
Unsupervised neural image clustering with VaDE, Freq-VaDE, and Dual-VaDE.
|
|
4
|
+
Author and maintainer: Wugeng Zheng. Code license: MIT.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
python -m pip install dual-vade
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Requires Python 3.10 or newer. Runtime dependencies are NumPy, PyTorch, and
|
|
13
|
+
scikit-learn. For GPU use, install a PyTorch build compatible with your GPU and
|
|
14
|
+
driver before installing this package. Use `device="cpu"` for CPU operation.
|
|
15
|
+
|
|
16
|
+
Give the model your prepared images, train without labels, save the complete
|
|
17
|
+
model, and reload it for inference on new images. There is no required dataset,
|
|
18
|
+
official pretrained model, model registry, or automatic model download.
|
|
19
|
+
The interface encapsulates neural training and GMM fitting; it does not claim
|
|
20
|
+
source-code or weight secrecy.
|
|
21
|
+
|
|
22
|
+
## Train, cluster, and save
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from dual_vade import DualVaDE
|
|
26
|
+
|
|
27
|
+
# images: a float array shaped (N, 128, 128), with values in [0, 1].
|
|
28
|
+
# No ground-truth labels are passed to fit.
|
|
29
|
+
model = DualVaDE(n_clusters=3, device="cuda")
|
|
30
|
+
model.fit(images, epochs=200, verbose=True)
|
|
31
|
+
labels = model.predict(images)
|
|
32
|
+
model.save("my_model")
|
|
33
|
+
|
|
34
|
+
# Later, with no training data or further fitting:
|
|
35
|
+
restored = DualVaDE.load("my_model", device="cpu")
|
|
36
|
+
new_labels = restored.predict(new_images)
|
|
37
|
+
probabilities = restored.predict_proba(new_images)
|
|
38
|
+
embeddings = restored.transform(new_images)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`fit` trains the neural encoders, decoder, and native mixture prior, then fits a
|
|
42
|
+
separate diagonal GMM to the final training posterior means. It is not a GMM-only
|
|
43
|
+
refit. A new constructor starts from random neural weights. Calling `fit` again,
|
|
44
|
+
or after loading your own saved model, continues those weights with a new
|
|
45
|
+
optimizer and final GMM. `fit` returns `self`; `predict` returns cluster IDs.
|
|
46
|
+
The user chooses `n_clusters`; the package does not select it automatically or
|
|
47
|
+
assign semantic names to clusters.
|
|
48
|
+
|
|
49
|
+
## Model choices
|
|
50
|
+
|
|
51
|
+
- `VaDE(n_clusters=3, ...)`: spatial-only.
|
|
52
|
+
- `FreqVaDE(n_clusters=3, spectral="reim", ...)`: frequency-only.
|
|
53
|
+
- `DualVaDE(n_clusters=3, spectral="reim", ...)`: spatial and frequency branches.
|
|
54
|
+
|
|
55
|
+
The four spectral choices are `fullfreq`, `lowfreq`, `polarfreq`, and `reim`.
|
|
56
|
+
ReIm is the default for FreqVaDE and DualVaDE. The old internal FullCplx source
|
|
57
|
+
is named PolarFreq publicly. Together with VaDE, this provides nine training
|
|
58
|
+
configurations. The preserved network implementations are unchanged.
|
|
59
|
+
|
|
60
|
+
## Input requirements
|
|
61
|
+
|
|
62
|
+
Supply finite, real-valued grayscale image arrays shaped `(N,H,W)` or
|
|
63
|
+
`(N,1,H,W)`, already scaled to `[0,1]`. All images must match the constructor's
|
|
64
|
+
`image_size`: default 128, or a power of two at least 64. Training requires at
|
|
65
|
+
least `max(2, n_clusters)` images; practical training usually needs more data.
|
|
66
|
+
|
|
67
|
+
The same format applies to medical or non-medical images; no specific dataset is
|
|
68
|
+
required. RGB images, 3D volumes, and file-directory paths are not direct inputs.
|
|
69
|
+
The package does not silently resize, normalize, register, or convert raw images.
|
|
70
|
+
Use the same preparation at training and prediction time. Train only on your
|
|
71
|
+
training split when evaluating held-out generalization.
|
|
72
|
+
|
|
73
|
+
`examples/train_local.py` reads your prepared `.npy` array or a `.npz` archive
|
|
74
|
+
containing `images`. See `docs/TRAINING.md` for the API, training recipe, and
|
|
75
|
+
lifecycle, and `docs/BUNDLE_FORMAT.md` for saved model contents.
|
|
76
|
+
|
|
77
|
+
## Saved models and scope
|
|
78
|
+
|
|
79
|
+
`save("my_model")` creates a new directory containing `config.json`,
|
|
80
|
+
`model_state.pth`, `clusterer.npz`, and `manifest.json`. A network `.pth` alone
|
|
81
|
+
cannot restore the full clustering pipeline. Loading and prediction never train
|
|
82
|
+
or fit a GMM. The package does not upload your images or saved models.
|
|
83
|
+
|
|
84
|
+
The MIT license covers this software, not rights to anyone's input data. This
|
|
85
|
+
release distributes neither datasets nor official pretrained weights/GMMs.
|
|
86
|
+
Users are responsible for the permissions applicable to their data and any
|
|
87
|
+
models they choose to share. Outputs are cluster IDs, not clinical diagnoses.
|
|
88
|
+
|
|
89
|
+
## Validation
|
|
90
|
+
|
|
91
|
+
The installed code-only rc3 wheel passed HPG job `46757892` on RTX PRO 6000.
|
|
92
|
+
All nine configurations trained on generated non-medical grayscale images,
|
|
93
|
+
without external data or pretrained assets. Neural parameter updates, final GMM,
|
|
94
|
+
held-out inference, saved-model GPU/CPU reload, CPU training, continued training,
|
|
95
|
+
and the CLI example passed. See `docs/TRAINING_VALIDATION_CODE_ONLY.json`.
|
|
96
|
+
The published TestPyPI wheel then passed the same nine-configuration checks in
|
|
97
|
+
job `46761432`, installed into a fresh package venv with the validated HPG
|
|
98
|
+
scientific dependencies. Its origin and API hash were verified, with no source
|
|
99
|
+
checkout used for package imports. See `docs/TESTPYPI_INSTALL_VALIDATION.json`.
|
|
100
|
+
Short functional checks do not establish clustering quality on a user's dataset.
|
|
101
|
+
Historical results are retained in `docs/VALIDATION_STATUS.md`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# User model bundle, schema version 1
|
|
2
|
+
|
|
3
|
+
A complete saved model contains four files:
|
|
4
|
+
|
|
5
|
+
- `config.json`: schema version, model ID, method (`vade`, `freq`, `dual`),
|
|
6
|
+
spectral representation (`none`, `fullfreq`, `lowfreq`, `polarfreq`, `reim`),
|
|
7
|
+
`n_clusters`, `latent_dim`, `image_size`, random seed, and input metadata.
|
|
8
|
+
- `model_state.pth`: a plain PyTorch state dict, including inference buffers.
|
|
9
|
+
- `clusterer.npz`: the fitted post-hoc diagonal GMM, with numeric arrays
|
|
10
|
+
`weights`, `means`, and `covariances`, in fixed component order.
|
|
11
|
+
- `manifest.json`: schema version, SHA-256 checksums, training origin, history,
|
|
12
|
+
GMM details, and parent-bundle identity when training continues from a saved model.
|
|
13
|
+
|
|
14
|
+
Input metadata declares `intensity_range: [0,1]`, `normalization: "none"`,
|
|
15
|
+
and `resize: "none"`. Inputs must already satisfy that contract. This format
|
|
16
|
+
is not restricted to a particular dataset and is not a raw-image preprocessing
|
|
17
|
+
pipeline. Prediction must use the same input preparation as training.
|
|
18
|
+
|
|
19
|
+
The post-hoc clusterer is distinct from the neural network's native mixture prior.
|
|
20
|
+
Both network and clusterer must be retained. Loading never fits either component.
|
|
21
|
+
Training images, labels, subject IDs, per-image embeddings, and optimizer states
|
|
22
|
+
are not saved. A later fit continues weights, not an interrupted optimizer run.
|
|
23
|
+
|
|
24
|
+
The loader verifies checksums, uses `weights_only=True` for the state dict and
|
|
25
|
+
`allow_pickle=False` for the GMM arrays, and validates dimensions and finite
|
|
26
|
+
parameters. A local manifest detects corruption; it is not an independent proof
|
|
27
|
+
that a model from someone else is trustworthy. Load models from trusted sources.
|
|
28
|
+
|
|
29
|
+
`save` never overwrites an existing destination. Saving a loaded model without
|
|
30
|
+
further training copies its four original files byte-for-byte. Further training
|
|
31
|
+
changes only the in-memory model and any new export, not the source folder.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Code-only release checklist
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
Stable release preparation `0.1.0`, author and maintainer Wugeng Zheng. The only supported
|
|
6
|
+
workflow is user images -> full neural fit -> post-hoc GMM -> save/load/predict.
|
|
7
|
+
The three classes expose nine training configurations. There is no official
|
|
8
|
+
pretrained entry point, registry, model hosting requirement, or dataset helper.
|
|
9
|
+
The package contains no dataset, trained weights, or fitted GMM assets.
|
|
10
|
+
|
|
11
|
+
The user confirmed team authorship and the supervisor's request to publish on
|
|
12
|
+
2026-10-19. MIT applies to the package code. This is not a license for user data
|
|
13
|
+
or a blanket license for user-trained model assets. A separate OASIS agreement
|
|
14
|
+
or official-weight redistribution check is not a prerequisite for this code-only
|
|
15
|
+
release, which distributes neither. Users must comply with terms of their own data.
|
|
16
|
+
|
|
17
|
+
`release-approval.json` records code rights and the code-only scope. On 2026-10-19,
|
|
18
|
+
the user separately approved formal PyPI `0.1.0` after TestPyPI validation passed.
|
|
19
|
+
Per-index and per-version allowlists limit authorization to TestPyPI `0.1.0rc3`
|
|
20
|
+
and PyPI `0.1.0`. A later version needs a new approval record. Model redistribution,
|
|
21
|
+
public asset URLs, and pretrained download checks are not release gates. Do not
|
|
22
|
+
upload historical private model bundles.
|
|
23
|
+
|
|
24
|
+
## Private verification
|
|
25
|
+
|
|
26
|
+
The prior rc2 fit API passed HPG job 46754905. Removing download interfaces changes
|
|
27
|
+
the inference source hash, so that record does not authorize the rc3 candidate.
|
|
28
|
+
`tools/run_hpg_training.sbatch` builds and installs the current wheel in a Slurm
|
|
29
|
+
Pro 6000 allocation and trains all nine configurations on generated non-medical
|
|
30
|
+
grayscale images. No reference dataset or pretrained assets are needed. It also
|
|
31
|
+
checks CPU training, model save/reload, continued training from a user's saved
|
|
32
|
+
model, held-out probability contracts, the CLI example, source-wheel rebuild,
|
|
33
|
+
MIT metadata, and expected public-upload refusal.
|
|
34
|
+
|
|
35
|
+
The code-only validation passed as HPG job 46757892 (COMPLETED, exit 0:0).
|
|
36
|
+
`docs/TRAINING_VALIDATION_CODE_ONLY.json` retains the report, and the matching
|
|
37
|
+
source hash/job are recorded in `release-approval.json`. The original wheel and
|
|
38
|
+
source distribution were downloaded, hash-verified, and retained in ignored
|
|
39
|
+
`dist/0.1.0rc3-hpg46757892/`. Citation, MIT metadata, strict Twine checks, source
|
|
40
|
+
rebuild, and the expected refusal of both public upload gates passed as well.
|
|
41
|
+
Short validation is not an accuracy claim. Public test upload is now separately
|
|
42
|
+
authorized. TestPyPI installation and full training/save-load verification then
|
|
43
|
+
passed GPU job 46761432; the evidence is recorded in `testpypi_install_validation`.
|
|
44
|
+
Formal PyPI 0.1.0 is now separately authorized; its final build and upload are pending.
|
|
45
|
+
|
|
46
|
+
## Publication sequence
|
|
47
|
+
|
|
48
|
+
1. Review package contents and Git history for private files, credentials, data,
|
|
49
|
+
old model assets, and required upstream notices before making anything public.
|
|
50
|
+
2. Complete the TestPyPI pending publisher, if still missing. The user enters any
|
|
51
|
+
password/MFA themselves. Never read, store, or request these values.
|
|
52
|
+
3. Configure GitHub environments `testpypi` and `pypi`, matching Trusted Publishing.
|
|
53
|
+
Review branch/environment protections before enabling upload.
|
|
54
|
+
4. With explicit authorization, publish the reviewed code repository and set
|
|
55
|
+
`public_release_authorized` with a non-sensitive approval reference.
|
|
56
|
+
5. Dispatch `publish.yml` on main, target `testpypi`, with confirmation
|
|
57
|
+
`PUBLISH dual-vade testpypi`. TestPyPI is public, not private storage.
|
|
58
|
+
6. In an HPG GPU allocation, install dependencies from PyPI, install this package
|
|
59
|
+
from TestPyPI using `--no-deps`, and verify training, save/load, and prediction.
|
|
60
|
+
Record the job ID and version in `testpypi_install_validation`.
|
|
61
|
+
7. Set the package, citation, and public version to `0.1.0`; rebuild and review.
|
|
62
|
+
Obtain formal release approval, then dispatch target `pypi` with confirmation
|
|
63
|
+
`PUBLISH dual-vade pypi`. Verify clean installation of the published package.
|
|
64
|
+
|
|
65
|
+
The workflow remains manual-only and build-only by default. No push/tag trigger
|
|
66
|
+
publishes anything. No passwords or long-lived API tokens are needed by the
|
|
67
|
+
prepared OIDC upload workflow. Do not replace an already uploaded file/version.
|
|
68
|
+
|
|
69
|
+
## Account setup last observed: 2026-10-19
|
|
70
|
+
|
|
71
|
+
PyPI account `zhengwugeng` registered a pending publisher for project `dual-vade`,
|
|
72
|
+
owner `ZhengWugeng`, repository `dual-vade`, workflow `publish.yml`, environment
|
|
73
|
+
`pypi`. TestPyPI has the matching publisher with environment `testpypi`;
|
|
74
|
+
the user completed sensitive confirmations personally. The GitHub code repository
|
|
75
|
+
is public. Its `testpypi` environment permits only the `main` branch. Workflow run
|
|
76
|
+
39357159452 on commit 145461c successfully built and published `0.1.0rc3` to
|
|
77
|
+
TestPyPI on 2026-10-19 at 22:07 EDT. The formal PyPI job was skipped. Published
|
|
78
|
+
artifact hashes and matching runtime sources were verified; the release receipt
|
|
79
|
+
is `docs/TESTPYPI_RELEASE.json`. HPG index-install validation passed job 46761432;
|
|
80
|
+
its public summary is `docs/TESTPYPI_INSTALL_VALIDATION.json` and its detailed
|
|
81
|
+
training report is `docs/TESTPYPI_TRAINING_VALIDATION.json`.
|
|
82
|
+
Formal PyPI 0.1.0 is now explicitly authorized; build/upload are being prepared.
|
|
83
|
+
|
|
84
|
+
After either index upload, `tools/run_hpg_testpypi.sbatch` must use the exact published version,
|
|
85
|
+
public wheel SHA-256, and validated training API hash. It installs a fresh copy of
|
|
86
|
+
the package from the explicitly selected index (`--no-deps`, no fallback index), reusing the validated
|
|
87
|
+
HPG scientific dependencies. The package itself must resolve inside the new venv,
|
|
88
|
+
not the checkout. The full nine-configuration verification runs on Pro 6000. This
|
|
89
|
+
is not a claim of reinstalling the entire CUDA/PyTorch dependency stack.
|
|
90
|
+
|
|
91
|
+
## HPG runtime reuse detail
|
|
92
|
+
|
|
93
|
+
The selected HPG Python is already a virtual environment. A child venv with
|
|
94
|
+
`--system-site-packages` does not expose the parent venv's installed packages;
|
|
95
|
+
job 46760982 therefore installed the test wheel but failed before training.
|
|
96
|
+
The corrected installer uses a fresh package venv and a .pth entry for only the
|
|
97
|
+
selected parent's site-packages. It records exact dependency locations/versions
|
|
98
|
+
and checks visibility first. User-site and checkout PYTHONPATH stay disabled.
|
|
99
|
+
The package-origin and pinned wheel/API checks remain unchanged. Do not interpret
|
|
100
|
+
successful pip installation alone as successful model/runtime validation.
|
|
101
|
+
|
|
102
|
+
## Formal 0.1.0 authorization
|
|
103
|
+
|
|
104
|
+
The user approved formal publication after job 46761432 passed. Package, citation,
|
|
105
|
+
and public version are now 0.1.0; inference.py, training.py, and all four network
|
|
106
|
+
implementations are unchanged. The formal gate additionally binds the TestPyPI
|
|
107
|
+
installation record to this exact training API hash. The workflow still requires
|
|
108
|
+
manual dispatch on main with `PUBLISH dual-vade pypi`; no push/tag auto-publishes.
|
|
109
|
+
|
|
110
|
+
The existing HPG installer supports `DUAL_VADE_INDEX_TARGET=pypi` as well as its
|
|
111
|
+
original `testpypi` default. It uses a fixed official index/JSON endpoint for each,
|
|
112
|
+
checks the public wheel hash before installation, and reruns all nine configurations
|
|
113
|
+
from the installed package. Formal-index results use `pypi-install-validation.json`.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Neural training and complete-model inference
|
|
2
|
+
|
|
3
|
+
## Public API
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from dual_vade import DualVaDE, FreqVaDE, VaDE
|
|
7
|
+
|
|
8
|
+
model = DualVaDE(
|
|
9
|
+
n_clusters=3, spectral="reim", latent_dim=32, image_size=128,
|
|
10
|
+
device="cuda", batch_size=16, random_state=123,
|
|
11
|
+
)
|
|
12
|
+
model.fit(train_images, epochs=200, learning_rate=1.5e-4, verbose=True)
|
|
13
|
+
labels = model.predict(test_images)
|
|
14
|
+
model.save("my_model")
|
|
15
|
+
restored = DualVaDE.load("my_model", device="cpu")
|
|
16
|
+
labels = restored.predict(test_images)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`FreqVaDE` has the same spectral choices. Use `VaDE(n_clusters=3, ...)` without
|
|
20
|
+
a spectral argument for the spatial baseline. Methods use sklearn-style
|
|
21
|
+
fit/predict calls; full sklearn estimator/cloning/GridSearchCV compatibility is
|
|
22
|
+
not claimed.
|
|
23
|
+
|
|
24
|
+
Only images explicitly passed to `fit` are used for neural optimization or final
|
|
25
|
+
GMM fitting. Do not concatenate held-out/test data into that call. The local training
|
|
26
|
+
example treats every image in its input file as training data; separate your
|
|
27
|
+
held-out data before invoking it. `predict`,
|
|
28
|
+
`predict_proba`, `transform`, `reconstruct`, `save`, and `load` never train a model.
|
|
29
|
+
No external dataset, pretrained weights, labels, or model download is needed.
|
|
30
|
+
Train on your own compatible images; choose the cluster count with n_clusters.
|
|
31
|
+
|
|
32
|
+
## Input contract
|
|
33
|
+
|
|
34
|
+
Prepared finite grayscale arrays, `(N,H,W)` or `(N,1,H,W)`, with intensities in
|
|
35
|
+
[0,1]. Constructor image_size is a power of two >=64, default 128. At least
|
|
36
|
+
`max(2, n_clusters)` training images are required. No registration, resizing,
|
|
37
|
+
RGB conversion, normalization, or raw-volume preprocessing is hidden in fit.
|
|
38
|
+
Maintain the same input convention at prediction time.
|
|
39
|
+
|
|
40
|
+
## What fit actually does
|
|
41
|
+
|
|
42
|
+
1. Put the neural network in training mode and enable all neural parameters.
|
|
43
|
+
2. For each epoch, shuffle only the supplied training images; evaluate the
|
|
44
|
+
variational encoder/decoder and trainable native mixture-prior objective;
|
|
45
|
+
backpropagate, clip gradients, and take AdamW optimizer steps.
|
|
46
|
+
3. After the final neural epoch, switch to evaluation mode and collect posterior
|
|
47
|
+
means for those same training images, in their original supplied order.
|
|
48
|
+
4. Fit a separate sklearn diagonal GMM to those means, with the requested K.
|
|
49
|
+
5. Commit the fitted GMM and training history to the model and return `self`.
|
|
50
|
+
|
|
51
|
+
Defaults: AdamW learning rate 1.5e-4, weight decay 2e-4, gradient norm clip 5,
|
|
52
|
+
cosine decay to min(5e-5, initial learning rate). Loss weights are reconstruction
|
|
53
|
+
BCE 0.2, fused/spatial KL 0.01, native GMM negative log-likelihood 0.05, and mixture
|
|
54
|
+
balance 0.01. Dual FullFreq/ReIm/PolarFreq also use branch-level KL 0.01 per branch;
|
|
55
|
+
LowFreq retains its original concat recipe without those extra branch terms.
|
|
56
|
+
The post-hoc GMM uses float64 embeddings, diagonal covariance, ten restarts,
|
|
57
|
+
reg_covar 1e-4, and at most 200 EM iterations. Non-finite gradients/losses or a
|
|
58
|
+
non-converged final GMM fail rather than silently creating a different K model.
|
|
59
|
+
|
|
60
|
+
This is a shared user-training pipeline, not a promise to reproduce every original
|
|
61
|
+
family/seed's complete epoch-dependent research schedule, early stopping, or paper
|
|
62
|
+
metrics. It does not select checkpoints using test data and does not repeatedly
|
|
63
|
+
refit GMMs during neural epochs. Training quality on a new dataset needs its own
|
|
64
|
+
evaluation.
|
|
65
|
+
|
|
66
|
+
## Lifecycle and reproducibility
|
|
67
|
+
|
|
68
|
+
- New constructors create random networks; inference/save raises NotFittedError
|
|
69
|
+
until fit succeeds. `epochs=0` is rejected: fit cannot be a GMM-only shortcut.
|
|
70
|
+
- A loaded user-saved model already supports inference. Calling fit on
|
|
71
|
+
it continues its current neural weights, using a fresh optimizer and final GMM.
|
|
72
|
+
Repeated fit likewise continues weights; construct a new object for a fresh start.
|
|
73
|
+
- Prediction never updates weights, BatchNorm statistics, or GMM parameters.
|
|
74
|
+
- Basic invalid options/input are rejected before invalidating an existing model.
|
|
75
|
+
Once training starts, failure invalidates inference/save until a successful fit
|
|
76
|
+
or a new load; original on-disk bundles are never changed by fitting.
|
|
77
|
+
- `history_` records epoch losses and learning rates; `n_iter_` records the most
|
|
78
|
+
recent successful fit's epoch count. They survive saving/loading.
|
|
79
|
+
- `random_state` defaults to 123 and controls initialization, shuffling, sampling,
|
|
80
|
+
dropout, and GMM initialization. Seeded initialization/training restores the
|
|
81
|
+
caller's NumPy and relevant Torch RNG states. None uses unseeded randomness.
|
|
82
|
+
|
|
83
|
+
## Saved model
|
|
84
|
+
|
|
85
|
+
`save` creates a new directory and never overwrites an existing one:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
my_model/
|
|
89
|
+
config.json
|
|
90
|
+
model_state.pth
|
|
91
|
+
clusterer.npz
|
|
92
|
+
manifest.json
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The config stores architecture/input settings. The state dict includes network
|
|
96
|
+
parameters and inference buffers. NPZ contains fixed post-hoc weights, means, and
|
|
97
|
+
diagonal covariances. Manifest records SHA-256 values, training origin, epochs,
|
|
98
|
+
optimizer-step count, history, GMM details, and parent bundle identity when relevant.
|
|
99
|
+
It does not store training images, subject IDs, or per-sample embeddings/labels.
|
|
100
|
+
|
|
101
|
+
Loading requires no training dataset, gradient updates, or GMM re-fitting.
|
|
102
|
+
Optimizer/scheduler states are not saved: a later fit continues weights, not an
|
|
103
|
+
exact interrupted optimizer run. Continued-training outputs retain user-trained provenance. Saving an unmodified
|
|
104
|
+
loaded model copies its verified bundle byte-for-byte and preserves its history.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"source": "1_FullFreq-GMVAE/models/vae.py",
|
|
4
|
+
"package_file": "src/dual_vade/_models/fullfreq.py",
|
|
5
|
+
"sha256": "893f5f157cf13b03cdb8eca4b2e0c91e14116ee37050f7f0db3c700c970e78e9"
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"source": "2_LowFreq-GMVAE/models/vae.py",
|
|
9
|
+
"package_file": "src/dual_vade/_models/lowfreq.py",
|
|
10
|
+
"sha256": "329ed5758cf89478f35d2dbefbffe9de94d5589a3c4883e2ceb41fb7a11cb1d7"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"source": "3_ReIm-GMVAE/models/vae.py",
|
|
14
|
+
"package_file": "src/dual_vade/_models/reim.py",
|
|
15
|
+
"sha256": "8332a9d1cac901edcb3e3676d1662335e43a7adef056d755e351c19a8f52668f"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"source": "4_FullCplx-GMVAE/models/vae.py",
|
|
19
|
+
"package_file": "src/dual_vade/_models/polarfreq.py",
|
|
20
|
+
"sha256": "fda98c02074974b0564347b26cc9efd07b37647421d7c439dc8ecef2c3db1caa"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Train a new Dual-VaDE on prepared local images, then save the complete model."""
|
|
2
|
+
import argparse
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from dual_vade import DualVaDE
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
12
|
+
parser.add_argument("--images", type=Path, required=True, help="Training images only: prepared .npy array or .npz with an images key")
|
|
13
|
+
parser.add_argument("--output", type=Path, required=True, help="A new model directory")
|
|
14
|
+
parser.add_argument("--n-clusters", type=int, default=3)
|
|
15
|
+
parser.add_argument("--spectral", choices=("fullfreq", "lowfreq", "polarfreq", "reim"), default="reim")
|
|
16
|
+
parser.add_argument("--epochs", type=int, default=200)
|
|
17
|
+
parser.add_argument("--batch-size", type=int, default=16)
|
|
18
|
+
parser.add_argument("--device", choices=("cpu", "cuda"), default="cpu")
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
args.images = args.images.expanduser()
|
|
21
|
+
args.output = args.output.expanduser()
|
|
22
|
+
if args.output.exists():
|
|
23
|
+
parser.error("Output directory already exists; choose a new path")
|
|
24
|
+
if args.images.suffix == ".npz":
|
|
25
|
+
with np.load(args.images, allow_pickle=False) as saved:
|
|
26
|
+
images = saved["images"]
|
|
27
|
+
else:
|
|
28
|
+
images = np.load(args.images, allow_pickle=False)
|
|
29
|
+
model = DualVaDE(n_clusters=args.n_clusters, spectral=args.spectral,
|
|
30
|
+
device=args.device, batch_size=args.batch_size)
|
|
31
|
+
model.fit(images, epochs=args.epochs, verbose=True)
|
|
32
|
+
model.save(args.output)
|
|
33
|
+
print(f"Saved complete trained model to {args.output}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "dual-vade"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Unsupervised image clustering with trainable VaDE, Freq-VaDE, and Dual-VaDE"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{name = "Wugeng Zheng"}]
|
|
13
|
+
maintainers = [{name = "Wugeng Zheng"}]
|
|
14
|
+
requires-python = ">=3.10"
|
|
15
|
+
dependencies = ["numpy>=1.24", "torch>=2.6", "scikit-learn>=1.3"]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
Repository = "https://github.com/ZhengWugeng/dual-vade"
|
|
19
|
+
|
|
20
|
+
[tool.setuptools.packages.find]
|
|
21
|
+
where = ["src"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"release_scope": "trainable-code-only",
|
|
4
|
+
"code_rights_confirmed": true,
|
|
5
|
+
"public_release_authorized": true,
|
|
6
|
+
"approval_record": "2026-10-19: user confirmed team authorship and supervisor instruction to publish code for training user-provided images, with no official model or dataset distribution. User approved public GitHub and TestPyPI 0.1.0rc3, then explicitly approved formal PyPI 0.1.0 after successful HPG TestPyPI install/training validation 46761432. Authorization is limited to these versions.",
|
|
7
|
+
"testpypi_install_validation": {
|
|
8
|
+
"hpg_job_id": "46761432",
|
|
9
|
+
"version": "0.1.0rc3",
|
|
10
|
+
"training_api_sha256": "09f16c8afa85a5ffd999df68b6efeb216b41a97ed9d57f2688ec513421c14459",
|
|
11
|
+
"wheel_sha256": "19343ed5f0b781e0c0fda15711c14eeac811b1283bff3e7a736736feec8377a6"
|
|
12
|
+
},
|
|
13
|
+
"training_validation": {
|
|
14
|
+
"hpg_job_id": "46757892",
|
|
15
|
+
"training_api_sha256": "09f16c8afa85a5ffd999df68b6efeb216b41a97ed9d57f2688ec513421c14459"
|
|
16
|
+
},
|
|
17
|
+
"authorized_publish_targets": [
|
|
18
|
+
"testpypi",
|
|
19
|
+
"pypi"
|
|
20
|
+
],
|
|
21
|
+
"authorized_publish_versions": {
|
|
22
|
+
"testpypi": "0.1.0rc3",
|
|
23
|
+
"pypi": "0.1.0"
|
|
24
|
+
}
|
|
25
|
+
}
|