Ray分布式机器学习-Raydp(与Spark集成)

参考:官网文档

背景:

公司现在使用机器学习是通过python使用sklearn进行训练模型的。但是毕竟sklearn是单机版的包,无法分布式进行机器学习。

吐槽一下Spark ML,大家可以忽略:我有使用Scala语言通过Spark ML对数学科学家的Python Sklearn写好的代码进行复写。但是出现了一些问题,比如在写随机森林的时候,二分类还好,多分类问题的时候,问题就出来了,多分类的时候因为训练集label不均衡导致训练准确率0.1(手动添加哭的表情),使用过采样的方式再加上参数优化勉强准确率升到0.71,远远不及Python Sklearn的0.86(没办法,Sklearn的随机森林包有class_weigt=‘balance’的参数自动平衡)。

后来公司有安装Ray来实现分布式机器学习。处理步骤如下:

1、训练数据和预测数据的预处理需要手动ETL执行。

2、然后再ray.put到Ray节点上。

3、模型处理结束后生成的数据。

4、然后再ray.get到Hive表。

现在想使用一套代码将数据预处理、模型训练、写入Hive表集成到一起。

因此调研了一些Ray和Spark集成的东西。

截止2022-06-22,官网已经支持了SparkDataframe。文章内容可能有点过期,因为当时写文章时候,Ray是没有支持Spark的。

 

Ray与SparkDF集成

Ray的数据集加载介绍:

Ray Datasets are the standard way to load and exchange data in Ray libraries and applications. Datasets provide basic distributed data transformations such as map, filter, and repartition, and are compatible with a variety of file formats, datasources, and distributed frameworks.

Ray数据集是在Ray库和应用程序中加载和交换数据的标准方式。数据集提供基本的分布式数据转换,如映射、过滤和重分区,并与各种文件格式、数据源和分布式框架兼容。

过期信息:目前看来。Ray并没有支持Spark。但是官网是有规划的。

虽然现在不直接支持。但是官网有推荐使用 RayDP (Spark on Ray) 

第三方框架:

区别:

Spark on Ray:在机器学习端对使用Spark对数据进行预处理,然后拉取到机器学习端进行处理。

Ray on Spark:将机器学习端放入Spark数据端,在数据端对数据处理完直接进行机器学习。

 RayDP (Spark on Ray) (Git)(下载地址)

主要是使用Python代码去开发,使用PySpark对数据进行预处理,然后在Ray上进行训练。

版本要求:Python3.6以上,PySpark3.0以上,Ray1.3以上.

RayDP combines your Spark and Ray clusters, making it easy to do large scale data processing using the PySpark API and seemlessly use that data to train your models using TensorFlow and PyTorch.

RayDP 结合了您的 Spark 和 Ray 集群,可以轻松地使用 PySpark API 进行大规模数据处理,并使用 TensorFlow 和 PyTorch 无缝地使用这些数据来训练您的模型。

RayDP可以直接通过Spark去操作Hive数据,进行数据处理。处理结束后直接将数据用于Ray使用。

Analytics-zoo (Ray on Spark) (Git)(相关文档)

主要是使用Spark开发去内联Ray代码进行机器学习。RayOnSpark把Ray跑在了Spark大数据集群之上。

Analytics Zoo seamless scales TensorFlow, Keras and PyTorch to distributed big data (using Spark, Flink & Ray).

英特尔公司开源:Analytics Zoo 将 TensorFlow、Keras 和 PyTorch 无缝扩展到分布式大数据(使用 Spark、Flink 和 Ray)。

Analytics Zoo底层依赖于一系列现有的常用框架,包括主流的深度学习框架、分布式计算框架、Python数据处理库等,在这些框架之上搭建了一套非常完整的数据分析和人工智能的流水线,包括支持用户在Spark上跑分布式的 TensorFlow和PyTorch,只需要做很小的代码改动就可以在大数据平台运行主流的深度学习框架;对Spark DataFrame和ML Pipeline提供了原生的深度学习支持;也提供了轻量级的API对训练好的模型做线上推理。在流水线之上,Analytics Zoo提供了ML workflow,帮助用户自动化地去构建大规模的人工智能应用,比如对时间序列做可扩展的预测,以及分布式Cluster Serving。最上层对很多常见领域,如推荐、时间序列、计算机视觉、自然语言处理等等,提供了开箱即用的模型以及参考案例。

如果使用Ray on Spark 操作,操作步骤:

1、使用Ray进行机器学习代码开发。

2、使用Spark调用Ray进行训练。

运行架构

Spark会在Driver节点上起一个SparkContext的实例,SparkContext会在整个集群起多个Spark Executer执行Spark的任务。

除了SparkContext之外,RayOnSpark设计中还会在Spark Driver中创建一个RayContext的实例,利用现有的SparkContext将Ray在集群里启动起来,Ray的进程会伴随着在Spark Executer,包括一个Ray Master进程和其它的Raylet进程。

RayContext 也会在Spark Executer中创建RayManager来管理这些Ray的进程,任务结束后自动将Ray的进程关掉,同时释放Ray所占用的资源。

在同一个YARN集群上同时有Spark和Ray,这样我们就能够将in-memory的Spark RDD或DataFrame直接运行在Ray的应用中,使用Spark的数据做新兴人工智能应用的开发。

Raydp代码的:

下面是我自己测试并使用Raydp的代码,测试已经通过了。

读取HDFS并写入HDFS/本地。raydp好像没办法直接使用Hive元数据,我也没去研究写Catalog。都是一样的用法。

from ray.util.joblib import register_ray
from datetime import datetime
import joblib  # jbolib模块
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import re
import ray
import jieba
from sklearn.preprocessing import LabelEncoder
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from pyspark.sql.functions import col
import raydp

ray.shutdown()
raydp.stop_spark()
ray.init(address='auto')
#注意参数不要写太大,否则报错。
spark = raydp.init_spark('raydp_demo',
                         num_executors=2,
                         executor_cores=2,
                         executor_memory='4G'
                         )

print("创建完成,开始读取HDFS数据,并转换为PD")
train = ray.put(spark.read.csv("hdfs://127.0.0.1:4007/data/tmp/LYL/tarin/catemapping.csv").toDF("x1", "x2", "x3", "x4", "x5", "x6","x7").toPandas())
pre = ray.put(spark.read.csv("hdfs://127.0.0.1:4007/data/tmp/LYL/p/taoxipre_p.csv").toDF("skuid", "p1", "p2", "p3").toPandas())
cate  = ray.put(spark.read.csv("hdfs://127.0.0.1:4007/data/tmp/LYL/c/fenlei_hdfs.csv").toDF("b1", "b2").toPandas())
print("数据读取转换成功")


#机器学习处理过程不写了
@ray.remote
class xx(object):
   def model_predict
   return data

#Ray并行训练
ply=xx.remote(train,pre,cate)
data_result = plys.model_predict.remote()
data_results = ray.get(data_result)

# 离线写入本地 传入参数为本地path
data_results.to_csv(sys.argv[1], header=False, index=False)

fs_directory="hdfs://127.0.0.1:4007/data/tmp/LYL/out/"
# 写入HDFS
spark.createDataFrame(data_results).coalesce(1).write.csv(path=fs_directory, header=False, sep=",", mode='overwrite')

raydp.stop_spark()
ray.shutdown()


被官网的demo带跑偏了(with joblib.parallel_backend('ray'):写法)。写完跑的卡资源。又优化了一遍。

注意注意注意!!!!

  • class 需要Ray并行执行,需要@ray.remote,且分发的class需要加(object)否则报错。
  • 数据需要需要Ray并行执行,需要用ray.put(data)把数据加入到对象存储中。
  • 数据处理结束,从对象存储中取出需要ray.get(data)。

当然有的写的多余了。比如都CSV了,还手动加Schema。如果是读取text的话,可以手动加。读取指定格式,比如Hive的默认格式 \001  ,需要使用 spark.read.option("sep", "\001").csv(path)

读取了HDFS数据,转换PD用于训练及测试。

建议:

ray.shutdown()
raydp.stop_spark()

一定记住注册的先后顺序。ray和raydp先关,然后都初始化后,再进行所有操作(建立spark啥的)。

每次初始化前都要执行一下,我这边遇到了不初始化占用没释放的问题。

官网虽然推荐了RayMLDataset.from_spark方法来写入HDFS。但是我这边使用时候报错。反正都是一句代码的事,我用原生的比较舒服。

2021-09-17

哔了狗了。根本不用装RayDP,直接装pySpark的包,就可以直接使用。

记得安装包,和配置环境变量,还有集群配置文件记得配置到pySpark包下。

# 本地spark 启动
from pyspark.sql import SparkSession
import os

memory = '10g'
pyspark_submit_args = ' --driver-memory ' + memory + ' pyspark-shell'
os.environ["PYSPARK_SUBMIT_ARGS"] = pyspark_submit_args

from pyspark import SparkContext, SQLContext, SparkConf
from pyspark.sql import SparkSession
from pyspark.sql import functions as fn
spark = SparkSession \
    .builder \
    .appName("demo-local") \
    .enableHiveSupport()\
    .config('spark.sql.shuffle.partitions', 200).config("spark.default.parallelism", "200")\
    .config('spark.driver.memory', '4g').config('spark.driver.maxResultsSize', '4g') \
    .config('spark.executor.memory', '4g').config('spark.executor.cores', '5').config('spark.executor.instances','4') \
    .getOrCreate()


#--------------------------------------------

# 远程集群spark 启动
from pyspark.sql import SparkSession
import os

memory = '10g'
pyspark_submit_args = ' --driver-memory ' + memory + ' pyspark-shell'
os.environ["PYSPARK_SUBMIT_ARGS"] = pyspark_submit_args

from pyspark import SparkContext, SQLContext, SparkConf
from pyspark.sql import SparkSession
from pyspark.sql import functions as fn
spark = SparkSession \
    .builder.master('yarn') \
    .appName("demo-yarn") \
    .enableHiveSupport()\
    .config('spark.yarn.queue','bi') \
    .config('spark.sql.shuffle.partitions', 200).config("spark.default.parallelism", "200")\
    .config('spark.driver.memory', '4g').config('spark.driver.maxResultsSize', '4g') \
    .config('spark.executor.memory', '12g').config('spark.executor.cores', '4').config('spark.executor.instances','10') \
    .getOrCreate()

#--------------------------------------------
#两种读取方式
#1、spark读取
#2、pandas读取
#扩展:
#spark->pandas  SParkDf.toPandas()
#pandas->spark  spark.createDataFrame(PandasDF)

# 1、使用Spark去读取CSV文件
val sdf = spark.read.option("header", "true").csv("file:///data/jupyter/lyl/测试.csv")
#  Spark读取Hive文件
val spark_df=spark.sql("select * from table")

#2、将pandas的df转换为spark的虚表
 import pandas as pd 
 df =pd.read_csv("测试.csv")
 df
 list1=list(map(str,list(df['order_id'].values)))
 tr=tuple(list1)
 tr
#转换为Spark虚表
 spark.createDataFrame(df).createTempView("df1")
# 创建完虚表持久化存内存,除非SparkSession断开,否则虚表不会自动删除,以下命令为手动删除
 spark.sql("drop table df1") 



#--------------------------------------------
#两种写入方式
# 1、API写入
fs_directory="/data/tmp/lyl/out/"
spark.createDataFrame(spark_df).coalesce(10).write.csv(path=fs_directory, header=False, sep=",", mode='overwrite')

# 2、SparkSql写入
spark.createDataFrame(spark_df).createTempView("df1")
spark.sql("insert overwrite table select * from df1")

调研了一周的RayDP。结果发现可以直接搞。瞬间郁闷了。之前有个问题是,我的Spark是分布式的对数据进行处理。怎么能put到Ray上呢。

比如我的Ray在100、101、102节点

Spark在10~99节点。

难道会自动把数据汇总到100、101、102节点么?

谁在做这个呢?Spark还是Ray呢。

测试了一下,是可以直接汇总到Ray上的。

带着这个疑问,没再深钻源码(不是我没有极客精神,主要是没时间了)。

马上调研Alluxio了。没时间继续看Ray的问题了。公司要求是能用就行。

2021-12-01补充:

1、ray1.5版本后,当在其他节点(非ray节点)执行python脚本时,可以初始化ray时候,使用ray的client去链接。

ray.init("ray://<head_node_host>:<port>")

2、最近我们公司数学科学家进行卷积神经网络处理时,发现ray虽然链接到集群,但是提交只在一台节点上执行,并且只在1台节点的1个worker(核)上执行。(1核满载百核围观)

原来是只申请了一个Ray的Actor导致的。我这边建议数学科学家把社交网络图分成十份,然后申请10个Actor,分别把数据放到对应的,分布式计算。不太清楚会对数据结果产生多大影响。回头问问数学科学家训练后的结果。有知道的麻烦评论区告诉我。

3、对于sklearn包还是建议使用with joblib.parallel_backend('ray'):写法去做。对于随机森林RF这类建议使用申请Actor的方式进行并行训练。大大降低训练时间。

2022-06-26问题补充:

发现web页面看板dashboard无法打开。

排查思路:

1、查看服务器日志没发现异常。正常

2、通过ray status查看状态一切正常。正常

3、lsof -i:8265 查看端口是否使用,发现正在使用。正常

4、curl ip:8265 发现异常了。发现异常

localhost:8265和127.0.0.1:8265可以访问到,但是IP直接是访问不到的。

5、查看绑定端口,发现绑定端口是127.0.0.1.绑定本地是访问不到的。

6、重新启动ray。绑定指定IP。访问就正常了。 

 

 

转载的话请备注我的博客地址!

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值