npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tfjs-node-grid-search

v0.7.28

Published

tfjs-node-grid-search (TNGS) is a grid search utility for TensorFLow.js in Node.js. Simply define ranges of hyperparameters. For every combination, TNGS will create a model, train it and test it using your data set. Results are logged and (optionally) wri

Downloads

16

Readme

tfjs-node-grid-search · npm version MIT License

| Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | -------------------- | | Statements | Branches | Functions | Lines |

tfjs-node-grid-search (TNGS) is a grid search utility for TensorFLow.js in Node.js. Simply define ranges of hyperparameters. For every combination, TNGS will create a model, train it and test it using your data set. Results are logged and (optionally) written as a CSV file.

Learn about running TensorFlow in Node.

Documentation

joemullenix-ks.github.io/tfjs-node-grid-search

Installation

npm i tfjs-node-grid-search

Linux/macOS

After installing, you may need to compile the TF bindings locally. If you see this error message, follow its "npm rebuild" instructions:

Uncaught Error: The Node.js native addon module (tfjs_binding.node) can not be found at path: /Users/PROJECT/node_modules/@tensorflow/tfjs-node/lib/napi-v7/tfjs_binding.node.

Please run command 'npm rebuild @tensorflow/tfjs-node build-addon-from-source' to rebuild the native addon module.

Note: If the rebuild fails, you may need C++ tools. That's outside the scope of this package, but fairly easy to solve. Please search "g++" for more info.

Full Example

The example code below is written for TypeScript. To run it as plain JS, do the following:

  • Add "type": "module" to your package.json, or use CommonJS to require the library. See #1 of the step-by-step guide (below).
  // For plain JS without ES modules, change this...
  import * as tngs from 'tfjs-node-grid-search';

  // ...to this.
  const tngs = require('tfjs-node-grid-search');
  • Remove types from the scoring callback's signature (line ~98), like so:
  // For plain JS, change this...
  const evaluatePrediction = (target: number[], prediction: number[])...

  // ...to this.
  const evaluatePrediction = (target, prediction)...

Full Example:

import * as tngs from 'tfjs-node-grid-search';

console.log('Welcome to TNGS!');

//NOTE: This example demonstrates a very simple XOR problem. It runs these two
//      network models:
//      - zero hidden layers (SLP), which cannot learn XOR
//      - one hidden layer (MLP), which learns XOR

//NOTE: We wrap the example in an async function, because tfjs's model fit
//      is asynchronous.
const main = async () => {
  // First, we define the axes for our grid search (the available axis types are
  // enumerated in Axis (with many more coming soon!)).
  // For each axis, we set a begin and end boundary, which are inclusive. We
  // also create a progression across that range, i.e. the values at which
  // we'll stop to train and test a model.

  const axes = [];

  // test first with zero hidden layers, then one hidden layer
  axes.push(
    new tngs.Axis(
      tngs.AxisTypes.LAYERS,
      0,
      1,
      new tngs.LinearProgression(1)
    )
  );

  const axisSet = new tngs.AxisSet(axes);

  // Next, we define the static parameters. That is, those params that never
  // change during our grid search.

  const modelStatics = new tngs.ModelStatics({
    epochs: 200,
    learnRate: 0.005,
    validationSplit: 0.5
  });

  // Next, we setup options that will govern the Grid itself, as well as the
  // search process.

  const gridOptions = new tngs.GridOptions({
    repetitions: 2,
    resultsDirectory: '',
    validationSetSizeMin: 1,
    writeResultsAsCSV: true
  });

  // Now we load and configure the data set. A fresh copy of this data will be
  // used to train and test each 'iteration' of the grid search (i.e. each
  // unique combination of dynamic params).

  // set aside one third of the cases for post-training generalization tests
  const TEST_DATA_FRACTION = 1 / 3;

  //NOTE: We load each XOR case three times, for 12 total cases.
  //      - Four are reserved for generalization (TEST_DATA_FRACTION, above).
  //      - With a 'validationSplit' of 0.5, the remaining eight are divided
  //        into four each for testing and validation.
  //
  //      The output labels are ["false", "true"].

  //NOTE: We use hard-coded data for demonstration purposes, only. The more
  //      common usage is to load data from disk or url, via DataSetFetcher.
  //      Please see #4 in the step-by-step guide (below).
  const dataSet = new tngs.DataSet(
    [
      [0, 0], [0, 1], [1, 0], [1, 1],
      [0, 0], [0, 1], [1, 0], [1, 1],
      [0, 0], [0, 1], [1, 0], [1, 1]
    ],
    [
      [1, 0], [0, 1], [0, 1], [1, 0],
      [1, 0], [0, 1], [0, 1], [1, 0],
      [1, 0], [0, 1], [0, 1], [1, 0]
    ]);

  const sessionData = new tngs.SessionData(
    TEST_DATA_FRACTION,
    dataSet,
    false
  );

  // This callback is used by the Grid during generalization testing. At the end
  // of each iteration, after the network is trained, the Grid makes predictions
  // using the test data. For each case, it calls this function, passing the
  // known targets and its prediction.
  // We evaluate each prediction, and return a PredictionEvaluation. This lets
  // the system calculate the network's score.
  //	- If the prediction is acceptable, set the first argument ("correct")
  //    to true.
  //	- The second argument (which is optional) is "delta". This lets you give
  //    each prediction a more detailed score, in arbitrary units.

  const evaluatePrediction = (target: number[], prediction: number[]) => {
    const targettedIndex = tngs.Utils.ArrayFindIndexOfHighestValue(target);

    const predictedIndex = tngs.Utils.ArrayFindIndexOfHighestValue(prediction);

    //NOTE: This example is written for a multi-class classification network
    //      (one-hot targets).

    // if the network chose the correct index, we pass true
    const correct = targettedIndex === predictedIndex;

    // we also pass a delta value, measuring how close the prediciton is to 1.0
    // (or 100% probability)
    const delta = 1 - prediction[predictedIndex];

    return new tngs.PredictionEvaluation(correct, delta);
  };

  // We're now ready to create the Grid, and run the search!

  const grid = new tngs.Grid(
    axisSet,
    modelStatics,
    sessionData,
    evaluatePrediction,
    gridOptions
  );

  await grid.Run();
};

// run the example, with a top-level exception handler
main().catch(reason => {
  console.log('There was a problem.', reason);
});

Step-By-Step

These steps demonstrate more advanced techniques, and some of the options left out of the full example (above).

1. Include the library

There are two supported methods for including the TNGS library:

Via ES Modules (import)

import * as tngs from 'tfjs-node-grid-search';

Via CommonJS (require)

const tngs = require('tfjs-node-grid-search');

NOTE: It's important the grid search be done in an async context, because the filesystem reads and the model training process are asynchronous.

2. Define the grid's axes (the dynamic parameters)

Decide which hyperparameters will have a range of values. For each, create an Axis. Use an array of axes to instatiate an AxisSet. Example:

  const axes = [];

  // test batch sizes from 8 to 16 inclusive, in linear steps of 4 (8, 12, 16)
  axes.push(
    new tngs.Axis(
      tngs.AxisTypes.BATCH_SIZE,
      8,
      16,
      new tngs.LinearProgression(4)
    )
  );

  // test learning rates from 0.01 to 0.001 inclusive, in linear steps of 0.003
  // (0.01, 0.007, 0.004, 0.001)
  axes.push(
    new tngs.Axis(
      tngs.AxisTypes.LEARN_RATE,
      0.01,
      0.001,
      new tngs.LinearProgression(0.003)
    )
  );

  const axisSet = new tngs.AxisSet(axes);

3. Define the static model parameters

Set values for model parameters that will not change throughout the search. Instantiate a ModelStatics with these values.

  const modelStatics = new tngs.ModelStatics({
    epochs: 5,
    hiddenLayers: 1,
    validationSplit: 0.25
  });

4. Fetch the data set

Fetch your training/testing data by passing the filepaths to an instance of DataSetFetcher, and fetching an instance of DataSet.

NOTE: DataSetFetcher is designed to take the Node launch-args vector (see example), but any array of strings will work. It expects the third item in the array to be the path to your inputs file, and the fourth to be the path to your targets file.

Use the DataSet to instantiate a SessionData, which also takes your input-standardization preference (true in this example, meaning 'use default algorithm').

If you use process.argv as designed, consider launching your app like this:

Example command line
  > node my-tngs-app.js data_inputs.txt data_targets.txt

Example VSCode configuration property (in launch.json)
  "args": ["data_inputs.txt", "data_targets.txt"]
  const dataSetFetcher = new tngs.DataSetFetcher(process.argv);

  const dataSet = await dataSetFetcher.Fetch();

  // set aside 10% of these cases for post-training generalization tests
  const TEST_DATA_FRACTION = 0.1;

  const sessionData = new tngs.SessionData(
    TEST_DATA_FRACTION,
    dataSet,
    true
  );

5. Define the evaluation callback

One callback must be supplied to the search, so that predictions can be scored. Define a function that takes two arrays ("target" and "prediction"), and returns an instance of PredictionEvaluation. The search process will gather your scores, and create a report telling you how each combination of hyperparameters performed.

  const evaluatePrediction = (target: number[], prediction: number[]) => {
    const targettedIndex = tngs.Utils.ArrayFindIndexOfHighestValue(target);

    const predictedIndex = tngs.Utils.ArrayFindIndexOfHighestValue(prediction);

    return new tngs.PredictionEvaluation(targettedIndex === predictedIndex);
  };

6. Run the grid search

You're all set! Instantiate a Grid, and call Run(). Again, this should be done in an async function, as TensorFlow's model.fit is asynchronous.

  const grid = new tngs.Grid(
    axisSet,
    modelStatics,
    sessionData,
    evaluatePrediction
  );

  await grid.Run();

7. (OPTIONAL) Define search parameters

There are optional settings that do not affect your neural network. If you wish to modify these, instantiate a GridOptions. Otherwise defaults will be used.

  const gridOptions = new tngs.GridOptions({
    epochStatsDepth: 3,
    repetitions: 2,
    resultsDirectory: '',
    validationSetSizeMin: 1000,
    writeResultsAsCSV: true
  });

  // create a grid with customized options
  const grid = new tngs.Grid(
    axisSet,
    modelStatics,
    sessionData,
    evaluatePrediction,
    gridOptions
  );

8. (OPTIONAL) Define reporting callbacks

By default, the system reports results and stats to the log at the end of every epoch. If you wish to do your own analysis, you may provide callbacks for end-of-batch, end-of-epoch and end-of-iteration (where 'iteration' is a single model's run). These optional functions have the following signatures:

callbackReportIteration(duration: number, predictions: number[][], proofInputs: Array, proofTargets: number[][])

callbackReportEpoch(duration: number, epoch: number, logs: tf.Logs, epochStats: EpochStats)

callbackReportBatch(duration: number, batch: number, logs: tf.Logs | undefined)

Pass them to Grid. No return value(s) are expected.

  // create a grid with customized options
  const grid = new tngs.Grid(
    axisSet,
    modelStatics,
    sessionData,
    evaluatePrediction,
    null, // null for default settings, otherwise an instance of GridOptions
    callbackReportIteration,
    callbackReportEpoch,
    callbackReportBatch
  );

Roadmap

  • Rewrite DataSetFetcher as a utility, rather than a class to be instantiated.
  • Support data fetch via URL.
  • Offer custom schedules (as opposed to progressions) of grid-axis steps.
  • Optional callback for create-model.
  • Optional callback for compile-model (and/or more initializers, optimizers, loss functions and metrics).
  • Support additional hyperparams (e.g. bias, dropouts).
  • Detection and intervention/early-exit on overfit/stuck.
  • Support other network types (e.g. regression) w/ templates.
  • Support more complex networks (e.g. multi in/out, convolutions).
  • Write the model & weight files. // UNBLOCKED by the node bug as of 2.8.0!
  • Smart-start method for skipping poor/unlucky initial weights.
  • Node FORK/SPAWN for parallel runs.
  • Localization, as desired (very few external strings).

Legacy TF versions on Windows

:warning: An older version of tfjs-node (v2.7.0) has an installation problem on Windows. See details

    TLDR, here's the workaround:

    Copy tensorflow.dll
        from: node_modules\@tensorflow\tfjs-node\deps\lib
          to: node_modules\@tensorflow\tfjs-node\lib\napi-v6

License

tfjs-node-grid-search is MIT licensed.