Modern Python Engineering
Chapitre 9
09 - Machine Learning
> **Durée :** 3 semaines > **Objectif :** Maîtriser l'écosystème ML Python : scikit-learn, PyTorch, LangChain, MLflow, et déploiement.
Cours 09 : Machine Learning avec Python
1. scikit-learn : Pipelines et Préprocessing
1.1 Pipelines
Les pipelines enchaînent transformations et modèle final :
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
numeric_features = ["age", "salary", "experience"]
categorical_features = ["department", "role"]
numeric_transformer = Pipeline([
("scaler", StandardScaler()),
])
categorical_transformer = Pipeline([
("encoder", OneHotEncoder(handle_unknown="ignore")),
])
preprocessor = ColumnTransformer([
("num", numeric_transformer, numeric_features),
("cat", categorical_transformer, categorical_features),
])
pipeline = Pipeline([
("preprocessor", preprocessor),
("classifier", RandomForestClassifier(n_estimators=100)),
])
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
1.2 Préprocessing avancé
from sklearn.preprocessing import (
StandardScaler, MinMaxScaler, RobustScaler,
OneHotEncoder, LabelEncoder, OrdinalEncoder,
KBinsDiscretizer, PolynomialFeatures, FunctionTransformer
)
from sklearn.impute import SimpleImputer, KNNImputer
from sklearn.feature_selection import SelectKBest, mutual_info_classif
from sklearn.decomposition import PCA
1.3 Modèles disponibles
from sklearn.linear_model import LogisticRegression, LinearRegression, Ridge, Lasso
from sklearn.svm import SVC, SVR
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
from sklearn.ensemble import (
RandomForestClassifier, GradientBoostingClassifier,
AdaBoostClassifier, ExtraTreesClassifier, VotingClassifier, StackingClassifier
)
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
2. PyTorch : Deep Learning
2.1 Tensors
import torch
x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float32)
x = torch.zeros(3, 4)
x = torch.ones(2, 3)
x = torch.randn(5, 10)
x = torch.arange(0, 10)
y = x @ x.T
z = x * 2
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
x = x.to(device)
2.2 Autograd
x = torch.randn(3, requires_grad=True)
y = x ** 2 + 2 * x + 1
z = y.sum()
z.backward()
print(x.grad)
2.3 nn.Module
import torch.nn as nn
import torch.nn.functional as F
class MLP(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super().__init__()
self.fc1 = nn.Linear(input_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, output_dim)
self.dropout = nn.Dropout(0.3)
self.bn1 = nn.BatchNorm1d(hidden_dim)
def forward(self, x):
x = F.relu(self.bn1(self.fc1(x)))
x = self.dropout(x)
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
model = MLP(784, 256, 10).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
2.4 Data Loading
from torch.utils.data import Dataset, DataLoader
class CustomDataset(Dataset):
def __init__(self, data, labels):
self.data = torch.tensor(data, dtype=torch.float32)
self.labels = torch.tensor(labels, dtype=torch.long)
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx], self.labels[idx]
dataset = CustomDataset(X_train, y_train)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
2.5 Training Loop
from tqdm import tqdm
def train_epoch(model, dataloader, criterion, optimizer, device):
model.train()
total_loss = 0
correct = 0
total = 0
for inputs, targets in tqdm(dataloader):
inputs, targets = inputs.to(device), targets.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
total_loss += loss.item()
_, predicted = outputs.max(1)
total += targets.size(0)
correct += predicted.eq(targets).sum().item()
return total_loss / len(dataloader), 100. * correct / total
for epoch in range(10):
train_loss, train_acc = train_epoch(model, train_loader, criterion, optimizer, device)
print(f"Epoch {epoch}: Loss={train_loss:.4f}, Acc={train_acc:.2f}%")
2.6 Sauvegarde
torch.save(model.state_dict(), "model.pth")
model.load_state_dict(torch.load("model.pth"))
3. LangChain
3.1 Installation
pip install langchain langchain-community langchain-openai
3.2 Chains
from langchain_openai import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = PromptTemplate.from_template("Explique {topic} simplement.")
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.invoke({"topic": "gradient descent"})
3.3 RAG
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
docs = splitter.split_documents(documents)
vectorstore = Chroma.from_documents(docs, OpenAIEmbeddings())
qa = RetrievalQA.from_chain_type(llm=llm, retriever=vectorstore.as_retriever())
response = qa.invoke({"query": "Quelle est la capitale de la France?"})
3.4 Agents
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain.tools import tool
@tool
def calculate(expr: str) -> str:
return str(eval(expr))
agent = create_tool_calling_agent(llm, [calculate], prompt)
executor = AgentExecutor(agent=agent, tools=[calculate], verbose=True)
4. OpenAI SDK
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello"}],
temperature=0.7,
)
print(response.choices[0].message.content)
5. MLflow
import mlflow
mlflow.set_experiment("mon-projet")
with mlflow.start_run():
mlflow.log_param("lr", 0.001)
mlflow.log_metric("accuracy", 0.95)
mlflow.sklearn.log_model(model, "model")
6. Feature Engineering
import polars as pl
df = df.with_columns([
pl.col("date").dt.year().alias("year"),
pl.col("amount").rolling_mean(7).alias("ma7"),
pl.col("amount").shift(1).alias("lag1"),
])
7. Déploiement FastAPI
from fastapi import FastAPI
from pydantic import BaseModel
import mlflow
app = FastAPI()
model = mlflow.pyfunc.load_model("models:/mon-modele/latest")
class Input(BaseModel):
age: float
salary: float
@app.post("/predict")
async def predict(data: Input):
return {"prediction": model.predict([[data.age, data.salary]])[0]}
@app.get("/health")
async def health():
return {"status": "ok"}
8. Évaluation
from sklearn.metrics import classification_report, confusion_matrix
print(classification_report(y_true, y_pred))
cm = confusion_matrix(y_true, y_pred)
9. Diagrammes
Diagramme en cours de génération...
10. Bonnes pratiques
- Fixer les seeds pour la reproductibilité
- Logger toutes les expériences dans MLflow
- Versionner les modèles dans le Model Registry
- Tester le preprocessing et l'API
- Monitorer le data drift en production