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

Commit 2d06188

Browse files
Shashi456Eugene Burmako
authored andcommitted
Adding conv transpose 1d & 3d (#174)
* adding conv transpose 3d * updating to callAsFunction * Updating to refactored conv3dbackpropinput' * adding conv 1d transpose * Updating seed to int32 and some errors * Adding tests for transposed conv * Adding tests to var all tests * removing transposed conv2d test * Minor test mistake * Fixing tests and layers * Indentation and spacing errors * Update LayerTests.swift * Add property doc
1 parent b5a49b7 commit 2d06188

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

Sources/TensorFlow/Layers/Convolutional.swift

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,102 @@ public extension Conv3D {
350350
}
351351
}
352352

353+
/// A 1-D transposed convolution layer (e.g. temporal transposed convolution over images).
354+
///
355+
/// This layer creates a convolution filter that is transpose-convolved with the layer input
356+
/// to produce a tensor of outputs.
357+
@frozen
358+
public struct TransposedConv1D<Scalar: TensorFlowFloatingPoint>: Layer {
359+
/// The 1-D convolution kernel.
360+
public var filter: Tensor<Scalar>
361+
/// The bias vector.
362+
public var bias: Tensor<Scalar>
363+
/// The element-wise activation function.
364+
@noDerivative public let activation: Activation
365+
/// The strides of the sliding window for spatial dimensions.
366+
@noDerivative public let stride: Int
367+
/// The padding algorithm for convolution.
368+
@noDerivative public let padding: Padding
369+
/// The paddingIndex property allows us to handle computation based on padding.
370+
@noDerivative public let paddingIndex: Int
371+
372+
/// The element-wise activation function type.
373+
public typealias Activation = @differentiable (Tensor<Scalar>) -> Tensor<Scalar>
374+
375+
/// Creates a `TransposedConv1D` layer with the specified filter, bias,
376+
/// activation function, strides, and padding.
377+
///
378+
/// - Parameters:
379+
/// - filter: The 3-D convolution kernel.
380+
/// - bias: The bias vector.
381+
/// - activation: The element-wise activation function.
382+
/// - strides: The strides of the sliding window for spatial dimensions.
383+
/// - padding: The padding algorithm for convolution.
384+
public init(
385+
filter: Tensor<Scalar>,
386+
bias: Tensor<Scalar>,
387+
activation: @escaping Activation = identity,
388+
stride: Int = 1,
389+
padding: Padding = .valid
390+
) {
391+
self.filter = filter
392+
self.bias = bias
393+
self.activation = activation
394+
self.stride = stride
395+
self.padding = padding
396+
self.paddingIndex = padding == .same ? 0 : 1
397+
}
398+
399+
/// Returns the output obtained from applying the layer to the given input.
400+
///
401+
/// - Parameter input: The input to the layer.
402+
/// - Returns: The output.
403+
@differentiable
404+
public func callAsFunction(_ input: Tensor<Scalar>) -> Tensor<Scalar> {
405+
let batchSize = input.shape[0]
406+
let w = (input.shape[1] - (1 * paddingIndex)) *
407+
stride + (filter.shape[0] * paddingIndex)
408+
let c = filter.shape[2]
409+
let newShape = Tensor<Int32>([Int32(batchSize), 1, Int32(w), Int32(c)])
410+
return activation(conv2DBackpropInput(
411+
input.expandingShape(at: 1),
412+
shape: newShape,
413+
filter: filter.expandingShape(at: 0),
414+
strides: (1, 1, stride, 1),
415+
padding: padding) + bias)
416+
}
417+
}
418+
419+
public extension TransposedConv1D {
420+
/// Creates a `TransposedConv1D` layer with the specified filter shape, strides, padding, and
421+
/// element-wise activation function. The filter tensor is initialized using Glorot uniform
422+
/// initialization with the specified generator. The bias vector is initialized with zeros.
423+
///
424+
/// - Parameters:
425+
/// - filterShape: The shape of the 3-D convolution kernel.
426+
/// - strides: The strides of the sliding window for spatial dimensions.
427+
/// - padding: The padding algorithm for convolution.
428+
/// - activation: The element-wise activation function.
429+
/// - generator: The random number generator for initialization.
430+
init(
431+
filterShape: (Int, Int, Int),
432+
stride: Int = 1,
433+
padding: Padding = .valid,
434+
activation: @escaping Activation = identity,
435+
filterInitializer: ParameterInitializer<Scalar> = glorotUniform(),
436+
biasInitializer: ParameterInitializer<Scalar> = zeros()
437+
) {
438+
let filterTensorShape = TensorShape([
439+
filterShape.0, filterShape.1, filterShape.2])
440+
self.init(
441+
filter: filterInitializer(filterTensorShape),
442+
bias: biasInitializer([filterShape.2]),
443+
activation: activation,
444+
stride: stride,
445+
padding: padding)
446+
}
447+
}
448+
353449
/// A 2-D transposed convolution layer (e.g. spatial transposed convolution over images).
354450
///
355451
/// This layer creates a convolution filter that is transpose-convolved with the layer input
@@ -449,6 +545,107 @@ public extension TransposedConv2D {
449545
}
450546
}
451547

548+
549+
/// A 3-D transposed convolution layer (e.g. spatial transposed convolution over images).
550+
///
551+
/// This layer creates a convolution filter that is transpose-convolved with the layer input
552+
/// to produce a tensor of outputs.
553+
@frozen
554+
public struct TransposedConv3D<Scalar: TensorFlowFloatingPoint>: Layer {
555+
/// The 5-D convolution kernel.
556+
public var filter: Tensor<Scalar>
557+
/// The bias vector.
558+
public var bias: Tensor<Scalar>
559+
/// The element-wise activation function.
560+
@noDerivative public let activation: Activation
561+
/// The strides of the sliding window for spatial dimensions.
562+
@noDerivative public let strides: (Int, Int, Int)
563+
/// The padding algorithm for convolution.
564+
@noDerivative public let padding: Padding
565+
/// The paddingIndex property allows us to handle computation based on padding.
566+
@noDerivative public let paddingIndex: Int
567+
568+
/// The element-wise activation function type.
569+
public typealias Activation = @differentiable (Tensor<Scalar>) -> Tensor<Scalar>
570+
571+
/// Creates a `TransposedConv3D` layer with the specified filter, bias,
572+
/// activation function, strides, and padding.
573+
///
574+
/// - Parameters:
575+
/// - filter: The 5-D convolution kernel.
576+
/// - bias: The bias vector.
577+
/// - activation: The element-wise activation function.
578+
/// - strides: The strides of the sliding window for spatial dimensions.
579+
/// - padding: The padding algorithm for convolution.
580+
public init(
581+
filter: Tensor<Scalar>,
582+
bias: Tensor<Scalar>,
583+
activation: @escaping Activation = identity,
584+
strides: (Int, Int, Int) = (1, 1, 1),
585+
padding: Padding = .valid
586+
) {
587+
self.filter = filter
588+
self.bias = bias
589+
self.activation = activation
590+
self.strides = strides
591+
self.padding = padding
592+
self.paddingIndex = padding == .same ? 0 : 1
593+
}
594+
595+
/// Returns the output obtained from applying the layer to the given input.
596+
///
597+
/// - Parameter input: The input to the layer.
598+
/// - Returns: The output.
599+
@differentiable
600+
public func callAsFunction(_ input: Tensor<Scalar>) -> Tensor<Scalar> {
601+
let batchSize = input.shape[0]
602+
let w = (input.shape[1] - (1 * paddingIndex)) *
603+
strides.0 + (filter.shape[0] * paddingIndex)
604+
let h = (input.shape[2] - (1 * paddingIndex)) *
605+
strides.1 + (filter.shape[1] * paddingIndex)
606+
let d = (input.shape[3] - (1 * paddingIndex)) *
607+
strides.2 + (filter.shape[2] * paddingIndex)
608+
let c = filter.shape[3]
609+
let newShape = Tensor<Int32>([Int32(batchSize), Int32(w), Int32(h), Int32(d), Int32(c)])
610+
return activation(conv3DBackpropInput(
611+
input,
612+
shape: newShape,
613+
filter: filter,
614+
strides: (1, strides.0, strides.1, strides.2, 1),
615+
padding: padding) + bias)
616+
}
617+
}
618+
619+
public extension TransposedConv3D {
620+
/// Creates a `TransposedConv3D` layer with the specified filter shape, strides, padding, and
621+
/// element-wise activation function. The filter tensor is initialized using Glorot uniform
622+
/// initialization with the specified generator. The bias vector is initialized with zeros.
623+
///
624+
/// - Parameters:
625+
/// - filterShape: The shape of the 5-D convolution kernel.
626+
/// - strides: The strides of the sliding window for spatial dimensions.
627+
/// - padding: The padding algorithm for convolution.
628+
/// - activation: The element-wise activation function.
629+
/// - generator: The random number generator for initialization.
630+
init(
631+
filterShape: (Int, Int, Int, Int, Int),
632+
strides: (Int, Int, Int) = (1, 1, 1),
633+
padding: Padding = .valid,
634+
activation: @escaping Activation = identity,
635+
filterInitializer: ParameterInitializer<Scalar> = glorotUniform(),
636+
biasInitializer: ParameterInitializer<Scalar> = zeros()
637+
) {
638+
let filterTensorShape = TensorShape([
639+
filterShape.0, filterShape.1, filterShape.2, filterShape.3, filterShape.4])
640+
self.init(
641+
filter: filterInitializer(filterTensorShape),
642+
bias: biasInitializer([filterShape.4]),
643+
activation: activation,
644+
strides: strides,
645+
padding: padding)
646+
}
647+
}
648+
452649
/// A 2-D depthwise convolution layer.
453650
///
454651
/// This layer creates seperable convolution filters that are convolved with the layer input to produce a

Tests/TensorFlowTests/LayerTests.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,18 @@ final class LayerTests: XCTestCase {
302302
XCTAssertEqual(grads.1.bias, [4, 4, 4, 4])
303303
}
304304

305+
func testTransposedConv1D() {
306+
let filter = Tensor(shape: [4, 1, 1], scalars: (0..<4).map(Float.init))
307+
let bias = Tensor<Float>([8])
308+
let layer = TransposedConv1D(filter: filter, bias: bias, activation: identity,
309+
stride: 1, padding: .same)
310+
let input = Tensor(shape: [1, 4, 1], scalars: (0..<4).map(Float.init))
311+
let output = layer.inferring(from: input)
312+
let expected = Tensor<Float>(shape: [1, 1, 4, 1],
313+
scalars: [8, 9, 12, 18])
314+
XCTAssertEqual(output, expected)
315+
}
316+
305317
func testTransposedConv2D() {
306318
let filter = Tensor(shape: [4, 2, 1, 1], scalars: (0..<8).map(Float.init))
307319
let bias = Tensor<Float>([8])
@@ -314,6 +326,19 @@ final class LayerTests: XCTestCase {
314326
XCTAssertEqual(output, expected)
315327
}
316328

329+
330+
func testTransposedConv3D() {
331+
let filter = Tensor(shape: [2, 2, 2, 1, 1], scalars: (0..<8).map(Float.init))
332+
let bias = Tensor<Float>([8])
333+
let layer = TransposedConv3D(filter: filter, bias: bias, activation: identity,
334+
strides: (1, 1, 1), padding: .same)
335+
let input = Tensor(shape: [1, 2, 2, 2, 1], scalars: (0..<8).map(Float.init))
336+
let output = layer.inferring(from: input)
337+
let expected = Tensor<Float>(shape: [1, 2, 2, 2, 1],
338+
scalars: [8, 8, 8, 12, 8, 16, 24, 64])
339+
XCTAssertEqual(output, expected)
340+
}
341+
317342
func testSeparableConv1D() {
318343
let depthwiseFilter = Tensor(shape: [2, 2, 2], scalars: (0..<8).map(Float.init))
319344
let pointwiseFilter = Tensor(shape: [1, 4, 1], scalars: (0..<4).map(Float.init))
@@ -1334,7 +1359,9 @@ final class LayerTests: XCTestCase {
13341359
("testConv2DDilation", testConv2DDilation),
13351360
("testConv3D", testConv3D),
13361361
("testConv3DGradient", testConv3DGradient),
1362+
("testTransposedConv1D", testTransposedConv1D),
13371363
("testTransposedConv2D", testTransposedConv2D),
1364+
("testTransposedConv3D", testTransposedConv3D),
13381365
("testDepthwiseConv2D", testDepthwiseConv2D),
13391366
("testDepthwiseConv2DGradient", testDepthwiseConv2DGradient),
13401367
("testSeparableConv1D", testSeparableConv1D),

0 commit comments

Comments
 (0)