摘要
算法、数据和算力称为人工智能的三大要素,如果没有算力的支撑,人工智能难以落地。而Nvidia GPU的强劲算力是AI模型训练加速的首选,但是它的价格也确实不菲。如何能够简单,有效同时低成本的使用Nvidia GPU的算力,使用阿里云容器服务+ECI+Arena的方案是一个可以参考的选项。
而一谈起Nvidia GPU,大家首先会想到的就是深度学习,传统的机器学习和数据分析的方法对GPU的利用却很少,实际上Nvidia有一个非常优秀的项目RAPIDS,全称Real-time Acceleration Platform for Integrated Data Science,是NVIDIA针对数据科学和机器学习推出的GPU加速库。更多RAPIDS信息请参见官方网站。这是一个致力于将GPU加速带给传统算法的项目,并且提供了与Pandas和scikit-learn一致的用法和体验。实际上RAPIDS有三个模块:cuDF相当于Pandas,cuML相当于scikit-learn,cuGraph则是处理图数据的。由于它的兼容性很好,我们可以把RAPIDS与深度学习框架结合,用cuDF来利用GPU加速处理数据,然后使用TensorFlow和PyTorch的深度学习模型框架处理任务。
在本文中,我们将介绍如何利用TensorFlow和Rapids实现在阿里云容器服务上以图搜图的功能;同时通过ECI实现GPU资源的使用即申请,秒级的GPU资源准备速度,完成即释放,用户无需提前准备GPU实例;而站在使用者的角度,他并不需要和Kubernetes的基础设施打交道,通过arena的命令行,就可以实现包含GPU的RAPIDS环境的构建和运行,并且完成对GPU基础设施的管理。
执行步骤
步骤1:准备集群
准备托管k8s的集群,所谓托管k8s的集群就是这个k8s的管控节点由阿里云承担资源和运维成本,并且创建了虚拟的Kubelet节点
需要您已创建好容器服务 Kubernetes集群。 您可以选择管版的Kubernetes集群。
由于需要运行系统组件容器,节点中至少有一个Worker节点。
- 安装虚拟节点,具体可以参考虚拟节点。
- 配置virtual-kubelet-autoscaler,当集群内的GPU资源不足的时候,通过virtual-kubelet-autoscaler将弹出使用GPU的ECI实例。具体参考文档
步骤2:从无到有运行arena创建RAPIDS服务
1.安装arena$ wget http://kubeflow.oss-cn-beijing.aliyuncs.com/arena-installer-0.3.0-b556a36-linux-amd64.tar.gz $ tar -xvf arena*.tar.gz $ cd arena-installer $ ./install.sh
2.先运行一下arena命令查看集群的GPU资源, 可以看到在该用户集群下,有一个真实节点并没有包含GPU资源,同时存在了一个虚拟节点,该节点并不真实存在,无需付费,同时它提供了无限的GPU资源可以扩展。
$ arena top node
arena top node
NAME IPADDRESS ROLE STATUS GPU(Total) GPU(Allocated)
cn-shanghai.192.168.1.248 192.168.1.248 <none> ready 0 0
virtual-kubelet 172.20.2.18 agent ready 1000 0
-----------------------------------------------------------------------------------------
Allocated/Total GPUs In Cluster:
0/1000 (0%)
3.再提交rapids任务前,我们需要做一些准备。准备的目的是加速创建过程和简化访问操作。
3.1.设置访问方式。将访问方式设置为LoadBalancer(该方法只是为了示例简单,并不推荐您在生产环境开放外网ip方访问)
$ find /charts/ -name "*.yaml" | xargs sed -i "s/NodePort/LoadBalancer/g"
3.2.加速启动速度
3.2.1.GPU的容器镜像通常很大,以本实验要使用的rapids容器镜像为例,它的容量为14.7GB.通常启动时间会在10分钟左右。而通过镜像缓存的能力可以将这个从无到有的过程缩短到20s左右。
docker images | grep rapids
registry.cn-shanghai.aliyuncs.com/tensorflow-samples/rapids-samples 0.8.2-cuda10.0-runtime-ubuntu16.04 4597a0334d41 12 days ago 14.7GB
3.2.2.而在serverless kubernetes中,你只需要创建一个ImageCache CRD,就可以直接使用镜像缓存的能力。
$ cat > imagecache.yaml << EOF
apiVersion: eci.alibabacloud.com/v1
kind: ImageCache
metadata:
name: imagecache-rapids
spec:
images:
- registry.cn-shanghai.aliyuncs.com/tensorflow-samples/rapids-samples:0.8.2-cuda10.0-runtime-ubuntu16.04
imageCacheSize:
50
EOF
$ kubectl create -f imagecache.yaml
3.2.3.提交后稍等片刻。查看ImageCache状态,其中CACHID可以做后面提交任务时指定的snapshot-id
$ kubectl get imagecache
NAME AGE CACHEID PHASE PROGRESS
imagecache-rapids 3d9h imc-uf6dxdji7txxxxx Ready 100%
具体操作可以参考文档
4.提交rapids的开发环境
$ arena serve custom \
--name=rapids \
--selector=type=virtual-kubelet \
--toleration=all \
--annotation=k8s.aliyun.com/eci-image-snapshot-id=imc-uf6dxdji7txxxxx \
--annotation=k8s.aliyun.com/eci-instance-type=ecs.gn5i-c8g1.2xlarge \
--gpus=1 \
-e=PASSWORD=mypassw0rd \
--restful-port=80 \
--image=registry.cn-shanghai.aliyuncs.com/tensorflow-samples/rapids-samples:0.8.2-cuda10.0-runtime-ubuntu16.04
configmap/rapids-201912011815-custom-serving created
configmap/rapids-201912011815-custom-serving labeled
service/rapids-201912011815 created
deployment.extensions/rapids-201912011815-custom-serving created
--selector=type=virtual-kubelet表示通过Virtual Node启动Pod
--annotation=k8s.aliyun.com/eci-instance-type=ecs.gn5i-c8g1.2xlarge表示指定使用ECI的实例类型,ecs.gn5i-c8g1.2xlarge代表阿里云P4机型。具体规格可以查看文档
--annotation=k8s.aliyun.com/eci-image-snapshot-id=imc-uf6dxdji7txxxxx指定3.2.3步中的CACHEID
-e=PASSWORD=mypassw0rd就是通过环境变量PASSWORD设置访问RAPIDS notebook
--gpus=1表示申请的GPU数目
4.查看访问地址,这里是ENDPOINT_ADDRESS和PORTS的组合, 在本示例中它是106.15.173.2:80。同时发现该任务在32秒的时候就可以变成Running状态
$ arena serve list
NAME TYPE VERSION DESIRED AVAILABLE ENDPOINT_ADDRESS PORTS
rapids CUSTOM 201911181827 1 1 105.13.58.3 restful:80
$ arena serve get rapids
arena serve get rapids
NAME: rapids
NAMESPACE: default
VERSION: 201912011815
DESIRED: 1
AVAILABLE: 1
SERVING TYPE: CUSTOM
ENDPOINT ADDRESS: 106.15.173.2
ENDPOINT PORTS: restful:80
AGE: 32s
INSTANCE STATUS AGE READY RESTARTS NODE
rapids-201912011815-custom-serving-6b54d5cd-swcwz Running 32s 1/1 0 N/A
5.再次查看集群的GPU使用情况,发现已经有一个GPU资源被占用了
$ arena top node
NAME IPADDRESS ROLE STATUS GPU(Total) GPU(Allocated)
cn-shanghai.192.168.1.248 192.168.1.248 <none> ready 0 0
virtual-kubelet 172.20.2.20 agent ready 1000 1
-----------------------------------------------------------------------------------------
Allocated/Total GPUs In Cluster:
1/1000 (0%)
6.如果想查询是哪个Pod占用了这个GPU, 可以在原有命令中加一个-d就可以看到具体的Pod名称。
$ arena top node -d
NAME: cn-shanghai.192.168.1.248
IPADDRESS: 192.168.1.248
ROLE: <none>
Total GPUs In Node cn-shanghai.192.168.1.248: 0
Allocated GPUs In Node cn-shanghai.192.168.1.248: 0 (0%)
-----------------------------------------------------------------------------------------
NAME: virtual-kubelet
IPADDRESS: 172.20.2.20
ROLE: agent
NAMESPACE NAME GPU REQUESTS
default rapids-201912011815-custom-serving-6b54d5cd-swcwz 1
Total GPUs In Node virtual-kubelet: 1000
Allocated GPUs In Node virtual-kubelet: 1 (0%)
-----------------------------------------------------------------------------------------
Allocated/Total GPUs In Cluster: 1/1000 (0%)
7.根据步骤4中的访问地址和端口,打开本地浏览器。输入http://{ENDPOINT ADDRESS}:{ENDPOINT PORT},在本例子中是http://105.13.58.3:80
说明: 推荐使用Chrome浏览器。
8.输入启动命令中设置的密码,然后单击Log in。 在本例子中,密码为mypassw0rd
步骤三:执行以图搜图的示例
1.进入示例所在目录cuml。
2.双击cuml_knn.ipynb文件。
3.单击。
说明: 单击一次执行一个cell,请单击至示例执行结束,详细说明请参见示例执行过程。
示例执行过程
图像搜索示例的执行过程分为三个步骤:处理数据集、提取图片特征和搜索相似图片。本文示例结果中对比了GPU加速的RAPIDS cuml KNN与CPU实现的scikit-learn KNN的性能。
1.处理数据集。
1.1 下载和解压数据集。 本文示例中使用了STL-10数据集,该数据集中包含10万张未打标的图片,图片的尺寸均为:96 x 96 x 3, 您可以使用其他数据集,为便于提取图片特征,请确保数据集中图片的尺寸相同。
本文示例提供了download_and_extract(data_dir)方法供您下载和解压STL-10数据集。RAPIDS镜像中已经将数据集下载到./data目录,您可以执行download_and_extract()方法直接解压数据集。
1.2. 读取图片。 从数据集解压出的数据为二进制格式,执行read_all_images(path_to_data)方法加载数据并转换为NHWC(batch, height, width, channels)格式,以便用Tensorflow提取图片特征。
1.3. 展示图片。 执行show_image(image)方法随机展示一张数据集中的图片。
1.4. 分割数据集。 按照9:1的比例把数据集分为两部分,分别用于创建图片索引库和搜索图片。
2.提取图片特征。 使用开源框架Tensorflow和Keras提取图片特征,其中模型为基于ImageNet数据集的ResNet50(notop)预训练模型。
2.1 设定Tensorflow参数。 Tensorflow默认使用所有GPU显存,我们需要留出部分GPU显存供cuML使用。您可以选择一种方法设置GPU显存参数:
方法1:依据运行需求进行显存分配。
config.gpu_options.allow_growth = True
方法2:设定可以使用的GPU显存比例。本示例中使用方法2,并且GPU显存比例默认设置为0.3,即Tensorflow可以使用整块GPU显存的30%,您可以依据应用场景修改比例。
config.gpu_options.per_process_gpu_memory_fraction = 0.3
2.2 下载ResNet50(notop)预训练模型。 连接公网下载模型(大小约91M),目前该模型已经被保存到/root/.keras/models/目录。
您可以执行model.summary()方法查看模型的网络结构。
2.2 提取图片特征。 对分割得到的两个图片数据集执行model.predict()方法提取图片特征。
搜索相似图片。
3.1 使用cuml KNN搜索相似图片。 通过k=3设置K值为3,即查找最相似的3张图片,您可以依据使用场景自定义K值。
其中,knn_cuml.fit()方法为创建索引阶段,knn_cuml.kneighbors()为搜索近邻阶段。
KNN向量检索耗时791 ms。
3.2 使用scikit-learn KNN搜索相似图片。 通过n_neighbors=3设置K值为3,通过n_jobs=-1设置使用所有CPU进行近邻搜索。
说明: ecs.gn5i-c8g1.2xlarge的配置为8 vCPU。
KNN向量检索耗时7分34秒。
3.3 对比cuml KNN和scikit-learn KNN的搜索结果。 对比两种方式的KNN向量检索速度,使用GPU加速的cuml KNN耗时791 ms,使用CPU的scikit-learn KNN耗时7min 34s。前者为后者的近600倍。
验证两种方式的输出结果是否相同,输出结果为两个数组:
- distance:最小的K个距离值。本示例中搜索了10000张图片,K值为3,因此distance.shape=(10000,3)。
- indices:对应的图片索引。indices.shape=(10000, 3)。
由于本示例所用数据集中存在重复图片,容易出现图片相同但索引不同的情况,因此使用distances,不使用indices对比结果。考虑到计算误差,如果两种方法得出的10000张图片中的3个最小距离值误差都小于1,则认为结果相同。
图片搜索结果
本示例从1万张搜索图片中随机选择5张图片并搜索相似图片,最终展示出5行4列图片。
第一列为搜索图片,第二列至第四列为图片索引库中的相似图片,且相似性依次递减。每张相似图片的标题为计算的距离,数值越大相似性越低。
步骤4:清理工作
$ arena serve delete rapids
service "rapids-201912011815" deleted
deployment.extensions "rapids-201912011815-custom-serving" deleted
configmap "rapids-201912011815-custom-serving" deleted
INFO[0000] The Serving job rapids with version 201912011815 has been deleted successfully
总结
本文介绍通过Arena+阿里云Serverless Kubernetes快速,简单,低成本的使用RAPIDS加速数据科学。