augmask 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.
Files changed (36) hide show
  1. augmask-0.1.0/LICENSE +21 -0
  2. augmask-0.1.0/PKG-INFO +326 -0
  3. augmask-0.1.0/README.md +286 -0
  4. augmask-0.1.0/augmask/__init__.py +5 -0
  5. augmask-0.1.0/augmask/augment.py +192 -0
  6. augmask-0.1.0/augmask/baselines/__init__.py +1 -0
  7. augmask-0.1.0/augmask/baselines/forestdiff.py +73 -0
  8. augmask-0.1.0/augmask/baselines/miwae.py +110 -0
  9. augmask-0.1.0/augmask/data/__init__.py +7 -0
  10. augmask-0.1.0/augmask/data/datasets.py +190 -0
  11. augmask-0.1.0/augmask/data/missingness.py +96 -0
  12. augmask-0.1.0/augmask/data/scenario.py +221 -0
  13. augmask-0.1.0/augmask/data/schema.py +60 -0
  14. augmask-0.1.0/augmask/data/split.py +32 -0
  15. augmask-0.1.0/augmask/data/transforms.py +78 -0
  16. augmask-0.1.0/augmask/evaluate.py +164 -0
  17. augmask-0.1.0/augmask/models/__init__.py +1 -0
  18. augmask-0.1.0/augmask/models/cdtd.py +301 -0
  19. augmask-0.1.0/augmask/models/common.py +63 -0
  20. augmask-0.1.0/augmask/models/tabddpm.py +211 -0
  21. augmask-0.1.0/augmask/models/tabdiff.py +223 -0
  22. augmask-0.1.0/augmask/run.py +122 -0
  23. augmask-0.1.0/augmask.egg-info/PKG-INFO +326 -0
  24. augmask-0.1.0/augmask.egg-info/SOURCES.txt +34 -0
  25. augmask-0.1.0/augmask.egg-info/dependency_links.txt +1 -0
  26. augmask-0.1.0/augmask.egg-info/requires.txt +25 -0
  27. augmask-0.1.0/augmask.egg-info/top_level.txt +1 -0
  28. augmask-0.1.0/pyproject.toml +53 -0
  29. augmask-0.1.0/setup.cfg +4 -0
  30. augmask-0.1.0/tests/test_cdtd.py +61 -0
  31. augmask-0.1.0/tests/test_evaluate.py +42 -0
  32. augmask-0.1.0/tests/test_missingness_baselines.py +89 -0
  33. augmask-0.1.0/tests/test_pipeline.py +198 -0
  34. augmask-0.1.0/tests/test_run.py +25 -0
  35. augmask-0.1.0/tests/test_tabddpm.py +67 -0
  36. augmask-0.1.0/tests/test_tabdiff.py +53 -0
augmask-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jungkyu Kim, Taeyoung Park, Kibok Lee
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.
augmask-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,326 @@
1
+ Metadata-Version: 2.4
2
+ Name: augmask
3
+ Version: 0.1.0
4
+ Summary: AugMask: training diffusion models on incomplete tabular data via stochastic augmentation and masking (ICML 2026)
5
+ Author: Jungkyu Kim, Taeyoung Park, Kibok Lee
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/normal-kim/AugMask
8
+ Project-URL: Paper, https://arxiv.org/abs/2606.03347
9
+ Keywords: diffusion models,tabular data,missing data,data augmentation,synthetic data
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: <3.13,>=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: numpy<2,>=1.24
21
+ Requires-Dist: pandas>=2.0
22
+ Requires-Dist: scipy>=1.10
23
+ Requires-Dist: scikit-learn>=1.3
24
+ Requires-Dist: lightgbm>=4.0
25
+ Provides-Extra: torch
26
+ Requires-Dist: torch>=2.1; extra == "torch"
27
+ Provides-Extra: data
28
+ Requires-Dist: xlrd>=2.0; extra == "data"
29
+ Provides-Extra: eval
30
+ Requires-Dist: xgboost>=2.0; extra == "eval"
31
+ Requires-Dist: sdmetrics>=0.17; extra == "eval"
32
+ Provides-Extra: baselines
33
+ Requires-Dist: ForestDiffusion==1.0.6; extra == "baselines"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=7; extra == "dev"
36
+ Requires-Dist: matplotlib>=3.7; extra == "dev"
37
+ Provides-Extra: all
38
+ Requires-Dist: augmask[baselines,data,dev,eval,torch]; extra == "all"
39
+ Dynamic: license-file
40
+
41
+ # AugMask
42
+
43
+ Official code for **AugMask: Training Diffusion Models on Incomplete Tabular Data via Stochastic Augmentation and Masking** (ICML 2026).
44
+ Jungkyu Kim, Taeyoung Park, Kibok Lee. [[arXiv]](https://arxiv.org/abs/2606.03347) [[OpenReview]](https://openreview.net/forum?id=Ot8lh7vvGe)
45
+
46
+ ## 💡 Why AugMask
47
+
48
+ **Diffusion models for tabular data are built for complete rows**, but real tables have missing entries. The usual workarounds fail:
49
+
50
+ - **Constant fills become data.** A model trained on mean-filled rows *treats the fills as observed values* and learns to reproduce them. Below, the filled rows collapse onto a cross at the column means.
51
+ - **Masking the loss is not enough.** Filled entries left out of the loss are *still read as inputs*, so the model learns from rows that real data rarely contains, such as a hot day paired with the average dew point.
52
+
53
+ AugMask fixes both:
54
+
55
+ 1. **Augment.** A missing entry is *unrealized data that could take many different values*, so AugMask *samples* it instead of imputing it deterministically. Each missing entry $`x_j`$ is drawn from a conditional model of its column, $`x_j \sim \hat{p}_j(x_j \mid x_{\mathrm{obs}}, m)`$: the augmenter receives the row's observed values $`x_{\mathrm{obs}}`$ and its missingness mask $`m`$ as inputs. This repository uses LGBM-S, lightweight per-column LightGBM models, as the augmenter; the sampled rows resemble the complete data (right panel).
56
+ 2. **Mask.** The denoising loss is computed on observed entries only, so the sampled values serve as *context, never as targets*.
57
+
58
+ <picture>
59
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/normal-kim/AugMask/main/assets/overview_dark.png">
60
+ <img src="https://raw.githubusercontent.com/normal-kim/AugMask/main/assets/overview_light.png" alt="Mean filling collapses rows onto a cross at the column means; augmented fills follow the joint distribution. Inputs are filled, and the loss uses observed entries only." width="100%">
61
+ </picture>
62
+
63
+ AugMask needs no change to the model itself. This repository applies it to three diffusion models: [TabDDPM](https://proceedings.mlr.press/v202/kotelnikov23a.html) (ICML 2023; [code](https://github.com/yandex-research/tab-ddpm)), [CDTD](https://proceedings.iclr.cc/paper_files/paper/2025/hash/61f4e5747b1b753cb35546b15d981f76-Abstract-Conference.html) (ICLR 2025; [code](https://github.com/muellermarkus/cdtd)) and [TabDiff](https://proceedings.iclr.cc/paper_files/paper/2025/hash/5c882988ce5fac487974ee4f415b96a9-Abstract-Conference.html) (ICLR 2025; [code](https://github.com/MinkaiXu/TabDiff)).
64
+
65
+ ## 📦 Install
66
+
67
+ Python 3.10–3.12.
68
+
69
+ ```bash
70
+ pip install augmask # the augmentation (LGBM-S) alone
71
+ pip install "augmask[all]" # plus the diffusion backbones, baselines and evaluation
72
+ ```
73
+
74
+ To run the experiments, clone this repository and create the exact environment used for the results:
75
+
76
+ ```bash
77
+ git clone https://github.com/normal-kim/AugMask.git && cd AugMask
78
+ conda env create -f environment.yml && conda activate augmask
79
+ ```
80
+
81
+ ## 🧩 Use AugMask with your own model
82
+
83
+ ```python
84
+ from augmask import Encoded, LGBMSAugmenter
85
+
86
+ train = Encoded(X_num, X_cat, M_num, M_cat)
87
+ fills = [LGBMSAugmenter(draw=k).augment(train, [], n_categories)[0] for k in range(5)]
88
+
89
+ # train your denoiser on fills[step % 5], with the loss restricted to observed entries
90
+ loss = (per_entry_loss * mask).sum() / mask.sum()
91
+ ```
92
+
93
+ The augmenter takes an encoded table with `n` rows:
94
+
95
+ | Input | Shape | Type | Content |
96
+ |---|---|---|---|
97
+ | `X_num` | `(n, d_num)` | float | numerical columns, quantile-normal transformed; `NaN` where missing |
98
+ | `X_cat` | `(n, d_cat)` | int | categorical columns, integer encoded as `0 … K_j−1`; `−1` where missing |
99
+ | `M_num`, `M_cat` | same as above | bool | `True` where observed |
100
+ | `n_categories` | `d_cat` | list of int | number of categories `K_j` of each categorical column |
101
+
102
+ `augment` returns `X_num` and `X_cat` with every missing entry filled; each `draw` gives an independent fill, with the observed entries unchanged. In this repository, the backbones receive one fill per training step: `X_num` (float32, standardized) and `X_cat` (int64 codes), together with the masks. The target is one of the columns (the first numerical column for regression, the first categorical column for classification).
103
+
104
+ ### Your own augmenter
105
+
106
+ Any object with an `augment` method can replace LGBM-S:
107
+
108
+ ```python
109
+ from augmask.run import run
110
+
111
+ class MyAugmenter:
112
+ def augment(self, train, others, n_categories):
113
+ # fit on `train` (an Encoded table); return [(X_num, X_cat), ...] for `train` and each of `others`,
114
+ # with every missing entry filled: floats in X_num, codes 0 … K_j−1 in X_cat.
115
+ # It is called once per fill (5 by default), so return a new random draw on each call.
116
+ ...
117
+
118
+ run("adult", "mcar", 0.5, "tabdiff-augmask", augmenter=MyAugmenter())
119
+ ```
120
+
121
+ ## 🚀 Run
122
+
123
+ Datasets are downloaded from UCI on first use.
124
+
125
+ ```bash
126
+ # one method on one scenario: train, sample and evaluate -> runs/<dataset>/<mechanism>/p<p>/seed<s>/<method>.{csv,json}
127
+ python -m augmask.run --dataset adult --mechanism mcar --p 0.5 --method cdtd-augmask
128
+
129
+ # all experiments (6 datasets x 5 missing rates x 3 seeds x 14 methods) followed by the summary tables
130
+ bash scripts/run_all.sh 0 # on GPU 0
131
+ bash scripts/run_all.sh 1 beijing news # or split the datasets across GPUs and servers,
132
+ python scripts/make_tables.py # then summarise the merged runs/
133
+ ```
134
+
135
+ Methods are `{tabddpm,cdtd,tabdiff}-{noaug,meanmask,augfull,augmask}`, plus the baselines `forestdiff` and `miwae`.
136
+
137
+ | Strategy | Missing inputs filled with | Loss on |
138
+ |---|---|---|
139
+ | `noaug` | train mean / mode | all entries |
140
+ | `meanmask` | train mean / mode | observed entries (similar to [MissDiff](https://arxiv.org/abs/2307.00467); Ouyang et al., 2023) |
141
+ | `augfull` (ours) | samples from the augmenter (LGBM-S) | all entries |
142
+ | `augmask` (ours) | samples from the augmenter (LGBM-S) | observed entries |
143
+
144
+ **Alternating fills.** For improved performance, this implementation gives `augfull` and `augmask` 5 independent fills of the missing inputs and alternates between them during training, one per step (`--fills 5`, the default). `--fills 1` uses the single fill of the paper. The gain is largest at high missing rates.
145
+
146
+ **Masks and fills.** The first run of a scenario saves its missingness masks and augmented fills to `artifacts/<dataset>/<mechanism>/p<p>/seed<s>.npz`; later runs of the same scenario reuse them, so LGBM-S runs once per scenario rather than once per method.
147
+
148
+ **Runtime.** LGBM-S takes a few seconds per fill (fit on the train split, fill train and validation; AMD Ryzen 9 5950X, 32 threads); the default 5 fills take about five times as long, once per scenario:
149
+
150
+ | Missing | Adult | Default | Shoppers | Magic | Beijing | News |
151
+ |---|---:|---:|---:|---:|---:|---:|
152
+ | 10% | 3.5 s | 2.0 s | 1.3 s | 0.3 s | 2.9 s | 5.7 s |
153
+ | 50% | 4.5 s | 2.3 s | 1.5 s | 0.3 s | 4.1 s | 4.9 s |
154
+ | 90% | 6.0 s | 2.0 s | 1.1 s | 0.3 s | 5.8 s | 2.9 s |
155
+
156
+ ## 📊 Reference results
157
+
158
+ Adult and Beijing, MCAR with 50% and 90% missing, one seed, averaged over the four scenarios (×100, best in bold). Utility is downstream task performance: a model trained on synthetic rows and tested on real ones, relative to training on real rows.
159
+
160
+ - **Missing-aware:** [ForestDiff](https://proceedings.mlr.press/v238/jolicoeur-martineau24a.html) (AISTATS 2024) and [MIWAE](https://proceedings.mlr.press/v97/mattei19a.html) (ICML 2019).
161
+ - **Missing-unaware**, trained with the four strategies above: [TabDDPM](https://proceedings.mlr.press/v202/kotelnikov23a.html) (ICML 2023), [CDTD](https://proceedings.iclr.cc/paper_files/paper/2025/hash/61f4e5747b1b753cb35546b15d981f76-Abstract-Conference.html) (ICLR 2025) and [TabDiff](https://proceedings.iclr.cc/paper_files/paper/2025/hash/5c882988ce5fac487974ee4f415b96a9-Abstract-Conference.html) (ICLR 2025).
162
+
163
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
164
+ |---|---:|---:|---:|---:|---:|
165
+ | TabDDPM + NoAug | 68.2 | 54.7 | 45.1 | 8.7 | 66.0 |
166
+ | TabDDPM + MeanMask | 94.9 | 88.5 | 91.9 | 28.3 | 74.5 |
167
+ | TabDDPM + AugFull (ours) | 95.7 | 90.2 | 93.5 | 31.7 | 75.6 |
168
+ | TabDDPM + AugMask (ours) | 94.1 | 88.5 | 90.8 | 35.6 | 77.9 |
169
+ | CDTD + NoAug | 68.5 | 55.7 | 44.5 | 9.1 | 66.2 |
170
+ | CDTD + MeanMask | 93.6 | 84.5 | 93.2 | 23.0 | 73.6 |
171
+ | CDTD + AugFull (ours) | 95.5 | 91.0 | 91.2 | 33.2 | 77.2 |
172
+ | CDTD + AugMask (ours) | 97.8 | 93.4 | 98.0 | 37.8 | 76.7 |
173
+ | TabDiff + NoAug | 66.9 | 54.3 | 43.8 | 9.0 | 67.9 |
174
+ | TabDiff + MeanMask | 96.6 | 88.8 | 95.0 | 29.3 | 73.3 |
175
+ | TabDiff + AugFull (ours) | 95.7 | 91.4 | 93.1 | 32.3 | 76.6 |
176
+ | TabDiff + AugMask (ours) | **98.1** | **94.4** | **98.4** | **39.1** | **79.1** |
177
+ | ForestDiff | 69.7 | 66.7 | 65.6 | 22.4 | 66.4 |
178
+ | MIWAE | 97.5 | 85.2 | 97.1 | 29.1 | 72.4 |
179
+
180
+ <details>
181
+ <summary>Per scenario</summary>
182
+
183
+ **Adult, 50% missing**
184
+
185
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
186
+ |---|---:|---:|---:|---:|---:|
187
+ | TabDDPM + NoAug | 81.9 | 71.9 | 51.5 | 15.5 | 95.9 |
188
+ | TabDDPM + MeanMask | 94.7 | 89.7 | 90.8 | 30.9 | 97.3 |
189
+ | TabDDPM + AugFull (ours) | 97.2 | 92.3 | 97.3 | 35.1 | 96.8 |
190
+ | TabDDPM + AugMask (ours) | 96.2 | 92.6 | 95.7 | 43.3 | 97.2 |
191
+ | CDTD + NoAug | 83.3 | 74.2 | 54.1 | 16.6 | 96.4 |
192
+ | CDTD + MeanMask | 95.3 | 92.0 | 93.1 | 34.4 | 96.8 |
193
+ | CDTD + AugFull (ours) | 96.7 | 94.0 | 94.8 | 37.9 | **97.9** |
194
+ | CDTD + AugMask (ours) | 98.9 | 97.2 | 97.3 | **46.0** | 97.9 |
195
+ | TabDiff + NoAug | 81.0 | 72.3 | 50.8 | 15.9 | 95.1 |
196
+ | TabDiff + MeanMask | 96.2 | 93.6 | 96.1 | 35.7 | 97.5 |
197
+ | TabDiff + AugFull (ours) | 97.1 | 94.2 | 98.1 | 36.5 | 97.2 |
198
+ | TabDiff + AugMask (ours) | **99.1** | **97.8** | **99.4** | 45.4 | 97.7 |
199
+ | ForestDiff | 90.7 | 91.3 | 95.4 | 34.8 | 97.0 |
200
+ | MIWAE | 97.3 | 94.9 | 98.3 | 30.8 | 95.5 |
201
+
202
+ **Adult, 90% missing**
203
+
204
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
205
+ |---|---:|---:|---:|---:|---:|
206
+ | TabDDPM + NoAug | 66.3 | 46.2 | 10.9 | 1.8 | 71.7 |
207
+ | TabDDPM + MeanMask | 94.6 | 85.4 | 92.9 | 11.8 | 93.7 |
208
+ | TabDDPM + AugFull (ours) | 94.6 | 85.0 | 95.8 | 13.6 | 92.4 |
209
+ | TabDDPM + AugMask (ours) | 93.3 | 85.0 | 90.1 | 30.0 | 94.9 |
210
+ | CDTD + NoAug | 69.0 | 50.1 | 11.9 | 1.9 | 70.0 |
211
+ | CDTD + MeanMask | 93.2 | 75.3 | 90.3 | 9.1 | 87.5 |
212
+ | CDTD + AugFull (ours) | 93.9 | 84.2 | 91.2 | 14.0 | 92.6 |
213
+ | CDTD + AugMask (ours) | **97.9** | 90.9 | **99.1** | 30.9 | 94.0 |
214
+ | TabDiff + NoAug | 65.9 | 47.1 | 10.5 | 1.8 | 78.9 |
215
+ | TabDiff + MeanMask | 95.5 | 87.2 | 90.9 | 10.3 | 91.6 |
216
+ | TabDiff + AugFull (ours) | 94.4 | 85.1 | 94.9 | 14.1 | 93.8 |
217
+ | TabDiff + AugMask (ours) | 97.4 | **91.1** | 97.5 | **33.0** | **95.4** |
218
+ | ForestDiff | 36.3 | 26.9 | 1.4 | 0.3 | 72.3 |
219
+ | MIWAE | 97.6 | 89.1 | 96.5 | 14.2 | 92.0 |
220
+
221
+ **Beijing, 50% missing**
222
+
223
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
224
+ |---|---:|---:|---:|---:|---:|
225
+ | TabDDPM + NoAug | 74.2 | 64.1 | 70.3 | 15.9 | 50.5 |
226
+ | TabDDPM + MeanMask | 95.3 | 92.1 | 92.0 | 37.4 | 58.0 |
227
+ | TabDDPM + AugFull (ours) | 97.6 | 94.8 | 97.2 | 39.8 | 62.3 |
228
+ | TabDDPM + AugMask (ours) | 97.4 | 93.0 | 94.9 | 39.0 | 65.5 |
229
+ | CDTD + NoAug | 72.2 | 64.8 | 68.1 | 16.6 | 56.4 |
230
+ | CDTD + MeanMask | 95.2 | 92.7 | 97.0 | 39.6 | 64.6 |
231
+ | CDTD + AugFull (ours) | 97.5 | 95.5 | 95.2 | 42.1 | 66.0 |
232
+ | CDTD + AugMask (ours) | 98.5 | 96.0 | 98.5 | **42.9** | 62.8 |
233
+ | TabDiff + NoAug | 72.2 | 64.4 | 69.2 | 16.4 | 53.5 |
234
+ | TabDiff + MeanMask | 97.2 | 94.7 | 97.9 | 38.6 | 59.9 |
235
+ | TabDiff + AugFull (ours) | 97.7 | 95.8 | 95.8 | 40.5 | 64.1 |
236
+ | TabDiff + AugMask (ours) | **98.8** | **96.3** | **98.9** | 40.8 | **67.9** |
237
+ | ForestDiff | 91.9 | 90.6 | 93.1 | 35.1 | 62.4 |
238
+ | MIWAE | 97.8 | 87.1 | 97.2 | 39.2 | 58.3 |
239
+
240
+ **Beijing, 90% missing**
241
+
242
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
243
+ |---|---:|---:|---:|---:|---:|
244
+ | TabDDPM + NoAug | 50.4 | 36.6 | 47.7 | 1.6 | 46.1 |
245
+ | TabDDPM + MeanMask | 94.9 | 86.6 | 91.7 | 33.3 | 49.1 |
246
+ | TabDDPM + AugFull (ours) | 93.6 | 88.6 | 83.8 | 38.1 | 50.9 |
247
+ | TabDDPM + AugMask (ours) | 89.6 | 83.4 | 82.7 | 30.1 | 53.9 |
248
+ | CDTD + NoAug | 49.5 | 33.7 | 43.8 | 1.0 | 42.0 |
249
+ | CDTD + MeanMask | 90.5 | 78.1 | 92.3 | 9.0 | 45.4 |
250
+ | CDTD + AugFull (ours) | 93.7 | 90.4 | 83.6 | **38.8** | 52.2 |
251
+ | CDTD + AugMask (ours) | 95.6 | 89.6 | 97.0 | 31.6 | 52.1 |
252
+ | TabDiff + NoAug | 48.6 | 33.7 | 44.9 | 1.8 | 44.3 |
253
+ | TabDiff + MeanMask | 97.4 | 79.8 | 95.2 | 32.3 | 44.3 |
254
+ | TabDiff + AugFull (ours) | 93.8 | 90.8 | 83.7 | 38.2 | 51.2 |
255
+ | TabDiff + AugMask (ours) | 97.0 | **92.6** | **98.0** | 37.3 | **55.6** |
256
+ | ForestDiff | 59.7 | 58.1 | 72.4 | 19.6 | 33.7 |
257
+ | MIWAE | **97.5** | 69.5 | 96.2 | 32.3 | 43.9 |
258
+
259
+ </details>
260
+
261
+ ## 🔧 Pipeline
262
+
263
+ 1. **Collect** the six UCI tables (Adult, Default, Shoppers, Magic, Beijing, News).
264
+ 2. **Split** each table 60/20/20 into train, validation and test.
265
+ 3. **Mask** train and validation entries (the target included) completely at random (MCAR) or depending on the other columns (MAR); the test split stays complete.
266
+ 4. **Encode**, using the observed train entries: numerical columns are quantile-normal transformed, categorical columns are integer encoded.
267
+ 5. **Fill** the missing entries with the train mean / mode, or *sample* them with the augmenter (LGBM-S by default; 5 fills, alternated during training).
268
+ 6. **Standardize** the numerical columns.
269
+ 7. **Train** the backbone and sample as many rows as the train split.
270
+ 8. **Map** the samples back to the original columns and values.
271
+ 9. **Evaluate** against the complete train and test splits: Shape, Trend, α-Precision, β-Recall and Utility (downstream task performance), following [TabSyn](https://proceedings.iclr.cc/paper_files/paper/2024/hash/e9750610639c3e7a849cff746bf60dbd-Abstract-Conference.html) (ICLR 2024).
272
+
273
+ <details>
274
+ <summary>Repository structure</summary>
275
+
276
+ ```
277
+ augmask/
278
+ ├── augment.py # step 5: LGBM-S (Algorithm 1), mean/mode fill, the Augmenter interface
279
+ ├── run.py # one method on one scenario: build, train, sample, evaluate
280
+ ├── evaluate.py # step 9: Shape, Trend, α-Precision, β-Recall, Utility
281
+ ├── data/
282
+ │ ├── datasets.py # step 1: download and clean the six UCI tables
283
+ │ ├── split.py # step 2: fixed stratified 60/20/20 split
284
+ │ ├── missingness.py # step 3: MCAR and MAR masks
285
+ │ ├── transforms.py # steps 4 and 6: quantile-normal, integer codes, standardization
286
+ │ ├── scenario.py # steps 3-6 and 8 for one scenario; saves and loads masks and fills
287
+ │ └── schema.py # column types, target and task
288
+ ├── models/ # step 7
289
+ │ ├── tabddpm.py, cdtd.py, tabdiff.py # the backbones, each with an optional loss mask
290
+ │ └── common.py # seeding, batching (one fill per step), EMA
291
+ └── baselines/
292
+ ├── forestdiff.py # the official ForestDiffusion package, wrapped
293
+ └── miwae.py
294
+ scripts/
295
+ ├── run_all.sh # all experiments, then the summary tables
296
+ ├── make_tables.py # rank tables with Wilcoxon markers
297
+ └── plot_overview.py # the figure above
298
+ tests/ # pipeline, missingness, baselines, evaluation, each backbone, the runner
299
+ ```
300
+
301
+ </details>
302
+
303
+ ## 📝 Notes on this release
304
+
305
+ The data pipeline was rebuilt for this release (60/20/20 split, a new MAR generator), so numbers differ slightly from the paper. Most model and training settings follow Appendix C.6 of the paper. The exception is alternating fills: AugFull and AugMask alternate between 5 fills of the missing inputs during training, which performs better at high missing rates in this implementation (`--fills 1` restores the paper's single fill). LGBM-S follows Algorithm 1; `LGBMSAugmenter(log_var_offset=augmask.augment.LOG_CHI2_BIAS)` optionally corrects the downward bias of its variance model.
306
+
307
+ LGBM-S is a per-coordinate augmenter: it samples each missing entry independently given the observed values and the mask, so it does not model the dependence between entries that are missing together. An augmenter that samples the missing coordinates jointly is a natural direction for future work, and can be plugged in as described in [Your own augmenter](#your-own-augmenter).
308
+
309
+ ## 📚 Citation
310
+
311
+ ```bibtex
312
+ @inproceedings{kim2026augmask,
313
+ title = {{AugMask}: Training Diffusion Models on Incomplete Tabular Data via Stochastic Augmentation and Masking},
314
+ author = {Kim, Jungkyu and Park, Taeyoung and Lee, Kibok},
315
+ booktitle = {Proceedings of the 43rd International Conference on Machine Learning},
316
+ series = {Proceedings of Machine Learning Research},
317
+ volume = {306},
318
+ year = {2026}
319
+ }
320
+ ```
321
+
322
+ ## 🙏 Acknowledgements
323
+
324
+ The backbones are adapted from [TabDDPM](https://github.com/yandex-research/tab-ddpm), [CDTD](https://github.com/muellermarkus/cdtd) and [TabDiff](https://github.com/MinkaiXu/TabDiff) (MIT). The metrics follow [TabSyn](https://github.com/amazon-science/tabsyn) (Apache-2.0), α-Precision/β-Recall follow [synthcity](https://github.com/vanderschaarlab/synthcity), and MIWAE follows [hyperimpute](https://github.com/vanderschaarlab/hyperimpute) (MIT). ForestDiff uses the official [ForestDiffusion](https://github.com/SamsungSAILMontreal/ForestDiffusion) package.
325
+
326
+ Released under the MIT License.
@@ -0,0 +1,286 @@
1
+ # AugMask
2
+
3
+ Official code for **AugMask: Training Diffusion Models on Incomplete Tabular Data via Stochastic Augmentation and Masking** (ICML 2026).
4
+ Jungkyu Kim, Taeyoung Park, Kibok Lee. [[arXiv]](https://arxiv.org/abs/2606.03347) [[OpenReview]](https://openreview.net/forum?id=Ot8lh7vvGe)
5
+
6
+ ## 💡 Why AugMask
7
+
8
+ **Diffusion models for tabular data are built for complete rows**, but real tables have missing entries. The usual workarounds fail:
9
+
10
+ - **Constant fills become data.** A model trained on mean-filled rows *treats the fills as observed values* and learns to reproduce them. Below, the filled rows collapse onto a cross at the column means.
11
+ - **Masking the loss is not enough.** Filled entries left out of the loss are *still read as inputs*, so the model learns from rows that real data rarely contains, such as a hot day paired with the average dew point.
12
+
13
+ AugMask fixes both:
14
+
15
+ 1. **Augment.** A missing entry is *unrealized data that could take many different values*, so AugMask *samples* it instead of imputing it deterministically. Each missing entry $`x_j`$ is drawn from a conditional model of its column, $`x_j \sim \hat{p}_j(x_j \mid x_{\mathrm{obs}}, m)`$: the augmenter receives the row's observed values $`x_{\mathrm{obs}}`$ and its missingness mask $`m`$ as inputs. This repository uses LGBM-S, lightweight per-column LightGBM models, as the augmenter; the sampled rows resemble the complete data (right panel).
16
+ 2. **Mask.** The denoising loss is computed on observed entries only, so the sampled values serve as *context, never as targets*.
17
+
18
+ <picture>
19
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/normal-kim/AugMask/main/assets/overview_dark.png">
20
+ <img src="https://raw.githubusercontent.com/normal-kim/AugMask/main/assets/overview_light.png" alt="Mean filling collapses rows onto a cross at the column means; augmented fills follow the joint distribution. Inputs are filled, and the loss uses observed entries only." width="100%">
21
+ </picture>
22
+
23
+ AugMask needs no change to the model itself. This repository applies it to three diffusion models: [TabDDPM](https://proceedings.mlr.press/v202/kotelnikov23a.html) (ICML 2023; [code](https://github.com/yandex-research/tab-ddpm)), [CDTD](https://proceedings.iclr.cc/paper_files/paper/2025/hash/61f4e5747b1b753cb35546b15d981f76-Abstract-Conference.html) (ICLR 2025; [code](https://github.com/muellermarkus/cdtd)) and [TabDiff](https://proceedings.iclr.cc/paper_files/paper/2025/hash/5c882988ce5fac487974ee4f415b96a9-Abstract-Conference.html) (ICLR 2025; [code](https://github.com/MinkaiXu/TabDiff)).
24
+
25
+ ## 📦 Install
26
+
27
+ Python 3.10–3.12.
28
+
29
+ ```bash
30
+ pip install augmask # the augmentation (LGBM-S) alone
31
+ pip install "augmask[all]" # plus the diffusion backbones, baselines and evaluation
32
+ ```
33
+
34
+ To run the experiments, clone this repository and create the exact environment used for the results:
35
+
36
+ ```bash
37
+ git clone https://github.com/normal-kim/AugMask.git && cd AugMask
38
+ conda env create -f environment.yml && conda activate augmask
39
+ ```
40
+
41
+ ## 🧩 Use AugMask with your own model
42
+
43
+ ```python
44
+ from augmask import Encoded, LGBMSAugmenter
45
+
46
+ train = Encoded(X_num, X_cat, M_num, M_cat)
47
+ fills = [LGBMSAugmenter(draw=k).augment(train, [], n_categories)[0] for k in range(5)]
48
+
49
+ # train your denoiser on fills[step % 5], with the loss restricted to observed entries
50
+ loss = (per_entry_loss * mask).sum() / mask.sum()
51
+ ```
52
+
53
+ The augmenter takes an encoded table with `n` rows:
54
+
55
+ | Input | Shape | Type | Content |
56
+ |---|---|---|---|
57
+ | `X_num` | `(n, d_num)` | float | numerical columns, quantile-normal transformed; `NaN` where missing |
58
+ | `X_cat` | `(n, d_cat)` | int | categorical columns, integer encoded as `0 … K_j−1`; `−1` where missing |
59
+ | `M_num`, `M_cat` | same as above | bool | `True` where observed |
60
+ | `n_categories` | `d_cat` | list of int | number of categories `K_j` of each categorical column |
61
+
62
+ `augment` returns `X_num` and `X_cat` with every missing entry filled; each `draw` gives an independent fill, with the observed entries unchanged. In this repository, the backbones receive one fill per training step: `X_num` (float32, standardized) and `X_cat` (int64 codes), together with the masks. The target is one of the columns (the first numerical column for regression, the first categorical column for classification).
63
+
64
+ ### Your own augmenter
65
+
66
+ Any object with an `augment` method can replace LGBM-S:
67
+
68
+ ```python
69
+ from augmask.run import run
70
+
71
+ class MyAugmenter:
72
+ def augment(self, train, others, n_categories):
73
+ # fit on `train` (an Encoded table); return [(X_num, X_cat), ...] for `train` and each of `others`,
74
+ # with every missing entry filled: floats in X_num, codes 0 … K_j−1 in X_cat.
75
+ # It is called once per fill (5 by default), so return a new random draw on each call.
76
+ ...
77
+
78
+ run("adult", "mcar", 0.5, "tabdiff-augmask", augmenter=MyAugmenter())
79
+ ```
80
+
81
+ ## 🚀 Run
82
+
83
+ Datasets are downloaded from UCI on first use.
84
+
85
+ ```bash
86
+ # one method on one scenario: train, sample and evaluate -> runs/<dataset>/<mechanism>/p<p>/seed<s>/<method>.{csv,json}
87
+ python -m augmask.run --dataset adult --mechanism mcar --p 0.5 --method cdtd-augmask
88
+
89
+ # all experiments (6 datasets x 5 missing rates x 3 seeds x 14 methods) followed by the summary tables
90
+ bash scripts/run_all.sh 0 # on GPU 0
91
+ bash scripts/run_all.sh 1 beijing news # or split the datasets across GPUs and servers,
92
+ python scripts/make_tables.py # then summarise the merged runs/
93
+ ```
94
+
95
+ Methods are `{tabddpm,cdtd,tabdiff}-{noaug,meanmask,augfull,augmask}`, plus the baselines `forestdiff` and `miwae`.
96
+
97
+ | Strategy | Missing inputs filled with | Loss on |
98
+ |---|---|---|
99
+ | `noaug` | train mean / mode | all entries |
100
+ | `meanmask` | train mean / mode | observed entries (similar to [MissDiff](https://arxiv.org/abs/2307.00467); Ouyang et al., 2023) |
101
+ | `augfull` (ours) | samples from the augmenter (LGBM-S) | all entries |
102
+ | `augmask` (ours) | samples from the augmenter (LGBM-S) | observed entries |
103
+
104
+ **Alternating fills.** For improved performance, this implementation gives `augfull` and `augmask` 5 independent fills of the missing inputs and alternates between them during training, one per step (`--fills 5`, the default). `--fills 1` uses the single fill of the paper. The gain is largest at high missing rates.
105
+
106
+ **Masks and fills.** The first run of a scenario saves its missingness masks and augmented fills to `artifacts/<dataset>/<mechanism>/p<p>/seed<s>.npz`; later runs of the same scenario reuse them, so LGBM-S runs once per scenario rather than once per method.
107
+
108
+ **Runtime.** LGBM-S takes a few seconds per fill (fit on the train split, fill train and validation; AMD Ryzen 9 5950X, 32 threads); the default 5 fills take about five times as long, once per scenario:
109
+
110
+ | Missing | Adult | Default | Shoppers | Magic | Beijing | News |
111
+ |---|---:|---:|---:|---:|---:|---:|
112
+ | 10% | 3.5 s | 2.0 s | 1.3 s | 0.3 s | 2.9 s | 5.7 s |
113
+ | 50% | 4.5 s | 2.3 s | 1.5 s | 0.3 s | 4.1 s | 4.9 s |
114
+ | 90% | 6.0 s | 2.0 s | 1.1 s | 0.3 s | 5.8 s | 2.9 s |
115
+
116
+ ## 📊 Reference results
117
+
118
+ Adult and Beijing, MCAR with 50% and 90% missing, one seed, averaged over the four scenarios (×100, best in bold). Utility is downstream task performance: a model trained on synthetic rows and tested on real ones, relative to training on real rows.
119
+
120
+ - **Missing-aware:** [ForestDiff](https://proceedings.mlr.press/v238/jolicoeur-martineau24a.html) (AISTATS 2024) and [MIWAE](https://proceedings.mlr.press/v97/mattei19a.html) (ICML 2019).
121
+ - **Missing-unaware**, trained with the four strategies above: [TabDDPM](https://proceedings.mlr.press/v202/kotelnikov23a.html) (ICML 2023), [CDTD](https://proceedings.iclr.cc/paper_files/paper/2025/hash/61f4e5747b1b753cb35546b15d981f76-Abstract-Conference.html) (ICLR 2025) and [TabDiff](https://proceedings.iclr.cc/paper_files/paper/2025/hash/5c882988ce5fac487974ee4f415b96a9-Abstract-Conference.html) (ICLR 2025).
122
+
123
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
124
+ |---|---:|---:|---:|---:|---:|
125
+ | TabDDPM + NoAug | 68.2 | 54.7 | 45.1 | 8.7 | 66.0 |
126
+ | TabDDPM + MeanMask | 94.9 | 88.5 | 91.9 | 28.3 | 74.5 |
127
+ | TabDDPM + AugFull (ours) | 95.7 | 90.2 | 93.5 | 31.7 | 75.6 |
128
+ | TabDDPM + AugMask (ours) | 94.1 | 88.5 | 90.8 | 35.6 | 77.9 |
129
+ | CDTD + NoAug | 68.5 | 55.7 | 44.5 | 9.1 | 66.2 |
130
+ | CDTD + MeanMask | 93.6 | 84.5 | 93.2 | 23.0 | 73.6 |
131
+ | CDTD + AugFull (ours) | 95.5 | 91.0 | 91.2 | 33.2 | 77.2 |
132
+ | CDTD + AugMask (ours) | 97.8 | 93.4 | 98.0 | 37.8 | 76.7 |
133
+ | TabDiff + NoAug | 66.9 | 54.3 | 43.8 | 9.0 | 67.9 |
134
+ | TabDiff + MeanMask | 96.6 | 88.8 | 95.0 | 29.3 | 73.3 |
135
+ | TabDiff + AugFull (ours) | 95.7 | 91.4 | 93.1 | 32.3 | 76.6 |
136
+ | TabDiff + AugMask (ours) | **98.1** | **94.4** | **98.4** | **39.1** | **79.1** |
137
+ | ForestDiff | 69.7 | 66.7 | 65.6 | 22.4 | 66.4 |
138
+ | MIWAE | 97.5 | 85.2 | 97.1 | 29.1 | 72.4 |
139
+
140
+ <details>
141
+ <summary>Per scenario</summary>
142
+
143
+ **Adult, 50% missing**
144
+
145
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
146
+ |---|---:|---:|---:|---:|---:|
147
+ | TabDDPM + NoAug | 81.9 | 71.9 | 51.5 | 15.5 | 95.9 |
148
+ | TabDDPM + MeanMask | 94.7 | 89.7 | 90.8 | 30.9 | 97.3 |
149
+ | TabDDPM + AugFull (ours) | 97.2 | 92.3 | 97.3 | 35.1 | 96.8 |
150
+ | TabDDPM + AugMask (ours) | 96.2 | 92.6 | 95.7 | 43.3 | 97.2 |
151
+ | CDTD + NoAug | 83.3 | 74.2 | 54.1 | 16.6 | 96.4 |
152
+ | CDTD + MeanMask | 95.3 | 92.0 | 93.1 | 34.4 | 96.8 |
153
+ | CDTD + AugFull (ours) | 96.7 | 94.0 | 94.8 | 37.9 | **97.9** |
154
+ | CDTD + AugMask (ours) | 98.9 | 97.2 | 97.3 | **46.0** | 97.9 |
155
+ | TabDiff + NoAug | 81.0 | 72.3 | 50.8 | 15.9 | 95.1 |
156
+ | TabDiff + MeanMask | 96.2 | 93.6 | 96.1 | 35.7 | 97.5 |
157
+ | TabDiff + AugFull (ours) | 97.1 | 94.2 | 98.1 | 36.5 | 97.2 |
158
+ | TabDiff + AugMask (ours) | **99.1** | **97.8** | **99.4** | 45.4 | 97.7 |
159
+ | ForestDiff | 90.7 | 91.3 | 95.4 | 34.8 | 97.0 |
160
+ | MIWAE | 97.3 | 94.9 | 98.3 | 30.8 | 95.5 |
161
+
162
+ **Adult, 90% missing**
163
+
164
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
165
+ |---|---:|---:|---:|---:|---:|
166
+ | TabDDPM + NoAug | 66.3 | 46.2 | 10.9 | 1.8 | 71.7 |
167
+ | TabDDPM + MeanMask | 94.6 | 85.4 | 92.9 | 11.8 | 93.7 |
168
+ | TabDDPM + AugFull (ours) | 94.6 | 85.0 | 95.8 | 13.6 | 92.4 |
169
+ | TabDDPM + AugMask (ours) | 93.3 | 85.0 | 90.1 | 30.0 | 94.9 |
170
+ | CDTD + NoAug | 69.0 | 50.1 | 11.9 | 1.9 | 70.0 |
171
+ | CDTD + MeanMask | 93.2 | 75.3 | 90.3 | 9.1 | 87.5 |
172
+ | CDTD + AugFull (ours) | 93.9 | 84.2 | 91.2 | 14.0 | 92.6 |
173
+ | CDTD + AugMask (ours) | **97.9** | 90.9 | **99.1** | 30.9 | 94.0 |
174
+ | TabDiff + NoAug | 65.9 | 47.1 | 10.5 | 1.8 | 78.9 |
175
+ | TabDiff + MeanMask | 95.5 | 87.2 | 90.9 | 10.3 | 91.6 |
176
+ | TabDiff + AugFull (ours) | 94.4 | 85.1 | 94.9 | 14.1 | 93.8 |
177
+ | TabDiff + AugMask (ours) | 97.4 | **91.1** | 97.5 | **33.0** | **95.4** |
178
+ | ForestDiff | 36.3 | 26.9 | 1.4 | 0.3 | 72.3 |
179
+ | MIWAE | 97.6 | 89.1 | 96.5 | 14.2 | 92.0 |
180
+
181
+ **Beijing, 50% missing**
182
+
183
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
184
+ |---|---:|---:|---:|---:|---:|
185
+ | TabDDPM + NoAug | 74.2 | 64.1 | 70.3 | 15.9 | 50.5 |
186
+ | TabDDPM + MeanMask | 95.3 | 92.1 | 92.0 | 37.4 | 58.0 |
187
+ | TabDDPM + AugFull (ours) | 97.6 | 94.8 | 97.2 | 39.8 | 62.3 |
188
+ | TabDDPM + AugMask (ours) | 97.4 | 93.0 | 94.9 | 39.0 | 65.5 |
189
+ | CDTD + NoAug | 72.2 | 64.8 | 68.1 | 16.6 | 56.4 |
190
+ | CDTD + MeanMask | 95.2 | 92.7 | 97.0 | 39.6 | 64.6 |
191
+ | CDTD + AugFull (ours) | 97.5 | 95.5 | 95.2 | 42.1 | 66.0 |
192
+ | CDTD + AugMask (ours) | 98.5 | 96.0 | 98.5 | **42.9** | 62.8 |
193
+ | TabDiff + NoAug | 72.2 | 64.4 | 69.2 | 16.4 | 53.5 |
194
+ | TabDiff + MeanMask | 97.2 | 94.7 | 97.9 | 38.6 | 59.9 |
195
+ | TabDiff + AugFull (ours) | 97.7 | 95.8 | 95.8 | 40.5 | 64.1 |
196
+ | TabDiff + AugMask (ours) | **98.8** | **96.3** | **98.9** | 40.8 | **67.9** |
197
+ | ForestDiff | 91.9 | 90.6 | 93.1 | 35.1 | 62.4 |
198
+ | MIWAE | 97.8 | 87.1 | 97.2 | 39.2 | 58.3 |
199
+
200
+ **Beijing, 90% missing**
201
+
202
+ | Method | Shape ↑ | Trend ↑ | α-Precision ↑ | β-Recall ↑ | Utility ↑ |
203
+ |---|---:|---:|---:|---:|---:|
204
+ | TabDDPM + NoAug | 50.4 | 36.6 | 47.7 | 1.6 | 46.1 |
205
+ | TabDDPM + MeanMask | 94.9 | 86.6 | 91.7 | 33.3 | 49.1 |
206
+ | TabDDPM + AugFull (ours) | 93.6 | 88.6 | 83.8 | 38.1 | 50.9 |
207
+ | TabDDPM + AugMask (ours) | 89.6 | 83.4 | 82.7 | 30.1 | 53.9 |
208
+ | CDTD + NoAug | 49.5 | 33.7 | 43.8 | 1.0 | 42.0 |
209
+ | CDTD + MeanMask | 90.5 | 78.1 | 92.3 | 9.0 | 45.4 |
210
+ | CDTD + AugFull (ours) | 93.7 | 90.4 | 83.6 | **38.8** | 52.2 |
211
+ | CDTD + AugMask (ours) | 95.6 | 89.6 | 97.0 | 31.6 | 52.1 |
212
+ | TabDiff + NoAug | 48.6 | 33.7 | 44.9 | 1.8 | 44.3 |
213
+ | TabDiff + MeanMask | 97.4 | 79.8 | 95.2 | 32.3 | 44.3 |
214
+ | TabDiff + AugFull (ours) | 93.8 | 90.8 | 83.7 | 38.2 | 51.2 |
215
+ | TabDiff + AugMask (ours) | 97.0 | **92.6** | **98.0** | 37.3 | **55.6** |
216
+ | ForestDiff | 59.7 | 58.1 | 72.4 | 19.6 | 33.7 |
217
+ | MIWAE | **97.5** | 69.5 | 96.2 | 32.3 | 43.9 |
218
+
219
+ </details>
220
+
221
+ ## 🔧 Pipeline
222
+
223
+ 1. **Collect** the six UCI tables (Adult, Default, Shoppers, Magic, Beijing, News).
224
+ 2. **Split** each table 60/20/20 into train, validation and test.
225
+ 3. **Mask** train and validation entries (the target included) completely at random (MCAR) or depending on the other columns (MAR); the test split stays complete.
226
+ 4. **Encode**, using the observed train entries: numerical columns are quantile-normal transformed, categorical columns are integer encoded.
227
+ 5. **Fill** the missing entries with the train mean / mode, or *sample* them with the augmenter (LGBM-S by default; 5 fills, alternated during training).
228
+ 6. **Standardize** the numerical columns.
229
+ 7. **Train** the backbone and sample as many rows as the train split.
230
+ 8. **Map** the samples back to the original columns and values.
231
+ 9. **Evaluate** against the complete train and test splits: Shape, Trend, α-Precision, β-Recall and Utility (downstream task performance), following [TabSyn](https://proceedings.iclr.cc/paper_files/paper/2024/hash/e9750610639c3e7a849cff746bf60dbd-Abstract-Conference.html) (ICLR 2024).
232
+
233
+ <details>
234
+ <summary>Repository structure</summary>
235
+
236
+ ```
237
+ augmask/
238
+ ├── augment.py # step 5: LGBM-S (Algorithm 1), mean/mode fill, the Augmenter interface
239
+ ├── run.py # one method on one scenario: build, train, sample, evaluate
240
+ ├── evaluate.py # step 9: Shape, Trend, α-Precision, β-Recall, Utility
241
+ ├── data/
242
+ │ ├── datasets.py # step 1: download and clean the six UCI tables
243
+ │ ├── split.py # step 2: fixed stratified 60/20/20 split
244
+ │ ├── missingness.py # step 3: MCAR and MAR masks
245
+ │ ├── transforms.py # steps 4 and 6: quantile-normal, integer codes, standardization
246
+ │ ├── scenario.py # steps 3-6 and 8 for one scenario; saves and loads masks and fills
247
+ │ └── schema.py # column types, target and task
248
+ ├── models/ # step 7
249
+ │ ├── tabddpm.py, cdtd.py, tabdiff.py # the backbones, each with an optional loss mask
250
+ │ └── common.py # seeding, batching (one fill per step), EMA
251
+ └── baselines/
252
+ ├── forestdiff.py # the official ForestDiffusion package, wrapped
253
+ └── miwae.py
254
+ scripts/
255
+ ├── run_all.sh # all experiments, then the summary tables
256
+ ├── make_tables.py # rank tables with Wilcoxon markers
257
+ └── plot_overview.py # the figure above
258
+ tests/ # pipeline, missingness, baselines, evaluation, each backbone, the runner
259
+ ```
260
+
261
+ </details>
262
+
263
+ ## 📝 Notes on this release
264
+
265
+ The data pipeline was rebuilt for this release (60/20/20 split, a new MAR generator), so numbers differ slightly from the paper. Most model and training settings follow Appendix C.6 of the paper. The exception is alternating fills: AugFull and AugMask alternate between 5 fills of the missing inputs during training, which performs better at high missing rates in this implementation (`--fills 1` restores the paper's single fill). LGBM-S follows Algorithm 1; `LGBMSAugmenter(log_var_offset=augmask.augment.LOG_CHI2_BIAS)` optionally corrects the downward bias of its variance model.
266
+
267
+ LGBM-S is a per-coordinate augmenter: it samples each missing entry independently given the observed values and the mask, so it does not model the dependence between entries that are missing together. An augmenter that samples the missing coordinates jointly is a natural direction for future work, and can be plugged in as described in [Your own augmenter](#your-own-augmenter).
268
+
269
+ ## 📚 Citation
270
+
271
+ ```bibtex
272
+ @inproceedings{kim2026augmask,
273
+ title = {{AugMask}: Training Diffusion Models on Incomplete Tabular Data via Stochastic Augmentation and Masking},
274
+ author = {Kim, Jungkyu and Park, Taeyoung and Lee, Kibok},
275
+ booktitle = {Proceedings of the 43rd International Conference on Machine Learning},
276
+ series = {Proceedings of Machine Learning Research},
277
+ volume = {306},
278
+ year = {2026}
279
+ }
280
+ ```
281
+
282
+ ## 🙏 Acknowledgements
283
+
284
+ The backbones are adapted from [TabDDPM](https://github.com/yandex-research/tab-ddpm), [CDTD](https://github.com/muellermarkus/cdtd) and [TabDiff](https://github.com/MinkaiXu/TabDiff) (MIT). The metrics follow [TabSyn](https://github.com/amazon-science/tabsyn) (Apache-2.0), α-Precision/β-Recall follow [synthcity](https://github.com/vanderschaarlab/synthcity), and MIWAE follows [hyperimpute](https://github.com/vanderschaarlab/hyperimpute) (MIT). ForestDiff uses the official [ForestDiffusion](https://github.com/SamsungSAILMontreal/ForestDiffusion) package.
285
+
286
+ Released under the MIT License.
@@ -0,0 +1,5 @@
1
+ """AugMask: training diffusion models on incomplete tabular data (ICML 2026)."""
2
+ from .augment import Augmenter, Encoded, LGBMSAugmenter, MeanModeFill
3
+
4
+ __version__ = "0.1.0"
5
+ __all__ = ["Augmenter", "Encoded", "LGBMSAugmenter", "MeanModeFill"]