I denna modul utforskar vi tillämpningen av kontinuerlig integrering och driftsättning (CI/CD) specifikt för maskininlärningssystem. Vi kommer att lära oss hur man anpassar traditionella CI/CD-praktiker för att hantera de unika utmaningarna med ML, inklusive dataversionshantering, modellvalidering och A/B-testning. Modulen fokuserar på att bygga automatiserade arbetsflöden som säkerställer att ML-modeller kan utvecklas, testas och driftsättas på ett säkert och effektivt sätt.
Efter denna modul kommer du att:
Aspekt | Traditionell CI/CD | ML CI/CD |
Artefakter | Kodbaserade binärer/containers | Modeller + kod + konfiguration |
Validering | Enhetstester, integrationstester | Datakvalitetstester, modellvalideringstester |
Miljövariabler | Konfiguration, endpoints | Feature stores, modellparametrar |
Byggsteg | Kompilering, paketisering | Träning, validering, paketisering |
Tidsåtgång | Minuter | Timmar till dagar (för träning) |
Driftsättning | Vanligtvis atomär | Kan kräva gradvis utrullning |
Rollback | Relativt enkel (tidigare version) | Komplex (återställ data + modell) |
ML-system introducerar ett nytt koncept: Continuous Training (CT), som innebär automatisk omträning av modeller när:
# .github/workflows/mlops-ci.yml name: MLOps CI Pipeline on: push: branches: [ main, dev ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.9' - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 pytest pytest-cov if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Lint with flake8 run: | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - name: Test with pytest run: | pytest tests/ --cov=src - name: Run data quality checks run: | python scripts/validate_data.py --data-path data/sample.csv - name: Run lightweight model training run: | python scripts/train_model.py --small-dataset --epochs 1 - name: Validate model performance run: | python scripts/evaluate_model.py --model-path models/latest.h5 --threshold 0.7
# .github/workflows/dvc-data-ci.yml name: DVC Data Pipeline CI on: push: branches: [ main ] paths: - 'data/**' - 'dvc.yaml' - 'params.yaml' jobs: data_pipeline: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.9' - name: Install dependencies run: | python -m pip install --upgrade pip pip install dvc dvc[s3] if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-1 - name: Pull DVC data run: | dvc pull - name: Run DVC pipeline run: | dvc repro - name: Run data validation run: | python scripts/validate_data_pipeline.py - name: Push DVC results run: | dvc push
# .github/workflows/mlops-cd.yml name: MLOps CD Pipeline on: workflow_run: workflows: ["MLOps CI Pipeline"] branches: [main] types: [completed] jobs: deploy: runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.9' - name: Install dependencies run: | python -m pip install --upgrade pip pip install awscli boto3 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-1 - name: Download model from S3 run: | aws s3 cp s3://my-model-bucket/models/latest/ ./models/latest/ --recursive - name: Validate model run: | python scripts/validate_model_for_production.py --model-path models/latest/model.h5 - name: Compare with current production model run: | python scripts/compare_models.py --new-model models/latest/model.h5 --threshold 0.05 - name: Package model as Docker image run: | docker build -t my-model-service:${{ github.sha }} . - name: Push Docker image to ECR run: | aws ecr get-login-password | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.eu-west-1.amazonaws.com docker tag my-model-service:${{ github.sha }} ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.eu-west-1.amazonaws.com/my-model-service:${{ github.sha }} docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.eu-west-1.amazonaws.com/my-model-service:${{ github.sha }} - name: Update ECS service run: | aws ecs update-service --cluster ml-cluster --service my-model-service --force-new-deployment - name: Wait for deployment run: | aws ecs wait services-stable --cluster ml-cluster --services my-model-service - name: Validate deployment run: | python scripts/validate_deployment.py --endpoint https://api.example.com/model
# kubernetes/model-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: model-service spec: replicas: 10 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 selector: matchLabels: app: model-service template: metadata: labels: app: model-service spec: containers: - name: model-service image: my-model-registry/model-service:latest ports: - containerPort: 8080 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10 resources: limits: cpu: "1" memory: "2Gi" requests: cpu: "0.5" memory: "1Gi"
import boto3 import json # Skapa en SageMaker-klient sagemaker_client = boto3.client('sagemaker') # Konfigurera A/B-testning response = sagemaker_client.create_endpoint_config( EndpointConfigName='my-ab-test-config', ProductionVariants=[ { 'VariantName': 'ModelA', 'ModelName': 'model-a', 'InitialInstanceCount': 1, 'InstanceType': 'ml.c5.large', 'InitialVariantWeight': 0.5 }, { 'VariantName': 'ModelB', 'ModelName': 'model-b', 'InitialInstanceCount': 1, 'InstanceType': 'ml.c5.large', 'InitialVariantWeight': 0.5 } ] ) # Skapa eller uppdatera endpoint response = sagemaker_client.create_endpoint( EndpointName='my-ab-test-endpoint', EndpointConfigName='my-ab-test-config' ) # För att anropa en specifik modellvariant för testning runtime_client = boto3.client('sagemaker-runtime') response = runtime_client.invoke_endpoint( EndpointName='my-ab-test-endpoint', ContentType='application/json', TargetVariant='ModelA', # Specifikt anropa ModelA Body=json.dumps({"features": [1, 2, 3, 4]}) )
def evaluate_and_approve_model(model_path, production_model_path, metrics_threshold): """Utvärd och godkänn en modell baserat på tröskelvärden""" # Ladda modellerna new_model = load_model(model_path) prod_model = load_model(production_model_path) # Utvärdera på testdata new_metrics = evaluate_model(new_model, test_data) prod_metrics = evaluate_model(prod_model, test_data) # Beräkna prestandaförbättring improvement = {} for metric_name, threshold in metrics_threshold.items(): improvement[metric_name] = new_metrics[metric_name] - prod_metrics[metric_name] # Kontrollera om alla tröskelvärden är uppfyllda approval_decision = True approval_reasons = [] for metric_name, threshold in metrics_threshold.items(): if improvement[metric_name] < threshold: approval_decision = False approval_reasons.append(f"{metric_name} förbättring {improvement[metric_name]:.4f} under tröskelvärde {threshold}") if approval_decision: approval_reasons.append("Alla prestandamätvärden möter förbättringströskelvärdena") return { "approved": approval_decision, "reasons": approval_reasons, "metrics": new_metrics, "improvement": improvement }
def monitor_and_rollback_if_needed(deployed_model_id, performance_threshold, window_size=1000): """Övervaka modellprestanda och återställ om den faller under tröskelvärdet""" # Hämta senaste prestandamätvärden recent_metrics = get_recent_metrics(deployed_model_id, window_size) # Beräkna genomsnittlig prestanda avg_performance = np.mean([m['accuracy'] for m in recent_metrics]) if avg_performance < performance_threshold: print(f"Modellprestanda {avg_performance:.4f} under tröskelvärde {performance_threshold}") # Hämta tidigare (stabil) modellversion previous_stable_model = get_previous_stable_model() # Initiera rollback rollback_success = initiate_rollback(deployed_model_id, previous_stable_model.id) if rollback_success: print(f"Rollback till modell {previous_stable_model.id} lyckades") # Skicka notifiering till team send_alert( subject="Automatisk modellrollback utförd", message=f"Modell {deployed_model_id} återställd till {previous_stable_model.id} på grund av låg prestanda" ) else: print("Rollback misslyckades, eskalera till manuell åtgärd") # Skicka larmnotifiering för manuell åtgärd send_alert( subject="AKUT: Modellrollback misslyckades", message=f"Manuell åtgärd krävs för modell {deployed_model_id}", priority="high" ) else: print(f"Modellprestanda {avg_performance:.4f} över tröskelvärde {performance_threshold}, ingen åtgärd krävs")
1. CI Pipeline: Implementera en CI-pipeline för ett ML-projekt med GitHub Actions 2. CD Pipeline: Skapa en CD-pipeline för automatisk driftsättning av ML-modeller 3. A/B-testning: Implementera A/B-testning för två modellvarianter 4. Rollback-strategi: Designa och implementera en rollback-strategi för ML-modeller
I nästa modul kommer vi att utforska containerisering och orkestrering av ML-applikationer, där vi kommer att lära oss hur man paketerar och driftsätter ML-modeller i containeriserade miljöer.