TensorFlow Model (Cont.)


Data Normalization
Data should be normalized before being used in a neural network. A range of 0 - 1 using min-max are often best for numerical data:

const inputMin = inputTensor.min( );
const inputMax = inputTensor.max( );
const labelMin = labelTensor.min( );
const labelMax = labelTensor.max( );
const nmInputs = inputTensor.sub( inputMin ).div( inputMax.sub( inputMin ) );
const nmLabels = labelTensor.sub( labelMin ).div( labelMax.sub( labelMin ) );

TensorFlow Model
A machine learning model is an algorithm that produces output from input. This example uses 3 lines to define a ML model:

const model = tf.sequential( );
model.add( tf.layers.dense( { inputShape: [1], units: 1, useBias: true } ) );
model.add( tf.layers.dense( { units: 1, useBias: true } ) );
const model = tf.sequential( );
It creates a sequential ML model, which the input flows directly to the output. Other models can have multiple inputs and multiple outputs. The sequential one is the easiest ML model. It allows you to build a model layer by layer, with weights that correspond to the next layer.

model.add( )
It is used to add two layers to the model.

tf.layer.dense
It is a layer type that works in most cases. It multiplies its inputs by a weight-matrix and adds a number (bias) to the result.

inputShape: [1]
It is because we have 1 input (x = horsepower).

units: 1
It defines the size of the weight matrix: 1 weight for each input (x value).
http://undcemcs01.und.edu/~wen.chen.hu/course/525/13/car.html
<html>
 <head>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis"></script>
  <title>Car TensorFlow.js</title>
 </head>
 <body>
  <form>
   Horsepower:
   <input name="hp" id="hp" size="7" value="85" />
   <input type="button" onClick="runTF( )" value="Train and test" />
   <input type="reset" />
   The predicted MPG is <span id="answer">0.00</span> miles per gallon.
   <div id="plot1"></div>
   <div id="plot2"></div>
  </form>

  <script>
   // Extracting the required data 
   function extractData( obj ) {
    return { x:obj.Horsepower, y:obj.Miles_per_Gallon };
   }
   function removeErrors( obj ) {
    return ( ( obj.x != null ) && ( obj.y != null ) );
   }

   // Plotting the extracted data
   function tfPlot( values, surface ) {
    tfvis.render.scatterplot( surface,
     { values:values, series:[ 'Original', 'Predicted' ] },
     { xLabel:'Horsepower', yLabel:'MPG' } );
   }

   // Main function
   async function runTF( ) {
    const jsonData = await fetch( "carsData.json" );
    let values = await jsonData.json( );
    values = values.map( extractData ).filter( removeErrors );

    // Plotting the Data
    const surface1 = document.getElementById( "plot1" );
    const surface2 = document.getElementById( "plot2" );
    tfPlot( values, surface1 );

    // Converting the input to Tensors
    const inputs = values.map( obj => obj.x );
    const labels = values.map( obj => obj.y );
    const inputTensor = tf.tensor2d( inputs, [inputs.length, 1] );
    const labelTensor = tf.tensor2d( labels, [labels.length, 1] );
    const inputMin = inputTensor.min( );  
    const inputMax = inputTensor.max( );
    const labelMin = labelTensor.min( );
    const labelMax = labelTensor.max( );
    const nmInputs = inputTensor.sub(inputMin).div( inputMax.sub(inputMin) );
    const nmLabels = labelTensor.sub(labelMin).div( labelMax.sub(labelMin) );

    // Creating a Tensorflow model
    const model = tf.sequential( ); 
    model.add( tf.layers.dense( { inputShape:[1], units:1, useBias:true } ) );
    model.add( tf.layers.dense( { units: 1, useBias: true } ) );
    model.compile( { loss:'meanSquaredError', optimizer:'sgd' } );

    // Starting training
    await trainModel( model, nmInputs, nmLabels, surface2 );

    // Un-normalizing the data
    let unX = tf.linspace( 0, 1, 100 );      
    let unY = model.predict( unX.reshape( [100, 1] ) );      
    const unNormunX = unX
     .mul( inputMax.sub( inputMin ) )
     .add( inputMin );
    const unNormunY = unY
     .mul( labelMax.sub( labelMin ) )
     .add( labelMin );
    unX = unNormunX.dataSync( );
    unY = unNormunY.dataSync( );

    // Testing the model
    const predicted = Array.from(unX).map( (val, i) => {
     return { x: val, y: unY[i] }
    } );
    tfPlot( [values, predicted], surface1 );

    // Finding the MPG of the input horsepower
    var hp = parseInt( document.getElementById( "hp" ).value );
    unX.sort( ( a, b ) => a[0] - b[0] );
    let x1 = unX[0];
    for ( let i = 1; i < unX.length-1; i++ ) {
     let x2 = unX[i];
     if ( ( x1 <= hp ) && ( hp < x2 ) ) {
      document.getElementById("answer").innerHTML = Math.round( unY[i-1] );
      break;
     }
     x1 = x2;
    }
   }     // End of the main function runTF( )

   // Asyncronous function to train the model
   async function trainModel( model, inputs, labels, surface ) {
    const batchSize = 25;
    const epochs = 50;
    const callbacks = tfvis.show.fitCallbacks(
     surface, ['loss'], { callbacks:['onEpochEnd'] } );
    return await model.fit( inputs, labels,
     { batchSize, epochs, shuffle:true, callbacks:callbacks } );
   }     // End of trainModel
  </script>
 </body>
</html>