0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Genie Code for ML でチャーン予測を一気通貫 (2) — Unity Catalog登録からModel Servingまで任せる

0
Posted at

はじめに

Data + AI Summit 2026で、Genie CodeにML向けの強化が入った「Genie Code for ML」が発表されました。データ探索からモデルの学習・比較、MLflow/Unity Catalogへの登録、サービングエンドポイントへのデプロイまでのMLワークフローを、自然言語の依頼から自律的に進めるエージェントです。

Genie Code for MLについては、以前フルページのコマンドセンターを起点に、チャーン予測をデータ探索からデプロイ手前まで試しました。

今回はその続編です。前回踏み込まなかった後半戦 — Unity Catalogへのモデル登録、バッチ推論、Model Servingエンドポイントへのデプロイ — までを含めて、EDAからサービングまで通しで任せてみました。進め方も変えて、フルページではなくノートブックにGenie Codeを伴走させるランブック形式です。エージェントがどこで何を判断し、どこで人間に確認を求めてくるのか。そのあたりも含めて書いていきます。

Agentic MLとは

「エージェント」と「ML」の組み合わせには2つの方向があります。

1つは、エージェントシステムの部品としてMLモデルを使う方向です。予測モデルをツールとしてエージェントから呼び出す、いわばML for agentsで、Mosaic AIエージェントの文脈で語られてきたのはこちらです。

Agentic MLはその逆で、MLエンジニアリングの作業そのもの — EDA、特徴量作成、学習・比較、レジストリ登録、デプロイ — をエージェントに書かせて進めさせる、agents for MLの方向です。AutoMLの発展形と捉えると分かりやすいのですが、単発の最適化ランに閉じるAutoMLと違い、エージェントは実験の文脈を保持しながら対話で往復できます。「今の比較結果からベストモデルを登録して」「それをサービングして」と、ワークフロー全体を会話でつないでいけるのが違いです。

fig_agentic_ml.png

Genie Code for MLは、このAgentic MLのDatabricks実装ということになります。

ウォークスルーの進め方

前回はフルページのGenie Codeから始めて、過程はスレッドに・成果物はノートブックに、という二層で進めました。今回は最初からノートブック側に軸足を置き、「土台データの準備セル + 各ステップでGenie Codeに渡す依頼文 + 成果物の検証セル」で構成したランブック形式のノートブックを用意して、依頼文を順番にGenie Codeへ投げていく形で進めます。

題材は前回と同じく通信サービス風の顧客チャーン (解約) 予測ですが、データは単一テーブルのシンプルな構成に変えています。前回は特徴量エンジニアリングの実力を試すことが主眼だったのに対し、今回の主眼はワークフロー後半 (登録・推論・サービング) と承認まわりの挙動だからです。合成データなので誰でも再現できます。

先に環境の注意: 基本環境は「機械学習」を選ぶ

ここが最初のハマりどころです。最初にStandard環境で走らせたところ、Genie CodeがLightGBMを使おうとして %pip install lightgbm を挿入し、Pythonカーネルが再起動されました。前回の記事ではこの自己リカバリ (エラーを読んで自らインストール→再起動→再実行) を見どころとして紹介しましたが、ランブック形式では事情が変わります。再起動されると、それ以前のセルで定義した変数がすべて消えるため、上から順に流して成立するノートブックではなくなってしまうのです。

対策はシンプルで、ノートブックの環境パネルで基本環境に「機械学習」を選択することです。Databricks Runtime for Machine Learning相当のパッケージ (LightGBM含む) がプリインストールされた環境になるため、途中の %pip install 自体が発生しなくなります。

Screenshot 2026-08-17 at 21.00.25.png

準備: 合成チャーンデータの作成

まず土台となるデータを作ります。解約フラグ (churn) が契約期間・月額料金・サポート問い合わせ回数・契約種別と相関を持つよう、ロジスティック関数ベースで生成しています。この「仕込み」を後でエージェントが見抜けるか、が答え合わせのポイントになります。

dbutils.widgets.text("catalog", "takaakiyayoi_catalog", "カタログ")
dbutils.widgets.text("schema", "agentic_ml_demo", "スキーマ")

catalog = dbutils.widgets.get("catalog")
schema = dbutils.widgets.get("schema")

spark.sql(f"CREATE SCHEMA IF NOT EXISTS {catalog}.{schema}")
spark.sql(f"USE {catalog}.{schema}")
import numpy as np
import pandas as pd

np.random.seed(42)
n = 5000

tenure = np.random.exponential(scale=24, size=n).clip(1, 72).astype(int)
monthly_charge = np.round(np.random.uniform(2000, 12000, size=n), -2)
support_calls = np.random.poisson(lam=1.5, size=n).clip(0, 10)
contract_type = np.random.choice(["month_to_month", "one_year", "two_year"], size=n, p=[0.5, 0.3, 0.2])
payment_method = np.random.choice(["credit_card", "bank_transfer", "convenience_store"], size=n, p=[0.5, 0.35, 0.15])
has_options = np.random.binomial(1, 0.4, size=n)

# チャーン確率: 短い契約期間・高い月額・多いサポート問い合わせ・月次契約ほど高い
logit = (
    -1.2
    - 0.05 * tenure
    + 0.00025 * monthly_charge
    + 0.35 * support_calls
    + np.where(contract_type == "month_to_month", 1.0, np.where(contract_type == "one_year", 0.2, -0.6))
    - 0.4 * has_options
)
churn_prob = 1 / (1 + np.exp(-logit))
churn = np.random.binomial(1, churn_prob)

pdf = pd.DataFrame({
    "customer_id": [f"C{i:05d}" for i in range(n)],
    "tenure_months": tenure,
    "monthly_charge_yen": monthly_charge,
    "support_calls_6m": support_calls,
    "contract_type": contract_type,
    "payment_method": payment_method,
    "has_options": has_options,
    "churn": churn,
})

spark.createDataFrame(pdf).write.mode("overwrite").saveAsTable("customers_churn")

これで5,000件の customers_churn テーブルができます。ここから先のコードは一切自分では書きません。すべてGenie Codeへの依頼文とその成果物です。

ステップ1: EDAを依頼する

最初の依頼文です。@customers_churn の部分は、入力欄で @ を手打ちして候補から選択すると確実にテーブル参照として認識されます。

@customers_churn テーブルの内容を把握し、チャーン (churn列) と各特徴量の関係を可視化するEDAを、このノートブックにセルを追加して実施してください。グラフはplotlyを使ってください。

plotlyを明示しているのは、matplotlib/seabornを使われるとグラフの日本語ラベルが豆腐化しやすいためです。日本語環境でGenie Codeに可視化を任せるときの定番の一言です。

Screenshot 2026-08-17 at 20.47.44.png

Genie Codeは「合成データで構造は既知なので日付カラムやカテゴリフィルタの検証は不要」と判断してEDAセル4つ (概要統計、チャーン分布と数値特徴量、カテゴリ特徴量のチャーン率、相関ヒートマップ) を直接追加し、実行まで進めました。完了報告には相関の要約表が付いてきます。

特徴量 相関係数 傾向
tenure_months -0.37 契約期間が短いほど解約しやすい
monthly_charge_yen +0.26 月額が高いほど解約しやすい
support_calls_6m +0.14 サポート問い合わせが多いほど解約しやすい
has_options -0.06 オプション加入者はやや解約しにくい

カテゴリ特徴量では contract_type の影響が最も大きく、月次契約 (month_to_month) のチャーン率が突出している、という報告でした。データ生成時に仕込んだ相関構造をすべて正しく検出しています。

追加されたセルを実行した結果のグラフも見ておきます。指示通りplotlyで描画され、日本語ラベルも問題ありません。

Screenshot 2026-08-17 at 21.01.35.png
newplot.png

ステップ2: モデルの学習と比較

次の依頼です。

customers_churn テーブルを使ってチャーン予測の分類モデルを学習してください。複数のアルゴリズム (例: ロジスティック回帰、ランダムフォレスト、LightGBM) を比較し、実験はMLflowにロギングしてください。評価指標はAUCとF1を使い、最後に比較結果をまとめてください。

ここでの見どころは、コードを書き始める前の判断です。

Screenshot 2026-08-17 at 20.48.01.png

Genie Codeはまずデータを確認し、「チャーン率は54.1%で2クラスはほぼ均衡しているため、クラス重みの調整は不要」と宣言してから実装に入りました。前回の不均衡データ (チャーン率17.9%) ではscale_pos_weightと閾値調整に踏み込んだのに対し、今回はデータを見て「対策不要」と判断している。不均衡対策をやるかやらないかを、データに応じて根拠付きで決めているわけです。指示していない前処理判断を勝手にやるのではなく、判断とその根拠を先に表明してから進むスタイルです。

もう1つ面白かったのは途中の自己修正です。

Serverless環境では spark.databricks.notebook.path が利用できないため、実験名を直接指定するよう修正します。

最初に書いたコードがサーバレス環境の制約に当たり、自分で診断してエクスペリメント名の直接指定に切り替えています。結果として、ノートブック既定のエクスペリメントではなく、ノートブックと同じフォルダに churn_experiment という名前付きエクスペリメントが作られました。

生成された学習コードの構造も見ておきます。前処理 (StandardScaler + OneHotEncoder) をColumnTransformerでまとめ、各モデルとPipelineに組み込んだ上で、親子構造のMLflowランとしてロギングしています。

# 親ラン
with mlflow.start_run(run_name="churn_model_comparison") as parent_run:
    for name, clf in models.items():
        with mlflow.start_run(run_name=name, nested=True) as child_run:
            pipe = Pipeline([
                ('preprocessor', preprocessor),
                ('classifier', clf)
            ])
            pipe.fit(X_train, y_train)
            ...
            mlflow.log_metric("test_auc", auc)
            mlflow.log_metric("test_f1", f1)

            signature = infer_signature(X_train, pipe.predict(X_train))
            mlflow.sklearn.log_model(
                pipe, name="model",
                signature=signature,
                input_example=X_train.head(3)
            )

シグネチャとinput_exampleまで自発的に付けています。前処理込みのPipelineをモデルとしてログしているので、後段の推論では生データをそのまま入力できます。この設計は後で効いてきます。

比較結果は以下の通りでした。

モデル AUC F1
LogisticRegression 0.8132 0.7691
RandomForest 0.8057 0.7732
LightGBM 0.7911 0.7626

ベストはロジスティック回帰。Genie Codeの完了報告には「データの生成過程がロジスティック関数に基づいているため、線形モデルが最も適合しています」とありました。合成データの種明かしまで見抜かれています。実データではまずこうはならないでしょうが、結果に対する解釈まで返してくるのは検証相手として頼もしい挙動です。

サイドバーの「エクスペリメント」から churn_experiment を開くと、親ラン churn_model_comparison の下に3モデルの子ランがぶら下がる構造と、各ランのメトリクスが確認できます。

Screenshot 2026-08-17 at 21.02.56.png

ここから先が、前回踏み込まなかった後半戦です。

ステップ3: Unity Catalogへの登録

先ほどの比較で最も性能の良かったモデルを、Unity Catalogのモデルレジストリに takaakiyayoi_catalog.agentic_ml_demo.churn_model として登録してください。モデルには説明コメントも付けてください。

Screenshot 2026-08-17 at 20.48.12.png

登録自体は指示通りですが、依頼には含めていなかった気の利いた仕事が2つありました。

1つ目は champion エイリアスの付与です。登録したversion 1に自発的にエイリアスを設定しています。2つ目は説明コメントの中身で、アルゴリズム名だけでなく評価指標の実測値、特徴量一覧、前処理パイプラインの構成まで書き込まれていました。

client.update_registered_model(
    name=registered_model_name,
    description=(
        "通信サービスの顧客チャーン(解約)予測モデル。"
        f"アルゴリズム: {best_model_name}, "
        f"Test AUC={results_df.iloc[0]['AUC']:.4f}, F1={results_df.iloc[0]['F1']:.4f}"
        "特徴量: tenure_months, monthly_charge_yen, support_calls_6m, "
        "contract_type, payment_method, has_options。"
        "前処理(StandardScaler + OneHotEncoder)を含むsklearn Pipelineとしてログ済み。"
    )
)

client.set_registered_model_alias(
    name=registered_model_name,
    alias="champion",
    version=result.version
)

モデルを作った本人だからこそ書ける文書化が、登録と同時に済んでいる形です。エイリアスを軸にした運用 (championを差し替えるだけで後段が追従する) を最初から敷いてくれるのは、MLOpsの型として素直に良い出発点です。

カタログエクスプローラでモデルを開くと、説明コメントとchampionエイリアスが登録されているのが確認できます。

Screenshot 2026-08-17 at 21.03.53.png

ステップ4: バッチ推論

登録した churn_model を使って customers_churn 全件にバッチ推論を行い、結果を churn_predictions テーブルとして保存してください。チャーン確率の高い顧客トップ10も表示してください。

ここで承認モデルの見どころが出ました。実行許可のダイアログに、レビュー推奨の警告が付いています。

Screenshot 2026-08-17 at 20.37.15.png

生成コードが saveAsTablemode 'overwrite' で書いているため、「既存データを破壊しうる書き込みなのでunsafe」とフラグを立て、明示的な実行確認を求めてきました。外部API呼び出しやワークスペース変更はないがテーブル書き込みが破壊的、と理由まで書いてあります。編集の承認と実行の許可が別レイヤーになっている上に、実行の中でも破壊的な操作には一段強いレビューがかかる構造です。

推論コードで1点面白いのは確率の取り出し方です。

# UCからchampionモデルをロード
model = mlflow.pyfunc.load_model(f"models:/{model_name}@champion")
df_all['churn_pred'] = model.predict(df_all[feature_cols])

# predict_probaはpyfuncでは直接使えないので、内部モデルを取り出して確率を計算
sk_model = mlflow.sklearn.load_model(f"models:/{model_name}@champion")
proba = sk_model.predict_proba(df_all[feature_cols])[:, 1]
df_all['churn_probability'] = proba

pyfuncフレーバーでは predict_proba が直接使えないため、sklearnフレーバーで取り出し直して確率を計算しています。この制約を知らずに自分で書くと一度はつまずく箇所を、コメント付きで正しく回避してきました。

Screenshot 2026-08-17 at 20.48.25.png

結果は5,000件が churn_predictions に保存され、チャーン確率トップ10は「ほぼ全員がmonth_to_month契約、契約期間1〜11ヶ月、月額1万円超、サポート問い合わせ4〜8回、確率97〜99%で全員実際に解約済み」という報告でした。EDAで見えた構造と整合する、納得感のあるトップ10です。

ステップ5: サービングエンドポイントへのデプロイ

最後に、リアルタイム推論用のエンドポイントまで任せてみます。エンドポイントは稼働コストが発生するので、試す場合はscale to zeroを付けておくのが安全です。

churn_model の最新バージョンをModel Servingエンドポイント churn-endpoint としてデプロイしてください。scale to zeroを有効にしてください。

Screenshot 2026-08-17 at 20.48.37.png

まず、コストへの言及がありました。

エンドポイント作成は稼働コストが発生しますが、ご指示に従って進めます。scale-to-zeroを有効にするため、未使用時のコストは最小限に抑えられます。

依頼にコストの含意があることを認識した上で、指示通りに進める。この一言があるだけで安心感が違います。

そして生成コードには、依頼を超えた要素が盛り込まれていました。モニタリング用スキーマを自動作成し、AI Gatewayの推論テーブル (inference table) と使用量トラッキングを有効にしています。

w.serving_endpoints.create(
    name=endpoint_name,
    config=EndpointCoreConfigInput(served_entities=[ServedEntityInput(
        entity_name=registered_model_name,
        entity_version=str(model_version),
        scale_to_zero_enabled=True,
        workload_size="Small",
    )]),
    ai_gateway=AiGatewayConfig(
        inference_table_config=AiGatewayInferenceTableConfig(
            catalog_name=catalog,
            schema_name=monitoring_schema,
            table_name_prefix=endpoint_name.replace("-", "_"),
            enabled=True,
        ),
        usage_tracking_config=AiGatewayUsageTrackingConfig(enabled=True),
    ),
)

「デプロイして」としか言っていないのに、デプロイ後の監視まで込みの構成にしてくる。プロダクションMLの型が組み込まれている感触です。さらにReady待機のポーリング関数とテストリクエストのセルを続けて追加し、契約3ヶ月・月額1万円・サポート5回・月次契約という高リスクプロファイルの顧客で予測1 (解約) が返ることまで確認して完了報告となりました。完了報告には不要になった際の削除方法 (w.serving_endpoints.delete("churn-endpoint")) の案内まで含まれています。

サービング画面でエンドポイントを開くと、稼働状態とscale to zero、AI Gatewayの推論テーブル設定が確認できます。

Screenshot 2026-08-17 at 21.06.09.png

まとめ

Genie Code for MLに顧客チャーン予測をEDAからサービングまで通しで任せてみて、分かったことをまとめます。

  • 依頼文5つで、EDA → 3モデル比較 (MLflow親子ラン) → UC登録 → バッチ推論 → サービングまで完走した。前回デプロイ手前で止めた続きを、今回は走り切った形。ML固有のコードは1行も自分で書いていない
  • データを見てから判断を表明するスタイル: クラス均衡を確認して「重み調整は不要」と宣言してから実装に入る。前回の不均衡データではscale_pos_weightと閾値調整に踏み込んでおり、データに応じて対処を変えている
  • サーバレス環境の制約 (spark.databricks.notebook.path 不可) に自分で気づいて修正し、名前付きエクスペリメントを作成する。ロギング先は完了報告で確認できる
  • 指示していないベストプラクティスを自発的に敷く: シグネチャ/input_example、前処理込みPipeline、championエイリアス、詳細なモデル説明、サービングの推論テーブルと使用量トラッキング
  • overwrite書き込みには「destructive/unsafe」のレビュー推奨が付き、明示的な実行確認が求められる。破壊的操作には一段強いレビューがかかる
  • 環境まわりのハマりどころ: ランブック形式で使うなら基本環境はStandardではなく「機械学習」を選ぶ (途中の %pip install によるカーネル再起動を防ぐ)

一番の収穫は、エージェントが「作業」だけでなく「型」を持ち込んでくることでした。エイリアス運用、前処理込みのモデルロギング、推論テーブルによる監視 — どれもプロダクションMLで後から効いてくる要素ですが、最初の1周から当たり前のように敷かれていきます。一方で、環境選択やエクスペリメントの置き場所のような足回りは依然として人間側の責任範囲です。そこさえ押さえれば、MLワークフローの立ち上げの体感速度はかなり変わりそうです。

参考リンク

はじめてのDatabricks

はじめてのDatabricks

Databricks無料トライアル

Databricks無料トライアル

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?