背景 — 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を把握できたら、次に推奨するロードマップを紹介します:
- 実際のデータセットで練習:KaggleのTitanic、House Prices — Irisよりもずっとmessyなデータでfeature engineeringを練習するのに最適です。
- Cross-validation:train/testを1回だけ分割する代わりに
cross_val_scoreを使いましょう — データ分割の偶然に左右されずにmodelを評価できます。 - Feature engineering:欠損値の処理、カテゴリのエンコーディング、スケーリング — 実際のプロジェクトでは作業の80%を占め、model trainingは残りの20%に過ぎません。
- ハイパーパラメータのチューニング:パラメータ最適化には
GridSearchCVまたはRandomizedSearchCVを使いましょう — サーチスペースが大きい場合はRandomizedSearchCVの方がはるかに高速です。 - MLflow:modelのバージョン管理とexperiment tracking — チームで作業する場合や、多数の実験を並行して実行する場合には必須です。
MLに数学やデータサイエンスの深い知識は必要ありません。大切なのは、解こうとしている問題が何かを理解し、適切なmetricを選び、modelをdeployして終わりではないことを忘れないことです。残りは?ターミナルを開いて始めるだけです。

