Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -273,29 +273,29 @@ 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 modelLayers array of layer sizes including input and output layers

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know the constructor is private, but is it necessary to change this name?

@zhengruifeng zhengruifeng Dec 12, 2019

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is needed, since all estimators and their models should share the same params, and there is by chance a param named layers...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about just removing modelLayers in model?
since the value (array of layer sizes) can be easily obtain by $(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],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we update migration guild?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srowen Sean, this question is for you.

@Since("1.5.0") val modelLayers: Array[Int],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question, why need to change this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, i see. MultilayerPerceptronParams has layers too?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename layers of MultilayerPerceptronParams instead?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but renaming layers of MultilayerPerceptronParams will also break the API.

@Since("2.0.0") val weights: Vector)
extends ProbabilisticClassificationModel[Vector, MultilayerPerceptronClassificationModel]
with Serializable with MLWritable {
with MultilayerPerceptronParams with Serializable with MLWritable {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related to this change. But do we use MultilayerPerceptronClassificationModel in executors? Like not every classification model extends Serializable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about this. Seems only the tree related model extends Serializable.


@Since("1.6.0")
override val numFeatures: Int = layers.head
override val numFeatures: Int = modelLayers.head

private[ml] val mlpModel = FeedForwardTopology
.multiLayerPerceptron(layers, softmaxOnTop = true)
.multiLayerPerceptron(modelLayers, softmaxOnTop = true)
.model(weights)

/**
* Returns layers in a Java List.
*/
private[ml] def javaLayers: java.util.List[Int] = {
layers.toList.asJava
modelLayers.toList.asJava
}

/**
Expand All @@ -308,7 +308,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, modelLayers, weights)
.setParent(parent)
copyValues(copied, extra)
}

Expand All @@ -322,11 +323,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 = modelLayers.last

@Since("3.0.0")
override def toString: String = {
s"MultilayerPerceptronClassificationModel: uid=$uid, numLayers=${layers.length}, " +
s"MultilayerPerceptronClassificationModel: uid=$uid, numLayers=${modelLayers.length}, " +
s"numClasses=$numClasses, numFeatures=$numFeatures"
}
}
Expand All @@ -353,7 +354,7 @@ object MultilayerPerceptronClassificationModel
// Save metadata and Params
DefaultParamsWriter.saveMetadata(instance, path, sc)
// Save model data: layers, weights

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no layers now.

val data = Data(instance.layers, instance.weights)
val data = Data(instance.modelLayers, instance.weights)
val dataPath = new Path(path, "data").toString
sparkSession.createDataFrame(Seq(data)).repartition(1).write.parquet(dataPath)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.modelLayers

def transform(dataset: Dataset[_]): DataFrame = {
pipeline.transform(dataset)
Expand Down
3 changes: 3 additions & 0 deletions project/MimaExcludes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ 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"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question. Is this worth to break the API, @huaxingao ?


// Data Source V2 API changes
(problem: Problem) => problem match {
case MissingClassProblem(cls) =>
Expand Down
13 changes: 8 additions & 5 deletions python/pyspark/ml/classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -2145,7 +2145,9 @@ class MultilayerPerceptronClassifier(JavaProbabilisticClassifier, _MultilayerPer
>>> model = mlp.fit(df)
>>> model.setFeaturesCol("features")
MultilayerPerceptronClassificationModel...
>>> model.layers
>>> model.getMaxIter()
100
>>> model.modelLayers
[2, 2, 2]
>>> model.weights.size
12
Expand All @@ -2170,15 +2172,15 @@ class MultilayerPerceptronClassifier(JavaProbabilisticClassifier, _MultilayerPer
>>> model_path = temp_path + "/mlp_model"
>>> model.save(model_path)
>>> model2 = MultilayerPerceptronClassificationModel.load(model_path)
>>> model.layers == model2.layers
>>> model.modelLayers == model2.modelLayers
True
>>> model.weights == model2.weights
True
>>> mlp2 = mlp2.setInitialWeights(list(range(0, 12)))
>>> model3 = mlp2.fit(df)
>>> model3.weights != model2.weights
True
>>> model3.layers == model.layers
>>> model3.modelLayers == model.modelLayers
True

.. versionadded:: 1.6.0
Expand Down Expand Up @@ -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.
Expand All @@ -2284,7 +2287,7 @@ class MultilayerPerceptronClassificationModel(JavaProbabilisticClassificationMod

@property
@since("1.6.0")
def layers(self):
def modelLayers(self):
"""
array of layer sizes including input and output layers.
"""
Expand Down