Skip to learning content

DATA SCIENCE PYTHON PLAYGROUND

Machine Learning · Learn / Refresh

← All challenges
ML-X15 · Neural networks · 45–60 minutes

A neural regressor with trustworthy units

mlp_reg · validation · reference · final-test discipline

Complete workflow and evidence

The question

Predict Wine quality using the fixed 600-row teaching sample, feature scaling and an internal target transformation. Inspect convergence and original-unit errors.

data/ml-learning/ML-X15.csv Complete workflow and evidence

Your inputs

Challenge input · ML-X15

600 observations. One red or white wine sample. Load the CSV file yourself.

Download challenge input CSV · Original source dataset · Source and original dictionary

Ready to load: comma-delimited UTF-8, header row, no saved index. No filtering or target construction is required. Exact seed-42 sample of 600 rows after source deduplication; do not sample or deduplicate it again.

ML-X15 · first 8 prepared rows
fixed acidityvolatile aciditycitric acidresidual sugarchloridesfree sulfur dioxidetotal sulfur dioxidedensitypHsulphatesalcoholquality
5.30.320.126.60.043221410.99373.360.610.46
6.40.30.274.40.055171350.99253.230.4412.26
6.50.220.322.20.02836920.990763.270.5911.97
80.240.486.80.047131340.996163.230.7105
8.90.290.351.90.06725570.9973.181.3610.36
6.60.210.392.30.041311020.992213.220.5810.97
8.80.240.542.50.08325570.99833.390.549.25
5.40.150.322.50.03710510.988783.040.5812.66

Column meanings and units

quality: ordered sensory score (0–10). alcohol: volume percent. pH: acidity scale. Other chemistry units follow the linked source dictionary.

The score is ordinal but modelled as regression here. ML removes exact duplicate rows before splitting. Chemistry must be available at prediction time; predictive associations do not establish effects of changing an ingredient.

Ready to load: comma-delimited UTF-8, header row, no saved index. No filtering or target construction is required. Exact seed-42 sample of 600 rows after source deduplication; do not sample or deduplicate it again.

Input schema
ColumnStored type
fixed acidityfloat64
volatile acidityfloat64
citric acidfloat64
residual sugarfloat64
chloridesfloat64
free sulfur dioxidefloat64
total sulfur dioxidefloat64
densityfloat64
pHfloat64
sulphatesfloat64
alcoholfloat64
qualityint64

How we validate this model

Reserve 20% of the declared population for the final test using split seed 42. Use a random split.

Use the same five training folds, shuffled with seed 42, for reference comparison, candidates and selection. Use out-of-fold training predictions for diagnosis. Select with negative RMSE (larger is better) and compare with a training-mean reference. Report final RMSE in original target units. Open final-test evidence after selection and diagnosis.

The file is available in the Python working folder. Load it yourself.

Your task
  1. Predict Wine quality using the fixed 600-row teaching sample, feature scaling and an internal target transformation.
  2. Inspect convergence and original-unit errors.
cv_resultsreference_resultschosen_nameloss_curvesresidualsfinal_rmse

Outputs you create

cv_results
  1. Dataframe indexed by the requested model IDs, with initial_score, selected_score and settings columns.
  2. Scores are observed mean training-fold scores; settings describes the chosen recipe.
reference_results
  1. cross_validate result for the dummy reference; test_score contains five scores.
chosen_name
  1. Model ID nominated from training evidence.
loss_curves
  1. Dictionary of available neural training-loss histories by model ID.
residuals
  1. Training-only actual, predicted and actual-minus-predicted residual evidence.
final_rmse
  1. Root mean squared error in original target units.

Deliverables

These conditions describe a complete workflow. Use the declared Python variables beside the editor; Check inspects evidence from your Run.

Python name key

Teal outlines name table columns. Violet left rules name Python objects. Dotted rules mark syntax, functions and parameters. Use each name exactly as shown.

Words in this task
DataFrame
a table with rows and columns
numeric
a number, rather than text
dummy
a simple reference model used for comparison
folds
training-data groups used to compare models on the same held-out rows
out-of-fold
predictions made for training rows by models that did not fit those rows
aligned
matched by row or category identity, rather than guessed position
residuals
observed values minus predicted values
RMSE
root mean squared error: an error measure that gives larger errors more weight
  1. Data boundary

    1. Load the supplied challenge CSV, define the declared X/y and keep aligned row identities through the split.
    2. Keep learned preparation inside each training fit.
  2. Validation & selection

    1. Compare the requested initial model families with a dummy reference on matching training folds.
    2. Evaluate the declared settings where applicable and choose from training evidence.
  3. Diagnosis

    1. Use out-of-fold training predictions to inspect residuals or class errors before opening the final test.
    2. Inspect neural convergence and loss history as well as prediction error.
  4. Final evidence

    1. Fit the nominated model on training rows and evaluate the reserved rows once.
    2. Report saved predictions, the requested metrics and labelled evidence
    3. Explain limitations without using the final test for further selection.

The playground workflow

Use the same data boundary as ML → Workflow: frame, split, explore, prepare, validate against a reference, diagnose, then make the final evaluation. Each step answers one question.

  1. Choose what to predictEstimate wine quality from 11 selected inputs; duplicate chemistry rows are removed before the split.
  2. Split data and save the test setWhich rows are available for learning, and which are being saved for the final check?
  3. Explore training dataDoes fixed acidity show a training pattern related to wine quality?
  4. Define the preparation recipeWhat preparation does this particular model need?
  5. Build the model pipelineCan one small hidden layer learn nonlinear numeric patterns?
  6. Validate the initial modelDo validation results stay reasonably similar across folds?
  7. Compare with a simple referenceDo the selected features beat a simple guess on the same training folds?
  8. Tune the modelDoes a different small hidden-layer size help?
  9. Inspect validation errorsDo training-only residuals form a roughly random cloud around zero?
  10. Final testHow does the saved final wine quality error compare with the initial-model CV and any selected-setting CV for Wine Quality · Multiple continuous features, and what pattern did the model miss?

Data policy Keep target-derived inputs out of X. Use training-only selection for prediction.

Help Hints & explained solution

Choose the amount of help you need. Opening one hint does not reveal the others.

Hint 1 — Think

Scaled optimisation must return meaningful output units.

Hint 2 — Tools

MLPRegressor, TransformedTargetRegressor and nested width parameters.

Revisit: Neural regression checkpoint

Hint 3 — Approach

Keep target transformation inside fitting, compare widths and report inverse-transformed predictions.

Explained solution

This complete script replaces the editor contents. It includes the input setup and the workflow.

import numpy as np
import pandas as pd
df=pd.read_csv('data/ml-learning/ML-X15.csv')
from sklearn.base import clone
from sklearn.model_selection import train_test_split, KFold, cross_validate, cross_val_predict, GridSearchCV
from sklearn.dummy import DummyRegressor
from sklearn.metrics import root_mean_squared_error

import matplotlib.pyplot as plt
import seaborn as sns
X=df[['fixed acidity', 'volatile acidity', 'citric acid', 'residual sugar', 'chlorides', 'free sulfur dioxide', 'total sulfur dioxide', 'density', 'pH', 'sulphates', 'alcohol']]
y=df['quality']
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=.2,random_state=42)
folds=KFold(n_splits=5,shuffle=True,random_state=42)
candidates={}
grids={}
# Prepare inputs and construct the candidate pipeline.
from sklearn.preprocessing import StandardScaler
preprocessor = StandardScaler()
from sklearn.pipeline import Pipeline
from sklearn.neural_network import MLPRegressor
from sklearn.compose import TransformedTargetRegressor
from sklearn.preprocessing import StandardScaler
network = MLPRegressor(hidden_layer_sizes=(24,), max_iter=800, early_stopping=True, tol=0.001, random_state=42)
model = TransformedTargetRegressor(regressor=network, transformer=StandardScaler())
pipeline = Pipeline([('prepare', preprocessor), ('model', model)])
model = pipeline
candidates['mlp_reg']=model
grids['mlp_reg']={'model__regressor__hidden_layer_sizes': [(16,), (24,)]}
# Validate initial candidates on matching training folds.
initial_results={name:cross_validate(candidate,X_train,y_train,cv=folds,scoring='neg_root_mean_squared_error') for name,candidate in candidates.items()}
# Compare with a simple reference before tuning.
reference_results=cross_validate(DummyRegressor(strategy='mean'),X_train,y_train,cv=folds,scoring='neg_root_mean_squared_error')
rows=[]
selected={}
for name,candidate in candidates.items():
    initial=initial_results[name]
    grid=grids[name]
    if grid:
        search=GridSearchCV(candidate,grid,cv=folds,scoring='neg_root_mean_squared_error',n_jobs=1)
        search.fit(X_train,y_train)
        selected[name]=search.best_estimator_
        validation_score=float(search.best_score_)
        settings=search.best_params_
    else:
        selected[name]=candidate
        validation_score=float(np.mean(initial['test_score']))
        settings='Keep defaults: no production search'
    rows.append({'model':name,'initial_score':float(np.mean(initial['test_score'])),'selected_score':validation_score,'settings':str(settings)})
cv_results=pd.DataFrame(rows).set_index('model')
# This solution nominates by mean training-fold score. Other evidence-based choices can be defensible.
chosen_name=cv_results.selected_score.idxmax()
chosen=selected[chosen_name]
oof_predictions=cross_val_predict(chosen,X_train,y_train,cv=folds)
diagnostic_y=y_train
residuals=pd.DataFrame({'actual':diagnostic_y,'predicted':oof_predictions,'residual':diagnostic_y-oof_predictions})
fig,ax=plt.subplots(figsize=(6,4))
sns.scatterplot(x=residuals.predicted,y=residuals.residual,s=12,ax=ax)
ax.axhline(0,color='black')
ax.set(xlabel='Training-validation prediction',ylabel='Residual',title='Training-only residuals')
fig.tight_layout()
fig.savefig('diagnostic.png',dpi=150,bbox_inches='tight')
final_model=clone(chosen).fit(X_train,y_train)
final_predictions=final_model.predict(X_test)
final_rmse=root_mean_squared_error(y_test,final_predictions)
print('Final RMSE:',final_rmse)
loss_curves={}
for name,candidate in selected.items():
    fitted=candidate.named_steps['model']
    if hasattr(fitted,'regressor_'):
        fitted=fitted.regressor_
    if hasattr(fitted,'loss_curve_'):
        loss_curves[name]=list(fitted.loss_curve_)
View input setup in this script

These opening lines are included in the complete script above. They load the challenge input.

import numpy as np
import pandas as pd
df=pd.read_csv('data/ml-learning/ML-X15.csv')

Why these steps exist

  • The input is the exact fixed deduplicated 600-row sample. Feature scaling and target transformation are fitted within each neural training fit; width comparison and loss/residual evidence precede final evaluation. Predicting through the wrapper restores quality-score units for RMSE.

Another valid approach

Equivalent ordinary Python is welcome. A defensible candidate or grouping need not match the solution’s choice; objective evidence must remain consistent.

Ctrl/⌘+Enter: Run · Tab: indent · Esc then Tab: leave editor

Python loads when you run. Code and results stay in this activity only.

Run your code to inspect its output. Check uses that same run.