MLOps ZoomCamp 2.3 〜MLFlowでパラメータ最適化をする〜

はじめに

DataTalksClubのMLOps Zoomcampで学んだことをoutputします。

参考動画

www.youtube.com

解説

import xgboost as xgb
from hyperopt import fmin, tpe, hp, STATUS_OK, Trials
from hyperopt.pyll import scope

ここがそもそも全然わからなかったので、概要だけ理解する。 xgboostは勾配ブースティングに基づいた機械学習アルゴリズムの1つ。並列処理ができることによる高速学習と、決定木ベースのアンサンブル学習(複数のモデルを組み合わせた学習)による高い精度が特徴とのこと。詳しくはこちら(https://devops-blog.virtualtech.jp/entry/20250312/1741767926)が参考になりそう。 hyperoptは、機械学習モデルのパラメータを最適化してくれるpythonのライブラリである。 fminには最小にしたい目的関数を記述する。

train = xgb.DMatrix(x_train, label=y_train)
val = xgb.DMatrix(x_val, label=y_val)

公式document(https://xgboost.readthedocs.io/en/latest/r_docs/R-package/docs/reference/xgb.train.html)をみた感じ、 学習データはxgb.DMatrixの形のみとある。 そのため、訓練データ、テストデータはそれぞれDMatrixに変える必要がある。

def objective(params):
    with mlflow.start_run():
        mlflow.set_tag("model","xgboost")
        mlflow.log_params(params)
        booster = xgb.train(
            params=params,
            dtrain=train,
            num_boost_round=1000,
            evals=[(valid,'validation')],
            early_stopping_rounds=50
        )
        y_pred = booster.predict(data=valid)
        rmse = root_mean_squared_error(y_true=y_val,y_pred=y_pred)
  mlflow.log_metric("rmse", rmse)
        return {'loss': rmse, 'status': STATUS_OK}

ここでは最小にしたい目的関数を返す関数を作成している。のちに、hyperoptのfmin使用時に設定する。 lossとstatusを返すようにしている。 xgb.trainの詳細はこちら(https://xgboost.readthedocs.io/en/stable/python/python_api.html)。

search_space = {
    'max_depth': scope.int(hp.quniform('max_depth', 4, 100, 1)),
    'learning_rate': hp.loguniform('learning_rate', -3, 0), # [exp(-3),exp(0)] = [0.05,1]
    'reg_alpha': hp.loguniform('reg_alpha', -5, -1), # [exp(-5),exp(-1)] = [0.006,0.36]
    'reg_lambda': hp.loguniform('reg_lambda', -6, -1), # [exp(-6),exp(-1)] = [0.002,0.36]
    'min_child_weight': hp.loguniform('min_child_weight', -1, 3), # [exp(-1),exp(3)] = [0.36,20]
    'objective': 'reg:linear',
    'seed': 42
}

こちらは探索空間を設定している。 今回最適化したいパラメータとその範囲を決めている。 hpは値を特定の範囲内でサンプリングするhyperoptライブラリのモジュール(部品)。 hpの公式documentはこちら。(https://hyperopt.github.io/hyperopt/getting-started/search_spaces/#defining-a-search-space) scopeはhyperopt.pyllのモジュールで、hpでサンプリングした後にかます変換関数のイメージ。

best_result = fmin(
    fn=objective,
    space=search_space,
    algo=tpe.suggest,
    max_evals=50,
    trials=Trials()
)

ここでhyperoptのfminを使用することで、ハイパーパラメータの調整を自動的にしてくれる。 fnが対象の目的関数(最小化したいやつ)、 spaceが対象のハイパーパラメータと範囲をまとめた探索空間、 algoが調整方法のアルゴリズム(TPEを使用)、 max_evalsが調整回数の上限、 trialsが試行方法である。

詳しいhyperopt.fminの参考資料はこちら(https://docs.databricks.com/aws/en/machine-learning/automl-hyperparam-tuning/hyperopt-concepts)。

これで実際に2時間程度学習します。 最終的には、「validation-rmse:9.40764」から「validation-rmse:5.88858 」くらいまで改善されます。

with mlflow.start_run():
    best_params = {
        'learning_rate' : 0.0842453986645059,
        'max_depth': 36,
        'min_child_weight': 7.741919852106371,
        'objective': 'reg:linear',
        'reg_alpha': 0.12023902213678989,
        'reg_lambda': 0.06371166574210547,
        'seed': 42
    }
    mlflow.log_params(best_params)
    booster = xgb.train(
            params=best_params,
            dtrain=train,
            num_boost_round=1000,
            evals=[(valid,'validation')],
            early_stopping_rounds=50
        )
    y_pred = booster.predict(data=valid)
    rmse = root_mean_squared_error(y_true=y_val,y_pred=y_pred)
    mlflow.log_metric("rmse", rmse)
    mlflow.xgboost.log_model(booster, artifact_path='models_mlflow')
    

mlflow側のローカル画面から実際にrmseが最小のものを見つけ、そのパラメータをbest_paramsとして再度評価をし、 modelとして保存している。mlflow.xgboost.log_modelを使用することで、下記のように、modelが保存される。

実際にモデルとして保存されている様子
下記のように、モデルの詳細情報(環境情報、依存関係等)が分かるため、再現性が生まれます。これはとても良いですね。
実際に保存されたモデルのartifactデータ

最後に

モデルのデータをmlflow側に残す方法についてある程度理解できました。 特にhyperoptやxgboost等の使い方がわかりました。 また、PytorchやTensorflowでも同様にmlflowとの連携ができるっぽいので、今後そちらでもやってみたいです。

MLOps ZoomCamp 2.2 〜MLFlowで変数の保存をする〜

はじめに

DataTalksClubのMLOps Zoomcampで学んだことをoutputします。

参考動画

www.youtube.com

解説

動画が4年前ということもあり、mlflowの形式が少し変わっていた。 動画では、

mlflow ui --backend-store-uri  sqlite:///mlflow.db

だったが、現在はuiという表記ではうまくいかない。

mlflow server --backend-store-uri  sqlite:///mlflow.db

だといける。 --backend-store-uri sqlite:///mlflow.db とすることで、指定したデータベース(SQLite、ローカル簡潔かな)に保存してくれる。 なお、実行したフォルダ内に、指定したdbが保存されるため、上記の例で言えば、 mlflow.dbが実行場所のフォルダに格納される。

with mlflow.start_run():
    mlflow.set_tag("developer","kawarin")
    mlflow.log_param("train-data-path", "./data/yellow_tripdata_2026-01.parquet")
    mlflow.log_param("valid-data-path", "./data/yellow_tripdata_2026-02.parquet")
    model = LinearRegression()
    model.fit(x_train, y_train)
    y_pred = model.predict(x_val)
    loss = root_mean_squared_error(y_val, y_pred)
    mlflow.log_metric("rmse", loss)

上記が基本的なコード。 これを実行した後は、先ほどのコマンドで起動したブラウザを見れば、実際に実験結果が格納されている。 set_tagはkeyとvalueをセットにしたものを格納でき、メモの役割として使用するものだと理解している。 log_paramは実験での変数を保存するもの。 metricは評価変数のことで、今回はRMSE(二乗平均平方根誤差)を採用している。

実際にMLFlow側から見えるログ
上記のように実行ごとに実験結果が保存されるため、複数存在する。
実験結果の一つを押した時
上記のように実験結果の1つを選択すると、具体的な数値が格納されていることがわかる。

最後に

mlflowを使用し、変数の保存ができるようになりました。

MLOps ZoomCamp 2.1 〜MLFlowの概要理解〜

はじめに

DataTalksClubのMLOps Zoomcampで学んだことをoutputします。

参考動画

www.youtube.com

解説

MLflowの概要

MLFlowはアメリカのDatabricks社が提供している、オープンソースのAI platformであり、これを使用することで、データの監視、評価、最適化などができる。 MLFlowで追跡可能なものは以下の通りである。

  • ソースコード
  • ハイパーパラメータ
  • データ
  • モデル
  • メトリックス(loss,accuracy等)
  • 環境

なぜ追跡機能が必要なのか

データサイエンティストとして働く場合、データに対し、再現性のある結果を求めている。 そのため、環境、ハイパーパラメータ、データなどの情報を追跡しておくことで、再現性のある結果を出せる。 また、モデルの最適化の点でも必要とのこと。 追跡機能があることで、どの部分をどの程度最適化すればいいのかがわかるので。

Excelに残すのじゃダメなの

やはり「視認性の低さ」「フォーマットの標準的な形がない」のがExcelにlogを残すことのデメリットである。

MLFlowを使用するにあたって

オープンソースで、基本的には自分でインストールする形になります。

conda info -e # 仮想環境一覧表示
conda activate [自分の仮想環境] # 仮想環境ON
pip install mlflow # mlflowのインストール
mlflow server --port 5000 # mlflow起動

上記のようにして実行し、mlflowを正常に実行できるか確認する。 起動したら、「http://127.0.0.1:5000/」にアクセスできればOK。

MLOps ZoomCamp 1.5 〜MLOps 成熟度モデルについて〜

はじめに

DataTalksClubのMLOps Zoomcampで学んだことをoutputします。

MLOps 成熟度モデルについて

英語では、MLOps maturity modelと言われている。 公式ドキュメント通りに言うなら、MLOps成熟度モデルは、機械学習が入ったプロジェクトの運用・構築の段階的なレベルを定義している。

具体的なレベル分け

・Level0 : No MLOps これはMLOpsが全く組み込まれていない状態。モデルの学習、構築、運用、テストが全て手作業。 モデルのパフォーマンス監視の仕組みが作られていない。

・Level1 : DevOps but no MLOps これは、レベル0とは違い、DevOpsが入っている。コードのversion管理がされている。モデルの構築、テストは自動化されている。 ただ、モデルの結果の追跡が難しい。(監視機能はない)

・Level2 : Automated training 名前の通り、モデルの学習が自動化されている。さらにモデルの監視も組み込まれている。 リリース自体は手動でやる必要がある。

・Level3 : Automated model deployment モデルのデプロイが自動化されている。全てのコードにテスト機能があり、デプロイのたびにテストが自動で実行される。

・Level4 : Full MLOps automated operations システムの全てが自動化されている。時に学習改善できるパートを提案し、自動的に新しいモデルとして改良版を提供する。

最後に

短くなりましたが、MLOpsにもレベルがあると言うことがなんとなく理解できました。 ただ、動画を見た感じ、どのレベルに持っていくかを慎重に考える必要がありました。例えばPOC段階なのに、いきなりLevel4まで持っていくのは、 時間、コストの面で良くないですね。ちゃんとそのprojectのモデルが高い精度を安定して出せることを確認し、サービスとして今後も自動的に改善しながら運用・保守するべきだと判断すれば、レベル4まで持っていくのも良いでしょう。

MLOps ZoomCamp 1.3 〜タクシーの乗車時間を推測する〜

はじめに

DataTalksClubのMLOps Zoomcampという、オンライン講座で詰まった点やわかった点などをまとめます。

解説

import seaborn as sns

を何気に初めて見た。 どうやらPythonの可視化ライブラリの一つらしく、matplotlibベースだそう。

今回はアメリカのニューヨーク政府が公式で公開しているタクシーデータを使用し、乗車時間を推測しようというものだ。 今回のProjectのフォルダ内で、

wget https://d37ci6vzurychx.cloudfront.net/trip-data/yellow_tripdata_2026-01.parquet

をすれば簡単にダウンロードできる。 ダウンロードサイトはこちら。(https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page)

df = pd.read_parquet('data/yellow_tripdata_2026-01.parquet')

その後、上記のようにparquet形式のファイルを読み込む。どうやらPandasのread_parquetをすることで、DataFrame型が返ってくるらしい。 DataFrame型はExcelとかでよく見る表形式のデータ構造?のようなもので、データ解析によく使えるものなんだと思う。

df['duration'] = df.tpep_dropoff_datetime - df.tpep_pickup_datetime
df.duration = df.duration.apply(lambda td: td.total_seconds() / 60) # apply is like map.

ここでは、DataFrameに新たに、durationという項目を追加している。tpep_dropoff_datetime=降車時刻,tpep_pickup_datetime=乗車時刻なので、その差分を乗車時間としている。 また、duration自体は、「0 days 00:05:33」となっているが、これを分単位に直すために、applyを使用している。 applyはDataFrame型のものに対し使用でき、要素全てを指定した処理で加工できる。(map的な)

df = df[(df.duration >= 5.0) & (df.duration <= 10.0)]

これは、乗車時間が5分以上10分以下のデータのみに絞ったコード。 indexに(df.duration >= 5.0) & (df.duration <= 10.0)を割り当てていてちょっと慣れない部分もある。

(df.duration >= 5.0) & (df.duration <= 10.0)の出力は、

0          True
1          True
2          True
9          True
19         True
           ... 
3724869    True
3724871    True
3724873    True
3724882    True
3724887    True
Name: duration, Length: 894105, dtype: bool

乗車時間が5分以上10分以下のデータのindexを指している。

ちなみに、今回のタクシーデータの列名等の詳細情報はこちらに書かれています。(https://www.nyc.gov/assets/tlc/downloads/pdf/data_dictionary_trip_records_yellow.pdf)

categorical = ['PULocationID','DOLocationID']
numerical = ['trip_distance']

データ分析において、データの形式として、大きく質的変数(categorical)と量的変数(numerical)に分かれる。 質的変数は性別や血液型のような数値では表せないラベル系の変数。 量的変数は名前の通り、気温や距離などのような、数値で表せる変数。

df[categorical + numerical].iloc[:10]

ilocは、index locationということで、指定した行番号、列番号で取得するメソッド。

categorical = ['PULocationID','DOLocationID']
numerical = ['trip_distance']

dv = DictVectorizer()
train_dicts = df_train[categorical + numerical].to_dict(orient='records')
x_train = dv.fit_transform(train_dicts)

eval_dicts = df_eval[categorical + numerical].to_dict(orient='records')
x_val = dv.transform(eval_dicts)

DictVectorizerは辞書型のデータをスパース形式に変換するものらしい。 そこでfit_transformをすることで、正規化っぽいことをしているのかな。(深くは知らない)

lr = LinearRegression()
lr.fit(x_train, y_train)
y_pred = lr.predict(x_val)
loss = mean_squared_error(y_val, y_pred)
print(loss)

線形回帰モデルを使用し、fitで学習、predictで推論をしている。MSE(平均二乗誤差)で損失を取得。

with open('models/lin_reg.bin', 'wb') as f:
    pickle.dump((dv, lr), f)

ここで、学習済モデルの保存をしている。

sns.histplot(y_pred, label='prediction')
sns.histplot(y_train, label='actual')

plt.legend()
plt.show()

実際にモデルの予測値と実値をグラフ化している。

image1

どうしてもズレが出てしまう原因として、 学習に使用している変数の個数が不十分なことやモデルの選定などが挙げられる。

最後に

ある程度入門として基本的な学習の流れを終えたのは良かったと思います。 特にDataFrameの扱い方や実際のモデルの学習・推論方法等が学べたのが大きいです。