Machine Learningをゼロから始める:理論から実践まで scikit-learnで学ぶ

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

背景 — DevOpsエンジニアもMachine Learningを知るべき理由

正直に言うと、以前はMLにそれほど興味がありませんでした。それはデータサイエンティストの仕事で、自分はdeployとmonitorだけやっていれば十分だと思っていたのです。しかし、あるスプリントで顧客チャーン予測機能をpipelineに組み込む必要が生じ、そのmodelをREST APIにラップしてproductionに展開するのが自分の担当になりました。その時に初めて痛感しました:MLの仕組みを理解していなければ、modelが奇妙な結果を出した理由を説明できず、何をmonitorすべきかもわからず、いつretrainすべきかもわからないのです。

Machine Learningは多くの人が思うほど難解ではありません。コアとなるアイデアはとてもシンプルです:コンピューターにデータから学習させて予測を行わせるif email が "割引" を含む then spamのようなハードコードされたルールを書く代わりに、過去のデータを使ってコンピューターに自動でpatternを見つけさせるのです。

最もよく使われるMLの3種類:

  • Supervised learning:ラベル付きデータから学習します。例:住宅価格の予測、スパムメールの分類。
  • Unsupervised learning:ラベルなしデータから自動でpatternを見つけます。例:購買行動によるカスタマーのクラスタリング。
  • Reinforcement learning:Agentが試行錯誤を通じて学習し、reward/penaltyを受け取ります。ゲームAI、自律ロボットのナビゲーションなどに応用されています。

初心者の方は、まずsupervised learningに集中することをお勧めします — 結果を明確に測定でき、toolingも充実しており、ドキュメントも他の2種類と比べて格段に多いです。

ML環境のセットアップ

システム要件

Python 3.9+で十分です。最初からvirtualenvを使いましょう — 些細なことに聞こえるかもしれませんが、後々プロジェクト間のパッケージ競合から救われます。

# virtualenvを作成
python3 -m venv ml-env
source ml-env/bin/activate  # Linux/macOS
# ml-env\Scripts\activate   # Windows

# 必要なライブラリをインストール
pip install scikit-learn pandas numpy matplotlib joblib

インストールの確認

import sklearn
import pandas as pd
import numpy as np

print(f"scikit-learn: {sklearn.__version__}")
print(f"pandas: {pd.__version__}")
print(f"numpy: {np.__version__}")

エラーなく動けば問題ありません。自分はproductionでscikit-learn 1.4.xを使っています — APIは安定しており、マイナーバージョン間でbreaking changeがほとんどありません。

最初のモデルを構築する — 基本的な分類

ここではIrisデータセットを使います — 4つのfeature(花びらと萼の長さ・幅)を持つ150個の花のサンプルで、3種に分類されます。あまり実用的ではありませんが、data cleaningに迷わずflowを全体的に理解するには十分です。

データの読み込みと探索

from sklearn.datasets import load_iris
import pandas as pd

# データセットを読み込む
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['target'] = iris.target
df['species'] = df['target'].map({0: 'setosa', 1: 'versicolor', 2: 'virginica'})

print(df.head())
print(df.describe())
print(df['species'].value_counts())

各種50サンプルずつが出力されます — 完全にバランスの取れたデータセットで、追加処理は必要ありません。実際の業務ではそうはいきませんが、それはfeature engineeringの話です。

Train/Testの分割

このステップは非常に重要で、初心者が最もよく見落とすところです。同じデータでtrainとevaluateを行うと、modelがそのデータを「記憶」して虚偽の良い結果を報告します — これをoverfittingと呼びます。productionにdeployして初めて問題に気付くことになります。

from sklearn.model_selection import train_test_split

X = iris.data   # Features(数値列×4)
y = iris.target # Labels (0, 1, 2)

# 80%をtrain、20%をtest — random_stateで結果の再現性を確保
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

print(f"Train size: {len(X_train)}")  # 120
print(f"Test size: {len(X_test)}")    # 30

stratify=yパラメータは、trainとtest setでのclassの比率を同じにすることを保証します。データが不均衡な場合に特に重要です — たとえばデータセットのclass 0が90%を占める場合、stratifyしないとtest setが全てclass 0になり、modelが何も学習していないのにaccuracyが90%と報告されることがあります。

Random ForestでModelをトレーニングする

Random Forestを選んだのは名前が格好いいからではなく、robustで、tuningがほとんど不要で、ベースラインでも大抵90%以上を達成できるからです。入門に最適です。

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report

# モデルを初期化
model = RandomForestClassifier(n_estimators=100, random_state=42)

# トレーニング — 1行で完了
model.fit(X_train, y_train)

# テストセットで予測
y_pred = model.predict(X_test)

# 評価
print(f"Accuracy: {accuracy_score(y_test, y_pred):.2%}")
print("\nClassification Report:")
print(classification_report(y_test, y_pred, target_names=iris.target_names))

Irisデータでは通常約97%のaccuracyを達成できます。しかしaccuracyだけを見てはいけません — classification_reportの方が重要です。各classのprecision/recall/f1を個別に確認できるからです。実際の業務では、データが不均衡なのはほぼデフォルトの状態で、accuracyだけでは騙されやすいです。

モデルの保存と本番環境でのモニタリング

Modelのシリアライズと再利用

トレーニング後はシリアライズが必要です — deployのたびに最初からtrainするわけにはいきません。scikit-learnの場合、大きなnumpy arrayを含むオブジェクトにはjoblibの方がpickleより高速です。

import joblib

# モデルを保存
joblib.dump(model, 'iris_model.pkl')
print("Model saved!")

# 本番コードで再読み込み
loaded_model = joblib.load('iris_model.pkl')

# 新しいデータで予測をテスト
sample = [[5.1, 3.5, 1.4, 0.2]]  # Setosaです
prediction = loaded_model.predict(sample)
probability = loaded_model.predict_proba(sample)

print(f"Predicted: {iris.target_names[prediction[0]]}")
print(f"Confidence: {probability.max():.2%}")

予測のログとモニタリング

これは私が経験した中で最も高くついた教訓です。チームがmodelをdeployした後… そのまま忘れてしまいました。3ヶ月後、季節によるデータ分布の変化でaccuracyが94%から71%に低下しました — これがデータドリフトと呼ばれる現象です。原因を突き止めるのに丸1週間かかりました。それ以来、全ての予測をログに記録しています:

import json
from datetime import datetime

def log_prediction(input_data, prediction, confidence, model_version="1.0"):
    """予測をログに記録し、後でモニタリングとドリフト検出に活用する"""
    log_entry = {
        "timestamp": datetime.utcnow().isoformat(),
        "model_version": model_version,
        "input": input_data,
        "prediction": int(prediction),
        "confidence": float(confidence),
    }
    # JSONLファイルに追記 — 後で解析しやすい
    with open("prediction_logs.jsonl", "a") as f:
        f.write(json.dumps(log_entry) + "\n")
    return log_entry

# Usage
log_prediction(
    input_data=[5.1, 3.5, 1.4, 0.2],
    prediction=0,
    confidence=0.98
)

シンプルなデータドリフト検出

十分な予測ログが溜まったら、inputの現在のdistributionとtrain時のdistributionを比較して、ドリフトを早期に発見します:

import numpy as np

def check_feature_drift(reference_data, new_data, threshold=0.1):
    """各featureのmeanを比較してドリフトを検出する"""
    ref_mean = np.mean(reference_data, axis=0)
    new_mean = np.mean(new_data, axis=0)
    
    # リファレンスデータのstdで正規化
    drift = np.abs(ref_mean - new_mean) / (np.std(reference_data, axis=0) + 1e-8)
    
    feature_names = iris.feature_names
    for i, d in enumerate(drift):
        status = "WARNING" if d > threshold else "OK"
        print(f"[{status}] {feature_names[i]}: drift={d:.3f}")
    
    return drift

# 毎週定期的に実行
check_feature_drift(X_train, X_test)

この関数は自分のproductionで毎週日曜にcronで実行しています。ドリフトが閾値0.2を超えると自動的にretrainのチケットが作成されます — 手動で監視する必要がありません。

次のステップ

flowを把握できたら、次に推奨するロードマップを紹介します:

  1. 実際のデータセットで練習:KaggleのTitanic、House Prices — Irisよりもずっとmessyなデータでfeature engineeringを練習するのに最適です。
  2. Cross-validation:train/testを1回だけ分割する代わりにcross_val_scoreを使いましょう — データ分割の偶然に左右されずにmodelを評価できます。
  3. Feature engineering:欠損値の処理、カテゴリのエンコーディング、スケーリング — 実際のプロジェクトでは作業の80%を占め、model trainingは残りの20%に過ぎません。
  4. ハイパーパラメータのチューニング:パラメータ最適化にはGridSearchCVまたはRandomizedSearchCVを使いましょう — サーチスペースが大きい場合はRandomizedSearchCVの方がはるかに高速です。
  5. MLflow:modelのバージョン管理とexperiment tracking — チームで作業する場合や、多数の実験を並行して実行する場合には必須です。

MLに数学やデータサイエンスの深い知識は必要ありません。大切なのは、解こうとしている問題が何かを理解し、適切なmetricを選び、modelをdeployして終わりではないことを忘れないことです。残りは?ターミナルを開いて始めるだけです。

Share: