Made-With-ML

GokuMohandasMIT更新放缓 有专家点评

一套从数据工程、模型训练到部署迭代的完整教程,帮你把机器学习项目做成可上线的生产级应用。

Learn how to develop, deploy and iterate on production-grade ML applications.

v1.1.0发布于 2026-03-04 23:24 UTC49,986star +34 / 7 天7,822fork同步于 2026-11-10 23:20 UTC

专家点评

上手难度:进阶更新于 2026-11-11

一套把 ML 从 notebook 推到 CI/CD 和线上服务的完整工程化教程,用来替换零散的 MLOps 拼装经验

适用场景

  • 团队要从零建立 ML 工程规范:它把数据校验、实验跟踪、模型注册、服务化串成一条可跑的链路,比自己拼 MLflow + Ray + CI 模板少走弯路
  • 新人 onboarding:notebook 与工程化脚本两套等价实现,可以先看 notebook 理解概念,再对照脚本看生产写法,比直接读框架文档快
  • 需要 Ray 落地参考:数据并行、训练、调参、服务四类负载都有对应示例,适合在引入 Ray 前先跑通一遍看 API 形态
  • 离线复现教学/内训:单机笔记本即可跑全流程(GPU 置 0),适合做工作坊或课程材料

踩坑

  • 环境变量是硬门槛:不 export PYTHONPATH 模块导入直接失败,还要自建 .env 填 GITHUB_USERNAME,clone 完直接跑会卡在这里
  • 资源参数默认按 GPU 配:--gpu-per-worker 默认 1,纯 CPU 机器不改 --num-workers/--cpu-per-worker/--gpu-per-worker 会起不来或极慢
  • issue 里反复出现的是运行环境替代问题(Colab/Binder 在中国大陆不可用、旧课程去哪了),说明教程对托管 notebook 环境有依赖,离线或受限网络下要自己解决运行载体

同类对比

真要系统学 MLOps 工程化,同类还有 full-stack-deep-learning 和 mlops-zoomcamp;如果只是想找可直接抄的模板而非教程,去看 cookiecutter-data-science 或各框架官方示例更省事

怎么上手

由 README 提炼,执行前请确认
git clone https://github.com/GokuMohandas/Made-With-ML.git .
python3 -m venv venv
source venv/bin/activate
python3 -m pip install -r requirements.txt
硬件要求按 README 说明

README 未给出具体显存要求;本地单机笔记本即可运行全部代码(速度较慢),示例配置默认 --gpu-per-worker 1,纯 CPU 时可设为 0;Anyscale 侧使用 g5.4xlarge 计算配置(含 GPU)。

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

从实验到生产的 ML 全流程实战教程,带你用 Python 搭出可迭代的可靠 ML 系统

一套从数据工程、模型训练到部署迭代的完整教程,帮你把机器学习项目做成可上线的生产级应用。

核心能力

  • 按第一性原理讲解每个 ML 概念再动手写代码
  • 用 Ray 在 Python 内横向扩展数据、训练、调参、服务负载
  • 用 MLflow 跟踪实验并管理模型注册表
  • 提供 notebook 与工程化脚本两套等价实现
  • 覆盖 CI/CD 与 MLOps 组件串联的端到端流程

容易踩的坑

推荐使用 Python 3.10,并需先 export PYTHONPATH=$PYTHONPATH:$PWD 否则模块导入失败;需自行创建 .env 并填写 GITHUB_USERNAME;训练脚本的 --num-workers/--cpu-per-worker/--gpu-per-worker 要按本机资源改,本地笔记本建议 GPU 设为 0;MLflow UI 默认监听 8080 端口,远程环境需额外暴露端口。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

1 个版本v1.1.0
  1. v1.1.0最新正式版2026-03-04 · 2026-03-04 23:24 UTC

    Release prior to agentic workflow updates

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Lessons

    Learn how to combine machine learning with software engineering to design, develop, deploy and iterate on production-grade ML applications.

    Overview

    In this course, we'll go from experimentation (design + development) to production (deployment + iteration). We'll do this iteratively by motivating the components that will enable us to build a reliable production system.

    • 💡 First principles: before we jump straight into the code, we develop a first principles understanding for every machine learning concept.
    • 💻 Best practices: implement software engineering best practices as we develop and deploy our machine learning models.
    • 📈 Scale: easily scale ML workloads (data, train, tune, serve) in Python without having to learn completely new languages.
    • ⚙️ MLOps: connect MLOps components (tracking, testing, serving, orchestration, etc.) as we build an end-to-end machine learning system.
    • 🚀 Dev to Prod: learn how to quickly and reliably go from development to production without any changes to our code or infra management.
    • 🐙 CI/CD: learn how to create mature CI/CD workflows to continuously train and deploy better models in a modular way that integrates with any stack.

    Audience

    Machine learning is not a separate industry, instead, it's a powerful way of thinking about data that's not reserved for any one type of person.

    • 👩‍💻 All developers: whether software/infra engineer or data scientist, ML is increasingly becoming a key part of the products that you'll be developing.
    • 👩‍🎓 College graduates: learn the practical skills required for industry and bridge gap between the university curriculum and what industry expects.
    • 👩‍💼 Product/Leadership: who want to develop a technical foundation so that they can build amazing (and reliable) products powered by machine learning.

    Set up

    Be sure to go through the course for a much more detailed walkthrough of the content on this repository. We will have instructions for both local laptop and Anyscale clusters for the sections below, so be sure to toggle the ► dropdown based on what you're using (Anyscale instructions will be toggled on by default). If you do want to run this course with Anyscale, where we'll provide the structure, compute (GPUs) and community to learn everything in one day, join our next upcoming live cohort → sign up here!

    Cluster

    We'll start by setting up our cluster with the environment and compute configurations.

    We can create an Anyscale Workspace using the webpage UI.

    - Workspace name: `madewithml`
    - Project: `madewithml`
    - Cluster environment name: `madewithml-cluster-env`
    # Toggle `Select from saved configurations`
    - Compute config: `madewithml-cluster-compute-g5.4xlarge`
    

    Alternatively, we can use the CLI to create the workspace via anyscale workspace create ...

    If you don't want to do this course locally or via Anyscale, you have the following options:

    Git setup

    Create a repository by following these instructions: Create a new repository → name it Made-With-ML → Toggle Add a README file (very important as this creates a main branch) → Click Create repository (scroll down)

    Now we're ready to clone the repository that has all of our code:

    git clone https://github.com/GokuMohandas/Made-With-ML.git .
    

    Credentials

    touch .env
    
    # Inside .env
    GITHUB_USERNAME="CHANGE_THIS_TO_YOUR_USERNAME"  # ← CHANGE THIS
    
    source .env
    

    Virtual environment

    export PYTHONPATH=$PYTHONPATH:$PWD
    python3 -m venv venv  # recommend using Python 3.10
    source venv/bin/activate  # on Windows: venv\Scripts\activate
    python3 -m pip install --upgrade pip setuptools wheel
    python3 -m pip install -r requirements.txt
    pre-commit install
    pre-commit autoupdate
    

    Highly recommend using Python 3.10 and using pyenv (mac) or pyenv-win (windows).

    Our environment with the appropriate Python version and libraries is already all set for us through the cluster environment we used when setting up our Anyscale Workspace. So we just need to run these commands:

    export PYTHONPATH=$PYTHONPATH:$PWD
    pre-commit install
    pre-commit autoupdate
    

    Notebook

    Start by exploring the jupyter notebook to interactively walkthrough the core machine learning workloads.

    # Start notebook
    jupyter lab notebooks/madewithml.ipynb
    

    Click on the Jupyter icon    at the top right corner of our Anyscale Workspace page and this will open up our JupyterLab instance in a new tab. Then navigate to the notebooks directory and open up the madewithml.ipynb notebook.

    Scripts

    Now we'll execute the same workloads using the clean Python scripts following software engineering best practices (testing, documentation, logging, serving, versioning, etc.) The code we've implemented in our notebook will be refactored into the following scripts:

    madewithml
    ├── config.py
    ├── data.py
    ├── evaluate.py
    ├── models.py
    ├── predict.py
    ├── serve.py
    ├── train.py
    ├── tune.py
    └── utils.py
    

    Note: Change the --num-workers, --cpu-per-worker, and --gpu-per-worker input argument values below based on your system's resources. For example, if you're on a local laptop, a reasonable configuration would be --num-workers 6 --cpu-per-worker 1 --gpu-per-worker 0.

    Training

    export EXPERIMENT_NAME="llm"
    export DATASET_LOC="https://raw.githubusercontent.com/GokuMohandas/Made-With-ML/main/datasets/dataset.csv"
    export TRAIN_LOOP_CONFIG='{"dropout_p": 0.5, "lr": 1e-4, "lr_factor": 0.8, "lr_patience": 3}'
    python madewithml/train.py \
        --experiment-name "$EXPERIMENT_NAME" \
        --dataset-loc "$DATASET_LOC" \
        --train-loop-config "$TRAIN_LOOP_CONFIG" \
        --num-workers 1 \
        --cpu-per-worker 3 \
        --gpu-per-worker 1 \
        --num-epochs 10 \
        --batch-size 256 \
        --results-fp results/training_results.json
    

    Tuning

    export EXPERIMENT_NAME="llm"
    export DATASET_LOC="https://raw.githubusercontent.com/GokuMohandas/Made-With-ML/main/datasets/dataset.csv"
    export TRAIN_LOOP_CONFIG='{"dropout_p": 0.5, "lr": 1e-4, "lr_factor": 0.8, "lr_patience": 3}'
    export INITIAL_PARAMS="[{\"train_loop_config\": $TRAIN_LOOP_CONFIG}]"
    python madewithml/tune.py \
        --experiment-name "$EXPERIMENT_NAME" \
        --dataset-loc "$DATASET_LOC" \
        --initial-params "$INITIAL_PARAMS" \
        --num-runs 2 \
        --num-workers 1 \
        --cpu-per-worker 3 \
        --gpu-per-worker 1 \
        --num-epochs 10 \
        --batch-size 256 \
        --results-fp results/tuning_results.json
    

    Experiment tracking

    We'll use MLflow to track our experiments and store our models and the MLflow Tracking UI to view our experiments. We have been saving our experiments to a local directory but note that in an actual production setting, we would have a central location to store all of our experiments. It's easy/inexpensive to spin up your own MLflow server for all of your team members to track their experiments on or use a managed solution like Weights & Biases, Comet, etc.

    export MODEL_REGISTRY=$(python -c "from madewithml import config; print(config.MODEL_REGISTRY)")
    mlflow server -h 0.0.0.0 -p 8080 --backend-store-uri $MODEL_REGISTRY
    

    If you're running this notebook on your local laptop then head on over to http://localhost:8080/ to view your MLflow dashboard.

    If you're on Anyscale Workspaces, then we need to first expose the port of the MLflow server. Run the following command on your Anyscale Workspace terminal to generate the public URL to your MLflow server.

    APP_PORT=8080
    echo https://$APP_PORT-port-$ANYSCALE_SESSION_DOMAIN
    

    Evaluation

    export EXPERIMENT_NAME="llm"
    export RUN_ID=$(python madewithml/predict.py get-best-run-id --experiment-name $EXPERIMENT_NAME --metric val_loss --mode ASC)
    export HOLDOUT_LOC="https://raw.githubusercontent.com/GokuMohandas/Made-With-ML/main/datasets/holdout.csv"
    python madewithml/evaluate.py \
        --run-id $RUN_ID \
        --dataset-loc $HOLDOUT_LOC \
        --results-fp results/evaluation_results.json
    
    {
      "timestamp": "June 09, 2023 09:26:18 AM",
      "run_id": "6149e3fec8d24f1492d4a4cabd5c06f6",
      "overall": {
        "precision": 0.9076136428670714,
        "recall": 0.9057591623036649,
        "f1": 0.9046792827719773,
        "num_samples": 191.0
      },
    ...
    

    Inference

    export EXPERIMENT_NAME="llm"
    export RUN_ID=$(python madewithml/predict.py get-best-run-id --experiment-name $EXPERIMENT_NAME --metric val_loss --mode ASC)
    python madewithml/predict.py predict \
        --run-id $RUN_ID \
        --title "Transfer learning with transformers" \
        --description "Using transformers for transfer learning on text classification tasks."
    
    [{
      "prediction": [
        "natural-language-processing"
      ],
      "probabilities": {
        "computer-vision": 0.0009767753,
        "mlops": 0.0008223939,
        "natural-language-processing": 0.99762577,
        "other": 0.000575123
      }
    }]
    

    Serving

    # Start
    ray start --head
    
    # Set up
    export EXPERIMENT_NAME="llm"
    export RUN_ID=$(python madewithml/predict.py get-best-run-id --experiment-name $EXPERIMENT_NAME --metric val_loss --mode ASC)
    python madewithml/serve.py --run_id $RUN_ID
    

    Once the application is running, we can use it via cURL, Python, etc.:

    # via Python
    import json
    import requests
    title = "Transfer learning with transformers"
    description = "Using transformers for transfer learning on text classification tasks."
    json_data = json.dumps({"title": title, "description": description})
    requests.post("http://127.0.0.1:8000/predict", data=json_data).json()
    
    ray stop  # shutdown
    

    In Anyscale Workspaces, Ray is already running so we don't have to manually start/shutdown like we have to do locally.

    # Set up
    export EXPERIMENT_NAME="llm"
    export RUN_ID=$(python madewithml/predict.py get-best-run-id --experiment-name $EXPERIMENT_NAME --metric val_loss --mode ASC)
    python madewithml/serve.py --run_id $RUN_ID
    

    Once the application is running, we can use it via cURL, Python, etc.:

    # via Python
    import json
    import requests
    title = "Transfer learning with transformers"
    description = "Using transformers for transfer learning on text classification tasks."
    json_data = json.dumps({"title": title, "description": description})
    requests.post("http://127.0.0.1:8000/predict", data=json_data).json()
    

    Testing

    # Code
    python3 -m pytest tests/code --verbose --disable-warnings
    
    # Data
    export DATASET_LOC="https://raw.githubusercontent.com/GokuMohandas/Made-With-ML/main/datasets/dataset.csv"
    pytest --dataset-loc=$DATASET_LOC tests/data --verbose --disable-warnings
    
    # Model
    export EXPERIMENT_NAME="llm"
    export RUN_ID=$(python madewithml/predict.py get-best-run-id --experiment-name $EXPERIMENT_NAME --metric val_loss --mode ASC)
    pytest --run-id=$RUN_ID tests/model --verbose --disable-warnings
    
    # Coverage
    python3 -m pytest tests/code --cov madewithml --cov-report html --disable-warnings  # html report
    python3 -m pytest tests/code --cov madewithml --cov-report term --disable-warnings  # terminal report
    

    Production

    From this point onwards, in order to deploy our application into production, we'll need to either be on Anyscale or on a cloud VM / on-prem cluster you manage yourself (w/ Ray). If not on Anyscale, the commands will be slightly different but the concepts will be the same.

    If you don't want to set up all of this yourself, we highly recommend joining our upcoming live cohort{:target="_blank"} where we'll provide an environment with all of this infrastructure already set up for you so that you just focused on the machine learning.

    Authentication

    These credentials below are automatically set for us if we're using Anyscale Workspaces. We do not need to set these credentials explicitly on Workspaces but we do if we're running this locally or on a cluster outside of where our Anyscale Jobs and Services are configured to run.

    export ANYSCALE_HOST=https://console.anyscale.com
    export ANYSCALE_CLI_TOKEN=$YOUR_CLI_TOKEN  # retrieved from Anyscale credentials page
    

    Cluster environment

    The cluster environment determines where our workloads will be executed (OS, dependencies, etc.) We've already created this cluster environment for us but this is how we can create/update one ourselves.

    export CLUSTER_ENV_NAME="madewithml-cluster-env"
    anyscale cluster-env build deploy/cluster_env.yaml --name $CLUSTER_ENV_NAME
    

    Compute configuration

    The compute configuration determines what resources our workloads will be executes on. We've already created this compute configuration for us but this is how we can create it ourselves.

    export CLUSTER_COMPUTE_NAME="madewithml-cluster-compute-g5.4xlarge"
    anyscale cluster-compute create deploy/cluster_compute.yaml --name $CLUSTER_COMPUTE_NAME
    

    Anyscale jobs

    Now we're ready to execute our ML workloads. We've decided to combine them all together into one job but we could have also created separate jobs for each workload (train, evaluate, etc.) We'll start by editing the $GITHUB_USERNAME slots inside our workloads.yaml file:

    runtime_env:
      working_dir: .
      upload_path: s3://madewithml/$GITHUB_USERNAME/jobs  # <--- CHANGE USERNAME (case-sensitive)
      env_vars:
        GITHUB_USERNAME: $GITHUB_USERNAME  # <--- CHANGE USERNAME (case-sensitive)
    

    The runtime_env here specifies that we should upload our current working_dir to an S3 bucket so that all of our workers when we execute an Anyscale Job have access to the code to use. The GITHUB_USERNAME is used later to save results from our workloads to S3 so that we can retrieve them later (ex. for serving).

    Now we're ready to submit our job to execute our ML workloads:

    anyscale job submit deploy/jobs/workloads.yaml
    

    Anyscale Services

    And after our ML workloads have been executed, we're ready to launch our serve our model to production. Similar to our Anyscale Jobs configs, be sure to change the $GITHUB_USERNAME in serve_model.yaml.

    ray_serve_config:
      import_path: deploy.services.serve_model:entrypoint
      runtime_env:
        working_dir: .
        upload_path: s3://madewithml/$GITHUB_USERNAME/services  # <--- CHANGE USERNAME (case-sensitive)
        env_vars:
          GITHUB_USERNAME: $GITHUB_USERNAME  # <--- CHANGE USERNAME (case-sensitive)
    

    Now we're ready to launch our service:

    # Rollout service
    anyscale service rollout -f deploy/services/serve_model.yaml
    
    # Query
    curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $SECRET_TOKEN" -d '{
      "title": "Transfer learning with transformers",
      "description": "Using transformers for transfer learning on text classification tasks."
    }' $SERVICE_ENDPOINT/predict/
    
    # Rollback (to previous version of the Service)
    anyscale service rollback -f $SERVICE_CONFIG --name $SERVICE_NAME
    
    # Terminate
    anyscale service terminate --name $SERVICE_NAME
    

    CI/CD

    We're not going to manually deploy our application every time we make a change. Instead, we'll automate this process using GitHub Actions!

    1. Create a new github branch to save our changes to and execute CI/CD workloads:
    git remote set-url origin https://github.com/$GITHUB_USERNAME/Made-With-ML.git  # <-- CHANGE THIS to your username
    git checkout -b dev
    
    1. We'll start by adding the necessary credentials to the /settings/secrets/actions page of our GitHub repository.
    export ANYSCALE_HOST=https://console.anyscale.com
    export ANYSCALE_CLI_TOKEN=$YOUR_CLI_TOKEN  # retrieved from https://console.anyscale.com/o/madewithml/credentials
    
    1. Now we can make changes to our code (not on main branch) and push them to GitHub. But in order to push our code to GitHub, we'll need to first authenticate with our credentials before pushing to our repository:
    git config --global user.name $GITHUB_USERNAME  # <-- CHANGE THIS to your username
    git config --global user.email [email protected]  # <-- CHANGE THIS to your email
    git add .
    git commit -m ""  # <-- CHANGE THIS to your message
    git push origin dev
    

    Now you will be prompted to enter your username and password (personal access token). Follow these steps to get personal access token: New GitHub personal access token → Add a name → Toggle repo and workflow → Click Generate token (scroll down) → Copy the token and paste it when prompted for your password.

    1. Now we can start a PR from this branch to our main branch and this will trigger the workloads workflow. If the workflow (Anyscale Jobs) succeeds, this will produce comments with the training and evaluation results directly on the PR.
    1. If we like the results, we can merge the PR into the main branch. This will trigger the serve workflow which will rollout our new service to production!

    Continual learning

    With our CI/CD workflow in place to deploy our application, we can now focus on continually improving our model. It becomes really easy to extend on this foundation to connect to scheduled runs (cron), data pipelines, drift detected through monitoring, online evaluation, etc. And we can easily add additional context such as comparing any experiment with what's currently in production (directly in the PR even), etc.

    FAQ

    Jupyter notebook kernels

    Issues with configuring the notebooks with jupyter? By default, jupyter will use the kernel with our virtual environment but we can also manually add it to jupyter:

    python3 -m ipykernel install --user --name=venv
    

    Now we can open up a notebook → Kernel (top menu bar) → Change Kernel → venv. To ever delete this kernel, we can do the following:

    jupyter kernelspec list
    jupyter kernelspec uninstall venv