Machine Learning Designer の Python スクリプトコンポーネントを使用すると、依存パッケージのインストールやカスタム Python 関数の実行ができます。
コンポーネントの場所
[Python スクリプト] コンポーネントは、Machine Learning Designer コンポーネントリストの [UserDefinedScript] フォルダーにあります。
前提条件
-
DLC の権限が付与されている必要があります。詳細については、「Cloud service dependencies and authorizations: DLC」をご参照ください。
-
Python スクリプトコンポーネントはDLC コンピューティングリソース上で実行されます。 ワークスペースにDLC コンピューティングリソースを関連付けてください。 ワークスペースの管理。
-
Python スクリプトコンポーネントはコードを OSS に保存します。OSS のバケットを作成してください。詳細については、「Create a bucket」をご参照ください。
重要OSS バケットは、Machine Learning Designer および DLC と同じリージョンにある必要があります。
-
RAM ユーザーは、ワークスペースで [アルゴリズム開発] ロールを持つ必要があります。詳細については、「Manage workspace members」をご参照ください。MaxCompute をデータソースとして使用する場合は、さらに MaxCompute Developer ロールも付与してください。
コンポーネントの設定
-
入力ポート
Python スクリプトコンポーネントには 4 つの入力ポートがあります。これらを OSS パスまたは MaxCompute テーブルのデータに接続します。
-
OSS パス入力
上流コンポーネントの OSS パスからの入力は、スクリプトが実行されるノードにマウントされます。システムは、マウントされたファイルパスを引数として渡します。たとえば、
--input1 /ml/input/data/input1は 1 番目の入力ポートのパスを指定します。スクリプト内では、/ml/input/data/input1にマウントされたファイルをローカルファイルとして読み取ります。 -
MaxCompute テーブル入力
MaxCompute テーブル入力はマウントされません。システムは、テーブル情報を URI 引数として渡します。たとえば、
python main.py --input1 odps://some-project-name/tables/tableは 1 番目の入力ポートの MaxCompute テーブルを示します。コードテンプレートのparse_odps_url関数を使用して、ProjectName、TableName、Partition などのメタデータを解析できます。詳細については、「使用例」をご参照ください。
-
-
出力ポート
Python スクリプトコンポーネントには 4 つの出力ポートがあります。OSS Output Port 1 と OSS Output Port 2 は OSS パスに出力します。Table Output Port 1 と Table Output Port 2 は MaxCompute テーブルに出力します。
-
OSS パス出力
[Code Config] タブの Job output path に設定した OSS パスは、
/ml/output/にマウントされます。OSS Output Port 1 と OSS Output Port 2 は、それぞれ/ml/output/output1と/ml/output/output2に対応します。スクリプト内でこれらのディレクトリにファイルを書き込むと、下流コンポーネントに渡せます。 -
MaxCompute テーブル出力
ワークスペースに MaxCompute プロジェクトがある場合、システムは一時テーブル URI をスクリプトに渡します。例:
python main.py --output3 odps://<some-project-name>/tables/<output-table-name>。PyODPS を使用してこのテーブルを作成し、データを書き込んだ後、接続を介して下流コンポーネントに渡すことができます。
-
-
パラメータ
[Code Config]
パラメータ
説明
Job output path
ジョブ出力の OSS パスです。
-
設定した OSS ディレクトリは、ジョブコンテナ内の
/ml/output/にマウントされます。/ml/output/に書き込まれたデータは、対応する OSS ディレクトリに永続化されます。 -
出力ポート OSS Output-1 と OSS Output-2 は、
/ml/output/配下のサブパスoutput1とoutput2に対応します。OSS 出力ポートを下流コンポーネントに接続すると、下流コンポーネントは対応するサブパスからデータを受け取ります。
Code source
(いずれか 1 つを選択)
Literal code
-
Python code:コードの保存先となる OSS パスです。エディタに入力したコードはこのパスに保存されます。デフォルトのファイル名は
main.pyです。重要初回に [Save] をクリックする前に、指定した OSS パスに同名ファイルが存在しないことを確認してください。存在する場合、既存ファイルが上書きされます。
-
Python code editor:エディタにはデフォルトでサンプルコードが用意されています。詳細については、「使用例」をご参照ください。エディタに直接コードを記述します。
Specify Git configuration
-
Git repository address:Git リポジトリのアドレスです。
-
Code branch:コードブランチです。デフォルト値は master です。
-
Code commit:コミット ID です。ブランチより優先されます。指定した場合、ブランチ設定は無視されます。
-
Git username:プライベートリポジトリにアクセスする必要がある場合に必須です。
-
Git access token:プライベートコードリポジトリにアクセスするために必須です。詳細については、「Appendix: Obtain a GitHub account token」をご参照ください。
Select code source
-
Select code source repositories:作成済みのコード設定を選択します。詳細については、「Code configurations」をご参照ください。
-
Code branch:コードブランチです。デフォルト値は master です。
-
Code commit:コミット ID です。ブランチより優先されます。指定した場合、ブランチ設定は無視されます。
Select OSS path
OSS Code Path フィールドで、コードのアップロード先パスを選択します。
Command
python main.pyなど、実行するコマンドです。説明システムは、スクリプト名とポート接続に基づいて実行コマンドを生成します。手動で設定する必要はありません。
Advanced option
-
Third-party dependencies:Python の
requirements.txt形式でサードパーティライブラリを指定します。システムはノードの実行前にこれらのライブラリをインストールします。cycler==0.10.0 # via matplotlib kiwisolver==1.2.0 # via matplotlib matplotlib==3.2.1 numpy==1.18.5 pandas==1.0.4 pyparsing==2.4.7 # via matplotlib python-dateutil==2.8.1 # via matplotlib, pandas pytz==2020.1 # via pandas scipy==1.4.1 # via seaborn -
Enable container monitoring:フォールトトレランス監視パラメータ用の設定テキストボックスを表示します。
[Run Config]
パラメータ
説明
[リソースグループを選択]
パブリック DLC リソースグループを選択します:
-
パブリックリソースグループの場合は、InstanceType を設定します。CPU または GPU インスタンスを選択します。デフォルト:
ecs.c6.large。
デフォルトでは、現在のワークスペースのデフォルト DLC クラウドネイティブリソースグループが使用されます。
[VPC 設定]
既存の Virtual Private Cloud (VPC) を選択します。
[セキュリティグループ]
既存のセキュリティグループを選択します。
Advanced option
このオプションを選択した場合、次のパラメータを設定します:
-
Instance count:インスタンス数です。デフォルト値は 1 です。
-
Job image URI:ジョブイメージの URI です。デフォルトイメージは XGBoost 1.6.0 を使用します。ディープラーニングフレームワークが必要な場合は、イメージを変更してください。
-
Job type:分散実行向けにコードを実装している場合にのみ変更してください。サポートされる値:
-
XGBoost/LightGBM Job
-
TensorFlow Job
-
PyTorch Job
-
MPI Job
-
-
使用例
デフォルトのサンプルコード
Python スクリプトコンポーネントには、デフォルトで次のサンプルコードが用意されています。
import os
import argparse
import json
"""
Python Script コンポーネントのサンプルコード
"""
# 現在のワークスペースにおけるデフォルトの MaxCompute 実行環境。MaxCompute のプロジェクト名とエンドポイントが含まれます。
# この環境は、現在のワークスペースに MaxCompute プロジェクトが存在する場合にのみ挿入されます。
# 例: {"endpoint": "http://service.cn.maxcompute.aliyun-inc.com/api", "odpsProject": "lq_test_mc_project"}.
ENV_JOB_MAX_COMPUTE_EXECUTION = "JOB_MAX_COMPUTE_EXECUTION"
def init_odps():
from odps import ODPS
# 現在のワークスペースにおけるデフォルトの MaxCompute プロジェクト情報。
mc_execution = json.loads(os.environ[ENV_JOB_MAX_COMPUTE_EXECUTION])
o = ODPS(
access_id="<YourAccessKeyId>",
secret_access_key="<YourAccessKeySecret>",
# プロジェクトが存在するリージョンに基づいてエンドポイントを選択します。例: http://service.cn-shanghai.maxcompute.aliyun-inc.com/api.
endpoint=mc_execution["endpoint"],
project=mc_execution["odpsProject"],
)
return o
def parse_odps_url(table_uri):
from urllib import parse
parsed = parse.urlparse(table_uri)
project_name = parsed.hostname
# Path is like /tables/table_name/partition1/partition2...
path_parts = parsed.path.strip('/').split('/')
if len(path_parts) >= 2 and path_parts[0] == 'tables':
table_name = path_parts[1]
if len(path_parts) > 2:
partition = '/'.join(path_parts[2:])
else:
partition = None
else:
# Fallback for unexpected format
table_name = None
partition = None
return project_name, table_name, partition
def parse_args():
parser = argparse.ArgumentParser(description="PythonV2 component script example.")
parser.add_argument("--input1", type=str, default=None, help="Component input port 1.")
parser.add_argument("--input2", type=str, default=None, help="Component input port 2.")
parser.add_argument("--input3", type=str, default=None, help="Component input port 3.")
parser.add_argument("--input4", type=str, default=None, help="Component input port 4.")
parser.add_argument("--output1", type=str, default=None, help="Output OSS port 1.")
parser.add_argument("--output2", type=str, default=None, help="Output OSS port 2.")
parser.add_argument("--output3", type=str, default=None, help="Output MaxComputeTable 1.")
parser.add_argument("--output4", type=str, default=None, help="Output MaxComputeTable 2.")
args, _ = parser.parse_known_args()
return args
def write_table_example(args):
# 例: PAI提供のパブリックテーブルから、Table Output Port 1 (--output3) で指定された一時テーブルにデータをコピーするSQL文を実行します。
output_table_uri = args.output3
o = init_odps()
project_name, table_name, partition = parse_odps_url(output_table_uri)
o.run_sql(f"create table {project_name}.{table_name} as select * from pai_online_project.heart_disease_prediction;")
def write_output1(args):
# 例: データ結果をマウントされた OSS パス (OSS Output Port 1 のサブディレクトリ) に書き込みます。結果は接続を介して下流コンポーネントに渡せます。
output_path = args.output1
os.makedirs(output_path, exist_ok=True)
p = os.path.join(output_path, "result.text")
with open(p, "w") as f:
f.write("TestAccuracy=0.88")
if __name__ == "__main__":
args = parse_args()
print("Input1={}".format(args.input1))
print("Output1={}".format(args.output1))
# write_table_example(args)
# write_output1(args)
一般的な関数の説明:
-
init_odps():MaxCompute テーブルデータを読み取るための ODPS インスタンスを初期化します。AccessKeyId と AccessKeySecret を指定してください。詳細については、「Obtain an AccessKey pair」をご参照ください。 -
parse_odps_url(table_uri):入力の MaxCompute テーブル URI を解析し、プロジェクト名、テーブル名、パーティションを返します。table_uriの形式はodps://${your_projectname}/tables/${table_name}/${pt_1}/${pt_2}/です。例:odps://test/tables/iris/pa=1/pb=1。pa=1/pb=1は多段パーティションです。 -
parse_args():スクリプトに渡される引数を解析します。入力データと出力データは、引数として実行スクリプトに渡されます。
例 1:他のコンポーネントとの連携
この例では、心臓病予測テンプレートを変更して、Python スクリプトコンポーネントを他の 機械学習デザイナー コンポーネントと組み合わせて使用する方法を説明します。パイプライン構成:
-
心疾患予測テンプレートからパイプラインを作成し、開きます。詳細については、「Heart disease prediction」をご参照ください。
-
キャンバスに Python スクリプトコンポーネントをドラッグし、名前を
SMOTEに変更して、次のコードを設定します。重要imblearnライブラリはデフォルトイメージに含まれていません。[Code Config] タブの Third-party dependencies フィールドにimblearnを追加してください。ライブラリはノードの実行前に自動的にインストールされます。import argparse import json import os from odps.df import DataFrame from imblearn.over_sampling import SMOTE from urllib import parse from odps import ODPS ENV_JOB_MAX_COMPUTE_EXECUTION = "JOB_MAX_COMPUTE_EXECUTION" def init_odps(): # 現在のワークスペースにおけるデフォルトの MaxCompute プロジェクト情報。 mc_execution = json.loads(os.environ[ENV_JOB_MAX_COMPUTE_EXECUTION]) o = ODPS( access_id="<Your_AccessKeyId>", secret_access_key="<Your_AccessKeySecret>", # プロジェクトが存在するリージョンに基づいてエンドポイントを選択します。例: http://service.cn-shanghai.maxcompute.aliyun-inc.com/api. endpoint=mc_execution["endpoint"], project=mc_execution["odpsProject"], ) return o def get_max_compute_table(table_uri, odps): parsed = parse.urlparse(table_uri) project_name = parsed.hostname table_name = parsed.path.split('/')[2] table = odps.get_table(project_name + "." + table_name) return table def run(): parser = argparse.ArgumentParser(description='PythonV2 component script example.') parser.add_argument( '--input1', type=str, default=None, help='Component input port 1.' ) parser.add_argument( '--output3', type=str, default=None, help='Output MaxComputeTable 1.' ) args, _ = parser.parse_known_args() print('Input1={}'.format(args.input1)) print('output3={}'.format(args.output3)) o = init_odps() imbalanced_table = get_max_compute_table(args.input1, o) df = DataFrame(imbalanced_table).to_pandas() sm = SMOTE(random_state=2) X_train_res, y_train_res = sm.fit_resample(df, df['ifhealth'].ravel()) new_table = o.create_table(get_max_compute_table(args.output3, o).name, imbalanced_table.schema, if_not_exists=True) with new_table.open_writer() as writer: writer.write(X_train_res.values.tolist()) if __name__ == '__main__': run()<Your_AccessKeyId> と <Your_AccessKeySecret> を実際の値に置き換えます。詳細については、「Obtain an AccessKey pair」をご参照ください。
-
SMOTE コンポーネントを 分割 コンポーネントの下流に接続します。SMOTE は、少数クラスの合成サンプルを生成してトレーニングデータをオーバーサンプリングし、クラス分布のバランスを取ります。
-
SMOTE コンポーネントの新しいデータを、学習用の Logistic Regression for Binary Classification コンポーネントに接続します。
-
学習済みモデルを、左側ブランチと同じ予測データおよび評価コンポーネントに接続して、並列比較します。コンポーネントの実行後、可視化アイコン (
) をクリックして評価結果を確認します。モデル評価結果では、オーバーサンプリングなしの Binary Classification Evaluation の ROC AUC は
0.924で、混同行列は true-0/predicted-0:43、true-0/predicted-1:6、true-1/predicted-0:9、true-1/predicted-1:33 です。SMOTE によるオーバーサンプリングありの Binary Classification Evaluation-2 の ROC AUC は0.917で、混同行列は true-0/predicted-0:41、true-0/predicted-1:8、true-1/predicted-0:9、true-1/predicted-1:33 です。追加のオーバーサンプリングでモデル性能が大きく改善していないことから、元のサンプル分布とモデルが十分に有効であったことが分かります。
例 2:DLC ジョブのオーケストレーション
Machine Learning Designer で複数の Python スクリプトコンポーネントを接続して、DLC ジョブのパイプラインをオーケストレーションします。 次の例では、実行順序を制御するために DAG 形式で配置された 4 つの DLC ジョブを開始します。
DLC ジョブのコードが上流ノードからデータを読み取らず、下流ノードへデータを渡さない場合、接続はスケジューリング依存関係と実行順序のみを表します。
ワークフローの DAG トポロジーは次のとおりです:DLC Job 1→DLC Job 2→DLC Job 3、DLC Job 1→DLC Job 4、DLC Job 2→DLC Job 4。各 DLC ジョブノードの [Code Config] タブで、次を設定します:
-
OSS Authorization:OSS へのアクセスを承認します。
-
Job output path:出力先を指定します。
-
Code path:例:
oss://xxx/python/。 -
Code:Python コードファイルを編集します。例:DLC Job 4 は
main4.pyに対応し、内容はimport time; print("DLC task4"); time.sleep(5)です。
パイプラインを DataWorks にデプロイして、スケジュール実行します。詳細については、「Use DataWorks to schedule Machine Learning Designer pipelines for offline execution」をご参照ください。
例 3:グローバル変数の受け渡し
-
グローバル変数を設定します。
Machine Learning Designer パイプラインページで、キャンバスの空白領域をクリックし、右側のペインの [グローバル変数] タブで変数を設定します。
例:変数名
arg1に値test1、変数名arg2に値1234を追加します。${variable_name}を使用して変数を参照します。変数名は 1~20 文字で、先頭は英字である必要があります。オフラインスケジューリングでグローバル変数を使用する場合は、オフラインスケジューリング設定でも同じ変数名を設定してください。 -
グローバル変数は、次のいずれかの方法で Python スクリプトコンポーネントに渡すことができます。
-
Python スクリプトコンポーネントノードをクリックします。[Code Config] タブで Advanced option を選択し、Command フィールドでグローバル変数を入力パラメータとして設定します。たとえば、
python main.py --arg1 ${arg1} --arg2 ${arg2}と入力すると、コマンドライン引数としてグローバル変数をスクリプトに渡せます。 -
argparseを使用して引数を解析するように Python コードを修正します。次のコードでは、手順 1 で設定したグローバル変数を例として使用しています。実際のグローバル変数に合わせてコードを更新してください。[Code Config] タブのコード編集領域でコードを置き換えます。
import os import argparse import json """ Python Script コンポーネントのサンプルコード """ ENV_JOB_MAX_COMPUTE_EXECUTION = "JOB_MAX_COMPUTE_EXECUTION" def init_odps(): from odps import ODPS mc_execution = json.loads(os.environ[ENV_JOB_MAX_COMPUTE_EXECUTION]) o = ODPS( access_id="<YourAccessKeyId>", secret_access_key="<YourAccessKeySecret>", endpoint=mc_execution["endpoint"], project=mc_execution["odpsProject"], ) return o def parse_odps_url(table_uri): from urllib import parse parsed = parse.urlparse(table_uri) project_name = parsed.hostname # Path is like /tables/table_name/partition1/partition2... path_parts = parsed.path.strip('/').split('/') if len(path_parts) >= 2 and path_parts[0] == 'tables': table_name = path_parts[1] if len(path_parts) > 2: partition = '/'.join(path_parts[2:]) else: partition = None else: # Fallback for unexpected format table_name = None partition = None return project_name, table_name, partition def parse_args(): parser = argparse.ArgumentParser(description="PythonV2 component script example.") parser.add_argument("--input1", type=str, default=None, help="Component input port 1.") parser.add_argument("--input2", type=str, default=None, help="Component input port 2.") parser.add_argument("--input3", type=str, default=None, help="Component input port 3.") parser.add_argument("--input4", type=str, default=None, help="Component input port 4.") parser.add_argument("--output1", type=str, default=None, help="Output OSS port 1.") parser.add_argument("--output2", type=str, default=None, help="Output OSS port 2.") parser.add_argument("--output3", type=str, default=None, help="Output MaxComputeTable 1.") parser.add_argument("--output4", type=str, default=None, help="Output MaxComputeTable 2.") # 設定したグローバル変数に基づいてコードを追加します。 parser.add_argument("--arg1", type=str, default=None, help="Argument 1.") parser.add_argument("--arg2", type=int, default=None, help="Argument 2.") args, _ = parser.parse_known_args() return args def write_table_example(args): output_table_uri = args.output3 o = init_odps() project_name, table_name, partition = parse_odps_url(output_table_uri) o.run_sql(f"create table {project_name}.{table_name} as select * from pai_online_project.heart_disease_prediction;") def write_output1(args): output_path = args.output1 os.makedirs(output_path, exist_ok=True) p = os.path.join(output_path, "result.text") with open(p, "w") as f: f.write("TestAccuracy=0.88") if __name__ == "__main__": args = parse_args() print("Input1={}".format(args.input1)) print("Output1={}".format(args.output1)) # 設定したグローバル変数に基づいてコードを追加します。 print("Argument1={}".format(args.arg1)) print("Argument2={}".format(args.arg2)) # write_table_example(args) # write_output1(args)
-