Google Cloud Platform (GCP)の ai-platform ( 旧 ml-engine ) のジョブは、リソース(CPU,GPU,メモリーなど)を実行時だけ消費する点がリーズナブル。(=主な課金は実行時だけとなるため)
ジョブは、Cloud Shell などに python のパッケージとして準備する。
≪パッケージの例≫
このようにパッケージ化されたジョブは、コマンド:
$gcloud ai-platform jobs submit training ジョブ名 [引数・・]
によって、プロビジョニング(実行用マシンに展開) ~ 実行される。(詳細は後述)
ジョブの実行時において、データの入出力は「バケット」と呼ばれるデータ保存領域に対しておこなう。バケットは主に、GCP の "Storage" サービスからアクセスし、ローカル端末からのアップロードやダウンロードが可能。
≪バケットの例≫
gs://hoge-project-208807-mltest/
バケットの中にディレクトリを掘ることが出来る。
gs://hoge-project-208807-mltest/data/
ジョブの中で、バケットに対して単純なファイルの読み書きをしようとして手間取ったので、ファイル読み書きの最小限のノウハウを書きとめた、というのが本稿の趣旨。
ちなみにジョブの中でディスクの一時作業ファイルに読み書きするのは普通にできる。
open("data_file","w") as f:
f.write("hore")
open("data_file","r") as f:
print(f.read())
=普通杉。
しかし、このようにして出来る一時作業ファイルは、ジョブ停止時にアクセス(アップロード/ダウンロード)することができない。
一方、ジョブ内からバケットに次のようにアクセスしようとしてもうまくいかない。
f = open("gs://hoge-project-208807-mltest/data/data_file.txt","w")
(ジョブ内からバケットにマウントする方法とか、あるかもしれないが現状不明。。※)
以下、ごちゃごちゃ書きましたが、ベストアンサーを知りたい方は、一番下の ※ にジャンプして下さい!
以下は試行錯誤の結果で、ジョブ内からバケットにアクセスする例。
config/config.yaml :
config.yaml には、CPU,GPU,メモリなどの規模を設定する。
当然、規模が大きいほど高単価となる。
今回は実験用なので "BASIC" (基本構成)。
trainingInput:
scaleTier: BASIC
trainer/__init__.py :
# __init__.py は、特に初期化の必要が無ければ空ファイルでよい。
trainer/setup.py :
setup.py は、よくわからんけど、とりあえず呪文を書いておく。
name ぐらいは、適当にそれらしい名前を書いておこう。
rom setuptools import find_packages
from setuptools import setup
REQUIRED_PACKAGES = [
]
setup(
name='file_io',
version='0.1',
install_requires=REQUIRED_PACKAGES,
packages=find_packages(),
include_package_data=True,
requires=[]
)
trainer/task.py :
さてさて、これが本体である。
import argparse
import os
import tensorflow as tf
### コマンドライン引数を得るためのおまじない。
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'--data_dir',
help='GCS or local path to training data',
required=True
)
parser.add_argument(
'--job-dir',
help='this model ignores this field, but it is required by gcloud',
default='junk'
)
args = parser.parse_args()
arguments = args.__dict__
# unused args provided by service
arguments.pop('job_dir', None)
arguments.pop('job-dir', None)
### おまじないは以上
# コマンドライン引数から、バケット内のデータ入出力用のパスを得る。
data_dir = arguments.pop('data_dir')
# data_dir の内容をジョブのログに出力。
print('data_dir', data_dir)
# 対象ファイル名を指定し、そのパス名を得る。
# 今回は読み書き対象を同一のファイルとしたが、当然別々でも可能。
path = os.path.join(data_dir, "data_file.txt")
# tensorflow の write_file / read_file 各メソッドを使って
# 対象ファイルの読み書きを writer と reader に定義する。
writer = tf.write_file(path, "CONTENT", name=None)
reader = tf.read_file(path, name=None)
# これらのメソッドに対応した tensorflow のバージョンが必要で
# そのために、後述の ai-platform の引数では、
# 適切なランタイムバージョンの指定を要する。
# 今回は、'--runtime-version 1.13' を指定。
# tensorflow のセッション開始。
sess = tf.Session()
print(sess.run(writer)) # ファイルへの書き込みを実行
print(sess.run(reader)) # ファイルからの読み込みを実行
・・・しかし、単にバケット内ファイルの読み書きをしたいだけなのに、いちいち tensorflow ライブラリをひっぱってくるのもなんだか大層だなぁ・・。
(他のノウハウがあれば教えてください。)
Cloud Shell での実行例:
$ cd cloudml-test
$ PROJECT_ID=$(gcloud config list project --format "value(core.project)");BUCKET="${PROJECT_ID}-mltest";JOB_NAME="job_$(date +%Y%m%d_%H%M%S)"
$ gcloud ai-platform jobs submit training ${JOB_NAME} --package-path trainer --module-name trainer.task --staging-bucket gs://${BUCKET} --job-dir gs://${BUCKET}/${JOB_NAME} --runtime-version 1.13 --region us-central1 --config config/config.yaml -- --data_dir gs://${BUCKET}/data
繰り返すが、--runtime-version 1.13 がキモ。
このランタイムバージョンを前提として、前述の task.py に記載した
ファイルの読み書き方法が有効となる。
≪参考:ランタイムバージョンのリスト≫
https://cloud.google.com/ml-engine/docs/tensorflow/runtime-version-list?hl=ja
バケット内の所定の場所で、ジョブによって data_file.txt が作成されたことを
Cloud Shell から、確認する。(GCP の Storage でも確認可能)
$ gsutil ls gs://hoge-project-208807-mltest/data/
gs://hoge-project-208807-mltest/data/
gs://hoge-project-208807-mltest/data/data_file.txt
参考ソース
git clone https://github.com/GoogleCloudPlatform/cloudml-dist-mnist-example
※ その後、
調べてみると、この人がベストアンサー(たぶん)してくれてました。
https://stackoverflow.com/questions/55150560/google-ml-engine-cloud-storage-as-a-file
(以下引用)
from tensorflow.python.lib.io import file_io
with file_io.FileIO("gc://bucket_name/foobar.txt","w") as f:
f.write("FOO")
f.flush()
print("Write foobar.txt")
with file_io.FileIO("gc://bucket_name/foobar.txt","r") as f:
for line in f:
print("Read foobar.txt: "+line)
ただ、やっぱり tensorflow ライブラリを引くんですね。。まぁいいけど。