Skip to content

Commit

Permalink
add more test
Browse files Browse the repository at this point in the history
  • Loading branch information
yiheng committed Dec 6, 2017
1 parent cc5e338 commit 62cc9cb
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 14 deletions.
Expand Up @@ -55,31 +55,38 @@ class SpatialSeperableConvolution[T: ClassTag](
val dataFormat: DataFormat,
var wRegularizer: Regularizer[T] = null,
var bRegularizer: Regularizer[T] = null,
var pRegularizer: Regularizer[T] = null
var pRegularizer: Regularizer[T] = null,
val initDepthWeight: Tensor[T] = null,
val initPointWeight: Tensor[T] = null,
val initBias: Tensor[T] = null
)(implicit ev: TensorNumeric[T])
extends AbstractModule[Tensor[T], Tensor[T], T]{

private val internalChannel = nInputChannel * depthMultiplier

private val channelDim = if (dataFormat == DataFormat.NCHW) 2 else 4

private val depthWeight = if (dataFormat == DataFormat.NCHW) {
private val depthWeight = if (initDepthWeight != null) {
initDepthWeight
} else if (dataFormat == DataFormat.NCHW) {
Tensor[T](depthMultiplier, nInputChannel, kW, kH)
} else {
Tensor[T](kW, kH, nInputChannel, depthMultiplier)
Tensor[T](kW, kH, nInputChannel, depthMultiplier)
}

private val depthGradWeight = Tensor[T].resizeAs(depthWeight)

private val pointWeight = if (dataFormat == DataFormat.NCHW) {
private val pointWeight = if (initPointWeight != null) {
initPointWeight
} else if (dataFormat == DataFormat.NCHW) {
Tensor[T](nOutputChannel, internalChannel, 1, 1)
} else {
Tensor[T](1, 1, internalChannel, nOutputChannel)
}

private val pointGradWeight = Tensor[T].resizeAs(pointWeight)

private val bias = Tensor[T](nOutputChannel)
private val bias = if (initBias != null) initBias else Tensor[T](nOutputChannel)

private val gradBias = Tensor[T].resizeAs(bias)

Expand Down Expand Up @@ -117,6 +124,8 @@ class SpatialSeperableConvolution[T: ClassTag](
initGradBias = gradBias
)

reset()

override def parameters(): (Array[Tensor[T]], Array[Tensor[T]]) = {
(Array(depthWeight, pointWeight, bias), Array(depthGradWeight, pointGradWeight, gradBias))
}
Expand All @@ -125,7 +134,8 @@ class SpatialSeperableConvolution[T: ClassTag](
require(input.nDimension() == 4, "SpatialSeperableConvolution only accept 4D input")
require(input.isContiguous(), "SpatialSeperableConvolution require contiguous input")
require(nInputChannel == input.size(channelDim),
"input tensor channel dimension size doesn't match layer nInputChannel")
s"input tensor channel dimension size(${input.size(channelDim)}) doesn't " +
s"match layer nInputChannel $nInputChannel")

SpatialSeperableConvolution.copyWeight(depthConv.weight, input.size(channelDim),
depthMultiplier, depthWeight, dataFormat)
Expand Down Expand Up @@ -186,31 +196,40 @@ class SpatialSeperableConvolution[T: ClassTag](
depthConv.gradWeight, depthGradWeight, dataFormat)
}


override def reset(): Unit = {
if (initDepthWeight == null) depthWeight.rand()
if (initPointWeight == null) pointWeight.rand()
if (initBias == null) bias.zero()
zeroGradParameters()
}
}

object SpatialSeperableConvolution {
def apply[T: ClassTag](nInputChannel: Int, nOutputChannel: Int, depthMultiplier: Int,
kW: Int, kH: Int, sW: Int = 1, sH: Int = 1, pW: Int = 0, pH: Int = 0,
hasBias: Boolean = true, dataFormat: DataFormat = DataFormat.NCHW,
wRegularizer: Regularizer[T] = null, bRegularizer: Regularizer[T] = null,
pRegularizer: Regularizer[T] = null)(implicit ev: TensorNumeric[T])
: SpatialSeperableConvolution[T] = new SpatialSeperableConvolution(nInputChannel,
pRegularizer: Regularizer[T] = null, initDepthWeight: Tensor[T] = null,
initPointWeight: Tensor[T] = null, initBias: Tensor[T] = null
)(implicit ev: TensorNumeric[T])
: SpatialSeperableConvolution[T] = new SpatialSeperableConvolution[T](nInputChannel,
nOutputChannel, depthMultiplier, kW, kH, sW, sH, pW, pH, hasBias, dataFormat, wRegularizer,
bRegularizer)
bRegularizer, pRegularizer, initDepthWeight, initPointWeight, initBias)

private[bigdl] def copyWeight[T](weight: Tensor[T], nInputChannel: Int,
depthMultiplier: Int, sourceWeight: Tensor[T], dataFormat: DataFormat): Unit = {
val kInputDim = if (dataFormat == DataFormat.NHWC) 3 else 2
val kOutputDim = if (dataFormat == DataFormat.NHWC) 4 else 1
val delta = if (dataFormat == DataFormat.NHWC) 0 else 1
weight.zero()
var in = 0
while(in < nInputChannel) {
var out = 0
while(out < depthMultiplier) {
// weight is a 5D tensor with a group dimension
weight.select(kInputDim + 1, in + 1).select(kOutputDim, in * depthMultiplier + out + 1)
.copy(sourceWeight.select(kInputDim, in + 1).select(kOutputDim - 1, out + 1))
weight.select(kInputDim + 1, in + 1)
.select(kOutputDim + delta, in * depthMultiplier + out + 1)
.copy(sourceWeight.select(kInputDim, in + 1).select(kOutputDim - 1 + delta, out + 1))
out += 1
}
in += 1
Expand All @@ -223,12 +242,13 @@ object SpatialSeperableConvolution {
): Unit = {
val kInputDim = if (dataFormat == DataFormat.NHWC) 3 else 2
val kOutputDim = if (dataFormat == DataFormat.NHWC) 4 else 1
val delta = if (dataFormat == DataFormat.NHWC) 0 else 1
var in = 0
while(in < nInputChannel) {
var out = 0
while(out < depthMultiplier) {
targetGrad.select(kInputDim, in + 1).select(kOutputDim - 1, out + 1)
.copy(sourceGrad.select(kInputDim + 1, in + 1).select(kOutputDim,
targetGrad.select(kInputDim, in + 1).select(kOutputDim - 1 + delta, out + 1)
.copy(sourceGrad.select(kInputDim + 1, in + 1).select(kOutputDim + delta,
in * depthMultiplier + out + 1))
out += 1
}
Expand Down
@@ -0,0 +1,90 @@
/*
* Copyright 2016 The BigDL Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intel.analytics.bigdl.nn

import com.intel.analytics.bigdl.nn.abstractnn.DataFormat
import com.intel.analytics.bigdl.tensor.Tensor
import org.scalatest.{FlatSpec, Matchers}

class SpatialSeperableConvolutionSpec extends FlatSpec with Matchers {
"SpatialSeperableConvolution NHWC and NCHW" should "have same output" in {
val depthWeightNHWC = Tensor[Float](2, 2, 3, 1).rand()
val depthWeightNCHW = depthWeightNHWC.transpose(1, 4).transpose(2, 4).transpose(2, 3)
.contiguous()
val pointWeightNHWC = Tensor[Float](1, 1, 3, 6).rand()
val pointWeightNCHW = pointWeightNHWC.transpose(1, 4).transpose(2, 4).transpose(2, 3)
.contiguous()
val convNHWC = SpatialSeperableConvolution[Float](3, 6, 1, 2, 2, dataFormat = DataFormat.NHWC,
initDepthWeight = depthWeightNHWC, initPointWeight = pointWeightNHWC)
val convNCHW = SpatialSeperableConvolution[Float](3, 6, 1, 2, 2, dataFormat = DataFormat.NCHW,
initDepthWeight = depthWeightNCHW, initPointWeight = pointWeightNCHW)
val inputNHWC = Tensor[Float](2, 24, 24, 3).rand()
val inputNCHW = inputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
val outputNHWC = convNHWC.forward(inputNHWC)
val outputNCHW = convNCHW.forward(inputNCHW)
val convert = outputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
convert.almostEqual(outputNCHW, 1e-5) should be(true)
val gradOutputNHWC = Tensor[Float](2, 23, 23, 6).rand()
val gradOutputNCHW = gradOutputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
val gradInputNHWC = convNHWC.backward(inputNHWC, gradOutputNHWC)
val gradInputNCHW = convNCHW.backward(inputNCHW, gradOutputNCHW)
val convertGradInput = gradInputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
convertGradInput.almostEqual(gradInputNCHW, 1e-5) should be(true)

convNHWC.parameters()._2.zip(convNCHW.parameters()._2).map { case(p1, p2) =>
if (p1.nDimension() == 4) {
val convert = p2.transpose(1, 4).transpose(1, 3).transpose(2, 3)
p1.almostEqual(convert, 1e-3) should be(true)
} else {
p1.almostEqual(p2, 1e-3) should be(true)
}
}
}

"SpatialSeperableConvolution NHWC and NCHW" should "have same output when depth mul is 2" in {
val depthWeightNHWC = Tensor[Float](2, 2, 3, 2).rand()
val depthWeightNCHW = depthWeightNHWC.transpose(1, 4).transpose(2, 4).transpose(2, 3)
.contiguous()
val pointWeightNHWC = Tensor[Float](1, 1, 6, 6).rand()
val pointWeightNCHW = pointWeightNHWC.transpose(1, 4).transpose(2, 4).transpose(2, 3)
.contiguous()
val convNHWC = SpatialSeperableConvolution[Float](3, 6, 2, 2, 2, dataFormat = DataFormat.NHWC,
initDepthWeight = depthWeightNHWC, initPointWeight = pointWeightNHWC)
val convNCHW = SpatialSeperableConvolution[Float](3, 6, 2, 2, 2, dataFormat = DataFormat.NCHW,
initDepthWeight = depthWeightNCHW, initPointWeight = pointWeightNCHW)
val inputNHWC = Tensor[Float](2, 24, 24, 3).rand()
val inputNCHW = inputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
val outputNHWC = convNHWC.forward(inputNHWC)
val outputNCHW = convNCHW.forward(inputNCHW)
val convert = outputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
convert.almostEqual(outputNCHW, 1e-5) should be(true)
val gradOutputNHWC = Tensor[Float](2, 23, 23, 6).rand()
val gradOutputNCHW = gradOutputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
val gradInputNHWC = convNHWC.backward(inputNHWC, gradOutputNHWC)
val gradInputNCHW = convNCHW.backward(inputNCHW, gradOutputNCHW)
val convertGradInput = gradInputNHWC.transpose(2, 4).transpose(3, 4).contiguous()
convertGradInput.almostEqual(gradInputNCHW, 1e-5) should be(true)

convNHWC.parameters()._2.zip(convNCHW.parameters()._2).map { case(p1, p2) =>
if (p1.nDimension() == 4) {
val convert = p2.transpose(1, 4).transpose(1, 3).transpose(2, 3)
p1.almostEqual(convert, 1e-3) should be(true)
} else {
p1.almostEqual(p2, 1e-3) should be(true)
}
}
}
}

0 comments on commit 62cc9cb

Please sign in to comment.