diff --git a/mllib/src/main/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifier.scala b/mllib/src/main/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifier.scala index 8c5d768044ac3..afb7c667e81e4 100644 --- a/mllib/src/main/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifier.scala +++ b/mllib/src/main/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifier.scala @@ -30,6 +30,7 @@ import org.apache.spark.ml.param.shared._ import org.apache.spark.ml.util._ import org.apache.spark.ml.util.Instrumentation.instrumented import org.apache.spark.sql.{Dataset, Row} +import org.apache.spark.util.VersionUtils.majorMinorVersion /** Params for Multilayer Perceptron. */ private[classification] trait MultilayerPerceptronParams extends ProbabilisticClassifierParams @@ -247,7 +248,7 @@ class MultilayerPerceptronClassifier @Since("1.5.0") ( } trainer.setStackSize($(blockSize)) val mlpModel = trainer.train(data) - new MultilayerPerceptronClassificationModel(uid, myLayers, mlpModel.weights) + new MultilayerPerceptronClassificationModel(uid, mlpModel.weights) } } @@ -273,31 +274,22 @@ object MultilayerPerceptronClassifier * Each layer has sigmoid activation function, output layer has softmax. * * @param uid uid - * @param layers array of layer sizes including input and output layers * @param weights the weights of layers */ @Since("1.5.0") class MultilayerPerceptronClassificationModel private[ml] ( @Since("1.5.0") override val uid: String, - @Since("1.5.0") val layers: Array[Int], @Since("2.0.0") val weights: Vector) extends ProbabilisticClassificationModel[Vector, MultilayerPerceptronClassificationModel] - with Serializable with MLWritable { + with MultilayerPerceptronParams with Serializable with MLWritable { @Since("1.6.0") - override val numFeatures: Int = layers.head + override lazy val numFeatures: Int = $(layers).head - private[ml] val mlpModel = FeedForwardTopology - .multiLayerPerceptron(layers, softmaxOnTop = true) + @transient private[ml] lazy val mlpModel = FeedForwardTopology + .multiLayerPerceptron($(layers), softmaxOnTop = true) .model(weights) - /** - * Returns layers in a Java List. - */ - private[ml] def javaLayers: java.util.List[Int] = { - layers.toList.asJava - } - /** * Predict label for the given features. * This internal method is used to implement `transform()` and output [[predictionCol]]. @@ -308,7 +300,8 @@ class MultilayerPerceptronClassificationModel private[ml] ( @Since("1.5.0") override def copy(extra: ParamMap): MultilayerPerceptronClassificationModel = { - val copied = new MultilayerPerceptronClassificationModel(uid, layers, weights).setParent(parent) + val copied = new MultilayerPerceptronClassificationModel(uid, weights) + .setParent(parent) copyValues(copied, extra) } @@ -322,11 +315,11 @@ class MultilayerPerceptronClassificationModel private[ml] ( override protected def predictRaw(features: Vector): Vector = mlpModel.predictRaw(features) - override def numClasses: Int = layers.last + override def numClasses: Int = $(layers).last @Since("3.0.0") override def toString: String = { - s"MultilayerPerceptronClassificationModel: uid=$uid, numLayers=${layers.length}, " + + s"MultilayerPerceptronClassificationModel: uid=$uid, numLayers=${$(layers).length}, " + s"numClasses=$numClasses, numFeatures=$numFeatures" } } @@ -347,13 +340,13 @@ object MultilayerPerceptronClassificationModel class MultilayerPerceptronClassificationModelWriter( instance: MultilayerPerceptronClassificationModel) extends MLWriter { - private case class Data(layers: Array[Int], weights: Vector) + private case class Data(weights: Vector) override protected def saveImpl(path: String): Unit = { // Save metadata and Params DefaultParamsWriter.saveMetadata(instance, path, sc) - // Save model data: layers, weights - val data = Data(instance.layers, instance.weights) + // Save model data: weights + val data = Data(instance.weights) val dataPath = new Path(path, "data").toString sparkSession.createDataFrame(Seq(data)).repartition(1).write.parquet(dataPath) } @@ -367,13 +360,21 @@ object MultilayerPerceptronClassificationModel override def load(path: String): MultilayerPerceptronClassificationModel = { val metadata = DefaultParamsReader.loadMetadata(path, sc, className) + val (majorVersion, _) = majorMinorVersion(metadata.sparkVersion) val dataPath = new Path(path, "data").toString - val data = sparkSession.read.parquet(dataPath).select("layers", "weights").head() - val layers = data.getAs[Seq[Int]](0).toArray - val weights = data.getAs[Vector](1) - val model = new MultilayerPerceptronClassificationModel(metadata.uid, layers, weights) - + val df = sparkSession.read.parquet(dataPath) + val model = if (majorVersion < 3) { // model prior to 3.0.0 + val data = df.select("layers", "weights").head() + val layers = data.getAs[Seq[Int]](0).toArray + val weights = data.getAs[Vector](1) + val model = new MultilayerPerceptronClassificationModel(metadata.uid, weights) + model.set("layers", layers) + } else { + val data = df.select("weights").head() + val weights = data.getAs[Vector](0) + new MultilayerPerceptronClassificationModel(metadata.uid, weights) + } metadata.getAndSetParams(model) model } diff --git a/mllib/src/main/scala/org/apache/spark/ml/r/MultilayerPerceptronClassifierWrapper.scala b/mllib/src/main/scala/org/apache/spark/ml/r/MultilayerPerceptronClassifierWrapper.scala index 62f642142701b..96c588acc1406 100644 --- a/mllib/src/main/scala/org/apache/spark/ml/r/MultilayerPerceptronClassifierWrapper.scala +++ b/mllib/src/main/scala/org/apache/spark/ml/r/MultilayerPerceptronClassifierWrapper.scala @@ -40,7 +40,7 @@ private[r] class MultilayerPerceptronClassifierWrapper private ( pipeline.stages(1).asInstanceOf[MultilayerPerceptronClassificationModel] lazy val weights: Array[Double] = mlpModel.weights.toArray - lazy val layers: Array[Int] = mlpModel.layers + lazy val layers: Array[Int] = mlpModel.getLayers def transform(dataset: Dataset[_]): DataFrame = { pipeline.transform(dataset) diff --git a/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/._SUCCESS.crc b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/._SUCCESS.crc new file mode 100644 index 0000000000000..3b7b044936a89 Binary files /dev/null and b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/._SUCCESS.crc differ diff --git a/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/.part-00000.crc b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/.part-00000.crc new file mode 100644 index 0000000000000..330943d39c838 Binary files /dev/null and b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/.part-00000.crc differ diff --git a/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/_SUCCESS b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/_SUCCESS similarity index 100% rename from mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/_SUCCESS rename to mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/_SUCCESS diff --git a/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/part-00000 b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/part-00000 new file mode 100644 index 0000000000000..1c36ce6a65f92 --- /dev/null +++ b/mllib/src/test/resources/ml-models/hashingTF-2.4.4/metadata/part-00000 @@ -0,0 +1 @@ +{"class":"org.apache.spark.ml.feature.HashingTF","timestamp":1577833408759,"sparkVersion":"2.4.4","uid":"hashingTF_f4565fe7f7da","paramMap":{"numFeatures":100,"outputCol":"features","inputCol":"words","binary":true},"defaultParamMap":{"numFeatures":262144,"outputCol":"hashingTF_f4565fe7f7da__output","binary":false}} diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/data/._SUCCESS.crc b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/._SUCCESS.crc new file mode 100644 index 0000000000000..3b7b044936a89 Binary files /dev/null and b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/._SUCCESS.crc differ diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/data/.part-00000-fa18aaf6-d8df-4b90-8231-eb5f6ac12138-c000.snappy.parquet.crc b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/.part-00000-fa18aaf6-d8df-4b90-8231-eb5f6ac12138-c000.snappy.parquet.crc new file mode 100644 index 0000000000000..7d14cc322cf38 Binary files /dev/null and b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/.part-00000-fa18aaf6-d8df-4b90-8231-eb5f6ac12138-c000.snappy.parquet.crc differ diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/data/_SUCCESS b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/_SUCCESS new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/data/part-00000-fa18aaf6-d8df-4b90-8231-eb5f6ac12138-c000.snappy.parquet b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/part-00000-fa18aaf6-d8df-4b90-8231-eb5f6ac12138-c000.snappy.parquet new file mode 100644 index 0000000000000..bf6896afa0c96 Binary files /dev/null and b/mllib/src/test/resources/ml-models/mlp-2.4.4/data/part-00000-fa18aaf6-d8df-4b90-8231-eb5f6ac12138-c000.snappy.parquet differ diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/._SUCCESS.crc b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/._SUCCESS.crc new file mode 100644 index 0000000000000..3b7b044936a89 Binary files /dev/null and b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/._SUCCESS.crc differ diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/.part-00000.crc b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/.part-00000.crc new file mode 100644 index 0000000000000..322e18cad023a Binary files /dev/null and b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/.part-00000.crc differ diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/_SUCCESS b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/_SUCCESS new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/part-00000 b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/part-00000 new file mode 100644 index 0000000000000..59798f8e55526 --- /dev/null +++ b/mllib/src/test/resources/ml-models/mlp-2.4.4/metadata/part-00000 @@ -0,0 +1 @@ +{"class":"org.apache.spark.ml.classification.MultilayerPerceptronClassificationModel","timestamp":1577833765310,"sparkVersion":"2.4.4","uid":"mlpc_30aa2f44dacc","paramMap":{},"defaultParamMap":{"rawPredictionCol":"rawPrediction","predictionCol":"prediction","probabilityCol":"probability","labelCol":"label","featuresCol":"features"}} diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/._SUCCESS.crc b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/._SUCCESS.crc new file mode 100644 index 0000000000000..3b7b044936a89 Binary files /dev/null and b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/._SUCCESS.crc differ diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/.part-00000-f09b03f6-6e17-4756-b9ca-c5e505dcd898-c000.snappy.parquet.crc b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/.part-00000-f09b03f6-6e17-4756-b9ca-c5e505dcd898-c000.snappy.parquet.crc new file mode 100644 index 0000000000000..ce4d20f391d63 Binary files /dev/null and b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/.part-00000-f09b03f6-6e17-4756-b9ca-c5e505dcd898-c000.snappy.parquet.crc differ diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/_SUCCESS b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/_SUCCESS new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mllib/src/test/resources/test-data/strIndexerModel/data/part-00000-cfefeb56-2980-4c42-b8a7-a5a94265c479-c000.snappy.parquet b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/part-00000-f09b03f6-6e17-4756-b9ca-c5e505dcd898-c000.snappy.parquet similarity index 66% rename from mllib/src/test/resources/test-data/strIndexerModel/data/part-00000-cfefeb56-2980-4c42-b8a7-a5a94265c479-c000.snappy.parquet rename to mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/part-00000-f09b03f6-6e17-4756-b9ca-c5e505dcd898-c000.snappy.parquet index 917984c2608be..c09eddfc3b9dd 100644 Binary files a/mllib/src/test/resources/test-data/strIndexerModel/data/part-00000-cfefeb56-2980-4c42-b8a7-a5a94265c479-c000.snappy.parquet and b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/data/part-00000-f09b03f6-6e17-4756-b9ca-c5e505dcd898-c000.snappy.parquet differ diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/._SUCCESS.crc b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/._SUCCESS.crc new file mode 100644 index 0000000000000..3b7b044936a89 Binary files /dev/null and b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/._SUCCESS.crc differ diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/.part-00000.crc b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/.part-00000.crc new file mode 100644 index 0000000000000..24a9b178030ee Binary files /dev/null and b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/.part-00000.crc differ diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/_SUCCESS b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/_SUCCESS new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/part-00000 b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/part-00000 new file mode 100644 index 0000000000000..6261108fbd009 --- /dev/null +++ b/mllib/src/test/resources/ml-models/strIndexerModel-2.4.4/metadata/part-00000 @@ -0,0 +1 @@ +{"class":"org.apache.spark.ml.feature.StringIndexerModel","timestamp":1577831053235,"sparkVersion":"2.4.4","uid":"myStringIndexerModel","paramMap":{"inputCol":"myInputCol","outputCol":"myOutputCol","handleInvalid":"skip"},"defaultParamMap":{"outputCol":"myStringIndexerModel__output","handleInvalid":"error"}} diff --git a/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/.part-00000.crc b/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/.part-00000.crc deleted file mode 100644 index 1ac377ac0dac4..0000000000000 Binary files a/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/.part-00000.crc and /dev/null differ diff --git a/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/part-00000 b/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/part-00000 deleted file mode 100644 index 492a07ae06d5a..0000000000000 --- a/mllib/src/test/resources/test-data/hashingTF-pre3.0/metadata/part-00000 +++ /dev/null @@ -1 +0,0 @@ -{"class":"org.apache.spark.ml.feature.HashingTF","timestamp":1564446310495,"sparkVersion":"2.3.0-SNAPSHOT","uid":"hashingTF_8ced2ab477c1","paramMap":{"binary":true,"numFeatures":100,"outputCol":"features","inputCol":"words"}} diff --git a/mllib/src/test/resources/test-data/strIndexerModel/metadata/part-00000 b/mllib/src/test/resources/test-data/strIndexerModel/metadata/part-00000 deleted file mode 100644 index 5650199c36dca..0000000000000 --- a/mllib/src/test/resources/test-data/strIndexerModel/metadata/part-00000 +++ /dev/null @@ -1 +0,0 @@ -{"class":"org.apache.spark.ml.feature.StringIndexerModel","timestamp":1545536052048,"sparkVersion":"2.4.1-SNAPSHOT","uid":"strIdx_056bb5da1bf2","paramMap":{"outputCol":"index","inputCol":"str"},"defaultParamMap":{"outputCol":"strIdx_056bb5da1bf2__output","stringOrderType":"frequencyDesc","handleInvalid":"error"}} diff --git a/mllib/src/test/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifierSuite.scala b/mllib/src/test/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifierSuite.scala index 6b5fe6e49ffea..902af71e42f86 100644 --- a/mllib/src/test/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifierSuite.scala +++ b/mllib/src/test/scala/org/apache/spark/ml/classification/MultilayerPerceptronClassifierSuite.scala @@ -229,4 +229,17 @@ class MultilayerPerceptronClassifierSuite extends MLTest with DefaultReadWriteTe assert(expected.weights === actual.weights) } } + + test("Load MultilayerPerceptronClassificationModel prior to Spark 3.0") { + val mlpPath = testFile("ml-models/mlp-2.4.4") + val model = MultilayerPerceptronClassificationModel.load(mlpPath) + val layers = model.getLayers + assert(layers(0) === 4) + assert(layers(1) === 5) + assert(layers(2) === 2) + + val metadata = spark.read.json(s"$mlpPath/metadata") + val sparkVersionStr = metadata.select("sparkVersion").first().getString(0) + assert(sparkVersionStr == "2.4.4") + } } diff --git a/mllib/src/test/scala/org/apache/spark/ml/feature/HashingTFSuite.scala b/mllib/src/test/scala/org/apache/spark/ml/feature/HashingTFSuite.scala index be70cf89cdb21..722302e5a165f 100644 --- a/mllib/src/test/scala/org/apache/spark/ml/feature/HashingTFSuite.scala +++ b/mllib/src/test/scala/org/apache/spark/ml/feature/HashingTFSuite.scala @@ -89,7 +89,7 @@ class HashingTFSuite extends MLTest with DefaultReadWriteTest { } test("SPARK-23469: Load HashingTF prior to Spark 3.0") { - val hashingTFPath = testFile("test-data/hashingTF-pre3.0") + val hashingTFPath = testFile("ml-models/hashingTF-2.4.4") val loadedHashingTF = HashingTF.load(hashingTFPath) val mLlibHashingTF = new MLlibHashingTF(100) assert(loadedHashingTF.indexOf("a") === mLlibHashingTF.indexOf("a")) @@ -99,7 +99,7 @@ class HashingTFSuite extends MLTest with DefaultReadWriteTest { val metadata = spark.read.json(s"$hashingTFPath/metadata") val sparkVersionStr = metadata.select("sparkVersion").first().getString(0) - assert(sparkVersionStr == "2.3.0-SNAPSHOT") + assert(sparkVersionStr == "2.4.4") } test("read/write") { diff --git a/mllib/src/test/scala/org/apache/spark/ml/feature/StringIndexerSuite.scala b/mllib/src/test/scala/org/apache/spark/ml/feature/StringIndexerSuite.scala index d89930342e85d..b5ce2bace98f6 100644 --- a/mllib/src/test/scala/org/apache/spark/ml/feature/StringIndexerSuite.scala +++ b/mllib/src/test/scala/org/apache/spark/ml/feature/StringIndexerSuite.scala @@ -459,13 +459,13 @@ class StringIndexerSuite extends MLTest with DefaultReadWriteTest { } test("Load StringIndexderModel prior to Spark 3.0") { - val modelPath = testFile("test-data/strIndexerModel") + val modelPath = testFile("ml-models/strIndexerModel-2.4.4") val loadedModel = StringIndexerModel.load(modelPath) assert(loadedModel.labelsArray === Array(Array("b", "c", "a"))) val metadata = spark.read.json(s"$modelPath/metadata") val sparkVersionStr = metadata.select("sparkVersion").first().getString(0) - assert(sparkVersionStr == "2.4.1-SNAPSHOT") + assert(sparkVersionStr == "2.4.4") } } diff --git a/project/MimaExcludes.scala b/project/MimaExcludes.scala index 259d30a259196..c662f1d1bf72c 100644 --- a/project/MimaExcludes.scala +++ b/project/MimaExcludes.scala @@ -328,6 +328,10 @@ object MimaExcludes { // [SPARK-26457] Show hadoop configurations in HistoryServer environment tab ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.spark.status.api.v1.ApplicationEnvironmentInfo.this"), + // [SPARK-30144][ML] Make MultilayerPerceptronClassificationModel extend MultilayerPerceptronParams + ProblemFilters.exclude[IncompatibleResultTypeProblem]("org.apache.spark.ml.classification.MultilayerPerceptronClassificationModel.layers"), + ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.spark.ml.classification.MultilayerPerceptronClassificationModel.this"), + // Data Source V2 API changes (problem: Problem) => problem match { case MissingClassProblem(cls) => diff --git a/python/pyspark/ml/classification.py b/python/pyspark/ml/classification.py index 8e2b2396be14d..c684856408cc1 100644 --- a/python/pyspark/ml/classification.py +++ b/python/pyspark/ml/classification.py @@ -2145,7 +2145,9 @@ class MultilayerPerceptronClassifier(JavaProbabilisticClassifier, _MultilayerPer >>> model = mlp.fit(df) >>> model.setFeaturesCol("features") MultilayerPerceptronClassificationModel... - >>> model.layers + >>> model.getMaxIter() + 100 + >>> model.getLayers() [2, 2, 2] >>> model.weights.size 12 @@ -2170,7 +2172,7 @@ class MultilayerPerceptronClassifier(JavaProbabilisticClassifier, _MultilayerPer >>> model_path = temp_path + "/mlp_model" >>> model.save(model_path) >>> model2 = MultilayerPerceptronClassificationModel.load(model_path) - >>> model.layers == model2.layers + >>> model.getLayers() == model2.getLayers() True >>> model.weights == model2.weights True @@ -2178,7 +2180,7 @@ class MultilayerPerceptronClassifier(JavaProbabilisticClassifier, _MultilayerPer >>> model3 = mlp2.fit(df) >>> model3.weights != model2.weights True - >>> model3.layers == model.layers + >>> model3.getLayers() == model.getLayers() True .. versionadded:: 1.6.0 @@ -2274,7 +2276,8 @@ def setSolver(self, value): return self._set(solver=value) -class MultilayerPerceptronClassificationModel(JavaProbabilisticClassificationModel, JavaMLWritable, +class MultilayerPerceptronClassificationModel(JavaProbabilisticClassificationModel, + _MultilayerPerceptronParams, JavaMLWritable, JavaMLReadable): """ Model fitted by MultilayerPerceptronClassifier. @@ -2282,14 +2285,6 @@ class MultilayerPerceptronClassificationModel(JavaProbabilisticClassificationMod .. versionadded:: 1.6.0 """ - @property - @since("1.6.0") - def layers(self): - """ - array of layer sizes including input and output layers. - """ - return self._call_java("javaLayers") - @property @since("2.0.0") def weights(self):