Repository navigation
Conversation
Both models flattened every 3D input to (batch, steps * width) before embedding. That is right for nested codes, which become one code sequence, but a numeric 3D input such as a timeseries field (batch, time, features) then no longer matches the embedding's nn.Linear(features, dim), and the forward pass fails with "mat1 and mat2 shapes cannot be multiplied". Flatten only token inputs (processor.is_token()). Numeric 3D inputs keep their shape, so the embedding projects each step and the existing mean pooling averages them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
Test CoverageTotal: 70.29% (18098 / 25749 lines)
|
|
/review |
There was a problem hiding this comment.
Previous review
AI review: findings
The PR stops MLP and LogisticRegression from flattening numeric 3D inputs, so a (batch, time, features) timeseries is embedded per step by nn.Linear instead of crashing. Token inputs (processor.is_token()) are still flattened, so nested_sequence behaves as before. Changed files are pyhealth/models/mlp.py, pyhealth/models/logistic_regression.py and one new test in each model's test file. Blocking: when series in a batch have different lengths, collate pads the shorter ones with zeros (pyhealth/datasets/utils.py:285-320), and the models then derive the pooling mask from the embedded values. Each Linear has a bias, so padded steps embed to a nonzero vector, count as valid, and dilute the mean pooling; this is silent and affects the eICU time-series path the PR targets (mlp.py:357, logistic_regression.py:181). Non-blocking: the new tests use equal-length series and only assert output shape, so they miss this and do not check per-step embedding; they do cover the original crash. I found no existing component that duplicates this change. I could not run the test suite, so these conclusions come from reading the code. Overall risk is medium: the crash fix is sound, but the pooling bug should be fixed or tested before merge.
Findings outside the diff
- [high] Zero-padded time steps are counted in LogisticRegression mean pooling
pyhealth/models/logistic_regression.py:181
Same problem as in MLP. LogisticRegression.forward now passes an unflattened numeric series to the embedding. The pooling mask at line 181, (x.sum(dim=-1) != 0), is computed on the embedded tensor, so collate zero-padding (datasets/utils.py:285-320) embeds to the nonzero bias and is counted as valid. Fix: derive the mask from the raw input before embedding, and cover variable-length series in the test.
Reviewed commit 125689a
There was a problem hiding this comment.
Previous review
AI Review
suggestion: need change
decision: need change
Thank you for this contribution. Embedding time series per step in MLP and LogisticRegression, instead of flattening them, fixes a real crash, and the regression tests for it are welcome.
Coverage: the whole repository is at 67.94%, which meets the 60% bar. The changed files are below the 80% guideline: logistic_regression.py is at 75.68% and mlp.py is at 75.19%. I'm not asking for more coverage on its own, but the test suggested below should raise it.
To get this merged:
- Handle padding for variable-length series. When series in a batch have different lengths, collate pads the shorter ones with zeros. The models then derive the pooling mask from the embedded values (mlp.py around line 357, logistic_regression.py around line 181). Both Linear layers have a bias, so a padded step embeds to a nonzero vector. It is counted as valid and dilutes the mean pooling, silently. This hits the eICU time-series path the PR targets. Please compute the mask from the raw input before embedding, for example a step is padding if all its features are zero. Or use a mask from the processor if one exists.
- Add a test with series of different lengths in one batch. It should check that padding does not change the pooled result. For example, compare a short series in a padded batch with the same series on its own. The current tests use equal-length series and only assert the output shape, so they would not catch this.
Optional suggestions:
- Make the new tests assert something about per-step embedding beyond the output shape.
Reviewed commit 125689a
- select_trial: without a finished baseline trial there is nothing to compare with, so the best mean is selected (ties to the lower trial_id) instead of using the first finished trial as the reference. The prompt says so, and AutoImprover warns when promote_se is set without a baseline. - run_dir: meta.json records the metric, cv_folds, promote_se, seed, baseline, search space and data sizes. Resuming with any of them changed raises ValueError; budgets may change. Runs saved before meta.json are resumed with a warning. - Prompts show a failed trial by exception type and code line only (Trial.error_brief); the full message stays in trials.jsonl. - patient_folds deals patients with a positive label first, so every fold has positives when there are enough; warns when there are not. A failed CV score now says "undefined on a cross-validation fold". - A proposer error (API error, refusal) skips the turn; three in a row stop the run. A failed reflection keeps the current lessons. - ClaudeLLM checks for anthropic>=1.15; a live smoke test runs when ANTHROPIC_API_KEY is set. - eICU example: LogisticRegression and MLP are dropped on the sequence view until sunlabuiuc#1315 merges; the task lives only in the example; no references to files outside the repository. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
Collate pads shorter series in a batch with all-zero steps. The embedding's nn.Linear maps a padded step to its bias, so the pooling mask, built from the embedded values, counted padded steps as valid, and mean_pooling summed every step in the numerator whatever the mask said. A short series in a padded batch pooled differently than alone. - get_step_mask (models/utils.py) marks the steps of the raw series that hold data: a step is padding when all its features are zero. A series with no nonzero step keeps every step, so pooling it does not divide by zero. - MLP.forward passes that mask to forward_from_embedding as the extra mask element it already accepts; LogisticRegression uses it for its pooling. Token inputs keep the mask from the embedded values (their padding embeds to zero), and a mask from the processor is used as before. - mean_pooling multiplies by the mask before summing, so masked steps are left out of the numerator as well as the count. - Tests: series of 2, 5 and 3 steps (the last all zeros) pool the same in one padded batch as each on its own, and each pooled vector equals the mean of the per-step embeddings of the real steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
|
Thanks for the review. The padding issue is fixed in 6f84180. Required
Optional
One limitation remains. A real step whose features are all zero cannot be told apart from padding, because the timeseries processor has no mask. Such a step is now left out of the mean, both in a padded batch and on its own. |
|
/review |
There was a problem hiding this comment.
Previous review
AI review: findings
The PR makes MLP and LogisticRegression embed numeric 3D inputs per time step instead of flattening them, then mean-pool over the real steps using a mask taken from the raw values. Token inputs keep the flatten path, and the reported timeseries crash is fixed. The change touches only pyhealth/models/mlp.py and logistic_regression.py, adds get_step_mask to models/utils.py, and changes no base class or shared default. The public forward_from_embedding contract is unchanged, and the interpretability methods (IG, GIM, SHAP, LIME, DeepLift) already append raw masks, so they stay consistent. Redundancy: no existing component does this for these two models, and the inline mask expressions in rnn/tcn/stagenet are not reusable helpers, so there is no redundancy finding. Blocking (medium), mlp.py:364: the is_token() gate also catches NestedMultiHotProcessor (is_token False, no embedding layer, per-visit categorical vectors), which was flattened before, so the PR's claim that nested codes behave as before is wrong for it and nothing tests it. Blocking (medium), mlp.py:180: mean_pooling divides by a zero mask sum for an all-zero series when an interpretability method passes its raw-value mask, so base logits and attributions become NaN while plain forward stays finite, and the all-zero branch of get_step_mask is untested. Non-blocking (low), utils.py:70: any all-zero step counts as padding, so real zero steps such as leading forward-fill zeros are dropped from pooling, which the docstring states. The new tests cover the reported crash, padded-batch equivalence against single-sample runs, and a backward pass, matching the repo's usual level, though they check only shapes and equality. The 0.261 vs 0.240 benchmark and the 20-file test claim cannot be checked from the repository. Overall risk is low for the reported path, with the two blocking items affecting edge inputs.
Reviewed commit 6f84180
There was a problem hiding this comment.
Previous review
AI Review
suggestion: need change
decision: need change
Thank you for this contribution. Embedding time series per step in MLP and LogisticRegression, instead of flattening them, fixes a real crash, and the padding-aware pooling and the tests are well thought out. It is close, but there are two blocking problems.
To get this merged:
- The
not processor.is_token()check inmlp.py(around line 364) and inlogistic_regression.pyalso matches processors that are not token-based but whose 3D inputs were flattened before. One example isNestedMultiHotProcessor, which gives per-visit categorical vectors. These inputs now take the new per-step path, so the claim that nested codes behave as before does not hold for them. Please narrow the condition so that only true numeric series such astimeseriestake the new path. Alternatively, keep the old flatten behaviour for every other 3D input. Please also add a test for a multi-hot nested input. - In
MLP,mean_pooling(around line 180) divides bymask.sum(dim=1). When an interpretability method supplies its own raw-value mask for an all-zero series, that sum is 0. The result is NaN base logits and attributions, even though a plain forward pass stays finite. Please guard against a zero mask sum, for example by clamping the denominator to a minimum of 1. Please also add a test for an all-zero series, covering both theget_step_maskfallback and the case where an external mask is passed.
Optional suggestions:
- Treating every all-zero step as padding drops real zero steps, such as leading forward-filled zeros, from the pooling. Your docstring says this, but it would help to note it in the model docs as well.
- Coverage is 69.11% for the whole repository, which meets the 60% bar. Several changed files are below the 80% target:
mlp.pyat 76.47% andlogistic_regression.pyat 77.78%. Tests for the two items above should help raise them.utils.pyis at 85.29%.
Reviewed commit 6f84180
…ries - Only a 3D input whose embedding is an nn.Linear over its last dimension (timeseries, nested floats, stagenet_tensor, tensor) is embedded per step. Flattening those crashed, since the Linear takes one step's features. Every other 3D input is flattened as on master, including nested_multihot, which is not a token input but has no embedding layer. - mean_pooling averages a row with no valid step over all its steps and clamps the count, so a mask built from the raw values of an all-zero series (as the interpretability methods build it) no longer gives NaN, and forward_from_embedding matches forward for it. get_step_mask now only marks the steps that hold data; the all-zero rule lives in mean_pooling. - The class docstrings say that an all-zero step of a numeric series counts as padding, e.g. the zeros forward fill puts before the first measurement. - Tests: nested_multihot is flattened in both models (they pass on master too); an all-zero series through forward, through forward_from_embedding with a raw-value mask, and through Integrated Gradients; mean_pooling with an all-zero mask row; constructor arguments, activations, forward_from_embedding inputs and list inputs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
|
Thanks for the second review. Both blocking items are fixed in f21e45e.
Blocking
Optional
|
|
/review |
There was a problem hiding this comment.
Previous review
AI review: findings
The PR stops MLP and LogisticRegression from flattening 3D numeric inputs that an nn.Linear embeds (for example timeseries). Each time step is now embedded and mean-pooled over steps with nonzero features, using a mask from the new get_step_mask in pyhealth/models/utils.py. Token inputs such as nested codes are still flattened, and mean_pooling in both models now averages all steps for a row with no valid step instead of returning NaN. The interpretability methods already append raw-value masks, so their results match forward(). Blocking problems: none found. Non-blocking: the description says the split uses processor.is_token(), but the code checks for nn.Linear; the two agree for current processors. Non-blocking: real all-zero steps are excluded from pooling (documented), and forward_from_embedding without a mask still counts padded steps, unlike forward(). Non-blocking: mean_pooling and the per-step logic are copied between mlp.py and logistic_regression.py, the two test helpers are copied between the test files, and one test pins nested_multihot's passthrough. The new tests cover shape and backward pass, padded-batch pooling equal to single-series pooling, all-zero rows, and an IG run with a mask, in the style of the existing MLP and LR tests. I could not run the test suite or the eICU experiment with the tools available here, so the claimed pass counts and PR-AUC figures are unverified. Overall risk is low: behavior changes only for numeric 3D inputs that previously crashed and for all-invalid rows that previously gave NaN.
Findings outside the diff
- [low] forward_from_embedding without a mask counts padded steps, unlike forward
pyhealth/models/mlp.py:279
When no mask is passed, the mask is read from the embedded values. For a Linear-embedded series, padded steps embed to the bias, which is nonzero, so they are counted. forward() excludes them, so the same batch gives different logits through forward() and through forward_from_embedding(bare tensor). The interpretability methods append masks and are not affected, but direct callers are. Fix: document this in forward_from_embedding, or build get_step_mask(value) for 3D Linear-embedded inputs when no mask is given.
Reviewed commit f21e45e
There was a problem hiding this comment.
Previous review
AI Review
suggestion: human review
decision: not available
Haiku found that this change needs a human review: The change alters the forward_from_embedding contract that every interpretability method (IG, SHAP, LIME, DeepLift, GIM, IG-GIM) calls. It adds a trailing-mask convention and changes pooling semantics for numeric 3D inputs and all-invalid rows, and it adds a function to the shared pyhealth/models/utils.py. It does not edit a base class, but the reach into interpretability code means a human should confirm it.
Reviewed commit f21e45e
- forward_from_embedding's docstring says that a Linear-embedded series needs its step mask passed in (get_step_mask of the raw values, as forward and the interpretability methods do): a padded step embeds to the Linear's bias, so without the mask padded steps are counted. - Rename the nested_multihot tests to say what they pin: EmbeddingModel has no layer for nested_multihot, so the raw rows pass through and are flattened. - Drop the MLP constructor test, which checked code this PR does not touch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a processor supplies its own mask with 3D codes (tuple_time_text's attention mask, for example), forward flattened the mask with the codes but rebuilt the feature tuple from the original one, so pooling got the 3D mask. On master that only skewed the count; with mean_pooling now multiplying by the mask, the shapes no longer match and forward raised. forward now writes the flattened mask back into the tuple. Test: nested codes with a processor-supplied mask that masks out a real code pool to the mean of the kept codes' embeddings, computed by hand, and differ from the unmasked output. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the third review. The low findings are addressed in 3887d2c and 5f0ee46, and I've left a few as they are, with reasons below.
Changed
Left as they are
|
|
/review |
There was a problem hiding this comment.
Previous review
AI review: findings
The PR makes MLP and LogisticRegression embed 3D numeric inputs (timeseries, nested_sequence_floats, stagenet_tensor, tensor) once per time step through their nn.Linear instead of flattening them, which fixes the timeseries shape crash. Pooling uses a mask built from raw values by a new get_step_mask helper in models/utils.py. mean_pooling in both models now excludes masked steps and returns finite values for all-masked rows. This changes the output of the public static mean_pooling methods and the mask contract of MLP.forward_from_embedding, which IG, SHAP, LIME and DeepLift call. I checked that those methods append raw-value masks, so timeseries stays consistent across them. Blocking (medium): tests/core/test_mlp.py:191 and the matching LogisticRegression test cover only timeseries, so the per-step path for tensor, nested_sequence_floats and the (time, value)-tuple stagenet_tensor form, which the description claims, has no test. Non-blocking (low): a 3D mask appended by interpretability methods for nested codes is not flattened in MLP.forward (mlp.py:190), so the new pooling raises a broadcast error where master miscounted; 3D MultiHot inputs are now embedded per step, contrary to the description; the nested_multihot test pins a passthrough; and the test helpers are duplicated across two files. The padding and pooling tests are thorough, and the eICU numbers are stale as the PR says. I could not run the test suite with read-only tools, so the 259-test pass claim is unverified. Overall risk is low, with one blocking test gap.
Reviewed commit 5f0ee46
There was a problem hiding this comment.
Previous review
AI Review
suggestion: human review
decision: not available
Haiku found that this change needs a human review: The change edits MLP.forward_from_embedding and the public static mean_pooling methods of MLP and LogisticRegression. forward_from_embedding is the embedding-level contract that IG, SHAP, LIME, DeepLift and GIM call, and its mask semantics and pooling outputs change. It also adds a helper to the shared pyhealth/models/utils.py module.
Reviewed commit 5f0ee46
Both models' per-step path was tested only for timeseries. New tests run nested_sequence_floats, stagenet_tensor and tensor inputs forward and backward, and check each pools to the mean of its embedded steps. LogisticRegression now takes the value from a tuple input such as stagenet_tensor's (time, value); it raised before. MLP.forward flattens a 3D mask appended after the schema's elements with the codes, as it does a processor's mask, so the new pooling no longer raises on it. The nested_multihot tests say they document a limitation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ntMLEmFsQEQJKTmPGGw4T
|
Thanks for the review. Fixed in 5c2c548.
The 20 core test files that use |
|
/review |
There was a problem hiding this comment.
AI review: findings
The PR embeds 3D numeric inputs (timeseries, nested_sequence_floats, stagenet_tensor, tensor) step by step in MLP and LogisticRegression instead of flattening them, which fixes the crash on timeseries input. Padded steps are masked from raw values with a new get_step_mask in pyhealth/models/utils.py, mean_pooling excludes masked steps and no longer returns NaN, a processor or appended 3D mask is flattened with 3D codes, and LogisticRegression reads the value from tuple inputs. Interactions: the change stays inside the two models plus an additive utility, but it changes outputs of MLP.forward, MLP.forward_from_embedding and the static mean_pooling in both models, which the interpretability methods (IG, DeepLift, SHAP, LIME, GIM) call, so IG attributions on padded timeseries MLP inputs and outputs for masked token rows change. Redundancy: nothing existing does this, though the one-line step-mask rule overlaps EHRMamba's _create_mask, and mean_pooling and the per-step block are duplicated across MLP and LogisticRegression (non-blocking, low). Blocking problems: none found. Non-blocking: the nested_multihot test enshrines a passthrough that only works when the visit count matches embedding_dim, and the description overstates that interpretability methods reach the appended 3D-mask path. Tests cover the new paths well (per-step embedding, padding, masks, tuple input, IG finiteness), but I did not execute them because no shell was available, so the reported pass counts and coverage are unverified. Real all-zero steps are dropped as padding, which the docstrings state. Risk: low.
Reviewed commit 5c2c548
| [128, 32] | ||
| """ | ||
| return x.sum(dim=1) / mask.sum(dim=1, keepdim=True) | ||
| mask = mask.unsqueeze(-1) |
There was a problem hiding this comment.
[low] mean_pooling and the per-step block are copied from MLP
The new mean_pooling body (lines 127-129) is the same as MLP.mean_pooling (pyhealth/models/mlp.py:188-190), and the per-step detection and mask code in LogisticRegression.forward (lines 168-178) mirror MLP.forward (mlp.py:375-387). The duplication predates this PR, but the PR edits both copies in parallel and they can drift. Consider calling MLP.mean_pooling from LogisticRegression or moving the shared pooling into models/utils.py. Not blocking.
| return last_hidden_state | ||
|
|
||
|
|
||
| def get_step_mask(x: torch.Tensor) -> torch.Tensor: |
There was a problem hiding this comment.
[low] get_step_mask repeats an inline rule that already exists in EHRMamba
get_step_mask computes (x != 0).any(dim=-1), which is the same rule as EHRMamba._create_mask for TimeseriesProcessor (pyhealth/models/ehrmamba.py:186, torch.any(torch.abs(value) > 0, dim=-1)). The interpretability methods also inline the same expression (integrated_gradients.py:316 and others). It is not a full duplicate, since no existing helper does this job for MLP and LogisticRegression, so this is not blocking. Optionally reuse it in EHRMamba or the interpretability methods.
| torch.testing.assert_close(pooled[0], x[0, [0, 2]].mean(dim=0)) | ||
| torch.testing.assert_close(pooled[1], x[1].mean(dim=0)) | ||
|
|
||
| def test_nested_multihot_passes_through_flattened(self): |
There was a problem hiding this comment.
[low] nested_multihot test uses a fixed visit count that hides a crash
The passthrough test (and its MLP twin at tests/core/test_mlp.py:310) gives every sample two visits, so the flattened width always equals embedding_dim. With variable visit counts, collate pads the visit dimension to the batch maximum, the flattened width changes per batch, and the fixed nn.Linear (MLP.mlp and LogisticRegression.fc) fails. The PR states the limitation, so this is not blocking. Consider adding a batch with differing visit counts so the limitation is shown, or raising a clear error for nested_multihot.
| # Flatten mask properly if it exists | ||
| if mask.dim() == 3: | ||
| mask = mask.view(batch_size, seq_len * inner_len) | ||
| elif (len(feature) == len(schema) + 1 |
There was a problem hiding this comment.
[low] Appended 3D-mask flattening is not reached by the interpretability methods as described
The description says the appended 3D mask is flattened 'the way the interpretability methods append theirs'. For nested codes, IG embeds the codes with nn.Embedding (integrated_gradients.py:366-368), which gives 4D values. forward_from_embedding rejects 4D values (mlp.py:298-301), so IG never reaches this branch, which only direct callers of forward use. The branch is correct for direct callers, but the description should say so, or the test should cover forward_from_embedding with a flattened input. Not blocking.
There was a problem hiding this comment.
AI Review
suggestion: human review
decision: not available
Haiku found that this change needs a human review: The change alters existing public behavior of MLP and LogisticRegression, not just new code paths. MLP.forward_from_embedding and the static mean_pooling in both models now return different values for masked rows, and interpretability methods (IG, DeepLift, SHAP, LIME, GIM) call forward_from_embedding, so their attributions on padded timeseries MLP inputs change. It also adds a helper to the shared pyhealth/models/utils.py. These are output contracts other code relies on, so a human should review them.
Reviewed commit 5c2c548
|
Thanks. The review found no blocking problems. Here is how I've handled the four lows:
I also merged this branch into current master locally (master is 22 commits ahead), and the MLP and LogisticRegression tests pass there. |
Problem
MLPandLogisticRegressionfail on anytimeseriesinput field:Both models flatten every 3D input to
(batch, steps * width)before embedding. That is right for nested codes (nested_sequence), which become one code sequence. A numeric 3D input such as a time series(batch, time, features)is then flattened too, so it no longer matches the embedding'snn.Linear(features, dim). Above, 48 hourly steps of 21 lab and vital values became 1,008 columns. RNN, Transformer, CNN and RETAIN handle the same input, because they never flatten.This came up running the in-hospital mortality task on the eICU demo with a 48-step lab and vital series.
Fix
Which inputs are embedded per step. A 3D input is embedded per step when its embedding is an
nn.Linear:timeseries,nested_sequence_floats,stagenet_tensorandtensor, whose Linear runs over the last dimension.multihotis also embedded by annn.Linear, but its samples are 1D, so it is not 3D in practice.nested_sequence) andnested_multihot, which has no embedding layer and passes through as raw multi-hot rows.LogisticRegression. It now takes the value from a tuple input such asstagenet_tensor's(time, value), asMLPdoes. Before, it raisedValueErroron any tuple input.Padding.
get_step_mask(pyhealth/models/utils.py): a step is padding when all its features are zero.MLP.forward_from_embeddingneeds that mask passed in. It cannot tell padding from data in embedded values. Callers passing a Linear-embedded series must append the mask, asforwardand the interpretability methods do. Its docstring says so.mean_pooling, in both models.A mask given with 3D codes. When a processor supplies its own mask, as
tuple_time_text's attention mask does,MLP.forwardflattens the mask with the codes and now passes the flattened mask on to the pooling. Masked tokens are therefore left out of the mean. A 3D mask that a caller offorwardpasses as an extra element after the schema's elements is flattened the same way. (The interpretability methods append their mask in this form, but for nested codes they go throughforward_from_embedding, which does not support nested codes, so only direct callers offorwardreach this path.) Before, the original 3D mask was passed on. On master that only skewed the count; with the new pooling, it would have raised.Tests
In
tests/core/test_mlp.pyandtests/core/test_logistic_regression.py:test_timeseries_and_nested_codes: atimeseriesfield plus anested_sequencefield run a forward and backward pass. Without the fix, both models fail with the error above.test_other_numeric_3d_inputs_are_embedded_per_step:nested_sequence_floats,stagenet_tensorandtensorfields each run a forward and backward pass, and each sample pools to the mean of its embedded steps. Thestagenet_tensorcase fails onLogisticRegressionwithout the tuple fix.test_padded_steps_are_left_out_of_the_mean: series of 2, 5 and 3 steps (the last all zeros) pool the same in one padded batch as each on its own. Each pooled vector equals the mean of the per-step embeddings of the real steps.test_nested_multihot_passes_through_flattened: documents the current passthrough ofnested_multihotas a limitation. It passes on master too.test_all_zero_series_with_an_external_mask(MLP):forward_from_embeddingwith a raw-value mask matchesforwardfor an all-zero series, and Integrated Gradients attributions are finite.test_mean_pooling_without_a_valid_step(LogisticRegression): checks the pooling rule directly.test_a_mask_given_with_3d_codes_is_flattened_with_them(MLP): nested codes with a mask that masks out a real code, given once as the processor's mask and once appended. Both pool to the mean of the kept codes' embeddings, computed by hand, and differ from the unmasked output. The appended case fails without the fix.test_forward_from_embedding_inputs(MLP) andtest_list_inputs_and_unsupported_ranks(LogisticRegression).Results:
MLPorLogisticRegressionpass: 276 tests, 5 skipped. They include the interpretability methods, calibration, early stopping and reproducibility tests.mlp.py84.14%,logistic_regression.py82.56%,models/utils.py84.38%.tools/check_pr_rules.pypasses.DTZ001).🤖 Generated with Claude Code