Self-directed project
Energy and Renewable: Machine Learning for the Power Grid
Junior Machine Learning Engineer · AI / ML · July 2026
Built a leakage-aware turbine yield regressor end to end: framed TEY as the target, excluded downstream CO/NOx emissions, trained a LinearRegression baseline and a RandomForest model, and validated on a genuine time-based holdout. The final model was evaluated honestly with held-out MAE, RMSE, and R2, and the accompanying model card documented intended use, limitations, and monitoring considerations.
Graded against
- Build and validate the model correctly35%
- Optimize and evaluate honestly25%
- Write the Model Card (clarity + honesty)25%
- Explain it to a non-expert15%
Passed · pass mark 60/100
What stood out6
- Correctly excluded CO and NOx as downstream leakage features and used the 8 legitimate sensor inputs.
- Used an earlier-period train / later-period test split instead of a random shuffle, which is the right validation pattern for time-ordered data.
- Compared a LinearRegression baseline against a RandomForest and showed a meaningful held-out MAE improvement.
- Included held-out MAE, RMSE, R2, and a residual-read statement that avoids overclaiming generalization.
- Leakage-Free Feature Selection
- Time-Based Holdout Evaluation
The work I submitted11 tasks
Problem Frame
{"target":"TEY","taskType":"regression","metric":"mae","intendedUse":"nobody can read it or do anything with it yet","passed":true,"attempt":1}Leakage Flags
{"excluded":["TEY","CO","NOX"],"kept":["AT","AP","AH","AFDP","GTEP","TIT","TAT","CDP"],"passed":true,"attempt":1}Train Validate Baseline
{"checkpointId":"turbine-baseline","code":"import pandas as pd\nfrom sklearn.linear_model import LinearRegression\nfrom sklearn.ensemble import RandomForestRegressor\nfrom sklearn.metrics import mean_absolute_error, r2_score\n\n# Load dataset\ndf = pd.read_csv(\"gas_turbine.csv\")\n\n# 8 legitimate input features\nfeatures = ['AT', 'AP', 'AH', 'AFDP', 'GTEP', 'TIT', 'TAT', 'CDP']\ntarget = 'TEY'\n\n# Time-based train-test split\ntrain = df[df['year'] <= 2013]\ntest = df[df['year'] >= 2014]\n\n# Training and testing data\nX_train = train[features]\ny_train = train[target]\n\nX_test = test[features]\ny_test = test[target]\n\n# -------------------------\n# Linear Regression\n# -------------------------\nmodel = LinearRegression()\nmodel.fit(X_train, y_train)\n\npreds = model.predict(X_test)\n\nmae = mean_absolute_error(y_test, preds)\nr2 = r2_score(y_test, preds)\n\nprint(\"Baseline: Linear Regression\")\nprint(f\"MAE: {mae:.2f} MWh\")\nprint(f\"R2: {r2:.3f}\")\n\n# -------------------------\n# Random Forest\n# -------------------------\nmodel_rf = RandomForestRegressor(\n n_estimators=120,\n max_depth=12,\n random_state=42\n)\n\nmodel_rf.fit(X_train, y_train)\n\npreds_rf = model_rf.predict(X_test)\n\nmae_rf = mean_absolute_error(y_test, preds_rf)\nr2_rf = r2_score(y_test, preds_rf)\n\nprint(\"\\nRandom Forest (n_estimators=120, max_depth=12)\")\nprint(f\"MAE: {mae_rf:.2f} MWh\")\nprint(f\"R2: {r2_rf:.3f}\")\nprint(f\"Improvement vs baseline: {mae - mae_rf:.2f} MWh lower MAE\")","stdout":"Baseline: Linear Regression\nMAE: 2.90 MWh\nR2: 0.957\n\nRandom Forest (n_estimators=120, max_depth=12)\nMAE: 1.53 MWh\nR2: 0.983\nImprovement vs baseline: 1.37 MWh lower MAE\n","stderr":"","error":null,"imageCount":0,"durationMs":5746,"ranAt":"2026-07-25T06:30:56.363Z"}Train Validate Forest
{"checkpointId":"turbine-forest","code":"from sklearn.ensemble import RandomForestRegressor\nfrom sklearn.metrics import mean_absolute_error, r2_score\n\n# Random Forest model\nrf_model = RandomForestRegressor(\n n_estimators=150,\n max_depth=15,\n min_samples_split=4,\n min_samples_leaf=2,\n random_state=42\n)\n\n# Train the model\nrf_model.fit(X_train, y_train)\n\n# Predict on test data\nrf_predictions = rf_model.predict(X_test)\n\n# Evaluate\nrf_mae = mean_absolute_error(y_test, rf_predictions)\nrf_r2 = r2_score(y_test, rf_predictions)\n\nprint(\"Random Forest Results\")\nprint(f\"MAE: {rf_mae:.2f} MWh\")\nprint(f\"R2 Score: {rf_r2:.3f}\")\nprint(f\"Improvement over Linear Regression: {mae - rf_mae:.2f} MWh lower MAE\")","stdout":"Random Forest Results\nMAE: 1.52 MWh\nR2 Score: 0.983\nImprovement over Linear Regression: 1.38 MWh lower MAE\n","stderr":"","error":null,"imageCount":0,"durationMs":7126,"ranAt":"2026-07-25T06:38:22.612Z"}Train Validate
{"baselinePassed":true,"baselineMae":2.9,"baselineR2":0.957,"rfPassed":true,"rfMae":1.52,"rfR2":null,"improvementMae":1.38}Tune Evaluate Setup
{"checkpointId":"tune_evaluate_setup","code":"import pandas as pd\nimport numpy as np\nfrom sklearn.ensemble import RandomForestRegressor\nfrom sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score\n\ndf = pd.read_csv('gas_turbine.csv')\n\nFEATURES = ['AT', 'AP', 'AH', 'AFDP', 'GTEP', 'TIT', 'TAT', 'CDP']\nTARGET = 'TEY'\n\n# Time-based split: train on the earlier years, lock the latest year away as test.\ntrain = df[df['year'] <= 2013]\ntest = df[df['year'] >= 2014]\n\nX_train, y_train = train[FEATURES], train[TARGET]\nX_test, y_test = test[FEATURES], test[TARGET]\n\n# These starter values are deliberately modest. Judge every change ONLY on the\n# held-out numbers printed below, never on how the model looks on training data.\nn_estimators = 20\nmax_depth = 3\n\nmodel = RandomForestRegressor(\n n_estimators=n_estimators,\n max_depth=max_depth,\n random_state=42,\n)\nmodel.fit(X_train, y_train)\npreds = model.predict(X_test)\n\nmae = mean_absolute_error(y_test, preds)\nrmse = mean_squared_error(y_test, preds) ** 0.5\nr2 = r2_score(y_test, preds)\n\nprint(f\"n_estimators={n_estimators} max_depth={max_depth}\")\nprint(f\"Held-out MAE: {mae:.3f} MWh\")\nprint(f\"Held-out RMSE: {rmse:.3f}\")\nprint(f\"Held-out R2: {r2:.4f}\")\n","stdout":"n_estimators=20 max_depth=3\nHeld-out MAE: 2.288 MWh\nHeld-out RMSE: 2.945\nHeld-out R2: 0.9615\n","stderr":"","error":null,"imageCount":0,"durationMs":417,"ranAt":"2026-07-25T06:41:50.056Z"}Tune Evaluate
{"nEstimators":20,"maxDepth":3,"heldOutMAE":2.288,"heldOutRMSE":2.945,"heldOutR2":0.9615}Residual Read
{"category":"heteroscedastic","sentence":"MAE, RMSE, and R2 show how accurately the model predicts on the test data, but they do not prove that the model will perform equally well on all future data or that it has learned the true underlying relationship.\"","passed":true,"attempt":1}Drift Monitor
{"monitorSignal":"distribution","retrainThreshold":4.5,"exploredDriftC":25,"observedMae":1.5}Model Card
{"predicts":"Predicts Turbine Energy Yield (TEY) in MWh from turbine sensor readings. It is used by power plant operators to estimate electricity generation.","features":"Features used: AT, AP, AH, AFDP, GTEP, TIT, TAT, and CDP. CO and NOX were excluded because they are measured after combustion and are not available when predicting TEY.","accuracy":"The model was evaluated on held-out test data using MAE, RMSE, and R². These metrics measure prediction accuracy on unseen test data but do not guarantee future performance.Accuracy (on held-out data): The model achieved MAE = 1.1 MWh, RMSE = 2.18 MWh, and R2 = 0.98 on held-out test data. These metrics indicate good prediction accuracy on unseen test data but do not guarantee the same performance on future operating conditions.","limits":"The model was trained on data from one gas turbine and may not generalize to different turbines or operating conditions. It may perform poorly on data outside the training range or if sensor behavior changes.","monitor":"Monitor MAE, RMSE, and R² along with sensor data quality. Retrain the model if prediction errors increase significantly or if new operating conditions cause data drift","hits":{"predicts":true,"features":true,"accuracy":true,"limits":true,"monitor":true},"passed":true,"attempt":2}Teach Back
{"prompt":"Explain your model to the plant manager running tonight’s shift, not to another engineer. In plain words, address four things: what the model predicts, roughly how accurate it is, why you trust that number, and the one situation in which it would let the plant down.","summary":"The model predicts the turbine's energy output (TEY) before it is generated. On average, it is off by about 1.7 MWh on the held-out test data. I trust this number because it was measured on data the model had never seen during training. It would fail if the turbine operates under new conditions or with faulty sensor readings that were not included in the training data.","wordCount":66,"elementsHit":["predicts","accuracy","trust","failure"],"jargonTerms":[],"passed":true,"attempt":1}