{"id":17485697,"url":"https://github.com/doktormike/neuralnethack","last_synced_at":"2026-06-27T22:00:30.767Z","repository":{"id":28626351,"uuid":"32145107","full_name":"DoktorMike/neuralnethack","owner":"DoktorMike","description":"My research code from my phd in neural networks.","archived":false,"fork":false,"pushed_at":"2026-06-27T15:35:44.000Z","size":2431,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-06-27T16:07:51.578Z","etag":null,"topics":["backpropagation-learning-algorithm","c-plus-plus","deep-learning","deep-neural-networks","deeplearning","feedforward-neural-network","neural-networks"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DoktorMike.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2015-03-13T08:51:29.000Z","updated_at":"2026-06-27T15:35:47.000Z","dependencies_parsed_at":"2022-08-02T16:00:38.671Z","dependency_job_id":null,"html_url":"https://github.com/DoktorMike/neuralnethack","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/DoktorMike/neuralnethack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoktorMike%2Fneuralnethack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoktorMike%2Fneuralnethack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoktorMike%2Fneuralnethack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoktorMike%2Fneuralnethack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DoktorMike","download_url":"https://codeload.github.com/DoktorMike/neuralnethack/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoktorMike%2Fneuralnethack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34869004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backpropagation-learning-algorithm","c-plus-plus","deep-learning","deep-neural-networks","deeplearning","feedforward-neural-network","neural-networks"],"created_at":"2024-10-19T02:08:31.940Z","updated_at":"2026-06-27T22:00:30.703Z","avatar_url":"https://github.com/DoktorMike.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NeuralNetHack\n\n[![CI](https://github.com/DoktorMike/neuralnethack/actions/workflows/ci.yml/badge.svg)](https://github.com/DoktorMike/neuralnethack/actions/workflows/ci.yml)\n![Coverage](./coverage-badge.svg)\n![Code Style](./format-badge.svg)\n![C++23](https://img.shields.io/badge/C%2B%2B-23-blue)\n![License](https://img.shields.io/badge/license-MIT-green)\n\nThis is the MLP and ensemble-of-MLPs library I've kept maintained, however infrequent, since 2004. It's small, fast, and stays out of your way: a C++23 core, an optional BLAS dependency, and nothing else. I reach for it on tabular problems where libtorch is overkill and I actually want to see what the optimizer is doing. If that sounds like your kind of thing, read on.\n\n## Features\n\n- **Activations**: Sigmoid, TanH, Linear, ReLU, Leaky ReLU, ELU\n- **Topology**: sequential MLP with optional residual (skip) connections, merged pre-activation between same-width layers\n- **Output heads**: linear or sigmoid output, plus optional softmax for multi-class classification\n- **Optimizers**: SGD with momentum, Adam/AdamW, L-BFGS\n- **Loss functions**: cross-entropy, summed square error, with optional per-class weights for imbalanced data\n- **Normalization**: batch normalization, layer normalization\n- **Regularization**: dropout (inverted), weight elimination\n- **Ensembles**: weighted ensemble of MLPs with bootstrap, cross-split, or hold-out sampling, trained in parallel via OpenMP\n- **Model selection**: grid search over regularization with cross-validation\n- **Feature selection**: backward elimination via saliency / clamping\n- **Evaluation**: ROC/AUC (with bootstrap confidence interval and a one-sided p-value), Hosmer-Lemeshow goodness of fit, confusion matrix (binary and multi-class) with accuracy / precision / recall / F1 / MCC / balanced accuracy / macro variants, regression metrics (MAE, MAPE, sMAPE, RMSE, R²)\n- **Uncertainty**: ensemble spread, total/aleatoric/epistemic entropy decomposition (Depeweg et al. 2018), and split-conformal prediction sets and intervals with coverage guarantees\n- **Diagnostics**: per-trainer learning-curve files (train and validation error per epoch), gnuplot-friendly\n- **Serialization**: binary save/load for models and ensembles\n- **Performance**: BLAS-accelerated batch GEMM training, devirtualized activations, SIMD-friendly loops\n- **Distribution**: ships as a CMake static library *and* a generated single-header amalgamation (stb-style) for drop-in use\n\n## Who is this for?\n\nIf you're doing tabular regression or classification in C++ and you actually care about *how confident* the model is (ensembles for spread, conformal sets for coverage guarantees, an explicit aleatoric/epistemic split), this is one of the few C++ libraries that treats that as the point rather than an afterthought. I built it for that and I keep using it for that.\n\nIt's not a libtorch replacement and I'm not going to pretend it is. Reach for something else if:\n\n- you need GPUs, big tensors, or anything Transformer-shaped → [**flashlight**](https://github.com/flashlight/flashlight).\n- you want trees, SVMs, k-means, or Python/Julia bindings alongside the NN bits → [**mlpack**](https://github.com/mlpack/mlpack).\n- you just want a header-only CNN demo → [**tiny-dnn**](https://github.com/tiny-dnn/tiny-dnn) (caveat: it's been quiet since around 2020).\n- you're allowed to use Python → **scikit-learn** or **PyTorch**. Don't be a hero.\n\nIf you want the receipts, a full feature-by-feature comparison with the same libraries lives in [`doc/comparison.md`](doc/comparison.md).\n\n## Speed\n\nAt realistic data scale this library is fast. On UCI Covertype (581k rows, 54 features, 7-class softmax MLP, 5 epochs) it lands the **lowest inference latency and the highest test accuracy** in a head-to-head against mlpack and tiny-dnn under identical config. On tiny-matrix workloads (Pima, 8-32-1) mlpack still wins on training time because BLAS per-call dispatch overhead dominates over the actual compute there. Numbers and the bench harness live in [`doc/comparison.md`](doc/comparison.md#speed-and-accuracy-on-real-benchmarks) and [`bench/`](bench/).\n\n## Build\n\n```sh\nmake          # configure + build\nmake test     # run all tests\nmake coverage # build with gcov, run tests, generate HTML report\nmake format   # apply clang-format to all source files\nmake clean    # remove build directories\n```\n\nYou'll need GCC 13+ or Clang 17+ for C++23. BLAS is auto-detected (install `libopenblas-dev` or similar for best performance), and you can switch it off with `cmake -B build -DNNH_USE_BLAS=OFF` if you really want to.\n\nOpenMP is also auto-detected and used to train ensemble members in parallel. Control with `OMP_NUM_THREADS` at run time, or disable at configure time with `cmake -B build -DNNH_OPENMP=OFF`.\n\n## Single-header amalgamation\n\nIf you'd rather not depend on the CMake build, the whole library is also shipped as a single header at `single_include/neuralnethack.hh`. Drop it into your project, follow the stb-style consumer pattern, and you're done -- no library to build, no CMake target to link against:\n\n```cpp\n// in exactly ONE translation unit:\n#define NNH_IMPLEMENTATION\n#include \"neuralnethack.hh\"\n\n// every other TU just:\n#include \"neuralnethack.hh\"\n```\n\nCompile with `g++ -std=c++23 -O2 your_app.cc`. The amalgamation is self-contained: BLAS and OpenMP are *optional*, not required to compile -- if you want them, define `USE_BLAS` / `NNH_USE_OPENMP` and link the matching libraries (`-lopenblas` / `-fopenmp`).\n\nThe header is regenerated by `scripts/amalgamate.py` (topo-sorts the public headers by include deps, dedupes system includes, gates the implementation under `NNH_IMPLEMENTATION`):\n\n```sh\nmake single-include   # regenerate + smoke-compile\n```\n\nCI runs the same target on every PR and fails if `single_include/neuralnethack.hh` ends up out of sync with the source tree, so the committed artifact always matches the rest of the repo.\n\n## Quick start: learning XOR\n\n```cpp\n#include \"mlp/Mlp.hh\"\n#include \"mlp/Adam.hh\"\n#include \"mlp/SummedSquare.hh\"\n#include \"mlp/Serialization.hh\"\n#include \"datatools/CoreDataSet.hh\"\n#include \"datatools/DataSet.hh\"\n#include \"datatools/Pattern.hh\"\n\n#include \u003ciostream\u003e\n#include \u003cmemory\u003e\n#include \u003cstring\u003e\n#include \u003cvector\u003e\n\nusing namespace MultiLayerPerceptron;\nusing namespace DataTools;\n\nint main()\n{\n    // Build the XOR dataset\n    auto core = std::make_shared\u003cCoreDataSet\u003e();\n    double xor_in[][2]  = {{0,0}, {0,1}, {1,0}, {1,1}};\n    double xor_out[][1] = {{0},   {1},   {1},   {0}};\n    for (int i = 0; i \u003c 4; ++i) {\n        std::vector\u003cdouble\u003e in(xor_in[i], xor_in[i] + 2);\n        std::vector\u003cdouble\u003e out(xor_out[i], xor_out[i] + 1);\n        core-\u003eaddPattern(Pattern(std::to_string(i), in, out));\n    }\n    DataSet data;\n    data.coreDataSet(core);\n\n    // 2-4-1 network with ReLU hidden and sigmoid output\n    std::vector\u003cuint\u003e arch = {2, 4, 1};\n    std::vector\u003cstd::string\u003e types = {\"relu\", \"logsig\"};\n    Mlp mlp(arch, types, false);\n\n    // Optional: enable BatchNorm and a bit of dropout\n    mlp.normType(NormType::BatchNorm);\n    mlp.dropoutRate(0.1);\n\n    // Train with Adam for 2000 epochs\n    SummedSquare error(mlp, data);\n    Adam trainer(mlp, data, error, 0.001, 4 /*batch*/, 0.01 /*lr*/);\n    trainer.numEpochs(2000);\n    trainer.train(std::cout);\n\n    // Evaluate\n    for (int i = 0; i \u003c 4; ++i) {\n        const auto\u0026 out = mlp.propagate(data.pattern(i).input());\n        std::cout \u003c\u003c xor_in[i][0] \u003c\u003c \" XOR \" \u003c\u003c xor_in[i][1]\n                  \u003c\u003c \" = \" \u003c\u003c out[0] \u003c\u003c std::endl;\n    }\n\n    // Save and reload\n    saveMlpBinary(mlp, \"xor.nnh\");\n    auto loaded = loadMlpBinary(\"xor.nnh\");\n    std::cout \u003c\u003c \"Loaded: \" \u003c\u003c loaded-\u003epropagate(data.pattern(1).input())[0] \u003c\u003c std::endl;\n}\n```\n\n## Residual (skip) connections\n\nEach layer can optionally take a residual input from an earlier layer. The skip source's output is added element-wise into the target layer's pre-activation, before the activation function:\n\n```\nz = W · y_prev + b + y_skip       // skip added before activation\ny = act(z)\n```\n\nPre-activation rather than post-activation, because the existing activation-derivative formulas all express f'(z) in terms of f(z). Putting the skip in pre-activation means that bookkeeping keeps working without any extra plumbing.\n\nTwo hard constraints:\n\n- **Source must come earlier in the chain.** A layer can only skip from a layer with a smaller index. `skipFrom()` aborts otherwise.\n- **Source and target must have the same width.** The merge is element-wise, so the shapes have to line up.\n\n### Layer indexing\n\nThis is the part that trips people up. Indices count up from the first hidden layer. The input vector is *not* a layer. So for an architecture `[n_in, n_h1, n_h2, n_h3, n_out]`:\n\n```\narch:    [n_in,    n_h1,    n_h2,    n_h3,    n_out]\n                    ^        ^        ^        ^\n                  layer 0  layer 1  layer 2  layer 3 (output)\n```\n\nWhich means in `arch = [2, 4, 4, 1]` (input plus two width-4 hidden plus width-1 output), layers 0 and 1 are both width 4 and can be wired together with a skip.\n\n### From C++\n\n```cpp\nstd::vector\u003cuint\u003e arch = {2, 4, 4, 1};\nstd::vector\u003cstd::string\u003e types = {\"tansig\", \"tansig\", \"logsig\"};\nMlp mlp(arch, types, false);\n\n// Layer 1's pre-activation gets layer 0's output added in.\nmlp.skipFrom(/*target=*/1, /*source=*/0);\n```\n\nPass `-1` as the source to clear an existing skip on a given target.\n\n### From a TOML config\n\nUnder `[network]`, add `skip_connections` as an array of `[target, source]` pairs:\n\n```toml\n[network]\nsize = [2, 4, 4, 1]\nactivations = [\"tansig\", \"tansig\", \"logsig\"]\nerror_fcn = \"kullback\"\nskip_connections = [[1, 0]]   # layer 1 receives skip from layer 0\n```\n\nOne skip source per target layer (later entries for the same target overwrite earlier ones). Multiple targets are free to share the same source.\n\nFor a full worked example with an ensemble of residual MLPs, see `examples/xor_residual_ensemble.cc`.\n\n## Multi-class classification (softmax)\n\nFor K-way classification, use a linear output layer of width K and turn softmax on. Pair it with the cross-entropy loss and the (target - output) shortcut at the output layer gives you exactly the right gradient (no derivative on softmax to apply explicitly, the math cancels).\n\nFrom C++:\n\n```cpp\nstd::vector\u003cuint\u003e arch = {4, 8, 3};                  // 4-feature input, 3 classes\nstd::vector\u003cstd::string\u003e types = {\"tansig\", \"purelin\"};\nMlp mlp(arch, types, /*softmax=*/true);\n```\n\nFrom a TOML config:\n\n```toml\n[network]\nsize = [4, 8, 3]\nactivations = [\"tansig\", \"purelin\"]\nsoftmax = true\nerror_fcn = \"kullback\"\n```\n\nTargets should be one-hot encoded (one column per class in the data file, `out_cols = \"6-8\"` for example). Worked examples in `examples/multiclass_iris.cc`, `examples/multiclass_wine.cc`, and `examples/multiclass_synthetic.cc`.\n\n## Uncertainty quantification\n\nA point prediction without a sense of how much to trust it is half an answer.\nNeuralNetHack treats uncertainty as a first-class output, not an afterthought.\n\n**Epistemic vs aleatoric.** For an ensemble of classifiers, the entropy of\nthe averaged prediction decomposes into the part that comes from genuine class\noverlap (aleatoric, irreducible) and the part that comes from the members\ndisagreeing (epistemic, which shrinks with more data and grows out of\ndistribution). This is the Depeweg et al. 2018 decomposition:\n\n```cpp\n#include \"evaltools/Uncertainty.hh\"\nusing namespace EvalTools::Uncertainty;\n\n// Per-member probability vectors (e.g. softmax outputs), or pass an Ensemble.\nauto d = decomposeEntropy(ensemble, input);\nstd::cout \u003c\u003c \"total=\" \u003c\u003c d.total\n          \u003c\u003c \" aleatoric=\" \u003c\u003c d.aleatoric\n          \u003c\u003c \" epistemic=\" \u003c\u003c d.epistemic \u003c\u003c \"\\n\";\n```\n\nHigh epistemic with low aleatoric is the classic \"the model is guessing\nbecause it has not seen anything like this\" signal. See\n`examples/iris_ensemble_uncertainty.cc` and `spiral_ensemble_uncertainty.cc`\nfor the full per-grid-point version, and `cubic_ensemble_uncertainty.cc` for\nthe regression-spread analogue.\n\n**Conformal prediction.** When you need a distribution-free coverage\nguarantee rather than a heuristic score, calibrate a `Conformal` predictor on\nheld-out data and get prediction sets (classification) or intervals\n(regression) that contain the truth at the requested rate. See\n`evaltools/Conformal.hh`.\n\n**AUC confidence.** `Roc::aucBootstrapCI` resamples the evaluation set to put\na confidence interval and a one-sided p-value around the AUC, so \"0.82\" comes\nwith \"and here is how sure we are it beats chance.\"\n\n## Examples\n\nWorked examples live in `examples/` and build as separate executables:\n\n```sh\ncmake --build build --target xor_residual_ensemble\n./build/xor_residual_ensemble        # default ensemble size\n./build/xor_residual_ensemble 11     # custom ensemble size\n```\n\nThe ensemble examples take an optional positional argument: the number of ensemble members.\n\n| Example | What it shows |\n|---|---|\n| `xor_residual_ensemble.cc` | Residual MLP (2-4-4-1 with skip 0→1) trained five times from different inits and combined into an `Ensemble` with uniform 1/N weighting. Reports per-member outputs and the ensemble's averaged prediction on each XOR pattern. |\n| `residual_vs_plain.cc` | A 12-layer tanh MLP on a synthetic regression task, trained twice with identical init: with and without 5 residual blocks. The residual variant converges to roughly half the MSE of the plain one, because tanh's saturating activation makes gradients vanish across 12 layers without the skip identity path. Loss curves go to `residual_vs_plain.csv`. |\n| `residual_ensemble_uncertainty.cc` | Ensemble of 7 residual MLPs trained on `x ∈ [-3, 3]` and evaluated on `x ∈ [-6, 6]`. Inside the training range the members agree (std ≈ 0.01); outside it they extrapolate to wildly different functions (std ≈ 0.5, 30× wider). The growing spread is epistemic uncertainty, made visible. |\n| `cubic_ensemble_uncertainty.cc` | Same uncertainty story on the canonical Amini *Deep Evidential Regression* cubic benchmark: `y = x^3 + N(0, 3)` trained on `x ∈ [-4, 4]` and evaluated on `x ∈ [-6, 6]`. ReLU members extrapolate piecewise-linearly into OOD where the truth is super-linear, so the mean prediction undershoots dramatically and the spread balloons. |\n| `multiclass_synthetic.cc` | Tiny softmax demo on a synthetic 3-region planar split. No data files, no fuss. Prints train/test accuracy. |\n| `multiclass_iris.cc` | Softmax MLP on the UCI Iris dataset (3 classes, 4 features). Loads `datasets/iris/iris.{trn,tst}.tab`, Z-normalises, trains, reports accuracy. |\n| `multiclass_wine.cc` | Same for the UCI Wine dataset (3 classes, 13 features). |\n| `iris_ensemble_uncertainty.cc` | Ensemble of softmax MLPs on the petal-length / petal-width pair, with the full Depeweg et al. 2018 entropy decomposition: total, aleatoric, and epistemic per grid point. Plot via `scripts/plotexamplesresultdata.r`. |\n| `spiral_ensemble_uncertainty.cc` | Three-arm Archimedean spiral, same decomposition. Useful as a sanity check that the network is doing what you think it's doing. |\n\n## Run from a config file\n\nDon't want to write any C++? You don't have to. The `neuralnethack` binary takes a single config file and does the whole thing: parses the data, normalises it, trains an ensemble (with model selection if you ask for one), evaluates on the test set, and writes everything to disk.\n\n```sh\n./build/neuralnethack config.toml\n```\n\nThere's a working example under `datasets/pima/` if you want something to run right now:\n\n```sh\ncd datasets/pima\n../../build/neuralnethack config-pima.toml\n```\n\nFor multi-class classification, similar configs ship with the iris and wine datasets:\n\n```sh\ncd datasets/iris   \u0026\u0026 ../../build/neuralnethack config-iris.toml\ncd datasets/wine   \u0026\u0026 ../../build/neuralnethack config-wine.toml\n```\n\nEvery output file is suffixed with whatever you put in the `suffix` field, so you can run a few experiments side by side without clobbering each other:\n\n- `result.\u003csuffix\u003e.txt`: train/test AUC (binary) or accuracy (multi-class).\n- `networks.\u003csuffix\u003e.xml`: the trained ensemble, ready to reload.\n- `outputlist.\u003csuffix\u003e.txt`: per-pattern model outputs (toggle with `save_output_list`).\n- `saliencies.\u003csuffix\u003e.txt`: input saliencies, handy for feature selection.\n- `myconfig.debug`: the parsed config, so you can sanity-check what was actually used.\n- `\u003ccurve\u003e_NNN.dat` (when `output.learning_curve_file` is set): per-member learning curves, one row per epoch with `epoch  trainErr  valErr`. The validation error comes from each member's out-of-bag split.\n\nThe other CLI tools (`ann`, `modelselector`, `featureselector`, `saliency`, `auc`) all read the same config format. Pick the one that matches what you're after.\n\n### Config file format\n\nConfigs are TOML. Sections group related settings, named keys replace the old positional tuples (no more counting arguments), and comments use `#`. A minimal binary-classification config looks like this:\n\n```toml\nsuffix = \"myrun\"\nseed = 42\nnormalization = \"Z\"          # \"Z\" or \"no\"\nproblem_type = \"class\"       # \"class\" or \"regr\"\n\n[data.train]\nfile = \"data/train.tab\"\nid_col = 0                   # 0 = no id column\nin_cols = \"1-8\"              # range string, 1-indexed\nout_cols = \"9\"\nrow_range = \"0\"              # \"0\" = all rows\n\n[data.test]\nfile = \"data/test.tab\"\nid_col = 0\nin_cols = \"1-8\"\nout_cols = \"9\"\nrow_range = \"0\"\n\n[network]\nsize = [8, 4, 1]\nactivations = [\"relu\", \"logsig\"]   # one per non-input layer\nerror_fcn = \"kullback\"             # \"sumsqr\" or \"kullback\"\nsoftmax = false                    # true for multi-class with linear output\nweight_init = \"glorot\"             # \"glorot\" (default) or \"legacy_uniform\".\n                                   # glorot picks Xavier uniform for saturating\n                                   # activations and He uniform for ReLU-family,\n                                   # both scaled to fan-in / fan-out. Biases\n                                   # initialise to zero. legacy_uniform is the\n                                   # pre-4.1.0 U(-0.5, 0.5) draw, kept for\n                                   # back-compat with serialised models.\n# Optional residual connections: each entry is [target_layer, source_layer]\n# (0-indexed, source \u003c target, both layers must have matching width).\n# skip_connections = [[2, 0]]\n\n[training]\nmethod = \"adam\"              # \"gd\", \"adam\", \"qn\"\nmax_epochs = 2000\n\n[training.adam]\nlearning_rate = 0.001\nbeta1 = 0.9\nbeta2 = 0.999\nepsilon = 1e-8\nweight_decay = 0.01\n\n[training.early_stopping]\npatience = 0                 # 0 disables (default). When \u003e 0 the trainer stops\nmin_delta = 0.0              # if val loss has not improved by min_delta for\n                             # `patience` recorded epochs, and the model weights\n                             # are restored to the best-val snapshot.\n\n[regularization.weight_elim]\nenabled = false\nalpha = 0.01\nw0 = 1.0\n\n[ensemble]\nmethod = \"bagg\"              # \"bagg\", \"cs\"\nruns = 5\nparts = 2\nsplit = \"rnd\"                # \"rnd\" or \"ser\"\nvary_weights = false\n\n[model_selection]\nmethod = \"cv\"                # \"cv\", \"boot\", \"hold\", \"none\"\nruns = 3\nparts = 5\nsplit = \"rnd\"\nfraction = 0.2\n\n[output]\nsave_session = true\nsave_output_list = true\n# learning_curve_file = \"curve.dat\"   # optional, per-member files \u003cstem\u003e_NNN.\u003cext\u003e\n```\n\nSee `datasets/pima/config-pima.toml` for a fully commented version with every field.\n\n#### Migrating from the legacy format\n\nConfigs from version 2.x and earlier used a space-separated `{Identifier} {Value} {Value} ...` format with `%` comments. There's a script for that:\n\n```sh\nscripts/migrate-config.py old-config.txt -o new-config.toml\n```\n\nIt handles the field rename, splits the positional tuples (`GDParam`, `AdamParam`, `EnsParam`, `MSParam`, `WeightElim`, `Vary`) into named keys, and drops the result into the right section. Eyeball the output before running it for real, since the legacy format had a few oddities.\n\n## License\n\nMIT, Copyright (c) 2004-2026 Michael Green\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoktormike%2Fneuralnethack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoktormike%2Fneuralnethack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoktormike%2Fneuralnethack/lists"}