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