tp05
This commit is contained in:
6
TP05/.ipynb_checkpoints/Untitled-checkpoint.ipynb
Normal file
6
TP05/.ipynb_checkpoints/Untitled-checkpoint.ipynb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"cells": [],
|
||||||
|
"metadata": {},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
1325
TP05/Untitled.ipynb
Normal file
1325
TP05/Untitled.ipynb
Normal file
File diff suppressed because one or more lines are too long
390
TP05/tp05.py
Normal file
390
TP05/tp05.py
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
"""
|
||||||
|
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