Xgboost: [DISCUSSÃO] Integração com PySpark

Criado em 25 out. 2016  ·  53Comentários  ·  Fonte: dmlc/xgboost

Acabei de notar que existem alguns pedidos de integração com PySpark http://dmlc.ml/2016/03/14/xgboost4j-portable-distributed-xgboost-in-spark-flink-and-dataflow.html

Também recebi alguns e-mails de usuários discutindo o mesmo assunto

Eu gostaria de iniciar uma discussão aqui sobre se / quando devemos iniciar este trabalho

@tqchen @terrytangyuan

python

Comentários muito úteis

@CodingCat @tqchen A comunidade de ciência de dados definitivamente se beneficiará com a implementação do XGboost no PySpark, porque:

  • Em geral, Python é a linguagem nº 3 em março de 2017, com 10,2% de popularidade (contra 1,8% para Scala)
    http://redmonk.com/sogrady/2017/03/17/language-rankings-1-17/
    https://jobsquery.it/stats/language/group;
  • Em termos de desempenho PySpark vs Scala, eu diria que não importa muito porque é quase tudo Scala sob o capô para o Spark, certo?
  • Eu conheço, pelo menos, 3 startups de IA que usam PySpark para produção e, de forma mais geral, Python é muito mais popular para cientistas de dados e em demanda no mercado de trabalho (
    http://r4stats.com/articles/popularity/)
    Minha conclusão: XGboost implementado no PySpark terá maior impacto para DataScience entre todas as outras implementações.
    (PS, uma vez estável, certifique-se de que Cloudera o implementa)

Todos 53 comentários

@CodingCat Você sabe o quão grande é a comunidade PySpark? A maioria das pessoas usa apenas a API Scala. Parece que muitas coisas precisam ser reimplementadas em Python - corrija-me se eu estiver errado.

Acho que o PySpark é bastante prevalente na comunidade de cientistas de dados, o que significa no cenário de prototipagem rápida, etc. Ouvi falar de muitos casos em que cientistas de dados usam o pySpark para analisar grandes volumes de dados.

Por outro lado, a maioria dos cenários de nível de produção são baseados na API Scala (eu só conheço um único caso em que as pessoas estão usando o PySpark em produção em grande escala)

Sim, sinto que a API Python atual deve ser capaz de lidar com a maioria das necessidades de prototipagem. Pessoalmente, me preocupo mais com o Spark quando queremos mais coisas prontas para produção. Talvez devêssemos deixar a discussão aqui para que as pessoas possam discutir suas necessidades. Nesse ínterim, seria ótimo se você pudesse fornecer alguns detalhes sobre abordagens / estimativas / etapas para a integração.

acabei de notar algumas discussões na comunidade

http://apache-spark-developers-list.1001551.n3.nabble.com/Blocked-PySpark-changes-td19712.html

parece que o desenvolvimento do PySpark está atrasado ... como biblioteca downstream, voto em hold on para me dedicar à integração do PySpark .....

Sim, também é difícil depurar quaisquer problemas que você encontrou (pelo menos quando eu estava tentando no ano passado) ...

No roteiro (# 873), dizia Python distribuído foi implementado. Isso significa que xgboost pode ser executado em um cluster hadoop com python? (Não estou me referindo a pyspark)

sim veja o exemplo postado no link

Qual é a diferença entre executar o xgboost no cluster hadoop com python e executar o xgboost no cluster hadoop com scala api? Existem grandes diferenças de desempenho?
Acho que ainda há muitas pessoas usando o pyspark como modelo de produção.

@ yiming-chen, o objetivo do xgboost4j-spark é unificar ETL e treinamento de modelo no mesmo pipeline

a questão se resume a qual idioma os usuários usam ao fazer ETL? com base na minha observação e experiência, 95% dos usuários estão construindo seu sistema ETL com scala

@CodingCat Não sei de onde você tirou sua estatística de 95%, mas o PySpark é definitivamente muito usado em minha experiência. Por exemplo, estamos tentando integrar o Airflow para agendar o trabalho para nosso pipeline e o Python seria adequado para essa situação.

@berch PySpark é amplamente utilizado por você e você vai integrar com o fluxo de ar ... é relevante para o que eu disse?

@CodingCat @tqchen A comunidade de ciência de dados definitivamente se beneficiará com a implementação do XGboost no PySpark, porque:

  • Em geral, Python é a linguagem nº 3 em março de 2017, com 10,2% de popularidade (contra 1,8% para Scala)
    http://redmonk.com/sogrady/2017/03/17/language-rankings-1-17/
    https://jobsquery.it/stats/language/group;
  • Em termos de desempenho PySpark vs Scala, eu diria que não importa muito porque é quase tudo Scala sob o capô para o Spark, certo?
  • Eu conheço, pelo menos, 3 startups de IA que usam PySpark para produção e, de forma mais geral, Python é muito mais popular para cientistas de dados e em demanda no mercado de trabalho (
    http://r4stats.com/articles/popularity/)
    Minha conclusão: XGboost implementado no PySpark terá maior impacto para DataScience entre todas as outras implementações.
    (PS, uma vez estável, certifique-se de que Cloudera o implementa)

sinta-se à vontade para enviar um PR, você encontrará o custo

para evitar voltar ao tópico uma e outra vez, fecharei a discussão com a conclusão de que

  • Eu, pessoalmente, não votarei para continuar este esforço para integrar o PySpark (por enquanto)

  • qualquer outra pessoa é mais do que bem-vinda para contribuir nisso, no entanto, precisamos considerar pelo menos o seguinte:

    • não introduza outro pacote python

    • compatibilidade com versões anteriores para a API Python atual ao implementar integração

    • lidar com os recursos atrasados ​​do ML do pyspark

Portanto, não podemos usar o pyspark para carregar o modelo XGBoost-spark? @CodingCat

então, na verdade, scala XGBoost pode ser envolvido de forma mais menos dolorosa na API do PySpark JavaEstimator . Joguei um pouco e tenho esse protótipo:

from pyspark.ml.wrapper import JavaEstimator, JavaModel
from pyspark.ml.param.shared import *
from pyspark.ml.util import *
from pyspark.context import SparkContext

class XGBoost(JavaEstimator, JavaMLWritable, JavaMLReadable, HasRegParam, HasElasticNetParam):

    def __init__(self, paramMap = {}):
        super(XGBoost, self).__init__()
        scalaMap = SparkContext._active_spark_context._jvm.PythonUtils.toScalaMap(paramMap)
        self._java_obj = self._new_java_obj(
            "ml.dmlc.xgboost4j.scala.spark.XGBoostEstimator", self.uid, scalaMap)
        self._defaultParamMap = paramMap
        self._paramMap = paramMap

    def setParams(self, paramMap = {}):
        return self._set(paramMap)

    def _create_model(self, javaTrainingData):
        return JavaModel(javaTrainingData)

Acho que ainda precisa de algum trabalho, mas consegui executar Xgboost no PySpark com ele.

Wieslaw agradece por compartilhar o snippet de código no wrapper XGBoost PySpark. Você pode compartilhar o código ao invocar a classe XGBoost com os parâmetros apropriados?

Obrigado

@wpopielarski , é um trabalho incrível que você fez. Você pode compartilhar o código ao invocar o XGBoost com os parâmetros necessários? Isso seria uma grande ajuda!

isso é algo como:

        from app.xgboost import XGBoost
        xgboost_params = {
            "eta"  : 0.023,
            "max_depth" : 10,
            "min_child_weight" : 0.3,
            "subsample" : 0.7,
            "colsample_bytree" : 0.82,
            "colsample_bylevel" : 0.9,
            "base_score" : base_score,
            "eval_metric" : "auc",
            "seed" : 49,
            "silent" : 1,
            "objective" : "binary:logistic",
            "round" : 10,
            "nWorkers" : 2,
            "useExternalMemory" : True
        }
        xgboost_estimator = XGBoost.XGBoost(xgboost_params)
...
        model = xgboost_estimator.fit(data)

Estou quase fazendo uma RP com o suporte adequado da PySpark.

@thesuperzapper , isso é ótimo!

Quanto tempo você acha que demoraria para embrulhar? Compartilhe ideias enquanto progride.

Obrigado!

oi, escrevo uma versão simples com ParamGridBuilder caso haja alguém interessado, é muito fácil personalizá-lo.

  • 1 crie um diretório de pacote mkdir -p ml/dmlc/xgboost4j/scala em qualquer diretório PYTHONPATH válido.
  • 2 copie o código abaixo para ml/dmlc/xgboost4j/scala/spark.py
from pyspark.ml.classification import JavaClassificationModel, JavaMLWritable, JavaMLReadable, TypeConverters, Param, \
    Params, HasFeaturesCol, HasLabelCol, HasPredictionCol, HasRawPredictionCol, SparkContext
from pyspark.ml.wrapper import JavaModel, JavaWrapper, JavaEstimator


class XGBParams(Params):
    '''

    '''
    eta = Param(Params._dummy(), "eta",
                "step size shrinkage used in update to prevents overfitting. After each boosting step, we can directly get the weights of new features. and eta actually shrinks the feature weights to make the boosting process more conservative",
                typeConverter=TypeConverters.toFloat)
    max_depth = Param(Params._dummy(), "max_depth",
                      "maximum depth of a tree, increase this value will make the model more complex / likely to be overfitting. 0 indicates no limit, limit is required for depth-wise grow policy.range: [0,∞]",
                      typeConverter=TypeConverters.toInt)
    min_child_weight = Param(Params._dummy(), "min_child_weight",
                             "minimum sum of instance weight (hessian) needed in a child. If the tree partition step results in a leaf node with the sum of instance weight less than min_child_weight, then the building process will give up further partitioning. In linear regression mode, this simply corresponds to minimum number of instances needed to be in each node. The larger, the more conservative the algorithm will berange: [0,∞]",
                             typeConverter=TypeConverters.toFloat)
    max_delta_step = Param(Params._dummy(), "max_delta_step",
                           "Maximum delta step we allow each tree’s weight estimation to be. If the value is set to 0, it means there is no constraint. If it is set to a positive value, it can help making the update step more conservative. Usually this parameter is not needed, but it might help in logistic regression when class is extremely imbalanced. Set it to value of 1-10 might help control the update.",
                           typeConverter=TypeConverters.toInt)
    subsample = Param(Params._dummy(), "subsample",
                      "subsample ratio of the training instance. Setting it to 0.5 means that XGBoost randomly collected half of the data instances to grow trees and this will prevent overfitting.",
                      typeConverter=TypeConverters.toFloat)
    colsample_bytree = Param(Params._dummy(), "colsample_bytree",
                             "subsample ratio of columns when constructing each tree",
                             typeConverter=TypeConverters.toFloat)
    colsample_bylevel = Param(Params._dummy(), "colsample_bylevel",
                              "subsample ratio of columns for each split, in each level.",
                              typeConverter=TypeConverters.toFloat)
    max_leaves = Param(Params._dummy(), "max_leaves",
                       "Maximum number of nodes to be added. Only relevant for the ‘lossguide’ grow policy.",
                       typeConverter=TypeConverters.toInt)

    def __init__(self):
        super(XGBParams, self).__init__()

class XGBoostClassifier(JavaEstimator, JavaMLWritable, JavaMLReadable, XGBParams,
                        HasFeaturesCol, HasLabelCol, HasPredictionCol, HasRawPredictionCol):
    def __init__(self, paramMap={}):
        super(XGBoostClassifier, self).__init__()
        scalaMap = SparkContext._active_spark_context._jvm.PythonUtils.toScalaMap(paramMap)
        self._java_obj = self._new_java_obj("ml.dmlc.xgboost4j.scala.spark.XGBoostEstimator", self.uid, scalaMap)
        self._defaultParamMap = paramMap
        self._paramMap = paramMap

    def setParams(self, paramMap={}):
        return self._set(paramMap)

    def _create_model(self, java_model):
        return XGBoostClassificationModel(java_model)


class XGBoostClassificationModel(JavaModel, JavaClassificationModel, JavaMLWritable, JavaMLReadable):

    def getBooster(self):
        return self._call_java("booster")

    def saveBooster(self, save_path):
        jxgb = JavaWrapper(self.getBooster())
        jxgb._call_java("saveModel", save_path)
  • 3 jogue-o como um modelo normal do pyspark!

@AakashBasuRZT @haiy , agora estamos trabalhando nisso adequadamente na edição nº 3370, com PR nº 3376 fornecendo suporte inicial.

@haiy, você poderia me mostrar um trecho de código que se encaixa no classificador em algum conjunto de dados arbitrário? Eu segui os pontos 1 e 2 que você delineou, mas não consigo entender o seu terceiro ponto.

@sagnik-rzt verifique este exemplo

@haiy , estou tentando executar isto:

import pyspark
import pandas as pd
from dmlc.xgboost4j.scala.spark import XGBoostClassifier
from sklearn.utils import shuffle

sc = pyspark.SparkContext('local[2]')
spark = pyspark.sql.SparkSession(sc)
df = pd.DataFrame({'x1': range(10), 'x2': [10] * 10, 'y': shuffle([0 for i in range(5)] + [1 for i in range(5)])})
sdf = spark.createDataFrame(df)
X = sdf.select(['x1', 'x2'])
Y = sdf.select(['y'])
print(X.show(5))

params = {'objective' :'binary:logistic', 'n_estimators' : 10, 'max_depth' : 3, 'learning_rate' : 0.033}
xgb_model = XGBoostClassifier(params)

e está dando esta exceção:

Traceback (most recent call last):
  File "/home/sagnikb/PycharmProjects/auto_ML/pyspark_xgboost.py", line 20, in <module>
    xgb_model = XGBoostClassifier(params)
  File "/usr/lib/ml/dmlc/xgboost4j/scala/spark.py", line 47, in __init__
    self._java_obj = self._new_java_obj("ml.dmlc.xgboost4j.scala.spark.XGBoostEstimator", self.uid, scalaMap)
  File "/usr/local/lib/python3.6/dist-packages/pyspark/ml/wrapper.py", line 63, in _new_java_obj
    return java_obj(*java_args)
TypeError: 'JavaPackage' object is not callable

Error in sys.excepthook:
Traceback (most recent call last):
  File "/home/sagnikb/PycharmProjects/auto_ML/pyspark_xgboost.py", line 20, in <module>
    xgb_model = XGBoostClassifier(params)
  File "/usr/lib/ml/dmlc/xgboost4j/scala/spark.py", line 47, in __init__
    self._java_obj = self._new_java_obj("ml.dmlc.xgboost4j.scala.spark.XGBoostEstimator", self.uid, scalaMap)
  File "/usr/local/lib/python3.6/dist-packages/pyspark/ml/wrapper.py", line 63, in _new_java_obj
    return java_obj(*java_args)
TypeError: 'JavaPackage' object is not callable
Exception ignored in: <bound method JavaParams.__del__ of XGBoostClassifier_4f9eb5d1388e9e1424a4>
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/pyspark/ml/wrapper.py", line 105, in __del__
    SparkContext._active_spark_context._gateway.detach(self._java_obj)
  File "/usr/local/lib/python3.6/dist-packages/py4j/java_gateway.py", line 1897, in detach
    java_object._detach()
AttributeError: 'NoneType' object has no attribute '_detach'

Ambiente:
Python 3.6
Spark 2.3
Scala 2.11

@ sagnik-rzt
não tenho certeza, mas você adiciona xgboost-spark.jar com deps no caminho de classe do spark?

@wpopielarski Ei, não, não fiz isso. Alguma ideia de onde posso encontrar esse arquivo jar?

@ sagnik-rzt oi, faça o download do jar aqui , é o jar oficial de gordura spark4j. . Sorrry, acabei de descobrir que meu jar foi construído com base no mac, tente construí-lo como @wpopielarski sugere. E coloque-o no diretório de depuração de faíscas, como $SPARK_HOME/jars .

precisa construí-lo seu próprio :), com maven e perfil assembly que constrói jarra gorda em jvm-packages/xgboost-spark/target ou assim.

@sagnik-rzt não tenho certeza do que você vai fazer, mas para construir um jar gordo para seu sistema operacional, basta clonar o projeto dmlc xgboost github, cd para jvm-packages e executar mvn com assemby profile. Não tenho a menor idéia de como escrever um arquivo de compilação do Gradle.

Certo, criei um jar grande com dependências e copiei e colei em $ SPARK_HOME / jars.
No entanto, a mesma exceção ainda se aplica:

Traceback (most recent call last):
  File "/home/sagnikb/PycharmProjects/xgboost/test_import.py", line 21, in <module>
    clf = xgb(params)
  File "/usr/lib/ml/dmlc/xgboost4j/scala/spark.py", line 48, in __init__
    self._java_obj = self._new_java_obj("dmlc.xgboost4j.scala.spark.XGBoostEstimator", self.uid, scalaMap)
  File "/usr/local/lib/python3.6/dist-packages/pyspark/ml/wrapper.py", line 63, in _new_java_obj
    return java_obj(*java_args)
TypeError: 'JavaPackage' object is not callable

desculpe, mas você executa em cluster, localmente a partir de algum projeto IDE? Se você é
usando spark-submit é melhor adicionar dependências ao switch --jars

29/06/2018 14:30 GMT + 02: 00 sagnik-rzt [email protected] :

Ok, então eu construí um grande jar com dependências e, em seguida, copiei e colei
para $ SPARK_HOME / jars.
No entanto, a mesma exceção ainda se aplica:

Traceback (última chamada mais recente):
Arquivo "/home/sagnikb/PycharmProjects/xgboost/test_import.py", linha 21, em
clf = xgb (params)
Arquivo "/usr/lib/ml/dmlc/xgboost4j/scala/spark.py", linha 48, em __init__
self._java_obj = self._new_java_obj ("dmlc.xgboost4j.scala.spark.XGBoostEstimator", self.uid, scalaMap)
Arquivo "/usr/local/lib/python3.6/dist-packages/pyspark/ml/wrapper.py", linha 63, em _new_java_obj
return java_obj (* java_args)
TypeError: o objeto 'JavaPackage' não pode ser chamado

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/dmlc/xgboost/issues/1698#issuecomment-401339910 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/ALEzS3JmKjO0AZ6JMzcixwCce0_3zRM0ks5uBh3ogaJpZM4KgAY_
.

No momento, estou trabalhando no rebaseamento do # 3376 para o novo branch de faísca. Nesse ínterim, algumas pessoas perguntaram como usar o código atual no XGBoost-0.72.

Aqui está um arquivo zip com o código pyspark para XGBoost-0.72.
Download: sparkxgb.zip

Tudo que você precisa fazer é:

  1. Adicione os jars normais do Scala XGBoost e dependências ao seu trabalho. (por exemplo, usando --jars ou a configuração spark.jars ).
  2. Assim que o trabalho for iniciado, execute-o em python: (ou em qualquer local que todos os executores possam ver)
sc.addPyFile("hdfs:///XXXX/XXXX/XXXX/sparkxgb.zip")
  1. Teste com o seguinte código. (Supondo que você moveu sample_binary_classification_data.txt para um local acessível, normalmente é em $SPARK_HOME/data/mllib/sample_binary_classification_data.txt )
from sparkxgb import XGBoostEstimator

# Load Data
dataPath = "sample_binary_classification_data.txt"
dataDF = spark.read.format("libsvm").load(dataPath)

# Split into Train/Test
trainDF, testDF = dataDF.randomSplit([0.8, 0.2], seed=1000)

# Define and train model
xgboost = XGBoostEstimator(
    # General Params
    nworkers=1, nthread=1, checkpointInterval=-1, checkpoint_path="",
    use_external_memory=False, silent=0, missing=float("nan"),

    # Column Params
    featuresCol="features", labelCol="label", predictionCol="prediction", 
    weightCol="weight", baseMarginCol="baseMargin", 

    # Booster Params
    booster="gbtree", base_score=0.5, objective="binary:logistic", eval_metric="error", 
    num_class=2, num_round=2, seed=None,

    # Tree Booster Params
    eta=0.3, gamma=0.0, max_depth=6, min_child_weight=1.0, max_delta_step=0.0, subsample=1.0,
    colsample_bytree=1.0, colsample_bylevel=1.0, reg_lambda=0.0, alpha=0.0, tree_method="auto",
    sketch_eps=0.03, scale_pos_weight=1.0, grow_policy='depthwise', max_bin=256,

    # Dart Booster Params
    sample_type="uniform", normalize_type="tree", rate_drop=0.0, skip_drop=0.0,

    # Linear Booster Params
    lambda_bias=0.0
)
xgboost_model = xgboost.fit(trainDF)

# Transform test set
xgboost_model.transform(testDF).show()

# Write model/classifier
xgboost.write().overwrite().save("xgboost_class_test")
xgboost_model.write().overwrite().save("xgboost_class_test.model")

Observação:

  • Isso só funcionará para Spark 2.2+
  • Pipelines e ParamGridBuilder são meio que suportados, use o objeto pipeline modificado com from sparkxgb.pipeline import XGBoostPipeline,XGBoostPipelineModel como faria com os objetos normais.
  • Você deve usar float ("+ inf") para valores ausentes ao invés de float ("nan") para valores nulos serem tratados corretamente devido a um erro no XGboost-0.72.
  • Você não pode carregar de volta objetos de modelo não treinados, (consulte # 3035)
  • Esta API mudará com o lançamento completo do suporte pyspark.

@thesuperzapper Estou tentando testar isso com o pyspark no notebook jupyter.

Meu sistema:
python 3.6.1
xgboost 0.72
faísca 2.2.0
java 1.8
scala 2.12

Quando tento carregar o XGBoostEstimator, recebo:

Exception in thread "Thread-19" java.lang.NoClassDefFoundError: ml/dmlc/xgboost4j/scala/EvalTrait
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetPublicMethods(Class.java:2902)
    at java.lang.Class.getMethods(Class.java:1615)
    at py4j.reflection.ReflectionEngine.getMethodsByNameAndLength(ReflectionEngine.java:345)
    at py4j.reflection.ReflectionEngine.getMethod(ReflectionEngine.java:305)
    at py4j.reflection.ReflectionEngine.getMethod(ReflectionEngine.java:326)
    at py4j.Gateway.invoke(Gateway.java:272)
    at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
    at py4j.commands.CallCommand.execute(CallCommand.java:79)
    at py4j.GatewayConnection.run(GatewayConnection.java:214)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: ml.dmlc.xgboost4j.scala.EvalTrait
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 12 more

Isso é um bug ou estou faltando alguns requisitos?

@BogdanCojocar Parece que você está perdendo a biblioteca xgboost.

Você precisa de ambos os jars para o xgboost funcionar corretamente:

Você pode baixar os jars necessários a partir desses links de maven.

Obrigado @thesuperzapper. Funciona bem. Excelente trabalho com esta integração com o pyspark!

Alguma sugestão sobre como salvar o modelo treinado para impulsionar para carregar no módulo python?

@ ericwang915 Normalmente, para obter um modelo que interopera com outras bibliotecas XGBoost, você usaria o método .booster.saveModel("XXX/XXX") no objeto de modelo, onde XXX é um caminho local (não HDFS) no driver Spark. Se você usar outro método de salvamento, obterá um erro (consulte: # 2480)

No entanto, esqueci de adicionar um método para chamar a função save nessa versão do wrapper. Farei isso amanhã se tiver tempo. (Eu moro na Nova Zelândia ... então fusos horários)

Obrigada. A propósito, durante o processo de treinamento, não há nenhum registro mostrando as métricas de avaliação e a rodada de reforço, embora o silêncio seja definido como 1.

@thesuperzapper obrigado pela instrução. Consegui seguir suas instruções para treinar / salvar o modelo xgboost no pyspark. Alguma ideia sobre como acessar outra função do modelo xgboost como (scala) getFeatureScore ()?

@ccdtzccdtz atualmente estou reconectando o wrapper pyspark, pois o 0.8 teve grandes mudanças na API Spark, quando terminar, pretendo ter paridade de recursos com a API Spark Scala.

Eu não expus o método de reforço nativo em meu wrapper pyspark inicial, mas se você usar a API Spark Scala, poderá chamar xgboost_model_object.nativeBooster.getFeatureScore( e usá-lo normalmente.

Eu vi o XGBoost no pyspark falhar consistentemente se for executado 2 ou mais vezes. Estou executando no mesmo conjunto de dados com o mesmo código. Na primeira vez, ele é bem-sucedido, mas na segunda vez e, subsequentemente, ele falha. Estou usando o XGBoost 0.72 no Spark 2.3. Tenho que reiniciar o shell pyspark para executar o trabalho com êxito novamente.

Eu uso xgboost.trainWithDataFrame para fins de treinamento.

Alguém já viu essa questão?

Olá @thesuperzapper
O que você prescreveu funciona para mim em um único nó de trabalho.
No entanto, quando tento executar o pyspark xgboost em um usando mais de um trabalhador (3 neste caso), os executores ficam ociosos e desligam depois de um tempo.
Este é o código que estou tentando executar no conjunto de dados do Titanic (que é um pequeno conjunto de dados):

from pyspark.sql.session import SparkSession
from pyspark.sql.types import *
from pyspark.ml.feature import StringIndexer, VectorAssembler
from pyspark.ml import Pipeline
from pyspark.sql.functions import col

spark = SparkSession\
        .builder\
        .appName("PySpark XGBOOST Titanic")\
        .getOrCreate()

#spark.sparkContext.addPyFile("../sparkxgb.zip")

from automl.sparkxgb import XGBoostEstimator

schema = StructType(
  [StructField("PassengerId", DoubleType()),
    StructField("Survival", DoubleType()),
    StructField("Pclass", DoubleType()),
    StructField("Name", StringType()),
    StructField("Sex", StringType()),
    StructField("Age", DoubleType()),
    StructField("SibSp", DoubleType()),
    StructField("Parch", DoubleType()),
    StructField("Ticket", StringType()),
    StructField("Fare", DoubleType()),
    StructField("Cabin", StringType()),
    StructField("Embarked", StringType())
  ])

df_raw = spark\
  .read\
  .option("header", "true")\
  .schema(schema)\
  .csv("titanic.csv")


df = df_raw.na.fill(0)

sexIndexer = StringIndexer() \
    .setInputCol("Sex") \
    .setOutputCol("SexIndex") \
    .setHandleInvalid("keep")

cabinIndexer = StringIndexer() \
    .setInputCol("Cabin") \
    .setOutputCol("CabinIndex") \
    .setHandleInvalid("keep")

embarkedIndexer = StringIndexer() \
    .setInputCol("Embarked") \
    .setOutputCol("EmbarkedIndex") \
    .setHandleInvalid("keep")

vectorAssembler  = VectorAssembler()\
  .setInputCols(["Pclass", "SexIndex", "Age", "SibSp", "Parch", "Fare", "CabinIndex", "EmbarkedIndex"])\
  .setOutputCol("features")

xgboost = XGBoostEstimator(nworkers=2,
    featuresCol="features",
    labelCol="Survival",
    predictionCol="prediction"
)

pipeline = Pipeline().setStages([sexIndexer, cabinIndexer, embarkedIndexer, vectorAssembler, xgboost])
trainDF, testDF = df.randomSplit([0.8, 0.2], seed=24)

model  =pipeline.fit(trainDF)
print(trainDF.schema)

Este é o rastreamento de pilha:
Tracker started, with env={DMLC_NUM_SERVER=0, DMLC_TRACKER_URI=172.16.1.5, DMLC_TRACKER_PORT=9093, DMLC_NUM_WORKER=3}2018-09-04 08:52:55 ERROR TaskSchedulerImpl:70 - Lost executor 0 on 192.168.49.43: Remote RPC client disassociated. Likely due to containers exceeding thresholds, or network issues. Check driver logs for WARN messages.2018-09-04 08:52:55 ERROR AsyncEventQueue:91 - Interrupted while posting to TaskFailedListener. Removing that listener.java.lang.InterruptedException: ExecutorLost during XGBoost Training: ExecutorLostFailure (executor 0 exited caused by one of the running tasks) Reason: Remote RPC client disassociated. Likely due to containers exceeding thresholds, or network issues. Check driver logs for WARN messages. at org.apache.spark.TaskFailedListener.onTaskEnd(SparkParallelismTracker.scala:116) at org.apache.spark.scheduler.SparkListenerBus$class.doPostEvent(SparkListenerBus.scala:45) at org.apache.spark.scheduler.AsyncEventQueue.doPostEvent(AsyncEventQueue.scala:37) at org.apache.spark.scheduler.AsyncEventQueue.doPostEvent(AsyncEventQueue.scala:37) at org.apache.spark.util.ListenerBus$class.postToAll(ListenerBus.scala:91)

O executor está preso em:
org.apache.spark.RDD.foreachPartition(RDD.scala:927) ml.dmlc.xgboost4j.scala.spark.XGBoost$$anonfun$trainDistributed$4$$anon$1.run(XGBoost.scala:348)

Ambiente: Python 3.5.4, Spark versão 2.3.1, Xgboost 0.72

Você pode compartilhar suas configurações de xgboost e spark? Quantos
trabalhadores (trabalhadores xgboost), executores de faísca, núcleos etc.

-Nitin

Na terça-feira, 4 de setembro de 2018 às 5h03, sagnik-rzt [email protected] escreveu:

Olá @thesuperzapper https://github.com/thesuperzapper
O que você prescreveu funciona para mim em um único nó de trabalho.
No entanto, quando tento executar o pyspark xgboost em um usando mais de um
trabalhador, os executores ficam ociosos e desligam depois de um tempo.
Este é o rastreamento de pilha:
'' '
Rastreador iniciado, com env = {DMLC_NUM_SERVER = 0, DMLC_TRACKER_URI = 172.16.1.5,
DMLC_TRACKER_PORT = 9093, DMLC_NUM_WORKER = 3} 04/09/2018 08:52:55 ERROR
TaskSche dulerImpl: 70 - Executor perdido 0 em 192.168.49.43: RPC remoto
cliente desassociado. Provavelmente devido a contêineres que excedem os limites, ou
problemas de rede. Verifique os logs do driver para mensagens WARN. 2018-09-04 08:52:55
ERROR AsyncE ventQueue: 91 - Interrompido ao postar em TaskFailedListener.
Removendo esse listener.java.lang.InterruptedException: ExecutorLost durante
Treinamento XGBoost: ExecutorLostFailure (executor 0 saiu causado por um dos
as tarefas em execução) Motivo: Cliente RPC remoto desassociado. Provavelmente devido a
contêineres que excedem os limites ou problemas de rede. Verifique os registros do driver para
Mensagens de AVISO. no
org.apache.spark.TaskFailedListener.onTaskEnd (SparkParallelismTracker.scala: 116)
no
org.apache.spark.scheduler.SparkListenerBus $ class.doPostEvent (SparkListenerBus.scala: 45)
no
org.apache.spark.scheduler.AsyncEventQueue.doPostEvent (AsyncEventQueue.scala: 37)
no
org.apache.spark.scheduler.AsyncEventQueue.doPostEvent (AsyncEventQueue.scala: 37)
em org.apache.spark.util.ListenerBus $ class.postToAll (ListenerBus.scala: 91)
'' '

-
Você está recebendo isto porque comentou.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/dmlc/xgboost/issues/1698#issuecomment-418341557 ou mudo
o segmento
https://github.com/notifications/unsubscribe-auth/AJY-XklxsZB_FE7ZoAarV_fqw8D3JqxWks5uXmwqgaJpZM4KgAY_
.

@ sagnik-rzt Estou surpreso que funcione, já que aquele wrapper do pyspark suporta apenas XGboost 0.72, ainda estamos trabalhando no 0.8.

@thesuperzapper , com base na versão que você forneceu, refiz algumas partes para suportar o xgboost 0.80.
No entanto, estou terminando com um erro py4j.protocol.Py4JError: ml.dmlc.xgboost4j.scala.spark.XGBoostClassifier does not exist in the JVM . Eu forneci uma descrição completa aqui . Você poderia dar uma olhada?

Todos os códigos são colocados aqui .

Há muito mais mudanças necessárias do que você fez para fazê-lo funcionar com o 0.8.

O principal motivo de não ter lançado apenas uma versão 0.8 é porque realmente não quero fazer um objeto de pipeline específico do xgboost como fiz no 0.72. Estou trabalhando em uma maneira de fazer com que o objeto pyspark xgboost funcione com a persistência de pipeline padrão.

@thesuperzapper , ao usar os códigos para 0,72, usei o objeto XGBoostEstimator diretamente, sem usar o objeto XGBoostPipeline . E enquanto fazia isso, percebi que o treinamento / adaptação não é distribuído entre os trabalhadores do cluster. É necessário usar XGBoostPipeline para distribuição entre os trabalhadores?

Se não for esse o caso, você sabe por que o treinamento não é distribuído entre os trabalhadores?

Atualizar
Tentei o treinamento definindo XGBoostEstimator como um estágio em XGBoostPipeline mas o problema persiste. O treinamento não é distribuído entre os trabalhadores quando eu o executo no cluster, enquanto ocorre com outros modelos compatíveis com pyspark.

Você já observou esse comportamento? Como faço para resolver isso?

Eu recodifiquei principalmente o wrapper para XGBoost 0.8, mas como meu cluster de trabalho ainda está em 2.2, não posso testá-lo facilmente no modo distribuído, já que meu cluster Dockerized Spark 2.3 não consegue nem treinar modelos Scala XGBoost distribuídos sem obter problemas de localização aleatória ausente .

Acho que os problemas que @ sagnik-rzt e outros estão enfrentando estão relacionados à configuração do cluster ou a algum problema mais profundo com o Spark-Scala XGBoost.

Você consegue treinar um modelo no Spark-Scala XGBoost?

Obrigado @thesuperzapper , pensei que os locais de embaralhamento fossem tratados internamente, ou seja, seriam tratados independentemente da configuração do cluster. Mas eu encontrei este post stackoverflow, então implementarei essas sugestões.

Além disso, você poderia compartilhar sua versão 0.8, se estiver pronta? Posso testar a distribuição no meu cluster. Possui spark 2.3.1 e python 3.5.

depois de salvar o modelo e carregar, obtendo o seguinte erro

IllegalArgumentException: u'requirement falhou: Erro ao carregar metadados: Esperado nome de classe org.apache.spark.ml.Pipeline, mas encontrado nome de classe org.apache.spark.ml.PipelineModel '

você pode por favor ajudar com isso. obrigado

import pyspark
from pyspark.sql.session import SparkSession
from pyspark.sql.types import *
from pyspark.ml.feature import StringIndexer, VectorAssembler
from pyspark.ml import Pipeline
from pyspark.sql.functions import col

spark.sparkContext.addPyFile("sparkxgb.zip")
from sparkxgb import XGBoostEstimator
schema = StructType(
  [StructField("PassengerId", DoubleType()),
    StructField("Survival", DoubleType()),
    StructField("Pclass", DoubleType()),
    StructField("Name", StringType()),
    StructField("Sex", StringType()),
    StructField("Age", DoubleType()),
    StructField("SibSp", DoubleType()),
    StructField("Parch", DoubleType()),
    StructField("Ticket", StringType()),
    StructField("Fare", DoubleType()),
    StructField("Cabin", StringType()),
    StructField("Embarked", StringType())
  ])

df_raw = spark\
  .read\
  .option("header", "true")\
  .schema(schema)\
  .csv("train.csv")

 df = df_raw.na.fill(0)

 sexIndexer = StringIndexer()\
  .setInputCol("Sex")\
  .setOutputCol("SexIndex")\
  .setHandleInvalid("keep")

cabinIndexer = StringIndexer()\
  .setInputCol("Cabin")\
  .setOutputCol("CabinIndex")\
  .setHandleInvalid("keep")

embarkedIndexer = StringIndexer()\
  .setInputCol("Embarked")\
  .setOutputCol("EmbarkedIndex")\
  .setHandleInvalid("keep")

vectorAssembler = VectorAssembler()\
  .setInputCols(["Pclass", "SexIndex", "Age", "SibSp", "Parch", "Fare", "CabinIndex", "EmbarkedIndex"])\
  .setOutputCol("features")
xgboost = XGBoostEstimator(
    featuresCol="features", 
    labelCol="Survival", 
    predictionCol="prediction"
)

pipeline = Pipeline().setStages([sexIndexer, cabinIndexer, embarkedIndexer, vectorAssembler, xgboost])
model = pipeline.fit(df)
model.transform(df).select(col("PassengerId"), col("prediction")).show()

model.save("model_xgboost")
loadedModel = Pipeline.load("model_xgboost")


IllegalArgumentException: u'requirement failed: Error loading metadata: Expected class name org.apache.spark.ml.Pipeline but found class name org.apache.spark.ml.PipelineModel'


#predict2 = loadedModel.transform(df)

Tentei a seguinte opção

from pyspark.ml import PipelineModel
#model.save("model_xgboost")
loadedModel = PipelineModel.load("model_xgboost")

Obtendo o seguinte erro

Nenhum módulo denominado ml.dmlc.xgboost4j.scala.spark

DEV DOWNLOAD LINK: sparkxgb.zip

Esta versão funcionará com o XGBoost-0.8, mas por favor não use para nada além de testes, ou contribuir para este tópico, pois as coisas irão mudar.
(Observe também: removi todas as portas traseiras do Spark 2.2, portanto, ele só oferece suporte ao Spark 2.3)

O principal problema que eu conheço com essa versão, é que os modelos de classificação não carregam novamente após serem salvos, apresentando o erro: TypeError: 'JavaPackage' object is not callable . No entanto, estranhamente XGBoostPipelineModel funciona bem com um estágio de classificação XGBoost. Isso me leva a pensar que é um problema para mim, alguém pode verificar se os modelos de classificação de leitura funcionam para eles?

Apesar de tudo, estou tentando implementar DefaultParamsWritable de maneira adequada, o que eliminaria a necessidade do XGBoostPipeline dedicado, que será muito mais fácil de manter a longo prazo, então o problema de leitura / gravação deve se tornar irrelevante de qualquer maneira. (Isso também pode permitir que a persistência no CrossValidator funcione)

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

mhnamaki picture mhnamaki  ·  3Comentários

pplonski picture pplonski  ·  3Comentários

FabHan picture FabHan  ·  4Comentários

nicoJiang picture nicoJiang  ·  4Comentários

frankzhangrui picture frankzhangrui  ·  3Comentários