Train Model บน Cloud ด้วย Google Cloud Machine Learning Engine

Sunday, August 13, 2017 12:06 PM 1 Comments , ,

สวัสดีครับผมจะมาเล่าประสบการณ์ การทดลองใช้งาน Google Cloud Machine Learning Engine หรือเรียกสั้นๆ Cloud ML Engine โดยความสามารถของมันก็คือ การนำ Model Machine Learning ที่เราสร้างขึ้นมาโดย Tensorflow หรือ Keras (Tensorflow Backend) ไป Train บน Cloud ของ Google นั่นเอง ซึ่งการใช้งาน Cloud ML Engine นั้นมีค่าใช้จ่ายคร่าวๆดังนี้




วิธีการคำนวนราคาโดยละเอียดจะคิดเป็น ML training unit ซึ่งสามารถตรวจสอบได้ที่ https://cloud.google.com/ml-engine/pricing

สำหรับ Account Google Cloud ใหม่ Google จะให้ Credit 300 USD ใช้ได้ 12 เดือน ดูรายละเอียดได้ที่ https://cloud.google.com/free/ (ผมก็ใช้ไอตัว Free Credit เนี่ยแหละมาลองเล่น)

สิ่งที่ต้องการ

- Account Google Cloud ที่ผูกบัตรเครดิตเรียบร้อย
- เครื่องที่ติดตั้ง  Google Cloud SDK เสร็จเรียบร้อย (วิธีการติดตั้งดูที่ https://cloud.google.com/sdk/downloads)
- Code Model ที่ต้องการ Train (ในที่นี้ผมจะ Train โดยใช้ Keras ที่มี Tensorflow เป็น Backend โดยใช้โปรเจค thai-handwriting-number)

สร้างโปรเจค

- ทำการสร้างโปรเจคใหม่หรือจะใช้โปรเจคเดิมที่มีอยู่แล้วก็ได้

สร้างโปรจคใหม่โดยใช้ชื่อ test-cloud-ml

- รันคำสั่งในเครื่องเราเอง

gcloud projects list

ก็จะขึ้นโปรเจคใหม่ที่เราสร้างขึ้นมาดังภาพ



ตั้งให้โปรเจค test-cloud-ml เป็นโปรเจคหลัก (ใช้ PROJECT_ID)

gcloud config set project test-cloud-ml-176413

เปิดการใช้งาน Google Cloud Machine Learning Engine ให้กับโปรเจคของเรา โดยเข้าไปที่ APIs & services > Library

เลือก Machine Learning Engine API

กด Enable

สิ่งที่ควรรู้ก่อนการปรับปรุง Code เพื่อนำไป Train บน Cloud

-  Cloud ML Runtime นั้นใช้ Python 2.7 สามารถตรวจสอบ Package ต่างๆและ Version ของ Cloud ML Runtime ได้ที่ https://cloud.google.com/ml-engine/docs/concepts/runtime-version-list

- ข้อมูลที่เรา Read / Write ทั้งหมดนั้นต้องผ่าน Google Cloud Storage  (เราควรจัดการ Dataset ที่จะใช้ Train ให้สะดวกในการใช้งาน ในที่นี้จะจัดการ Dataset ให้อยู่ใน format ของ pickle ซึ่งต้องใช้ protocol version 2)

ปรับปรุง Code สำหรับ Train บน Cloud

1. จัดการอัพโหลด Dataset ที่ใช้ในการ Train อัพโหลดขึ้น Google Cloud Stroage เสียก่อน ในที่นี้ผม Serialize ข้อมูลด้วย pickle แล้วเซฟเป็นไฟล์ไว้ โดยต้องใช้ protolcol version 2

pickle.dump(DATASET_VARIABLE, open("dataset.pkl", "wb"), protocol = 2)

1.1 จัดการสร้าง bucket ของ Google Cloud Storage โดยในที่นี้จะตั้งชื่อ bucket ว่า kittinan (bucket เปรียบเสมือนตู้คอนเทนเนอร์อันนึงที่เอาไว้จัดเก็บข้อมูลของเรา https://cloud.google.com/storage/docs/key-terms#buckets)  โดยสร้าง bucket ด้วยคำสั่ง

 gsutil mb gs://kittinan/
หรือเราสามารถสร้างในหน้าเวปก็ได้



1.2 อัพโหลดไฟล์ Dataset เข้า Google Cloud Storage ด้วยคำสั่ง

gsutil cp SOURCE gs:://BUCKET_NAME/
ตัวอย่าง
gsutil cp src/thainumber_28.pkl gs://kittinan/
ภาพเมื่ออัพโหลดไฟล์เสร็จ

ลองเข้าไปดูในหน้าเวป Google Cloud Storage ก็จะเจอไฟล์ที่เราเพิ่งอัพโหลดเพิ่มขึ้นมา


2. สร้าง Folder ชื่ออะไรก็ได้ (ในที่นี้จะใช้ชื่อ Folder ว่า cloud-ml-engine) โดยมีโครงสร้างไฟล์ดังนี้



setup.py
ระบุข้อมูลต่างๆ และ require package ต่างๆ
https://cloud.google.com/ml-engine/docs/how-tos/packaging-trainer#recommended_project_structure

'''Cloud ML Engine package configuration.'''
from setuptools import setup, find_packages
setup(name='thai-handwriting-number',
version='0.1',
packages=find_packages(),
include_package_data=True,
description='Thai Handwriting Number Keras Model on Cloud ML Engine',
author='Kittinan',
license='MIT',
install_requires=[
'keras',
'h5py'],
zip_safe=False)
view raw setup.py hosted with ❤ by GitHub

config.yml
ระบุค่า parameter ต่างๆของการ Train ครั้งนี้ ในที่นี้ระบุว่าให้ใช้ scaleTiear ที่เป็น BASIC_GPU
https://cloud.google.com/ml-engine/reference/rest/v1/projects.jobs#traininginput

trainingInput:
scaleTier: BASIC_GPU
view raw config.yml hosted with ❤ by GitHub

trainer > __init__.py
ไฟล์เปล่าๆ ใช้สำหรับ setuptools ที่อยู่ในไฟล์ setup.py 

trainer > model.py
- ใช้ argparse module ในการ parse argument ต่างๆ โดยใน code นี้จะทำการ parse argument ของ training data และ job directory (Google Cloud Storage Path)

- Read / Write File ต่างๆ ด้วย tensorflow.python.lib.io.file_io แทน

from tensorflow.python.lib.io import file_io
import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
import argparse
import pickle
import sys
import random
import h5py
from sklearn.cross_validation import train_test_split
from datetime import datetime
epochs = 50
def train_model(train_file = "./data/thainumber.pkl", job_dir='./tmp/thainumber', **args):
print("Running Train Model")
# set the logging path for ML Engine logging to Storage bucket
logs_path = job_dir + '/logs/' + datetime.now().isoformat()
print('Using logs_path located at {}'.format(logs_path))
#http://techqa.info/programming/question/40133223/Pickled-scipy-sparse-matrix-as-input-data-
if sys.version_info < (3,):
data = pickle.load(file_io.FileIO(train_file, mode='r'))
else:
data = pickle.loads(file_io.read_file_to_string(train_file))
random_state = random.randint(1, 1024)
x_train, x_test, y_train, y_test = train_test_split(data['X'], data['Y'], train_size=0.7, random_state=random_state)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
num_classes = 10
input_shape = (28, 28, 1)
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
# Model
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.summary()
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
batch_size = 128
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test),
callbacks=[])
scores = model.evaluate(x_train, y_train)
print("\n%s: %.2f%%" % (model.metrics_names[1], scores[1] * 100))
scores = model.evaluate(x_test, y_test)
print("\nTEST %s: %.2f%%" % (model.metrics_names[1], scores[1] * 100))
# Save the weight of model locally
model.save_weights('model_weight.h5')
# Save the weight of model to the Cloud Storage bucket's jobs directory
with file_io.FileIO('model_weight.h5', mode='r') as input_f:
with file_io.FileIO(job_dir + '/model_weight.h5', mode='w+') as output_f:
output_f.write(input_f.read())
if __name__ == '__main__':
# Parse the input arguments for common Cloud ML Engine options
parser = argparse.ArgumentParser()
parser.add_argument(
'--train-file',
help='Cloud Storage bucket or local path to training data')
parser.add_argument(
'--job-dir',
help='Cloud storage bucket to export the model and store temp files')
args = parser.parse_args()
arguments = args.__dict__
print(arguments)
train_model(**arguments)
view raw model.py hosted with ❤ by GitHub

ทดสอบรันบนเครื่องตัวเองก่อนด้วยคำสั่ง

gcloud ml-engine local train \
  --job-dir ./ \
  --module-name trainer.model \
  --package-path ./trainer \
  -- \
  --train-file ../thainumber_28.pkl
--job-dir : directory หรือ Google Cloud Storage ที่ใช้ในการ Package ไฟล์ (ML Engine จะส่ง argument job-dir ไปยัง script โปรแกรมเราด้วย)
--module-name : module ที่เราต้องการรัน
--package-path : path ของ module ที่เราจะรัน
option ที่อยู่หลัง -- \ : คือ argument สำหรับ script โปรแกรมเรา

ภาพการรันบนเครื่องตัวเอง

รันบน Cloud ML Engine
ผมสร้าง Shell Script ขึ้นมาชื่อไฟล์ start_train.sh เพื่อความสะดวกในการแก้ไขค่าต่างๆและการรัน โดยมีคำสั่งดังนี้

#!/bin/bash
export BUCKET_NAME=kittinan
export JOB_NAME="thai_number_train_$(date +%Y%m%d_%H%M%S)"
export JOB_DIR=gs://$BUCKET_NAME/$JOB_NAME
#export REGION=asia-east1
export REGION=us-east1
gcloud ml-engine jobs submit training $JOB_NAME \
--job-dir $JOB_DIR \
--runtime-version 1.2 \
--module-name trainer.model \
--package-path ./trainer \
--region $REGION \
--config config.yaml \
-- \
--train-file gs://$BUCKET_NAME/thainumber_28.pkl
view raw start_train.sh hosted with ❤ by GitHub

export environment variables ต่างๆดังนี้
BUCKET_NAME - ชื่อ Bucket Google Cloud Storage ที่เราใช้
JOB_NAME - ชื่อ job ของเรา
JOB_DIR - Path Bucket Google Cloud Storage
REGION - จะใช้ region ไหนในการรัน (ในที่นี้ใช้ US เพราะถูกกว่า)

Option ต่างๆของ command gcloud ก็จะคล้ายๆกับการรันแบบ Local โดยที่มีเพิ่มเติมขึ้นมาดังนี้

--runtime-version : จะใช้ runtime version ไหน (https://cloud.google.com/ml-engine/docs/concepts/runtime-version-list)
--region : จะใช้ region ไหน
--config : config yaml ไฟล์ ที่ระบุ training input ต่างๆ
--train-file : Argument ของ script ที่ระบุ Path Training Data ที่อยู่บน  Google Cloud Storage

ลองรัน ./start_train.sh กันเลย



เมื่อเรารันสำเร็จ gcloud command จะแนะนำคำสั่งในการ stream log ให้เรา เมื่อลองรันดูก็จะเห็น log ในการรันบน cloud ต่างๆดังภาพ


เราสามารถเข้าไปดู Log ในเวปได้ด้วยนะ

https://console.cloud.google.com/mlengine/jobs




หลังจากรันเสร็จลองเข้าไปดูใน  Bucket ของเราก็จะพบกับ folder ที่มีชื่อ job name ของเรา


เมื่อเข้าไปใน folder ก็จะพบ model_weight.h5 ที่เราสั่งเซฟไว้ใน Code ของเราหลัง Train เสร็จ

เป็นอันเสร็จสิ้นการทดลองเล่น Google Cloud Machine Learning Engine ครับผม

เพิ่มเติม

ทดลอง Download weight model มาลองรัน predict ดู


สามารถดู Code ทั้งหมดได้ที่
https://github.com/kittinan/thai-handwriting-number
https://github.com/kittinan/thai-handwriting-number/tree/master/src/cloud-ml-engine

อ้างอิง
https://cloud.google.com/ml-engine
https://github.com/clintonreece/keras-cloud-ml-engine

1 comment: