Как экспортировать модель воды как MOJO из газированной воды в scala для загрузки с помощью EasyPredictModelWrapper

Моя цель - экспортировать модель воды, обученную на Spark с помощью scala (с использованием газированной воды), чтобы я мог импортировать ее в приложение без Spark.

Таким образом:

  • с использованием scala (в документации показаны примеры только для r и python)
  • экспортировать модель, построенную с использованием газированной воды (вода с искрой)
  • импортировать модель в scala (без Spark и H2O кластера, только пакет hex-genmodel)

Поэтому я использую ModelSerializationSupport для экспорта и MojoModel.load для импорта

val gbmParams = new GBMParameters()
gbmParams._train = train
gbmParams._response_column = "target"
gbmParams._ntrees = 5
gbmParams._valid = valid
gbmParams._nfolds = 3 
gbmParams._min_rows = 1
gbmParams._distribution = DistributionFamily.multinomial
val gbm = new GBM(gbmParams)
val gbmModel = gbm.trainModel.get
val mojoPath = "./model.zip"
ModelSerializationSupport.exportMOJOModel(gbmModel, new File(mojoPath).toURI, force = true)
val simpleModel = new EasyPredictModelWrapper(MojoModel.load(mojoPath))

Не работает с

error in opening zip file
java.util.zip.ZipException: error in opening zip file
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(ZipFile.java:220)
at java.util.zip.ZipFile.<init>(ZipFile.java:150)
at java.util.zip.ZipFile.<init>(ZipFile.java:121)
at hex.genmodel.ZipfileMojoReaderBackend.<init>(ZipfileMojoReaderBackend.java:13)
at hex.genmodel.MojoModel.load(MojoModel.java:33)
...

Похоже, что экспортер mojo не использует тот же формат, что и в hex.genmodel (очевидно, zip)

Запуск на h2o 2.1.23 (2.1.24 дает сбой при построении кластера, как указано на https://0xdata.atlassian.net/browse/SW-776) и Spark 2.1

-- Обновить:

Использование класса ModelSerializationSupport для загрузки собственного экспорта также не работает с тем же исключением:

ModelSerializationSupport.loadMOJOModel(new File(mojoPath).toURI)

Экспорт и загрузка H2OModel
Загрузка обратно как H2OModel (то есть с газированной водой) работает:

val h2oModelPath = "./model_h2o"
ModelSerializationSupport.exportH2OModel(gbmModel, new File(h2oModelPath).toURI, force = true)
val loadedModel: GBMModel = ModelSerializationSupport.loadH2OModel(new File(h2oModelPath).toURI)

Экспорт и загрузка модели H2OMOJOM
Загрузка ее обратно с помощью H2OMOJOModel работает (скопировано из реализации H2OGBM):

val mojoModel = new H2OMOJOModel(ModelSerializationSupport.getMojoData(gbmModel))
mojoModel.write.overwrite.save(mojoPath)
H2OMOJOModel.load(mojoPath) 

Экспорт H2OGBM с импортом MojoModel
Однако попытка импорта с использованием обычного MojoModel не удалась:

val gbm = new H2OGBM(gbmParams)(h2oContext, myspark.sqlContext)
val gbmModel = gbm.trainModel(gbmParams)
val mojoPath = "./models.zip"
gbmModel.write.overwrite.save(mojoPath)
MojoModel.load(mojoPath)

за следующим исключением:

./models.zip/model.ini (No such file or directory)
java.io.FileNotFoundException: ./models.zip/model.ini (No such file or directory)

person gerben    schedule 27.03.2018    source источник


Ответы (1)


Решение фактически объясняется в getMojoModel (который принимает либо Model[_,_,_], либо Array[Byte]) на ModelSerializationSupport

Реализация getMojoModel(Model[_,_,_]) использует байтовый массив для хранения getMojoData(Model[_,_,_]), а затем считывает его обратно из этого байтового массива.

Быстрая проверка работает следующим образом:

val config = new EasyPredictModelWrapper.Config()
config.setModel(ModelSerializationSupport.getMojoModel(gbmModel))
config.setConvertUnknownCategoricalLevelsToNa(true)
val easyPredictModelWrapper = new EasyPredictModelWrapper(config)

Таким образом, теперь мы можем воспроизвести его самостоятельно, но без использования класса ModelSerializationSupport (поскольку он является частью газированной воды).

Сначала сохраните данные mojo в файл:

val path = java.nio.file.Files.createTempFile("model", ".mojo")
path.toFile.deleteOnExit()
path.toString
import java.io.FileOutputStream
val outputStream = new FileOutputStream(path.toFile)
try {
  gbmModel.getMojo.writeTo(outputStream
}
finally if (outputStream != null) outputStream.close()

А затем прочтите байты (в другом приложении scala):

val is = new FileInputStream(path.toFile)
val reader = MojoReaderBackendFactory.createReaderBackend(is, MojoReaderBackendFactory.CachingStrategy.MEMORY)
val mojoModel = ModelMojoReader.readFrom(reader)
val config = new EasyPredictModelWrapper.Config()
config.setModel(mojoModel)
config.setConvertUnknownCategoricalLevelsToNa(true)
val easyPredictModelWrapper = new EasyPredictModelWrapper(config)
person gerben    schedule 27.03.2018