tramdag.modules¶
tramdag.modules ¶
The term modules: one nn.Module per term, built from the term's spec class.
A spec term (Term subclass: LS, CS, VC, I) is plain
data and carries the spec-level rules, and its module attribute is the
class here that trains it (LinearShift.module is LinearShiftModule). A
module is constructed from its term and the node's parent schema,
module(term, schema) (the intercept slot adds n_params), holds the
term's network and owns the runtime behaviour:
shift_value/theta_value, post_init, regularizer, finalize,
score_columns and the side-input contract. This module imports nothing
from spec: it reads the schema, {parent: "continuous" |
n_levels}, and a term's parents and options, so spec can import it.
A custom term is two classes: a ShiftModule
subclass with __init__(term, schema) and shift_value, and a Term
subclass for the options and checks whose module is that class.
The widths and the activation of every network are options of the term
classes, written once in the signatures in spec; the modules
here take what they are given.
| Module | Network | Term |
|---|---|---|
LinearShiftModule |
Linear(n, 1, bias=False) |
LS |
ComplexShiftModule |
hidden stack to 1 output, no bias | CS |
ComplexInterceptModule |
hidden stack to n_params outputs, no bias |
I(...) |
AdditiveInterceptModule |
one stack per parent, outputs summed | additive I |
SimpleInterceptModule |
free parameter vector, no parent | I() |
VaryingCoefficientModule |
beta0 + penalized hidden stack to 1 |
VC |
Parent features: a continuous parent enters raw, in one column; an ordinal
parent one-hot, in levels columns. ACTIVATIONS maps the activation
names a term may name to their torch.nn classes.
TermModule ¶
Bases: Module
What every term module shares: the input transform and its calibration.
Source code in src/tramdag/modules.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | |
input_transform
property
¶
input_transform
The term's frozen network-input transform, or None.
A module's __init__ registers one (_attach_input_transform) when
the term declares input_transform= over continuous parents; a plain
class attribute would shadow the registered submodule.
parameter_groups ¶
parameter_groups()
Give the term's parameters by role; all of them are "networks" here.
A term with a free intercept or an interpretable coefficient overrides
this and gives those as "intercept" or "linear".
Source code in src/tramdag/modules.py
217 218 219 220 221 222 223 | |
calibrate ¶
calibrate(train_df)
Freeze this term's data-dependent state: the input-transform stats.
Node.calibrate calls this once per term; a term without
an input_transform has nothing to freeze. The intercept slot has a
second step on top of this one, see
calibrate_intercept.
Source code in src/tramdag/modules.py
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | |
ShiftModule ¶
Bases: TermModule, ABC
A shift term's behavior hooks, on top of its network.
__init__(term, schema) builds the network from the term's options and the
parents' widths in the schema, and sets key (the node's ModuleDict key)
and parents (the term's written parents); a subclass may keep more
(VaryingCoefficientModule keeps mods/t_is_ord/propensity_col).
Layers are built in a fixed order under fixed attribute names, so
state-dict paths and the seeded RNG stream stay bit-stable.
scored is true when score_columns gives coefficients. order
sets the summing order of the shifts; VC comes last, which is the pinned
order.
Source code in src/tramdag/modules.py
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 | |
shift_value
abstractmethod
¶
shift_value(node, feats)
Give this term's contribution to the node's shift, shape (n,).
feats holds the node's encoded parents plus this term's side
columns (frozen from the data frame during training, injected live
by the flow at query time).
Source code in src/tramdag/modules.py
267 268 269 270 271 272 273 274 | |
post_init ¶
post_init()
Re-apply construction-time invariants after a global weight init.
Source code in src/tramdag/modules.py
276 277 | |
regularizer ¶
regularizer()
Give the term's penalty on the total-likelihood scale, or None.
Source code in src/tramdag/modules.py
279 280 281 | |
finalize ¶
finalize(node, feats)
Run the term's post-fit step (after the after-fit callbacks).
Source code in src/tramdag/modules.py
283 284 | |
score_columns ¶
score_columns(node, feats, dlds)
Give the per-observation score columns of this term's coefficients.
Empty for a term with no interpretable coefficient (CS).
Source code in src/tramdag/modules.py
286 287 288 289 290 291 | |
side_columns ¶
side_columns()
Name the data-frame columns this term needs beyond the parents.
Training reads them from train_df like any other column (so the
validation split and the minibatch slicing come for free).
Source code in src/tramdag/modules.py
293 294 295 296 297 298 299 | |
check_column ¶
check_column(node_name, col, values)
Validate one side column's values at fit time.
Source code in src/tramdag/modules.py
301 302 | |
live_side ¶
live_side(flow, values, n)
Recompute the side columns from the fitted flow, at query time.
Source code in src/tramdag/modules.py
304 305 306 | |
extra_columns ¶
extra_columns(flow)
List the extra columns queries must tensorize for live_side.
Source code in src/tramdag/modules.py
308 309 310 | |
InterceptModule ¶
Bases: TermModule, ABC
The intercept slot's behavior hooks, on top of its network.
A node has exactly one intercept term (normalization guarantees
node.terms[0]); it produces the transform parameters theta.
groups carries the parent groups (empty for a simple intercept,
one tuple for a joint net, one per parent for an additive one) and
ci_parents their flat order. intercept_module picks the class
for an I term; each class constructs from (term, schema, n_params).
Source code in src/tramdag/modules.py
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 | |
calibrate_intercept ¶
calibrate_intercept(train_df, own, ut)
Freeze the input stats and set the transform's domain, once.
The intercept slot calibrates one thing more than every other term, so
it says so in its own name rather than widening calibrate: own
is the node's training column and ut its monotone transform
(None for an ordinal node: cutpoints have no domain), whose
range_q/1 - range_q quantiles map onto the pre-scaled domain.
Source code in src/tramdag/modules.py
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | |
theta_value
abstractmethod
¶
theta_value(node, feats, n)
Give the transform parameters, shape (n, P).
Source code in src/tramdag/modules.py
349 350 351 | |
marginal_start ¶
marginal_start(theta)
Set the calibrated marginal start; only a free intercept has one.
Source code in src/tramdag/modules.py
353 354 | |
SimpleInterceptModule ¶
Bases: InterceptModule
The free simple intercept: one theta vector, no parents.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
term
|
Intercept
|
The parentless intercept term. |
required |
schema
|
dict[str, str | int]
|
The parent schema (unused: no parents to size). |
required |
n_params
|
int
|
Number of transform parameters. |
required |
Source code in src/tramdag/modules.py
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 | |
forward ¶
forward(n)
Broadcast the parameters over a batch of n rows, shape (n, P).
Source code in src/tramdag/modules.py
375 376 377 | |
theta_value ¶
theta_value(node, feats, n)
Broadcast the free theta over the batch.
Source code in src/tramdag/modules.py
379 380 381 | |
parameter_groups ¶
parameter_groups()
Give the free theta as "intercept".
Source code in src/tramdag/modules.py
383 384 385 | |
marginal_start ¶
marginal_start(theta)
Start at the node's data marginal.
Source code in src/tramdag/modules.py
387 388 389 390 | |
ComplexInterceptModule ¶
Bases: InterceptModule
A single (possibly joint multi-parent) complex intercept net.
Several parents given to one term feed a single network, so they interact.
The term's units, activation and batch_norm size the network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
term
|
Intercept
|
The intercept term, with at least one parent. |
required |
schema
|
dict[str, str | int]
|
The parent schema, |
required |
n_params
|
int
|
Number of transform parameters to produce. |
required |
Source code in src/tramdag/modules.py
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 | |
forward ¶
forward(x)
Map parent features (n, n_features) to parameters (n, n_params).
Source code in src/tramdag/modules.py
423 424 425 | |
theta_value ¶
theta_value(node, feats, n)
Run the one net over the joint parent features.
Source code in src/tramdag/modules.py
427 428 429 | |
AdditiveInterceptModule ¶
Bases: InterceptModule
allow_interaction=False: one net per parent, outputs summed.
Each parent reshapes the transform independently, in unconstrained
coefficient space. The submodule stays named nets, because
tests/test_statedict_stability.py pins the state-dict path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
term
|
Intercept
|
The intercept term with |
required |
schema
|
dict[str, str | int]
|
The parent schema, |
required |
n_params
|
int
|
Number of transform parameters to produce. |
required |
Source code in src/tramdag/modules.py
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | |
theta_value ¶
theta_value(node, feats, n)
Sum the per-parent nets in coefficient space.
Source code in src/tramdag/modules.py
467 468 469 470 471 472 | |
LinearShiftModule ¶
Bases: ShiftModule
LS: one raw-unit coefficient per feature of the single parent, no bias.
Keyed by the parent's name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
term
|
LinearShift
|
The term, with its one parent. |
required |
schema
|
dict[str, str | int]
|
The parent schema, for the parent's feature width. |
required |
Source code in src/tramdag/modules.py
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | |
forward ¶
forward(x)
Give the shift (n,) from the encoded features (n, n_features).
Source code in src/tramdag/modules.py
501 502 503 | |
parameter_groups ¶
parameter_groups()
Give the weight as "linear".
Source code in src/tramdag/modules.py
505 506 507 | |
shift_value ¶
shift_value(node, feats)
Give the raw parent column times the weight, with no input transform.
Source code in src/tramdag/modules.py
509 510 511 | |
score_columns ¶
score_columns(node, feats, dlds)
One column per weight: the parent (continuous) or its one-hot levels.
\(\partial \ell_i / \partial \beta = (\partial \ell_i / \partial s_i)\, x_i\), analytic and exact.
Source code in src/tramdag/modules.py
513 514 515 516 517 518 519 520 521 522 523 | |
ComplexShiftModule ¶
Bases: ShiftModule
CS: an additive network shift g(x) over its parents.
One net over the concatenated parents, keyed 'a' or 'a+b'. The
term's units, activation and batch_norm size the network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
term
|
ComplexShift
|
The term, with its parents. |
required |
schema
|
dict[str, str | int]
|
The parent schema, |
required |
Source code in src/tramdag/modules.py
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | |
forward ¶
forward(x)
Give the shift (n,) from the encoded features (n, n_features).
Source code in src/tramdag/modules.py
554 555 556 | |
shift_value ¶
shift_value(node, feats)
Give the net over this term's (possibly input-transformed) features.
Source code in src/tramdag/modules.py
558 559 560 | |
VaryingCoefficientModule ¶
Bases: ShiftModule
VC: \(\beta(\text{mod})\, x_t\) with \(\beta(x) = \beta_0 + b_\Theta(x)\).
The weights of \(b_\Theta\) carry the L2 penalty (l2; fit adds
penalty * l2() / n to the node's mean NLL, which equals adding
penalty * l2() to the summed NLL); \(\beta_0\) is not penalized. The
output layer starts at zero, so \(\beta(x) = \beta_0\) at construction. With
n_features == 0 there is no network and the term is LS(t). After
the fit, recenter moves the training-mean of b_theta into
beta0 through the center buffer, a reparameterization that leaves
the modelled function unchanged. Keyed by the treatment's name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
term
|
VaryingCoefficient
|
The term: treatment first in |
required |
schema
|
dict[str, str | int]
|
The parent schema, for the modifiers' feature widths and the treatment's kind. |
required |
Source code in src/tramdag/modules.py
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 | |
beta ¶
beta(mod_feats, n)
Give the effect values \(\beta(x)\), shape (n,).
mod_feats is None if, and only if, the term has no modifiers;
n is the batch size, used in that case.
Source code in src/tramdag/modules.py
612 613 614 615 616 617 618 619 620 | |
forward ¶
forward(t, mod_feats)
Give the shift \(\beta(\text{modifiers})\, t\), shape (n,).
t is the raw treatment column (n, 1), mod_feats the encoded
modifier features or None without modifiers.
Source code in src/tramdag/modules.py
622 623 624 625 626 627 628 | |
l2 ¶
l2()
Sum the squared b_theta weights, the penalized quantity.
beta0 is never included; 0 without modifiers.
Source code in src/tramdag/modules.py
630 631 632 633 634 635 636 637 | |
recenter ¶
recenter(mod_feats)
Re-split beta0 and b_theta so b_theta has mean zero.
The mean is taken over mod_feats. The removed constant moves into
beta0, so the modelled function does not change. Without
modifiers this does nothing.
Source code in src/tramdag/modules.py
639 640 641 642 643 644 645 646 647 648 649 650 651 | |
regressor ¶
regressor(feats)
Give the (n, 1) column \(\beta\) multiplies: the treatment, raw.
The one-hot level-1 indicator for a binary ordinal treatment, the
value itself for a continuous one; a centered term subtracts its
propensity column (the regressor t - e_hat(x) of Robinson, 1988). It is
also the score of beta0, so score_columns reads it here.
Source code in src/tramdag/modules.py
653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 | |
shift_value ¶
shift_value(node, feats)
\(\beta(\text{modifiers})\) times the regressor, guarding a centered term.
Source code in src/tramdag/modules.py
672 673 674 675 676 | |
post_init ¶
post_init()
Re-zero the head's output layer: \(\beta(x) = \beta_0\) at start.
Source code in src/tramdag/modules.py
678 679 680 681 | |
regularizer ¶
regularizer()
Give the penalty \(\lambda \lVert b_\Theta \rVert^2\) on the NLL scale.
None without a head to shrink (no modifiers, or penalty 0).
Source code in src/tramdag/modules.py
683 684 685 686 687 688 689 690 | |
parameter_groups ¶
parameter_groups()
Give \(\beta_0\) as "linear" and \(b_\Theta\) as "networks".
Source code in src/tramdag/modules.py
692 693 694 695 | |
finalize ¶
finalize(node, feats)
Re-split beta0/b_theta: the head sums to zero over train.
Source code in src/tramdag/modules.py
697 698 699 700 | |
score_columns ¶
score_columns(node, feats, dlds)
One column, keyed by the treatment: the beta0 score.
\(\partial s / \partial \beta_0\) is the term's own regressor, so forward
and score share one definition by construction.
Source code in src/tramdag/modules.py
702 703 704 705 706 707 708 709 | |
side_columns ¶
side_columns()
Name the propensity column a centered term reads from the frame.
Source code in src/tramdag/modules.py
711 712 713 | |
check_column ¶
check_column(node_name, col, values)
Propensities are probabilities.
Source code in src/tramdag/modules.py
715 716 717 718 719 720 721 | |
live_side ¶
live_side(flow, values, n)
Give the full-data propensity from the flow's own treatment node.
Detached (no gradient reaches the treatment node from this node's
loss) and derived from the current parent values, so
do-mutilated sampling centers with the intervened t. Training
uses the frozen column.
Source code in src/tramdag/modules.py
723 724 725 726 727 728 729 730 731 732 733 734 | |
extra_columns ¶
extra_columns(flow)
Give the treatment's parents (a treatment cannot be centered itself).
Source code in src/tramdag/modules.py
736 737 738 | |
feat_width ¶
feat_width(schema, parents)
Total feature width of the parents (ordinal one-hot, continuous raw).
Source code in src/tramdag/modules.py
134 135 136 | |
intercept_module ¶
intercept_module(term, schema, n_params)
Construct the node's intercept module from its I term.
One I term becomes one of three modules: the free theta without
parents, one joint net over all parents, or one net per parent with the
coefficient vectors summed (allow_interaction=False).
Source code in src/tramdag/modules.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |