Чтобы это работало, вам нужен доступ к определенному MlReadable
объекту.
import org.apache.spark.ml.util.MLReadable
def modelFromBytes[M](obj: MLReadable[M], modelArray: Array[Byte]): M = {
val tempPath: Path = ???
...
obj.read.load(tempPath.toString)
}
, который позже можно использовать как:
val bytes: Array[Byte] = ???
modelFromBytes(CountVectorizerModel, bytes)
Обратите внимание, что, несмотря на первое появление, здесь нет ничего рекурсивного - MLReadable[M]
относится к объекту-компаньону, а не к классу как таковому. Так, например, CountVectorizerModel
объект равен MLReadable
, а CountVectorizeModel
класс - нет.
Внутри Spark MLReader
обрабатывает это по-другому - создает экземпляр класса, используя отражение , а затем устанавливает его Params
. Однако этот путь не будет очень полезным для вас здесь *.
Если требуется совместимость с текущим API, вы можете попытаться сделать читаемый объект неявным:
def modelFromBytes[M](modelArray: Array[Byte])(implicit obj: MLReadable[M]): M = {
...
}
, а затем
implicit val readable: MLReadable[CountVectorizerModel] = CountVectorizerModel
modelFromBytes[CountVectorizerModel](bytes)
* Технически говоря, можно получить сопутствующий объект с помощью отражения
def modelFromBytesCV[M <: MLWritable](
modelArray: Array[Byte])(implicit ct: ClassTag[M]): M = {
val tempPath: Path = ???
...
val cls = Class.forName(ct.runtimeClass.getName + "$");
cls.getField("MODULE$").get(cls).asInstanceOf[MLReadable[M]]
.read.load(tempPath.toString))
}
но я не думаю, что этот путь стоит здесь исследовать. В частности, мы не можем предоставить здесь строгие границы типов - использование MLWritable
является хаком для ограничения человеческих ошибок, но довольно бесполезно для компилятора.