|
| 1 | +# Ventilator pressure prediction |
| 2 | + |
| 3 | +The first competition dataset is [Google Brain ventilator pressure |
| 4 | +prediction](https://www.kaggle.com/c/ventilator-pressure-prediction). |
| 5 | +`VentilatorDataset` inherits the benchmark's `Dataset` interface. |
| 6 | +The comparison reuses `BenchmarkConfig` and `BenchmarkResults`, with a dedicated |
| 7 | +training entry point because this is aligned sequence regression with a masked |
| 8 | +objective rather than ordinary future forecasting. |
| 9 | + |
| 10 | +The reference is version 6 of theoviel's [Deep Learning Starter: Simple |
| 11 | +LSTM](https://www.kaggle.com/code/theoviel/deep-learning-starter-simple-lstm), |
| 12 | +retrieved through Kaggle's public kernels API. Its protocol is: |
| 13 | + |
| 14 | +- Five unshuffled GroupKFold splits over CSV rows, grouped by `breath_id`. |
| 15 | +- Each sample contains all 80 steps of a breath. No pressure is an input. |
| 16 | +- Inputs: R, C, u_in, cumulative u_in within each breath, u_out. |
| 17 | +- Training loss: inspiration MAE normalized separately within each breath, |
| 18 | + averaged over the batch. Evaluation: global MAE over all inspiration rows. |
| 19 | +- Final-epoch predictions, all five folds, and a global out-of-fold score. |
| 20 | +- Adam, batch size 128, 200 epochs, learning rate 0.001 decaying linearly |
| 21 | + to zero, no warmup, shuffled training batches with the final partial batch |
| 22 | + dropped. The TensorFlow translation uses the same architectural dimensions: |
| 23 | + Dense(256), Dense(512), bidirectional LSTM(512), Dense(512), Dense(1). |
| 24 | + |
| 25 | +TensorFlow and PyTorch initialize weights and implement recurrent cells |
| 26 | +differently. This is an architecture and protocol translation, not a claim of |
| 27 | +bitwise reproduction of the original notebook's score. Both comparison models |
| 28 | +use identical data, folds, loss, optimizer schedule, epochs and batch size. |
| 29 | +The initial TFTS comparator uses the library's default final-state RNN. The |
| 30 | +aligned experiment uses the same library class with opt-in sequence outputs, |
| 31 | +input projection, and two bidirectional 128-unit LSTM layers. The existing |
| 32 | +forecasting default is preserved. Depth and direction now reach the recurrent |
| 33 | +encoder, including correct stacked widths and bidirectional states. |
| 34 | + |
| 35 | +From `Time-series-prediction/`: |
| 36 | + |
| 37 | +```bash |
| 38 | +uv sync --extra benchmark |
| 39 | +uv tool run --from kaggle kaggle competitions download \ |
| 40 | + -c ventilator-pressure-prediction -p ../data/ventilator |
| 41 | +unzip ../data/ventilator/ventilator-pressure-prediction.zip -d ../data/ventilator |
| 42 | +uv run python -m benchmark.ventilator --data ../data/ventilator/train.csv \ |
| 43 | + --threads 4 --output outputs/ventilator |
| 44 | +uv run python -m unittest tests.test_ventilator |
| 45 | +``` |
| 46 | + |
| 47 | +Outputs include the raw CSV checksum, protocol manifest, saved fold IDs, |
| 48 | +per-fold weights and training losses, out-of-fold predictions, and results in |
| 49 | +the benchmark JSON/CSV formats. Partially filled prediction files contain NaNs; |
| 50 | +they are not complete OOF results. Only after all folds finish is a model's |
| 51 | +global result written. A shorter `--epochs` run is a pipeline check, not a |
| 52 | +reproduction of the notebook's training budget. Keep such outputs separate. |
| 53 | + |
| 54 | +The competition archive has now been downloaded with authorized credentials |
| 55 | +and passed its ZIP integrity check. The adapter loaded 75,450 complete breaths |
| 56 | +and 2,290,968 inspiration rows from the original training CSV. Credentials are |
| 57 | +stored outside the repository in Kaggle's standard `~/.kaggle/access_token` |
| 58 | +file with owner-only permissions, so subsequent tasks under the same account |
| 59 | +can reuse them. Do not commit competition data or credentials. |
| 60 | + |
| 61 | +The initial real-data CPU runs completed with the same 10-epoch training |
| 62 | +budget, batch size 128, seed 42, and all five reference folds. Their complete |
| 63 | +OOF predictions were checked for shape, finiteness, identical fold IDs and |
| 64 | +source CSV checksum. Independently recomputed global inspiration MAEs: |
| 65 | + |
| 66 | +| Model | OOF inspiration MAE | |
| 67 | +| --- | ---: | |
| 68 | +| Full reference TensorFlow LSTM | 0.7606188493 | |
| 69 | +| Default TFTS RNN | 1.5785328745 | |
| 70 | + |
| 71 | +Their output paths are `outputs/ventilator_reference_10e` and |
| 72 | +`outputs/ventilator_rnn_10e`. This shorter training budget is explicitly |
| 73 | +different from the notebook's 200 epochs; these are not reproduced notebook |
| 74 | +scores. |
| 75 | + |
| 76 | +The next configuration is fixed in `benchmark/configs/ventilator.yaml` before |
| 77 | +observing its fold results. It changes only the TFTS architecture, preserving |
| 78 | +the five inputs, raw data, fold assignment, loss, optimizer schedule and |
| 79 | +training budget. The initial comparison motivated this architecture change; |
| 80 | +the outer-fold results are development benchmarks, not an untouched final test. |
| 81 | +No validation checkpoint selection, fold-specific hyperparameters or pressure |
| 82 | +inputs are used. Reuse the completed baseline and run the new TFTS comparator: |
| 83 | + |
| 84 | +```bash |
| 85 | +uv run python -m benchmark.ventilator --config benchmark/configs/ventilator.yaml \ |
| 86 | + --models rnn --data ../data/ventilator/train.csv --threads 6 |
| 87 | +``` |
| 88 | + |
| 89 | +Omit `--models rnn` to reproduce both models from scratch in one command. |
| 90 | +`return_sequences=True` requires the input length to match the declared |
| 91 | +prediction length; it uses the recurrent output at each step, while the |
| 92 | +default path continues to forecast from the final state. |
| 93 | + |
| 94 | +The aligned TFTS comparator is configurable as a bidirectional LSTM through |
| 95 | +`benchmark/configs/ventilator.yaml` (`rnn_type: lstm`, `bi_direction: true`), |
| 96 | +independent of the library's default unidirectional final-state forecasting. |
| 97 | + |
| 98 | +The aligned runs both completed: `outputs/ventilator_aligned_10e` (128-unit |
| 99 | +configuration, log `/tmp/ventilator-aligned-10e.log`) and |
| 100 | +`outputs/ventilator_aligned_final_10e` (the current `ventilator.yaml`, |
| 101 | +512-unit). Each wrote all five fold checkpoints and a complete OOF prediction |
| 102 | +array. I recomputed the global inspiration MAE from each saved array against |
| 103 | +the raw CSV and each reproduces its recorded `results.json` to the last digit; |
| 104 | +all arrays are 100% finite. |
| 105 | + |
| 106 | +| Model | Config | OOF inspiration MAE | |
| 107 | +| --- | --- | ---: | |
| 108 | +| Full reference TensorFlow LSTM | baseline | 0.7606188493 | |
| 109 | +| Default TFTS RNN | final-state forecast | 1.5785328745 | |
| 110 | +| Aligned TFTS BiLSTM | 128-unit, 2 layers, per-step | 0.8277517977 | |
| 111 | +| Aligned TFTS BiLSTM | 512-unit, 2 layers, per-step | **0.6821537014** | |
| 112 | + |
| 113 | +The 512-unit bidirectional configuration beats the same-data reference |
| 114 | +baseline (0.6822 vs 0.7606) under an identical five-fold protocol, optimizer |
| 115 | +schedule, epochs and batch size on CPU. These 10-epoch scores are |
| 116 | +development benchmarks on the aligned protocol, not reproductions of the |
| 117 | +notebook's 200-epoch budget. |
| 118 | + |
| 119 | +Resume an interrupted RNN run with its original saved protocol: |
| 120 | + |
| 121 | +```bash |
| 122 | +uv run python -m benchmark.resume_aligned_final --data ../data/ventilator/train.csv \ |
| 123 | + --output outputs/ventilator_aligned_10e --threads 6 |
| 124 | +``` |
| 125 | + |
| 126 | +The resume command reads architecture, seed, learning rate, epochs and batch |
| 127 | +size from `manifest.json`, checks the CSV checksum and saved breath IDs, and |
| 128 | +uses the same training implementation as a fresh run. It trains missing |
| 129 | +checkpoints and rebuilds all OOF predictions from checkpoints. Use `--folds 3 4` |
| 130 | +only to explicitly retrain those folds. Other models' results are preserved. |
| 131 | +Fresh training requires a new output directory; `benchmark.ventilator --resume` |
| 132 | +also supports recovery when supplied with the original configuration. |
| 133 | + |
| 134 | +Implementation checks: 530 project tests passed, with seven environment-gated |
| 135 | +skips, using the existing Makefile and optional example dependencies: |
| 136 | + |
| 137 | +```bash |
| 138 | +make test UV_RUN='uv run --frozen --with matplotlib --with scikit-learn --with optuna' |
| 139 | +``` |
| 140 | + |
| 141 | +RNN regressions cover configured stacking/direction, GRU/LSTM state shapes, |
| 142 | +sequence gradients, padding invariance, declared-length rejection, and config |
| 143 | +round trips. The original trained GRU checkpoint was also loaded after the |
| 144 | +changes and reproduced its saved fold-0 predictions. The legacy Encoder import |
| 145 | +remains the same class. The aligned run's saved fold arrays were checked against |
| 146 | +both the completed reference and scikit-learn on the original row grouping. |
| 147 | + |
| 148 | +## Reference transformer versus the TFTS BERT |
| 149 | + |
| 150 | +```bash |
| 151 | +uv run python -m benchmark.ventilator_bert --config benchmark/configs/ventilator_bert.yaml \ |
| 152 | + --data ../data/ventilator/train.csv --threads 12 |
| 153 | +``` |
| 154 | + |
| 155 | +This comparison implements the objective's reference notebook, cdeotte's |
| 156 | +["TensorFlow Transformer [0.112]"](https://www.kaggle.com/code/cdeotte/tensorflow-transformer-0-112), |
| 157 | +in TensorFlow and evaluates it against the TFTS encoder-only `Bert` with its |
| 158 | +per-timestep `regression_head` — the TFTS model whose architecture matches the |
| 159 | +reference (an encoder without a decoder). |
| 160 | + |
| 161 | +The reference uses the engineered features (`add_features` plus one-hot |
| 162 | +R/C/R__C, RobustScaled to 64 channels) with the three-channel target stack |
| 163 | +`[pressure, pressure_diff, pressure_integral]`, 12 transformer blocks |
| 164 | +(embed 64, heads 8, ff 128, dropout 0) with 0.7/0.3 skip connections, masked |
| 165 | +multi-channel MAE weighted to inspiration rows, cosine-with-restarts learning |
| 166 | +rate, and final-epoch global OOF inspiration MAE on the pressure channel. |
| 167 | + |
| 168 | +Both models train on the same five folds, features, loss, optimizer schedule, |
| 169 | +epochs and batch size, on CPU only. The comparison lives in |
| 170 | +`benchmark/ventilator_bert.py` (`run_comparison`), driven by |
| 171 | +`benchmark/configs/ventilator_bert.yaml`. Partial runs (`--folds < 5`) write |
| 172 | +per-fold weights and NaNs for the untouched folds; a global OOF result is only |
| 173 | +written after all five folds complete. Use `--folds 1 --epochs 1` as a fast |
| 174 | +pipeline check before a full budget run. |
| 175 | + |
| 176 | +Implementation checks for this path: `tests/test_ventilator.py` now covers the |
| 177 | +three-segment cosine-with-restarts trace, the normalized masked multi-channel |
| 178 | +loss (expiration rows contribute nothing), and rank mismatch rejection. |
| 179 | + |
| 180 | +### Historical results (6 epochs, 5 folds, batch 64, CPU) |
| 181 | + |
| 182 | +These results used the earlier preprocessing implementation, which divided |
| 183 | +zero-IQR features by `1e-9`. That bug inflated sparse feature values and has |
| 184 | +been corrected to use unit scale. The scores below are historical artifacts, |
| 185 | +not validated results for the corrected pipeline. Both models must be retrained |
| 186 | +in fresh output directories before comparing their scores again. |
| 187 | + |
| 188 | +| model | fold0 | fold1 | fold2 | fold3 | fold4 | OOF | |
| 189 | +|----------|----------|----------|----------|----------|----------|-----------| |
| 190 | +| reference| 7.447909 | 7.577437 | 7.536457 | 7.513165 | 7.437241 | **7.502448** | |
| 191 | +| bert | 7.344947 | 7.407012 | 7.383770 | 7.337602 | 7.275442 | **7.349761** | |
| 192 | + |
| 193 | +For that historical run, TFTS BERT's OOF inspiration MAE was lower by 0.1527 |
| 194 | +(7.3498 vs 7.5024). This is not evidence of superiority under corrected |
| 195 | +preprocessing. Outputs and |
| 196 | +the fold indices/breath ids are in `outputs/ventilator_bert_6e/` |
| 197 | +(`results.json`, `results.csv`, `*_oof.npy`, per-fold weights and histories); |
| 198 | +both OOF arrays are 100% finite and their per-fold scores were re-derived |
| 199 | +independently from the saved arrays as a verification pass. |
| 200 | + |
| 201 | +Both transformer models now honor architecture overrides from the YAML |
| 202 | +`models` mapping. `benchmark.learning_rate` controls the cosine schedule's |
| 203 | +peak. The verifier uses the same model factory, rejects nonfinite or wrongly |
| 204 | +shaped OOF arrays, and requires the recorded `inspiration_mae` to match. |
| 205 | +Rechecking saved prediction arrays alone does not validate preprocessing. |
0 commit comments