Azure Machine Learning で Scikit Learn ベースのモデルを開発する estimator実行ファイル


# 実行ファイルの構成

先ほどの記事で紹介した、AMLSの実行スクリプトから直接呼ばれるExperimentのRunスクリプトの構成です。
ScikitLearn準拠のRunnerという雰囲気ですが、いろいろやってみたところ、別にScikitLearnに特化した話はあまりなく、単にパラメータの受け渡し、結果の登録というところさえできれば良いようです。
あまり特色は無いので、clrライブラリを使った.NETライブラリの統合時の参考となれば。

（題材としては度々登場するBayesServerのライブラリを想定しています。実際には直接インポートするのではなく.NETのカスタムライブラリとして多少ラップしているのですが、ダイレクトに読み込んでも何とかなる代物ではあります。）

## import, import, import
azuremlからはRunだけ持ってくれば、スケルトンとしての機能は果たせるようです。
sklearn周りはシンプル。
clr周りは引数に登場する型を個別にimportする必要があります。
pythonの型と.NETの型の翻訳を頭においておいてください。

``` estimator.py
import argparse
from azureml.core.run import Run

# scikit learn
import sklearn
from sklearn.model_selection import train_test_split
import joblib

# clr classes
import clr
clr.AddReference('System.Data')
clr.AddReference('System.Collections')
from System import Array, Linq, Nullable, Double
from System.Data import *
from System.Collections import *
from System.Collections.Generic import List

# estimator
from estimator import *

```

以下のように.NETのライブラリ自体のありかをsys.pathに指定する必要があります。
手元はWindowsの場合、AMLSのノードはLinuxなので切り替えが必要です。
ここでは.NET Standard 2.0ベースのライブラリを想定しています。

``` estimator.py
# Windows
# sys.path.append(r"C:\Users\***\Debug\netstandard2.0")
# Linux
# sys.path.append("/mnt/****")
```

## 起動スクリプト

main()関数として、必要な引数を指定した形で構成します。
この部分がAMLSとユーザースクリプトの主要なパラメータ引き渡しのやり方になります。
ScikitLearnのインターフェイスは結局全然関係ないことに気づきます。。。

## コンテキストの読み込み

おまじないの類ですが、最初に行っておきます。

``` estimator.py
# get current run in context
run = Run.get_context()
datafile = "datasource.csv"
option = "option"
```

## データ読み込み
main関数にてcsvからデータを読み込みtrain/predictで分割です。
この辺はScikitLearnの関数を利用、、してもいいし、しなくてもいい。
BNEstimatorというクラスにすべてが詰め込まれています。
実は前後で.NETクラスとPandasのデータフレーム変換が発生するのでいくつか変換コードが入りますが、ここでは省略しています。

``` estimator.py
def main():
    print('starting estimater')
    # parse argument of main function
    parser = argparse.ArgumentParser()
    parser.add_argument('--datafile', type=str, default='',
                        help='Data file for train/test')
    parser.add_argument('--option1', type=str, default='',
                        help='option1')
    args = parser.parse_args()
    # load data from data
    basepath = "./data/"
    df = pd.read_csv(basepath + datafile)
    target = df.pop(targetColumn)
    # split data set
    X_train, X_test, y_train, y_test = train_test_split(
        df, target, test_size=0.2, shuffle=False)
    y_test.to_csv('./logs/y_test.csv')
    y = pd.concat([y_train, y_test], axis=0)
    glabels = get_labels_from_dbl_series(y)
```

BNEstimatorというメインのクラスに実行内容をセットします。
カラムの意味指定をしている感じですが、そのうち公開します。

``` estimator.py
    estimator = BNEstimator(
        idColumn="id",
        partitionColumn=partitionColumn, 
        ...)
```

## fitしてpredict

インターフェイスといっても、これだけ合わせるだけじゃん、というのが正直なところ。そしてAMLSにとっては、それすらどうでも良いらしく、Scikit Learnにラップした恩恵は皆無。。。

``` estimator.py
    # fit
    estimator.fit(X_train, y_train)
    # store trained model 
    estimator.save_network("./logs/")
    # predict
    prediction = estimator.predict(X_test)
    prediction.to_csv('./logs/prediction.csv')
```

## 結果を評価

評価の時に少しScikit Learnの恩恵を感じます。

``` estimator.py
    # evaluate
    eval_tupple = evaluate_prediction(y_test, prediction )
    run.log('Hammloss', np.float(eval_tupple.hammloss))
    run.log('Accuracy', np.float(eval_tupple.accuracy))
    run.log('macro_f1', np.float(eval_tupple.macro_f1))
    run.log('micro_f1', np.float(eval_tupple.micro_f1))
if __name__ == '__main__':
    print('start main')
    main()
```