📝

MLOps ZoomCamp Week1:MLOps基礎から始める機械学習プロダクション化入門

に公開

# はじめに

個人的には、機械学習を.ipynb形式で扱うことが多いですが、幅広く使われる機械学習(例:ChatGPT)に目を向ければ、
機械学習モデルがシステムの一部として組み込まれていることがほとんどだと思います。

そんな経験がない私は、「モデルは作れたけど、システムに組み込んで動かすにはどうしたらいいの?」
「アプリ化して、保守・運用していく仕組み作りは?」という分からないことだらけです。

ただ、MLOps(Machine Learning Operations) というのがそこにアプローチできる技術?概念?であることは知ってました。

そこで、本記事では、DataTalks.ClubのMLOps ZoomCampの内容をベースに、MLOpsの基礎概念から実際の環境構築までを
まとめていきたいと思います。
(ZoomCampでは、Week1〜Week6となっていますので、それぞれでまとめていきます。)

# MLOpsとは何か

## レストラン経営に例えて理解するMLOps

MLOpsについて理解するために、「レストランの経営」に例えてみます。

従来の機械学習プロジェクトは、優秀なシェフ(データサイエンティスト)が1人で美味しい料理(高精度なモデル)を作るようなものでした。
しかし、規模感の大きいレストランを上手く回していくには、シェフ1人では困難なわけです。

  • 食材の調達(データ収集・前処理)
  • レシピの作成(モデルの再現性)
  • クオリティの維持(モデルの監視)
  • 効率的な調理手順(パイプラインの自動化)
  • 顧客への継続的なサービス提供(本番環境での安定運用)

これら全てを統合管理するのが、レストランマネージャー的な役割を担うMLOpsです。

## MLOpsの定義

改めて、MLOpsは「Machine Learning Operations」の略で、
機械学習システムの開発・デプロイ・運用を効率化・自動化するための実践的なアプローチを指します。
DevOpsの考え方を機械学習領域に適用したものと考えるとイメージしやすいでしょう。

具体的には、以下の要素を含んでいます:

  • 継続的インテグレーション(CI):コードとデータの変更を自動的にテスト
  • 継続的デプロイメント(CD)):モデルの自動的な本番環境への展開
  • 継続的トレーニング(CT):新しいデータでのモデルの自動再学習
  • モニタリング:モデルの性能とデータの変化を監視

# MLOps成熟度モデル

MLOpsの導入レベルは一般的に3段階に分類されます。
ここでは、Google Cloudが提案する成熟度モデルを参考にまとめていきます。

## Level0:手動プロセス

特徴

  • 全ての工程が手動
  • .ipynbでの分析が中心
  • 本番環境への展開は稀で、手動作業

課題:

  • 再現性が低い
  • スケーラビリティがない
  • エラーが起きやすい

## Level1:MLパイプラインの自動化

特徴

  • トレーニングパイプラインの自動化
  • 継続的なモデルの学習
  • 学習結果の追跡と管理

改善点

  • データから学習までが自動化
  • 実験の再現性が向上

## Level2:CI/CDパイプラインの自動化

特徴

  • MLパイプライン自体のCI/CD
  • 自動テストとデプロイメント
  • 継続的監視とA/Bテスト

改善点

  • 完全自動化された信頼性の高いシステム
  • 迅速な実験とフィードバックループ

# NY Taxiデータセット - 実践例の紹介

MLOps ZoomCampでは、ニューヨークのタクシー運行データを使って例にしてますので、そちらを元に実践的に学んでいきたいと思います。

## データセットの概要

# データセットの例
{
    "pickup_datetime": "2021-01-01 00:30:00",
    "dropoff_datetime": "2021-01-01 00:45:00",
    "pickup_location_id": 161,
    "dropoff_location_id": 230,
    "trip_distance": 2.5,
    "fare_amount": 15.0,
    "tip_amount": 3.0,
    "total_amount": 18.0
}

## 課題設定

このデータを使って、以下のような実際のビジネスの課題を解決したいと思います:

  1. 乗車時間の予測:顧客により正確な到着時間を提供
  2. 需要予測:効率的なタクシー配車
  3. 料金最適化:動的料金設定

# 環境セットアップ

実際にMLOpsを実践していく環境を構築していきましょう。

## 必要な前提知識

これから記載していく内容には、以下の前提知識があると理解が早いと思います:

  • Python:基本的なプログラミングスキル
  • Docker:コンテナの基本概念
  • コマンドライン:基本的な操作
  • 機械学習:scikit-learn等の基本的な使用経験
  • プログラミング経験:1年以上の経験

## 環境構築手順

1. Dockerのインストール

# macOS(Homebrew)
brew install --cask docker

2. Pythonと仮想環境の準備

# macOS
brew install miniforge  
# インストール後のメッセージにしたがってください。
# その後、ターミナルを再起動すると、(base)と記載されているはずです。

conda --version  # インストールの確認
conda create -n mlops-zoomcamp python=3.9

3. 必要なライブラリのインストール

conda activate mlops-zoomcamp
conda install pandas scikit-learn jupyter pytest black isort
pip install mlflow prefect flask

4. プロジェクト構造の作成

今回のプロジェクトに用いるディレクトリの構成(git管理も考慮)

# プロジェクトディレクトリの作成
mkdir mlops-taxi-project
cd mlops-taxi-project

# ディレクトリ構造の作成
mkdir -p data/{raw,processed,external}
mkdir -p notebooks
mkdir -p src/{data,models,utils}
mkdir -p tests
mkdir -p models
mkdir -p reports

# 基本ファイルの作成
touch README.md
touch .gitignore
touch environment.yml
mlops-taxi-project/
├── data/
│   ├── raw/              # 元データ
│   ├── processed/        # 前処理済みデータ
│   └── external/         # 外部データ
├── notebooks/            # Jupyter notebooks
├── src/
│   ├── data/            # データ処理スクリプト
│   ├── models/          # モデル関連スクリプト
│   └── utils/           # ユーティリティ
├── tests/               # テストスクリプト
├── models/              # 保存されたモデル
├── reports/             # レポート・図表
├── environment.yml      # Conda環境設定
├── .gitignore          # Git無視ファイル
└── README.md           # プロジェクト説明

.gitignoreファイルの整理:

# .gitignore
__pycache__/
*.py[cod]
*$py.class
.env
.venv
.DS_Store
data/raw/*
models/*.pkl
.ipynb_checkpoints/
mlruns/

5. Dockerについて(Week4で詳しく学習予定)

Week1ではローカル環境で行います。ただし、MLOpsではDockerを組み込むことも大切なので、改めて学習することにします。
ここでは、インストールができているか確認します。

# Dockerがインストールされているか確認
docker --version

Dockerの役割(詳細はWeek4で):

  • モデルのコンテナ化
  • 本番環境での一貫した実行環境
  • マイクロサービスとしてのモデル展開

## 環境確認

# ./tests/test_environment.py
# test_environment.py
import pandas as pd
import sklearn
import mlflow

def test_imports():
    """必要なライブラリがインポートできるかテスト"""
    print("✅ pandas:", pd.__version__)
    print("✅ scikit-learn:", sklearn.__version__)
    print("✅ mlflow:", mlflow.__version__)

def test_conda_environment():
    """Conda環境の確認"""
    import os
    import subprocess
    
    # アクティブな環境名を取得
    conda_env = os.environ.get('CONDA_DEFAULT_ENV', 'None')
    print(f"✅ Active conda environment: {conda_env}")
    
    # Python実行パスの確認
    python_path = subprocess.check_output(['which', 'python']).decode().strip()
    print(f"✅ Python path: {python_path}")
    
    # 環境内のパッケージ数
    result = subprocess.check_output(['conda', 'list']).decode()
    package_count = len([line for line in result.split('\n') if line and not line.startswith('#')])
    print(f"✅ Installed packages: {package_count}")

if __name__ == "__main__":
    print("=== MLOps環境テスト ===")
    test_imports()
    print("\n=== Conda環境テスト ===")
    test_conda_environment()

環境テストの実行:

# 仮想環境をactivate
conda activate mlops-zoomcamp

# テストスクリプトの実行
python test_environment.py

# データの準備と探索的データ分析

ここからは、例題データを用いて、それぞれ動かして確認していきたいと思います。
ファイル形式は .py or .ipynb どちらでも問題ありません。
作成した.ipynbはnotebooks/に格納してください。
(例:notebooks/01_data_exploration.ipynb)

1. NY Taxiデータのダウンロード

# 必要なライブラリのimport
import os
import pandas as pd

# データのダウンロード
url = "https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2021-01.parquet"
df = pd.read_parquet(url)

# ローカルに保存(今後の再利用のため)
df.to_parquet('../data/raw/yellow_tripdata_2021-01.parquet')

print(f"データ形状: {df.shape}")
print(f"列名: {df.columns.tolist()}")
print(f"データサイズ: {df.memory_usage(deep=True).sum() / 1024**2:.1f} MB")

2. 基本的な探索的データ分析

# 必要なライブラリ
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# まず、仮想環境がアクティブになっていることを確認
# conda activate mlops-zoomcamp

import pandas as pd
import numpy as np

# データのダウンロード
df = pd.read_parquet("../data/raw/yellow_tripdata_2021-01.parquet")

print(f"データ形状: {df.shape}")
print(f"列名: {df.columns.tolist()}")

# 基本統計
print("\n=== 基本統計情報 ===")
print(df.describe())

# 欠損値の確認
print("\n=== 欠損値の確認 ===")
print(df.isnull().sum())

# 乗車時間の計算
df['trip_duration'] = (
    df['tpep_dropoff_datetime'] - df['tpep_pickup_datetime']
).dt.total_seconds() / 60

# 異常値の確認
print(f"\n=== 乗車時間の統計 ===")
print(f"平均: {df['trip_duration'].mean():.2f} 分")
print(f"中央値: {df['trip_duration'].median():.2f} 分")
print(f"最小値: {df['trip_duration'].min():.2f} 分")
print(f"最大値: {df['trip_duration'].max():.2f} 分")

plt.style.use('seaborn-v0_8')  # より美しいプロット
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
plt.hist(df['trip_duration'].clip(0, 60), bins=50, alpha=0.7, color='skyblue', edgecolor='black')
plt.xlabel('Trip Duration (minutes)')
plt.ylabel('Frequency')
plt.title('Trip Duration Distribution (0-60 min)')
plt.grid(True, alpha=0.3)

plt.subplot(1, 3, 2)
plt.hist(df['trip_distance'].clip(0, 20), bins=50, alpha=0.7, color='lightgreen', edgecolor='black')
plt.xlabel('Trip Distance (miles)')
plt.ylabel('Frequency')
plt.title('Trip Distance Distribution (0-20 miles)')
plt.grid(True, alpha=0.3)

plt.subplot(1, 3, 3)
# サンプリングして散布図の描画を高速化
sample_size = min(10000, len(df))
df_sample = df.sample(n=sample_size, random_state=42)
plt.scatter(df_sample['trip_distance'], df_sample['trip_duration'], 
           alpha=0.3, s=1, color='coral')
plt.xlabel('Trip Distance (miles)')
plt.ylabel('Trip Duration (minutes)')
plt.title(f'Distance vs Duration (n={sample_size:,})')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig("../reports/img")

# 環境情報の出力
print(f"\n=== 使用環境 ===")
print(f"Python: {pd.__version__}")
print(f"Pandas: {pd.__version__}")
print(f"Numpy: {np.__version__}")

3. 簡単な機械学習モデル

# 必要なライブラリのimport
import numpy as np
from sklearn.ensemble import RandomForestRegressor
from sklearn.linear_model import LinearRegression
from sklearn.metrics import root_mean_squared_error, mean_absolute_error, r2_score
from sklearn.model_selection import train_test_split

# 特徴量とターゲットの準備
df = pd.read_parquet("../data/raw/yellow_tripdata_2021-01.parquet")
df['trip_duration'] = (
    df['tpep_dropoff_datetime'] - df['tpep_pickup_datetime']
).dt.total_seconds() / 60
features = ['trip_distance', 'passenger_count', 'PULocationID', 'DOLocationID']
X = df[features].fillna(0)
y = df['trip_duration'].fillna(0)

# 異常値の除去(より詳細な前処理)
print("=== データクリーニング ===")
print(f"クリーニング前のデータ数: {len(df):,}")

# 合理的な範囲でのフィルタリング
mask = (
    (y > 1) & (y < 120) &  # 1分〜2時間
    (X['trip_distance'] > 0) & (X['trip_distance'] < 50) &  # 0〜50マイル
    (X['passenger_count'] > 0) & (X['passenger_count'] <= 6)  # 1〜6人
)

X_clean, y_clean = X[mask], y[mask]
print(f"クリーニング後のデータ数: {len(X_clean):,}")
print(f"除去されたデータ: {len(df) - len(X_clean):,} ({(len(df) - len(X_clean))/len(df)*100:.1f}%)")

# 訓練・テストデータの分割
X_train, X_test, y_train, y_test = train_test_split(
    X_clean, y_clean, test_size=0.2, random_state=42
)

print(f"\n=== データ分割 ===")
print(f"訓練データ: {len(X_train):,}")
print(f"テストデータ: {len(X_test):,}")

# 複数モデルの比較
models = {
    'Linear Regression': LinearRegression(),
    'Random Forest': RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1)
}

results = {}

print(f"\n=== モデル学習と評価 ===")
for name, model in models.items():
    print(f"\n{name} を学習中...")
    
    # モデルの学習
    model.fit(X_train, y_train)
    
    # 予測
    y_pred = model.predict(X_test)
    
    # 評価指標の計算
    rmse = mean_squared_error(y_test, y_pred, squared=False)
    mae = mean_absolute_error(y_test, y_pred)
    r2 = r2_score(y_test, y_pred)
    
    results[name] = {
        'RMSE': rmse,
        'MAE': mae,
        'R²': r2
    }
    
    print(f"RMSE: {rmse:.2f} minutes")
    print(f"MAE: {mae:.2f} minutes")
    print(f"R²: {r2:.3f}")

# 結果の比較
print(f"\n=== モデル比較 ===")
import pandas as pd
results_df = pd.DataFrame(results).T
print(results_df.round(3))

# 最良モデル
best_model_name = results_df['RMSE'].idxmin()
best_model = models[best_model_name]

print(f"\n最良モデル: {best_model_name}")

# まとめ

Week1では、MLOpsの基礎概念を学び、実践的な環境のセットアップ、簡単な探索的データ分析、モデル構築を行いました:

次回、Week2では、MLflowを用いたモデル管理について学んでいきます。

参考情報

Discussion