『阿里云云栖号』阿里云云效团队在家高效开发实录,战“疫”期( 二 )


Mars的开发过程中 , 我们核心关注的几点包括:
我们希望Mars足够简单 , 只要会用Numpy、pandas或scikit-learn就会用Mars 。 避免重复造轮子 , 我们希望能利用到这些库已有的成果 , 只需要能让他们被调度到多核/多机上即可 。 声明式和命令式兼得 , 用户可以在这两者之间自由选择 , 灵活度和性能兼而有之 。 足够健壮 , 生产可用 , 能应付各种failover的情况 。当然这些是我们的目标 , 也是我们一直努力的方向 。
Marstensor:Numpy的并行和分布式加速器上面说过 , 我们的目标之一是 , 只要会用Numpy等数据科学包 , 就会用Mars 。 我们直接来看代码 , 还是以蒙特卡洛为例 。 变成Mars的代码是什么样子呢?
importmars.tensorasmtN=10**10data=https://pcff.toutiao.jxnews.com.cn/p/20200416/mt.random.uniform(-1,1,size=(N,2))inside=(mt.sqrt((data**2).sum(axis=1))<1).sum()pi=(4*inside/N).execute()print('pi:%.5f'%pi)可以看到 , 区别就只有两处:importnumpyasnp变成importmars.tensorasmt , 后续的np.都变成mt.;pi在打印之前调用了一下.execute()方法 。
也就是默认情况下 , Mars会按照声明式的方式 , 代码本身移植的代价极低 , 而在真正需要一个数据的时候 , 通过.execute()去触发执行 。 这样能最大限度得优化性能 , 以及减少中间过程内存消耗 。
这里 , 我们还将数据的规模扩大了1000倍 , 来到了100亿个点 。 之前1/1000的数据量的时候 , 在我的笔记本上需要757ms;而现在数据扩大一千倍 , 光data就需要150G的内存 , 这用Numpy本身根本无法完成 。 而使用Mars , 计算时间只需要3min44s , 而峰值内存只需要1G左右 。 假设我们认为内存无限大 , Numpy需要的时间也就是之前的1000倍 , 大概是12min多 , 可以看到Mars充分利用了多核的能力 , 并且通过声明式的方式 , 极大减少了中间内存占用 。
前面说到 , 我们试图让声明式和命令式兼得 , 而使用命令式的风格 , 只需要在代码的开始配置一个选项即可 。
importmars.tensorasmtfrommars.configimportoptionsoptions.eager_mode=True#打开eagermode后 , 每一次调用都会立即执行 , 行为和Numpy就完全一致N=10**7data=https://pcff.toutiao.jxnews.com.cn/p/20200416/mt.random.uniform(-1,1,size=(N,2))inside=(mt.linalg.norm(data,axis=1)<1).sum()pi=4*inside/N#不需要调用.execute()了print('pi:%.5f'%pi.fetch())#目前需要fetch()来转成float类型 , 后续我们会加入自动转换MarsDataFrame:pandas的并行和分布式加速器看过怎么样轻松把Numpy代码迁移到Marstensor , 想必读者也知道怎么迁移pandas代码了 , 同样也只有两个区别 。 我们还是以movielens的代码为例 。
importmars.dataframeasmdratings=md.read_csv('ml-20m/ratings.csv')ratings.groupby('userId').agg({'rating':['sum','mean','max','min']}).execute()MarsLearn:scikit-learn的并行和分布式加速器MarsLearn也同理 , 这里就不做过多阐述了 。 但目前Marslearn支持的scikit-learn算法还不多 , 我们也在努力移植的过程中 , 这需要大量的人力和时间 , 欢迎感兴趣的同学一起参与 。
importmars.dataframeasmdfrommars.learn.neighborsimportNearestNeighborsdf=md.read_csv('data.csv')#输入是CSV文件 , 包含20万个向量 , 每个向量10个元素nn=NearestNeighbors(n_neighbors=10)nn.fit(df)#这里fit的时候也会整体触发执行 , 因此机器学习的高层接口都是立即执行的neighbors=nn.kneighbors(df).fetch()#kneighbors也已经触发执行 , 只需要fetch数据这里要注意的是 , 对于机器学习的fit、predict等高层接口 , MarsLearn也会立即触发执行 , 以保证语义的正确性 。
RAPIDS:GPU上的数据科学相信细心的观众已经发现 , GPU好像没有被提到 。 不要着急 , 这就要说到RAPIDS 。