diff --git a/docs/machine-learning/advanced-ml-topics/mlops/ci-cd.mdx b/docs/machine-learning/advanced-ml-topics/mlops/ci-cd.mdx index e69de29..bb37d71 100644 --- a/docs/machine-learning/advanced-ml-topics/mlops/ci-cd.mdx +++ b/docs/machine-learning/advanced-ml-topics/mlops/ci-cd.mdx @@ -0,0 +1,123 @@ +--- +title: "CI/CD/CT: Automated Pipelines for ML" +sidebar_label: CI/CD for ML +description: "Exploring Continuous Integration, Continuous Delivery, and Continuous Training in MLOps." +tags: [mlops, cicd, continuous-training, automation, jenkins, github-actions] +--- + +In traditional software, we have **CI** (Continuous Integration) and **CD** (Continuous Delivery). However, Machine Learning introduces a third dimension: **Data**. Because data changes over time, we need a third pillar: **CT** (Continuous Training). + +## 1. The Three Pillars of MLOps Automation + +To build a robust ML system, we must automate three distinct cycles: + +### Continuous Integration (CI) +Beyond testing code, ML CI involves testing **data schemas** and **models**. +* **Code Testing:** Unit tests for feature engineering logic. +* **Data Testing:** Validating that incoming data matches expected distributions. +* **Model Validation:** Ensuring the model architecture compiles and training runs without memory leaks. + +### Continuous Delivery (CD) +This is the automation of deploying the model as a service. +* **Artifact Packaging:** Wrapping the model in a [Docker container](./model-deployment#2-the-containerization-standard-docker). +* **Integration Testing:** Ensuring the API endpoint responds correctly to requests. +* **Deployment:** Moving the model to a staging or production environment using [Canary or Blue-Green strategies](./model-deployment#3-deployment-strategies). + +### Continuous Training (CT) +This is unique to ML. It is a property of an ML system that automatically retrains and serves the model based on new data or [Model Drift](./monitoring#1-why-models-decay). + +## 2. The MLOps Maturity Levels + +Google defines the evolution of CI/CD in ML through three levels of maturity: + +1. **Level 0 (Manual):** Every step (data prep, training, deployment) is done manually in notebooks. +2. **Level 1 (Automated Training):** The pipeline is automated. Whenever new data arrives, the training and validation happen automatically (CT). +3. **Level 2 (CI/CD Pipeline Automation):** The entire workflow—from code commits to model monitoring—is a fully automated CI/CD pipeline. + +## 3. The Automated Workflow + +The following diagram illustrates how a code change or a "Drift" alert triggers a sequence of automated events. + +```mermaid +graph TD + Code[Code Commit / Data Drift Alert] --> CI[CI: Build & Test] + + subgraph Pipeline [Automated ML Pipeline] + CI --> Train[Continuous Training] + Train --> Eval[Model Evaluation] + Eval --> Validate{Meets Threshold?} + end + + Validate -- No --> Fail[Alert Developer] + Validate -- Yes --> Register[Model Registry] + + Register --> CD[CD: Deploy to Prod] + CD --> Monitor[Monitoring & Observability] + Monitor -- Drift Detected --> Code + + style Pipeline fill:#f0f4ff,stroke:#5c7aff,stroke-width:2px,color:#333 + style Validate fill:#fff3e0,stroke:#ef6c00,color:#333 + style Register fill:#c8e6c9,stroke:#2e7d32,color:#333 + +``` + +## 4. Key Components of the Pipeline + +* **Feature Store:** A centralized repository where features are stored and shared, ensuring that the same feature logic is used in both training and serving. +* **Model Registry:** A "version control" for models. It stores trained models, their metadata (hyperparameters, accuracy), and their environment dependencies. +* **Metadata Store:** Records every execution of the pipeline, allowing you to trace a specific model version back to the exact dataset and code used to create it. + +## 5. Tools of the Trade + +Depending on your cloud provider, the tools for CI/CD/CT vary: + +| Component | Open Source | AWS | Google Cloud | +| --- | --- | --- | --- | +| **Orchestration** | Kubeflow / Airflow | Step Functions | Vertex AI Pipelines | +| **CI/CD** | GitHub Actions / GitLab | CodePipeline | Cloud Build | +| **Tracking** | MLflow | SageMaker Experiments | Vertex AI Metadata | +| **Storage** | DVC (Data Version Control) | S3 | GCS | + +## 6. Implementation: A GitHub Actions Snippet + +A simple CI task to check if a model's accuracy meets a threshold before allowing a "Push" to production. + +```yaml +name: Model Training CI +on: [push] + +jobs: + train-and-validate: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Run Training & Evaluation + run: python train.py # Script generates 'metrics.json' + + - name: Check Accuracy Threshold + run: | + ACCURACY=$(jq '.accuracy' metrics.json) + if (( $(echo "$ACCURACY < 0.85" | bc -l) )); then + echo "Accuracy too low ($ACCURACY). Deployment failed." + exit 1 + fi + +``` + +## References + +* **Google Cloud:** [MLOps: Continuous delivery and automation pipelines](https://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning) +* **ThoughtWorks:** [Continuous Delivery for Machine Learning (CD4ML)](https://martinfowler.com/articles/cd4ml.html) +* **MLflow:** [Introduction to Model Registry](https://www.mlflow.org/docs/latest/model-registry.html) + +--- + +**With CI/CD/CT, your model is now a living, breathing part of your infrastructure. But how do we ensure it remains ethical and unbiased throughout these cycles?** \ No newline at end of file diff --git a/docs/machine-learning/advanced-ml-topics/mlops/data-versioning.mdx b/docs/machine-learning/advanced-ml-topics/mlops/data-versioning.mdx index e69de29..f44d88f 100644 --- a/docs/machine-learning/advanced-ml-topics/mlops/data-versioning.mdx +++ b/docs/machine-learning/advanced-ml-topics/mlops/data-versioning.mdx @@ -0,0 +1,110 @@ +--- +title: "Data Versioning: The Git for Data" +sidebar_label: Data Versioning +description: "Understanding how to track changes in datasets to ensure reproducibility and auditability in ML experiments." +tags: [mlops, data-versioning, dvc, reproducibility, data-lake] +--- + +In traditional software development, versioning code with **Git** is enough to recreate any state of an application. In Machine Learning, code is only half the story. The resulting model depends on both the **Code** and the **Data**. + +If you retrain your model today and get different results than yesterday, you need to know exactly which version of the dataset was used. **Data Versioning** provides the "undo button" for your data. + +## 1. Why Git Isn't Enough for Data + +Git is designed to track small text files. It struggles with the large binary files (CSV, Parquet, Images, Audio) typically used in ML for several reasons: + +* **Storage Limits:** Storing gigabytes of data in a Git repository slows down operations significantly. +* **Diffing:** Git cannot efficiently show differences between two 5GB binary files. +* **Cost:** Hosting large blobs in GitHub or GitLab is expensive and inefficient. + +**Data Versioning tools** solve this by tracking "pointers" (metadata) in Git, while storing the actual data in external storage (S3, GCS, Azure Blob). + +## 2. The Core Concept: Metadata vs. Storage + +Data versioning works by creating a **hash** (unique ID) of your data files. + +1. **The Data:** Stored in a scalable cloud bucket (e.g., AWS S3). +2. **The Metafile:** A tiny text file containing the hash and file path. This file **is** committed to Git. + +
+ +The relationship between Git (tracking .dvc files) and S3 (tracking large datasets) + + +## 3. Workflow Logic + +The following diagram illustrates how DVC (Data Version Control) interacts with Git and remote storage to maintain synchronization. + +```mermaid +graph TD + subgraph Local_Machine [Local Workspace] + Code[script.py] -- "git commit" --> Git[(Git Repo)] + Data[data.csv] -- "dvc add" --> Meta[.dvc file] + Meta -- "git commit" --> Git + end + + subgraph Storage [Remote Storage] + Data -- "dvc push" --> Cloud[(S3 / GCS Bucket)] + end + + subgraph Collaborator [Team Member] + Git -- "git pull" --> NewMeta[.dvc file] + NewMeta -- "dvc pull" --> NewData[data.csv] + Cloud -- download --> NewData + end + + style Storage fill:#f1f8e9,stroke:#558b2f,color:#333 + style Git fill:#e1f5fe,stroke:#01579b,color:#333 + style Data fill:#fff3e0,stroke:#ef6c00,color:#333 + +``` + +## 4. Popular Data Versioning Tools + +| Tool | Focus | Best For | +| --- | --- | --- | +| **DVC (Data Version Control)** | Open-source, Git-like CLI. | Teams already comfortable with Git. | +| **Pachyderm** | Data lineage and pipelining. | Complex data pipelines on Kubernetes. | +| **LakeFS** | Git-like branches for Data Lakes. | Teams using S3/GCS as their primary data source. | +| **W&B Artifacts** | Integrated with experiment tracking. | Visualizing data lineage alongside model training. | + +## 5. Implementation with DVC + +DVC is the most popular tool because it integrates seamlessly with your existing Git workflow. + +```bash +# 1. Initialize DVC in your project +dvc init + +# 2. Add a large dataset (this creates data.csv.dvc) +dvc add data/train_images.zip + +# 3. Track the metadata in Git +git add data/train_images.zip.dvc .gitignore +git commit -m "Add raw training images version 1.0" + +# 4. Push the actual data to a remote (S3, GCS, etc.) +dvc remote add -d myremote s3://my-bucket/data +dvc push + +# 5. Switching versions +git checkout v2.0-experiment +dvc checkout # This physically swaps the data files in your folder + +``` + +## 6. The Benefits of Versioning Data + +* **Reproducibility:** You can recreate the exact environment of a model trained 6 months ago. +* **Compliance & Auditing:** In regulated industries (finance/healthcare), you must be able to show exactly what data was used to train a model to explain its decisions. +* **Collaboration:** Multiple researchers can work on different versions of the data without overwriting each other's work. +* **Data Lineage:** Tracking the "ancestry" of a dataset—knowing that `clean_data.csv` was generated from `raw_data.csv` using `clean.py`. + +## References + +* **DVC Documentation:** [Get Started with DVC](https://dvc.org/doc/start) +* **LakeFS:** [Git for Data Lakes](https://lakefs.io/) + +--- + +**Data versioning is the foundation of a reproducible pipeline. Now that we can track our data and code, how do we track the experiments and hyperparameter results?** \ No newline at end of file diff --git a/docs/machine-learning/advanced-ml-topics/mlops/model-deployment.mdx b/docs/machine-learning/advanced-ml-topics/mlops/model-deployment.mdx index e69de29..f17ad3c 100644 --- a/docs/machine-learning/advanced-ml-topics/mlops/model-deployment.mdx +++ b/docs/machine-learning/advanced-ml-topics/mlops/model-deployment.mdx @@ -0,0 +1,112 @@ +--- +title: "Model Deployment: Moving from Lab to Production" +sidebar_label: Deployment +description: "Strategies for serving machine learning models, including batch vs. real-time, containerization, and deployment patterns." +tags: [mlops, deployment, docker, kubernetes, api, serving] +--- + +**Model Deployment** is the process of integrating a machine learning model into an existing production environment where it can take in data and return predictions. It is the final stage of the ML pipeline, but it is also the beginning of the model's "life" where it provides actual value. + +## 1. Deployment Modes + +Before choosing a tool, you must decide how the users will consume the predictions. + +| Mode | Description | Example | +| :--- | :--- | :--- | +| **Request-Response (Real-time)** | The model lives behind an API. Predictions are returned instantly (low latency). | **Fraud Detection** during a credit card swipe. | +| **Batch Scoring** | The model runs on a large set of data at scheduled intervals (e.g., every night). | **Recommendation Emails** sent to users once a day. | +| **Streaming** | The model consumes data from a queue (like Kafka) and outputs predictions continuously. | **Log Monitoring** for cybersecurity threats. | + +## 2. The Containerization Standard: Docker + +In MLOps, we don't just deploy code; we deploy the **environment**. To avoid the "it works on my machine" problem, we use **Docker**. + +A Docker container packages the model file, the Python runtime, and all dependencies (NumPy, Scikit-Learn, etc.) into a single image that runs identically on any server. + +## 3. Deployment Strategies + +Deploying a model isn't just about "overwriting" the old one. We use strategies to minimize risk. + +* **Blue-Green Deployment:** You have two identical environments. You route traffic to "Green" (new model). If it fails, you instantly flip back to "Blue" (old model). +* **Canary Deployment:** You route 5% of traffic to the new model. If the metrics look good, you slowly increase it to 100%. +* **A/B Testing:** You run two models simultaneously and compare their real-world performance (e.g., which one leads to more clicks?). + +## 4. Logical Workflow: The Deployment Pipeline + +The following diagram illustrates the path from a trained model to a live API endpoint. + +```mermaid +graph LR + Model[Trained Model File .pkl / .h5] --> Wrap[API Wrapper: Flask/FastAPI] + Wrap --> Docker[Docker Image] + Docker --> Registry[Container Registry] + + subgraph Infrastructure [Production Environment] + Registry --> K8s[Kubernetes / Cloud Run] + K8s --> LoadBalancer[Load Balancer] + end + + User((User)) --> LoadBalancer + LoadBalancer --> K8s + + style Docker fill:#e1f5fe,stroke:#01579b,color:#333 + style K8s fill:#fff3e0,stroke:#ef6c00,color:#333 + style Model fill:#c8e6c9,stroke:#2e7d32,color:#333 + +``` + +## 5. Model Serving Frameworks + +While you can write your own API using **FastAPI**, dedicated "Model Serving" tools handle scaling and versioning better: + +1. **TensorFlow Serving:** Highly optimized for TF models. +2. **TorchServe:** The official serving library for PyTorch. +3. **KServe (formerly KFServing):** A serverless way to deploy models on Kubernetes. +4. **BentoML:** A framework that simplifies the packaging and deployment of any Python model. + +## 6. Implementation Sketch (FastAPI + Uvicorn) + +This is a minimal example of serving a Scikit-Learn model as a REST API. + +```python +from fastapi import FastAPI +import joblib +import pydantic + +app = FastAPI() + +# 1. Load the pre-trained model +model = joblib.load("model_v1.pkl") + +# 2. Define the input schema +class InputData(pydantic.BaseModel): + feature_1: float + feature_2: float + +# 3. Create the prediction endpoint +@app.post("/predict") +def predict(data: InputData): + prediction = model.predict([[data.feature_1, data.feature_2]]) + return {"prediction": int(prediction[0])} + +# Run with: uvicorn main:app --reload + +``` + +## 7. Post-Deployment: Monitoring + +Once a model is live, its performance will likely decrease over time (**Model Drift**). We must monitor: + +* **Latency:** How long does a prediction take? +* **Data Drift:** Is the incoming data different from the training data? +* **Concept Drift:** Has the relationship between features and the target changed? + +## References + +* **Google Cloud:** [Practices for MLOps and CI/CD](https://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning) +* **FastAPI:** [Official Documentation](https://fastapi.tiangolo.com/) +* **MLOps.community:** [Deployment Patterns](https://mlops.community/) + +--- + +**Deployment is just the beginning. How do we ensure our model stays accurate as the world changes?** \ No newline at end of file diff --git a/docs/machine-learning/advanced-ml-topics/mlops/monitoring.mdx b/docs/machine-learning/advanced-ml-topics/mlops/monitoring.mdx index e69de29..f4ac423 100644 --- a/docs/machine-learning/advanced-ml-topics/mlops/monitoring.mdx +++ b/docs/machine-learning/advanced-ml-topics/mlops/monitoring.mdx @@ -0,0 +1,102 @@ +--- +title: Model Monitoring & Observability +sidebar_label: Monitoring +description: "Detecting data drift, model decay, and system performance issues in production ML systems." +tags: [mlops, monitoring, data-drift, observability, prometheus, grafana] +--- + +In traditional software, monitoring focuses on system health (CPU, RAM, Uptime). In **MLOps**, we must monitor the **Data** and the **Model Logic**. A model's code might be bug-free, but its predictions can still become "rotten" because the world around it changes. + +## 1. Why Models "Decay" + +Unlike software that stays the same until a new version is pushed, ML models degrade silently. This is usually caused by two types of "Drift": + +### A. Data Drift (Feature Drift) +This happens when the statistical properties of the **input data** change. +* **Example:** A facial recognition model trained on people in their 20s starts failing as the user base ages. +* **Metric:** Population Stability Index (PSI) or Kullback-Leibler (KL) Divergence. + +### B. Concept Drift +This happens when the **relationship** between the input and the target changes. +* **Example:** A house price prediction model built before a housing market crash. The houses (inputs) are the same, but the prices (targets) follow new rules. + +## 2. The Monitoring Pyramid + +Effective MLOps monitoring covers three distinct layers: + +| Layer | What to Monitor | Tools | +| :--- | :--- | :--- | +| **System Health** | Latency, Throughput, CPU/GPU usage, Memory leaks. | Prometheus, Grafana | +| **Data Quality** | Missing values, Schema changes, Outliers, Feature distributions. | Great Expectations, Evidently AI | +| **Model Performance** | Accuracy, Precision, Recall, F1-Score (requires "Ground Truth"). | MLflow, Weights & Biases | + +## 3. Detecting Drift + +The following diagram illustrates the feedback loop required to detect and act upon model decay. + +```mermaid +graph TD + In[Live Input Data] --> Predict[Model Prediction] + Predict --> Store[Data Store / Log] + + subgraph Analysis [Monitoring Service] + Store --> Dist[Distribution Analysis] + Train_Data[(Training Data Baseline)] --> Dist + Dist --> Threshold{Drift Detected?} + end + + Threshold -- Yes --> Alert[Trigger Alert / Retrain Pipeline] + Threshold -- No --> Sleep[Continue Monitoring] + + style Analysis fill:#f3e5f5,stroke:#7b1fa2,color:#333 + style Threshold fill:#fff3e0,stroke:#ef6c00,color:#333 + style Train_Data fill:#e1f5fe,stroke:#01579b,color:#333 + +``` + +## 4. Ground Truth & Latency + +Monitoring performance (Accuracy/F1) is difficult because you often don't get the "correct answer" (Ground Truth) immediately. + +* **Immediate Feedback:** In an Ad-click model, you know within seconds if the user clicked. +* **Delayed Feedback:** In a Loan Default model, you might not know the "Ground Truth" for years. + +**Strategy:** When ground truth is delayed, rely heavily on **Data Drift** detection as a proxy for performance. + +## 5. Implementation: Drift Detection with Evidently AI + +[Evidently AI](https://www.evidentlyai.com/) is a popular open-source tool for generating reports on data drift. + +```python +from evidently.report import Report +from evidently.metric_preset import DataDriftPreset + +# 1. Create a drift report +report = Report(metrics=[ + DataDriftPreset(), +]) + +# 2. Compare reference data (training) vs. current data (production) +report.run(reference_data=training_df, current_data=production_df) + +# 3. Export as HTML or JSON for automated alerts +report.save_html("drift_report.html") + +``` + +## 6. Closing the Loop: Automated Retraining + +When monitoring detects significant drift, the system should trigger a **Retraining Pipeline**: + +1. Fetch the newly labeled data. +2. Run the training script on the new dataset. +3. Validate that the new model outperforms the current one. +4. Deploy the new version via [Canary Deployment](./model-deployment#3-deployment-strategies). + +## References + +* **Evidently AI:** [Data Drift Detection Guide](https://docs.evidentlyai.com/) + +--- + +**Monitoring is the "safety net" of AI. But how do we build the entire pipeline so that training, testing, and deployment happen automatically?** \ No newline at end of file diff --git a/docs/machine-learning/advanced-ml-topics/mlops/reproducibility.mdx b/docs/machine-learning/advanced-ml-topics/mlops/reproducibility.mdx index e69de29..8d6db50 100644 --- a/docs/machine-learning/advanced-ml-topics/mlops/reproducibility.mdx +++ b/docs/machine-learning/advanced-ml-topics/mlops/reproducibility.mdx @@ -0,0 +1,121 @@ +--- +title: "Reproducibility: The Gold Standard of ML" +sidebar_label: Reproducibility +description: "Ensuring consistent results across environments by versioning code, data, models, and environments." +tags: [mlops, reproducibility, experiment-tracking, dvc, docker] +--- + +In traditional science, a result is only valid if another scientist can follow the same steps and achieve the same outcome. In Machine Learning, **Reproducibility** is notoriously difficult because a model's output is sensitive to tiny changes in data, code, random seeds, and even the hardware drivers used. + +If you cannot reproduce a model, you cannot reliably debug it, audit it for bias, or deploy it with confidence. + +## 1. The Four Pillars of Reproducibility + +To achieve 100% reproducibility, you must lock down four specific dimensions of your project: + +| Pillar | What it covers | Primary Tool | +| :--- | :--- | :--- | +| **Code** | The exact logic, preprocessing, and model architecture. | **Git** | +| **Data** | The specific version of the dataset (including the split). | **DVC / LakeFS** | +| **Environment** | OS version, Python version, and library dependencies. | **Docker / Conda** | +| **Randomness** | Seed values for weight initialization and data shuffling. | **Code-level Seeds** | + +## 2. Managing the "Random" Factor + +Most ML algorithms involve stochastic (random) processes. To ensure the same result every time, you must set a **Global Seed**. Without this, your model weights will initialize differently every time you click "Run." + +```python +import numpy as np +import torch +import random + +def set_seed(seed=42): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + # Ensure deterministic behavior in GPU operations + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + +set_seed(42) + +``` + +## 3. The Reproducibility Workflow (Mermaid) + +The following diagram illustrates how the four pillars converge into a "Reproducible Artifact" that can be shared across a team. + +```mermaid +graph TD + subgraph Development [Source Control] + Code[Python Scripts] + Config[Hyperparameters.yaml] + end + + subgraph Data_Control [Data Versioning] + Dataset[(Dataset v1.2)] + end + + subgraph Env_Control [Environment] + Docker[Docker Image / venv] + end + + Code & Config & Dataset & Docker --> Execution{Model Training} + + Execution --> Result[Metric: 94% Accuracy] + Result --> Registry[Model Registry: Versioned Artifact] + + style Registry fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px,color:#333 + style Docker fill:#e1f5fe,stroke:#01579b,color:#333 + style Dataset fill:#fff3e0,stroke:#ef6c00,color:#333 + +``` + +## 4. Environment Freezing + +Dependency versions change constantly. A package like `scikit-learn` might update its default parameters between versions, changing your model's output. + +**Bad practice:** + +```text +pandas +scikit-learn + +``` + +**Good practice (Frozen):** + +```text +pandas==2.1.0 +scikit-learn==1.3.2 + +``` + +Better yet, use a **Docker Container** to ensure that even the C++ libraries and OS-level drivers (like CUDA) remain identical. + +## 5. Experiment Tracking: The "Lab Notebook" + +Reproducibility is not just about the final model; it's about the journey. **Experiment Tracking** tools act as a digital lab notebook, recording every attempt, every hyperparameter tweak, and every failure. + +* **MLflow:** Tracks parameters, metrics, and "artifacts" (the model files). +* **Weights & Biases (W&B):** Provides visual dashboards to compare different runs and identify exactly which change led to an improvement. + +## 6. Checklist for Reproducible ML + +Before considering a model "ready," ensure you can answer "Yes" to these: + +1. [ ] Is the code committed to Git? +2. [ ] Is the data versioned (e.g., via DVC)? +3. [ ] Are all random seeds explicitly set? +4. [ ] Is there a `requirements.txt` or `Dockerfile`? +5. [ ] Are the hyperparameters logged in an experiment tracker? + +## References + +* **Papers with Code:** [The Reproducibility Checklist](https://www.cs.mcgill.ca/~jpineau/ReproducibilityChecklist.pdf) +* **MLflow:** [Running Reproducible Projects](https://mlflow.org/docs/latest/projects.html) + +--- + +**Reproducibility allows you to trust your own results. But how do we scale this trust to an entire organization with thousands of features?** \ No newline at end of file diff --git a/docusaurus.config.js b/docusaurus.config.js index 9d48cc4..52f3453 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -347,6 +347,7 @@ const config = { "ruby", "rust", "java", + "yaml", ], }, docs: { diff --git a/static/img/tutorials/ml/git-dvc-s3.png b/static/img/tutorials/ml/git-dvc-s3.png new file mode 100644 index 0000000..342586a Binary files /dev/null and b/static/img/tutorials/ml/git-dvc-s3.png differ diff --git a/static/img/tutorials/ml/git-dvc.jpg b/static/img/tutorials/ml/git-dvc.jpg new file mode 100644 index 0000000..e8c1630 Binary files /dev/null and b/static/img/tutorials/ml/git-dvc.jpg differ