Skip to content
This repository was archived by the owner on Jul 1, 2023. It is now read-only.

Add flatten & reshape layers #32

Merged
merged 8 commits into from
Feb 28, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions Sources/DeepLearning/Layer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -605,10 +605,36 @@ public struct UpSampling2D<Scalar: TensorFlowFloatingPoint>: Layer {
public func applied(to input: Tensor<Scalar>, in _: Context) -> Tensor<Scalar> {
let shape = input.shape
let (batchSize, height, width, channels) = (shape[0], shape[1], shape[2], shape[3])
let reshapeSize = Tensor<Int32>([batchSize, height, 1, width, 1, channels])
let scaleOnes = Tensor<Scalar>(ones: [1, 1, size, 1, size, 1])
let upSampling = input.reshaped(toShape: reshapeSize) * scaleOnes
let upSampledShape = Tensor<Int32>([batchSize, height * size, width * size, channels])
return upSampling.reshaped(toShape: upSampledShape)
let upSampling = input.reshaped(to: [batchSize, height, 1, width, 1, channels]) * scaleOnes
return upSampling.reshaped(to: [batchSize, height * size, width * size, channels])
}
}

@_fixed_layout
public struct Flatten<Scalar: TensorFlowFloatingPoint>: Layer {
@differentiable
public func applied(to input: Tensor<Scalar>, in _: Context) -> Tensor<Scalar> {
let batchSize = input.shape[0]
Copy link
Contributor

@rxwei rxwei Feb 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #22848 changed the behavior of differentiation through non-differentiable types, so it may break this function (requiring a .withoutDerivative() call somewhere). Holding off until the next toolchain release (Monday) will be a good idea.

Also, a more efficient implementation would be to use .shapeTensor whenever you need to get the shape from a tensor, and not use any values of TensorShape type in the function body. On line 510, newShape can be defined using Tensor's concatenation operator, Tensor.concatenated(with:) or ++. (BTW, concatenation ops do not have a derivative defined yet, would you like to add one?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New toolchain released!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My recommendation would be:

public func applied(to input: Tensor<Scalar>, in _: Context) -> Tensor<Scalar> {
    let batchSize = input.shape[0]
    let remaining = input.shape[1..<input.rank].contiguousSize
    return input.reshaped(to: [batchSize, remaining])
}

But I see also @rxwei's point about preferring purely TF-side shape manipulation.

If we don't switch to that completely, though, we should replace reshaped(toShape: Tensor<Int32>(foo)) with reshaped(to: foo) (assuming that actually works now).

Copy link
Contributor

@rxwei rxwei Feb 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following is the host-free version.

public func applied(to input: Tensor<Scalar>, in _: Context) -> Tensor<Scalar> {
    let batchSize = input.shapeTensor[0]
    let remaining = input.shapeTensor[1..<input.rank].product()
    return input.reshaped(toShape: Tensor([batchSize, remaining]))
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The host-free version is currently an order of magnitude slower on CPU than the hosty version (about 30us for mine and 300us for yours).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh that's terrible. Ok.

let remaining = input.shape[1..<input.rank].contiguousSize
return input.reshaped(to: [batchSize, remaining])
}
}

@_fixed_layout
public struct Reshape<Scalar: TensorFlowFloatingPoint>: Layer {
@noDerivative public let shape: Tensor<Int32>

public init(shape: Tensor<Int32>) {
self.shape = shape
}

public init(_ shape: TensorShape) {
self.init(shape: Tensor(shape.dimensions))
}

@differentiable
public func applied(to input: Tensor<Scalar>, in _: Context) -> Tensor<Scalar> {
return input.reshaped(toShape: shape)
}
}