CCCマーケティング TECH Labの Tech Blog

TECH Labスタッフによる格闘記録やマーケティング界隈についての記事など

Titanicデータで機械学習のモデルを作ってAzure Machine Learningの理解を深めてみました

こんにちは、技術開発ユニットの三浦です。クリスマスも過ぎ、2021年も残りわずかとなりました。振り返ると、今年は時間の流れの速さを実感することが多かったなと思います。小学校に入学した子どもが成長していく姿や街が変化していく様子。そういったことを目にする機会が多かったからでしょうか。その中での自身のあり方、みたいなことを色々と考えさせられた一年でした。

さて、前回はAzure Machine Learningの概要や構成概念などについてご紹介しました。

Azure Machine Learningについて - CCCマーケティング データベースマーケティング研究所の Tech Blog

今回はAzure Machine Learningで簡単な機械学習モデルを作るところまで試してみたので、忘れないうちに記録に残しておこうと思います。なお、今回の記事は前回の記事でご紹介したe-book「Mastering Azure Machine Learning」の「Section3: Training Machine Learning Models - Chapter7. Building ML models using Azure Machine Learning」を参考にしています。コードスニペットはこちらで紹介されているものを私の環境で動くように手を加えたものになります。このChapterでは機械学習の入門データとして有名なTitanic号のデータを使って乗客の生死を予測するモデルをLightGBMで生成します。シンプルなため、Azure Machine Learningを一通り触れてみるにはよいテーマだと思います。今回使ったリソースやフローをまとめると以下のようになります。

f:id:miu4930:20211226191020p:plain
Tutorial-Flow(見づらくてすみません・・・)

データの準備→Compute Clusterの作成→モデル学習スクリプトの作成→スクリプトの実行→結果の確認が今回行うことです。コードの作成や実行はWebブラウザで起動できるAzure Machine Learning Studioのnotebook環境で行いました。また、notebookを実行するためのVMを1台、用意しました。notebookの作成画面からVM作成画面へ進むことが出来ます。

f:id:miu4930:20211227085518p:plain
VMの作成はこちらから進めます

VMのサイズは「Standard_D11_v2($0.23/hr)」を使ってみました。

データの準備

使用するデータをWebから取得して、カテゴリ変数の加工や不要な変数の削除を行います。その後Workspaceのデフォルトのデータ保存先(Datastore)であるBlob StorageにCSVファイルにしてアップロードし、名前をつけてDatasetとして登録します。Datasetとして登録することで、以降の作業で作成するモデル学習用のCompute Clusterから、ここでつけた名前でデータに簡単にアクセスが出来るようになります。

データの取得と加工

from azureml.core import Dataset
from azureml.data.dataset_factory import DataType
from azureml.core import Workspace, Experiment

ws = Workspace.from_config()
exp = Experiment(workspace=ws, name="titanic-tutorial")

#Webからtitanicのデータを取得し、DataFrameにする
web_path ='https://dprepdata.blob.core.windows.net/demo/Titanic.csv'
titanic_ds = Dataset.Tabular.from_delimited_files(
        path=web_path, set_column_types={'Survived': DataType.to_bool()}
    )
titanic_df = titanic_ds.to_pandas_dataframe()

#カテゴリ項目の加工など、簡単な前処理
titanic_df.loc[titanic_df['Sex'] == 'female', 'Sex'] = 0
titanic_df.loc[titanic_df['Sex'] == 'male', 'Sex'] = 1
titanic_df.loc[titanic_df['Embarked'] == 'S', 'Embarked'] = 0
titanic_df.loc[titanic_df['Embarked'] == 'C', 'Embarked'] = 1
titanic_df.loc[titanic_df['Embarked'] == 'Q', 'Embarked'] = 2
titanic_df = titanic_df.drop(columns=['PassengerId','Name','Ticket','Cabin'])

Datasetへの登録

import os

from azureml.core import Dataset

#DataFrameをAzurebolob経由でDatasetに登録する
def df_to_dataset(ws, df, name, data_dir='./data'):
    data_path = os.path.join(data_dir, '%s.csv' % name)

    df.to_csv(data_path,index=False)

    datastore = ws.get_default_datastore()

    datastore.upload(src_dir=data_dir, target_path=data_dir,overwrite=True)

    dataset = Dataset.Tabular.from_delimited_files(
        datastore.path(data_path)
    )

    dataset.register(workspace=ws, name=name, create_new_version=True)
    return dataset

df_to_dataset(ws, titanic_df, 'titanic_cleaned', 'data')

Datasetに登録すると、Azure Machine Learning からデータの中身の確認が出来ます。値の分布なども可視化出来ます。

f:id:miu4930:20211227090117p:plain
Datasetの確認

Datasetに登録済みのデータを取得するコードは以下のようになります。

#Datasetから取得してみて登録されているか確認する
from azureml.core import Dataset, Workspace
ws = Workspace.from_config()
Dataset.get_by_name(workspace=ws, name='titanic_cleaned').to_pandas_dataframe().head(3)

Compute Clusterの作成

モデル学習を行うCompute Clusterを作成します。

まず、Compute Clusterを作成する関数を以下のように用意します。同じ名前のClusterがすでにあればそれを、なければ作成し、作成されたものを返すつくりになっています。

from azureml.core.compute import ComputeTarget, AmlCompute
from azureml.core.compute_target import ComputeTargetException

#Compute Clusterを作る
def get_aml_cluster(ws, cluster_name, vm_size='STANDARD_D2_V2', max_node=4):
    try:
        cluster = ComputeTarget(workspace=ws, name=cluster_name)
    except ComputeTargetException:
        compute_config = AmlCompute.provisioning_configuration(
            vm_size=vm_size, max_nodes=max_node)
        cluster = ComputeTarget.create(ws, cluster_name, compute_config)
        cluster.wait_for_completion(show_output=True)
    return cluster

続いてClusterの環境設定情報を作成する関数を以下のように用意します。環境設定情報には使用するPythonのライブラリなども含まれています。

from azureml.core.runconfig import RunConfiguration
from azureml.core.conda_dependencies import CondaDependencies
from azureml.core.runconfig import DEFAULT_CPU_IMAGE

def run_config(target, packages=None):
    packages = packages or []
    config = RunConfiguration()
    config.target = target
    config.environment.docker.enabled = True
    config.environment.docker.base_image = DEFAULT_CPU_IMAGE
    azureml_pip_packages = [
        'azureml-defaults', 'azureml-contrib-interpret',
        'azureml-core', 'azureml-telemetry',
        'azureml-interpret','sklearn-pandas', 'azureml-dataprep'
    ]
    config.auto_prepare_environment = True
    config.environment.python.user_managed_dependencies = False
    config.environment.python.conda_dependencies = CondaDependencies.create(
        pip_packages=azureml_pip_packages + packages
    )
    return config

上で用意した関数を実行し、Compute Clusterと環境設定情報を作成します。

aml_cluster = get_aml_cluster(ws, cluster_name='tutorial_cluster',vm_size='STANDARD_D2_V2')
run_amlcompute = run_config(aml_cluster, ['numpy', 'pandas', 'matplotlib', 'seaborn', 'scikit-learn','lightgbm'])

モデル学習スクリプトの作成

Computer Clusterで実行するスクリプトファイルを作成します。notebookのセルの中に書いた内容を、マジックコマンド%%writefileで書き出しましたが、別の環境で作ってファイルをアップロードしてもよいと思います。スクリプトの中で先の処理で登録済みのDataset(titan-cleaned)からデータを取得し、使用します。このスクリプトは実行すると、Workspaceに紐づくExperiment(あとでtitanic-tutorialという名前で作成します)の中にRunとして登録されます。モデル学習時のハイパーパラメータや精度など、後で複数のRunの間で比較したい指標はRun.get_context()で取得できる実行中のRunオブジェクトの関数 run.log(metric-name, value) を呼び出すと記録しておくことが出来ます。記録した値はMachine Learning Studioで確認することが出来ます。同様に、run.log_image(image-name, plot=fig) を実行するとグラフなどの画像を記録することが出来ます。

また、ハイパーパラメータなどはスクリプトを実行する時の引数で指定するようにしています。

%%writefile script/train_lgbm.py
import argparse
import joblib

from azureml.core import Dataset, Run
import lightgbm as lgbm
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, accuracy_score, recall_score, precision_score, f1_score
import matplotlib.pyplot as plt


#スクリプト実行時の引数の解析
parser = argparse.ArgumentParser()
parser.add_argument('--boosting', type=str, dest='boosting', default='dart')
parser.add_argument('--num-boost-round', type=int, dest='num_boost_round', default=500)
parser.add_argument('--early-stopping', type=int, dest='early_stopping_rounds', default=200)
parser.add_argument('--drop-rate', type=float, dest='drop_rate', default=0.15)
parser.add_argument('--learning-rate', type=float, dest='learning_rate', default=0.001)
parser.add_argument('--min-data-in-leaf', type=int, dest='min_data_in_leaf', default=20)
parser.add_argument('--feature-fraction', type=float, dest='feature_fraction', default=0.7)
parser.add_argument('--num-leaves', type=int, dest='num_leaves', default=40)
args = parser.parse_args()

lgbm_params = {
    'application': 'binary',
    'metric': 'binary_logloss',
    'learning_rate': args.learning_rate,
    'boosting': args.boosting,
    'drop_rate': args.drop_rate,
    'min_data_in_leaf': args.min_data_in_leaf,
    'feature_fraction': args.feature_fraction,
    'num_leaves': args.num_leaves
}


run = Run.get_context()
ws = run.experiment.workspace

#Dataset取得
dataset = Dataset.get_by_name(workspace=ws, name='titanic_cleaned')
df = dataset.to_pandas_dataframe()

y = df.pop('Survived')
x_train, x_test, y_train, y_test = train_test_split(df, y, test_size=0.2, random_state=42)
categorical_features = ['Sex', 'Pclass', 'Embarked']

train_data = lgbm.Dataset(
    data=x_train, 
    label=y_train, 
    categorical_feature=categorical_features,
    free_raw_data=False)
test_data = lgbm.Dataset(
    data=x_test, 
    label=y_test, 
    categorical_feature=categorical_features,
    free_raw_data=False)

#学習時のイテレーションで呼び出すcallbackの定義
def azure_ml_callback(run):
    def callback(env):
        if env.evaluation_result_list:
            for data_name, eval_name, result, _ in env.evaluation_result_list:
                run.log('%s (%s' % (eval_name, data_name), result)
    callback.order = 10
    return callback

#ハイパーパラメータをExperimentのlogに残す
for k, v in lgbm_params.items():
    run.log(k, v)

clf = lgbm.train(train_set=train_data,
    params=lgbm_params,
    valid_sets=[train_data, test_data],
    valid_names=['train', 'val'],
    num_boost_round=args.num_boost_round,
    early_stopping_rounds=args.early_stopping_rounds,
    callbacks=[azure_ml_callback(run)]
)

#テストデータでの評価とlogging
y_pred = clf.predict(x_test)
run.log('auc (test)', roc_auc_score(y_test, y_pred))
run.log('accuracy (test)', accuracy_score(y_test, y_pred.round()))
run.log('precision(test)', precision_score(y_test, y_pred.round()))
run.log('recall(test)', recall_score(y_test, y_pred.round()))
run.log('f1(test)', f1_score(y_test, y_pred.round()))

#変数重要度のグラフの出力
fig = plt.figure()
ax = plt.subplot(111)
lgbm.plot_importance(clf, ax=ax)
run.log_image('feature importance',plot=fig)

#モデルの保存と登録
joblib.dump(clf, 'lgbm.pkl')
run.upload_file('lgbm.pkl','lgbm.pkl')
run.register_model(
    model_name='lgbm_titanic', model_path='lgbm.pkl'
)

スクリプトの実行

スクリプトをCompute Clusterで実行します。"titanic-tutorial" という名前のExperimentを作成し、Experimentオブジェクト(exp)の関数 exp.submit(src)で実行します。実行するとRunオブジェクトが返ってきて、その実行状況は RunDetails(run).show()でnotebook上にwidgetで表示させることが出来ます。

from azureml.core import Workspace, Experiment
from azureml.core import ScriptRunConfig
from azureml.widgets import RunDetails

script_params = [
    '--boosting', 'dart',
    '--learning-rate', '0.01',
    '--drop-rate', '0.15',
]

ws = Workspace.from_config()
exp = Experiment(workspace=ws, name="titanic-tutorial")

script = 'train_lgbm.py'
script_folder = os.path.join(os.getcwd(), 'script')

src = ScriptRunConfig(
    source_directory=script_folder,
    script=script,
    run_config=run_amlcompute,
    arguments=script_params
)

run = exp.submit(src)
RunDetails(run).show()

f:id:miu4930:20211227091031p:plain
Runの進捗状況をWidgetで表示

結果の確認

処理結果はMachine Learning Studioで確認することが出来ます。Machine Learning Studioの左メニュー[Experiments]を選ぶと"titanic-tutorial"というExperimentがリストに表示されています。それをクリックすると、以下のような画面が表示されます。

f:id:miu4930:20211227091229p:plain
Experimentの確認

下のリストに実行したRunが表示されます。名前は自動的に振られるようです。リストで確認したいRunを選択すると、結果の詳細、[Metric]タブで run.log で出力した値、[Image]タブで run.log_image で出力したグラフを確認することが出来ます。また、[Outputs+logs]タブではClusterで出力された標準/エラー出力を見ることが出来ます。Runがエラーで失敗したときなどはこちらを参考すると良さそうです。

f:id:miu4930:20211227091316p:plain
[Metric]の内容

f:id:miu4930:20211227091350p:plain
[Images]タブにLightGBMの変数重要度グラフを表示

f:id:miu4930:20211227091439p:plain
Run同士でMetricの比較も出来ました

まとめ

ということで、今回はAzure Machine Learningで簡単な機械学習モデルを生成してみて、その機能に触れてみた話を紹介させて頂きました。今度は画像データで深層学習モデルを生成することをAzure Machine Learningでやってみたいと思います。

また、これが私の2021年最後の投稿になります。読んで頂きありがとうございました。また来年も色々なことにチャレンジしたり勉強して、ここにたくさん残していければと思っています。

それでは良いお年をお迎えください!