-
Notifications
You must be signed in to change notification settings - Fork 18.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- test known mappings: conv-pool-deconv stack, ReLU and 1x1 conv - test effects of padding - test rectangular/anisotropic coordinate mapping, test N-D - catch error cases: negative crop, scale mismatch, tops that are not spatially connected
- Loading branch information
Showing
1 changed file
with
192 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
import unittest | ||
|
||
import numpy as np | ||
import random | ||
|
||
import caffe | ||
from caffe import layers as L | ||
from caffe import params as P | ||
from caffe.coord_map import coord_map_from_to, crop | ||
|
||
|
||
def coord_net_spec(ks=3, stride=1, pad=0, pool=2, dstride=2, dpad=0): | ||
""" | ||
Define net spec for simple conv-pool-deconv pattern common to all | ||
coordinate mapping tests. | ||
""" | ||
n = caffe.NetSpec() | ||
n.data = L.Input(shape=dict(dim=[2, 1, 100, 100])) | ||
n.aux = L.Input(shape=dict(dim=[2, 1, 20, 20])) | ||
n.conv = L.Convolution( | ||
n.data, num_output=10, kernel_size=ks, stride=stride, pad=pad) | ||
n.pool = L.Pooling( | ||
n.conv, pool=P.Pooling.MAX, kernel_size=pool, stride=pool, pad=0) | ||
# for upsampling kernel size is 2x stride | ||
try: | ||
deconv_ks = [s*2 for s in dstride] | ||
except: | ||
deconv_ks = dstride*2 | ||
n.deconv = L.Deconvolution( | ||
n.pool, num_output=10, kernel_size=deconv_ks, stride=dstride, pad=dpad) | ||
return n | ||
|
||
|
||
class TestCoordMap(unittest.TestCase): | ||
def setUp(self): | ||
pass | ||
|
||
def test_conv_pool_deconv(self): | ||
""" | ||
Map through conv, pool, and deconv. | ||
""" | ||
n = coord_net_spec() | ||
# identity for 2x pool, 2x deconv | ||
ax, a, b = coord_map_from_to(n.deconv, n.data) | ||
self.assertEquals(ax, 1) | ||
self.assertEquals(a, 1) | ||
self.assertEquals(b, 0) | ||
# shift-by-one for 4x pool, 4x deconv | ||
n = coord_net_spec(pool=4, dstride=4) | ||
ax, a, b = coord_map_from_to(n.deconv, n.data) | ||
self.assertEquals(ax, 1) | ||
self.assertEquals(a, 1) | ||
self.assertEquals(b, -1) | ||
|
||
def test_pass(self): | ||
""" | ||
A pass-through layer (ReLU) and conv (1x1, stride 1, pad 0) | ||
both do identity mapping. | ||
""" | ||
n = coord_net_spec() | ||
ax, a, b = coord_map_from_to(n.deconv, n.data) | ||
n.relu = L.ReLU(n.deconv) | ||
n.conv1x1 = L.Convolution( | ||
n.relu, num_output=10, kernel_size=1, stride=1, pad=0) | ||
for top in [n.relu, n.conv1x1]: | ||
ax_pass, a_pass, b_pass = coord_map_from_to(top, n.data) | ||
self.assertEquals(ax, ax_pass) | ||
self.assertEquals(a, a_pass) | ||
self.assertEquals(b, b_pass) | ||
|
||
def test_padding(self): | ||
""" | ||
Padding conv adds offset while padding deconv subtracts offset. | ||
""" | ||
n = coord_net_spec() | ||
ax, a, b = coord_map_from_to(n.deconv, n.data) | ||
pad = random.randint(0, 10) | ||
# conv padding | ||
n = coord_net_spec(pad=pad) | ||
_, a_pad, b_pad = coord_map_from_to(n.deconv, n.data) | ||
self.assertEquals(a, a_pad) | ||
self.assertEquals(b - pad, b_pad) | ||
# deconv padding | ||
n = coord_net_spec(dpad=pad) | ||
_, a_pad, b_pad = coord_map_from_to(n.deconv, n.data) | ||
self.assertEquals(a, a_pad) | ||
self.assertEquals(b + pad, b_pad) | ||
# pad both to cancel out | ||
n = coord_net_spec(pad=pad, dpad=pad) | ||
_, a_pad, b_pad = coord_map_from_to(n.deconv, n.data) | ||
self.assertEquals(a, a_pad) | ||
self.assertEquals(b, b_pad) | ||
|
||
def test_multi_conv(self): | ||
""" | ||
Multiple bottoms/tops of a layer are identically mapped. | ||
""" | ||
n = coord_net_spec() | ||
# multi bottom/top | ||
n.conv_data, n.conv_aux = L.Convolution( | ||
n.data, n.aux, ntop=2, num_output=10, kernel_size=5, stride=2, | ||
pad=0) | ||
ax1, a1, b1 = coord_map_from_to(n.conv_data, n.data) | ||
ax2, a2, b2 = coord_map_from_to(n.conv_aux, n.aux) | ||
self.assertEquals(ax1, ax2) | ||
self.assertEquals(a1, a2) | ||
self.assertEquals(b1, b2) | ||
|
||
def test_rect(self): | ||
""" | ||
Anisotropic mapping is equivalent to its isotropic parts. | ||
""" | ||
n3x3 = coord_net_spec(ks=3, stride=1, pad=0) | ||
n5x5 = coord_net_spec(ks=5, stride=2, pad=10) | ||
n3x5 = coord_net_spec(ks=[3, 5], stride=[1, 2], pad=[0, 10]) | ||
ax_3x3, a_3x3, b_3x3 = coord_map_from_to(n3x3.deconv, n3x3.data) | ||
ax_5x5, a_5x5, b_5x5 = coord_map_from_to(n5x5.deconv, n5x5.data) | ||
ax_3x5, a_3x5, b_3x5 = coord_map_from_to(n3x5.deconv, n3x5.data) | ||
self.assertTrue(ax_3x3 == ax_5x5 == ax_3x5) | ||
self.assertEquals(a_3x3, a_3x5[0]) | ||
self.assertEquals(b_3x3, b_3x5[0]) | ||
self.assertEquals(a_5x5, a_3x5[1]) | ||
self.assertEquals(b_5x5, b_3x5[1]) | ||
|
||
def test_nd_conv(self): | ||
""" | ||
ND conv maps the same way in more dimensions. | ||
""" | ||
n = caffe.NetSpec() | ||
# define data with 3 spatial dimensions, otherwise the same net | ||
n.data = L.Input(shape=dict(dim=[2, 3, 100, 100, 100])) | ||
n.conv = L.Convolution( | ||
n.data, num_output=10, kernel_size=[3, 3, 3], stride=[1, 1, 1], | ||
pad=[0, 1, 2]) | ||
n.pool = L.Pooling( | ||
n.conv, pool=P.Pooling.MAX, kernel_size=2, stride=2, pad=0) | ||
n.deconv = L.Deconvolution( | ||
n.pool, num_output=10, kernel_size=4, stride=2, pad=0) | ||
ax, a, b = coord_map_from_to(n.deconv, n.data) | ||
self.assertEquals(ax, 1) | ||
self.assertTrue(len(a) == len(b)) | ||
self.assertTrue(np.all(a == 1)) | ||
self.assertEquals(b[0] - 1, b[1]) | ||
self.assertEquals(b[1] - 1, b[2]) | ||
|
||
def test_crop_of_crop(self): | ||
""" | ||
Map coordinates through Crop layer: | ||
crop an already-cropped output to the input and check change in offset. | ||
""" | ||
n = coord_net_spec() | ||
offset = random.randint(0, 10) | ||
ax, a, b = coord_map_from_to(n.deconv, n.data) | ||
n.crop = L.Crop(n.deconv, n.data, axis=2, offset=offset) | ||
ax_crop, a_crop, b_crop = coord_map_from_to(n.crop, n.data) | ||
self.assertEquals(ax, ax_crop) | ||
self.assertEquals(a, a_crop) | ||
self.assertEquals(b + offset, b_crop) | ||
|
||
def test_crop_helper(self): | ||
""" | ||
Define Crop layer by crop(). | ||
""" | ||
n = coord_net_spec() | ||
crop(n.deconv, n.data) | ||
|
||
def test_catch_unconnected(self): | ||
""" | ||
Catch mapping spatially unconnected tops. | ||
""" | ||
n = coord_net_spec() | ||
n.ip = L.InnerProduct(n.deconv, num_output=10) | ||
with self.assertRaises(RuntimeError): | ||
coord_map_from_to(n.ip, n.data) | ||
|
||
def test_catch_scale_mismatch(self): | ||
""" | ||
Catch incompatible scales, such as when the top to be cropped | ||
is mapped to a differently strided reference top. | ||
""" | ||
n = coord_net_spec(pool=3, dstride=2) # pool 3x but deconv 2x | ||
with self.assertRaises(AssertionError): | ||
crop(n.deconv, n.data) | ||
|
||
def test_catch_negative_crop(self): | ||
""" | ||
Catch impossible offsets, such as when the top to be cropped | ||
is mapped to a larger reference top. | ||
""" | ||
n = coord_net_spec(dpad=10) # make output smaller than input | ||
with self.assertRaises(AssertionError): | ||
crop(n.deconv, n.data) |