介绍
本系列将介绍如何在阿里云容器服务上运行Kubeflow, 本文介绍如何使用TfJob
运行模型训练。
TFJob简介
模型训练是机器学习最主要的实践场景,尤其以使用机器学习框架TensorFlow进行模型训练最为流行,但是随着机器学习的平台由单机变成集群,这个问题变得复杂了。GPU的调度和绑定,涉及到分布式训练的编排和集群规约属性的配置(cluster spec)也成了数据科学家们巨大的负担。
为了解决这一问题,一个新的资源类型TFJob,即TensorFlow Job被定义出来了。通过这个资源类型,使用TensorFlow的数据科学家无需编写复杂的配置,只需要关注数据的输入,代码的运行和日志的输入输出。
TFJob
定义
简单介绍一下TFJob
的定义, TFJob实际上遵循Kubernetes标准的API定义。本文介绍v1alpha1的版本,对于v1alpha2,Kubeflow 0.2会支持,目前仍在开发中。
TFJob
对象
| 属性 | 类型| 描述 |
|-------|-----|-------------|
| apiVersion | string
| api版本,目前为 kubeflow.org/v1alpha1
|
| kind | string
| Value representing the REST resource this object represents. In our case it's TFJob
|
| metadata | ObjectMeta
| 标准元数据定义. |
| spec | TFJobSpec
| TensorFlow job的定义 |
其中spec
非常重要,以下是其定义。
TFJobSpec
对象
属性 | 类型 | 描述 |
---|---|---|
ReplicaSpecs | TFReplicaSpec 类型数组 | 定义一组参加TensorFlow模型训练的成员 |
具体的TFReplicaSpec
定义:
TFReplicaSpec
对象
属性 | 类型 | 描述 |
---|---|---|
TfReplicaType | string | 参加TensorFlow模型训练的成员类型, 可以是 MASTER , WORKER 或者 PS 。当单机训练的时候,只需要指定Master 类型。 |
Replicas | int | 当前成员类型的数量,默认为 1 . |
Template | PodTemplateSpec | 这里是完全遵循Kubernetes Pod模板的定义,具体可以参考 Pod定义 |
前提要求
- Kubernetes集群包含GPU的能力
- Kubeflow核心模块安装
可以通过以下命令确认tf-operator
这个核心组件已经正常启动
kubectl get deploy -n kubeflow tf-job-operator
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
tf-job-operator 1 1 1 1 1m
运行TFJob
进行训练
其实数据科学家运行模型训练关心的是三件事:
- 数据从哪里来
- 如何运行机器学习的代码
- 训练结果(模型和日志)到哪里去
对于运行在桌面机上的机器学习代码来说,这是很容易的事情;但是如果您的运行时环境是多台机器的集群环境,这个工作就变得复杂了。而TFJob
希望机器学习训练的工程师们在集群的控制节点上实现中心化的配置管理,并且只需要了解如何编写和配置TFJob
就能够像在单机上运行机器学习代码。
本文将提供三个例子阐述如何在云上使用数据和训练结果:
1. 数据和训练结果都在容器内
以下为示例yaml:
apiVersion: kubeflow.org/v1alpha1
kind: TFJob
metadata:
name: mnist-simple-gpu
spec:
replicaSpecs:
- template:
spec:
containers:
- image: registry.aliyuncs.com/kubeflow-images-public/tf-mnist:gpu
name: tensorflow
command: ["python", "/app/main.py"]
resources:
limits:
nvidia.com/gpu: 1
restartPolicy: OnFailure
其中mnist代码来自 https://github.com/cheyang/tensorflow-sample-code/tree/master/tfjob/docker/mnist
将该模板保存到mnist-simple-gpu.yaml
, 并且创建TFJob
:
# kubectl create -f mnist-simple-gpu.yaml
现在可以看到TFJob
资源已经被创建了:
# kubectl get tfjob
NAME AGE
mnist-simple-gpu 1m
获得该TFJob
的RUNTIME ID,这个RUNTIME ID是TFJob和其对应Pod之间的关联
# RUNTIMEID=$(kubectl get tfjob mnist-simple-gpu -o=jsonpath='{.spec.RuntimeId}')
根据RUNTIME ID查询对应执行该训练任务
# kubectl get po -lruntime_id=$RUNTIMEID
NAME READY STATUS RESTARTS AGE
mnist-simple-gpu-master-825k-0-23033 1/1 Running 0 1m
在Pod运行过程中,可以通过kubectl logs
检查训练日志:
# kubectl logs mnist-simple-gpu-master-825k-0-23033
[...]
2018-06-04 11:20:01.014330: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1120] Creating TensorFlow device (/device:GPU:0) -> (device: 0, name: Tesla P100-PCIE-16GB, pci bus id: 0000:00:08.0, compute capability: 6.0)
2018-06-04 11:20:06.612938: I tensorflow/stream_executor/dso_loader.cc:139] successfully opened CUDA library libcupti.so.8.0 locally
Successfully downloaded train-images-idx3-ubyte.gz 9912422 bytes.
Extracting /data/tensorflow/input_data/train-images-idx3-ubyte.gz
Successfully downloaded train-labels-idx1-ubyte.gz 28881 bytes.
Extracting /data/tensorflow/input_data/train-labels-idx1-ubyte.gz
Successfully downloaded t10k-images-idx3-ubyte.gz 1648877 bytes.
Extracting /data/tensorflow/input_data/t10k-images-idx3-ubyte.gz
Successfully downloaded t10k-labels-idx1-ubyte.gz 4542 bytes.
Extracting /data/tensorflow/input_data/t10k-labels-idx1-ubyte.gz
Accuracy at step 0: 0.1446
Accuracy at step 10: 0.7515
Accuracy at step 20: 0.8397
Accuracy at step 30: 0.8606
...
Accuracy at step 980: 0.9642
Accuracy at step 990: 0.9662
Adding run metadata for 999
[...]
从日志中可以看到该训练过程需要先下载数据,再在GPU上进行训练,但是训练出来的结果只是留在了容器里,难于导出。
如果需要将模型导出,就需要借用Kubernetes的数据卷,我们来看一下第二个例子:
2. 将训练结果保存在OSS上
2.1 创建OSS数据卷kubeflow-oss
,具体可以参考文档创建OSS Bucket
2.2 创建OSS的PV, 以下为示例oss-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: kubeflow-oss-mnist
labels:
tfjob: kubeflow-oss-mnist
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
storageClassName: oss
flexVolume:
driver: "alicloud/oss"
options:
bucket: "kubeflow-oss"
url: "oss-cn-hangzhou.aliyuncs.com"
akId: ***
akSecret: ***
otherOpts: "-o allow_other"
akId, akSecret为访问OSS 所需的 AccessKey
url为oss Bucket的访问域名,如果
Bucket
和ECS
实例位于不同地域(Region),请选择 外网域名;如果位于相同地域,需要根据集群网络类型进行选择,若是VPC
网络,请选择VPC域名
,若是经典网络,请选择内网域名
。
更多细节,请参考kubernetes配置数据卷
# kubectl create -f oss-pv.yaml
persistentvolume "kubeflow-oss-mnist" created
2.3 利用oss-pvc.yaml
创建PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: kubeflow-oss-mnist
spec:
storageClassName: oss
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
selector:
matchLabels:
tfjob: kubeflow-oss-mnist
具体命令:
# kubectl create -f oss-pvc.yaml
persistentvolumeclaim "kubeflow-oss-mnist" created
一旦完成,就可以运行kubectl get pvc
检查pvc的创建结果:
# kubectl get pvc kubeflow-oss-mnist
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
kubeflow-oss-mnist Bound kubeflow-oss-mnist 10Gi RWX oss 1d
2.4 创建TFJob
apiVersion: kubeflow.org/v1alpha1
kind: TFJob
metadata:
name: mnist-simple-gpu-oss
spec:
replicaSpecs:
- template:
spec:
containers:
- image: registry.aliyuncs.com/kubeflow-images-public/tf-mnist:gpu
name: tensorflow
env:
- name: TEST_TMPDIR
value: /data
command: ["python", "/app/main.py"]
resources:
limits:
nvidia.com/gpu:
volumeMounts:
- name: kubeflow-oss-mnist
mountPath: "/data"
volumes:
- name: kubeflow-oss-mnist
persistentVolumeClaim:
claimName: kubeflow-oss-mnist
restartPolicy: OnFailure
将该模板保存到mnist-simple-gpu-oss.yaml
, 并且创建TFJob
:
# kubectl create -f mnist-simple-gpu-oss.yaml
tfjob "mnist-simple-gpu-oss" created
一旦TFJob
运行, 就能从OSS的客户端看到保存的模型文件,这些文件就会永远保存到OSS Bucket中。
3. 利用NAS上的训练数据进行模型训练
3.1 创建NAS数据卷,并且设置与当前Kubernetes集群的同一个具体vpc的挂载点。操作详见文档
3.2 在NAS上创建 /tfdata/tensorflow/input_data
的数据文件夹, 下载mnist训练所需要的数据
mkdir /nfs
mount -t nfs -o vers=4.0 0fc844b526-rqx39.cn-hangzhou.nas.aliyuncs.com:/ /nfs
mkdir -p /nfs/tfdata/tensorflow/input_data
cd /nfs/tfdata/tensorflow/input_data
wget https://raw.githubusercontent.com/cheyang/tensorflow-sample-code/master/data/t10k-images-idx3-ubyte.gz
wget https://raw.githubusercontent.com/cheyang/tensorflow-sample-code/master/data/t10k-labels-idx1-ubyte.gz
wget https://raw.githubusercontent.com/cheyang/tensorflow-sample-code/master/data/train-images-idx3-ubyte.gz
wget https://raw.githubusercontent.com/cheyang/tensorflow-sample-code/master/data/train-labels-idx1-ubyte.gz
cd /
umount /nfs
3.3 创建NAS的PV, 以下为示例nas-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: kubeflow-nas-mnist
labels:
tfjob: kubeflow-nas-mnist
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
storageClassName: nas
flexVolume:
driver: "alicloud/nas"
options:
mode: "755"
path: /tfdata
server: 0fc844b526-rqx39.cn-hangzhou.nas.aliyuncs.com
vers: "4.0"
3.4 利用nas-pvc.yaml
创建PVC
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: kubeflow-nas-mnist
spec:
storageClassName: nas
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
selector:
matchLabels:
tfjob: kubeflow-nas-mnist
具体命令:
# kubectl create -f nas-pvc.yaml
persistentvolumeclaim "kubeflow-nas-mnist" created
3.5 创建TFJob
apiVersion: kubeflow.org/v1alpha1
kind: TFJob
metadata:
name: mnist-simple-gpu-nas
spec:
replicaSpecs:
- template:
spec:
containers:
- image: registry.aliyuncs.com/kubeflow-images-public/tf-mnist:gpu
name: tensorflow
env:
- name: TEST_TMPDIR
value: /tfdata
command: ["python", "/app/main.py"]
resources:
limits:
nvidia.com/gpu:
volumeMounts:
- name: kubeflow-nas-mnist
mountPath: "/tfdata"
volumes:
- name: kubeflow-nas-mnist
persistentVolumeClaim:
claimName: kubeflow-nas-mnist
restartPolicy: OnFailure
将该模板保存到mnist-simple-gpu-nas.yaml
, 并且创建TFJob
:
# kubectl create -f mnist-simple-gpu-nas.yaml
tfjob "mnist-simple-gpu-nas" created
通过kubectl logs
检查训练日志, 可以看到这里的数据无需下载,直接解包就可以开始训练:
# kubectl logs mnist-simple-gpu-master-825k-0-23033
[...]
2018-06-06 11:12:55.968267: I tensorflow/stream_executor/dso_loader.cc:139] successfully opened CUDA library libcupti.so.8.0 locally
Extracting /tfdata/tensorflow/input_data/train-images-idx3-ubyte.gz
Extracting /tfdata/tensorflow/input_data/train-labels-idx1-ubyte.gz
Extracting /tfdata/tensorflow/input_data/t10k-images-idx3-ubyte.gz
Extracting /tfdata/tensorflow/input_data/t10k-labels-idx1-ubyte.gz
Accuracy at step 0: 0.1446
Accuracy at step 10: 0.7515
Accuracy at step 20: 0.8397
Accuracy at step 30: 0.8606
...
Accuracy at step 980: 0.9642
Accuracy at step 990: 0.9662
Adding run metadata for 999
[...]
通过TFJob UI
查看训练状态
1. 可以通过kubectl命令查看Jupyter Hub的外网访问ip, 在本例子里,外网ip为120.55.145.9
# kubectl get svc -n kubeflow tf-job-dashboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
tf-job-dashboard LoadBalancer 172.19.9.247 120.55.145.9 80:30911/TCP 2d
注意由于Kubeflow的TFJob目前仅仅提供http访问,并不推荐在生产环境使用LoadBalancer模式的Service,建议在生产环境,可以使用
kubectl proxy
的模式访问
2. 查看TFJob
的UI界面
总结
tf-operator
是Kubeflow的第一个CRD实现,解决的是TensorFlow模型训练的问题,它提供了广泛的灵活性和可配置,可以与阿里云上的NAS,OSS无缝集成,并且提供了简单的UI查看训练的历史记录。可以称得上是数据科学家用于TensorFlow模型训练的利器,与此同时Kubeflow社区还提供了包括pytorch-operator等其他机器学习框架的支持。