First tensorflow test.

This commit is contained in:
Alexander Nozik 2021-11-10 20:04:29 +03:00
parent c583320051
commit 6c2abdaab0
6 changed files with 127 additions and 47 deletions

View File

@ -7,6 +7,7 @@ description = "Google tensorflow connector"
dependencies { dependencies {
api(project(":kmath-tensors")) api(project(":kmath-tensors"))
api("org.tensorflow:tensorflow-core-api:0.3.3") api("org.tensorflow:tensorflow-core-api:0.3.3")
testImplementation("org.tensorflow:tensorflow-core-platform:0.3.3")
} }
readme { readme {

View File

@ -5,6 +5,7 @@ import org.tensorflow.Output
import org.tensorflow.ndarray.NdArray import org.tensorflow.ndarray.NdArray
import org.tensorflow.op.core.Constant import org.tensorflow.op.core.Constant
import org.tensorflow.types.TFloat64 import org.tensorflow.types.TFloat64
import space.kscience.kmath.expressions.Symbol
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.nd.DefaultStrides import space.kscience.kmath.nd.DefaultStrides
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.Shape
@ -13,20 +14,22 @@ import space.kscience.kmath.operations.DoubleField
public class DoubleTensorFlowOutput( public class DoubleTensorFlowOutput(
graph: Graph, graph: Graph,
output: Output<TFloat64> output: Output<TFloat64>,
) : TensorFlowOutput<Double, TFloat64>(graph, output) { ) : TensorFlowOutput<Double, TFloat64>(graph, output) {
override fun org.tensorflow.Tensor.actualizeTensor(): NdArray<Double> = output.asTensor()
override fun org.tensorflow.Tensor.actualizeTensor(): NdArray<Double> = this as TFloat64
} }
public class DoubleTensorFlowAlgebra internal constructor( public class DoubleTensorFlowAlgebra internal constructor(
graph: Graph graph: Graph,
) : TensorFlowAlgebra<Double, TFloat64, DoubleField>(graph) { ) : TensorFlowAlgebra<Double, TFloat64, DoubleField>(graph) {
override val elementAlgebra: DoubleField get() = DoubleField override val elementAlgebra: DoubleField get() = DoubleField
override fun structureND( override fun structureND(
shape: Shape, shape: Shape,
initializer: DoubleField.(IntArray) -> Double initializer: DoubleField.(IntArray) -> Double,
): StructureND<Double> { ): StructureND<Double> {
val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array -> val res = TFloat64.tensorOf(org.tensorflow.ndarray.Shape.of(*shape.toLongArray())) { array ->
DefaultStrides(shape).forEach { index -> DefaultStrides(shape).forEach { index ->
@ -53,4 +56,20 @@ public class DoubleTensorFlowAlgebra internal constructor(
override fun Output<TFloat64>.wrap(): TensorFlowOutput<Double, TFloat64> = DoubleTensorFlowOutput(graph, this) override fun Output<TFloat64>.wrap(): TensorFlowOutput<Double, TFloat64> = DoubleTensorFlowOutput(graph, this)
override fun const(value: Double): Constant<TFloat64> = ops.constant(value) override fun const(value: Double): Constant<TFloat64> = ops.constant(value)
}
public fun DoubleField.produceWithTF(
block: DoubleTensorFlowAlgebra.() -> StructureND<Double>,
): StructureND<Double> = Graph().use { graph ->
val scope = DoubleTensorFlowAlgebra(graph)
scope.export(scope.block())
}
public fun DoubleField.produceMapWithTF(
block: DoubleTensorFlowAlgebra.() -> Map<Symbol, StructureND<Double>>,
): Map<Symbol, StructureND<Double>> = Graph().use { graph ->
val scope = DoubleTensorFlowAlgebra(graph)
scope.block().mapValues { scope.export(it.value) }
} }

View File

@ -0,0 +1,21 @@
package space.kscience.kmath.tensorflow
import org.tensorflow.Graph
import org.tensorflow.Output
import org.tensorflow.ndarray.NdArray
import org.tensorflow.types.TInt32
import org.tensorflow.types.TInt64
public class IntTensorFlowOutput(
graph: Graph,
output: Output<TInt32>,
) : TensorFlowOutput<Int, TInt32>(graph, output) {
override fun org.tensorflow.Tensor.actualizeTensor(): NdArray<Int> = this as TInt32
}
public class LongTensorFlowOutput(
graph: Graph,
output: Output<TInt64>,
) : TensorFlowOutput<Long, TInt64>(graph, output) {
override fun org.tensorflow.Tensor.actualizeTensor(): NdArray<Long> = this as TInt64
}

View File

@ -8,8 +8,13 @@ import org.tensorflow.Session
import org.tensorflow.ndarray.NdArray import org.tensorflow.ndarray.NdArray
import org.tensorflow.op.Ops import org.tensorflow.op.Ops
import org.tensorflow.op.core.Constant import org.tensorflow.op.core.Constant
import org.tensorflow.op.core.Max
import org.tensorflow.op.core.Min
import org.tensorflow.op.core.Sum
import org.tensorflow.types.TInt32
import org.tensorflow.types.family.TType import org.tensorflow.types.family.TType
import space.kscience.kmath.misc.PerformancePitfall import space.kscience.kmath.misc.PerformancePitfall
import space.kscience.kmath.misc.UnstableKMathAPI
import space.kscience.kmath.nd.Shape import space.kscience.kmath.nd.Shape
import space.kscience.kmath.nd.StructureND import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.operations.Ring import space.kscience.kmath.operations.Ring
@ -38,8 +43,8 @@ public value class TensorFlowArray<T>(public val tensor: NdArray<T>) : Tensor<T>
} }
public abstract class TensorFlowOutput<T, TT : TType>( public abstract class TensorFlowOutput<T, TT : TType>(
private val graph: Graph, protected val graph: Graph,
output: Output<TT> output: Output<TT>,
) : TensorFlowTensor<T> { ) : TensorFlowTensor<T> {
public var output: Output<TT> = output public var output: Output<TT> = output
@ -49,9 +54,10 @@ public abstract class TensorFlowOutput<T, TT : TType>(
protected abstract fun org.tensorflow.Tensor.actualizeTensor(): NdArray<T> protected abstract fun org.tensorflow.Tensor.actualizeTensor(): NdArray<T>
private val actualTensor by lazy { internal val actualTensor by lazy {
val session = Session(graph) Session(graph).use { session ->
TensorFlowArray(session.runner().fetch(output).run().first().actualizeTensor()) TensorFlowArray(session.runner().fetch(output).run().first().actualizeTensor())
}
} }
override fun get(index: IntArray): T = actualTensor[index] override fun get(index: IntArray): T = actualTensor[index]
@ -66,9 +72,9 @@ public abstract class TensorFlowOutput<T, TT : TType>(
} }
public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal constructor( public abstract class TensorFlowAlgebra<T, TT : TType, A : Ring<T>> internal constructor(
protected val graph: Graph protected val graph: Graph,
) : TensorAlgebra<T,A> { ) : TensorAlgebra<T, A> {
protected val ops: Ops by lazy { Ops.create(graph) } protected val ops: Ops by lazy { Ops.create(graph) }
@ -83,7 +89,7 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
private inline fun StructureND<T>.biOp( private inline fun StructureND<T>.biOp(
other: StructureND<T>, other: StructureND<T>,
operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT> operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT>,
): TensorFlowOutput<T, TT> { ): TensorFlowOutput<T, TT> {
val left = asTensorFlow().output val left = asTensorFlow().output
val right = other.asTensorFlow().output val right = other.asTensorFlow().output
@ -92,7 +98,7 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
private inline fun T.biOp( private inline fun T.biOp(
other: StructureND<T>, other: StructureND<T>,
operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT> operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT>,
): TensorFlowOutput<T, TT> { ): TensorFlowOutput<T, TT> {
val left = const(this) val left = const(this)
val right = other.asTensorFlow().output val right = other.asTensorFlow().output
@ -101,7 +107,7 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
private inline fun StructureND<T>.biOp( private inline fun StructureND<T>.biOp(
value: T, value: T,
operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT> operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT>,
): TensorFlowOutput<T, TT> { ): TensorFlowOutput<T, TT> {
val left = asTensorFlow().output val left = asTensorFlow().output
val right = const(value) val right = const(value)
@ -110,7 +116,7 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
private inline fun Tensor<T>.inPlaceOp( private inline fun Tensor<T>.inPlaceOp(
other: StructureND<T>, other: StructureND<T>,
operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT> operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT>,
): Unit { ): Unit {
val origin = asTensorFlow() val origin = asTensorFlow()
val left = origin.output val left = origin.output
@ -120,7 +126,7 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
private inline fun Tensor<T>.inPlaceOp( private inline fun Tensor<T>.inPlaceOp(
value: T, value: T,
operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT> operation: (left: Operand<TT>, right: Operand<TT>) -> Operand<TT>,
): Unit { ): Unit {
val origin = asTensorFlow() val origin = asTensorFlow()
val left = origin.output val left = origin.output
@ -128,8 +134,8 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
origin.output = operation(left, right).asOutput() origin.output = operation(left, right).asOutput()
} }
private inline fun unOp(value: StructureND<T>, operation: (Operand<TT>) -> Operand<TT>): TensorFlowOutput<T, TT> = private inline fun StructureND<T>.unOp(operation: (Operand<TT>) -> Operand<TT>): TensorFlowOutput<T, TT> =
operation(value.asTensorFlow().output).asOutput().wrap() operation(asTensorFlow().output).asOutput().wrap()
override fun T.plus(arg: StructureND<T>): TensorFlowOutput<T, TT> = biOp(arg, ops.math::add) override fun T.plus(arg: StructureND<T>): TensorFlowOutput<T, TT> = biOp(arg, ops.math::add)
@ -149,67 +155,79 @@ public abstract class TensorFlowAlgebra<T, TT : TType, A: Ring<T>> internal cons
override fun Tensor<T>.minusAssign(value: T): Unit = inPlaceOp(value, ops.math::sub) override fun Tensor<T>.minusAssign(value: T): Unit = inPlaceOp(value, ops.math::sub)
override fun Tensor<T>.minusAssign(other: StructureND<T>): Unit = inPlaceOp(other, ops.math::sub) override fun Tensor<T>.minusAssign(arg: StructureND<T>): Unit = inPlaceOp(arg, ops.math::sub)
override fun T.times(arg: StructureND<T>): TensorFlowOutput<T, TT> = biOp(arg, ops.math::mul) override fun T.times(arg: StructureND<T>): TensorFlowOutput<T, TT> = biOp(arg, ops.math::mul)
override fun StructureND<T>.times(arg: T): TensorFlowOutput<T, TT> = biOp(arg, ops.math::mul) override fun StructureND<T>.times(arg: T): TensorFlowOutput<T, TT> = biOp(arg, ops.math::mul)
override fun StructureND<T>.times(other: StructureND<T>): TensorFlowOutput<T, TT> = biOp(other, ops.math::mul) override fun StructureND<T>.times(arg: StructureND<T>): TensorFlowOutput<T, TT> = biOp(arg, ops.math::mul)
override fun Tensor<T>.timesAssign(value: T): Unit = inPlaceOp(value, ops.math::mul) override fun Tensor<T>.timesAssign(value: T): Unit = inPlaceOp(value, ops.math::mul)
override fun Tensor<T>.timesAssign(arg: StructureND<T>): Unit = inPlaceOp(arg, ops.math::mul) override fun Tensor<T>.timesAssign(arg: StructureND<T>): Unit = inPlaceOp(arg, ops.math::mul)
override fun StructureND<T>.unaryMinus(): TensorFlowOutput<T, TT> = unOp(this, ops.math::neg) override fun StructureND<T>.unaryMinus(): TensorFlowOutput<T, TT> = unOp(ops.math::neg)
override fun StructureND<T>.get(i: Int): Tensor<T> { override fun Tensor<T>.get(i: Int): Tensor<T> = unOp {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override fun StructureND<T>.transpose(i: Int, j: Int): Tensor<T> = unOp(this) { override fun Tensor<T>.transpose(i: Int, j: Int): Tensor<T> = unOp {
ops.linalg.transpose(it, ops.constant(intArrayOf(i, j))) ops.linalg.transpose(it, ops.constant(intArrayOf(i, j)))
} }
override fun Tensor<T>.view(shape: IntArray): Tensor<T> { override fun Tensor<T>.view(shape: IntArray): Tensor<T> = unOp {
TODO("Not yet implemented") ops.reshape(it, ops.constant(shape))
} }
override fun Tensor<T>.viewAs(other: StructureND<T>): Tensor<T> { override fun Tensor<T>.viewAs(other: StructureND<T>): Tensor<T> = biOp(other) { l, r ->
TODO("Not yet implemented") ops.reshape(l, ops.shape(r))
} }
override fun StructureND<T>.dot(other: StructureND<T>): TensorFlowOutput<T, TT> = biOp(other) { l, r -> override fun StructureND<T>.dot(other: StructureND<T>): TensorFlowOutput<T, TT> = biOp(other) { l, r ->
ops.linalg.matMul(l, r) ops.linalg.matMul(l, r)
} }
override fun diagonalEmbedding(diagonalEntries: Tensor<T>, offset: Int, dim1: Int, dim2: Int): Tensor<T> = ops.run { override fun diagonalEmbedding(
TODO("Not yet implemented") diagonalEntries: Tensor<T>,
offset: Int,
dim1: Int,
dim2: Int,
): TensorFlowOutput<T, TT> = diagonalEntries.unOp {
TODO()
} }
override fun StructureND<T>.sum(): T = TODO("Not yet implemented") override fun StructureND<T>.sum(): T = unOp {
ops.sum(it, ops.constant(intArrayOf()))
}.value()
override fun StructureND<T>.sum(dim: Int, keepDim: Boolean): Tensor<T> { override fun StructureND<T>.sum(dim: Int, keepDim: Boolean): TensorFlowOutput<T, TT> = unOp {
TODO("Not yet implemented") ops.sum(it, ops.constant(dim), Sum.keepDims(keepDim))
} }
override fun StructureND<T>.min(): T { override fun StructureND<T>.min(): T = unOp {
TODO("Not yet implemented") ops.min(it, ops.constant(intArrayOf()))
}.value()
override fun StructureND<T>.min(dim: Int, keepDim: Boolean): Tensor<T> = unOp {
ops.min(it, ops.constant(dim), Min.keepDims(keepDim))
} }
override fun StructureND<T>.min(dim: Int, keepDim: Boolean): Tensor<T> { override fun StructureND<T>.max(): T = unOp {
TODO("Not yet implemented") ops.max(it, ops.constant(intArrayOf()))
}.value()
override fun StructureND<T>.max(dim: Int, keepDim: Boolean): Tensor<T> = unOp {
ops.max(it, ops.constant(dim), Max.keepDims(keepDim))
} }
override fun StructureND<T>.max(): T { override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<Int> = IntTensorFlowOutput(
TODO("Not yet implemented") graph,
} ops.math.argMax(asTensorFlow().output, ops.constant(dim), TInt32::class.java).output()
).actualTensor
override fun StructureND<T>.max(dim: Int, keepDim: Boolean): Tensor<T> { @OptIn(UnstableKMathAPI::class)
TODO("Not yet implemented") override fun export(arg: StructureND<T>): StructureND<T> =
} if (arg is TensorFlowOutput<T, *>) arg.actualTensor else arg
override fun StructureND<T>.argMax(dim: Int, keepDim: Boolean): Tensor<T> {
TODO("Not yet implemented")
}
} }

View File

@ -0,0 +1,19 @@
package space.kscience.kmath.tensorflow
import org.junit.jupiter.api.Test
import space.kscience.kmath.nd.StructureND
import space.kscience.kmath.nd.structureND
import space.kscience.kmath.operations.DoubleField
class DoubleTensorFlowOps {
@Test
fun basicOps() {
val res = DoubleField.produceWithTF {
val initial = structureND(2, 2) { 1.0 }
initial + (initial * 2.0)
}
println(StructureND.toString(res))
}
}

View File

@ -3,6 +3,8 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/ */
@file:OptIn(PerformancePitfall::class)
package space.kscience.kmath.viktor package space.kscience.kmath.viktor
import org.jetbrains.bio.viktor.F64Array import org.jetbrains.bio.viktor.F64Array