Xgboost: [jvm-packages] java.lang.NullPointerException : null à ml.dmlc.xgboost4j.java.Booster.predict

Créé le 30 juil. 2020  ·  37Commentaires  ·  Source: dmlc/xgboost

Les exceptions NPE se produisent lorsqu'elles sont prédites via l'API JAVA.

java.lang.NullPointerException : null
à ml.dmlc.xgboost4j.java.Booster.predict(Booster.java:309)
à ml.dmlc.xgboost4j.java.Booster.predict(Booster.java:375)
à com.tuhu.predict.predict.BaseModelPredict.predict(BaseModelPredict.java:71)
à com.tuhu.predict.predict.XgboostFindPageModelPredict.predict(XgboostFindPageModelPredict.java:53)
à com.tuhu.predict.service.impl.MlpFindPageFeatureServiceImpl.featureProcess(MlpFindPageFeatureServiceImpl.java:65)
à com.tuhu.predict.api.controller.MlpFindPageController.recommendPredict(MlpFindPageController.java:49)
sur com.tuhu.predict.api.controller.MlpFindPageController$$FastClassBySpringCGLIB$$f694b9ff.invoke()
à org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
à org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:746)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
à org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
à org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
à org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
à org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
à org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
sur com.tuhu.springcloud.common.annotation.AbstractControllerLogAspect.doAround(AbstractControllerLogAspect.java:104)
à sun.reflect.NativeMethodAccessorImpl.invoke0 (méthode native)
à sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
à sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
à java.lang.reflect.Method.invoke(Method.java:498)
sur org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
sur org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
à org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:174)
à org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
à org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
à org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
sur com.tuhu.predict.api.controller.MlpFindPageController$$EnhancerBySpringCGLIB$$560ed775.recommendPredict()
à sun.reflect.NativeMethodAccessorImpl.invoke0 (méthode native)
à sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
à sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
à java.lang.reflect.Method.invoke(Method.java:498)
à org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:209)
à org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
sur org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
sur org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:877)
sur org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:783)
sur org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
à org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
à org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
à org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
à org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877)
à javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
à org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
à javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur com.tuhu.soter.starter.filter.SoterDefaultFilter.doFilter(SoterDefaultFilter.java:79)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur com.tuhu.boot.logback.filter.LogFilter.doFilter(LogFilter.java:54)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:158)
sur org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:126)
sur org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:111)
sur org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
sur org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur com.tuhu.boot.common.filter.HeartbeatFilter.doFilter(HeartbeatFilter.java:42)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur com.tuhu.boot.common.filter.MDCFilter.doFilter(MDCFilter.java:47)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
sur org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
sur org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
sur org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
sur org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
sur org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
sur org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
sur org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
à org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
à org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
à org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
à org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
sur org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
sur org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:677)
sur org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
sur org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
sur org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
sur org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
sur org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468)
sur org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
à java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
à java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
sur org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
sur java.lang.Thread.run(Thread.java:748)

Commentaire le plus utile

D'accord, je pense que je vais l'avoir prêt d'ici demain.

Tous les 37 commentaires

Parce que le modèle est formé via Python Sklearn, des incompatibilités se produisent plus tard. Pour gagner du temps, l'équipe d'algorithmes a déplacé le modèle XGB formé par Sklearn d'une couche sur le package Python XgBoost.

image

Quelle version de XGBoost utilisez-vous ? Auparavant, nous avons corrigé un bogue selon lequel le package jvm ne lève pas correctement l'exception lorsque la prédiction échoue et continue avec un tampon de prédiction vide.

Quelle version de XGBoost utilisez-vous ? Auparavant, nous avons corrigé un bogue selon lequel le package jvm ne lève pas correctement l'exception lorsque la prédiction échoue et continue avec un tampon de prédiction vide.

La version 1.0 de la plate-forme d'algorithmes de l'entreprise est utilisée et la version 0.9.0 du projet d'algorithme est utilisée en raison de problèmes de compatibilité de version. Les collègues de l'algorithme ont utilisé Python pour convertir le fichier de modèle 1.0 en 0.9.0. Je me demande si c'est causé par cette transformation

Je suggérerais d'attendre la version 1.2 (https://github.com/dmlc/xgboost/issues/5734) et de réessayer, nous avons quelques corrections de bogues importantes dans cette version. Je suggérerais également d'utiliser la même version ou une version ultérieure de xgboost pour la prédiction. Le modèle binaire de XGBoost est rétrocompatible, à l'avenir, le modèle basé sur JSON est recommandé.

J'ai rencontré le même problème avec 1.2.0. Donc le problème est toujours là.

J'ai aussi le même problème.
J'ai utilisé xgboost4j pour créer le modèle.

Y at-il un travail autour?

C'est un gros problème pour moi, ça fait échouer des emplois en production.

@ranInc Utilisez -vous la dernière version de XGBoost ? Jusqu'à présent, nous ne connaissons pas la cause exacte de ce problème. Nous allons y remédier dans la mesure du possible, et comme il n'y a aucune garantie quant au moment où le problème pourra être résolu, je vous suggère d'étudier une alternative en attendant.

@ranInc Vous pouvez nous aider en fournissant un petit exemple de programme que nous (développeurs) pouvons exécuter sur notre propre machine.

J'utilise 1.2.0, le dernier jar sur le référentiel maven.
L'alternative pour moi est de revenir à saprk 2.4.5 et d'utiliser xgboost 0.9 - et c'est ce que je fais maintenant.

Pour l'exemple : je vais essayer d'identifier le modèle/les données spécifiques qui provoquent l'échec du travail plus tard.

Salut,
J'ai trouvé le modèle/les données spécifiques.
Je le joins dans ce commentaire.
xgb1.2_bug.zip

voici comment vous recréez le bogue (gardez à l'esprit que si vous ne faites pas la répartition ici, cela fonctionne - cela a donc quelque chose à voir avec la quantité de données ou le type de données dans chaque partition):

from pyspark.ml.pipeline import PipelineModel
from pyspark.sql import SparkSession
from pyspark.sql.dataframe import DataFrame

df = spark.read.parquet("/tmp/6620294785024229130_features").repartition(200).persist()
df.count()

model = PipelineModel.read().load("/tmp/6620294785024229130_model_xg_only")

predictions = model.transform(df)

predictions.persist()
predictions.count()
predictions.show()

Avez-vous une idée de quand cela peut être résolu?
Cela m'empêche d'utiliser Spark 3.0...

@ranInc Pas encore. Nous vous informerons lorsque nous aurons corrigé le bogue. Aussi, pouvez-vous poster le code dans Scala ? Je ne pense pas que nous ayons jamais officiellement soutenu l'utilisation de PySpark avec XGBoost.

      import org.apache.spark.ml.{Pipeline, PipelineModel}
      val df = spark.read.parquet("/tmp/6620294785024229130_features").repartition(200).persist()
      df.count()

      val model = PipelineModel.read.load("/tmp/6620294785024229130_model_xg_only")

      val predictions = model.transform(df)

      predictions.persist()
      predictions.count()
      predictions.show()

Un autre pointeur,
il semble que le problème soit dû au fait que toutes les caractéristiques envoyées pour être prédites sont des zéros/manquantes.

Je suppose que personne ne travaille dessus ?
Cela signifie essentiellement que xgboost ne fonctionne pas du tout sur Spark 3.

Ouais désolé nos mains sont assez pleines en ce moment. Nous aborderons ce problème à un moment donné. Je demande respectueusement votre patience. Merci.

@ranInc J'ai eu un peu de temps aujourd'hui, j'ai donc essayé d'exécuter le script que vous avez fourni ici. J'ai reproduit l'erreur java.lang.NullPointerException .

Étrangement, la dernière version de développement (branche master ) ne plante pas de la même manière. Au lieu de cela, il produit une erreur

Exception dans le thread "principal" org.apache.spark.SparkException : Tâche abandonnée en raison d'un échec d'étape : la tâche 0 à l'étape 7.0 a échoué 1 fois, échec le plus récent : tâche perdue 0.0 à l'étape 7.0 (TID 11, d04389c5babb, pilote de l'exécuteur) : ml.dmlc.xgboost4j.java.XGBoostError : [00:36:10] /workspace/src/learner.cc:1179 : Échec de la vérification : learner_model_param_.num_feature >= p_fmat->Info().num_col_ (1 contre 2) : Le nombre de colonnes ne correspond pas au nombre de fonctionnalités dans le booster.

Je vais enquêter plus avant.

Je pense que le message d'erreur a du sens maintenant, votre entrée a plus de fonctionnalités que le modèle de prédiction.

Avant que le package jvm ne continue après l'échec de xgboost, ce qui entraîne un tampon de prédiction vide. J'ai ajouté un garde-chèque récemment.

Assurez-vous simplement que le nombre de colonnes de votre ensemble de données d'entraînement est supérieur ou égal à votre ensemble de données de prédiction.

Salut,
Le modèle a été créé en utilisant le même nombre de fonctionnalités.
Dans Spark, il utilise une colonne vectorielle et non plusieurs colonnes.
Dans tous les cas, la taille du vecteur est toujours la même, pour l'ajustement et la prédiction - sûr à 100 %.

Cela a quelque chose à voir avec les lignes avec toutes les fonctionnalités nulles/manquantes.
Vous pouvez voir que si vous filtrez à partir de la trame de données les lignes avec toutes les fonctionnalités nulles - cela fonctionne très bien.

@ranInc Pouvez-vous publier le programme Scala complet qui a généré le modèle ? Le message d'erreur semble suggérer que votre modèle a été formé avec une seule fonctionnalité.

Je ne pense pas que cela aidera beaucoup car le code est très générique et contient des transformateurs propitiatoires,
Le code lui-même est principalement pyspark et non scala.

La meilleure façon de voir que le nombre de fonctionnalités n'est pas un problème est simplement de filtrer les lignes avec toutes les fonctionnalités nulles et d'utiliser le modèle - cela fonctionne sans problème.
Vous pouvez également conserver toutes les lignes et repartitionner le dataframe pour utiliser une partition, et cela fonctionne également.

@ranInc J'ai filtré les lignes avec zéro et toujours face à la même erreur ( java.lang.NullPointerException ):

...
df.na.drop(minNonNulls = 1)
...

N'est-ce pas la bonne façon de procéder?

Je ne pense pas que cela aidera beaucoup car le code est très générique et contient des transformateurs propitiatoires

Je veux voir combien de fonctionnalités sont utilisées lors de la formation et au moment de la prédiction. Le message d'erreur

ml.dmlc.xgboost4j.java.XGBoostError : [00:36:10] /workspace/src/learner.cc:1179 : Échec de la vérification : learner_model_param_.num_feature >= p_fmat->Info().num_col_ (1 contre 2) : Le nombre de colonnes ne correspond pas au nombre de fonctionnalités dans le booster.

suggère que le modèle a été formé avec une seule caractéristique et que la prédiction est faite avec deux caractéristiques.

Pour le moment, je n'ai accès qu'au bloc de données et au modèle sérialisé que vous avez téléchargés. Je manque d'informations sur ce qui s'est passé dans la formation du modèle et sur ce qui n'a pas fonctionné, ce qui m'empêche de résoudre davantage le problème. Si votre programme contient des informations propriétaires, est-il possible de produire un exemple propre ?

  1. non, tu peux faire ceci :
import org.apache.spark.ml.linalg.{SparseVector, Vector}
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions.{callUDF, col}
.......
 val isVectorAllZeros = (col: Vector) => {
        col match {
          case sparse: SparseVector =>
            if(sparse.indices.isEmpty){
              true
            }else{
              false
            }
          case _ => false
        }
      }

      spark.udf.register("isVectorAllZeros", isVectorAllZeros)
      df = df.withColumn("isEmpty",callUDF("isVectorAllZeros",
        col("features_6620294785024229130"))).where("isEmpty == false")

vous pouvez également simplement repartitionner le dataframe comme ceci :

....
df = df.repartition(1)
....
  1. Je comprends, mais le code ne vous donnera pas grand-chose, car il utilise VectorAssembler, et vous ne pourrez pas savoir combien de fonctionnalités ont été réellement utilisées,
    Mais je suis sûr à 100% qu'il utilisait le même nombre de fonctionnalités.

Mais je suis sûr à 100% qu'il utilisait le même nombre de fonctionnalités.

Comment avez-vous assuré cela, si VectorAssembler a pour conséquence d'avoir un nombre variable de fonctionnalités ?

VectorAssembler crée toujours le même nombre de fonctionnalités, il a juste besoin de noms de colonnes à récupérer.
Le code lui-même est utilisé pour créer des milliers de modèles, il est donc très générique et obtient essentiellement une liste de noms à utiliser.

Je pourrai peut-être relancer la création du modèle et vous envoyer la trame de données utilisée pour le modèle - ou toute autre donnée dont vous avez besoin.
Cela me prendra cependant du temps et si vous utilisez ce que j'ai montré auparavant, vous verrez que le modèle fonctionne très bien avec 2 fonctionnalités.

@ranInc Permettez-moi de poser une autre question : est-il correct de dire que l'exemple de données a une colonne clairsemée (VectorAssembler) qui a au plus deux fonctionnalités ?

Non.
VectorAssembler est un Trasformer qui saisit plusieurs colonnes et les place dans une colonne Vector.
Les vecteurs sont toujours utilisés pour l'ajustement et la prédiction des modèles en étincelle.

L'exemple de dataframe ici a une colonne de vecteur.
Certaines rangées sont clairsemées, d'autres denses - toutes ont deux caractéristiques.

@ranInc Ainsi, toutes les lignes ont deux fonctionnalités, certaines valeurs sont manquantes et d'autres non. J'ai compris. Je vais essayer votre suggestion sur le filtrage des lignes vides.

Comme vous l'avez peut-être deviné, je suis assez nouveau dans l'écosystème Spark, donc l'effort de débogage peut s'avérer assez difficile. Nous avons actuellement besoin de plus de développeurs qui en savent plus sur la programmation Spark et Scala en général. Si vous connaissez personnellement quelqu'un qui aimerait nous aider à améliorer le package JVM de XGBoost, veuillez nous le faire savoir.

@ranInc J'ai essayé de filtrer les lignes vides selon votre suggestion :

Programme A : exemple de script, sans filtrage des lignes vides

import org.apache.spark.sql.SparkSession
import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.ml.linalg.{SparseVector, Vector}
import org.apache.spark.sql.functions.{callUDF, col}

object Main extends App {
  val spark = SparkSession
      .builder()
      .appName("XGBoost4J-Spark Pipeline Example")
      .getOrCreate()

  val df = spark.read.parquet("/home/ubuntu/data/6620294785024229130_features").repartition(200).persist()
  df.show()

  val model = PipelineModel.read.load("/home/ubuntu/data/6620294785024229130_model_xg_only")

  val predictions = model.transform(df)

  predictions.persist()
  predictions.count()
  predictions.show()
}

Programme B : Exemple avec filtrage des lignes vides

import org.apache.spark.sql.SparkSession
import org.apache.spark.ml.{Pipeline, PipelineModel}
import org.apache.spark.ml.linalg.{SparseVector, Vector}
import org.apache.spark.sql.functions.{callUDF, col}

object Main extends App {
  val spark = SparkSession
      .builder()
      .appName("XGBoost4J-Spark Pipeline Example")
      .getOrCreate()

  val isVectorAllZeros = (col: Vector) => {
    col match {
      case sparse: SparseVector => (sparse.indices.isEmpty)
      case _ => false
    }
  }
  spark.udf.register("isVectorAllZeros", isVectorAllZeros)

  val df = spark.read.parquet("/home/ubuntu/data/6620294785024229130_features").repartition(200).persist()
                .withColumn("isEmpty", callUDF("isVectorAllZeros", col("features_6620294785024229130")))
                .where("isEmpty == false")
  df.show()

  val model = PipelineModel.read.load("/home/ubuntu/data/6620294785024229130_model_xg_only")

  val predictions = model.transform(df)

  predictions.persist()
  predictions.count()
  predictions.show()
}

Quelques remarques

  • Avec la version stable 1.2.0, le programme A génère une erreur avec java.lang.NullPointerException . Juste avant le NPE, l'avertissement suivant s'affiche dans le journal d'exécution de Spark :
WARNING: /xgboost/src/learner.cc:979: Number of columns does not match number of features in booster. Columns: 0 Features: 1
  • Avec la version stable 1.2.0, le programme B se termine avec succès sans erreur.
  • Avec la version de développement (dernière branche master , commit 42d31d9dcb6f7c1cb7d0545e9ab3a305ecad0816), le programme A et le programme B échouent avec l'erreur suivante :
[12:44:57] /home/ubuntu/xgblatest/src/learner.cc:1179: Check failed: learner_model_param_.num_feature >= p_fmat->Info().num_col_ (1 vs. 2) : Number of columns does not match number of features in booster.                                                                                                        
Stack trace:                                                                                                                                   
  [bt] (0) /tmp/libxgboost4j14081654332866852928.so(dmlc::LogMessageFatal::~LogMessageFatal()+0x79) [0x7f7ef62c4e19]                             [bt] (1) /tmp/libxgboost4j14081654332866852928.so(xgboost::LearnerImpl::ValidateDMatrix(xgboost::DMatrix*, bool) const+0x20b) [0x7f7ef63f5f0b]                                                                                                                                              
  [bt] (2) /tmp/libxgboost4j14081654332866852928.so(xgboost::LearnerImpl::Predict(std::shared_ptr<xgboost::DMatrix>, bool, xgboost::HostDeviceVector<float>*, unsigned int, bool, bool, bool, bool, bool)+0x3c3) [0x7f7ef6400233]                                                             
  [bt] (3) /tmp/libxgboost4j14081654332866852928.so(XGBoosterPredict+0xec) [0x7f7ef62caa3c]                                                      [bt] (4) /tmp/libxgboost4j14081654332866852928.so(Java_ml_dmlc_xgboost4j_java_XGBoostJNI_XGBoosterPredict+0x47) [0x7f7ef62befd7]             
  [bt] (5) [0x7f80908a8270]

ce qui est étrange car, selon @ranInc , le modèle a été formé avec des données à deux caractéristiques.

  • J'ai construit la version 1.2.0-SNAPSHOT partir de la source (commit 71197d1dfa27c80add9954b10284848c1f165c40). Cette fois, le programme A et le programme B échouent avec l'erreur d'incompatibilité de fonctionnalité ( learner_model_param_.num_feature >= p_fmat->Info().num_col_ (1 vs. 2) : Number of columns does not match number of features in booster ).
  • La différence de comportement entre la version stable 1.2.0 et 1.2.0-SNAPSHOT était inattendue et me rendait assez nerveux. En particulier, le message d'avertissement de 1.2.0
WARNING: /xgboost/src/learner.cc:979: Number of columns does not match number of features in booster. Columns: 0 Features: 1

n'est pas trouvé dans la version 1.2.0 de la base de code C++. Au lieu de cela, l'avertissement se trouve dans la branche release_1.0.0 :
https://github.com/dmlc/xgboost/blob/ea6b117a5737f5beb2533fc89b3f3fcd72ecc04e/src/learner.cc#L972 -L982
Cela signifie-t-il donc que le fichier JAR 1.2.0 sur Maven Central a libxgboost4j.so de 1.0.0 ?? 🤯 😱

  • En effet, le fichier JAR 1.2.0 de Maven Central contient libxgboost4j.so c'est en fait la 1.0.0 (!!!). Pour le savoir, téléchargez xgboost4j_2.12-1.2.0.jar depuis Maven Central et extrayez le fichier libxgboost4j.so . Exécutez ensuite le script Python suivant pour vérifier la version du fichier de bibliothèque :
import ctypes

lib = ctypes.cdll.LoadLibrary('./libxgboost4j.so')

major = ctypes.c_int()
minor = ctypes.c_int()
patch = ctypes.c_int()

lib.XGBoostVersion(ctypes.byref(major), ctypes.byref(minor), ctypes.byref(patch))
print((major.value, minor.value, patch.value))  # prints (1, 0, 2), indicating version 1.0.2
  • Mis à part le problème de la version 1.0.0, nous voyons clairement que le modèle XGBoost formé ne reconnaissait qu'une seule fonctionnalité ( learner_model_param_.num_feature == 1 ). Peut-être que les données d'entraînement avaient une fonctionnalité vide à 100 % ? @ranInc

Voulez-vous que je récupère le dataframe utilisé pour créer le modèle ?
Si je peux le saisir, je pense que je peux créer un code scala simple qui crée le modèle.

@ranInc Je soupçonne que l'une des deux caractéristiques des données de formation consistait entièrement en valeurs manquantes, définissant learner_model_param_.num_feature sur 1. Alors oui, voir les données de formation sera très utile.

D'accord, je pense que je vais l'avoir prêt d'ici demain.

Créé #6426 pour garder une trace du problème de libxgboost4j.so non concordant. Ici (#5957), poursuivons la discussion sur la raison pour laquelle learner_model_param_.num_feature est défini sur 1.

Il semble que vous vous trompiez, les données d'entraînement n'ont pas de valeurs manquantes.
sur l'exemple de code ici, au lieu de m'appuyer sur la répartition pour reproduire l'échec, j'ai utilisé à la place une seule ligne (qui n'a que des caractéristiques nulles) pour la prédiction.

feature_creation.zip

import ml.dmlc.xgboost4j.scala.spark.XGBoostRegressor
import org.apache.spark.ml.Pipeline
import org.apache.spark.sql.SparkSession
import org.apache.spark.ml.PipelineModel
import org.apache.spark.sql.DataFrame

val df = spark.read.parquet("/tmp/6620294785024229130_only_features_creation").persist()
df.count()

val regressor = new XGBoostRegressor()
    .setFeaturesCol("features_6620294785024229130")
    .setLabelCol("label_6620294785024229130")
    .setPredictionCol("prediction")
    .setMissing(0.0F)
    .setMaxDepth(3)
    .setNumRound(100)
    .setNumWorkers(1)

val pipeline = new Pipeline().setStages(Array(regressor))
val model = pipeline.fit(df)

val pred = spark.read.parquet("/tmp/6620294785024229130_features").persist()
pred.count()
pred.where("account_code == 4011593987").show()
model.transform(pred.where("account_code == 4011593987")).show()
Cette page vous a été utile?
0 / 5 - 0 notes