update
This commit is contained in:
390
TP05/tp05.py
390
TP05/tp05.py
@@ -1,390 +0,0 @@
|
|||||||
"""
|
|
||||||
Projet de Machine Learning : Prédiction de Maladies Cardiaques (Version corrigée)
|
|
||||||
Dataset : UCI Heart Disease Dataset
|
|
||||||
Objectif : Comparer deux architectures de réseaux de neurones pour la prédiction de maladies cardiaques
|
|
||||||
"""
|
|
||||||
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
import urllib.request
|
|
||||||
import ssl
|
|
||||||
from sklearn.model_selection import train_test_split
|
|
||||||
from sklearn.preprocessing import StandardScaler
|
|
||||||
from tensorflow.keras.models import Sequential
|
|
||||||
from tensorflow.keras.layers import Dense, Dropout, BatchNormalization
|
|
||||||
from tensorflow.keras.optimizers import Adam
|
|
||||||
from tensorflow.keras.callbacks import EarlyStopping
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
# 1. Chargement des données avec gestion du SSL
|
|
||||||
def load_data():
|
|
||||||
try:
|
|
||||||
# Créer un contexte SSL non-vérifié (à utiliser avec précaution)
|
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context
|
|
||||||
|
|
||||||
# URL du dataset
|
|
||||||
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data"
|
|
||||||
|
|
||||||
# Définir les noms des colonnes
|
|
||||||
columns = ['age', 'sex', 'cp', 'trestbps', 'chol', 'fbs', 'restecg', 'thalach',
|
|
||||||
'exang', 'oldpeak', 'slope', 'ca', 'thal', 'target']
|
|
||||||
|
|
||||||
print("Téléchargement des données...")
|
|
||||||
|
|
||||||
# Télécharger directement dans un DataFrame
|
|
||||||
data = pd.read_csv(url, names=columns)
|
|
||||||
|
|
||||||
# En cas d'erreur, utiliser un dataset de démonstration
|
|
||||||
if data.empty:
|
|
||||||
raise Exception("Le dataset est vide")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Erreur lors du téléchargement des données: {e}")
|
|
||||||
print("Utilisation d'un dataset de démonstration...")
|
|
||||||
|
|
||||||
# Créer un petit dataset de démonstration
|
|
||||||
np.random.seed(42)
|
|
||||||
n_samples = 300
|
|
||||||
|
|
||||||
data = pd.DataFrame({
|
|
||||||
'age': np.random.normal(55, 10, n_samples),
|
|
||||||
'sex': np.random.binomial(1, 0.5, n_samples),
|
|
||||||
'cp': np.random.randint(0, 4, n_samples),
|
|
||||||
'trestbps': np.random.normal(130, 20, n_samples),
|
|
||||||
'chol': np.random.normal(240, 40, n_samples),
|
|
||||||
'fbs': np.random.binomial(1, 0.2, n_samples),
|
|
||||||
'restecg': np.random.randint(0, 3, n_samples),
|
|
||||||
'thalach': np.random.normal(150, 20, n_samples),
|
|
||||||
'exang': np.random.binomial(1, 0.3, n_samples),
|
|
||||||
'oldpeak': np.random.normal(1, 1, n_samples),
|
|
||||||
'slope': np.random.randint(0, 3, n_samples),
|
|
||||||
'ca': np.random.randint(0, 4, n_samples),
|
|
||||||
'thal': np.random.randint(0, 3, n_samples),
|
|
||||||
'target': np.random.binomial(1, 0.4, n_samples)
|
|
||||||
})
|
|
||||||
|
|
||||||
# Nettoyer les données
|
|
||||||
data = data.replace('?', np.nan)
|
|
||||||
data = data.dropna()
|
|
||||||
|
|
||||||
# Convertir les colonnes en nombres
|
|
||||||
for column in data.columns:
|
|
||||||
data[column] = pd.to_numeric(data[column])
|
|
||||||
|
|
||||||
# Binariser la target (0 pour pas de maladie, 1 pour maladie)
|
|
||||||
data['target'] = (data['target'] > 0).astype(int)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
# 2. Prétraitement des données
|
|
||||||
def preprocess_data(data):
|
|
||||||
# Séparer features et target
|
|
||||||
X = data.drop('target', axis=1)
|
|
||||||
y = data['target']
|
|
||||||
|
|
||||||
# Split train/test
|
|
||||||
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
|
|
||||||
|
|
||||||
# Standardisation
|
|
||||||
scaler = StandardScaler()
|
|
||||||
X_train_scaled = scaler.fit_transform(X_train)
|
|
||||||
X_test_scaled = scaler.transform(X_test)
|
|
||||||
|
|
||||||
return X_train_scaled, X_test_scaled, y_train, y_test
|
|
||||||
|
|
||||||
# 3. Premier modèle : Réseau dense classique
|
|
||||||
def create_model_1(input_shape):
|
|
||||||
model = Sequential([
|
|
||||||
Dense(64, activation='relu', input_shape=input_shape),
|
|
||||||
BatchNormalization(),
|
|
||||||
Dense(32, activation='relu'),
|
|
||||||
Dropout(0.3),
|
|
||||||
Dense(16, activation='relu'),
|
|
||||||
Dense(1, activation='sigmoid')
|
|
||||||
])
|
|
||||||
|
|
||||||
model.compile(optimizer=Adam(learning_rate=0.001),
|
|
||||||
loss='binary_crossentropy',
|
|
||||||
metrics=['accuracy'])
|
|
||||||
|
|
||||||
return model
|
|
||||||
|
|
||||||
# 4. Second modèle : Réseau plus profond avec régularisation plus forte
|
|
||||||
def create_model_2(input_shape):
|
|
||||||
model = Sequential([
|
|
||||||
Dense(128, activation='relu', input_shape=input_shape),
|
|
||||||
BatchNormalization(),
|
|
||||||
Dropout(0.3),
|
|
||||||
Dense(64, activation='relu'),
|
|
||||||
BatchNormalization(),
|
|
||||||
Dropout(0.3),
|
|
||||||
Dense(32, activation='relu'),
|
|
||||||
BatchNormalization(),
|
|
||||||
Dense(16, activation='relu'),
|
|
||||||
Dense(1, activation='sigmoid')
|
|
||||||
])
|
|
||||||
|
|
||||||
model.compile(optimizer=Adam(learning_rate=0.001),
|
|
||||||
loss='binary_crossentropy',
|
|
||||||
metrics=['accuracy'])
|
|
||||||
|
|
||||||
return model
|
|
||||||
|
|
||||||
# 5. Fonction d'entraînement et d'évaluation
|
|
||||||
def train_and_evaluate(model, X_train, X_test, y_train, y_test, model_name):
|
|
||||||
# Early stopping pour éviter le surapprentissage
|
|
||||||
early_stopping = EarlyStopping(
|
|
||||||
monitor='val_loss',
|
|
||||||
patience=10,
|
|
||||||
restore_best_weights=True,
|
|
||||||
verbose=1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Entraînement
|
|
||||||
history = model.fit(
|
|
||||||
X_train, y_train,
|
|
||||||
validation_split=0.2,
|
|
||||||
epochs=50, # Réduit pour la démonstration
|
|
||||||
batch_size=32,
|
|
||||||
callbacks=[early_stopping],
|
|
||||||
verbose=1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Évaluation
|
|
||||||
test_loss, test_accuracy = model.evaluate(X_test, y_test, verbose=0)
|
|
||||||
print(f"\n{model_name} - Test Accuracy: {test_accuracy:.4f}")
|
|
||||||
|
|
||||||
return history
|
|
||||||
|
|
||||||
# 6. Visualisation des résultats
|
|
||||||
def plot_training_history(history1, history2):
|
|
||||||
plt.figure(figsize=(12, 4))
|
|
||||||
|
|
||||||
# Plot accuracy
|
|
||||||
plt.subplot(1, 2, 1)
|
|
||||||
plt.plot(history1.history['accuracy'], label='Model 1 accuracy')
|
|
||||||
plt.plot(history1.history['val_accuracy'], label='Model 1 val accuracy')
|
|
||||||
plt.plot(history2.history['accuracy'], label='Model 2 accuracy')
|
|
||||||
plt.plot(history2.history['val_accuracy'], label='Model 2 val accuracy')
|
|
||||||
plt.title('Model Accuracy')
|
|
||||||
plt.xlabel('Epoch')
|
|
||||||
plt.ylabel('Accuracy')
|
|
||||||
plt.legend()
|
|
||||||
|
|
||||||
# Plot loss
|
|
||||||
plt.subplot(1, 2, 2)
|
|
||||||
plt.plot(history1.history['loss'], label='Model 1 loss')
|
|
||||||
plt.plot(history1.history['val_loss'], label='Model 1 val loss')
|
|
||||||
plt.plot(history2.history['loss'], label='Model 2 loss')
|
|
||||||
plt.plot(history2.history['val_loss'], label='Model 2 val loss')
|
|
||||||
plt.title('Model Loss')
|
|
||||||
plt.xlabel('Epoch')
|
|
||||||
plt.ylabel('Loss')
|
|
||||||
plt.legend()
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
# 7. Programme principal
|
|
||||||
def main():
|
|
||||||
print("Loading data...")
|
|
||||||
data = load_data()
|
|
||||||
print("Data shape:", data.shape)
|
|
||||||
|
|
||||||
print("\nPreprocessing data...")
|
|
||||||
X_train, X_test, y_train, y_test = preprocess_data(data)
|
|
||||||
input_shape = (X_train.shape[1],)
|
|
||||||
|
|
||||||
print("\nTraining Model 1...")
|
|
||||||
model1 = create_model_1(input_shape)
|
|
||||||
history1 = train_and_evaluate(model1, X_train, X_test, y_train, y_test, "Model 1")
|
|
||||||
|
|
||||||
print("\nTraining Model 2...")
|
|
||||||
model2 = create_model_2(input_shape)
|
|
||||||
history2 = train_and_evaluate(model2, X_train, X_test, y_train, y_test, "Model 2")
|
|
||||||
|
|
||||||
print("\nPlotting results...")
|
|
||||||
plot_training_history(history1, history2)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
Modèle 1 : Réseau Dense Classique
|
|
||||||
- C'est une architecture relativement simple et légère avec 4 couches :
|
|
||||||
1. Première couche : 64 neurones avec activation ReLU
|
|
||||||
- Cette couche initiale capture les patterns de base dans les données
|
|
||||||
- Suivie d'une normalisation par lots (BatchNormalization) pour stabiliser l'apprentissage
|
|
||||||
2. Deuxième couche : 32 neurones avec activation ReLU
|
|
||||||
- Suivie d'un Dropout de 30% pour éviter le surapprentissage
|
|
||||||
3. Troisième couche : 16 neurones avec activation ReLU
|
|
||||||
- Réduit progressivement la dimensionnalité
|
|
||||||
4. Couche de sortie : 1 neurone avec activation sigmoid
|
|
||||||
- Pour la prédiction binaire (malade/non malade)
|
|
||||||
|
|
||||||
Modèle 2 : Réseau Plus Profond
|
|
||||||
- C'est une architecture plus complexe avec 5 couches et plus de régularisation :
|
|
||||||
1. Première couche : 128 neurones avec activation ReLU
|
|
||||||
- Commence avec plus de neurones pour capturer des patterns plus complexes
|
|
||||||
- Suivie de BatchNormalization et Dropout 30%
|
|
||||||
2. Deuxième couche : 64 neurones avec activation ReLU
|
|
||||||
- Également suivie de BatchNormalization et Dropout
|
|
||||||
3. Troisième couche : 32 neurones avec activation ReLU
|
|
||||||
- Avec BatchNormalization
|
|
||||||
4. Quatrième couche : 16 neurones avec activation ReLU
|
|
||||||
5. Couche de sortie : 1 neurone avec activation sigmoid
|
|
||||||
|
|
||||||
Les principales différences sont :
|
|
||||||
1. Complexité : Le modèle 2 a plus de paramètres et de couches
|
|
||||||
2. Régularisation : Le modèle 2 utilise plus de BatchNormalization et de Dropout
|
|
||||||
3. Capacité d'apprentissage : Le modèle 2 peut capturer des relations plus complexes dans les données
|
|
||||||
|
|
||||||
L'idée est de comparer :
|
|
||||||
- Une approche simple qui pourrait suffire pour ce problème médical relativement simple
|
|
||||||
- Une approche plus complexe qui pourrait potentiellement capturer des patterns plus subtils
|
|
||||||
|
|
||||||
Les deux modèles utilisent le même optimiseur (Adam) avec le même learning rate (0.001) pour une comparaison équitable.
|
|
||||||
|
|
||||||
Cette configuration permet d'observer si la complexité supplémentaire du deuxième modèle apporte réellement un avantage en termes de performances, ou si le modèle plus simple est suffisant.
|
|
||||||
|
|
||||||
- ReLU (Rectified Linear Unit) est une fonction d'activation très populaire en deep learning : ReLu (x) = max (0,x)
|
|
||||||
|
|
||||||
- Le Dropout est une technique de régularisation cruciale en deep learning. Voici une explication détaillée :
|
|
||||||
Principe de base :
|
|
||||||
Pendant l'entraînement, à chaque itération
|
|
||||||
Désactive aléatoirement un certain pourcentage de neurones
|
|
||||||
Ces neurones sont temporairement "éteints" avec toutes leurs connexions
|
|
||||||
Le pourcentage est défini par le paramètre de dropout (ex: 0.3 = 30% des neurones)
|
|
||||||
|
|
||||||
- La BatchNormalization (ou normalisation par lots) est une technique très importante en deep learning. Voici une explication détaillée :
|
|
||||||
Principe fondamental :
|
|
||||||
Normalise les activations d'une couche pour chaque batch
|
|
||||||
Maintient la moyenne proche de 0 et l'écart-type proche de 1
|
|
||||||
S'applique avant la fonction d'activation
|
|
||||||
'''
|
|
||||||
|
|
||||||
'''
|
|
||||||
## Exercice 1 :
|
|
||||||
adapter le programme sur les données suivantes :
|
|
||||||
https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data
|
|
||||||
|
|
||||||
|
|
||||||
## Exercice 2 :
|
|
||||||
On vous demande d'implémenter 2 autres modèles en suivant le schéma du programme donné. Sur les 2 data-set.
|
|
||||||
|
|
||||||
L'objectif est de rendre un rapport explicatif complet sur au moins un des modèles ; le code doit être commenté et des tests (changement de paramètres : itération, taux, couches réseaux) doivent être fait.
|
|
||||||
|
|
||||||
### Premier Modèle : Random Forest Classifier
|
|
||||||
|
|
||||||
Ce modèle est particulièrement intéressant car il offre :
|
|
||||||
- Une excellente performance sur les données médicales
|
|
||||||
- Une interprétabilité des résultats
|
|
||||||
- Une facilité relative d'implémentation
|
|
||||||
|
|
||||||
Voici un exemple de structure pour l'implémentation :
|
|
||||||
|
|
||||||
```python
|
|
||||||
from sklearn.ensemble import RandomForestClassifier
|
|
||||||
from sklearn.model_selection import GridSearchCV
|
|
||||||
|
|
||||||
def create_model_rf(X_train, y_train):
|
|
||||||
# Création du modèle avec des hyperparamètres de base
|
|
||||||
rf_model = RandomForestClassifier(
|
|
||||||
n_estimators=100,
|
|
||||||
max_depth=10,
|
|
||||||
random_state=42
|
|
||||||
)
|
|
||||||
|
|
||||||
# Définition des paramètres à optimiser
|
|
||||||
param_grid = {
|
|
||||||
'n_estimators': [50, 100, 200],
|
|
||||||
'max_depth': [5, 10, 15],
|
|
||||||
'min_samples_split': [2, 5, 10]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Recherche des meilleurs paramètres
|
|
||||||
grid_search = GridSearchCV(
|
|
||||||
rf_model,
|
|
||||||
param_grid,
|
|
||||||
cv=5,
|
|
||||||
scoring='accuracy',
|
|
||||||
n_jobs=-1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Entraînement avec recherche des meilleurs paramètres
|
|
||||||
grid_search.fit(X_train, y_train)
|
|
||||||
|
|
||||||
return grid_search.best_estimator_
|
|
||||||
```
|
|
||||||
|
|
||||||
### Deuxième Modèle : XGBoost
|
|
||||||
|
|
||||||
XGBoost est un algorithme de boosting très performant qui permet souvent d'obtenir d'excellents résultats. Voici une structure d'implémentation :
|
|
||||||
|
|
||||||
```python
|
|
||||||
import xgboost as xgb
|
|
||||||
from sklearn.model_selection import cross_val_score
|
|
||||||
|
|
||||||
def create_model_xgb(X_train, y_train):
|
|
||||||
# Création du modèle avec des paramètres de base
|
|
||||||
xgb_model = xgb.XGBClassifier(
|
|
||||||
learning_rate=0.1,
|
|
||||||
n_estimators=100,
|
|
||||||
max_depth=5,
|
|
||||||
random_state=42
|
|
||||||
)
|
|
||||||
|
|
||||||
# Paramètres à optimiser
|
|
||||||
param_grid = {
|
|
||||||
'learning_rate': [0.01, 0.1, 0.3],
|
|
||||||
'n_estimators': [50, 100, 200],
|
|
||||||
'max_depth': [3, 5, 7]
|
|
||||||
}
|
|
||||||
|
|
||||||
# Optimisation des hyperparamètres
|
|
||||||
grid_search = GridSearchCV(
|
|
||||||
xgb_model,
|
|
||||||
param_grid,
|
|
||||||
cv=5,
|
|
||||||
scoring='accuracy',
|
|
||||||
n_jobs=-1
|
|
||||||
)
|
|
||||||
|
|
||||||
grid_search.fit(X_train, y_train)
|
|
||||||
|
|
||||||
return grid_search.best_estimator_
|
|
||||||
```
|
|
||||||
|
|
||||||
Pour faciliter l'implémentation, voici les points essentiels à comprendre :
|
|
||||||
|
|
||||||
Pour le Random Forest :
|
|
||||||
- C'est un ensemble d'arbres de décision
|
|
||||||
- Chaque arbre est entraîné sur un sous-ensemble aléatoire des données
|
|
||||||
- La prédiction finale est obtenue par vote majoritaire des arbres
|
|
||||||
- Les paramètres clés sont le nombre d'arbres (n_estimators) et la profondeur maximale (max_depth)
|
|
||||||
|
|
||||||
Pour XGBoost :
|
|
||||||
- C'est un algorithme de boosting qui construit les arbres séquentiellement
|
|
||||||
- Chaque nouvel arbre corrige les erreurs des arbres précédents
|
|
||||||
- Le learning_rate contrôle la contribution de chaque arbre
|
|
||||||
- La profondeur des arbres (max_depth) limite la complexité du modèle
|
|
||||||
|
|
||||||
Pour l'évaluation des modèles, on peut réutiliser les fonctions de visualisation existantes en les adaptant légèrement. Par exemple :
|
|
||||||
|
|
||||||
```python
|
|
||||||
def plot_model_comparison(models_results):
|
|
||||||
plt.figure(figsize=(10, 6))
|
|
||||||
|
|
||||||
for model_name, scores in models_results.items():
|
|
||||||
plt.plot(scores['val_accuracy'], label=f'{model_name} validation accuracy')
|
|
||||||
|
|
||||||
plt.title('Model Comparison')
|
|
||||||
plt.xlabel('Iteration')
|
|
||||||
plt.ylabel('Accuracy')
|
|
||||||
plt.legend()
|
|
||||||
plt.show()
|
|
||||||
```
|
|
||||||
|
|
||||||
'''
|
|
Reference in New Issue
Block a user