API Reference¶
Auto-generated from docstrings via mkdocstrings. Sections are ordered from most foundational to most specialized; within each section, classes precede free functions and type aliases.
1. Top-level package¶
nnx
¶
nnx — lightweight PyTorch training / eval / visualization toolkit.
The package is organized under nnx.nn (model, params, datasets, enums, nets,
callbacks) and two top-level helpers (nnx.utils.Utils, nnx.vis_utils.VisUtils).
The curated re-exports below give a flat surface for the most common imports
without forbidding the deep paths existing notebook code relies on.
__version__ = _version('thekaveh-nnx')
module-attribute
¶
LRFinderResult
dataclass
¶
Result of an :func:lr_finder sweep.
Attributes:
| Name | Type | Description |
|---|---|---|
lrs |
list[float]
|
list of learning rates actually exercised. Length matches
|
losses |
list[float]
|
list of loss values, one per LR. |
suggested_lr |
float
|
the recommended |
figure |
Figure
|
Plotly |
Source code in nnx/lr_finder.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | |
set_seed(seed: int, strict: bool = False) -> None
¶
Pin every RNG that affects training and toggle cuDNN deterministic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seed
|
int
|
integer seed shared across Python |
required |
strict
|
bool
|
when True also calls torch.use_deterministic_algorithms(True) and sets CUBLAS_WORKSPACE_CONFIG. Slower and may raise on ops that lack a deterministic CUDA implementation; opt in only when full bit-for-bit reproducibility matters. |
False
|
Source code in nnx/seeding.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
dataloader_worker_init_fn(worker_id: int) -> None
¶
DataLoader worker_init_fn that pins each worker's numpy/python seed
deterministically from the worker_id + the parent torch seed.
Pass as: DataLoader(..., worker_init_fn=dataloader_worker_init_fn).
Source code in nnx/seeding.py
76 77 78 79 80 81 82 83 84 85 86 | |
env_snapshot(force_refresh: bool = False) -> dict
¶
Capture a snapshot of the runtime environment for reproducibility.
Returned dict is JSON-serializable. Includes Python / torch / numpy
versions, GPU info if any, OS, and the git commit hash if running
inside a git repo. Safe to call from anywhere — failures degrade to
None per field rather than raising.
Result is memoized within the process (versions/hardware don't
change between calls). Caveat: the git_commit / git_dirty
fields are frozen at first call too, so a long session that commits
mid-run records the session-start git state in later runs'
metadata.yaml. Pass force_refresh=True to re-compute — useful
in tests that mutate the environment, or to re-stamp git state.
Source code in nnx/seeding.py
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
2. Orchestrators¶
2.1. NNModel — supervised orchestrator¶
nnx.nn.nn_model.NNModel
¶
Bases: _HubMixinBase
Top-level training/eval/predict wrapper around an nn.Module.
Inherits from :class:huggingface_hub.PyTorchModelHubMixin (when the
thekaveh-nnx[hub] extra is installed) to gain save_pretrained /
push_to_hub / from_pretrained. Without the extra installed,
those three methods raise a clear ImportError pointing at the extra;
no other NNModel functionality is affected.
Source code in nnx/nn/nn_model.py
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 244 245 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 311 312 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 355 356 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 391 392 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 430 431 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 473 474 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 524 525 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 561 562 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 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 | |
evaluate(loader: DataLoader, extra_metrics=None) -> NNEvaluationDataPoint
¶
Aggregate predictions across all batches in loader and compute
a single NNEvaluationDataPoint. Aggregating (rather than averaging
per-batch metrics) gives correct sample-weighted f1/precision/recall
when the final batch is short.
Raises ValueError if the loader yields zero batches — previously produced NaN metrics silently from np.mean over an empty list.
Source code in nnx/nn/nn_model.py
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 | |
export_state_dict(path: str) -> str
¶
Save just self.net.state_dict() to path.
The file is a plain torch.save of a state-dict — loadable by
any torch consumer without nnx installed, and by
:func:nnx.finetune.load_pretrained for the fine-tuning round-trip.
Companion to the NNCheckpoint format, which carries the params +
idp wrapper alongside the weights; export_state_dict strips
all of that and leaves just the weights.
Returns path so calls can be chained.
Source code in nnx/nn/nn_model.py
567 568 569 570 571 572 573 574 575 576 577 578 579 580 | |
freeze(*patterns: str) -> int
¶
Freeze parameters under self.net matching any of patterns
(fnmatch globs against the dotted parameter name). Returns the
number of parameters newly frozen.
Convenience wrapper around :func:nnx.finetune.freezing.freeze
— use the standalone function when freezing a module that isn't
self.net (e.g., a custom decoder hanging off this model).
Source code in nnx/nn/nn_model.py
547 548 549 550 551 552 553 554 555 556 557 558 | |
predict(X) -> PredictResult
¶
Run the network in eval mode and return logits + argmax classes.
Accepts any of:
np.ndarray(single input tensor) — historical API.tuple[np.ndarray, ...]— for multi-input networks.torch.Tensor/tuple[torch.Tensor, ...]— skips the numpy conversion when callers already have tensors.DataLoader— iterates the loader, runs predictions per batch, concatenates and returns the full result. Y labels in the batch (if present) are ignored.
Returns a PredictResult (a NamedTuple of (logits, classes))
that unpacks like the original 2-tuple.
Non-destructive: self.net.training is snapshotted before
switching to eval() and restored on exit (matches
NNModel.evaluate, nnx.viz.activation_map, and
nnx.lr_finder). Without this, a caller doing the common
train → predict → train-more pattern silently leaves the net
in .eval() mode.
Source code in nnx/nn/nn_model.py
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 | |
to_onnx(path: str, example_input: Union[torch.Tensor, tuple, np.ndarray], input_names: Optional[list[str]] = None, output_names: Optional[list[str]] = None, dynamic_batch: bool = True, opset_version: int = 17, dynamo: bool = False) -> str
¶
Export the underlying network to ONNX format.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
output filename (e.g., "model.onnx"). |
required |
example_input
|
Union[Tensor, tuple, ndarray]
|
a tensor (or tuple of tensors for multi-input nets) with realistic shape/dtype used to trace the network. |
required |
input_names
|
Optional[list[str]]
|
optional list of human-readable input port names. |
None
|
output_names
|
Optional[list[str]]
|
optional list of human-readable output port names. |
None
|
dynamic_batch
|
bool
|
when True (default), marks dim 0 as dynamic so the exported model accepts any batch size at inference. |
True
|
opset_version
|
int
|
ONNX opset to target. 17 is broadly supported by current runtimes. |
17
|
dynamo
|
bool
|
when False (default), uses the legacy TorchScript-based
|
False
|
Returns the path written. Network is put in eval mode for tracing.
Source code in nnx/nn/nn_model.py
243 244 245 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 311 312 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 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | |
train(params: NNTrainParams, callbacks: Optional[list[CallbackLike]] = None, train_step_fn: Optional[TrainStepFn] = None) -> NNRun
¶
Run the training loop and return the resulting NNRun.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
NNTrainParams
|
dataloaders + optim + scheduler + epochs + seed. The train_loader is required; val_loader is optional (skips the per-epoch evaluation when absent). |
required |
callbacks
|
Optional[list[CallbackLike]]
|
optional list of |
None
|
train_step_fn
|
Optional[TrainStepFn]
|
optional override for the per-batch training
step. When None (default), runs |
None
|
Returns:
| Type | Description |
|---|---|
NNRun
|
An |
NNRun
|
|
NNRun
|
object is returned with the in-memory idps list attached. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
FloatingPointError
|
from |
Source code in nnx/nn/nn_model.py
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 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 | |
unfreeze(*patterns: str) -> int
¶
Mirror of :meth:freeze — set requires_grad=True on
matching parameters.
Source code in nnx/nn/nn_model.py
560 561 562 563 564 565 | |
nnx.nn.nn_model.PredictResult
¶
Bases: NamedTuple
Structured result of NNModel.predict().
Unpacks positionally as (logits, classes) so callers doing
log, hat = model.predict(X) keep working after the upgrade from
the original 2-tuple. Field access (result.logits, result.classes)
is preferred for new code.
Source code in nnx/nn/nn_model.py
92 93 94 95 96 97 98 99 100 101 102 | |
nnx.nn.nn_model.TrainStepContext
dataclass
¶
Frozen bundle of state passed into a training-step function.
The default default_train_step runs the standard supervised
forward/backward/step. Users can pass their own
train_step_fn: Callable[[TrainStepContext], NNEvaluationDataPoint]
to NNModel.train() for non-supervised paradigms (autoencoder, VAE,
link prediction, recommendation, diffusion, etc.). The custom step
is fully responsible for forward, backward, optimizer.step,
gradient accumulation, AMP scale/unscale, grad clipping, and the
NaN/Inf guard — the context tells it what knobs are set; honoring
them is on the caller.
Source code in nnx/nn/nn_model.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
nnx.nn.nn_model.TrainStepFn = Callable[[TrainStepContext], NNEvaluationDataPoint]
module-attribute
¶
nnx.nn.nn_model.default_train_step(ctx: TrainStepContext) -> NNEvaluationDataPoint
¶
Standard supervised training step: forward → loss → backward → step.
This is the body that NNModel.train() runs when no custom
train_step_fn is supplied. It honors:
- gradient accumulation (zero_grad at cycle start, step at cycle
end). Caveat: when an epoch's batch count isn't a multiple of
accumulate_grad_batches, the trailing partial cycle's grads
are zeroed at the next epoch's first batch (or dropped at the
final epoch's end) without an optimizer step — size your loader
or accumulation factor accordingly.
- AMP (unscales before grad clip; scaler.step + update at cycle end)
- grad clipping by L2 norm
- the NaN/Inf guard (raises FloatingPointError on divergent loss)
- extra_metrics injection on the returned NNEvaluationDataPoint
Custom training-step functions can call this directly to layer on behavior (e.g., extra logging) without reimplementing the standard forward/backward dance.
Source code in nnx/nn/nn_model.py
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | |
2.2. GenerativeNNModel — decoder-only LM orchestrator¶
nnx.nn.generative_nn_model.GenerativeNNModel
¶
Bases: NNModel
Language model with an autoregressive generate() method.
tokenizer is held as a regular instance attribute (not a
constructor-arg of NNModel) so existing NNModel callers don't
have to know about it. It's required for generate() but
optional at construction — train-time you can build the model
first and attach the tokenizer later.
Source code in nnx/nn/generative_nn_model.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 311 312 313 314 315 | |
generate(prompt: str, *, max_new_tokens: int = 64, temperature: float = 1.0, top_k: Optional[int] = None, top_p: Optional[float] = None, repetition_penalty: float = 1.0, stop: Optional[list[str]] = None, seed: Optional[int] = None, use_cache: bool = True, logits_chain: Optional[LogitsChain] = None) -> str
¶
Autoregressive decode from prompt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prompt
|
str
|
input text. Encoded via |
required |
max_new_tokens
|
int
|
hard cap on new tokens emitted. Generation
also stops if the context window (max_seq_len) would be
exceeded and the model can't shrink the window further,
or if a |
64
|
temperature
|
float
|
0 means greedy (argmax). Higher values produce more diverse output. Routes through TemperatureScaling. |
1.0
|
top_k
|
Optional[int]
|
keep only the top-k logits. None disables. |
None
|
top_p
|
Optional[float]
|
nucleus (top-p) cutoff. None disables. |
None
|
repetition_penalty
|
float
|
divide previously-seen tokens' positive logits by this. 1.0 is no-op (default). |
1.0
|
stop
|
Optional[list[str]]
|
list of stop strings — generation halts once any of them appears in the decoded CONTINUATION (the prompt itself is not searched, so a prompt containing a stop string doesn't halt generation immediately; a stop string straddling the prompt/continuation boundary is likewise not detected — matching the generated-text-only convention HF uses). |
None
|
seed
|
Optional[int]
|
when set, sampling is reproducible — two calls with the same seed + prompt + model produce identical output. |
None
|
use_cache
|
bool
|
when True (default), uses an incremental KV cache — each new token only re-runs attention on the last position, not the whole prefix. When False, falls back to the full-recompute path (kept for regression testing). Both paths produce the same tokens for greedy decoding (sampling paths agree given the same seed). |
True
|
logits_chain
|
Optional[LogitsChain]
|
optional pre-built |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The full decoded string (prompt + generated continuation). |
Non-destructive: self.net.training is snapshotted before
switching to eval() and restored on exit (including the
exception path via try/finally). Matches the convention
used by NNModel.predict / NNModel.evaluate,
nnx.diffusion.sample, nnx.embeddings.embed_texts,
nnx.viz.activation_map, and nnx.lr_finder.
Source code in nnx/nn/generative_nn_model.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
2.3. Trainer — multi-optimizer orchestrator¶
nnx.trainer.trainer.Trainer
¶
Multi-optimizer training orchestrator.
Constructed around a single NNModel. At train() time, builds one torch.optim.Optimizer per entry in NNTrainerParams.optims (each scoped to its sub-net via NNOptimParams.param_groups) and invokes the user-supplied trainer_step_fn for each batch.
Same NNRun + per-tag NNCheckpoint cadence as NNModel.train(),
with the extra trainer block on NNRun preserving the multi-optim
configuration on disk.
Source code in nnx/trainer/trainer.py
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 311 312 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 355 356 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 391 392 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 430 431 432 | |
train(params: NNTrainerParams, trainer_step_fn: TrainerStepFn, callbacks: Optional[list[CallbackLike]] = None) -> NNRun
¶
Run the multi-optimizer training loop and return the resulting NNRun.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
NNTrainerParams
|
NNTrainerParams — train_loader + n_epochs + optims dict + (optional) schedulers dict + (optional) val_loader, seed, save_phase_checkpoints, extra_metrics. |
required |
trainer_step_fn
|
TrainerStepFn
|
required. |
required |
callbacks
|
Optional[list[CallbackLike]]
|
optional list of Callback instances. The callback
context exposes |
None
|
Returns:
| Type | Description |
|---|---|
NNRun
|
NNRun with per-iteration idps, persisted under runs/ |
NNRun
|
alongside the standard FIRST/Q1/Q2/Q3/LAST/BEST checkpoints. |
Raises:
| Type | Description |
|---|---|
ValueError
|
when params is None, params.train_loader is None, trainer_step_fn is None, or any optim's NNOptimParams.is_valid() returns False. |
Source code in nnx/trainer/trainer.py
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 311 312 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 355 356 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 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | |
nnx.trainer.trainer.TrainerStepContext
dataclass
¶
Per-batch state passed into a trainer_step_fn.
Mirrors TrainStepContext from NNModel.train() but with optimizer
(singular) replaced by optimizers (name-keyed dict) and schedulers
threaded through alongside — multi-optim hooks may want to reach
into either at any point during a step (e.g., for warmup logic).
model is the single NNModel the Trainer was constructed with;
model.net carries the actual nn.Module (which may itself be a
composite, e.g., a GAN-style wrapper exposing G and D as submodules).
Source code in nnx/trainer/trainer.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | |
nnx.trainer.trainer.TrainerStepFn = Callable[[TrainerStepContext], NNEvaluationDataPoint]
module-attribute
¶
nnx.trainer.params.NNTrainerParams
dataclass
¶
Configuration for Trainer.train() — the multi-optimizer parallel
to NNModel.train() / NNTrainParams.
optims is a name-keyed mapping of NNOptimParams; each entry
produces a distinct torch Optimizer. Use NNOptimParams.param_groups
on each entry (the fine-tuning hook from :mod:nnx.finetune) to scope an optimizer
to a subset of the model's parameters — e.g., one optim for the
generator sub-net (name_pattern="G.*"), one for the discriminator
(name_pattern="D.*") inside a single combined NNModel.
schedulers is similarly keyed and indexes the same names. Missing
entries default to ReduceLROnPlateau with the same defaults
NNTrainParams uses, so callers only have to populate schedulers for
the optims they want to customize.
seed, save_phase_checkpoints, extra_metrics, train_loader,
val_loader mirror NNTrainParams exactly — the orchestration around
a single user-supplied trainer_step_fn is otherwise identical.
Source code in nnx/trainer/params.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
builder() -> NNTrainerParamsBuilder
classmethod
¶
Return a composite multi-optim builder. See
NNTrainerParamsBuilder. Composes
NNOptimParams.builder() + NNSchedulerParams.builder().
Source code in nnx/trainer/params.py
119 120 121 122 123 124 125 126 127 | |
nnx.trainer.params_builder.NNTrainerParamsBuilder
¶
Composite builder for NNTrainerParams.
Reach via NNTrainerParams.builder(). The required setter is
.n_epochs(N); at least one .optimizer(name, params) call is
also required (NNTrainerParams.__post_init__ rejects empty
optims). Schedulers, seed, loaders, etc. are all chained optionals.
Source code in nnx/trainer/params_builder.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
build() -> NNTrainerParams
¶
Validate the key-subset invariant, then construct the dataclass.
schedulers.keys() ⊆ optims.keys() is the contract
NNTrainerParams.__post_init__ enforces. We check here so the
user sees the violation at the Builder boundary — e.g., they
called .scheduler("d", ...) without first calling
.optimizer("d", ...) — rather than at the dataclass ctor.
n_epochs has no meaningful default — call .n_epochs(N) before
.build(). Caught here too, for the same Builder-boundary reason.
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/trainer/params_builder.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | |
extra_metrics(metrics: Mapping[str, Callable]) -> NNTrainerParamsBuilder
¶
Extra metrics callables, name-keyed. Each is called with (y_pred, y_true) at every validation step.
Source code in nnx/trainer/params_builder.py
88 89 90 91 92 | |
n_epochs(n: int) -> NNTrainerParamsBuilder
¶
Number of training epochs. Required.
Source code in nnx/trainer/params_builder.py
42 43 44 45 | |
optimizer(name: str, params: NNOptimParams) -> NNTrainerParamsBuilder
¶
Register one optimizer under name. Each name gets its
own torch.optim.Optimizer at Trainer.train() time. Use
NNOptimParams.builder() (Plan 2) to construct params.
Source code in nnx/trainer/params_builder.py
47 48 49 50 51 52 | |
save_phase_checkpoints(value: bool) -> NNTrainerParamsBuilder
¶
Whether to write phase checkpoints (FIRST / Q1 / Q2 / Q3 /
LAST / BEST). Default True. The fluent contract is "last call
wins" — a prior .save_phase_checkpoints(False) followed by
.save_phase_checkpoints(True) leaves the dataclass at the
default (which state() then omits).
Source code in nnx/trainer/params_builder.py
67 68 69 70 71 72 73 74 | |
scheduler(name: str, params: NNSchedulerParams) -> NNTrainerParamsBuilder
¶
Register one scheduler under name. The name must match a
previously-registered .optimizer(name, ...) call — .build()
enforces the subset invariant.
Source code in nnx/trainer/params_builder.py
54 55 56 57 58 59 | |
seed(value: int) -> NNTrainerParamsBuilder
¶
Seed for reproducibility. None at default (no seeding via
params; the caller's set_seed() is the only path).
Source code in nnx/trainer/params_builder.py
61 62 63 64 65 | |
train_loader(loader: DataLoader) -> NNTrainerParamsBuilder
¶
Training DataLoader. Optional at Builder time (can be wired later via NNTrainerParams.with_train_loader).
Source code in nnx/trainer/params_builder.py
76 77 78 79 80 | |
val_loader(loader: DataLoader) -> NNTrainerParamsBuilder
¶
Validation DataLoader. Optional at Builder time (can be wired later via NNTrainerParams.with_val_loader).
Source code in nnx/trainer/params_builder.py
82 83 84 85 86 | |
3. Params¶
nnx.nn.params.nn_params.NNParams
dataclass
¶
Source code in nnx/nn/params/nn_params.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
resolve_from_state(state: dict) -> NNParams
staticmethod
¶
Dispatch to the params subclass that wrote state.
NNTransformerParams.state() always emits its required
architectural keys (vocab_size among them); base
NNParams.state() never does. Without this dispatch a
transformer state is silently downgraded to base NNParams —
the subclass keys are dropped, the reloaded run re-hashes to a
different id, and net rebuilding crashes. Every loader
(NNRun.load, the NNCheckpoint readers, hub
from_pretrained) resolves through here.
Source code in nnx/nn/params/nn_params.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
nnx.nn.params.nn_model_params.NNModelParams
dataclass
¶
Source code in nnx/nn/params/nn_model_params.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
nnx.nn.params.nn_train_params.NNTrainParams
dataclass
¶
Training configuration.
seed pins every RNG that affects training (Python random, NumPy,
torch CPU+CUDA, cuDNN) when NNModel.train() runs. None disables
seeding (default).
To preserve back-compat with previously-saved runs, seed is included
in state() ONLY when set — so existing runs with no seed continue to
hash to the same run.id.
Source code in nnx/nn/params/nn_train_params.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
nnx.nn.params.nn_optim_params.NNOptimParams
dataclass
¶
Optimizer config.
momentum is overloaded by optimizer kind:
- For SGD / SGD_NESTEROV: a single float, the SGD momentum coefficient.
- For ADAM / ADAM_AMSGRAD: a (beta1, beta2) tuple, passed as the
Adam betas= argument. The name is retained for backwards
compatibility — is_valid() enforces the per-optim shape.
grad_clip_norm clips gradients by global L2 norm before optimizer.step().
None = no clipping (back-compat default). Typical values: 1.0 for
transformers, 5.0 for RNNs.
accumulate_grad_batches enables gradient accumulation — the effective
batch size becomes batch_size * accumulate_grad_batches. The loss is
scaled by 1/N so the accumulated gradient is the mean across N batches.
Default 1 (back-compat: step every batch).
param_groups enables per-layer-group LR / weight_decay overrides — the
fine-tuning idiom of "small LR on the backbone, large LR on the head."
None = single-group behavior (every parameter at max_lr / weight_decay).
When set, the optimizer factory dispatches via
:func:nnx.finetune.param_groups.build_param_groups to construct
per-group dicts.
Source code in nnx/nn/params/nn_optim_params.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
builder() -> NNOptimParamsBuilder
classmethod
¶
Return a variant-aware builder. See NNOptimParamsBuilder.
Source code in nnx/nn/params/nn_optim_params.py
162 163 164 165 166 167 | |
nnx.nn.params.nn_optim_params_builder.NNOptimParamsBuilder
¶
Variant-aware builder for NNOptimParams.
Reach via NNOptimParams.builder(). Pick exactly one variant
method (adam, adam_amsgrad, sgd, sgd_nesterov), then chain
optional methods (grad_clip, accumulate_grad, param_groups),
then .build(). Method-call order is independent — a modifier
called before a variant survives the variant call, and the last
variant always wins.
Source code in nnx/nn/params/nn_optim_params_builder.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
accumulate_grad(batches: int) -> NNOptimParamsBuilder
¶
Accumulate gradients over batches mini-batches before
stepping. Default (no call) leaves the dataclass at 1.
Source code in nnx/nn/params/nn_optim_params_builder.py
135 136 137 138 139 140 | |
adam(*, max_lr: float, betas: tuple[float, float] = (0.9, 0.999), weight_decay: float = 0.0) -> NNOptimParamsBuilder
¶
torch.optim.Adam. betas is PyTorch's name for the
(beta1, beta2) tuple; the Builder maps it onto the underlying
NNOptimParams.momentum field (which holds the tuple for Adam
variants).
Source code in nnx/nn/params/nn_optim_params_builder.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
adam_amsgrad(*, max_lr: float, betas: tuple[float, float] = (0.9, 0.999), weight_decay: float = 0.0) -> NNOptimParamsBuilder
¶
torch.optim.Adam with amsgrad=True. Same betas mapping
as adam().
Source code in nnx/nn/params/nn_optim_params_builder.py
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
build() -> NNOptimParams
¶
Construct the dataclass from the fields the user touched.
Pre-empts the dataclass's missing-required-argument TypeError with an actionable Builder-level ValueError naming the variant methods — matches the [[builder-pattern-shape]] §11b convention that PR #52 established on NNTrainerParamsBuilder.
Forwards only the keys present in self._fields so the
dataclass defaults govern every untouched optional field —
that's what preserves the omit-when-default state() invariant.
Raises:
| Type | Description |
|---|---|
ValueError
|
if no variant method ( |
Source code in nnx/nn/params/nn_optim_params_builder.py
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
grad_clip(norm: float) -> NNOptimParamsBuilder
¶
Global-L2 gradient-norm clipping. None = no clipping (the dataclass default; this method is the opt-in path).
Source code in nnx/nn/params/nn_optim_params_builder.py
128 129 130 131 132 133 | |
param_groups(groups: list[NNParamGroupSpec]) -> NNOptimParamsBuilder
¶
Per-layer-group LR / weight_decay overrides (the fine-tuning idiom). Default (no call) leaves the dataclass at None (single-group behavior).
Source code in nnx/nn/params/nn_optim_params_builder.py
142 143 144 145 146 147 148 | |
sgd(*, max_lr: float, momentum: float = 0.9, weight_decay: float = 0.0) -> NNOptimParamsBuilder
¶
torch.optim.SGD. The float momentum stays as momentum
(no rename) — betas is an Adam-family term.
Source code in nnx/nn/params/nn_optim_params_builder.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
sgd_nesterov(*, max_lr: float, momentum: float = 0.9, weight_decay: float = 0.0) -> NNOptimParamsBuilder
¶
torch.optim.SGD with nesterov=True. Same momentum shape
as sgd().
Source code in nnx/nn/params/nn_optim_params_builder.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
nnx.nn.params.nn_scheduler_params.NNSchedulerParams
dataclass
¶
Source code in nnx/nn/params/nn_scheduler_params.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
builder() -> NNSchedulerParamsBuilder
classmethod
¶
Return a variant-aware builder. See NNSchedulerParamsBuilder.
Source code in nnx/nn/params/nn_scheduler_params.py
109 110 111 112 113 114 | |
nnx.nn.params.nn_scheduler_params_builder.NNSchedulerParamsBuilder
¶
Variant-aware builder for NNSchedulerParams.
Reach this via NNSchedulerParams.builder(). Each variant method
is self-contained — the user calls exactly one of them per builder
instance. Calling a second variant overwrites the first (last
write wins); .build() produces the dataclass.
Source code in nnx/nn/params/nn_scheduler_params_builder.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
build() -> NNSchedulerParams
¶
Construct the dataclass from the fields the user touched.
Pre-empts the dataclass's missing-required-argument TypeError with an actionable Builder-level ValueError naming the variant methods — matches the [[builder-pattern-shape]] §11b convention that PR #52 established on NNTrainerParamsBuilder.
Forwards only the keys present in self._fields so the
dataclass defaults govern every untouched field — that's what
preserves the omit-when-default state() invariant.
Raises:
| Type | Description |
|---|---|
ValueError
|
if no variant method ( |
Source code in nnx/nn/params/nn_scheduler_params_builder.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | |
cosine_annealing(*, T_max: int, min_lr: float, factor: float, patience: int, cooldown: int, threshold: float) -> NNSchedulerParamsBuilder
¶
torch.optim.lr_scheduler.CosineAnnealingLR — anneal LR over
T_max steps.
Source code in nnx/nn/params/nn_scheduler_params_builder.py
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | |
linear_warmup_decay(*, warmup_steps: int, total_steps: int, min_lr: float, factor: float, patience: int, cooldown: int, threshold: float) -> NNSchedulerParamsBuilder
¶
Linear warm-up to max_lr over warmup_steps, linear decay
to 0 over the remaining total_steps - warmup_steps. Used by
most transformer training recipes.
Source code in nnx/nn/params/nn_scheduler_params_builder.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | |
one_cycle(*, max_lr: float, total_steps: int, min_lr: float, factor: float, patience: int, cooldown: int, threshold: float) -> NNSchedulerParamsBuilder
¶
torch.optim.lr_scheduler.OneCycleLR — Smith one-cycle schedule
with peak LR max_lr over total_steps steps.
Source code in nnx/nn/params/nn_scheduler_params_builder.py
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
reduce_on_plateau(*, min_lr: float, factor: float, patience: int, cooldown: int, threshold: float) -> NNSchedulerParamsBuilder
¶
ReduceLROnPlateau — the default scheduler.
Sets the five plateau fields. kind is left at None (the
dataclass default), which preserves the omit-when-default
state() invariant for callers who used the original pre-enum
config.
Source code in nnx/nn/params/nn_scheduler_params_builder.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
step(*, step_size: int, min_lr: float, factor: float, patience: int, cooldown: int, threshold: float) -> NNSchedulerParamsBuilder
¶
torch.optim.lr_scheduler.StepLR — decay LR by factor every
step_size epochs. The plateau-shape fields (min_lr,
patience, cooldown, threshold) are not consumed by
StepLR but are required by the underlying NNSchedulerParams
dataclass and serialised for back-compat.
Source code in nnx/nn/params/nn_scheduler_params_builder.py
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | |
nnx.nn.params.nn_transformer_params.NNTransformerParams
dataclass
¶
Bases: NNParams
Source code in nnx/nn/params/nn_transformer_params.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
builder() -> NNTransformerParamsBuilder
classmethod
¶
Return a fluent LM-path builder. See NNTransformerParamsBuilder.
Source code in nnx/nn/params/nn_transformer_params.py
170 171 172 173 174 175 | |
nnx.nn.params.nn_transformer_params_builder.NNTransformerParamsBuilder
¶
Builder for NNTransformerParams.
Reach via NNTransformerParams.builder(). The six methods can be
chained in any order; .build() collects them, fills in the
LM-path defaults for the dead parent-NNParams fields, and
constructs the dataclass.
Source code in nnx/nn/params/nn_transformer_params_builder.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
build() -> NNTransformerParams
¶
Construct the dataclass.
Pre-empts the dataclass's missing-required-argument TypeError with an actionable Builder-level ValueError naming the setter methods that haven't been called yet — matches the [[builder-pattern-shape]] §11b convention that PR #52 established on NNTrainerParamsBuilder.
Fills in the dead parent-NNParams fields the TransformerNN
net never reads but the parent dataclass requires at
construction. activation mirrors the parent NNParams's
default (Activations.LEAKY_RELU); a Builder-default
mismatch here previously produced a different state() /
run.id than the direct-kwarg ctor.
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/nn/params/nn_transformer_params_builder.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
context(*, max_seq_len: int, rope_base: Optional[float] = None) -> NNTransformerParamsBuilder
¶
Context-length and RoPE base. max_seq_len is required;
rope_base=None is the sentinel for "use the dataclass default
(10000.0, the LLaMA / GPT convention)". The fluent contract is
"last call wins": .context(rope_base=500000.0).context(max_seq_len=128)
resets rope_base to the default — the second call's implicit
rope_base=None drops the prior override.
Source code in nnx/nn/params/nn_transformer_params_builder.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |
dropout(*, attn: float = 0.0, resid: float = 0.0) -> NNTransformerParamsBuilder
¶
Attention and residual dropout rates. Defaults are both 0.0 (modern LLM convention; regularization comes from data scale, not dropout).
Like .context(), a dropout() call specifies BOTH rates
together — each call fully replaces the pair, and a rate left
at its 0.0 default is reset, not carried over from a prior
call. So .dropout(resid=0.3).dropout(attn=0.5) yields
attn=0.5, resid=0.0 (the second call's implicit resid=0.0
drops the prior override); call .dropout(attn=0.5, resid=0.3)
once to set both. Same-field last-call-wins still holds:
.dropout(attn=0.5).dropout(attn=0.0) resets to attn=0.0.
The dataclass's omit-when-default state() then handles
run.id stability automatically.
Source code in nnx/nn/params/nn_transformer_params_builder.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
ffn(*, mult: int) -> NNTransformerParamsBuilder
¶
FFN expansion ratio. Default is 4 (the SwiGLU-friendly ratio); only call this method to override.
Source code in nnx/nn/params/nn_transformer_params_builder.py
62 63 64 65 66 | |
layers(*, n: int, heads: int, d_model: int) -> NNTransformerParamsBuilder
¶
Set depth (n_layers), attention head count (n_heads),
and hidden dimension (d_model). Enforces
d_model % heads == 0 immediately — this is the Builder's
safety value-add over the direct-kwarg ctor, which only
catches the mismatch at post_init time after all kwargs
have already been typed.
Source code in nnx/nn/params/nn_transformer_params_builder.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
tied_embeddings(value: bool) -> NNTransformerParamsBuilder
¶
Toggle weight-tying between input embeddings and LM head.
Default is True. The fluent contract is "last call wins" — a
prior .tied_embeddings(False) followed by .tied_embeddings(True)
leaves the dataclass at the default (which state() then omits).
Source code in nnx/nn/params/nn_transformer_params_builder.py
114 115 116 117 118 119 120 | |
vocab(size: int) -> NNTransformerParamsBuilder
¶
Set the vocabulary size. Mirrors into both input_dim and
output_dim on the parent NNParams (the LM convention).
Source code in nnx/nn/params/nn_transformer_params_builder.py
31 32 33 34 35 36 37 | |
nnx.nn.params.nn_tokenizer_params.NNTokenizerParams
dataclass
¶
Frozen dataclass holding a tokenizer + its on-disk pointer.
The dataclass is frozen so it can sit alongside NNTransformerParams /
NNModelParams in an NNRun without inviting in-place mutation. The
actual tokenizers.Tokenizer object is held in a repr=False field
so it doesn't bloat the str() output.
Source code in nnx/nn/params/nn_tokenizer_params.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
from_state(state: dict) -> NNTokenizerParams
staticmethod
¶
Load from a state dict produced by :meth:state. The single
required key is path; the tokenizer is reconstructed from
the file the path points to.
The path is stored exactly as the caller gave it to :meth:of —
typically cwd-relative — so loading from a different working
directory requires the same relative layout. That's deliberate:
storing an absolute path would break run portability across
machines, which is the more common need.
Source code in nnx/nn/params/nn_tokenizer_params.py
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | |
of(tokenizer: object, path: str) -> NNTokenizerParams
staticmethod
¶
Construct from a live Tokenizer instance and persist it to path.
This is the train-time entry point: train a tokenizer, then call
NNTokenizerParams.of(tk, path="runs/tok.json") to wrap it
with a paired on-disk artifact.
Source code in nnx/nn/params/nn_tokenizer_params.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
state() -> dict
¶
Return the serializable view — only the path goes into run.yaml.
Source code in nnx/nn/params/nn_tokenizer_params.py
97 98 99 | |
nnx.nn.params.nn_tokenizer_params.train_bpe(files: Optional[list[str]] = None, *, vocab_size: int = 8192, texts: Optional[list[str]] = None, special_tokens: Optional[list[str]] = None, min_frequency: int = 2) -> Tokenizer
¶
Train a BPE tokenizer on either a list of files or a list of texts.
Mirrors the HF "quick BPE" recipe — Whitespace pre-tokenizer + BPE
model + BpeTrainer. Returns the trained Tokenizer instance; the
caller is responsible for persisting via
NNTokenizerParams.of(tk, path=...).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
files
|
Optional[list[str]]
|
paths to plaintext files (one corpus line per file row).
If None, |
None
|
vocab_size
|
int
|
target vocab. Actual size may be smaller for tiny corpora. |
8192
|
texts
|
Optional[list[str]]
|
in-memory list of training strings — useful for unit tests and the examples without writing a temp file. |
None
|
special_tokens
|
Optional[list[str]]
|
e.g. |
None
|
min_frequency
|
int
|
minimum pair frequency to merge — higher values give smaller, more conservative vocabs. |
2
|
Returns:
| Name | Type | Description |
|---|---|---|
Tokenizer |
Tokenizer
|
a trained |
Source code in nnx/nn/params/nn_tokenizer_params.py
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | |
nnx.nn.params.nn_run.NNRun
dataclass
¶
Source code in nnx/nn/params/nn_run.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 311 312 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 355 356 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 391 392 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 430 431 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 473 474 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 | |
all(root: Optional[str] = None) -> list[NNRun]
staticmethod
¶
List every saved NNRun under the runs root, skipping the best
pointer. Returns [] when the runs/ directory doesn't exist yet.
Non-directory entries (stray files, .DS_Store) are filtered out
so they don't trigger spurious NNRun.load failures.
Source code in nnx/nn/params/nn_run.py
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | |
checkpoints(root: Optional[str] = None) -> list[Optional[NNCheckpoint]]
¶
Load this run's five phase checkpoints, in cadence order
(FIRST, Q1, Q2, Q3, LAST). Entries are None when the tag was
never written — e.g. runs trained with
save_phase_checkpoints=False write only LAST and BEST.
BEST is deliberately excluded: it duplicates whichever phase
checkpoint won, so including it would double-count. Load it
directly via NNCheckpoint.load(run=run.id,
type=Checkpoints.BEST).
Source code in nnx/nn/params/nn_run.py
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | |
nnx.nn.params.nn_checkpoint.NNCheckpoint
dataclass
¶
Source code in nnx/nn/params/nn_checkpoint.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 | |
from_file(path: str) -> Optional[NNCheckpoint]
staticmethod
¶
Load an NNCheckpoint from disk, auto-detecting pickle vs safetensors.
Returns None if the path doesn't exist or the loaded pickle
object isn't an NNCheckpoint instance.
Dispatch is by magic bytes:
torch.savewrites a ZIP archive in modern PyTorch (_use_new_zipfile_serialization=Trueis the default since PyTorch 1.6), so the file starts withb"PK\x03\x04".- Legacy
torch.save(with the zipfile serialization disabled) and bare pickle files begin with\x80(the pickle PROTO opcode for protocol >= 2). - safetensors files begin with a little-endian u64 header length
followed by a JSON object — byte 8 is always
{. The u64's LOW byte can legitimately be0x80(any header length ≡ 128 mod 256), which would collide with the pickle PROTO opcode — so safetensors is positively identified by byte 8 BEFORE thepickle check. The ZIP magic is checked first of all (a ZIP's byte 8 is the compression method, never{; a torch-LEGACY pickle has the fixed magic byte0xf9at offset 8, and a protocol ≥ 4 bare pickle has a frame-length byte there,0x00for any file under a terabyte. A protocol-2/3 bare pickle's byte 8 is content-dependent, but NNx never produces bare pickles and such a file failed under the old routing too).
Anything matching none of the positive sniffs falls through to the safetensors loader, whose error on a genuinely corrupt file is clearer than a misleading unpickle attempt.
SECURITY: the pickle branch calls torch.load(weights_only=False),
which unpickles arbitrary Python objects. NEVER call this on a
checkpoint file from an untrusted source — a malicious .pt file
can execute arbitrary code at load time. The default
./runs/<id>/checkpoints/ layout assumes the files were
produced locally by NNCheckpoint.save. For untrusted sources,
use the safetensors path on save and load: safetensors has no
arbitrary-code path.
Source code in nnx/nn/params/nn_checkpoint.py
202 203 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 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | |
load_optimizer_state(run: str, type: Checkpoints, root: Optional[str] = None) -> Optional[OrderedDict]
staticmethod
¶
Load the optimizer state sidecar for a checkpoint. Returns None when no sidecar exists (e.g., checkpoints written before resume support was added).
Loaded with weights_only=True — the optimizer state-dict
contains only tensors and standard scalar/dict/list types, so the
strict loader works AND it removes the arbitrary-code-execution
risk that the main NNCheckpoint.from_file documents.
Source code in nnx/nn/params/nn_checkpoint.py
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
save(run: str, type: Checkpoints, root: Optional[str] = None, optimizer_state: Optional[OrderedDict] = None) -> None
¶
Save the checkpoint to disk atomically.
When optimizer_state is supplied, a sibling file is written at
<id>/checkpoints/<type>.pt.opt.pt (the checkpoint path plus
an .opt.pt suffix) holding the optimizer state dict.
This sidecar is used by NNModel.train(resume_from=...) to warm-resume
with the prior optimizer momentum / Adam state.
Each of the two writes is individually atomic, but the PAIR is not: an interrupt between them leaves a fresh checkpoint beside the previous epoch's sidecar, and a warm-resume then restores one-epoch-stale optimizer state (weights stay correct; momentum is briefly mismatched). Accepted: a validation stamp would change the sidecar format for a one-epoch-soft failure mode.
Source code in nnx/nn/params/nn_checkpoint.py
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
to_file(path: str, format: Literal['pickle', 'safetensors'] = 'pickle') -> None
¶
Atomically write this NNCheckpoint to path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
destination path. Parent directory is created if missing. |
required |
format
|
Literal['pickle', 'safetensors']
|
one of:
|
'pickle'
|
Both formats write to <path>.tmp first and rename into place
so a KeyboardInterrupt during the underlying save can never leave
a half-written checkpoint at the destination — matching the
atomicity guarantee NNRun.save offers for YAML/CSV.
Source code in nnx/nn/params/nn_checkpoint.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | |
nnx.nn.params.nn_iteration_data_point.NNIterationDataPoint
dataclass
¶
One row in the per-iteration training log.
train_edp is computed from the current batch only. val_edp is the
per-epoch validation evaluation — populated only on the last idp of
each epoch (the idp at which the validation loop ran). Other idps in
the same epoch have val_edp=None. When reading idps.csv, group by
epoch_idx and take the row with val_edp set for per-epoch validation
metrics.
Source code in nnx/nn/params/nn_iteration_data_point.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | |
nnx.nn.params.nn_evaluation_data_point.NNEvaluationDataPoint
dataclass
¶
Per-batch / per-epoch evaluation metrics.
The four core fields (f1, recall, accuracy, precision) are computed by
of() via sklearn. loss and error are typically attached after the
fact by NNModel during training / evaluation.
extra is a free-form dict of user-supplied custom metric names to
floats. Populated when NNTrainParams.extra_metrics or evaluate(metrics=)
is set; empty by default (and omitted from state() when empty so that
pre-extra runs hash to the same run.id and pre-extra YAML loads cleanly).
Source code in nnx/nn/params/nn_evaluation_data_point.py
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
mean_of(edps: list[NNEvaluationDataPoint]) -> NNEvaluationDataPoint
staticmethod
¶
Unweighted-mean reduce a list of EDPs across every metric.
.. warning::
This is a **simple mean across edps**, NOT a sample-weighted
mean. With unequal batch sizes (the common case), the result
is statistically incorrect — a 1024-sample batch counts the
same as an 8-sample tail batch. For correct sample-weighted
metrics across batches, use :meth:`NNModel.evaluate`, which
concatenates predictions across the loader and computes once
on the full sample.
``mean_of`` is kept for back-compat with callers that already
depend on the unweighted-mean semantics; new code should
prefer :meth:`NNModel.evaluate` unless the unweighted form is
specifically what's wanted (e.g., averaging across runs, not
across batches within a run).
An extra key present on some but not all edps is averaged over
the edps where it IS present (skipped on the rest).
Source code in nnx/nn/params/nn_evaluation_data_point.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | |
of(Y: np.ndarray, Y_hat: np.ndarray, average: str = 'macro', extra_metrics: Optional[Mapping[str, Callable]] = None)
staticmethod
¶
Compute per-batch evaluation metrics.
average controls how f1/precision/recall reduce across classes.
Default "macro" treats all classes equally — the right choice for
multi-class classification and the only one that makes f1/precision/
recall mathematically distinct from accuracy. Pass "micro" to
recover the legacy behavior (numerically identical to accuracy for
single-label multi-class). Accuracy itself is not affected.
extra_metrics is a {name -> callable(Y, Y_hat) -> float} map of
user-supplied custom metrics. Each is invoked once on the aggregate
predictions and stored in the returned object's extra dict.
Source code in nnx/nn/params/nn_evaluation_data_point.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | |
4. Networks¶
nnx.nn.net.feed_fwd_nn.FeedFwdNN
¶
Bases: Module
Source code in nnx/nn/net/feed_fwd_nn.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
nnx.nn.net.graph_nn_base.GraphNNBase
¶
Bases: Module
Abstract base for GNN architectures.
Subclasses must implement _build_layers() returning an nn.ModuleList
of PyG message-passing layers. The forward loop applies all-but-last
layers with the configured activation + dropout, then a bare final layer.
Source code in nnx/nn/net/graph_nn_base.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | |
seed_count(batch) -> Optional[int]
¶
Number of seed rows at the head of a NeighborLoader subgraph.
NeighborLoader puts the batch_size seed nodes first and
appends their sampled neighbors — which can belong to other
splits. Loss and metrics must be computed on the seed rows only;
scoring neighbor rows leaks val/test labels into the training
loss and train labels into val metrics.
Returns None (no slicing) for anything that isn't a
NeighborLoader subgraph: plain full-graph Data has no
batch_size, and a multi-graph Batch.from_data_list
collation DOES carry batch_size (= num_graphs) but no
input_id — slicing there would truncate node-level output
to the graph count. input_id is the NeighborLoader-specific
marker (the seed indices), so it gates the slice.
Source code in nnx/nn/net/graph_nn_base.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | |
nnx.nn.net.graph_conv_nn.GraphConvNN
¶
Bases: GraphNNBase
Source code in nnx/nn/net/graph_conv_nn.py
9 10 11 12 13 14 15 16 17 18 19 | |
nnx.nn.net.graph_sage_nn.GraphSageNN
¶
Bases: GraphNNBase
Source code in nnx/nn/net/graph_sage_nn.py
9 10 11 12 13 14 15 16 17 18 19 | |
nnx.nn.net.graph_att_nn.GraphAttNN
¶
Bases: GraphNNBase
Source code in nnx/nn/net/graph_att_nn.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
nnx.nn.net.transformer_nn.TransformerNN
¶
Bases: Module
Source code in nnx/nn/net/transformer_nn.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
forward(tokens: torch.Tensor) -> torch.Tensor
¶
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokens
|
Tensor
|
(batch, seq) long tensor of token ids. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
(batch, seq, vocab_size) logits — pre-softmax. |
Source code in nnx/nn/net/transformer_nn.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
forward_with_cache(tokens: torch.Tensor, past_kvs: Optional[list[LayerKV]] = None) -> tuple[torch.Tensor, list[LayerKV]]
¶
Cache-threading forward used by GenerativeNNModel.generate.
Behaves like forward but additionally accepts a per-layer
list of (k, v) caches (or None entries on the first call)
and returns the updated per-layer caches alongside the logits.
The total attended-to length per layer is
past_kv_len + tokens.shape[1] — the caller is responsible
for ensuring that this stays within max_seq_len (the
generate loop slides a window when it would otherwise overflow).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokens
|
Tensor
|
(batch, seq) long tensor of token ids. During
incremental decode, |
required |
past_kvs
|
Optional[list[LayerKV]]
|
list of length |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Tensor, list[LayerKV]]
|
A tuple |
Source code in nnx/nn/net/transformer_nn.py
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | |
unpack_batch(batch)
¶
Make TransformerNN compatible with the standard supervised NNModel training loop.
For an LM the canonical batch is (tokens, targets) where
targets = tokens[:, 1:] shifted by one. We don't shift here —
the caller assembles the tuple — but we accept either a 2-tuple
(X, Y) or a plain tensor of tokens (next-token loss is then
computed in the train step).
Source code in nnx/nn/net/transformer_nn.py
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | |
nnx.nn.net.vit_nn.ViTNN
¶
Bases: Module
Small Vision Transformer encoder.
Forward contract:
forward(x: (B, C, H, W), mask: Optional[BoolTensor[B, n_patches]]=None)
→ (B, T_kept + 1, d_model) if mask provided (T_kept = mask.sum())
→ (B, n_patches + 1, d_model) otherwise.
The leading token is the learned CLS. Patches are flattened in
raster order (row-major over the patch grid). The optional mask
is the I-JEPA "context" mask: True positions are kept, False ones
are dropped before any attention runs, so gradients do not flow
through masked patches.
__init__ requires image_size, patch_size, and
in_channels for the patch-embedding convolution. image_size
must be divisible by patch_size — validated at construction.
Source code in nnx/nn/net/vit_nn.py
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 | |
forward(x: torch.Tensor, mask: Optional[torch.Tensor] = None) -> torch.Tensor
¶
Run the encoder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
(B, C, H, W) input image tensor. |
required |
mask
|
Optional[Tensor]
|
optional BoolTensor of shape |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
|
Tensor
|
kept patches (or |
Tensor
|
is the CLS token at position 0. |
Source code in nnx/nn/net/vit_nn.py
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 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 | |
patch_positions() -> torch.Tensor
¶
Return LongTensor[n_patches] of patch-token positions in
the full sequence (i.e., arange(1, n_patches + 1) — CLS is
position 0).
Exposed so the I-JEPA step factory can derive its context /
target position indices by boolean-masking this tensor instead
of rebuilding the arange (see jepa_train_step_factory).
Source code in nnx/nn/net/vit_nn.py
216 217 218 219 220 221 222 223 224 225 | |
unpack_batch(batch)
¶
Standard (X-tuple, Y) adapter. JEPA doesn't use Y but
the supervised linear-probe path on top of a frozen ViTNN does.
Source code in nnx/nn/net/vit_nn.py
277 278 279 280 281 282 283 284 285 | |
nnx.nn.net.vit_nn.ViTBlock
¶
Bases: Module
Pre-norm ViT block: x = x + attn(RMSNorm(x)); x = x + ffn(RMSNorm(x)).
Same shape as :class:nnx.nn.net.transformer_layers.TransformerBlock
but with bidirectional attention instead of causal. SwiGLU is
reused unchanged.
Source code in nnx/nn/net/vit_nn.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
nnx.nn.moe.MoELinear
¶
Bases: Module
Sparse top-k Mixture-of-Experts drop-in for :class:nn.Linear.
Forward pass:
- Router (a bias-less :class:
nn.Linear) projects input(B, in_features) → (B, num_experts)logits. top_klargest logits per row are kept; a softmax over thosekvalues produces the per-expert gating weight.- Each token is dispatched to its top-
kexperts; expert outputs are weighted by the gating weights and summed into the output tensor. self.last_aux_lossis populated with the Switch-style load-balancing penaltynum_experts · Σ_i f_i · P_i. This is a scalar tensor with gradients wired to the router so optimization of the main loss + this term pushes routing toward uniform expert usage.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_features
|
int
|
input feature dimension (matches |
required |
out_features
|
int
|
output feature dimension (matches |
required |
num_experts
|
int
|
number of expert sub-networks. Must be ≥ 2.
( |
required |
top_k
|
int
|
number of experts each input is routed through. Must
be ≥ 1 and ≤ |
2
|
Attributes:
| Name | Type | Description |
|---|---|---|
router |
bias-less :class: |
|
experts |
:class: |
|
top_k |
how many experts run per token. |
|
num_experts |
total expert count. |
|
last_aux_loss |
Tensor | None
|
scalar |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/nn/moe.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
5. Datasets¶
nnx.nn.dataset.nn_dataset_base.NNDatasetBase
dataclass
¶
Bases: ABC
Source code in nnx/nn/dataset/nn_dataset_base.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
nnx.nn.dataset.nn_dataset.NNDataset
dataclass
¶
Bases: NNDatasetBase
Vision dataset wrapper. val_proportion carves a validation slice
out of the source train=True split (NOT out of the test split, which
stays untouched for final evaluation).
Source code in nnx/nn/dataset/nn_dataset.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
nnx.nn.dataset.nn_graph_dataset.NNGraphDataset
dataclass
¶
Bases: NNDatasetBase
Source code in nnx/nn/dataset/nn_graph_dataset.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | |
nnx.nn.dataset.nn_tabular_dataset.NNTabularDataset
dataclass
¶
Bases: NNDatasetBase
Wrap a pandas DataFrame as train/val/test DataLoaders.
feature_cols columns are stacked into the input tensor; target_col
is the integer label column. Targets are coerced to int64 (long), so
use this for classification. For regression, prefer to construct the
DataLoaders yourself and pass them through NNTrainParams.
Source code in nnx/nn/dataset/nn_tabular_dataset.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
nnx.nn.dataset.nn_preference_dataset.NNPreferenceDataset
dataclass
¶
Bases: NNDatasetBase
Wrap parallel lists of (prompt, chosen, rejected) strings as DPO loaders.
Tokenizes every triple through tokenizer.encode once at
construction, pads/truncates to fixed lengths, then splits into
train / val / test DataLoader\ s with the same shape as the
rest of :class:NNDatasetBase (so callbacks and the standard
training loop work unchanged).
Each batch yielded is (prompt_ids, chosen_ids, rejected_ids)
where each entry is (B, T_*) torch.LongTensor.
Source code in nnx/nn/dataset/nn_preference_dataset.py
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
6. Enums¶
nnx.nn.enum.activations.Activations
¶
Bases: Enum
Source code in nnx/nn/enum/activations.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
nnx.nn.enum.checkpoints.Checkpoints
¶
Bases: Enum
Source code in nnx/nn/enum/checkpoints.py
7 8 9 10 11 12 13 14 15 16 17 18 19 | |
nnx.nn.enum.devices.Devices
¶
Bases: Enum
Source code in nnx/nn/enum/devices.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
get_torch_device() -> torch.device
staticmethod
¶
Convenience: auto-detect and return the corresponding torch.device
directly. Equivalent to Devices.get().torch_device().
Source code in nnx/nn/enum/devices.py
36 37 38 39 40 | |
torch_device() -> torch.device
¶
Explicit alias for self() — more readable in code that mixes
the enum and torch.device usage.
Source code in nnx/nn/enum/devices.py
22 23 24 25 | |
nnx.nn.enum.losses.Losses
¶
Bases: Enum
Source code in nnx/nn/enum/losses.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
nnx.nn.enum.nets.Nets
¶
Bases: Enum
Source code in nnx/nn/enum/nets.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
nnx.nn.enum.optims.Optims
¶
Bases: Enum
Source code in nnx/nn/enum/optims.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
__call__(net: nn.Module, lr_start: float, weight_decay: float, momentum: Union[float, tuple[float, float]], param_groups: Optional[list] = None, strict_param_groups: bool = False) -> optim.Optimizer
¶
Build the underlying torch optimizer.
When param_groups is None (back-compat default), constructs
the optimizer with a single group: every trainable parameter of
net at lr=lr_start, weight_decay=weight_decay.
When param_groups is set to a list of
:class:nnx.finetune.NNParamGroupSpec, dispatches to
:func:nnx.finetune.param_groups.build_param_groups to bucket
parameters by fnmatch pattern and apply per-group LR /
weight_decay overrides. Frozen parameters (requires_grad=False)
are dropped — the optimizer doesn't need to know about them.
strict_param_groups toggles between fine-tuning and
multi-optimizer-Trainer semantics: when False (default),
unmatched parameters go into a default group at lr_start;
when True, unmatched parameters are dropped from the optimizer
entirely. The Trainer passes True so disjoint optimizers don't
end up co-owning the same params via implicit default buckets.
Source code in nnx/nn/enum/optims.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
nnx.nn.enum.schedulers.Schedulers
¶
Bases: Enum
Source code in nnx/nn/enum/schedulers.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | |
7. Callbacks¶
nnx.nn.callbacks.Callback
¶
Base class for training callbacks. Override any subset of the hooks.
Source code in nnx/nn/callbacks.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
nnx.nn.callbacks.EarlyStopping
¶
Bases: Callback
Stop training when the monitored metric stops improving.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
monitor
|
str
|
which IDP field to track. "val_edp.error" (default), "val_edp.loss", "train_edp.error", or "train_edp.loss". |
'val_edp.error'
|
patience
|
int
|
epochs with no improvement before stopping. |
10
|
min_delta
|
float
|
minimum change to qualify as improvement. |
0.0
|
mode
|
str
|
"min" (default) for loss/error; "max" for accuracy/f1. |
'min'
|
Source code in nnx/nn/callbacks.py
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
nnx.nn.callbacks.LRMonitor
¶
Bases: Callback
Logs the current LR each epoch. History exposed at .history.
Source code in nnx/nn/callbacks.py
180 181 182 183 184 185 186 187 188 | |
nnx.nn.callbacks.ModelCheckpoint
¶
Bases: Callback
Save a custom-tagged checkpoint at user-specified epochs.
The standard train() loop already saves FIRST / Q1 / Q2 / Q3 / LAST / BEST via the Checkpoints enum. This callback adds ad-hoc save points outside that cycle — useful for sampling at fixed milestones (e.g., epoch 10, 20, 50) for downstream inspection.
Each match writes <cwd>/runs/<run.id>/checkpoints/<tag>_e<epoch>.pt
— cwd-relative, matching what :meth:NNRun.save and :class:NNCheckpoint
use when called from inside :meth:NNModel.train (the train() entry
point doesn't accept a root= parameter). The epoch suffix
prevents successive matches from overwriting each other when
epochs has multiple entries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
epochs
|
Optional[list[int]]
|
list of 0-indexed epoch numbers at which to save. Empty / None means the callback never fires (and never saves anything). |
None
|
tag
|
str
|
prefix in the filename, defaults to |
'custom'
|
Source code in nnx/nn/callbacks.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
nnx.nn.callbacks.TensorBoardCallback
¶
Bases: Callback
Stream train/val metrics + LR to a TensorBoard SummaryWriter.
Requires tensorboard to be installed — imported lazily so users who
don't use this callback don't pay the dependency cost.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_dir
|
Optional[str]
|
directory passed to SummaryWriter. None lets TensorBoard
pick its default (runs/ |
None
|
flush_each_epoch
|
bool
|
when True (default), calls writer.flush() so partial training is visible in TB even if the process crashes. |
True
|
Source code in nnx/nn/callbacks.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 244 | |
nnx.nn.callbacks.WandbCallback
¶
Bases: Callback
Stream train/val metrics + LR to Weights & Biases.
Requires wandb — lazily imported. Pass project= to start a new run,
or wandb_run= to attach to an externally-managed run.
Source code in nnx/nn/callbacks.py
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 | |
8. Fine-tuning (nnx.finetune)¶
nnx.finetune.freezing.freeze(module: nn.Module, *patterns: str) -> int
¶
Set requires_grad=False on every parameter under module
whose dotted name matches any of patterns.
Patterns use fnmatch shell-glob semantics: * matches any
sequence of characters including dots (not just one path segment),
? matches a single character, [seq] matches one character
from the set. Match is against the parameter's full dotted name,
e.g., encoder.layer.5.weight. So "encoder.*" matches every
parameter under the encoder subtree, including deeply nested ones
like encoder.layer.5.weight.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
any |
required |
*patterns
|
str
|
one or more fnmatch globs. If no patterns are
given, raises |
()
|
Returns:
| Type | Description |
|---|---|
int
|
The number of parameters newly frozen (i.e., previously had |
int
|
|
Source code in nnx/finetune/freezing.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
nnx.finetune.freezing.unfreeze(module: nn.Module, *patterns: str) -> int
¶
Mirror of :func:freeze — set requires_grad=True on matching
parameters. Returns the count newly unfrozen.
Source code in nnx/finetune/freezing.py
58 59 60 61 62 63 64 65 66 67 68 69 | |
nnx.finetune.freezing.frozen(module: nn.Module) -> list[str]
¶
List the dotted parameter names currently frozen under module.
Returned list is sorted by name for stable test assertions. Useful
for logging at train() entry so users can see exactly which
parameters are excluded from training.
Source code in nnx/finetune/freezing.py
72 73 74 75 76 77 78 79 | |
nnx.finetune.loading.load_pretrained(module: nn.Module, source: Union[str, Path, dict, nn.Module], *, key_map: Optional[dict[str, str]] = None, strict: bool = False, prefix: Optional[str] = None) -> LoadPretrainedResult
¶
Load weights into module from an external source.
The source can be
- a path (str or Path) to a
.pt/.pthfile holding a state-dict (loaded withweights_only=Truefor safety); - a state-dict (
dict) already in memory; - another
nn.Module, in which case its state-dict is used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
target module to load into. Mutated in place. |
required |
source
|
Union[str, Path, dict, Module]
|
see above. |
required |
key_map
|
Optional[dict[str, str]]
|
optional remapping from source keys to target keys,
applied AFTER |
None
|
strict
|
bool
|
when True, raise if any source key has no target match OR any target key has no source. Default False (fine-tuning commonly partial-loads). |
False
|
prefix
|
Optional[str]
|
optional prefix to strip from source keys before
matching. E.g., |
None
|
Returns:
| Type | Description |
|---|---|
LoadPretrainedResult
|
class: |
LoadPretrainedResult
|
unexpected key sets. |
Source code in nnx/finetune/loading.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | |
nnx.finetune.loading.LoadPretrainedResult
dataclass
¶
Outcome of a :func:load_pretrained call.
Compared with :meth:torch.nn.Module.load_state_dict, this gives
you back not just the missing/unexpected keys but also the list
of keys actually applied (after any remapping) — useful for
confirming the load did what you intended.
Source code in nnx/finetune/loading.py
24 25 26 27 28 29 30 31 32 33 34 35 36 | |
nnx.finetune.param_groups.NNParamGroupSpec
dataclass
¶
One row in :attr:NNOptimParams.param_groups.
Matches parameters whose dotted name matches name_pattern
(fnmatch glob) and applies the specified lr (absolute) or
lr_multiplier (multiplied by NNOptimParams.max_lr) and
optional weight_decay override.
Exactly one of lr and lr_multiplier may be set. If both are
None the matched parameters use the optimizer's default LR — handy
when you only want to override weight_decay for a group.
Example
Freeze nothing, but train the backbone at 1/100th the head's LR¶
and disable weight_decay on every bias term.¶
NNOptimParams( name=Optims.ADAM, max_lr=1e-3, momentum=(0.9, 0.999), weight_decay=5e-4, param_groups=[ NNParamGroupSpec(name_pattern="encoder.", lr_multiplier=0.01), NNParamGroupSpec(name_pattern=".bias", weight_decay=0.0), ], )
Source code in nnx/finetune/param_groups.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
nnx.finetune.param_groups.build_param_groups(module: nn.Module, specs: list[NNParamGroupSpec], *, default_lr: float, default_weight_decay: float, strict: bool = False) -> list[dict]
¶
Walk module's parameters, bucket them by the first matching
spec (or into a fallback default group), and return the list of
param-group dicts the optimizer expects.
Parameters with requires_grad=False are dropped — they're
frozen, the optimizer doesn't need to see them. (Without this, the
optimizer would still hold them in its state but they'd never
update; harmless but wasteful and confusing in optimizer.param_groups.)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
source of parameters to bucket. |
required |
specs
|
list[NNParamGroupSpec]
|
list of :class: |
required |
default_lr
|
float
|
LR for parameters that don't match any spec, or
for specs that omit both |
required |
default_weight_decay
|
float
|
WD for parameters that don't match any
spec's |
required |
strict
|
bool
|
when False (default, fine-tuning semantics), parameters
that match no spec go into a default group at |
False
|
Returns:
| Type | Description |
|---|---|
list[dict]
|
A list of dicts suitable for ``torch.optim.Optimizer( |
list[dict]
|
params, ...) |
Source code in nnx/finetune/param_groups.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |
9. Parameter-efficient fine-tuning (nnx.peft)¶
LoRA + DoRA + IA3 + Prefix-Tuning + Prompt-Tuning + Adapters. All methods share the same in-place wrap + save/load idiom (per-method save_*_weights / load_*_weights persist only the trainable delta).
9.1. LoRA¶
nnx.peft.lora.LoRALinear
¶
Bases: Module
Linear layer wrapped with a LoRA low-rank residual.
The original :class:nn.Linear lives at self.base with its
parameters frozen (requires_grad=False) on construction.
lora_A and lora_B are trainable; lora_A uses
Kaiming-uniform init and lora_B is zero-initialized so the
layer's output at step 0 equals the base layer's output exactly
— fine-tuning starts from the pretrained behavior and diverges
only as B picks up gradient.
The wrapper preserves the base layer's in_features /
out_features, so consumers that read base.weight.shape or
pass tensors through the layer don't change.
Source code in nnx/peft/lora.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
nnx.peft.lora.apply_lora_to(module: nn.Module, *name_patterns: str, r: int = 8, alpha: float = 16.0, dropout: float = 0.0) -> int
¶
Wrap every :class:nn.Linear submodule whose dotted name matches
any of name_patterns with a :class:LoRALinear. Returns the
number of layers wrapped.
Patterns use shell-style globs (fnmatch) against the dotted
submodule name as it appears in module.named_modules() — e.g.,
"layers.0", "encoder.*", "*" for every Linear.
The wrap is in-place: each matched layer is removed from its parent
and replaced with a :class:LoRALinear wrapping it. The base
layer's parameters end up frozen as a side effect of LoRALinear's
construction; the LoRA parameters (lora_A / lora_B) are
trainable by default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
root module to walk. The function mutates |
required |
name_patterns
|
str
|
at least one fnmatch glob. Empty raises. |
()
|
r
|
int
|
LoRA rank — passed through to :class: |
8
|
alpha
|
float
|
LoRA scaling numerator — passed through. |
16.0
|
dropout
|
float
|
dropout on the LoRA path — passed through. |
0.0
|
Returns:
| Type | Description |
|---|---|
int
|
The count of layers wrapped (may be 0 if no patterns match). |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Idempotency note: if a layer is already a :class:LoRALinear,
its inner .base is skipped — re-applying apply_lora_to
against the same patterns is a no-op for layers that already
carry a LoRA wrapper. The function returns the count of NEW wraps.
Source code in nnx/peft/lora.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
nnx.peft.lora.save_lora_weights(module: nn.Module, path: Union[str, Path]) -> str
¶
Save ONLY the LoRA parameters of module to path.
The output is a plain torch.save of a dict-subset of the full
state_dict, containing only keys with lora_A or lora_B in
them. Loadable via :func:load_lora_weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
any module that has been processed by
:func: |
required |
path
|
Union[str, Path]
|
destination file path. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The path written (so calls can be chained). |
Source code in nnx/peft/lora.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
nnx.peft.lora.load_lora_weights(module: nn.Module, source: Union[str, Path, dict]) -> int
¶
Load LoRA parameters into module from source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
must already have :class: |
required |
source
|
Union[str, Path, dict]
|
either a path to a file produced by
:func: |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of parameter tensors loaded. |
Loads via module.load_state_dict(..., strict=False) so the
base layer's frozen weights — which are NOT in the LoRA-only
checkpoint — don't trigger a missing-keys error.
Source code in nnx/peft/lora.py
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 | |
9.2. DoRA¶
nnx.peft.dora.DoRALinear
¶
Bases: LoRALinear
Linear layer wrapped with a DoRA weight decomposition.
Subclasses :class:LoRALinear to inherit the frozen-base + trainable
low-rank residual machinery (lora_A, lora_B, alpha/r scaling,
optional dropout, base-freeze-on-construction). Adds a trainable
per-output-row magnitude parameter (shape: out_features)
initialized from the column-wise L2 norm of the base weight.
The forward composes the LoRA residual into a combined weight
V = W_0 + (α/r) · BA, normalizes V row-wise, then re-scales
by the trainable magnitude:
``W = magnitude.unsqueeze(1) * V / ||V||_c``
``y = W · x + b``
At step 0, B is zero-initialized (inherited from LoRALinear)
so V = W_0 and ||V||_c == magnitude, giving W == W_0
exactly — fine-tuning starts from the pretrained behavior.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
Linear
|
the :class: |
required |
r
|
int
|
low-rank dim for the LoRA residual. Must be positive. |
8
|
alpha
|
float
|
scaling numerator. Effective LoRA scale is |
16.0
|
dropout
|
float
|
dropout on the LoRA update path. Range |
0.0
|
Source code in nnx/peft/dora.py
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
nnx.peft.dora.apply_dora_to(module: nn.Module, *name_patterns: str, r: int = 8, alpha: float = 16.0, dropout: float = 0.0) -> int
¶
Wrap every :class:nn.Linear submodule whose dotted name matches
any of name_patterns with a :class:DoRALinear. Returns the
number of layers wrapped.
Mirrors :func:nnx.peft.apply_lora_to — same fnmatch glob conventions,
same two-phase (collect-then-mutate) traversal, same idempotency
contract (existing DoRA/LoRA wrappers are not re-wrapped — the
parent-is-LoRALinear check covers DoRALinear by inheritance).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
root module to walk. Mutated in place. |
required |
name_patterns
|
str
|
at least one fnmatch glob. |
()
|
r
|
int
|
LoRA rank — passed through. |
8
|
alpha
|
float
|
LoRA scaling numerator — passed through. |
16.0
|
dropout
|
float
|
dropout on the LoRA update path — passed through. |
0.0
|
Returns:
| Type | Description |
|---|---|
int
|
The count of layers wrapped (may be 0 if no patterns match |
int
|
or every match is already wrapped). |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/peft/dora.py
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | |
9.3. IA3¶
nnx.peft.ia3.IA3Linear
¶
Bases: Module
Linear layer wrapped with an IA3 per-output-dim scaling vector.
The original :class:nn.Linear lives at self.base with its
parameters frozen (requires_grad=False) on construction.
scaling is the only trainable parameter: a length-out_features
vector initialized to all-ones so the layer's output at step 0
equals the base layer's output exactly.
Forward: y = base(x) * scaling (broadcast over the trailing dim).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base
|
Linear
|
the :class: |
required |
Source code in nnx/peft/ia3.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | |
nnx.peft.ia3.apply_ia3_to(module: nn.Module, *name_patterns: str) -> int
¶
Wrap every :class:nn.Linear submodule whose dotted name matches
any of name_patterns with an :class:IA3Linear. Returns the
number of layers wrapped.
Mirrors :func:nnx.peft.apply_lora_to — same fnmatch glob conventions,
same two-phase (collect-then-mutate) traversal, same idempotency
contract (existing IA3 wrappers are skipped via the parent-is-IA3Linear
check).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
root module to walk. Mutated in place. |
required |
name_patterns
|
str
|
at least one fnmatch glob. |
()
|
Returns:
| Type | Description |
|---|---|
int
|
The count of layers wrapped (may be 0 if no patterns match |
int
|
or every match is already wrapped). |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/peft/ia3.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | |
nnx.peft.ia3.save_ia3_weights(module: nn.Module, path: Union[str, Path]) -> str
¶
Save ONLY the IA3 scaling parameters of module to path.
The output is a torch.save of a dict-subset of the full
state_dict, containing only keys whose name includes scaling.
Loadable via :func:load_ia3_weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
any module that has been processed by
:func: |
required |
path
|
Union[str, Path]
|
destination file path. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The path written (so calls can be chained). |
Source code in nnx/peft/ia3.py
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | |
nnx.peft.ia3.load_ia3_weights(module: nn.Module, source: Union[str, Path, dict]) -> int
¶
Load IA3 scaling parameters into module from source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
module
|
Module
|
must already have :class: |
required |
source
|
Union[str, Path, dict]
|
either a path to a file produced by
:func: |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of parameter tensors loaded. |
Loads via module.load_state_dict(..., strict=False) so the
base layer's frozen weights — which are NOT in the IA3-only
checkpoint — don't trigger a missing-keys error.
Source code in nnx/peft/ia3.py
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
9.4. Prefix Tuning¶
nnx.peft.prefix.PrefixTuner
¶
Bases: Module
Wrap a :class:TransformerNN with learnable per-layer K/V prefixes.
Freezes every parameter of the wrapped model on construction and
registers n_layers pairs of (n_prefix, n_heads, head_dim)
K / V tensors as the only trainable parameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
TransformerNN
|
a :class: |
required |
n_prefix
|
int
|
number of virtual prefix tokens per layer. Must be > 0. |
10
|
n_layers
|
Optional[int]
|
number of leading transformer blocks to attach a prefix
to. |
None
|
Note on shape: the prefix uses n_heads and head_dim taken
from the model's params — there's no per-block override, since
every block in a TransformerNN shares the same attention shape.
Raises:
| Type | Description |
|---|---|
TypeError
|
if |
ValueError
|
if |
Source code in nnx/peft/prefix.py
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
forward(*args, **kwargs)
¶
Delegate to the wrapped model. The prefix injection happens inside each block's monkey-patched MHA forward.
Source code in nnx/peft/prefix.py
173 174 175 176 | |
prefix_state_dict() -> dict
¶
Return a state-dict containing only the prefix tensors,
keyed for round-trip via :meth:load_prefix_weights.
The keys are the same as self.state_dict() filtered to the
prefix entries — i.e., prefix_keys.0, prefix_values.0,
prefix_keys.1, …
Source code in nnx/peft/prefix.py
191 192 193 194 195 196 197 198 199 | |
trainable_parameters() -> Iterator[nn.Parameter]
¶
Yield only the learned prefix tensors.
The wrapped model's parameters are frozen on construction; this is the iterator you hand to an optimizer.
Source code in nnx/peft/prefix.py
178 179 180 181 182 183 184 185 | |
nnx.peft.prefix.save_prefix_weights(tuner: PrefixTuner, path: Union[str, Path]) -> str
¶
Save ONLY the prefix tensors of tuner to path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tuner
|
PrefixTuner
|
a :class: |
required |
path
|
Union[str, Path]
|
destination file path. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The path written, so calls can be chained. |
Source code in nnx/peft/prefix.py
202 203 204 205 206 207 208 209 210 211 212 213 214 | |
nnx.peft.prefix.load_prefix_weights(tuner: PrefixTuner, source: Union[str, Path, dict]) -> int
¶
Load prefix tensors into tuner from source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tuner
|
PrefixTuner
|
must already have the same prefix shape as the source
(same n_prefix, n_heads, head_dim, n_layers). Otherwise
|
required |
source
|
Union[str, Path, dict]
|
a path to a file produced by :func: |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of parameter tensors loaded. |
Source code in nnx/peft/prefix.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | |
9.5. Prompt Tuning¶
nnx.peft.prompt.PromptTuner
¶
Bases: Module
Wrap a :class:TransformerNN with a learnable soft prompt.
Freezes every base parameter and allocates an
(n_prompt_tokens, d_model) embedding tensor. The wrapper's
forward prepends the prompt to the token embeddings, runs the
stack, then trims the prompt positions off the logits before
returning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
TransformerNN
|
a :class: |
required |
n_prompt_tokens
|
int
|
number of soft-prompt slots. Must be > 0. |
20
|
The soft prompt is initialized with nn.init.normal_(std=0.02)
— the same scale Lester et al. use as their "random init" baseline.
Source code in nnx/peft/prompt.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
effective_max_seq_len: int
property
¶
Window available for REAL tokens: the soft prompt occupies
n_prompt_tokens of the wrapped model's max_seq_len slots.
GenerativeNNModel.generate reads this so its sliding window
never overflows the wrapped model mid-generation.
forward(tokens: torch.Tensor) -> torch.Tensor
¶
Run the wrapped model with the soft prompt prepended.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tokens
|
Tensor
|
(batch, seq) long tensor of token ids. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
(batch, seq, vocab_size) logits over the REAL token |
Tensor
|
positions only. The soft-prompt positions are scaffolding |
Tensor
|
and their logits are discarded. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/peft/prompt.py
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
prompt_state_dict() -> dict
¶
Return a state-dict containing only the soft-prompt tensor,
keyed for round-trip via :meth:load_prompt_weights.
Source code in nnx/peft/prompt.py
146 147 148 149 | |
trainable_parameters() -> Iterator[nn.Parameter]
¶
Yield only the soft-prompt tensor.
The wrapped model's parameters are frozen on construction; this is the iterator you hand to an optimizer.
Source code in nnx/peft/prompt.py
138 139 140 141 142 143 144 | |
nnx.peft.prompt.save_prompt_weights(tuner: PromptTuner, path: Union[str, Path]) -> str
¶
Save ONLY the soft-prompt tensor of tuner to path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tuner
|
PromptTuner
|
a :class: |
required |
path
|
Union[str, Path]
|
destination file path. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The path written, so calls can be chained. |
Source code in nnx/peft/prompt.py
152 153 154 155 156 157 158 159 160 161 162 163 164 | |
nnx.peft.prompt.load_prompt_weights(tuner: PromptTuner, source: Union[str, Path, dict]) -> int
¶
Load the soft-prompt tensor into tuner from source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tuner
|
PromptTuner
|
must already have the same prompt shape as the source
(same n_prompt_tokens, d_model). A shape mismatch is
surfaced by |
required |
source
|
Union[str, Path, dict]
|
a path to a file produced by :func: |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of parameter tensors loaded. |
Source code in nnx/peft/prompt.py
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
9.6. Adapters¶
nnx.peft.adapters.AdapterLayer
¶
Bases: Module
Bottleneck residual block: y = x + up(act(down(x))).
up.weight and up.bias are zero-initialized so at step 0
the layer's output equals its input exactly. Gradient flow
through up and down is unblocked from the first step;
only the magnitude of the residual starts at zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dim
|
int
|
input and output feature dimension. The adapter is shape-preserving. |
required |
bottleneck
|
int
|
hidden dimension. Typically much smaller than
|
required |
activation
|
Callable[[], Module]
|
|
GELU
|
Source code in nnx/peft/adapters.py
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | |
10. Pruning (nnx.prune)¶
nnx.prune.magnitude.magnitude_prune(net: nn.Module, sparsity: float, *, layer_pattern: str = '*', bake: bool = True) -> int
¶
Zero the smallest-magnitude entries of every matched layer's weight.
For each :class:nn.Linear submodule of net whose dotted name
matches layer_pattern (fnmatch glob), call
:func:torch.nn.utils.prune.l1_unstructured with
amount=sparsity. PyTorch's implementation zeros
round(sparsity · weight.numel()) entries — the ones with the
smallest absolute value — per layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net
|
Module
|
root module to walk. The function mutates |
required |
sparsity
|
float
|
fraction of weights to zero, in |
required |
layer_pattern
|
str
|
fnmatch glob against dotted submodule name.
|
'*'
|
bake
|
bool
|
when |
True
|
Returns:
| Type | Description |
|---|---|
int
|
The number of :class: |
int
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Idempotency note: calling magnitude_prune twice at the
same sparsity is a no-op for the second call — l1_unstructured
picks the smallest-magnitude entries, which after the first prune
are exactly the already-zeroed positions. The zero count stays the
same; nothing is double-pruned.
Source code in nnx/prune/magnitude.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
nnx.prune.semi_structured.semi_structured_24(net: nn.Module, *, layer_pattern: str = '*') -> int
¶
Swap each matched :class:nn.Linear's weight with a 2:4
semi-structured sparse tensor via :func:torchao.sparsity.sparsify_.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net
|
Module
|
root module to walk. The function mutates |
required |
layer_pattern
|
str
|
fnmatch glob against dotted submodule name.
|
'*'
|
Returns:
| Type | Description |
|---|---|
int
|
The number of :class: |
int
|
|
int
|
underlying |
int
|
avoids an unnecessary torchao dispatch and the CUDA-only kernel |
int
|
error on CPU runners with no Linear targets to swap). |
Raises:
| Type | Description |
|---|---|
ImportError
|
if |
RuntimeError
|
surfaced from the underlying
|
Pattern semantics: same fnmatch convention as
:func:nnx.peft.apply_lora_to and
:func:nnx.prune.magnitude_prune — dotted submodule names against
shell wildcards. Only :class:nn.Linear submodules are eligible
(Conv2d / BatchNorm / Embedding / etc. are skipped even under a
wildcard pattern).
Note on weights: torchao.sparsity.sparsify_ does NOT enforce
the 2:4 mask before the swap. Callers are expected to either
(a) magnitude-prune the weight to a valid 2:4 pattern beforehand
(via :func:magnitude_prune or a custom mask), or
(b) accept whatever 2:4 approximation
:func:torch.sparse.to_sparse_semi_structured picks (which keeps
the top-2-by-absolute-value entries per 4-group). For training
workflows, the standard recipe is to pre-mask, then train the
surviving entries.
Source code in nnx/prune/semi_structured.py
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | |
11. Model surgery (nnx.surgery)¶
Walkthrough at Model surgery. Every primitive returns a fresh nn.Module and composes with NNModel.train() for the "load checkpoint → surgery → refine" loop.
nnx.surgery.widen.widen(model: nn.Module, *, layer_name: str, new_width: int, rng_seed: Optional[int] = 0) -> nn.Module
¶
Net2WiderNet: grow a Linear's out_features to new_width.
Returns a deep copy of model with the named layer expanded and
the downstream Linear's in_features adjusted so the overall
forward output is preserved exactly (within FP rounding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
any :class: |
required |
layer_name
|
str
|
dotted name (as produced by |
required |
new_width
|
int
|
desired |
required |
rng_seed
|
Optional[int]
|
seed for the unit-duplication choices. Pass an int
for deterministic surgery; |
0
|
Returns:
| Type | Description |
|---|---|
Module
|
A new :class: |
Module
|
widened Linear in place. Forward output equals the original's |
Module
|
within |
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
TypeError
|
if the named submodule is not :class: |
ValueError
|
if |
Source code in nnx/surgery/widen.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
nnx.surgery.deepen.deepen(model: nn.Module, *, after_layer_name: str) -> nn.Module
¶
Net2DeeperNet: insert an identity-initialized Linear after the named layer. Function-preserving on ReLU networks only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
any :class: |
required |
after_layer_name
|
str
|
dotted name (as in
|
required |
Returns:
| Type | Description |
|---|---|
Module
|
A fresh :class: |
Module
|
original within |
Raises:
| Type | Description |
|---|---|
KeyError
|
if |
TypeError
|
if the layer is neither a ReLU-in-Sequential nor a Linear-in-FeedFwdNN-like ModuleList. |
ValueError
|
if the parent's activation is anything other than ReLU. Sigmoid / tanh / GELU break function-preservation. |
Source code in nnx/surgery/deepen.py
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | |
nnx.surgery.drop_layer.drop_layer(model: nn.Module, *, layer_name: Union[str, list[str]], importance: Callable[[nn.Module], float] | None = None) -> nn.Module
¶
Replace a named layer with :class:nn.Identity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Module
|
any :class: |
required |
layer_name
|
Union[str, list[str]]
|
either a dotted submodule name, or a list of
dotted names to choose from. When a list is given,
|
required |
importance
|
Callable[[Module], float] | None
|
optional callable |
None
|
Returns:
| Type | Description |
|---|---|
Module
|
A fresh :class: |
Module
|
class: |
Module
|
the dropped layer was shape-preserving (e.g. an activation or |
Module
|
a square Linear); otherwise calling forward on the surged |
Module
|
module will raise — by design, since silently corrupting the |
Module
|
shape would be worse than a loud failure. |
Raises:
| Type | Description |
|---|---|
KeyError
|
if any candidate name is missing. |
ValueError
|
if |
Source code in nnx/surgery/drop_layer.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
nnx.surgery.low_rank.low_rank_factorize(linear: nn.Linear, *, rank: int, method: str = 'svd') -> nn.Sequential
¶
Factor a Linear into two smaller Linears via rank-k SVD
truncation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
linear
|
Linear
|
an :class: |
required |
rank
|
int
|
the truncation rank |
required |
method
|
str
|
|
'svd'
|
Returns:
| Type | Description |
|---|---|
Sequential
|
class: |
Sequential
|
approximates the input layer. The first Linear has |
Sequential
|
|
Sequential
|
Linear carries the original bias verbatim. |
Raises:
| Type | Description |
|---|---|
TypeError
|
if |
ValueError
|
if |
Source code in nnx/surgery/low_rank.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
nnx.surgery.embedding.expand_embedding(emb: nn.Embedding, *, new_num_embeddings: int, init: InitStrategy = 'zeros') -> tuple[nn.Embedding, torch.Tensor]
¶
Return a larger Embedding whose first rows match emb exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
emb
|
Embedding
|
the source embedding. Its weights are read but not mutated. |
required |
new_num_embeddings
|
int
|
the desired |
required |
init
|
InitStrategy
|
how to initialize the new rows. |
'zeros'
|
Returns:
| Type | Description |
|---|---|
Embedding
|
|
Tensor
|
class: |
tuple[Embedding, Tensor]
|
|
tuple[Embedding, Tensor]
|
|
tuple[Embedding, Tensor]
|
as candidates for freezing during refinement. |
Raises:
| Type | Description |
|---|---|
TypeError
|
if |
ValueError
|
if |
Source code in nnx/surgery/embedding.py
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | |
12. Quantization (nnx.quantize)¶
PTQ INT8 weight-only + QAT 8da4w via torchao (the replacement for the removed torch.ao.quantization). Opt-in via pip install "thekaveh-nnx[quantize]".
nnx.quantize.ptq.quantize_int8(model: NNModel) -> NNModel
¶
Return a new :class:NNModel with int8 weight-only quantized net.
Deep-copies model.net and applies
torchao.quantization.quantize_(net, Int8WeightOnlyConfig()) to
the copy. Every nn.Linear submodule of the copy has its weight
parameter replaced with an :class:AffineQuantizedTensor (int8
per-channel, symmetric). Activations stay FP32 — only the weights
are stored in int8.
The original model is untouched. The returned NNModel shares
every other attribute (params, net_params, device,
loss_fn) with the original — only net is the quantized copy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
NNModel
|
a trained :class: |
required |
Returns:
| Type | Description |
|---|---|
NNModel
|
a new :class: |
NNModel
|
deep-copy of |
NNModel
|
|
NNModel
|
original; |
NNModel
|
(QAT lands in a separate module). |
Raises:
| Type | Description |
|---|---|
ImportError
|
if |
Source code in nnx/quantize/ptq.py
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
nnx.quantize.qat.qat_train_step_factory(base_step: Optional[TrainStepFn] = None, qat_config: str = '8da4w') -> TrainStepFn
¶
Return a :class:TrainStepFn that runs base_step against a
fake-quantized model.
The returned step is the same as base_step (or
:func:default_train_step when base_step is None) — fake-quant
insertion happens once, via :class:QATLifecycleCallback, on
on_train_begin. The per-batch forward/backward then exercises
those fake-quant ops automatically through the standard module
forward.
Why split the work between a factory and a callback?
- The factory validates
qat_configearly (at construction time) so misconfigurations surface before the data loader spins up. - The callback owns the lifecycle:
prepareat start,convertat end. Bundling that into the per-batch step would re-check the module state every iteration and complicate gradient flow.
Both pieces are needed in :meth:NNModel.train::
callback = QATLifecycleCallback(qat_config="8da4w")
step_fn = qat_train_step_factory(qat_config="8da4w")
model.train(params=..., callbacks=[callback], train_step_fn=step_fn)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_step
|
Optional[TrainStepFn]
|
optional underlying training step to wrap. |
None
|
qat_config
|
str
|
shortcut for the torchao QAT recipe. Currently only
|
'8da4w'
|
Returns:
| Name | Type | Description |
|---|---|---|
a |
TrainStepFn
|
class: |
TrainStepFn
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
ImportError
|
if |
Source code in nnx/quantize/qat.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | |
nnx.quantize.qat.QATLifecycleCallback
¶
Bases: Callback
Manage the torchao prepare / convert lifecycle around training.
Add to callbacks=[...] in :meth:NNModel.train. On train begin,
swaps every eligible :class:torch.nn.Linear in model.net for
its fake-quantized counterpart (the model now learns to be robust
to int4/int8 rounding). On train end, the fake-quantized linears
are converted to actually-quantized ones — the resulting model is
suitable for inference / export.
The mutation is in place on model.net: after training,
model.net IS the converted model. The callback exposes the
quantizer instance as self.quantizer for callers who want to
pickle quantizer-specific state alongside their checkpoint, and
tracks the prepare/convert phase via self.is_prepared and
self.is_converted for downstream inspection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
qat_config
|
str
|
torchao recipe shortcut. See
:func: |
'8da4w'
|
groupsize
|
int
|
group size for the int4 weight quantizer. 32 is the default — small enough to apply to toy nets in tests (where hidden_dim=64) while being a real-world setting. Larger groupsizes (128, 256) give better compression at the cost of accuracy. |
32
|
Source code in nnx/quantize/qat.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
on_train_begin(ctx: _CallbackContext) -> None
¶
Insert fake-quant ops into ctx.model.net in place.
Source code in nnx/quantize/qat.py
184 185 186 187 188 189 190 191 192 193 | |
on_train_end(ctx: _CallbackContext) -> None
¶
Convert fake-quant ops in ctx.model.net to true int4/int8 modules.
After this returns, ctx.model.net produces real quantized
outputs and is suitable for inference / ONNX export. The model
is no longer trainable through the usual FP32 optimizer path —
a fresh training session on the same NNModel would need a new
QATLifecycleCallback.
Source code in nnx/quantize/qat.py
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
13. Diffusion (nnx.diffusion)¶
nnx.diffusion.schedules.NoiseSchedulers
¶
Bases: Enum
Diffusion noise-schedule factory. Enum-as-factory pattern (like
:class:nnx.Nets, :class:nnx.Optims): each enum variant's
__call__ constructs the underlying :class:NoiseSchedule.
Source code in nnx/diffusion/schedules.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
__call__(T: int = 1000, *, beta_min: float = 0.0001, beta_max: float = 0.02, s: float = 0.008) -> NoiseSchedule
¶
Build a :class:NoiseSchedule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
T
|
int
|
number of diffusion timesteps. Larger T means more steps during training (one t per batch) and at sampling time (T-many reverse passes); 1000 is the DDPM default. |
1000
|
beta_min
|
float
|
LINEAR schedule lower endpoint; ignored for COSINE. |
0.0001
|
beta_max
|
float
|
LINEAR schedule upper endpoint; ignored for COSINE. |
0.02
|
s
|
float
|
COSINE schedule offset (Improved DDPM eq. 17); ignored for LINEAR. |
0.008
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
NoiseSchedule
|
class: |
NoiseSchedule
|
construction to migrate; the diffusion train step does this |
|
NoiseSchedule
|
implicitly when indexing the schedule tensors with batch |
|
NoiseSchedule
|
timesteps on the target device. |
Source code in nnx/diffusion/schedules.py
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
nnx.diffusion.schedules.NoiseSchedule
dataclass
¶
Precomputed DDPM noise schedule.
All tensors are 1D of length T and live on the same device. The
factory constructs them on CPU; :meth:to returns a new schedule
with every tensor migrated.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
NoiseSchedulers
|
which enum variant produced this schedule (for introspection). |
T |
int
|
number of diffusion timesteps. |
betas |
Tensor
|
per-step variance, |
alphas |
Tensor
|
|
alphas_cumprod |
Tensor
|
cumulative product of alphas ( |
sqrt_alphas_cumprod |
Tensor
|
|
sqrt_one_minus_alphas_cumprod |
Tensor
|
|
posterior_variance |
Tensor
|
variance of q(x_{t-1} | x_t, x_0), used by the reverse-step sampler. |
Source code in nnx/diffusion/schedules.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
to(device) -> NoiseSchedule
¶
Return a copy with every tensor moved to device. The kind
and T fields are unchanged.
Source code in nnx/diffusion/schedules.py
58 59 60 61 62 63 64 65 66 67 68 69 70 | |
nnx.diffusion.nets.DiffusionMLP
¶
Bases: Module
Conditional MLP for low-dim diffusion: forward(x_t, t) -> ε_pred.
Architecture: sinusoidal time embed → small projection → concat with flat x_t → MLP → linear head producing a noise prediction of the same shape as x_t. Bare ReLU activations, no skip connections — a single file's worth of code, enough to learn a 2D Gaussian mixture or a small tabular distribution.
Inputs of any rank are supported by flattening dimensions ≥ 1 before the MLP and un-flattening at the output. The network is NOT a U-Net — it has no spatial structure. For image-space diffusion, the same train/sample/schedule machinery works against a user-supplied U-Net.
Source code in nnx/diffusion/nets.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
forward(x: torch.Tensor, t: torch.Tensor) -> torch.Tensor
¶
Predict noise added to x at timestep t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
|
required |
t
|
Tensor
|
|
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Tensor of the same shape as |
Source code in nnx/diffusion/nets.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | |
unpack_batch(batch)
¶
Standard (X-tuple, Y) adapter so this net plays nicely with
the NNx dataloader contract. Y is unused by diffusion — every
consumer that calls unpack_batch discards it.
Source code in nnx/diffusion/nets.py
129 130 131 132 133 134 135 136 137 | |
nnx.diffusion.nets.sinusoidal_time_embed(t: torch.Tensor, dim: int) -> torch.Tensor
¶
Standard transformer-style sinusoidal positional embedding,
applied to scalar timesteps so the denoising network can condition
on t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t
|
Tensor
|
integer or float tensor of shape |
required |
dim
|
int
|
embedding dimension. Half of it carries sin frequencies,
half carries cos; |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Tensor of shape |
Source code in nnx/diffusion/nets.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
nnx.diffusion.training.diffusion_train_step_factory(schedule: NoiseSchedule) -> TrainStepFn
¶
Build a DDPM noise-prediction :class:TrainStepFn.
Each call to the returned step fn:
- Samples a random per-sample timestep
t ~ Uniform[0, T). - Samples Gaussian noise
ε ~ N(0, I)matching x_0's shape. - Computes
x_t = √ᾱ_t · x_0 + √(1 - ᾱ_t) · ε(forward diffusion). - Calls
model.net(x_t, t)to predictε_pred. - Backprops the MSE between
ε_predandε, steps the optimizer.
Loss is reported as both .loss and .error on the returned
EDP so BEST checkpoint tracking and the ReduceLROnPlateau scheduler
have a metric to lock onto. The standard supervised classification
metrics (accuracy/f1/...) are not meaningful for a generative
paradigm and stay zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schedule
|
NoiseSchedule
|
a :class: |
required |
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A function suitable for |
Source code in nnx/diffusion/training.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
nnx.diffusion.sampling.sample(model: NNModel, schedule: NoiseSchedule, shape: tuple[int, ...], *, device: Optional[torch.device] = None, generator: Optional[torch.Generator] = None) -> torch.Tensor
¶
Run T reverse-diffusion steps and return samples drawn from the distribution the model was trained on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
NNModel
|
an :class: |
required |
schedule
|
NoiseSchedule
|
the same :class: |
required |
shape
|
tuple[int, ...]
|
full tensor shape to generate, e.g., |
required |
device
|
Optional[device]
|
target device. Defaults to |
None
|
generator
|
Optional[Generator]
|
optional torch.Generator for reproducible sampling
(pass one built with |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
A tensor of shape |
Source code in nnx/diffusion/sampling.py
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
14. Training paradigms (nnx.paradigms)¶
Each factory returns a TrainStepFn for the train_step_fn= hook on NNModel.train. The training loop, checkpoint cadence, callbacks, and persistence are unchanged — only the per-batch update is swapped.
14.1. Knowledge distillation¶
nnx.paradigms.distillation.kd_train_step_factory(teacher: NNModel, *, alpha: float = 0.5, temperature: float = 4.0) -> TrainStepFn
¶
Build a knowledge-distillation :class:TrainStepFn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
teacher
|
NNModel
|
a fully-trained :class: |
required |
alpha
|
float
|
weight on the distillation (soft) loss. The hard-label
loss gets |
0.5
|
temperature
|
float
|
softmax temperature applied to BOTH student and
teacher logits before the KL. Higher T flattens the
distribution and exposes more dark knowledge; the
|
4.0
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/distillation.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
nnx.paradigms.distillation.feature_kd_train_step_factory(teacher: NNModel, *, auxiliary_layers: dict[str, str], alpha: float = 0.5, beta: float = 0.5, temperature: float = 4.0) -> TrainStepFn
¶
Build a FitNets-style feature-distillation :class:TrainStepFn.
Extends :func:kd_train_step_factory with an additional MSE term
matching named intermediate-layer activations between the (frozen)
teacher and the trainable student. Forward hooks register on the
pairs in auxiliary_layers; collected activations feed an
elementwise MSE that's mixed into the loss via beta::
L = α · KL_soft · T² + β · MSE(student_act, teacher_act) + (1 − α) · L_hard
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
teacher
|
NNModel
|
a fully-trained :class: |
required |
auxiliary_layers
|
dict[str, str]
|
dict mapping |
required |
alpha
|
float
|
weight on the soft (logit-KL) term. The hard-label
loss gets |
0.5
|
beta
|
float
|
weight on the feature-MSE term. 0.5 is the common starting point; tune downward if it dominates the logit term, upward to bias the student toward matching internal representations. |
0.5
|
temperature
|
float
|
softmax temperature for the logit-KL term —
identical contract to :func: |
4.0
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
TrainStepFn
|
train_step_fn=...)``. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/distillation.py
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 | |
nnx.paradigms.born_again.born_again_train(model: NNModel, *, generations: int = 3, train_params: NNTrainParams, **kd_kwargs: Any) -> list[NNRun]
¶
Iterate G generations of self-distillation on a single model.
Generation 0 trains plain (no teacher) — standard supervised loss.
Each subsequent generation uses a deep-copied, frozen, eval-mode
snapshot of the model after the prior generation completed as the
teacher for a Hinton-style KD step (via :func:kd_train_step_factory).
The same in-place model is reused across generations; only the
teacher snapshot is duplicated. This matches the original paper's
setup and keeps memory usage to two copies of the network at any
one time (the live student + the frozen teacher snapshot).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
NNModel
|
the :class: |
required |
generations
|
int
|
how many generations to run. |
3
|
train_params
|
NNTrainParams
|
passed unchanged to every :meth: |
required |
**kd_kwargs
|
Any
|
forwarded to :func: |
{}
|
Returns:
| Type | Description |
|---|---|
list[NNRun]
|
A list of :class: |
list[NNRun]
|
|
list[NNRun]
|
KD run that used generation |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/born_again.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
14.2. Contrastive¶
nnx.paradigms.contrastive.simclr_train_step_factory(*, temperature: float = 0.5) -> TrainStepFn
¶
Build a SimCLR :class:TrainStepFn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
temperature
|
float
|
temperature in :func: |
0.5
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
TrainStepFn
|
The training loader MUST yield batches of two augmented views |
TrainStepFn
|
per source sample — typically |
TrainStepFn
|
|
TrainStepFn
|
|
TrainStepFn
|
BatchNorm statistics see one view at a time; users who want |
TrainStepFn
|
all-at-once normalization can stack the views and forward once. |
TrainStepFn
|
Sharp edge: a labeled |
TrainStepFn
|
|
TrainStepFn
|
|
TrainStepFn
|
func: |
TrainStepFn
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/contrastive.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | |
nnx.paradigms.contrastive.nt_xent_loss(z1: torch.Tensor, z2: torch.Tensor, *, temperature: float = 0.5) -> torch.Tensor
¶
SimCLR's Normalized Temperature-scaled cross-entropy loss.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
z1
|
Tensor
|
|
required |
z2
|
Tensor
|
|
required |
temperature
|
float
|
divisor on the cosine similarity. Lower T sharpens the distribution; 0.5 is the SimCLR default. Must be > 0. |
0.5
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Scalar loss tensor (mean across the 2B positions in the batch). |
Raises:
| Type | Description |
|---|---|
ValueError
|
if shapes mismatch, |
Source code in nnx/paradigms/contrastive.py
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | |
14.3. Augmentation¶
nnx.paradigms.augmentation.mixup_train_step_factory(*, alpha: float = 0.4) -> TrainStepFn
¶
Build a Mixup :class:TrainStepFn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alpha
|
float
|
Beta-distribution shape parameter. |
0.4
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
TrainStepFn
|
Reports a Mixup-weighted |
TrainStepFn
|
loss honors the model's |
TrainStepFn
|
classification loss, not just CrossEntropy). |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/augmentation.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
nnx.paradigms.augmentation.cutmix_train_step_factory(*, alpha: float = 1.0) -> TrainStepFn
¶
Build a CutMix :class:TrainStepFn for 4D image batches.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alpha
|
float
|
Beta-distribution shape parameter for the area ratio.
|
1.0
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
TrainStepFn
|
inputs). Raises at step time on lower-rank input — CutMix's |
TrainStepFn
|
spatial cut isn't well-defined without H and W. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/augmentation.py
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
14.4. Mixture-of-Experts¶
MoELinear is the drop-in layer (documented in §4); moe_train_step_factory adds the Switch-style load-balancing aux loss to the supervised step.
nnx.paradigms.moe.moe_train_step_factory(*, aux_loss_weight: float = 0.01) -> TrainStepFn
¶
Build an MoE-aware supervised :class:TrainStepFn.
The returned step performs the standard supervised forward
(loss = m.loss_fn(net(X), Y)) and then adds the
Switch-style load-balancing penalty summed across every
:class:MoELinear layer in model.net, weighted by
aux_loss_weight. Backward, grad-clip, and optimizer step go
through :func:nnx._step_helpers.finalize_step for the same
NaN-guard + grad-clip tail as the other paradigm factories.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
aux_loss_weight
|
float
|
weight on the aux loss term ( |
0.01
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
TrainStepFn
|
single-input supervised net that contains ≥ 0 |
TrainStepFn
|
class: |
TrainStepFn
|
aux loss is 0 and the step is exactly supervised. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/moe.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
14.5. I-JEPA¶
Walkthrough at I-JEPA. The ViTNN encoder is documented in §4.
nnx.paradigms.jepa.jepa_train_step_factory(target_encoder: nn.Module, predictor: nn.Module, mask_fn: Callable[[int, torch.device], tuple[torch.Tensor, torch.Tensor]], *, ema_momentum: float = 0.996) -> TrainStepFn
¶
Build an I-JEPA :class:TrainStepFn.
Per step:
- Sample
(context_mask, target_mask)for the batch viamask_fn(n_patches, device). Both are 1-DBoolTensor[n_patches]and complementary — every patch is either context or target. - Forward each input image through
model.netwith the context mask, producing(B, T_ctx + 1, d_model)context embeddings (CLS at index 0). - Forward the full image (no mask) through
target_encoderunderno_gradto produce target embeddings. Slice out the positions intarget_maskonly. - Predict
(B, T_tgt, d_model)from context viapredictor. - MSE loss against the target embeddings.
- :func:
finalize_step— NaN guard, optimizer step, grad clip. - :func:
update_ema— EMA-update the target encoder frommodel.net.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_encoder
|
Module
|
an EMA copy of |
required |
predictor
|
Module
|
a :class: |
required |
mask_fn
|
Callable[[int, device], tuple[Tensor, Tensor]]
|
callable |
required |
ema_momentum
|
float
|
EMA decay used by :func: |
0.996
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
when |
Source code in nnx/paradigms/jepa.py
302 303 304 305 306 307 308 309 310 311 312 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 355 356 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 391 392 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 430 431 432 433 434 435 436 437 438 439 440 441 | |
nnx.paradigms.jepa.JEPAPredictor
¶
Bases: Module
Tiny ViT-like predictor: (context_embeds, target_positions)
-> predicted_target_embeds.
Architecture: project context_embeds to predictor_dim,
concatenate learnable mask tokens (one per target position) plus
that position's positional embedding, run a few ViT blocks, project
back to embed_dim, return the predictions at the target
positions only.
Kept deliberately small — the reference I-JEPA predictor is also
much narrower than the encoder. For our CIFAR-shape demo, two
blocks at predictor_dim = embed_dim // 2 is enough plumbing
to verify the loss decreases without dominating wall-clock time.
Source code in nnx/paradigms/jepa.py
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 | |
forward(context_embeds: torch.Tensor, context_positions: torch.Tensor, target_positions: torch.Tensor) -> torch.Tensor
¶
Predict embeddings at target_positions from
context_embeds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context_embeds
|
Tensor
|
|
required |
context_positions
|
Tensor
|
|
required |
target_positions
|
Tensor
|
|
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
|
Source code in nnx/paradigms/jepa.py
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 | |
nnx.paradigms.jepa.build_target_encoder(source: nn.Module) -> nn.Module
¶
Deep-copy source, freeze every parameter, return the copy.
The target encoder is updated only via :func:update_ema after
each optimizer step. Freezing here is belt-and-braces — even if a
user accidentally hands the target into an optimizer that scans
parameters(), requires_grad=False keeps the gradients off
and the optimizer's state empty for those tensors.
Source code in nnx/paradigms/jepa.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
nnx.paradigms.jepa.update_ema(source: nn.Module, target: nn.Module, momentum: float) -> None
¶
In-place EMA update: target ← momentum * target + (1 - momentum) * source.
Called once per training step from inside the JEPA train_step_fn.
Runs under torch.no_grad so the EMA tensors do not become part
of the autograd graph — the target encoder is supposed to be a
detached snapshot.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Module
|
the trainable module (i.e., |
required |
target
|
Module
|
the EMA copy returned by :func: |
required |
momentum
|
float
|
EMA decay in |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
when |
KeyError
|
when a target parameter has no same-named source parameter (the name-keyed update contract). |
Source code in nnx/paradigms/jepa.py
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | |
nnx.paradigms.jepa.random_block_mask(*, n_patches: int, grid_size: int, block_scale: tuple[float, float] = (0.15, 0.2), block_aspect: tuple[float, float] = (0.75, 1.5), generator: Optional[torch.Generator] = None, device: Optional[torch.device] = None) -> tuple[torch.Tensor, torch.Tensor]
¶
Sample one I-JEPA-style rectangular block mask on a patch grid.
Returns (context_mask, target_mask) where:
context_mask: BoolTensor[n_patches]— True at positions kept by the context encoder (i.e., NOT in the target block).target_mask: BoolTensor[n_patches]— True at positions the predictor is asked to predict (i.e., inside the target block, exactly the complement of context_mask).
The block is a single rectangle of randomly-sampled width/height
drawn from block_scale × n_patches with an aspect ratio in
block_aspect. Reference I-JEPA samples 4 target blocks per
image; this helper samples 1 — enough for the verify-the-plumbing
example we ship. Users can compose multiple calls if they want
the 4-block recipe.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_patches
|
int
|
total number of patch tokens. Must equal
|
required |
grid_size
|
int
|
width (= height) of the patch grid. The rectangular block is sampled in this coordinate system. |
required |
block_scale
|
tuple[float, float]
|
|
(0.15, 0.2)
|
block_aspect
|
tuple[float, float]
|
|
(0.75, 1.5)
|
generator
|
Optional[Generator]
|
optional |
None
|
device
|
Optional[device]
|
device on which the masks are placed. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[Tensor, Tensor]
|
A pair of |
Raises:
| Type | Description |
|---|---|
ValueError
|
when |
Source code in nnx/paradigms/jepa.py
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
14.6. DPO¶
Walkthrough at DPO.
nnx.paradigms.dpo.dpo_train_step_factory(ref_model: NNModel, *, beta: float = 0.1, pad_token_id: Optional[int] = None) -> TrainStepFn
¶
Build a Direct Preference Optimization :class:TrainStepFn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ref_model
|
NNModel
|
a frozen reference policy — typically a copy of the
SFT checkpoint that the trainable policy was initialized
from. Its |
required |
beta
|
float
|
temperature on the implicit reward. Larger |
0.1
|
pad_token_id
|
Optional[int]
|
the id the dataset used to right-pad chosen /
rejected responses ( |
None
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
TrainStepFn
|
The training loader MUST yield batches of three |
TrainStepFn
|
(prompt_ids, chosen_ids, rejected_ids) |
TrainStepFn
|
— typically from :class: |
TrainStepFn
|
tensors must already be padded / right-aligned by the dataset. |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/paradigms/dpo.py
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
15. Embeddings (nnx.embeddings)¶
End-to-end walkthrough at Embeddings. Opt-in via pip install "thekaveh-nnx[embeddings]".
nnx.embeddings.contrastive_trainer.ContrastiveTextDataset
¶
Bases: Dataset
Wraps (anchor, positive) string pairs as a torch Dataset.
Each __getitem__ returns a 2-tuple of strings (anchor,
positive). The default collate from :class:torch.utils.data.DataLoader
would attempt to stack these into tensors and crash; pair this
dataset with :func:pair_collate (or pass it directly to
:func:train_contrastive which wires the collate for you).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pairs
|
list[tuple[str, str]]
|
list of |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
if |
Source code in nnx/embeddings/contrastive_trainer.py
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | |
nnx.embeddings.contrastive_trainer.train_contrastive(backbone: Any, dataset: Union[ContrastiveTextDataset, list[tuple[str, str]]], *, n_epochs: int = 3, batch_size: int = 16, lr: float = 2e-05, temperature: float = 0.05, device: Optional[Union[str, torch.device]] = None, shuffle: bool = True, grad_clip_norm: Optional[float] = 1.0, weight_decay: float = 0.0, optimizer_cls: type = torch.optim.AdamW, verbose: bool = False) -> Any
¶
Train backbone on (anchor, positive) pairs via NT-Xent.
High-level wrapper around :func:nt_xent_loss. Builds a
:class:DataLoader with :func:pair_collate, instantiates an
optimizer over the backbone's trainable parameters, and runs
n_epochs of contrastive updates. The backbone is updated
in-place AND returned for chaining (e.g., directly into
:func:nnx.embeddings.export_to_faiss).
For more elaborate setups — callbacks, custom schedulers, multi-
optimizer training, run.id persistence under runs/<id>/ — use
:func:text_contrastive_train_step_factory with the standard
:meth:NNModel.train driver instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backbone
|
Any
|
text encoder. Either a
:class: |
required |
dataset
|
Union[ContrastiveTextDataset, list[tuple[str, str]]]
|
a :class: |
required |
n_epochs
|
int
|
number of full passes. Default 3 — contrastive fine-tuning of a pretrained encoder typically needs few. |
3
|
batch_size
|
int
|
pairs per batch. NT-Xent's in-batch-negatives scaling means bigger is usually better; 16-64 is typical for CPU sanity runs, hundreds for GPU. |
16
|
lr
|
float
|
optimizer learning rate. Default 2e-5 (the canonical SBERT fine-tune LR). |
2e-05
|
temperature
|
float
|
NT-Xent temperature. Default 0.05 (sharper than SimCLR's image default — text embedders work in a much higher-dim cosine space where small temperature helps). |
0.05
|
device
|
Optional[Union[str, device]]
|
target device. |
None
|
shuffle
|
bool
|
shuffle the dataset each epoch. Default True. |
True
|
grad_clip_norm
|
Optional[float]
|
global L2 grad-clip norm. |
1.0
|
weight_decay
|
float
|
AdamW weight decay. Default 0.0. |
0.0
|
optimizer_cls
|
type
|
optimizer constructor. Default
:class: |
AdamW
|
verbose
|
bool
|
print per-epoch mean loss. Default False. |
False
|
Returns:
| Type | Description |
|---|---|
Any
|
The (in-place-mutated) |
Raises:
| Type | Description |
|---|---|
ValueError
|
on a dataset of fewer than 2 pairs, batch_size < 2,
non-positive epochs, non-positive temperature, or a
non-positive
|
FloatingPointError
|
when the contrastive loss goes non-finite mid-training (check lr / temperature / input normalization). |
Source code in nnx/embeddings/contrastive_trainer.py
312 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 355 356 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 391 392 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 430 431 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 | |
nnx.embeddings.contrastive_trainer.embed_texts(backbone: Any, texts: list[str], *, batch_size: int = 64, device: Optional[Union[str, torch.device]] = None, normalize: bool = True) -> torch.Tensor
¶
Encode texts with backbone and return a (N, D) tensor.
Runs in torch.no_grad() + eval() mode — this is the
inference helper, not the training one. The trainer drives
:func:_encode directly so gradients flow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backbone
|
Any
|
text encoder — a sentence-transformers model or any
|
required |
texts
|
list[str]
|
input strings. May be empty (returns a |
required |
batch_size
|
int
|
how many texts per forward pass. Default 64. |
64
|
device
|
Optional[Union[str, device]]
|
target device. |
None
|
normalize
|
bool
|
if True, L2-normalize each row so dot products with
the result are cosine similarities. Default True because
FAISS's |
True
|
Returns:
| Type | Description |
|---|---|
Tensor
|
A |
Tensor
|
any autograd graph. |
Source code in nnx/embeddings/contrastive_trainer.py
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 | |
nnx.embeddings.contrastive_trainer.text_contrastive_train_step_factory(*, temperature: float = 0.5) -> TrainStepFn
¶
Build a :class:TrainStepFn for text-pair contrastive training.
This is the text-aware sibling of
:func:nnx.simclr_train_step_factory. The training loader must
yield (anchors: list[str], positives: list[str]) batches —
typically by pairing :class:ContrastiveTextDataset with
:func:pair_collate.
The step runs:
- Encode anchors through
model.net→z1. - Encode positives through
model.net→z2. - NT-Xent loss across the
(2B, 2B)similarity matrix. - Standard :func:
finalize_steptail (NaN guard, grad clip, optimizer step).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
temperature
|
float
|
NT-Xent temperature. Lower sharpens; 0.5 is the SimCLR default. Must be > 0. |
0.5
|
Returns:
| Type | Description |
|---|---|
TrainStepFn
|
A |
Raises:
| Type | Description |
|---|---|
ValueError
|
at factory-build time if |
Source code in nnx/embeddings/contrastive_trainer.py
243 244 245 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 | |
nnx.embeddings.faiss_export.export_to_faiss(backbone: Any, corpus: list[str], out_path: Union[str, Path], *, batch_size: int = 64, index_type: str = 'IndexFlatIP', normalize: Optional[bool] = None, device: Optional[Union[str, torch.device]] = None) -> str
¶
Embed corpus with backbone and write a FAISS index file.
The default IndexFlatIP + normalize=True combination is
cosine similarity: L2-normalize the embeddings, then use inner
product as the score. This is the standard FAISS-cosine recipe
(FAISS itself doesn't ship a cosine index; the normalize-then-IP
pattern is canonical).
The corpus order is preserved in the index — index.search's
returned ids are positions into corpus. The caller is
responsible for keeping a parallel list / DataFrame of original
document ids or metadata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backbone
|
Any
|
text encoder. Either a
:class: |
required |
corpus
|
list[str]
|
list of strings to embed. Order is the index's id space.
Empty raises :class: |
required |
out_path
|
Union[str, Path]
|
destination file path. The parent directory must
exist. The file is written via FAISS's native
|
required |
batch_size
|
int
|
forward-pass batch size. Default 64. |
64
|
index_type
|
str
|
FAISS index family to build. One of
|
'IndexFlatIP'
|
normalize
|
Optional[bool]
|
whether to L2-normalize each embedding before
insertion. |
None
|
device
|
Optional[Union[str, device]]
|
target device for the encode pass. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The string path written. Same value as |
str
|
returned for call-chain convenience. |
Raises:
| Type | Description |
|---|---|
ImportError
|
if |
ValueError
|
empty corpus, unknown |
Source code in nnx/embeddings/faiss_export.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | |
nnx.embeddings.faiss_export.export_to_safetensors(backbone: Any, out_path: Union[str, Path]) -> str
¶
Persist backbone.state_dict() to disk for downstream reload.
Prefers the safetensors format (canonical for HuggingFace Hub
artifacts and sentence-transformers ≥3) when the
:mod:safetensors package is importable. Falls back to plain
:func:torch.save when it isn't, so the function still works on
a vanilla pip install thekaveh-nnx without the embeddings extra. In
the fallback case out_path is written as a pickle blob; the
caller's reloader needs to use :func:torch.load.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
backbone
|
Any
|
anything with a |
required |
out_path
|
Union[str, Path]
|
destination file path. Conventionally suffixed
|
required |
Returns:
| Type | Description |
|---|---|
str
|
The string path written. |
Source code in nnx/embeddings/faiss_export.py
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | |
16. Interop (nnx.interop)¶
16.1. GGUF + Ollama¶
End-to-end walkthrough at GGUF & Ollama. Opt-in via pip install "thekaveh-nnx[gguf-write]".
nnx.interop.gguf.writer.write_gguf(transformer_nn: TransformerNN, tokenizer: NNTokenizerParams, out_path: str | os.PathLike, *, architecture: str = 'nnx_transformer', quantization: str = 'F16', model_name: Optional[str] = None) -> str
¶
Write a TransformerNN + tokenizer to a single .gguf file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transformer_nn
|
TransformerNN
|
A |
required |
tokenizer
|
NNTokenizerParams
|
An |
required |
out_path
|
str | PathLike
|
Destination |
required |
architecture
|
str
|
|
'nnx_transformer'
|
quantization
|
str
|
One of |
'F16'
|
model_name
|
Optional[str]
|
|
None
|
Returns:
| Type | Description |
|---|---|
str
|
The absolute path of the written file as a string. |
Raises:
| Type | Description |
|---|---|
ImportError
|
when |
ValueError
|
when an unknown quantization label is passed. |
Source code in nnx/interop/gguf/writer.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 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 244 245 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 | |
nnx.interop.gguf.tensor_name_map.map_tensors(net: TransformerNN) -> dict[str, np.ndarray]
¶
Walk a TransformerNN and return {gguf_name: numpy_array}.
The caller (write_gguf) then iterates this dict and calls
GGUFWriter.add_tensor for each entry. Splitting the iteration
here (rather than inlining it into the writer) keeps the naming
convention testable in isolation — see test_gguf_writer.py.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net
|
TransformerNN
|
A |
required |
Returns:
| Type | Description |
|---|---|
dict[str, ndarray]
|
Dict |
dict[str, ndarray]
|
separate tensors even though the NNx side stores them fused. |
dict[str, ndarray]
|
When |
dict[str, ndarray]
|
is omitted (llama.cpp re-uses |
dict[str, ndarray]
|
models). |
Source code in nnx/interop/gguf/tensor_name_map.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
nnx.interop.ollama.export_ollama_modelfile(transformer_nn: TransformerNN, tokenizer: NNTokenizerParams, out_dir: str | os.PathLike, *, system: str = '', parameters: Optional[dict] = None, template: Optional[str] = None, quantization: str = 'F16', model_name: Optional[str] = None) -> str
¶
Emit model.gguf + Modelfile into out_dir.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transformer_nn
|
TransformerNN
|
An NNx |
required |
tokenizer
|
NNTokenizerParams
|
Corresponding |
required |
out_dir
|
str | PathLike
|
Output directory. Created if it doesn't exist. |
required |
system
|
str
|
Optional system prompt; emitted as a |
''
|
parameters
|
Optional[dict]
|
Optional dict of Ollama runtime parameters
( |
None
|
template
|
Optional[str]
|
Optional chat template. Emitted as a
|
None
|
quantization
|
str
|
Forwarded to :func: |
'F16'
|
model_name
|
Optional[str]
|
Forwarded to :func: |
None
|
Returns:
| Type | Description |
|---|---|
str
|
Absolute path to the emitted |
Source code in nnx/interop/ollama.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
17. HuggingFace Hub + safetensors¶
Opt-in via pip install "thekaveh-nnx[hub]". Two integration surfaces:
- safetensors checkpoints —
NNCheckpoint.to_file(..., format="safetensors")andNNCheckpoint.from_file(..., format="safetensors")(see §3NNCheckpoint) read and write checkpoints in the safetensors format alongside the default pickle path. Loadable by outside-Python tools (ComfyUI, vLLM, AutoGPTQ). - Hub publish / load —
NNModelmixes inhuggingface_hub.PyTorchModelHubMixin, sosave_pretrained(local_dir),push_to_hub(repo_id), andNNModel.from_pretrained(repo_id)work directly on a trained model. The mixin methods are inherited and live onNNModelitself — see §2.1.
Walkthrough at HuggingFace Hub.
18. Generation (nnx.generation)¶
LogitsProcessor chain for autoregressive sampling. Used by GenerativeNNModel.generate() (§2.2). Pure-torch — no optional deps.
nnx.generation.LogitsProcessor
¶
Bases: Protocol
Callable protocol: logits, token_history -> adjusted_logits.
token_history is a flat list of int token ids generated so far
(across batch dim 0 — we assume a single-sequence batch in
GenerativeNNModel.generate). Processors that don't care about
history (temperature, top-k, top-p) simply ignore the arg.
Source code in nnx/generation/logits_processors.py
18 19 20 21 22 23 24 25 26 27 | |
nnx.generation.LogitsChain
dataclass
¶
A typed, ordered sequence of LogitsProcessors.
Build via LogitsChain.builder() for the safe / discoverable
path; or construct directly from a list for advanced cases. The
.apply() method runs the processors against a logits tensor in
order, returning the adjusted tensor.
Source code in nnx/generation/logits_chain.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
apply(logits: torch.Tensor, token_history: list[int]) -> torch.Tensor
¶
Run every processor in self.processors in order. Thin
wrapper around apply_chain.
Source code in nnx/generation/logits_chain.py
62 63 64 65 | |
builder() -> LogitsChainBuilder
classmethod
¶
Return a fluent builder. See LogitsChainBuilder.
Source code in nnx/generation/logits_chain.py
67 68 69 70 | |
nnx.generation.LogitsChainBuilder
¶
Fluent builder for a LogitsChain.
Method order at the call site doesn't matter — .build() sorts
the standard processors into NNx's canonical order (matching
generate()'s inline-kwargs chain; see the module docstring for
why temperature is deliberately last):
RepetitionPenalty → TopKFilter → TopPFilter → TemperatureScaling.
Custom processors (added via .custom(processor)) are appended in
the order they were added, after the canonical group.
Source code in nnx/generation/logits_chain.py
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
build() -> LogitsChain
¶
Construct the LogitsChain with processors in canonical order.
Standard processors that were chained are emitted in the
fixed _CANONICAL_ORDER; custom processors come after, in
the order they were added.
Source code in nnx/generation/logits_chain.py
116 117 118 119 120 121 122 123 124 125 126 127 128 | |
custom(processor: LogitsProcessor) -> LogitsChainBuilder
¶
Append a user-supplied LogitsProcessor after the canonical
group. Useful for logit-bias / forbidden-token / domain-specific
adjustments. Multiple .custom(...) calls append in order.
Source code in nnx/generation/logits_chain.py
109 110 111 112 113 114 | |
repetition_penalty(penalty: float) -> LogitsChainBuilder
¶
Add a RepetitionPenalty processor with the given penalty.
Source code in nnx/generation/logits_chain.py
89 90 91 92 | |
temperature(t: float) -> LogitsChainBuilder
¶
Add a TemperatureScaling processor with the given temperature.
Source code in nnx/generation/logits_chain.py
104 105 106 107 | |
top_k(k: int) -> LogitsChainBuilder
¶
Add a TopKFilter with the given k.
Source code in nnx/generation/logits_chain.py
94 95 96 97 | |
top_p(p: float) -> LogitsChainBuilder
¶
Add a TopPFilter (nucleus sampling) with the given p.
Source code in nnx/generation/logits_chain.py
99 100 101 102 | |
nnx.generation.TemperatureScaling
¶
Divide logits by temperature before sampling.
temperature == 0 is a special case: the chain reduces to greedy
decoding (argmax). We map argmax positions to +inf and others to
-inf so the downstream sampler picks deterministically without
branching on the temperature value.
Source code in nnx/generation/logits_processors.py
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
nnx.generation.TopKFilter
¶
Keep only the top-k logits per row; set the rest to -inf.
-inf survives the temperature divide (still -inf) and gets mapped to 0 probability mass by softmax, so the order top-k → temperature or temperature → top-k both work; we don't enforce an ordering.
Source code in nnx/generation/logits_processors.py
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
nnx.generation.TopPFilter
¶
Nucleus (top-p) sampling: keep the smallest set of tokens whose
cumulative probability exceeds top_p.
Edge case: if a single token already has probability >= top_p, only that token is retained.
Source code in nnx/generation/logits_processors.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
nnx.generation.RepetitionPenalty
¶
Penalize previously-seen tokens (HF-style).
For each token id i in token_history:
* if logits[..., i] > 0: divide by penalty (decreases mass).
* if logits[..., i] < 0: multiply by penalty (increases
magnitude → further decreases relative mass after softmax).
A penalty of 1.0 is a no-op (the back-compat default).
Source code in nnx/generation/logits_processors.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | |
nnx.generation.apply_chain(logits: torch.Tensor, *, token_history: list[int], processors: list[LogitsProcessor]) -> torch.Tensor
¶
Apply every processor in order. No-op when processors is empty.
Source code in nnx/generation/logits_processors.py
133 134 135 136 137 138 139 140 141 142 | |
nnx.generation.sample_next_token(logits: torch.Tensor, *, generator: Optional[torch.Generator] = None) -> int
¶
Draw one token id from softmax(logits).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logits
|
Tensor
|
shape (1, vocab) — single-sequence sample (the LM path's batch-1 generate scope). |
required |
generator
|
Optional[Generator]
|
optional torch.Generator for reproducible seeded sampling. When None, sampling uses the default RNG (still affected by torch.manual_seed at the call site). |
None
|
Returns:
| Type | Description |
|---|---|
int
|
An int token id. |
Source code in nnx/generation/sampling.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | |
19. Visualization¶
19.1. Run-output viz (nnx.vis_utils)¶
nnx.vis_utils
¶
Run-output visualization helpers.
VisUtils collects the Plotly-based visualizations for run outputs —
the artifacts produced after NNModel.train() has completed: training
curves, confusion matrices, classification reports, t-SNE projections
of held-out logits, etc. It is the sibling of nnx.viz (model-internals
visualization — weight histograms, activation maps, gradient flow,
Netron export); the two subpackages are deliberately independent and
do not share code.
Every method is a @staticmethod returning either a plotly.graph_objects.Figure
(for plots) or a pandas.DataFrame (for tables). The class itself
carries layout constants (TITLE_SIZE, LABEL_SIZE, FIG_SIZE,
MARGIN_SIZE) shared across methods, plus an opt-in RENDERER
override for environments where Plotly's default renderer doesn't
work (e.g., when serving from a headless container).
Convenience module-level aliases re-export the most common methods at
the bottom of this file so callers can write nnx.vis_utils.confusion_matrix(...)
instead of nnx.vis_utils.VisUtils.confusion_matrix(...).
confusion_matrix = VisUtils.confusion_matrix
module-attribute
¶
classification_report = VisUtils.classification_report
module-attribute
¶
multi_line_plot = VisUtils.multi_line_plot
module-attribute
¶
scatter_plot = VisUtils.scatter_plot
module-attribute
¶
two_dim_tsne_checkpoint_logits = VisUtils.two_dim_tsne_checkpoint_logits
module-attribute
¶
19.2. Model-internals viz (nnx.viz)¶
Opt-in via pip install "thekaveh-nnx[viz]" (pulls torchinfo + captum) and pip install "thekaveh-nnx[viz-interactive]" (adds the netron browser viewer for nnx.viz.netron_export(..., launch=True)).
nnx.viz.activation.activation_map(model: Union[nn.Module, NNModel], x: torch.Tensor, layer_name: str, *, max_channels: int = 16, cols: int = 4, fig_width: int = 900, cell_size: int = 180) -> go.Figure
¶
Capture the activation of layer_name for input x and render it.
Registers a forward hook on the named submodule, runs model(x) under
torch.no_grad(), then removes the hook and turns the captured tensor
into a Plotly heatmap layout:
- 4D
(N, C, H, W)activations: grid of up tomax_channelsper-channel heatmaps from the first sample (N=0). - 2D
(N, F)activations: single(N, F)heatmap. - Other ranks: flattened single-row heatmap (best-effort fallback).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Union[Module, NNModel]
|
An |
required |
x
|
Tensor
|
Input tensor (or any object) accepted by |
required |
layer_name
|
str
|
Dotted name from |
required |
max_channels
|
int
|
Cap on conv-channel subplots (4D case). Defaults to 16 — enough to spot patterns without crushing the layout for 256-channel feature maps. |
16
|
cols
|
int
|
Subplot columns in the 4D grid. |
4
|
fig_width
|
int
|
Total figure width in pixels. |
900
|
cell_size
|
int
|
Per-subplot square cell size (px). Total height scales with the row count. |
180
|
Returns:
| Type | Description |
|---|---|
Figure
|
A Plotly |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
RuntimeError
|
If the forward hook on |
Source code in nnx/viz/activation.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | |
nnx.viz.attribute.attribute(model: Union[nn.Module, NNModel], x: torch.Tensor, *, method: str = 'integrated_gradients', target: Any = None, **method_kwargs: Any) -> tuple[torch.Tensor, go.Figure]
¶
Compute input attributions via Captum and render a Plotly heatmap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Union[Module, NNModel]
|
An |
required |
x
|
Tensor
|
Input tensor to attribute. Shape |
required |
method
|
str
|
One of |
'integrated_gradients'
|
target
|
Any
|
Target class index (or per-batch indices) for classification
attributors. Forwarded verbatim to Captum's |
None
|
**method_kwargs
|
Any
|
Extra kwargs forwarded to the per-method
|
{}
|
Returns:
| Type | Description |
|---|---|
tuple[Tensor, Figure]
|
A tuple |
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
ValueError
|
If |
Source code in nnx/viz/attribute.py
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | |
nnx.viz.gradient_flow.gradient_flow(model: Union[nn.Module, NNModel]) -> go.Figure
¶
Return a Plotly bar chart of per-parameter L2 gradient norms.
Call AFTER loss.backward() and BEFORE optimizer.zero_grad().
Each bar is one trainable nn.Parameter of the model whose
.grad has been populated by the backward pass; bar height is
the L2 norm of that gradient.
Frozen parameters (requires_grad=False) are skipped. Parameters
whose gradient is None (typically because they weren't reached
during the forward pass) are also skipped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Union[Module, NNModel]
|
an |
required |
Returns:
| Type | Description |
|---|---|
Figure
|
A Plotly |
Figure
|
labeled by |
Raises:
| Type | Description |
|---|---|
ValueError
|
if no parameter has a populated gradient — most
often because |
Source code in nnx/viz/gradient_flow.py
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | |
nnx.viz.netron.netron_export(model: Union[nn.Module, NNModel], path: str, example_input: Union[torch.Tensor, tuple, np.ndarray], *, launch: bool = False, opset_version: int = 17, dynamic_batch: bool = True) -> str
¶
Export model to an ONNX file at path (optionally open Netron).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Union[Module, NNModel]
|
An |
required |
path
|
str
|
Output filename, e.g. |
required |
example_input
|
Union[Tensor, tuple, ndarray]
|
A tensor (or tuple of tensors) with realistic shape / dtype used to trace the network. |
required |
launch
|
bool
|
When True, call |
False
|
opset_version
|
int
|
ONNX opset to target. 17 is broadly supported by current runtimes. |
17
|
dynamic_batch
|
bool
|
When True (default), marks dim 0 as dynamic so the exported graph accepts any batch size at inference. |
True
|
Returns:
| Type | Description |
|---|---|
str
|
The path written (matches |
Raises:
| Type | Description |
|---|---|
ImportError
|
When |
Source code in nnx/viz/netron.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | |
nnx.viz.summary.summary(model: Union[nn.Module, NNModel], *, input_size: tuple[int, ...] | None = None, input_data: Union[torch.Tensor, tuple, list, None] = None, depth: int = 4, col_names: tuple[str, ...] = ('output_size', 'num_params', 'mult_adds')) -> ModelStatistics
¶
Return a torchinfo.ModelStatistics summary for model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Union[Module, NNModel]
|
An |
required |
input_size
|
tuple[int, ...] | None
|
Shape tuple for a synthetic dummy input, e.g. |
None
|
input_data
|
Union[Tensor, tuple, list, None]
|
An actual tensor / tuple / list to forward through the model.
Useful when the model takes multiple positional arguments or a non-tensor
input (graphs, dicts) that |
None
|
depth
|
int
|
Maximum module-nesting depth to expand in the table. |
4
|
col_names
|
tuple[str, ...]
|
Which torchinfo columns to include. Defaults to the three most useful ones for spotting parameter / FLOP regressions across runs. |
('output_size', 'num_params', 'mult_adds')
|
Returns:
| Type | Description |
|---|---|
ModelStatistics
|
The |
ModelStatistics
|
table, or access |
ModelStatistics
|
for programmatic regression assertions. |
Raises:
| Type | Description |
|---|---|
ImportError
|
If |
Source code in nnx/viz/summary.py
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | |
nnx.viz.weight_histogram.weight_histogram(model: Union[nn.Module, NNModel], *, bins: int = 64, cols: int = 3, fig_width: int = 1000, row_height: int = 200) -> go.Figure
¶
Return a Plotly grid of per-parameter weight histograms.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
Union[Module, NNModel]
|
An |
required |
bins
|
int
|
Number of histogram bins per parameter tensor. |
64
|
cols
|
int
|
Number of columns in the subplot grid. Rows are computed from the parameter count. |
3
|
fig_width
|
int
|
Figure width in pixels. |
1000
|
row_height
|
int
|
Per-row height in pixels; total height = |
200
|
Returns:
| Type | Description |
|---|---|
Figure
|
A Plotly |
Figure
|
Each subplot title is the dotted parameter name (e.g. |
Figure
|
Empty parameter tensors are skipped from the grid. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in nnx/viz/weight_histogram.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 | |
20. Utilities¶
nnx.utils
¶
Pretty-printing helpers used throughout nnx.
Both module-level functions (print_tree, print_table, flatten_dict)
and the legacy Utils class API are exported. New code should prefer the
module functions; Utils.method(...) is kept as a thin back-compat shim so
existing notebooks keep working.
print_tree(tree, level: int = 0, *, file=None) -> None
¶
Pretty-print a nested dict as an indented tree.
Pass file= (any object with .write) to redirect output away
from stdout — useful for capturing in tests or writing to a log.
Defaults to sys.stdout.
Source code in nnx/utils.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
print_table(data: dict, header: bool = True, title: Optional[str] = None, *, file=None) -> None
¶
Print data as a 2-column key/value table.
Pass file= to redirect output. Defaults to sys.stdout.
Source code in nnx/utils.py
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
flatten_dict(data: dict, parent_key: str = '', sep: str = '.') -> dict
¶
Flatten a nested dict so nested keys become parent.child style.
flatten_dict({"a": 1, "b": {"c": 2}})
Source code in nnx/utils.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 | |
20.1. Utils back-compat facade¶
nnx.Utils is a thin staticmethod facade over the module-level functions above, kept so existing notebook code that calls Utils.print_tree(...) / Utils.print_table(...) / Utils.flatten_dict(...) continues to work. New code should prefer the module-level functions directly.