Powerball (Lottery) prediction using TensorFlow.js LSTM Time Series - ChatGPT Generated

If you're looking to build a Tensorflow.js LSTM model to predict the Powerball numbers (or any lottery game based on time series data), it's important to note a few things upfront:

  • Lottery Numbers Are Random: The numbers in Powerball are drawn randomly. Predicting random numbers is inherently unreliable because there is no underlying pattern. However, for educational purposes, you could treat Powerball draws as a time series problem, even though the model won't have any real predictive power. In practice, this exercise could be useful for learning time series prediction techniques.
  • Data Structure: Powerball numbers consist of 5 white balls (with numbers from 1 to 69) and a Powerball (with numbers from 1 to 26). Each draw can be treated as a sequence of 6 numbers (5 white balls + 1 Powerball). In a time series context, this means you're predicting sequences of numbers over time.
  • Model Setup: The model will learn from historical data to predict the next Powerball numbers.

Steps:

  1. Prepare Powerball Data
  2. Preprocess the Data
  3. Build and Train the LSTM Model
  4. Make Predictions

Step 1: Install Tensorflow.js

Install Tensorflow.js if you haven't already:

npm install @tensorflow/tfjs

Or, if you're using a browser, include the TensorFlow.js script:


<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>

Step 2: Prepare and Preprocess the Data

Assume that you have the historical Powerball data in an array format, where each entry is a set of Powerball numbers:


const data = [
  [5, 10, 12, 22, 29, 16],  // Example Powerball set: 5, 10, 12, 22, 29, Powerball=16
  [8, 15, 19, 30, 35, 10],  // Another example
  [7, 10, 13, 20, 24, 13],  // Another example
  // Add more Powerball draws...
];

Each entry contains five regular numbers and one Powerball number. We will scale these numbers to the range [0, 1] for training.

Step 3: Preprocess the Data

You’ll need to scale the data, as neural networks work better when the data is normalized. We can use a MinMaxScaler for scaling the numbers between 0 and 1.


// Function to scale the data
function scaleData(data) {
  const min = Math.min(...data.flat());
  const max = Math.max(...data.flat());
  
  return data.map(draw => 
    draw.map(num => (num - min) / (max - min))  // Scale each number in the draw
  );
}

// Scale the Powerball data
const scaledData = scaleData(data);

Then, create a function to prepare the data for the LSTM model. We'll use previous draws (say 10 draws) to predict the next one.


function createSequences(data, lookBack) {
  let X = [];
  let y = [];
  
  for (let i = lookBack; i < data.length; i++) {
    const inputSeq = data.slice(i - lookBack, i);  // The last 'lookBack' draws
    const outputSeq = data[i];  // The next draw (target)
    X.push(inputSeq);
    y.push(outputSeq);
  }
  
  return { X, y };
}

const lookBack = 10;  // Use the last 10 draws to predict the next one
const { X, y } = createSequences(scaledData, lookBack);

You’ll need to convert X and y into tensors for training the model. The shape of X should be [samples, time steps, features] for LSTM input.


const XTensor = tf.tensor3d(X, [X.length, lookBack, X[0].length]);
const yTensor = tf.tensor2d(y, [y.length, y[0].length]);

Step 4: Build the LSTM Model

Now let’s define the LSTM model. Since we’re working with 6 features (5 regular numbers + 1 Powerball), the model’s output will be a vector of 6 predicted values (scaled).


async function buildLSTMModel(inputShape) {
  const model = tf.sequential();

  model.add(tf.layers.lstm({
    units: 50,
    inputShape: inputShape,
    returnSequences: false
  }));
  
  model.add(tf.layers.dense({
    units: 6  // 5 regular numbers + 1 Powerball
  }));

  model.compile({
    optimizer: 'adam',
    loss: 'meanSquaredError',
  });

  return model;
}

// Build the LSTM model
const model = await buildLSTMModel([lookBack, 6]);

Step 5: Train the Model

Now, let's train the model with the prepared data.


async function trainModel() {
  await model.fit(XTensor, yTensor, {
    epochs: 100,
    batchSize: 32,
    shuffle: false,
  });

  console.log("Model Training Complete");
}

trainModel();

Step 6: Make Predictions

Once the model is trained, you can use it to make predictions on the next draw.


async function predictNextDraw(inputSeq) {
  const inputTensor = tf.tensor3d([inputSeq], [1, lookBack, 6]);
  const prediction = model.predict(inputTensor);
  
  // Inverse the scaling to get the original numbers
  const predictionArray = prediction.arraySync()[0];
  const min = Math.min(...data.flat());
  const max = Math.max(...data.flat());

  const inverseScaledPrediction = predictionArray.map(num => num * (max - min) + min);
  
  console.log("Predicted Powerball draw:", inverseScaledPrediction);
}

// Use the last 10 draws as input to predict the next draw
const lastSequence = scaledData.slice(-lookBack);  // The last 10 draws
predictNextDraw(lastSequence);

Complete Code:

Here's the complete example put together:


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TensorFlow.js LSTM Powerball Prediction</title>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
</head>
<body>

  <script>
    // Sample Powerball data (5 regular numbers + Powerball)
    const data = [
      [5, 10, 12, 22, 29, 16],
      [8, 15, 19, 30, 35, 10],
      [7, 10, 13, 20, 24, 13],
      // Add more historical data...
    ];

    // Normalize the data
    function scaleData(data) {
      const min = Math.min(...data.flat());
      const max = Math.max(...data.flat());

      return data.map(draw => draw.map(num => (num - min) / (max - min)));
    }

    const scaledData = scaleData(data);

    // Create sequences for LSTM (lookBack = 10)
    function createSequences(data, lookBack) {
      let X = [];
      let y = [];

      for (let i = lookBack; i < data.length; i++) {
        const inputSeq = data.slice(i - lookBack, i);
        const outputSeq = data[i];
        X.push(inputSeq);
        y.push(outputSeq);
      }

      return { X, y };
    }

    const lookBack = 10;
    const { X, y } = createSequences(scaledData, lookBack);

    const XTensor = tf.tensor3d(X, [X.length, lookBack, 6]);
    const yTensor = tf.tensor2d(y, [y.length, 6]);

    // Build LSTM model
    async function buildLSTMModel(inputShape) {
      const model = tf.sequential();
      model.add(tf.layers.lstm({
        units: 50,
        inputShape: inputShape,
        returnSequences: false
      }));
      model.add(tf.layers.dense({ units: 6 }));
      model.compile({ optimizer: 'adam', loss: 'meanSquaredError' });
      return model;
    }

    async function trainModel() {
      const model = await buildLSTMModel([lookBack, 6]);
      await model.fit(XTensor, yTensor, { epochs: 100, batchSize: 32, shuffle: false });
      console.log("Model Training Complete");

      // Predict the next draw
      const lastSequence = scaledData.slice(-lookBack);
      const inputTensor = tf.tensor3d([lastSequence], [1, lookBack, 6]);
      const prediction = model.predict(inputTensor);

      const predictionArray = prediction.arraySync()[0];
      const min = Math.min(...data.flat());
      const max = Math.max(...data.flat());

      const inverseScaledPrediction = predictionArray.map(num => num * (max - min) + min);
      console.log("Predicted Powerball draw:", inverseScaledPrediction);
    }

    trainModel();
  </script>
</body>
</html>

Second Example

  1. Collect and Format the Data

    For this example, let's assume you've collected Powerball results as historical data in the format:

    
    [
      {"draw_date": "2023-01-01", "white_balls": [1, 12, 23, 34, 45], "powerball": 6},
      {"draw_date": "2023-01-04", "white_balls": [2, 13, 24, 35, 46], "powerball": 7},
      ...
    ]
    

    You will need to transform this data into a format that the LSTM can understand, which will involve creating sequences from past draws to predict future draws.

  2. Preprocess the Data

    Since LSTM models require numeric input, you need to normalize the data, especially the numbers, so that they are within a reasonable range.

    • Normalize the Data: Normalize the white ball numbers between 0 and 1, and the Powerball number between 0 and 1 as well.
    • Prepare Sequences: You can use previous draws to predict the next draw. For example, using the last n draws as inputs to predict the next one.
    
    function preparePowerballData(data, lookback) {
        const X = [];
        const y = [];
        
        for (let i = 0; i < data.length - lookback; i++) {
            const sequence = data.slice(i, i + lookback);
            const nextDraw = data[i + lookback];
            
            X.push(sequence);
            y.push(nextDraw);
        }
    
        // Convert to TensorFlow.js tensors
        return { X: tf.tensor(X), y: tf.tensor(y) };
    }
    
  3. Normalize the Data

    Normalize the Powerball numbers using MinMaxScaler:

    
    
    function normalizeData(data, minValue, maxValue) {
        return data.map(d => (d - minValue) / (maxValue - minValue));
    }
    
    function denormalizeData(normalizedData, minValue, maxValue) {
        return normalizedData * (maxValue - minValue) + minValue;
    }
    

    You would apply this normalization for the white balls (with a max of 69) and the Powerball (with a max of 26).

  4. Build the LSTM Model

    Next, let's build the LSTM model. We'll have an LSTM layer to capture the temporal dependencies and a Dense layer to output the prediction.

    
    function buildLSTMModel(lookback) {
        const model = tf.sequential();
        
        model.add(tf.layers.lstm({
            units: 50,
            activation: 'relu',
            inputShape: [lookback, 6], // 6 because 5 white balls + 1 Powerball
            returnSequences: false
        }));
        
        model.add(tf.layers.dense({ units: 6 })); // Output 6 numbers: 5 white balls and 1 Powerball
        
        model.compile({
            optimizer: 'adam',
            loss: 'meanSquaredError'
        });
        
        return model;
    }
    

    Here, the model has:

    • 50 LSTM units.
    • The input shape [lookback, 6] because each input sequence consists of 6 numbers (5 white balls + 1 Powerball).
    • The output shape is 6 because you want to predict 6 numbers.
  5. Train the Model

    Now, we train the model on the data.

    
    async function trainModel(model, X, y) {
        await model.fit(X, y, {
            epochs: 100,
            batchSize: 32,
            validationSplit: 0.1
        });
    }
    
  6. Making Predictions

    After training the model, you can use it to predict the next Powerball draw, based on the last few draws:

    
    async function predict(model, data, lookback) {
        const inputData = data.slice(data.length - lookback);  // Use the last 'lookback' draws
        const inputTensor = tf.tensor(inputData).reshape([1, lookback, 6]);
        
        const prediction = model.predict(inputTensor);
        const predictedNumbers = prediction.dataSync();
    
        // Denormalize the numbers to get them back to their original range
        const whiteBalls = predictedNumbers.slice(0, 5).map(num => denormalizeData(num, 1, 69));
        const powerball = denormalizeData(predictedNumbers[5], 1, 26);
        
        return { whiteBalls, powerball };
    }
    
  7. Putting It All Together

    Here's how you would put everything into one working example.

    
    async function main() {
        // Sample data (replace with your actual historical data)
        const rawData = [
            {"white_balls": [1, 12, 23, 34, 45], "powerball": 6},
            {"white_balls": [2, 13, 24, 35, 46], "powerball": 7},
            // Add more historical draws...
        ];
    
        // Flatten and normalize data
        const normalizedData = rawData.map(d => [
            ...normalizeData(d.white_balls, 1, 69),
            normalizeData([d.powerball], 1, 26)[0]
        ]);
    
        // Prepare data for training
        const lookback = 5;  // Look back at the last 5 draws to predict the next draw
        const { X, y } = preparePowerballData(normalizedData, lookback);
    
        // Build the LSTM model
        const model = buildLSTMModel(lookback);
    
        // Train the model
        await trainModel(model, X, y);
    
        // Predict the next draw
        const prediction = await predict(model, normalizedData, lookback);
    
        console.log(`Predicted White Balls: ${prediction.whiteBalls}`);
        console.log(`Predicted Powerball: ${prediction.powerball}`);
    }
    
    main();
    
  8. Important Considerations
    • Randomness of Lottery Numbers: As mentioned earlier, Powerball numbers are drawn randomly, and a machine learning model won't have any real predictive power. This model is more of an academic exercise than a practical application for predicting lottery numbers.
    • Evaluation: Normally, you would evaluate the model's accuracy using metrics like Mean Squared Error (MSE) or Mean Absolute Error (MAE). However, for random data like Powerball, these metrics won’t provide meaningful insights.
    • Feature Engineering: You could add additional features like the frequency of each number or the day of the week when the draw happens, but this still won't create a reliable model for predicting lottery numbers.
    • Overfitting: Given the random nature of the problem, the model will likely overfit on the historical data. You can try techniques like dropout layers or regularization to prevent this.

Conclusion

In this example, we’ve created a basic LSTM model using TensorFlow.js for time series forecasting, applied to the Powerball data. However, keep in mind that predicting lottery numbers is not feasible with machine learning models because the draws are random by design.

This tutorial is meant to be an exercise in using LSTM models with time series data, but don’t expect the model to give you accurate predictions for the next Powerball draw!

Comments

Popular posts from this blog

Decompiling Delphi - 3

Decompiling Delphi - 2

Demystifying DevExpress XtraReport for Silverlight - Part 3