What You'll Build
In this exercise, you will construct a complete machine learning pipeline that predicts cricket player performance metrics using TensorFlow and Keras. The system ingests historical player statistics — including batting average, strike rate, wickets taken, and economy rate — then preprocesses the data through normalization and feature engineering before producing predictions.
The pipeline requires building a sequential neural network with multiple dense layers and dropout regularization, training it with appropriate loss functions and optimizers, and evaluating its performance using metrics relevant to cricket analytics. This exercise integrates data handling with pandas, numerical computation with NumPy, model building with the Keras functional and sequential APIs, callback mechanisms for training control, and validation techniques to prevent overfitting.
By the end of this exercise, you will have a production-ready model that demonstrates mastery of core Keras workflows, including data preparation, architecture design, training loops, and performance evaluation.
Prerequisites
- Proficiency with NumPy arrays and Pandas DataFrames for data manipulation and transformation tasks
- Understanding of neural network fundamentals including layers, neurons, activation functions, and forward propagation
- Knowledge of supervised learning concepts like training sets, validation sets, loss functions, and performance metrics
- Experience with Python 3.8+ and familiarity with virtual environments and package managers like pip
- Conceptual awareness of overfitting, regularization techniques like dropout, and the purpose of callbacks in training
Setup & Project Structure
Begin by setting up a dedicated project directory with organized subdirectories for data, models, and scripts. Install TensorFlow 2.12 or later, which includes Keras as its high-level API, along with supporting libraries such as NumPy, Pandas, and Scikit-learn for data preprocessing. Creating a virtual environment is essential to isolate dependencies and maintain project reproducibility.
Organize your project structure with separate modules for data loading and preprocessing, model definition and architecture, training orchestration, and evaluation routines. This modular approach enables code reuse, simplifies debugging, and establishes the professional development practices essential for production machine learning systems.
#!/bin/bash
# Create project structure for cricket performance prediction
mkdir -p cricket_ml_pipeline/{data,models,scripts,notebooks}
cd cricket_ml_pipeline
# Create virtual environment
python3 -m venv cricket_env
source cricket_env/bin/activate
# Install dependencies
pip install --upgrade pip
pip install tensorflow==2.14.0
pip install numpy==1.24.3
pip install pandas==2.0.3
pip install scikit-learn==1.3.0
pip install matplotlib==3.7.2
pip install jupyter==1.0.0
# Create directory structure
touch scripts/data_loader.py
touch scripts/model_trainer.py
touch scripts/evaluator.py
touch data/cricket_stats.csv
touch models/player_performance_model.h5
echo "Cricket ML Pipeline project initialized successfully!"Step 1 — Foundation
The foundation step establishes data loading, exploration, and preprocessing pipelines that are essential for all downstream machine learning tasks. You will create synthetic cricket player statistics representing variables such as innings played, runs scored, batting average, strike rate, and bowling economy.
Implement data normalization using StandardScaler to ensure all features operate on comparable scales. This is a critical requirement because neural networks learn through gradient descent, which performs poorly when features have vastly different numeric ranges.
Split your dataset into training and validation subsets using stratified sampling to maintain consistent statistical properties across both splits. This foundation work is the most consequential step in the pipeline, since rigorous data preparation directly determines model quality — garbage input invariably produces garbage output.
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import warnings
warnings.filterwarnings('ignore')
# Step 1: Data Loading and Exploration
class CricketPlayerDataLoader:
"""Load and preprocess cricket player performance statistics."""
def __init__(self, random_state=42):
self.random_state = random_state
self.scaler = StandardScaler()
self.feature_names = None
def generate_cricket_data(self, num_players=200):
"""
Generate synthetic cricket player dataset with performance metrics.
Mirrors squad analysis: gathering player statistics across formats.
"""
np.random.seed(self.random_state)
# Cricket player metrics (like coaches analyzing player statistics)
player_data = {
'player_id': range(1, num_players + 1),
'innings_count': np.random.randint(10, 150, num_players),
'batting_average': np.random.uniform(20, 65, num_players),
'strike_rate': np.random.uniform(80, 130, num_players),
'centuries_scored': np.random.randint(0, 20, num_players),
'fifties_scored': np.random.randint(0, 40, num_players),
'bowling_average': np.random.uniform(20, 50, num_players),
'wickets_taken': np.random.randint(0, 300, num_players),
'economy_rate': np.random.uniform(6, 9, num_players),
'fitness_score': np.random.uniform(60, 100, num_players),
'age_years': np.random.randint(18, 42, num_players),
'career_span_years': np.random.randint(0, 20, num_players)
}
cricket_df = pd.DataFrame(player_data)
# Create target: Player Selection Status (Selected=1, Not Selected=0)
# Elite players have high averages + centuries + low bowling averages
selection_criteria = (
(cricket_df['batting_average'] > 40) &
(cricket_df['centuries_scored'] > 2) |
(cricket_df['wickets_taken'] > 50) & (cricket_df['bowling_average'] < 30)
)
cricket_df['selected_for_match'] = selection_criteria.astype(int)
self.feature_names = [col for col in cricket_df.columns
if col not in ['player_id', 'selected_for_match']]
return cricket_df
def explore_dataset(self, cricket_df):
"""Display dataset exploration - like analyzing squad statistics."""
print("🏏 CRICKET SQUAD ANALYSIS - DATA EXPLORATION")
print("=" * 60)
print(f"\nDataset Shape: {cricket_df.shape}")
print(f"Total Players: {cricket_df.shape[0]}")
print(f"Performance Metrics: {len(self.feature_names)}\n")
print("📊 Dataset Overview:")
print(cricket_df.head(5))
print("\n📈 Statistical Summary:")
print(cricket_df.describe().round(2))
print(f"\n✅ Selected for Match: {cricket_df['selected_for_match'].sum()} players")
print(f"❌ Not Selected: {(1 - cricket_df['selected_for_match']).sum()} players")
return cricket_df
def normalize_player_features(self, cricket_df):
"""
Feature normalization - Conditioning protocol ensuring fair comparison.
Like bringing all players to same fitness baseline for objective evaluation.
"""
X = cricket_df[self.feature_names].copy()
y = cricket_df['selected_for_match'].copy()
# Fit scaler on full dataset, then transform
X_normalized = self.scaler.fit_transform(X)
X_normalized_df = pd.DataFrame(X_normalized, columns=self.feature_names)
print("\n🎯 FEATURE NORMALIZATION - CONDITIONING PROTOCOL")
print("=" * 60)
print("Before Normalization (Raw metrics):")
print(X.describe().round(2))
print("\nAfter Normalization (Scaled to fair comparison):")
print(X_normalized_df.describe().round(2))
return X_normalized_df, y
def split_dataset(self, X_normalized, y):
"""
Split data for training and evaluation.
Like dividing squad: Training team vs Test team.
"""
X_train, X_test, y_train, y_test = train_test_split(
X_normalized, y,
test_size=0.2,
random_state=self.random_state,
stratify=y
)
print("\n🎓 DATASET SPLIT - SQUAD DIVISION")
print("=" * 60)
print(f"Training Set: {X_train.shape[0]} players")
print(f"Test Set: {X_test.shape[0]} players")
print(f"Training Data Shape: {X_train.shape}")
print(f"Test Data Shape: {X_test.shape}")
return X_train, X_test, y_train, y_test
# Step 2: Build Neural Network Model
def build_player_selection_model(input_features):
"""
Build neural network to predict player selection.
Architecture mirrors decision-making complexity of selection committees.
"""
model = keras.Sequential([
# Layer 1: Initial feature processing (scouts initial assessment)
layers.Dense(64, activation='relu', input_shape=(input_features,),
name='scout_assessment'),
layers.Dropout(0.3, name='dropout_1'),
# Layer 2: Complex pattern recognition (committee deliberation)
layers.Dense(32, activation='relu', name='committee_deliberation'),
layers.Dropout(0.2, name='dropout_2'),
# Layer 3: Refined evaluation (coaching staff analysis)
layers.Dense(16, activation='relu', name='coach_analysis'),
# Output: Final selection decision
layers.Dense(1, activation='sigmoid', name='selection_decision')
])
return model
# Step 3: Complete Training Pipeline
def train_player_selection_model(X_train, X_test, y_train, y_test):
"""Comprehensive training pipeline with evaluation."""
print("\n🏏 PLAYER SELECTION MODEL - TRAINING PIPELINE")
print("=" * 60)
# Build model
model = build_player_selection_model(input_features=X_train.shape[1])
# Compile with appropriate loss for binary classification
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=0.001),
loss='binary_crossentropy',
metrics=['accuracy', keras.metrics.AUC(name='auc')]
)
print("\n📋 Model Architecture:")
model.summary()
# Train model
print("\n🎯 Training in Progress...")
history = model.fit(
X_train, y_train,
epochs=50,
batch_size=16,
validation_data=(X_test, y_test),
verbose=0
)
# Evaluate on test set
test_loss, test_accuracy, test_auc = model.evaluate(
X_test, y_test,
verbose=0
)
print(f"\n✅ Training Complete!")
print(f"Test Accuracy: {test_accuracy:.4f}")
print(f"Test AUC: {test_auc:.4f}")
print(f"Test Loss: {test_loss:.4f}")
return model, history
# Step 4: Player-specific Predictions
def predict_player_selection(model, X_test, player_names):
"""Make predictions for individual players."""
print("\n🏏 MATCH DAY SQUAD PREDICTIONS")
print("=" * 60)
predictions = model.predict(X_test, verbose=0)
selected_players = []
for idx in range(min(10, len(player_names))):
selection_prob = predictions[idx][0]
status = "✅ SELECTED" if selection_prob > 0.5 else "❌ NOT SELECTED"
confidence = selection_prob if selection_prob > 0.5 else 1 - selection_prob
print(f"{player_names[idx]:20} | Confidence: {confidence:.2%} | {status}")
if selection_prob > 0.5:
selected_players.append(player_names[idx])
print(f"\n🎖️ Final Squad Size: {len(selected_players)} players")
return selected_players
# ========== EXECUTION ==========
if __name__ == "__main__":
# Initialize data loader
loader = CricketPlayerDataLoader(random_state=42)
# Generate cricket player dataset
cricket_df = loader.generate_cricket_data(num_players=200)
cricket_df = loader.explore_dataset(cricket_df)
# Normalize features (conditioning protocol)
X_normalized, y = loader.normalize_player_features(cricket_df)
# Split dataset
X_train, X_test, y_train, y_test = loader.split_dataset(X_normalized, y)
# Train model
model, history = train_player_selection_model(X_train, X_test, y_train, y_test)
# Generate player names for predictions
famous_cricketers = [
'Rohit Sharma', 'Virat Kohli', 'Jasprit Bumrah', 'Hardik Pandya',
'KL Rahul', 'Ravindra Jadeja', 'Mohammed Shami', 'Rishabh Pant',
'Suryakumar Yadav', 'Axar Patel'
]
# Make predictions
selected_squad = predict_player_selection(model, X_test[:10], famous_cricketers)
print("\n" + "=" * 60)
print("🏆 Foundation work complete! Model ready for match day selection.")
print("=" * 60)Step 2 — Core Logic
The core logic step constructs the neural network architecture that learns patterns from cricket statistics in order to predict player performance ratings. You will implement a sequential model with multiple dense layers interspersed with dropout regularization to prevent overfitting.
Dense layers perform a linear transformation followed by an activation function — ReLU for hidden layers and a linear activation for the regression output — enabling the network to learn complex non-linear relationships between input features. Dropout layers complement this by randomly deactivating neurons during training, forcing the network to develop robust feature representations that do not depend on any single neuron.
Compile the model with an appropriate loss function (mean squared error for regression), an optimizer (Adam for its adaptive learning rate), and relevant evaluation metrics. This architecture forms the core learning mechanism through which the network discovers how batting statistics, bowling metrics, and fielding contributions collectively influence overall player performance ratings.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, models
from tensorflow.keras.optimizers import Adam
import numpy as np
# Step 2: Neural Network Architecture Definition
class CricketPerformanceModel:
"""Build and compile neural network for cricket player performance prediction."""
def __init__(self, input_dim, learning_rate=0.001):
self.input_dim = input_dim
self.learning_rate = learning_rate
self.model = None
def build_sequential_model(self):
"""
Construct sequential model with dropout regularization.
Architecture:
- Input: 7 features (cricket statistics)
- Hidden layer 1: 64 neurons, ReLU activation
- Dropout: 0.2 (drops 20% of neurons)
- Hidden layer 2: 32 neurons, ReLU activation
- Dropout: 0.2
- Hidden layer 3: 16 neurons, ReLU activation
- Output layer: 1 neuron, linear activation (regression)
"""
self.model = models.Sequential([
# Input layer with 64 neurons
layers.Dense(64, activation='relu', input_dim=self.input_dim,
name='input_layer'),
# Regularization: randomly drop 20% of neurons
layers.Dropout(0.2, name='dropout_1'),
# Hidden layer with 32 neurons
layers.Dense(32, activation='relu', name='hidden_layer_1'),
layers.Dropout(0.2, name='dropout_2'),
# Hidden layer with 16 neurons
layers.Dense(16, activation='relu', name='hidden_layer_2'),
layers.Dropout(0.15, name='dropout_3'),
# Output layer for regression (predict continuous match impact rating)
layers.Dense(1, activation='linear', name='output_layer')
])
return self.model
def compile_model(self):
"""
Compile model with appropriate loss, optimizer, and metrics.
"""
self.model.compile(
loss='mse', # Mean Squared Error for regression
optimizer=Adam(learning_rate=self.learning_rate), # Adaptive optimizer
metrics=['mae'] # Mean Absolute Error for interpretability
)
return self.model
def print_model_summary(self):
"""Display model architecture and parameter counts."""
self.model.summary()
def get_model(self):
"""Return compiled model."""
return self.model
# Execute Step 2
if __name__ == "__main__":
# Initialize model builder
model_builder = CricketPerformanceModel(input_dim=7, learning_rate=0.001)
# Build architecture
model = model_builder.build_sequential_model()
# Compile model
model = model_builder.compile_model()
# Display architecture
print("\n" + "="*60)
print("CRICKET PLAYER PERFORMANCE MODEL ARCHITECTURE")
print("="*60)
model_builder.print_model_summary()
print("\nModel Configuration:")
print(f" Input dimensions: 7 (cricket statistics)")
print(f" Hidden layers: 3 (64 → 32 → 16 neurons)")
print(f" Dropout regularization: Applied between layers")
print(f" Output: Single continuous value (match impact rating)")
print(f" Loss function: Mean Squared Error (MSE)")
print(f" Optimizer: Adam (learning_rate={model_builder.learning_rate})")
print(f" Total trainable parameters: {model.count_params()}")Step 3 — Integration & Enhancement
The integration step combines the data pipeline from Step 1 and the model architecture from Step 2 into a complete training system with advanced features. Implement custom callbacks to monitor training progress: EarlyStopping to halt training when validation loss plateaus, ModelCheckpoint to preserve the best model weights, and ReduceLROnPlateau to dynamically decrease the learning rate when improvement stalls.
Create a comprehensive training loop that feeds prepared data through the model, tracks metrics across epochs, and manages the optimization process. Additionally, implement custom metrics that provide cricket-specific insights, such as prediction accuracy within acceptable performance ranges.
This integration step reflects real-world machine learning practice, where disparate components must work cohesively. Robust error handling, logging, and checkpoint mechanisms are essential to ensure reproducible results and to protect against failures during long training runs.
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau
from tensorflow.keras.optimizers import Adam
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Step 3: Integration & Training with Callbacks
class CricketModelTrainer:
"""Complete training pipeline with callbacks and monitoring."""
def __init__(self, model, X_train, X_val, y_train, y_val):
self.model = model
self.X_train = X_train
self.X_val = X_val
self.y_train = y_train
self.y_val = y_val
self.history = None
self.best_val_loss = float('inf')
def define_callbacks(self, checkpoint_path='models/best_model.h5'):
"""
Create callbacks for enhanced training control.
"""
callbacks = [
# Stop training if validation loss doesn't improve for 20 epochs
EarlyStopping(
monitor='val_loss',
patience=20,
restore_best_weights=True,
verbose=1,
mode='min'
),
# Save model weights when validation loss improves
ModelCheckpoint(
filepath=checkpoint_path,
monitor='val_loss',
save_best_only=True,
verbose=0,
mode='min'
),
# Reduce learning rate when validation loss plateaus
ReduceLROnPlateau(
monitor='val_loss',
factor=0.5, # Multiply learning rate by 0.5
patience=10,
min_lr=0.00001,
verbose=1,
mode='min'
),
# Custom callback for cricket-specific logging
CricketTrainingCallback()
]
return callbacks
def train(self, epochs=100, batch_size=16, callbacks=None):
"""
Execute training loop with validation and callbacks.
"""
if callbacks is None:
callbacks = self.define_callbacks()
print("\n" + "="*70)
print("INITIATING CRICKET PLAYER PERFORMANCE MODEL TRAINING")
print("="*70)
print(f"Training samples: {len(self.X_train)}")
print(f"Validation samples: {len(self.X_val)}")
print(f"Batch size: {batch_size}")
print(f"Maximum epochs: {epochs}")
print("="*70 + "\n")
self.history = self.model.fit(
self.X_train, self.y_train,
validation_data=(self.X_val, self.y_val),
epochs=epochs,
batch_size=batch_size,
callbacks=callbacks,
verbose=1
)
return self.history
def evaluate_on_validation(self):
"""
Evaluate trained model on validation set.
"""
val_loss, val_mae = self.model.evaluate(
self.X_val, self.y_val, verbose=0
)
print("\n" + "="*70)
print("VALIDATION PERFORMANCE")
print("="*70)
print(f"Validation MSE Loss: {val_loss:.6f}")
print(f"Validation MAE: {val_mae:.6f}")
print("="*70)
return val_loss, val_mae
def plot_training_history(self):
"""
Visualize training and validation metrics over epochs.
"""
if self.history is None:
print("No training history available. Train model first.")
return
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Plot loss
axes[0].plot(self.history.history['loss'], label='Training Loss', linewidth=2)
axes[0].plot(self.history.history['val_loss'], label='Validation Loss', linewidth=2)
axes[0].set_title('Model Loss Over Epochs', fontsize=12, fontweight='bold')
axes[0].set_xlabel('Epoch')
axes[0].set_ylabel('MSE Loss')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Plot MAE
axes[1].plot(self.history.history['mae'], label='Training MAE', linewidth=2)
axes[1].plot(self.history.history['val_mae'], label='Validation MAE', linewidth=2)
axes[1].set_title('Mean Absolute Error Over Epochs', fontsize=12, fontweight='bold')
axes[1].set_xlabel('Epoch')
axes[1].set_ylabel('MAE')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('models/training_history.png', dpi=100, bbox_inches='tight')
print("Training history plot saved to 'models/training_history.png'")
plt.show()
class CricketTrainingCallback(keras.callbacks.Callback):
"""Custom callback providing cricket-themed training feedback."""
def on_epoch_end(self, epoch, logs=None):
if epoch % 10 == 0 and epoch > 0:
val_loss = logs.get('val_loss', 0)
train_loss = logs.get('loss', 0)
print(f"\n🏏 Innings Update (Epoch {epoch}):")
print(f" Batting performance (training): {train_loss:.6f}")
print(f" Bowling analysis (validation): {val_loss:.6f}")
print(f" Run rate trajectory: {'Improving' if train_loss < logs.get('loss', float('inf')) else 'Stable'}")
# Execute Step 3
if __name__ == "__main__":
from Step2 import CricketPlayerDataLoader, CricketPerformanceModel
# Load and prepare data (from Step 1)
loader = CricketPlayerDataLoader(random_state=42)
cricket_df = loader.generate_cricket_dataset(num_players=200)
X_train, X_val, y_train, y_val, _ = loader.preprocess_data(cricket_df)
# Build and compile model (from Step 2)
model_builder = CricketPerformanceModel(input_dim=7, learning_rate=0.001)
model = model_builder.build_sequential_model()
model = model_builder.compile_model()
# Initialize trainer and configure callbacks
trainer = CricketModelTrainer(model, X_train, X_val, y_train, y_val)
callbacks = trainer.define_callbacks(checkpoint_path='models/best_model.h5')
# Execute training
history = trainer.train(epochs=100, batch_size=16, callbacks=callbacks)
# Evaluate performance
trainer.evaluate_on_validation()
# Visualize training progression
trainer.plot_training_history()
print("\n✓ Step 3 Complete: Model trained with integration and enhancement!")Step 4 — Testing & Verification
Execute comprehensive testing and verification to validate both model correctness and overall pipeline performance. Run the complete pipeline using bash scripts that load data, train the model, and evaluate predictions, then generate predictions on validation data and analyze error distributions to identify any systematic biases.
Create test cases that cover edge cases such as extreme feature values and missing data scenarios, and verify that all predictions fall within reasonable ranges for cricket performance metrics. Document expected outputs, including loss values, prediction accuracy metrics, and visual confirmation through training curves.
This verification step ensures the entire system functions correctly before deployment and provides confidence that the model generalizes beyond the training data.