mlops-python-package

fmindMIT活跃维护

一个可直接用于生产的 MLOps Python 包模板,集成 MLflow、scikit-learn、Pydantic、Pandera 和 uv,帮你快速搭起带实验追踪与数据校验的机器学习工程骨架。

Production-shaped MLOps Python package and reference implementation of the MLOps Coding Course: MLflow, scikit-learn, Pydantic, Pandera, uv, and mise.

v7.0.2发布于 2026-10-24 10:41 UTC1,421star200fork同步于 2026-11-10 05:54 UTC

怎么上手

由 README 提炼,执行前请确认
git clone [email protected]:fmind/mlops-python-package
cd mlops-python-package/
mise trust && mise install
mise run install
硬件要求按 README 说明

README 未说明显存或 GPU 要求,训练与推理基于 scikit-learn 与 Pandas,可在 CPU 上运行

需要 CUDA有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

一个生产级 MLOps Python 包参考实现,帮你快速搭起带 MLflow 追踪与模型注册的机器学习项目骨架

一个可直接用于生产的 MLOps Python 包模板,集成 MLflow、scikit-learn、Pydantic、Pandera 和 uv,帮你快速搭起带实验追踪与数据校验的机器学习工程骨架。

核心能力

  • 用 Pydantic 校验 YAML 配置并按 Discriminated Unions 自动选任务
  • 用 Pandera 校验 Pandas DataFrame 数据模式
  • 用 MLflow 做实验追踪、模型注册与数据集血缘
  • 用 OmegaConf 解析配置并支持多文件从左到右合并
  • 用 uv 管理依赖与虚拟环境,mise 统一 install/format/check/test/build 任务

容易踩的坑

需要 Python>=3.14、uv>=0.13 和 mise,版本不够会装不上;用 mise 任务时需先把 .env.example 复制为 .env;Docker Compose 直接启动前要先跑 mise run mlflow:db 初始化 SQLite;示例数据需用 notebooks/processing.ipynb 重新生成

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README
这个条目还没有人工点评。如果它对你很有用,欢迎到 提交仓库 页写下你的使用经验。

用户点评

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

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

    最近发版

    5 个版本v7.0.2
    1. v7.0.2最新正式版2026-10-24 · 2026-10-24 10:41 UTC

      [7.0.2] - 2026-10-24

      • Rebuild the published image with the patched dependencies: Pillow 13.0.0, certifi 2026.10.23, GitPython 3.2.1, filelock, MLflow 3.17.2 and setuptools fix critical and high advisories, among them GitPython code execution through submodule paths, an MLflow file-store path traversal and emSign roots with compromised intermediate keys.
    2. v7.0.12026-10-12 · 2026-10-12 19:11 UTC
      查看更新说明

      [7.0.1] - 2026-10-12

      • Separate the Debian license inventory from the vulnerability, secret and misconfiguration image gate.
    3. v6.1.02026-10-06 · 2026-10-06 11:43 UTC
      查看更新说明

      [6.1.0] - 2026-10-06

      🚀 Features

      • (io) Save custom models without pickle

      🐛 Bug Fixes

      • (io) Pass flavor-specific options through BuiltinSaver
      • (deps) Replace the deprecated pynvml shim with nvidia-ml-py
      • (io) Pass autolog_log_models to MLflow autolog
      • (core) Round predictions instead of truncating them
      • (jobs) Accept only the custom loader in ExplanationsJob
      • (jobs) Explain a promotion without registered versions
      • (utils) Type TimeSeriesSplitter.test_size as an int
      • (deps) Restore the shap 0.52 wheel for Linux
      • (docker) Build on the base image interpreter and smoke-test it
      • (mise) Redact leaks, repair the upgrade task, drop jq
      • (compose) Bind the MLflow server to localhost and track its image
      • (jobs) Skip the evaluator's SHAP step in EvaluationsJob
      • (core) Stop training on features that add up to the target
      • (io) Redact credentials from logged MLflow URIs
      • (confs) Evaluate the champion on the test split
      • Surface non-MLflow warnings and pass an explicit lineage source
      • (docker) Create the app user without the system-account flag
      • (io) Keep plyer backend probe warnings out of alerts

      ⚡ Performance

      • (check) Skip MLflow artifacts in the trivy scan
      • (jobs) Log a five-row input example with trained models

      ♻️ Refactor

      • (jobs) Evaluate with mlflow.models.evaluate

      📚 Documentation

      • Sync README and AGENTS.md with the package

      🧪 Testing

      • Keep MLflow test artifacts out of the repository
      • Mock plyer without loading its backend and fix assertions

      ⚙️ Build & CI

      • Scan with the check:scan task and explain dirty trees

      🧹 Miscellaneous

      • (mise) Drop stale short-name lock entries
      • Head configs with their docs and add a search ignore file

      Upgrade notes: the model no longer reads casual and registered, which add up to the target; retrain and re-promote models registered before 6.1.0. New models are logged without pickle (MLflow models from code + skops). Dependencies are on their latest releases, including pandas 3, and plotly moved to the notebook group.

      Validation: 64 tests passed, three existing expected failures, and 100% coverage; the full local gate, the Docker image build, and all six MLflow jobs pass, and CI is green on the release commit. Dependency audits report no findings.

    4. v6.0.12026-10-03 · 2026-10-03 04:50 UTC
      查看更新说明

      [6.0.1] - 2026-10-03

      🐛 Bug Fixes

      • (ci) Pin the security workflow runner to ubuntu-24.04 (#134)
      • (io) Handle Windows alerts and validate MLflow inputs (#142)

      Validation: 57 tests passed, three existing expected failures, and 100% coverage; the full local gate and fresh wheel installation pass. The configured dependency audits report no findings.

      Thanks to FlyH010 for the contribution in #142.

    5. v6.0.02026-08-10 · 2026-08-10 16:53 UTC
      查看更新说明

      [6.0.0] - 2026-08-10

      🚀 Features

      • [breaking] Track MLflow in SQLite and harden the canonical gate (#129)

      🐛 Bug Fixes

      • (check) Stop trivy from scanning caches and racing pip-audit (#132)

    README 原文

    未翻译

    MLOps Python Package

    ci.yml cd.yml Documentation License Release

    This repository contains a Python code base with best practices designed to support your MLOps initiatives.

    The package leverages several tools and tips to make your MLOps experience as flexible, robust, productive as possible.

    You can use this package as part of your MLOps toolkit or platform (e.g., Model Registry, Experiment Tracking, Realtime Inference, ...).

    Related Resources:

    Table of Contents

    Install

    This section details the requirements, actions, and next steps to kickstart your MLOps project.

    Prerequisites

    Installation

    1. Clone this GitHub repository on your computer
    # with ssh (recommended)
    $ git clone [email protected]:fmind/mlops-python-package
    # with https
    $ git clone https://github.com/fmind/mlops-python-package
    
    1. Run the project installation with mise
    cd mlops-python-package/
    mise trust && mise install
    mise run install
    
    1. Adapt the code base to your desire

    Next Steps

    Going from there, there are dozens of ways to integrate this package to your MLOps platform.

    For instance, you can use Databricks or AWS as your compute platform and model registry.

    It's up to you to adapt the package code to the solution you target. Good luck champ!

    Reference data and evaluation

    The bundled data comes from the UCI Bike Sharing dataset (data/hour.csv; attribution is in data/Readme.txt). The target cnt is the number of rentals per hour. casual and registered add up to the target and remain only in the raw CSV for analysis. The processing notebook removes them from every input Parquet file, so training and serving share a feature-only InputsSchema. Existing registered models keep their original signatures: retrain and register a new version before serving count-free inputs. Keep chronological splits and use only the training period for tuning and diagnostic curves.

    The PipelineJob in confs/pipeline.yaml passes the full tuned model configuration and selected parameters into training, records the tuning run ID on the training run, and evaluates the exact registered candidate version on heldout data. It changes the Champion alias only after the thresholds pass. Inference and explanations then use that same version, even if another run registers a newer candidate.

    Targets and predictions remain unsigned counts in the domain schemas. Evaluation converts them to floating point at the metric boundary so negative residuals cannot wrap into large positive errors.

    Usage

    This section explains how to configure the project code and execute it on your system.

    Configuration

    You can add or edit config files in the confs/ folder to change the program behavior.

    # confs/training.yaml
    job:
      KIND: TrainingJob
      inputs:
        KIND: ParquetReader
        path: data/inputs_train.parquet
      targets:
        KIND: ParquetReader
        path: data/targets_train.parquet
    

    This config file instructs the program to start a TrainingJob with 2 parameters:

    • inputs: dataset that contains the model inputs
    • targets: dataset that contains the model target

    You can find all the parameters of your program in the src/bikes/jobs/*.py files.

    You can also print the full schema supported by this package using uv run bikes --schema.

    MLflow URI precedence is explicit YAML configuration, then MLFLOW_TRACKING_URI / MLFLOW_REGISTRY_URI, then local SQLite. Without a registry override, the registry follows the effective tracking URI. Copy .env.example to .env when using mise tasks. For Docker Compose, use mise run docker:compose: it initializes the SQLite file before mounting it; direct Compose startup requires mise run mlflow:db first.

    Run notebooks with notebooks/ as the working directory and the project's .venv as the kernel. Run training before indicators.ipynb, and the explanations job before explain.ipynb. The indicators notebook follows the MLflow URI environment variables and defaults to the project's SQLite database.

    Execution

    The project code can be executed with uv during your development:

    uv run bikes confs/pipeline.yaml
    # Standalone stages remain available. Evaluate the recorded candidate first.
    uv run bikes confs/training.yaml
    uv run bikes confs/evaluations.yaml --extras '{"job": {"alias_or_version": 1}}'
    MODEL_VERSION=1 uv run bikes confs/promotion.yaml
    

    In production, you can build, ship, and run the project as a Python package:

    uv build
    uv publish # optional
    python -m pip install dist/bikes-*.whl
    bikes confs/inference.yaml
    

    You can also use this package as a library. This example expects the bundled Parquet inputs (regenerate them with notebooks/processing.ipynb when needed):

    from bikes import jobs
    
    from bikes.io import datasets
    
    job = jobs.TrainingJob(
        inputs=datasets.ParquetReader(path="data/inputs_train.parquet"),
        targets=datasets.ParquetReader(path="data/targets_train.parquet"),
    )
    with job as runner:
        runner.run()
    

    Additional tips:

    • You can pass extra configs from the command line using the --extras flag
      • Use it to pass runtime values (e.g., a result from previous job executions)
    • You can pass several config files in the command-line to merge them from left to right
      • You can define common configurations shared between jobs (e.g., model params)
    • The right job task will be selected automatically thanks to Pydantic Discriminated Unions
      • This is a great way to run any job supported by the application (training, tuning, ...)

    Automation

    This project includes several automation tasks to easily repeat common actions.

    AI agents: read AGENTS.md for the full stack, task vocabulary, and conventions before contributing.

    You can run the tasks from the command-line or VS Code extension.

    # execute the project DAG
    $ mise run project
    # build a code archive
    $ mise run build
    # list other actions
    $ mise tasks
    

    Use mise tasks for the current list. The task definitions live in mise.toml.

    Workflows

    This package supports three GitHub Workflows in .github/workflows:

    • ci.yml: run mise run all (format, check, test, build) and a clean-tree check on each push to main and Pull Request
    • cd.yml: run the complete release gate, publish API docs to GitHub Pages, and scan a candidate Docker image before assigning the release and latest tags to its immutable digest.
    • security.yml: scan the full Git history and checkout with gitleaks and trivy every week

    You can use and extend these workflows to automate repetitive package management tasks.

    Tools

    This sections motivates the use of developer tools to improve your coding experience.

    Automation

    Pre-defined actions to automate your project development.

    AI Assistant: Gemini Code Assist

    • Motivations:
      • Increase your coding productivity
      • Get code suggestions and completions
      • Reduce the time spent on reviewing code
    • Limitations:
      • Can generate wrong code, reviews, or summaries

    Commits: git-cliff

    • Motivations:
    • Limitations:
      • Requires disciplined commit messages
    • Alternatives:
      • Do It Yourself (DIY)

    Dependabot: Dependabot

    • Motivations:
      • Fix vulnerable dependencies with a ready pull request
      • Group the fixes into one pull request per ecosystem
      • Skip the noise of routine version bumps (version updates are off)
    • Limitations:
      • Can break your code
      • Covers only ecosystems with security advisories (not Docker images)
    • Alternatives:
      • Do It Yourself (DIY)

    Git Hooks: Lefthook

    • Motivations:
      • Check your code locally before a commit
      • Avoid wasting resources on your CI/CD
      • Thin hooks that delegate to mise run tasks
    • Limitations:
      • Add overhead before your commit
    • Alternatives:
      • Git Hooks: native but less convenient to use

    Tasks: Mise

    • Motivations:
      • Automate project workflows
      • One task vocabulary shared by git hooks and CI
      • Also manages the project toolchain (Python, uv, dprint, gitleaks, trivy, hadolint, actionlint, zizmor, git-cliff)
    • Limitations:
      • Not familiar to most developers
    • Alternatives:
      • Make: most popular, but awful syntax
      • Task: YAML task runner without toolchain management

    CI/CD

    Execution of automated workflows on code push and releases.

    Runner: GitHub Actions

    • Motivations:
      • Native on GitHub
      • Simple workflow syntax
      • Lots of configs if needed
      • Runs one command, mise run all, so CI and local hooks cannot disagree
    • Limitations:
      • SaaS Service
    • Alternatives:
      • GitLab: can be installed on-premise

    Workflow Linting: actionlint + zizmor

    • Motivations:
      • Workflows are code: actionlint checks their schema, expressions, and embedded shell
      • zizmor audits them for security (template injection, credential persistence, cache poisoning)
      • Both run offline inside mise run check:actions, so a broken workflow fails before it is pushed
    • Limitations:
      • Actions are pinned to full commit SHAs; version comments make updates reviewable under the default zizmor policy
    • Alternatives:
      • Discovering the mistake from a red run on main

    CLI

    Integrations with the Command-Line Interface (CLI) of your system.

    Parser: Argparse

    • Motivations:
      • Provide CLI arguments
      • Included in Python runtime
      • Sufficient for providing configs
    • Limitations:
      • More verbose for advanced parsing
    • Alternatives:
      • Typer: code typing for the win
      • Fire: simple but no typing
      • Click: more verbose

    Logging: Loguru

    • Motivations:
      • Show progress to the user
      • Work fine out of the box
      • Saner logging syntax
    • Limitations:
      • Doesn't let you deviate from the base usage
    • Alternatives:
      • Logging: available by default, but feel dated

    Code

    Edition, validation, and versioning of your project source code.

    Coverage: Coverage

    • Motivations:
      • Report code covered by tests
      • Identify code path to test
      • Show maturity to users
      • Measured through Pytest Cov, with a 100% gate (--cov-fail-under=100)
    • Limitations:
      • None
    • Alternatives:
      • None

    Editor: VS Code

    • Motivations:
      • Open source
      • Free, simple, open source
      • Great plugins for Python development
    • Limitations:
      • Require some configuration for Python
    • Alternatives:
      • PyCharm: provide a lot, cost a lot
      • Vim: I love it, but there is a VS Code plugin
      • Spacemacs: I love it even more, but not everybody loves LISP

    Formatting: Ruff

    • Motivations:
      • Super fast compared to others
      • Don't waste time arranging your code
      • Make your code more readable/maintainable
    • Limitations:
      • Still in version 0.x, but more and more adopted
    • Alternatives:
      • YAPF: more config options that you don't need
      • Isort + Black: slower and need two tools

    Quality: Ruff

    • Motivations:
    • Limitations:
      • None
    • Alternatives:
      • PyLint: too slow and too complex system
      • Flake8: too much plugins, I prefer Pylint in practice

    Security: Ruff

    • Motivations:
      • Detect security issues with the S (flake8-bandit) rules
      • Complemented by Trivy, gitleaks, and pip-audit in mise run check
      • No extra tool to install: folded into the linter
    • Limitations:
      • None
    • Alternatives:
      • Bandit: the standalone tool the S rules are based on

    Testing: Pytest

    • Motivations:
      • Write tests or pay the price
      • Super easy to write new test cases
      • Tons of good plugins (xdist, sugar, cov, ...)
    • Limitations:
      • Doesn't support parallel execution out of the box
    • Alternatives:

    Typing: ty

    • Motivations:
      • Static typing is cool!
      • Extremely fast, written in Rust by Astral
      • Same maintainers and ecosystem as Ruff and uv
    • Limitations:
      • Pre-1.0, still stabilizing its type-checking rules
    • Alternatives:
      • PyRight: check big code base by MicroSoft
      • PyType: check big code base by Google
      • Pyre: check big code base by Facebook

    Versioning: Git

    • Motivations:
      • If you don't version your code, you are a fool
      • Most popular source code manager (what else?)
      • Provide hooks to perform automation on some events
    • Limitations:
    • Alternatives:
      • Mercurial: loved it back then, but git is the only real option

    Configs

    Manage the configs files of your project to change executions.

    Format: YAML

    • Motivations:
      • Change execution without changing code
      • Readable syntax, support comments
      • Allow to use OmegaConf <3
    • Limitations:
      • Not supported out of the box by Python
    • Alternatives:
      • JSON: no comments, more verbose
      • TOML: less suited to config merge/sharing

    Parser: OmegaConf

    • Motivations:
      • Parse and merge YAML files
      • Powerful, doesn't get in your way
      • Achieve a lot with few lines of code
    • Limitations:
      • Do not support remote files (e.g., s3, gcs, ...)
    • Alternatives:
      • Hydra: powerful, but gets in your way
      • DynaConf: more suited for app development

    Validator: Pydantic

    • Motivations:
      • Validate your config before execution
      • Pydantic should be builtin (period)
      • Super charge your Python class
    • Limitations:
      • None
    • Alternatives:
      • Dataclass: simpler, but much less powerful
      • Attrs: no validation, less intuitive to use

    Formatter: dprint

    • Motivations:
      • Format JSON, Markdown, TOML, and YAML files
      • Fast, pluggable formatter written in Rust
      • Keep configs and docs consistent alongside Ruff for Python
    • Limitations:
      • Separate tool from the Python formatter (Ruff)
    • Alternatives:
      • Prettier: broader language support, but slower and Node-based

    Data

    Define the datasets to provide data inputs and outputs.

    Container: Pandas

    • Motivations:
      • Load data files in memory
      • Lingua franca for Python
      • Most popular options
    • Limitations:
    • Alternatives:
      • Polars: faster, saner, but less integrations
      • Pyspark: powerful, popular, distributed, so much overhead
      • Dask, Ray, Modin, Vaex, ...: less integration (even if it looks like pandas)

    Format: Parquet

    • Motivations:
      • Store your data on disk
      • Column-oriented (good for analysis)
      • Much more efficient and saner than text based
    • Limitations:
      • None
    • Alternatives:
      • CSV: human readable, but that's the sole benefit
      • Avro: good alternative for row-oriented workflow

    Schema: Pandera

    • Motivations:
      • Typing for dataframe
      • Communicate data fields
      • Support pandas and others
    • Limitations:
      • None
    • Alternatives:

    Docs

    Generate and share the project documentations.

    API: pdoc

    • Motivations:
      • Share docs with others
      • Simple tool, only does API docs
      • Get the job done, get out of your way
    • Limitations:
      • Only support API docs (i.e., no custom docs)
    • Alternatives:
      • Sphinx: More complete, overkill for simple projects
      • Mkdocs: More complete, but requires more setup

    Format: Google

    • Motivations:
      • Common style for docstrings
      • Most writeable out of alternatives
      • I often write a single line for simplicity
    • Limitations:
      • None
    • Alternatives:

    Hosting: GitHub Pages

    • Motivations:
      • Easy to setup
      • Free and simple
      • Integrated with GitHub
    • Limitations:
      • Only support static content
    • Alternatives:

    Model

    Toolkit to handle machine learning models.

    Evaluation: Scikit-Learn Metrics

    • Motivations:
      • Bring common metrics
      • Avoid reinventing the wheel
      • Avoid implementation mistakes
    • Limitations:
      • Limited set of metric to be chosen
    • Alternatives:
      • Implement your own: for custom metrics

    Format: Mlflow Model

    • Motivations:
      • Standard ML format
      • Store model dependencies
      • Strong community ecosystem
      • No pickle: the PyFunc adapter is logged as code (models from code) and the fitted pipeline with skops, which only reloads trusted types
    • Limitations:
      • None
    • Alternatives:

    Registry: Mlflow Registry

    • Motivations:
      • Save and load models
      • Separate production from consumption
      • Popular, open source, work on local system
    • Limitations:
      • None
    • Alternatives:

    Tracking: Mlflow Tracking

    • Motivations:
      • Keep track of metrics and params
      • Allow to compare model performances
      • Popular, open source, work on local system
    • Limitations:
      • None
    • Alternatives:

    MLflow 3: This package uses SQLite (sqlite:///mlflow.db) for tracking and registry metadata, with artifacts under ./mlruns. The file-store backend is disabled by default in current MLflow releases. Browse local runs with mise run mlflow:serve. Moving to a shared service also requires artifact access, authentication, and database operations; changing the URI configures the client connection.

    Package

    Define and build modern Python package.

    Evolution: Changelog

    • Motivations:
    • Limitations:
      • None
    • Alternatives:
      • None

    Format: Wheel

    • Motivations:
    • Limitations:
      • Doesn't ship with C/C++ dependencies (e.g., CUDA)
        • i.e., use Docker containers for this case
    • Alternatives:
      • Source: older format, less powerful
      • Conda: slow and hard to manage

    Manager: uv

    • Motivations:
      • Define and build Python package (with the uv_build backend)
      • Fast and compliant package manager
      • Pack every metadata in a single static file
    • Limitations:
      • Cannot add dependencies beyond Python (e.g., CUDA)
        • i.e., use Docker container for this use case
    • Alternatives:

    Runtime: Docker

    The runtime image contains the installed package and locked dependencies. It omits pip and its bootstrap wheels; rebuild the image when dependencies change.

    • Motivations:
      • Create isolated runtime
      • Container is the de facto standard
      • Package C/C++ dependencies with your project
    • Limitations:
      • Some company might block Docker Desktop, you should use alternatives
    • Alternatives:
      • Conda: slow and heavy resolver

    Image Linting: hadolint

    • Motivations:
      • Catches Dockerfile mistakes (unpinned bases, root users, unresolvable uids) before a build
      • Runs offline in under a second inside mise run check:dockerfile
    • Limitations:
      • Lints the recipe; trivy scans the checkout and the release workflow scans the built image before promoting its digest
    • Alternatives:

    Programming

    Select your programming environment.

    Language: Python

    • Motivations:
      • Great language for AI/ML projects
      • Robust with additional tools
      • Hundreds of great libs
    • Limitations:
      • Slow without C bindings
    • Alternatives:
      • R: specific purpose language
      • Julia: specific purpose language

    Version: Uv

    • Motivations:
      • Switch between Python version
      • Allow to select the best version
      • Support global and local dispatch
      • Pinned in .python-version (read by uv) and in the mise.toml toolchain
    • Limitations:
      • Keep both pins in sync when you upgrade Python
    • Alternatives:
      • Manual installation: time consuming
      • PyEnv: shell-based, require more setup

    Observability

    Reproducibility: Mlflow Project

    • Motivations:
      • Share common project formats.
      • Ensure the project can be reused.
      • Avoid randomness in project execution.
    • Limitations:
      • Mlflow Project is best suited for small projects.
    • Alternatives:

    Run an MLflow Project job with mise run project:run <job> (e.g. mise run project:run training); it passes --env-manager=local to reuse the uv-managed environment instead of creating a new one.

    Monitoring : Mlflow Evaluate

    • Motivations:
      • Compute the model metrics.
      • Validate model with thresholds.
      • Perform post-training evaluations.
    • Limitations:
      • Mlflow Evaluate is less feature-rich as alternatives.
    • Alternatives:
      • Giskard: open-core and super complete.
      • Evidently: open-source with more metrics.
      • Arize AI: more feature-rich but less flexible.
      • Grafana: you must do everything yourself.

    Alerting: Plyer

    • Motivations:
      • Simple solution.
      • Send notifications on system.
      • Cross-system: Mac, Linux, Windows.
    • Limitations:
      • Should not be used for large scale projects.
    • Alternatives:
      • Slack: for chat-oriented solutions.
      • Datadog: for infrastructure oriented solutions.

    Lineage: Mlflow Dataset

    • Motivations:
      • Store information in Mlflow.
      • Track metadata about run datasets.
      • Keep URI of the dataset source (e.g., website).
    • Limitations:
      • Not as feature-rich as alternative solutions.
    • Alternatives:

    Explainability: SHAP

    • Motivations:
      • Most popular toolkit.
      • Support various models (linear, model, ...).
      • Integration with Mlflow through the SHAP module.
    • Limitations:
      • Super slow on large dataset.
      • Mlflow SHAP module is not mature enough.
    • Alternatives:
      • LIME: not maintained anymore.

    Infrastructure: Mlflow System Metrics

    • Motivations:
      • Track infrastructure information (RAM, CPU, ...).
      • Integrated with Mlflow tracking.
      • Provide hardware insights.
    • Limitations:
      • Not as mature as alternative solutions.
    • Alternatives:
      • Datadog: popular and mature solution.

    Tips

    This sections gives some tips and tricks to enrich the develop experience.

    AI/ML Practices

    Data Catalog

    You should decouple the pointer to your data from how to access it.

    In your code, you can refer to your dataset with a tag (e.g., inputs, targets).

    This tag can then be associated to a reader/writer implementation in a configuration file:

    inputs:
      KIND: ParquetReader
      path: data/inputs_train.parquet
    targets:
      KIND: ParquetReader
      path: data/targets_train.parquet
    

    In this package, the implementation are described in src/bikes/io/datasets.py and selected by KIND.

    Hyperparameter Optimization

    You should select the best hyperparameters for your model using optimization search.

    The simplest projects can use a sklearn.model_selection.GridSearchCV to scan the whole search space.

    This package provides a simple interface to this hyperparameter search facility in src/bikes/utils/searchers.py.

    For more complex project, we recommend to use more complex strategy (e.g., Bayesian) and software package (e.g., Optuna).

    Data Splits

    You should properly split your dataset into a training, validation, and testing sets.

    • Training: used for fitting the model parameters
    • Validation: used to find the best hyperparameters
    • Testing: used to evaluate the final model performance

    The sets should be exclusive, and the testing set should never be used as training inputs!

    This package provides a simple deterministic strategy implemented in src/bikes/utils/splitters.py.

    Design Patterns

    Directed-Acyclic Graph

    You should use Directed-Acyclic Graph (DAG) to connect the steps of your ML pipeline.

    A DAG can express the dependencies between steps while keeping the individual step independent.

    The project task in mise.toml runs PipelineJob from confs/pipeline.yaml: tuning → training → holdout evaluation → promotion → inference → explanations. Stage failures propagate; a failed evaluation preserves the existing champion. Selected parameters are saved as selected_params.json in the tuning run. Standalone promotion requires a positive explicit version; it never selects the latest version implicitly.

    In production, we recommend to use a scalable system such as Airflow, Dagster, Prefect, Metaflow, or ZenML.

    Program Service

    You should provide a global context for the execution of your program.

    There are several approaches such as Singleton, Global Variable, or Component.

    This package takes inspiration from Clojure mount. It provides an implementation in src/bikes/io/services.py.

    Soft Coding

    You should separate the program implementation from the program configuration.

    Exposing configurations to users allow them to influence the execution behavior without code changes.

    This package seeks to expose as much parameter as possible to the users in configurations stored in the confs/ folder.

    SOLID Principles

    You should implement the SOLID principles to make your code as flexible as possible.

    • Single responsibility principle: Class has one job to do. Each change in requirements can be done by changing exactly one class.
    • Open/closed principle: Class is happy (open) to be used by others. Class is not happy (closed) to be changed by others.
    • Liskov substitution principle: Class can be replaced by any of its children. Children classes inherit parent's behaviours.
    • Interface segregation principle: When classes promise each other something, they should separate these promises (interfaces) into many small promises, so it's easier to understand.
    • Dependency inversion principle: When classes talk to each other in a very specific way, they both depend on each other to never change. Instead classes should use promises (interfaces, parents), so classes can change as long as they keep the promise.

    In practice, this mean you can implement software contracts with interface and swap the implementation.

    For instance, you can implement several jobs in src/bikes/jobs/*.py and swap them in your configuration.

    To learn more about the mechanism select for this package, you can check the documentation for Pydantic Tagged Unions.

    IO Separation

    You should separate the code interacting with the external world from the rest.

    The external is messy and full of risks: missing files, permission issue, out of disk ...

    To isolate these risks, you can put all the related code in an io package and use interfaces.

    Python Powers

    Context Manager

    You should use Python context manager to control and enhance an execution.

    Python provides contexts that can be used to extend a code block. For instance:

    # in src/bikes/scripts.py
    with setting.job as runner:  # validated configuration
        runner.run()  # run in context
    

    This pattern has the same benefit as Monad, a powerful programming pattern.

    The package uses src/bikes/jobs/*.py to handle exception and services.

    Python Package

    You should create Python package to create both library and application for others.

    Using Python package for your AI/ML project has the following benefits:

    • Build code archive (i.e., wheel) that be uploaded to Pypi.org
    • Install Python package as a library (e.g., like pandas)
    • Expose script entry points to run a CLI or a GUI

    To build a Python package with uv, you simply have to type in a terminal:

    # for all uv project
    uv build
    # for this project only
    mise run build
    

    Software Engineering

    Code Typing

    You should type your Python code to make it more robust and explicit for your user.

    Python provides the typing module for adding type hints and ty to checking them.

    # in src/bikes/core/models.py
    @abc.abstractmethod
    def fit(self, inputs: schemas.Inputs, targets: schemas.Targets) -> T.Self:
        """Fit the model on the given inputs and target."""
    
    
    @abc.abstractmethod
    def predict(self, inputs: schemas.Inputs) -> schemas.Outputs:
        """Generate an output with the model for the given inputs."""
    

    This code snippet clearly state the inputs and outputs of the method, both for the developer and the type checker.

    The package aims to type every functions and classes to facilitate the developer experience and fix mistakes before execution.

    Config Typing

    You should type your configuration to avoid exceptions during the program execution.

    Pydantic allows to define classes that can validate your configs during the program startup.

    # in src/bikes/utils/splitters.py
    class TrainTestSplitter(Splitter):
        shuffle: bool = False  # required (time sensitive)
        test_size: int | float = 24 * 30 * 2  # 2 months
        random_state: int = 42
    

    This code snippet allows to communicate the values expected and avoid error that could be avoided.

    The package combines both OmegaConf and Pydantic to parse YAML files and validate them as soon as possible.

    Dataframe Typing

    You should type your dataframe to communicate and validate their fields.

    Pandera supports dataframe typing for Pandas and other library like PySpark:

    Selected fields from the complete schemas:

    class InputsSchema(Schema):
        instant: papd.Index[papd.UInt32] = pa.Field(ge=0, unique=True)
        hr: papd.Series[papd.UInt8] = pa.Field(ge=0, le=23)
        temp: papd.Series[papd.Float16] = pa.Field(ge=0, le=1)
    

    The shared schema parsers reject fractional or overflowing integer values and invalid booleans before coercion. This excerpt needs the module's imports and base schema.

    This code snippet defines the fields of the dataframe and some of its constraint.

    The package encourages to type every dataframe used in src/bikes/core/schemas.py.

    Object Oriented

    You should use the Objected Oriented programming to benefit from polymorphism.

    Polymorphism combined with SOLID Principles allows to easily swap your code components.

    class Reader(abc.ABC, pdt.BaseModel):
        @abc.abstractmethod
        def read(self) -> pd.DataFrame:
            """Read a dataframe from a dataset."""
    

    This code snippet uses the abc module to define code interfaces for a dataset with a read/write method.

    The package defines class interface whenever possible to provide intuitive and replaceable parts for your AI/ML project.

    Semantic Versioning

    You should use semantic versioning to communicate the level of compatibility of your releases.

    Semantic Versioning (SemVer) provides a simple schema to communicate code changes. For package X.Y.Z:

    • Major (X): major release with breaking changed (i.e., imply actions from the benefit)
    • Minor (Y): minor release with new features (i.e., provide new capabilities)
    • Patch (Z): patch release to fix bugs (i.e., correct wrong behavior)

    Uv and this package leverage Semantic Versioning to let developers control the speed of adoption for new releases.

    Testing Tricks

    Parallel Testing

    You can run your tests in parallel to speed up the validation of your code base.

    Pytest can be extended with the pytest-xdist plugin for this purpose.

    This package runs it with mise run test:parallel (no coverage); the test gate stays serial because pytest-cov and pytest-xdist deadlock on this suite.

    Test Fixtures

    You should define reusable objects and actions for your tests with fixtures.

    Fixture can prepare objects for your test cases, such as dataframes, models, files.

    This package defines fixtures in tests/conftest.py to improve your testing experience.

    VS Code

    Code Workspace

    You can use VS Code workspace to define configurations for your project.

    Code Workspace can enable features (e.g. formatting) and set the default interpreter.

    {
      "settings": {
        "editor.formatOnSave": true,
        "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
      }
    }
    

    This package defines a workspace file that you can load from mlops-python-package.code-workspace.

    GitHub Copilot

    • Editor assistance can reduce repetitive work; measure its value on your own tasks.

    GitHub Copilot has been a huge productivity boost thanks to its smart completion.

    You should become familiar with the solution in less than a single coding session.

    VSCode VIM

    You can use VIM keybindings to more efficiently navigate and modify your code.

    • Editor assistance can reduce repetitive work; measure its value on your own tasks.

    Compared to GitHub Copilot, VIM can take much more time to master. Measure whether the navigation gains repay the learning time for your work.

    Resources

    This section provides resources for building packages for Python and AI/ML/MLOps.

    Python

    AI/ML/MLOps

    mise run clean removes caches and build outputs while preserving MLflow databases, run artifacts, and predictions. Back up experiment state before any deliberate manual reset.

    Use mise run mlflow:serve to browse experiments created by local jobs: their artifact URIs contain host paths. Compose can serve metadata from the same SQLite file, but those host paths are not automatically available inside the container. For new HTTP-first experiments, configure clients to reach the server and choose server-accessible artifact storage.

    The release image gate checks vulnerabilities, secrets and misconfigurations. It records license classifications separately: the Debian base includes reciprocal licenses that require an inventory, rather than a blanket license prohibition.