From 8c947e24cf89ca775be3371d4cfb23e89cbcce9f Mon Sep 17 00:00:00 2001 From: Piotr Gawrys Date: Mon, 6 Apr 2020 19:14:08 +0200 Subject: [PATCH 1/2] Add part of the Keras API --- build.sbt | 10 ++- project/build.properties | 2 +- .../scalapy/tensorflow/keras/Keras.scala | 21 +++++ .../tensorflow/keras/backend/Backend.scala | 8 ++ .../tensorflow/keras/datasets/Datasets.scala | 7 ++ .../tensorflow/keras/datasets/Mnist.scala | 8 ++ .../scalapy/tensorflow/keras/layers.scala | 31 ++++++++ .../tensorflow/keras/layers/Conv2D.scala | 23 ++++++ .../tensorflow/keras/layers/Dense.scala | 17 ++++ .../tensorflow/keras/layers/Dropout.scala | 11 +++ .../tensorflow/keras/layers/Flatten.scala | 7 ++ .../tensorflow/keras/layers/Layer.scala | 5 ++ .../tensorflow/keras/layers/Layers.scala | 49 ++++++++++++ .../keras/layers/MaxPooling2D.scala | 11 +++ .../tensorflow/keras/layers/package.scala | 30 +++++++ .../tensorflow/keras/losses/Losses.scala | 8 ++ .../tensorflow/keras/models/Models.scala | 7 ++ .../tensorflow/keras/models/Sequential.scala | 37 +++++++++ .../keras/optimizers/Optimizers.scala | 18 +++++ .../tensorflow/keras/utils/Utils.scala | 8 ++ .../scalapy/tensorflow/MnistExample.scala | 79 +++++++++++++++++++ 21 files changed, 392 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/Keras.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/backend/Backend.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Datasets.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Mnist.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Conv2D.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dense.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dropout.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Flatten.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layer.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layers.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/MaxPooling2D.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/package.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/losses/Losses.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Models.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Sequential.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/optimizers/Optimizers.scala create mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/utils/Utils.scala create mode 100644 src/test/scala/me/shadaj/scalapy/tensorflow/MnistExample.scala diff --git a/build.sbt b/build.sbt index 645ac61..5410c5e 100644 --- a/build.sbt +++ b/build.sbt @@ -1,8 +1,9 @@ import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} +import scala.sys.process._ organization in ThisBuild := "me.shadaj" -scalaVersion in ThisBuild := "2.12.8" +scalaVersion in ThisBuild := "2.12.10" addCommandAlias( "publishSignedAll", @@ -27,13 +28,14 @@ lazy val scalaPyTensorFlowCross = crossProject(JVMPlatform, NativePlatform) .in(file(".")) .settings( name := "scalapy-tensorflow", - libraryDependencies += "me.shadaj" %%% "scalapy-core" % "0.3.0", - libraryDependencies += "me.shadaj" %%% "scalapy-numpy" % "0.1.0+3-046d1d67", + libraryDependencies += "me.shadaj" %%% "scalapy-core" % "0.3.0+17-2bfe86de", + // TODO: update to released scalapy-numpy + libraryDependencies += "me.shadaj" %%% "scalapy-numpy" % "0.1.0+5-ad550211+20200401-1343", libraryDependencies += "org.scalatest" %%% "scalatest" % "3.1.0-SNAP8" % Test ).jvmSettings( libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.14.0" % Test, fork in Test := true, - javaOptions in Test += s"-Djava.library.path=${sys.env.getOrElse("JEP_PATH", "/usr/local/lib/python3.7/site-packages/jep")}" + javaOptions in Test += s"-Djna.library.path=${"python3-config --prefix".!!.trim}/lib" ).nativeSettings( scalaVersion := "2.11.12", libraryDependencies += "com.github.lolgab" %%% "scalacheck" % "1.14.1" % Test, diff --git a/project/build.properties b/project/build.properties index c0bab04..e018b89 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.2.8 +sbt.version=1.3.3 \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/Keras.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/Keras.scala new file mode 100644 index 0000000..2ea174f --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/Keras.scala @@ -0,0 +1,21 @@ +package me.shadaj.scalapy.tensorflow.keras + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.tensorflow.keras.backend.Backend +import me.shadaj.scalapy.tensorflow.keras.datasets.Datasets +import me.shadaj.scalapy.tensorflow.keras.models.Models +import me.shadaj.scalapy.tensorflow.keras.optimizers.Optimizers +import me.shadaj.scalapy.tensorflow.keras.utils.Utils +import me.shadaj.scalapy.tensorflow.keras.losses.Losses +import me.shadaj.scalapy.tensorflow.keras.layers.Layers + + +@py.native trait Keras extends py.Object { + def models: Models = py.native + def datasets: Datasets = py.native + def backend: Backend = py.native + def utils: Utils = py.native + def optimizers: Optimizers = py.native + def losses: Losses = py.native + def layers: Layers = py.native +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/backend/Backend.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/backend/Backend.scala new file mode 100644 index 0000000..5c0a4d1 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/backend/Backend.scala @@ -0,0 +1,8 @@ +package me.shadaj.scalapy.tensorflow.keras.backend + +import me.shadaj.scalapy.py + +@py.native trait Backend extends py.Object { + def image_data_format(): String = py.native +} + diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Datasets.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Datasets.scala new file mode 100644 index 0000000..81ddd4f --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Datasets.scala @@ -0,0 +1,7 @@ +package me.shadaj.scalapy.tensorflow.keras.datasets + +import me.shadaj.scalapy.py + +@py.native trait Datasets extends py.Object { + def mnist: Mnist = py.native +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Mnist.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Mnist.scala new file mode 100644 index 0000000..7b27dec --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/datasets/Mnist.scala @@ -0,0 +1,8 @@ +package me.shadaj.scalapy.tensorflow.keras.datasets + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.numpy.NDArray + +@py.native trait Mnist extends py.Object { + def load_data(path: String = "mnist.npz"): ((NDArray[Long], NDArray[Long]), (NDArray[Long], NDArray[Long])) = py.native +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala new file mode 100644 index 0000000..67da5a7 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala @@ -0,0 +1,31 @@ +package me.shadaj.scalapy.tensorflow + +import scala.util.control.NonFatal +import me.shadaj.scalapy.py.{PyValue, Reader} +import me.shadaj.scalapy.py + +// TODO: move to scalapy +package object keras { + implicit val tupleReader: Reader[py.|[Int, (Int, Int)]] = new Reader[py.|[Int, (Int, Int)]] { + override def read(v: PyValue): py.|[Int, (Int, Int)] = { + try { + v.getLong.toInt + } catch { + case NonFatal(_) => + val tuple = v.getTuple + (tuple(0).getLong.toInt, tuple(1).getLong.toInt) + } + } + } + + implicit def noneReader[T](implicit reader: Reader[T]): Reader[py.NoneOr[T]] = new Reader[py.NoneOr[T]] { + override def read(v: PyValue): py.NoneOr[T] = { + try { + reader.read(v) + } catch { + // TODO: I assume it's incorrect and could catch unrelated types + case NonFatal(_) => py.None + } + } + } +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Conv2D.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Conv2D.scala new file mode 100644 index 0000000..6056215 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Conv2D.scala @@ -0,0 +1,23 @@ +package me.shadaj.scalapy.tensorflow.keras +package layers + +import me.shadaj.scalapy.py + +@py.native trait Conv2D extends Layer { + def filters: Int = py.native + def kernel_size: py.|[Int, (Int, Int)] = py.native + def strides: py.|[Int, (Int, Int)] = py.native + def padding: String = py.native + def data_format: py.NoneOr[String] = py.native + def dilation_rate: py.|[Int, (Int, Int)] = py.native + def activation: py.NoneOr[String] = py.native + def use_bias: Boolean = py.native + def kernel_initializer: String = py.native + def bias_initializer: String = py.native + def kernel_regularizer: py.NoneOr[String] = py.native + def bias_regularizer: py.NoneOr[String] = py.native + def activity_regularizer: py.NoneOr[String] = py.native + def kernel_constraint: py.NoneOr[String] = py.native + def bias_constraint: py.NoneOr[String] = py.native + def input_shape: py.NoneOr[(Int, Int, Int)] = py.native +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dense.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dense.scala new file mode 100644 index 0000000..5f32d3b --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dense.scala @@ -0,0 +1,17 @@ +package me.shadaj.scalapy.tensorflow.keras +package layers + +import me.shadaj.scalapy.py + +@py.native trait Dense extends Layer { + def units: Int = py.native + def activation: py.NoneOr[String] = py.native + def use_bias: Boolean = py.native + def kernel_initializer: String = py.native + def bias_initializer: String = py.native + def kernel_regularizer: py.NoneOr[String] = py.native + def bias_regularizer: py.NoneOr[String] = py.native + def activity_regularizer: py.NoneOr[String] = py.native + def kernel_constraint: py.NoneOr[String] = py.native + def bias_constraint: py.NoneOr[String] = py.native +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dropout.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dropout.scala new file mode 100644 index 0000000..df6b4cf --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Dropout.scala @@ -0,0 +1,11 @@ +package me.shadaj.scalapy.tensorflow.keras +package layers + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.tensorflow.Tensor + +@py.native trait Dropout extends Layer { + def rate: Double = py.native + def noise_shape: py.NoneOr[Tensor] = py.native + def seed: py.NoneOr[Int] = py.native +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Flatten.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Flatten.scala new file mode 100644 index 0000000..843b5c8 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Flatten.scala @@ -0,0 +1,7 @@ +package me.shadaj.scalapy.tensorflow.keras.layers + +import me.shadaj.scalapy.py + +@py.native trait Flatten extends Layer { + def data_format: py.NoneOr[String] = py.native +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layer.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layer.scala new file mode 100644 index 0000000..1328140 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layer.scala @@ -0,0 +1,5 @@ +package me.shadaj.scalapy.tensorflow.keras.layers + +import me.shadaj.scalapy.py + +@py.native trait Layer extends py.Object diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layers.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layers.scala new file mode 100644 index 0000000..d7b749d --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/Layers.scala @@ -0,0 +1,49 @@ +package me.shadaj.scalapy.tensorflow.keras.layers + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.tensorflow.Tensor + +@py.native trait Layers extends py.Object { + def Conv2D(filters: Int, + kernel_size: py.|[Int, (Int, Int)], + strides: py.|[Int, (Int, Int)] = (1, 1), + padding: String = "valid", + data_format: py.NoneOr[String] = py.None, + dilation_rate: py.|[Int, (Int, Int)] = (1, 1), + activation: py.NoneOr[String] = py.None, + use_bias: Boolean = true, + kernel_initializer: String = "glorot_uniform", + bias_initializer: String = "zeros", + kernel_regularizer: py.NoneOr[String] = py.None, + bias_regularizer: py.NoneOr[String] = py.None, + activity_regularizer: py.NoneOr[String] = py.None, + kernel_constraint: py.NoneOr[String] = py.None, + bias_constraint: py.NoneOr[String] = py.None, + input_shape: py.NoneOr[(Int, Int, Int)] = py.None + ): Conv2D = py.nativeNamed + + def Dropout(rate: Double, + noise_shape: py.NoneOr[Tensor] = py.None, + seed: py.NoneOr[Int] = py.None + ): Dropout = py.nativeNamed + + def MaxPooling2D(pool_size: (Int, Int), + strides: py.NoneOr[py.|[Int, (Int, Int)]] = py.None, + padding: String = "valid", + data_format: py.NoneOr[String] = py.None + ): MaxPooling2D = py.nativeNamed + + def Flatten(data_format: py.NoneOr[String] = py.None): Flatten = py.nativeNamed + + def Dense(units: Int, + activation: py.NoneOr[String] = py.None, + use_bias: Boolean = true, + kernel_initializer: String = "glorot_uniform", + bias_initializer: String = "zeros", + kernel_regularizer: py.NoneOr[String] = py.None, + bias_regularizer: py.NoneOr[String] = py.None, + activity_regularizer: py.NoneOr[String] = py.None, + kernel_constraint: py.NoneOr[String] = py.None, + bias_constraint: py.NoneOr[String] = py.None + ): Dense = py.nativeNamed +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/MaxPooling2D.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/MaxPooling2D.scala new file mode 100644 index 0000000..5866d35 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/MaxPooling2D.scala @@ -0,0 +1,11 @@ +package me.shadaj.scalapy.tensorflow.keras.layers + +import me.shadaj.scalapy.tensorflow.Tensor +import me.shadaj.scalapy.py + +@py.native trait MaxPooling2D extends Layer { + def pool_size: (Int, Int) = py.native + def strides: py.NoneOr[py.|[Int, (Int, Int)]] = py.native + def padding: String = py.native + def data_format: py.NoneOr[String] = py.native +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/package.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/package.scala new file mode 100644 index 0000000..304e16e --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers/package.scala @@ -0,0 +1,30 @@ +package me.shadaj.scalapy.tensorflow.keras + +import scala.util.control.NonFatal +import me.shadaj.scalapy.py.{PyValue, Reader} +import me.shadaj.scalapy.py + +package object layers { + implicit val tupleReader: Reader[py.|[Int, (Int, Int)]] = new Reader[py.|[Int, (Int, Int)]] { + override def read(v: PyValue): py.|[Int, (Int, Int)] = { + try { + v.getLong.toInt + } catch { + case NonFatal(_) => + val tuple = v.getTuple + (tuple(0).getLong.toInt, tuple(1).getLong.toInt) + } + } + } + + implicit def noneReader[T](implicit reader: Reader[T]): Reader[py.NoneOr[T]] = new Reader[py.NoneOr[T]] { + override def read(v: PyValue): py.NoneOr[T] = { + try { + reader.read(v) + } catch { + // TODO: read None + case NonFatal(_) => py.None + } + } + } +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/losses/Losses.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/losses/Losses.scala new file mode 100644 index 0000000..cf28280 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/losses/Losses.scala @@ -0,0 +1,8 @@ +package me.shadaj.scalapy.tensorflow.keras.losses + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.py.PyFunction + +@py.native trait Losses extends py.Object { + def categorical_crossentropy: PyFunction = py.native +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Models.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Models.scala new file mode 100644 index 0000000..10d2fe3 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Models.scala @@ -0,0 +1,7 @@ +package me.shadaj.scalapy.tensorflow.keras.models + +import me.shadaj.scalapy.py + +@py.native trait Models extends py.Object { + def Sequential(): Sequential = py.native +} diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Sequential.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Sequential.scala new file mode 100644 index 0000000..d44de09 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/models/Sequential.scala @@ -0,0 +1,37 @@ +package me.shadaj.scalapy.tensorflow.keras +package models + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.tensorflow.keras.layers.Layer +import me.shadaj.scalapy.numpy.NDArray +import me.shadaj.scalapy.py.PyFunction +import me.shadaj.scalapy.tensorflow.keras.optimizers.Optimizer + +@py.native trait Sequential extends py.Object { + private val origDynamic = this.as[py.Dynamic] + + def add(layer: Layer): Unit = origDynamic.add(layer = layer).as[Unit] + + def compile(optimizer: py.|[String, Optimizer] = "rmsprop", + loss: py.NoneOr[PyFunction] = py.None, + metrics: Seq[String] = Seq.empty, + loss_weights: Seq[(Double, Double)] = Seq.empty, + sample_weight_mode: py.NoneOr[String] = py.None, + weighted_metrics: Seq[String] = Seq.empty, + target_tensors: py.NoneOr[String] = py.None + ) = origDynamic.compile(optimizer = optimizer, loss = loss, metrics = metrics, loss_weights = loss_weights, sample_weight_mode = sample_weight_mode, weighted_metrics = weighted_metrics, target_tensors = target_tensors).as[Unit] + + def fit(x: NDArray[Long], + y: NDArray[Long], + batch_size: py.NoneOr[Int] = py.None, + epochs: Int = 1, + verbose: Int = 1, + validation_data: py.NoneOr[(NDArray[Long], NDArray[Long])] = py.None + ): Unit = origDynamic.fit(x = x, y = y, batch_size = batch_size, epochs = epochs, verbose = verbose, validation_data = validation_data).as[Unit] + + def evaluate(x: NDArray[Long], + y: NDArray[Long], + batch_size: py.NoneOr[Int] = py.None, + verbose: Int = 1 + ): Seq[Double] = origDynamic.evaluate(x = x, y = y, batch_size = batch_size, verbose = verbose).as[Seq[Double]] +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/optimizers/Optimizers.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/optimizers/Optimizers.scala new file mode 100644 index 0000000..cd41501 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/optimizers/Optimizers.scala @@ -0,0 +1,18 @@ +package me.shadaj.scalapy.tensorflow.keras.optimizers + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.py.{PyValue, Reader} + +@py.native trait Optimizers extends py.Object { + def Adadelta(): Adadelta = py.native +} + +@py.native trait Optimizer extends py.Object + +@py.native class Adadelta(val value: PyValue) extends Optimizer + +object Adadelta { + implicit val reader: Reader[Adadelta] = new Reader[Adadelta] { + override def read(v: PyValue): Adadelta = new Adadelta(v) + } +} \ No newline at end of file diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/utils/Utils.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/utils/Utils.scala new file mode 100644 index 0000000..ef44a89 --- /dev/null +++ b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/utils/Utils.scala @@ -0,0 +1,8 @@ +package me.shadaj.scalapy.tensorflow.keras.utils + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.numpy.NDArray + +@py.native trait Utils extends py.Object { + def to_categorical(y: NDArray[Long], num_classes: py.NoneOr[Long] = py.None, dtype: String = "float32"): NDArray[Long] = py.native +} diff --git a/src/test/scala/me/shadaj/scalapy/tensorflow/MnistExample.scala b/src/test/scala/me/shadaj/scalapy/tensorflow/MnistExample.scala new file mode 100644 index 0000000..b397b84 --- /dev/null +++ b/src/test/scala/me/shadaj/scalapy/tensorflow/MnistExample.scala @@ -0,0 +1,79 @@ +package me.shadaj.scalapy.tensorflow + +import me.shadaj.scalapy.py +import me.shadaj.scalapy.numpy.NumPy +import me.shadaj.scalapy.tensorflow.keras.Keras +import me.shadaj.scalapy.tensorflow.keras.datasets.Mnist +import me.shadaj.scalapy.tensorflow.keras.models.Sequential + +object MnistExample extends App { + val tf = py.module("tensorflow").as[TensorFlow] + val np = py.module("numpy").as[NumPy] + val kerasA = py.module("keras").as[Keras] + val K = kerasA.backend + val layers = kerasA.layers + + val batch_size = 128 + val num_classes = 10 + val epochs = 10 + + val img_rows, img_cols = 28 + + val mnist: Mnist = kerasA.datasets.mnist + val ((x_train, y_train), (x_test, y_test)) = mnist.load_data() + + val (train, test, input_shape) = + if (K.image_data_format() == "channels_first") { + val train = x_train.reshape(Seq(x_train.shape(0), 1, img_rows, img_cols)) + val test = x_test.reshape(Seq(x_test.shape(0), 1, img_rows, img_cols)) + val input_shape = (1, img_rows, img_cols) + + (train, test, input_shape) + } else { + val train = x_train.reshape(Seq(x_train.shape(0), img_rows, img_cols, 1)) + val test = x_test.reshape(Seq(x_test.shape(0), img_rows, img_cols, 1)) + val input_shape = (img_rows, img_cols, 1) + + (train, test, input_shape) + } + + // TODO: not type safe + val trainImages = train.astype(np.float32) / 255 + val testImages = test.astype(np.float32) / 255 + + println(s"x_train shape: ${trainImages.shape}") + println(s"${trainImages.shape(0)} train samples") + println(s"${testImages.shape(0)} test samples") + + val trainLabels = kerasA.utils.to_categorical(y_train, num_classes) + val testLabels = kerasA.utils.to_categorical(y_test, num_classes) + + val model = kerasA.models.Sequential() + model.add(layers.Conv2D(filters = 32, kernel_size = (3, 3), activation = "relu", input_shape = input_shape)) + model.add(layers.Conv2D(filters = 64, kernel_size = (3, 3), activation = "relu")) + model.add(layers.MaxPooling2D((2, 2))) + model.add(layers.Dropout(0.25)) + model.add(layers.Flatten()) + model.add(layers.Dense(units = 128, activation="relu")) + model.add(layers.Dropout(0.5)) + model.add(layers.Dense(units = num_classes, activation="softmax")) + + model.compile( + loss = kerasA.losses.categorical_crossentropy, + optimizer = kerasA.optimizers.Adadelta(), + metrics = Seq("accuracy") + ) + + model.fit( + x = trainImages, + y = trainLabels, + batch_size = batch_size, + epochs = epochs, + verbose = 1, + validation_data = (testImages, testLabels)) + + val score = model.evaluate(x = testImages, y = testLabels, verbose = 0) + + println(s"Test loss: ${score(0)}") + println(s"Test accuracy: ${score(1)}") +} From 47a0341f0f67408c6ca32db93b16e4f53f4a2416 Mon Sep 17 00:00:00 2001 From: Piotr Gawrys Date: Mon, 6 Apr 2020 19:24:25 +0200 Subject: [PATCH 2/2] remove duplicated readers --- .../scalapy/tensorflow/keras/layers.scala | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala diff --git a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala b/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala deleted file mode 100644 index 67da5a7..0000000 --- a/src/main/scala/me/shadaj/scalapy/tensorflow/keras/layers.scala +++ /dev/null @@ -1,31 +0,0 @@ -package me.shadaj.scalapy.tensorflow - -import scala.util.control.NonFatal -import me.shadaj.scalapy.py.{PyValue, Reader} -import me.shadaj.scalapy.py - -// TODO: move to scalapy -package object keras { - implicit val tupleReader: Reader[py.|[Int, (Int, Int)]] = new Reader[py.|[Int, (Int, Int)]] { - override def read(v: PyValue): py.|[Int, (Int, Int)] = { - try { - v.getLong.toInt - } catch { - case NonFatal(_) => - val tuple = v.getTuple - (tuple(0).getLong.toInt, tuple(1).getLong.toInt) - } - } - } - - implicit def noneReader[T](implicit reader: Reader[T]): Reader[py.NoneOr[T]] = new Reader[py.NoneOr[T]] { - override def read(v: PyValue): py.NoneOr[T] = { - try { - reader.read(v) - } catch { - // TODO: I assume it's incorrect and could catch unrelated types - case NonFatal(_) => py.None - } - } - } -}