Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

DiCE: Counterfactual Explanations for Wine Quality

M2 MIASHS - Université de Lyon

In this notebook, we use DiCE (Diverse Counterfactual Explanations) to suggest changes to wine characteristics that would improve its predicted quality.

Goal: Generate counterfactuals. “If this average wine had higher alcohol and lower acidity, it would be classified as High Quality.”

import pandas as pd
import numpy as np
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn import metrics
import dice_ml

1. Load and Prepare Data

  • Dataset: Wine Quality (Multiclass: 0, 1, 2).

  • Task: Convert to Binary. Class 0 is “High Quality”, Classes 1 & 2 are “Average”.

  • We aim to flip “Average” wines to “High Quality”.

data = load_wine()
df = pd.DataFrame(data.data, columns=data.feature_names)
df["target"] = data.target

# Convert to Binary: Target 0 (High Quality) vs Others (Average)
# New Target: 1 = High Quality, 0 = Average
# Original 0 -> 1
# Original 1,2 -> 0
df["quality"] = df["target"].apply(lambda x: 1 if x == 0 else 0)
df = df.drop(columns=["target"])

print("Class Distribution (1=High Quality, 0=Average):")
print(df["quality"].value_counts())
Class Distribution (1=High Quality, 0=Average):
quality
0    119
1     59
Name: count, dtype: int64
# Split Data
train_df, test_df = train_test_split(
    df, test_size=0.2, random_state=42, stratify=df["quality"]
)

X_train = train_df.drop("quality", axis=1)
y_train = train_df["quality"]
X_test = test_df.drop("quality", axis=1)
y_test = test_df["quality"]

2. Train Random Forest

We train a robust Random Forest model.

model = RandomForestClassifier(n_estimators=50, max_depth=5, random_state=42)
model.fit(X_train, y_train)

# Evaluate
y_pred = model.predict(X_test)
print(f"Accuracy: {metrics.accuracy_score(y_test, y_pred):.4f}")
Accuracy: 0.9444

3. Setup DiCE

DiCE requires a wrapper around the model and dataset.

# DiCE Data Object
d = dice_ml.Data(
    dataframe=train_df, continuous_features=data.feature_names, outcome_name="quality"
)

# DiCE Model Object
m = dice_ml.Model(model=model, backend="sklearn")

# Initialize DiCE
exp = dice_ml.Dice(d, m, method="random")

4. Generate Counterfactuals

We select a wine predicted as “Average” (0) and ask DiCE to find minimal changes to make it “High Quality” (1).

# Find an "Average" wine from the test set
average_wine = X_test[y_test == 0].iloc[0:1]
print("Original Wine Features:")
print(average_wine.T)
print(f"Original Prediction: {model.predict(average_wine)[0]}")

# Generate Counterfactuals
# desired_class=1 means we want to flip the prediction to "High Quality"
# First, try without restricting features - this gives DiCE more flexibility
try:
    dice_exp = exp.generate_counterfactuals(
        average_wine,
        total_CFs=3,
        desired_class=1,
    )
    print("\nCounterfactuals generated successfully!")
except Exception as e:
    print(f"Error with default parameters: {e}")
    print("\nTrying with relaxed constraints...")
    # If that fails, try with smaller number of counterfactuals
    dice_exp = exp.generate_counterfactuals(
        average_wine,
        total_CFs=1,
        desired_class=1,
    )

# Visualize
dice_exp.visualize_as_dataframe(show_only_changes=True)
Original Wine Features:
                                 158
alcohol                        14.34
malic_acid                      1.68
ash                             2.70
alcalinity_of_ash              25.00
magnesium                      98.00
total_phenols                   2.80
flavanoids                      1.31
nonflavanoid_phenols            0.53
proanthocyanins                 2.70
color_intensity                13.00
hue                             0.57
od280/od315_of_diluted_wines    1.96
proline                       660.00
Original Prediction: 0
100%|██████████| 1/1 [00:00<00:00,  9.27it/s]

Counterfactuals generated successfully!
Query instance (original outcome : 0)

Loading...

Diverse Counterfactual set (new outcome: 1)
Loading...

Analyse et interprétation

Vin Original (Classe 0 - Qualité Moyenne) :

  • Alcool : 14,34%, Acide Malique : 1,68, Flavanoides : 1,31, Proline : 660,0

Découverte Clé : DiCE a identifié les Flavanoides et la Proline comme les principaux facteurs pour changer la prédiction de « Qualité Moyenne » à « Haute Qualité ».

Trois Scénarios Contrefactuels :

  1. CF#1 - Augmenter les Flavanoides à 4,45 + Augmenter la Proline à 1241,4

    • Changement le plus dramatique : Flavanoides augmentés de 240% (1,31 → 4,45)

    • Proline augmentée de 88% (660 → 1241,4)

    • Cela représente une concentration significative de polyphénols et de composés aromatiques

  2. CF#2 - Alternative Subtile : Augmenter les Flavanoides à 2,81 + Augmenter od280/od315 à 2,99

    • Augmentation modérée des flavanoides (115%)

    • Augmentation du rapport du contenu phénolique (od280/od315)

    • Ajustement plus réaliste et pratique

  3. CF#3 - Flavanoides Élevés (4,43) + Proline Modérée (791,6)

    • Un autre renforcement fort des flavanoides (238%)

    • Augmentation de proline inférieure à CF#1 (augmentation de 20%)

    • Équilibre entre les antioxydants et les autres composés

Interprétation : Pour améliorer ce vin de « Qualité Moyenne » à « Haute Qualité », le modèle indique que l’augmentation des flavanoides est essentielle (présente dans les 3 contrefactuels). Cela a du sens car les flavanoides sont des antioxydants clés qui définissent les vins premium. Les stratégies secondaires impliquent d’augmenter la proline ou le rapport phénolique.

Résultat et Interprétation

Résultat : DiCE a généré 3 contrefactuels distincts modifiant la prédiction de “Qualité Moyenne” à “Haute Qualité”.

Changements identifiés :

  • Flavanoids : Augmentation de 115-240% (présent dans tous les contrefactuels)

  • Proline : Augmentation de 20-88% (présent dans 2 contrefactuels)

  • od280/od315 : Augmentation de 53% (présent dans 1 contrefactuel)

  • Aucun changement requis pour les 11 autres caractéristiques

Interprétation : Les flavanoids (polyphénols/antioxydants) constituent le facteur prédictif critique pour la classification en Haute Qualité. DiCE fournit une explication prescriptive : modifier 2-3 caractéristiques ciblées suffit pour améliorer la qualité prédite, ce qui contraste avec les méthodes d’interprétabilité post-hoc comme SHAP.