2017-06-08 5 views
1

Ich möchte einen benutzerdefinierten Spark Transformer in Java erstellen.Erstellen eines benutzerdefinierten Transformers In Java funke ml

Der Transformer ist Text Preprocessor, der wie ein Tokenizer funktioniert. Es benötigt eine Eingabespalte und eine Ausgabespalte als Parameter.

Ich schaute mich um und ich fand 2 Scala Traits HasInputCol und HasOutputCol.

Wie kann ich eine Klasse erstellen, die Transformer erweitert und HasInputCol und OutputCol implementiert?

Mein Ziel ist es, so etwas zu haben.

// Dataset that have a String column named "text" 
    DataSet<Row> dataset; 

    CustomTransformer customTransformer = new CustomTransformer(); 
    customTransformer.setInputCol("text"); 
    customTransformer.setOutputCol("result"); 

    // result that have 2 String columns named "text" and "result" 
    DataSet<Row> result = customTransformer.transform(dataset); 

Antwort

1

Sie wollen wahrscheinlich Ihre CustomTransformer von org.apache.spark.ml.UnaryTransformer erben. Sie können so etwas wie dies versuchen:

import org.apache.spark.ml.UnaryTransformer; 
import org.apache.spark.ml.util.Identifiable$; 
import org.apache.spark.sql.types.DataType; 
import org.apache.spark.sql.types.DataTypes; 
import scala.Function1; 
import scala.collection.JavaConversions$; 
import scala.collection.immutable.Seq; 

import java.util.Arrays; 

public class MyCustomTransformer extends UnaryTransformer<String, scala.collection.immutable.Seq<String>, MyCustomTransformer> 
{ 
    private final String uid = Identifiable$.MODULE$.randomUID("mycustom"); 

    @Override 
    public String uid() 
    { 
     return uid; 
    } 


    @Override 
    public Function1<String, scala.collection.immutable.Seq<String>> createTransformFunc() 
    { 
     // can't use labmda syntax :(
     return new scala.runtime.AbstractFunction1<String, Seq<String>>() 
     { 
      @Override 
      public Seq<String> apply(String s) 
      { 
       // do the logic 
       String[] split = s.toLowerCase().split("\\s"); 
       // convert to Scala type 
       return JavaConversions$.MODULE$.iterableAsScalaIterable(Arrays.asList(split)).toList(); 
      } 
     }; 
    } 


    @Override 
    public void validateInputType(DataType inputType) 
    { 
     super.validateInputType(inputType); 
     if (inputType != DataTypes.StringType) 
      throw new IllegalArgumentException("Input type must be string type but got " + inputType + "."); 
    } 

    @Override 
    public DataType outputDataType() 
    { 
     return DataTypes.createArrayType(DataTypes.StringType, true); // or false? depends on your data 
    } 
} 
+0

Dies funktioniert nicht. Ich vermute es liegt an einem Bug. Ich bekomme '' 'java.lang.IllegalArgumentException: Anforderung fehlgeschlagen: Param d7ac3108-799c-4aed-a093-c85d12833a4e__inputCol gehört nicht zu fe3d99ba-e4eb-4e95-9412-f84188d936e3.''' – LonsomeHell

+0

@LonsomeHell, nur um zu überprüfen , sind Sie sicher, dass Sie es mit einer gültigen Eingabespalte konfiguriert haben? – SergGr

+0

Ja, ich habe setInput mit einem gültigen Spaltennamen verwendet. – LonsomeHell

0

Als SergGr vorgeschlagen, können Sie UnaryTransformer verlängern. Es ist jedoch ziemlich schwierig.

HINWEIS: Alle folgenden Kommentare gelten für Spark Version 2.2.0.

@Override 
public String uid() { 
    return getUid(); 
} 

private String getUid() { 

    if (uid == null) { 
     uid = Identifiable$.MODULE$.randomUID("mycustom"); 
    } 
    return uid; 
} 

Anscheinend Initialisierung uid im Konstruktor sie:

das Problem in SPARK-12606 beschrieben zu begegnen, wo sie "...Param null__inputCol does not belong to..." bekommen, sollten Sie String uid() wie diese umzusetzen. Aber die Sache ist, dass UnaryTransformer's inputCol (und outputCol) initialisiert wird, bevor uid in der erbenden Klasse initialisiert wird. Siehe HasInputCol:

final val inputCol: Param[String] = new Param[String](this, "inputCol", "input column name") 

Dies ist, wie Param aufgebaut ist:

def this(parent: Identifiable, name: String, doc: String) = this(parent.uid, name, doc) 

Wenn also parent.uid ausgewertet wird, wird die benutzerdefinierte uid() Implementierung genannt und an diesem Punkt uid ist immer noch null. Durch die Implementierung uid() mit Lazy-Evaluierung stellen Sie sicher, uid() nie Null zurückgibt. obwohl

In Ihrem Fall:

Param d7ac3108-799c-4aed-a093-c85d12833a4e__inputCol does not belong to fe3d99ba-e4eb-4e95-9412-f84188d936e3 

scheint es ein wenig anders zu sein. Da "d7ac3108-799c-4aed-a093-c85d12833a4e" != "fe3d99ba-e4eb-4e95-9412-f84188d936e3" aussieht, zeigt Ihre Implementierung der uid()-Methode bei jedem Aufruf einen neuen Wert an. Vielleicht in Ihrem Fall wurde es so umgesetzt:

@Override 
public String uid() { 
    return Identifiable$.MODULE$.randomUID("mycustom"); 
} 

By the way, wenn UnaryTransformer erstrecken, stellen Sie sicher, dass die Transformationsfunktion Serializable ist.

Verwandte Themen