繁体   English   中英

SBT:如何将类的实例打包为JAR?

[英]SBT: How to package an instance of a class as a JAR?

我的代码基本上是这样的:

class FoodTrainer(images: S3Path) { // data is >100GB file living in S3
  def train(): FoodClassifier       // Very expensive - takes ~5 hours!
}

class FoodClassifier {          // Light-weight API class
  def isHotDog(input: Image): Boolean
}

我想在JAR-assembly( sbt assembly )时,调用val classifier = new FoodTrainer(s3Dir).train()并发布具有classifier实例的JAR,该实例可立即供下游库用户使用。

最简单的方法是什么? 对此有哪些既定范式? 我知道它是ML项目中一个相当常见的成语,用于发布经过训练的模型,例如http://nlp.stanford.edu/software/stanford-corenlp-models-current.jar

如何使用sbt assembly执行此操作, sbt assembly大型模型类或数据文件签入到我的版本控制中?

您应该将训练产生的数据序列化到自己的文件中。 然后,您可以将此数据文件打包到JAR中。 您的生产代码打开文件并读取它而不是运行训练算法。

步骤如下。

在构建的资源生成阶段:

  1. 在构建的资源生成阶段生成模型。
  2. 将模型的内容序列化到托管资源文件夹中的文件。
    \n Compile中的resourceGenerators + = Def.task {\n   val classifier = new FoodTrainer(s3Dir).train()\n   val contents = FoodClassifier.serialize(分类器)\n   val file =(编译中的resourceManaged).value /“mypackage”/“food-classifier.model”\n   IO.write(文件,内容)\n   SEQ(文件)\n } .taskValue\n
  3. 资源将自动包含在jar文件中,并且不会出现在源树中。
  4. 要加载模型,只需添加读取资源的代码并解析模型。
    \n object FoodClassifierModel {\n   lazy val classifier = readResource(“/ mypackage / food-classifier.model”)\n   def readResource(resourceName:String):FoodClassifier = {\n     val stream = getClass.getResourceAsStream(resourceName)\n     val lines = scala.io.Source.fromInputStream(stream).getLines\n     val contents = lines.mkString(“\\ n”)\n     FoodClassifier.parse(内容)\n   }\n }\n 对象FoodClassifier {\n   def parse(content:String):FoodClassifier\n   def serialize(classfier:FoodClassifier):String\n }\n

当然,由于您的数据相当大,您需要使用流式序列化器和解析器来不会超载Java堆空间。 以上只是展示了如何在构建时打包资源。

http://www.scala-sbt.org/1.x/docs/Howto-Generating-Files.html

好的,我设法做到了:

  1. 将食物训练模块分成两个独立的SBT子模块: food-trainerfood-model 前者仅在编译时调用以创建模型并序列化为后者的生成资源 后者用作简单的工厂对象,用于从序列化版本实例化模型。 每个下游项目仅依赖于该food-model子模块。

  2. food-trainer拥有大部分代码,并且有一个可以序列化FoodModel的主要方法:

     object FoodTrainer { def main(args Array[String]): Unit = { val input = args(0) val outputDir = args(1) val model: FoodModel = new FoodTrainer(input).train() val out = new ObjectOutputStream(new File(outputDir + "/model.bin")) out.writeObject(model) } } 
  3. 添加编译时任务以在build.sbt生成food trainer模块:

     lazy val foodTrainer = (project in file("food-trainer")) lazy val foodModel = (project in file("food-model")) .dependsOn(foodTrainer) .settings( resourceGenerators in Compile += Def.task { val log = streams.value.log val dest = (resourceManaged in Compile).value IO.createDirectory(dest) runModuleMain( cmd = s"com.foo.bar.FoodTrainer $pathToImages ${dest.getAbsolutePath}", cp = (fullClasspath in Runtime in foodTrainer).value.files, log = log ) Seq(dest / "model.bin") } def runModuleMain(cmd: String, cp: Seq[File], log: Logger): Unit = { log.info(s"Running $cmd") val opt = ForkOptions(bootJars = cp, outputStrategy = Some(LoggedOutput(log))) val res = Fork.scala(config = opt, arguments = cmd.split(' ')) require(res == 0, s"$cmd exited with code $res") } 
  4. 现在在你的food-model模块中,你有这样的东西:

     object FoodModel { lazy val model: FoodModel = new ObjectInputStream(getClass.getResourceAsStream("/model.bin").readObject().asInstanceOf[FoodModel]) } 

现在,每个下游项目仅依赖于food-model并且只使用FoodModel.model 我们得益于:

  1. 这在运行时通过JAR的打包资源快速静态加载
  2. 无需在运行时训练模型(非常昂贵)
  3. 无需在版本控制中签入模型(二进制模型也非常大) - 它只包装到您的JAR中
  4. 无需将FoodTrainerFoodModel软件包分离到他们自己的JAR中(我们现在FoodModel在内部部署它们) - 相反,我们只是将它们保存在同一个项目中,但不同的子模块会被打包到一个JAR中。

这是一个想法,将您的模型放在一个资源文件夹中,该文件夹被添加到jar程序集中。 我认为如果它在该文件夹中,所有罐子都会与您的模型一起分发。 Lmk怎么样,欢呼!

检查一下从资源中读取:

https://www.mkyong.com/java/java-read-a-file-from-resources-folder/

它是用Java编写的,但你仍然可以在Scala中使用api。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM