提交 ac00ff57 authored 作者: Ian Goodfellow's avatar Ian Goodfellow

added Conv3D tests

上级 2434799d
...@@ -6,10 +6,10 @@ import theano ...@@ -6,10 +6,10 @@ import theano
class ConvTransp3D(theano.Op): class ConvTransp3D(theano.Op):
""" "Transpose" of Conv3D (Conv3D implements multiplication by an implicitly defined matrix W. This implements multiplication by its transpose) """ """ "Transpose" of Conv3D (Conv3D implements multiplication by an implicitly defined matrix W. This implements multiplication by its transpose) """
def __eq__(self,other): def __eq__(self,other):
return type(self) == type(other) return type(self) == type(other)
def __hash__(self): def __hash__(self):
return hash(type(self)) return hash(type(self))
def c_code_cache_version(self): def c_code_cache_version(self):
return (1,) return (1,)
...@@ -73,7 +73,7 @@ class ConvTransp3D(theano.Op): ...@@ -73,7 +73,7 @@ class ConvTransp3D(theano.Op):
else: else:
b_name = 'anon' b_name = 'anon'
dCdW.name = 'ConvTransp3D_dCdW.H='+H_name+',dCdR='+dCdR_name+',W='+W_name dCdW.name = 'ConvTransp3D_dCdW.H='+H_name+',dCdR='+dCdR_name+',W='+W_name
dCdb.name = 'ConvTransp3D_dCdb.H='+H_name+',dCdR='+dCdR_name+',W='+W_name+',b='+b_name dCdb.name = 'ConvTransp3D_dCdb.H='+H_name+',dCdR='+dCdR_name+',W='+W_name+',b='+b_name
dCdH.name = 'ConvTransp3D_dCdH.H='+H_name+',dCdR='+dCdR_name dCdH.name = 'ConvTransp3D_dCdH.H='+H_name+',dCdR='+dCdR_name
......
import unittest
import theano
import theano.tensor as T
from theano import function, Mode
from theano.tests import unittest_tools as utt
from theano.tensor.nnet.ConvTransp3D import convTransp3D
from theano.tensor.nnet.ConvGrad3D import convGrad3D
from theano.tensor.nnet.Conv3D import conv3D
import numpy as N
import copy
from scipy import sparse
from theano import shared
floatX = theano.config.floatX
class DummyConv3D:
"""A dummy version of Conv3D passed to verify_grad
Stores a fixed stride, since stride is not differentiable
Exposes only one scalar argument, which is used as the position
along a parametrically defined line, with 0 being at VwbVals
Direction of the line is chosen randomly at construction
The reason for locking the inputs to lie on this line is so that the
verify_grad will not need to test hundreds of variables. Disadvantage
is we can't be certain that all of them are correct, advantange is that
this random projection lets us test lots of variables very quickly """
def __init__(self, rng, VWbVals, d):
"""
param: rng Random number generator used to pick direction of the line
param: VWbVals tuple containing values to test V,W,b around
param: d shared variable for d, the stride
"""
self.V, self.W, self.b = VWbVals
self.dV, self.dW, self.db = shared(rng.uniform(-1,1,self.V.value.shape)), shared(rng.uniform(-1,1,self.W.value.shape)), shared(rng.uniform(-1,1,self.b.value.shape))
self.d = d
def __call__(self, t):
output = conv3D(self.V+t*self.dV,self.W+t*self.dW,self.b+t*self.db,self.d)
return output
class DummyConvGrad3D:
def __init__(self, rng, VdHvals, d, WShape):
"""
param: rng Random number generator used to pick direction of the line
param: VWbVals tuple containing values to test V,W,b around
param: d shared variable for d, the stride
"""
self.V, self.dCdH = VdHvals
self.dV, self.ddCdH = shared(rng.uniform(-1,1,self.V.value.shape)), shared(rng.uniform(-1,1,self.dCdH.value.shape))
self.d = d
self.WShape = WShape
def __call__(self, t):
output = convGrad3D(self.V+t*self.dV,self.d,self.WShape,self.dCdH + t * self.ddCdH)
return output
class DummyConvTransp3D:
def __init__(self, rng, WbHvals, d, RShape):
"""
param: rng Random number generator used to pick direction of the line
param: VWbVals tuple containing values to test V,W,b around
param: d shared variable for d, the stride
"""
self.W, self.b, self.H = WbHvals
self.dW = rng.uniform(-1,1,self.W.value.shape)
self.db = rng.uniform(-1,1,self.b.value.shape)
self.dH = rng.uniform(-1,1,self.H.value.shape)
self.dW, self.db, self.dH = shared(self.dW), shared(self.db), shared(self.dH)
self.d = d
self.RShape = RShape
def __call__(self, t):
output = convTransp3D(self.W+t*self.dW,self.b+t*self.db,self.d,self.H+t*self.dH, self.RShape)
return output
class TestConv3D(unittest.TestCase):
def setUp(self):
utt.seed_rng()
self.rng = N.random.RandomState(utt.fetch_seed())
mode = copy.copy(theano.compile.mode.get_default_mode())
mode.check_py_code = False
self.W = shared(N.ndarray(shape=(1,1,1,1,1), dtype=floatX))
self.b = shared(N.zeros(1,dtype=floatX))
self.rb = shared(N.zeros(1,dtype=floatX))
self.V = shared(N.ndarray(shape=(1,1,1,1,1), dtype=floatX))
self.d = shared(N.ndarray(shape=(3,),dtype=int))
self.H = conv3D(self.V, self.W, self.b, self.d)
self.H_func = function([], self.H, mode = mode)
self.H_shape_func = function( [], self.H.shape, mode = mode)
self.RShape = T.vector(dtype='int64')
self.otherH = T.TensorType(floatX,(False,False,False,False,False))(name='otherH')
self.transp = convTransp3D(self.W, self.rb, self.d, self.otherH, self.RShape)
self.transp_func = function([self.otherH,self.RShape],self.transp, mode=mode)
self.R = convTransp3D(self.W, self.rb, self.d, self.H, self.RShape)
self.R_func = function([self.RShape], self.R, mode = mode)
self.R_shape_func = function([self.RShape], self.R.shape)
self.reconsObj = T.sum(T.sqr(self.V-self.R))
self.reconsObjFunc = function([self.RShape], self.reconsObj, mode=mode)
self.gradientsFunc = function([self.RShape], [ T.grad(self.reconsObj, self.W), T.grad(self.reconsObj, self.H), T.grad(self.reconsObj, self.V), T.grad(self.reconsObj,self.b) ] , mode=mode)
self.check_c_against_python = function([self.RShape], [ T.grad(self.reconsObj, self.W), T.grad(self.reconsObj, self.H), T.grad(self.reconsObj, self.V), T.grad(self.reconsObj,self.b) ] , mode='DEBUG_MODE')
self.dCdW_shape_func = function([self.RShape], T.grad(self.reconsObj, self.W).shape, mode=mode)
def random_tensor(self,*dims):
return N.asarray(self.rng.uniform(-.05,.05,dims),dtype=floatX)
def randomize(self):
batchSize = self.rng.randint(1,4)
videoDur = self.rng.randint(8,30)
filterWidth = self.rng.randint(1,8)
filterHeight = self.rng.randint(1,8)
filterDur = self.rng.randint(1,8)
tsteps = self.rng.randint(1,4)
rsteps = self.rng.randint(1,4)
csteps = self.rng.randint(1,4)
videoDur = tsteps * filterDur + self.rng.randint(0,3)
videoWidth = csteps * filterWidth + self.rng.randint(0,3)
videoHeight = rsteps * filterHeight + self.rng.randint(0,3)
numFilters = self.rng.randint(1,3)
inputChannels = self.rng.randint(1,3)
self.d.value[0] = self.rng.randint(1,15)
self.d.value[1] = self.rng.randint(1,15)
self.d.value[2] = self.rng.randint(1,15)
outputHeight = int( (videoHeight - filterHeight) / self.d.value[0] )+1
outputWidth = int( (videoWidth - filterWidth) / self.d.value[1] )+1
outputDur = int( (videoDur - filterDur) / self.d.value[2] ) +1
self.W.value = self.random_tensor(numFilters,filterHeight,filterWidth,filterDur,inputChannels)
self.b.value = self.random_tensor(numFilters)
self.rb.value = self.random_tensor(inputChannels)
self.V.value = self.random_tensor(batchSize,videoHeight,videoWidth,videoDur,inputChannels)
self.rb.value = self.random_tensor(inputChannels)
def test_c_against_python(self):
self.randomize()
self.check_c_against_python(self.V.value.shape[1:4])
def test_c_against_mat_mul(self):
#Use a filter of the same size as the image, so the convolution is just a dense matrix multiply
#Check that dense matrix multiplication gives the same result as convolution
batchSize = self.rng.randint(1,10)
videoDur = self.rng.randint(3,10)
videoWidth = self.rng.randint(1,5)
videoHeight = self.rng.randint(1,5)
filterWidth = videoWidth
filterHeight = videoHeight
filterDur = videoDur
numFilters = self.rng.randint(1,3)
inputChannels = self.rng.randint(1,4)
self.d.value[0] = self.rng.randint(1,15)
self.d.value[1] = self.rng.randint(1,15)
self.d.value[2] = self.rng.randint(1,15)
self.W.value = self.random_tensor(numFilters,filterHeight,filterWidth,filterDur,inputChannels)
self.W.value *= (self.W.value < 1e-5)
self.b.value = self.random_tensor(numFilters)
self.V.value = self.random_tensor(batchSize,videoHeight,videoWidth,videoDur,inputChannels)
Hv = self.H_func()
assert Hv.shape[1] == 1
assert Hv.shape[2] == 1
assert Hv.shape[3] == 1
n = inputChannels * videoHeight * videoWidth * videoDur
W_mat = N.zeros((n, numFilters))
V_mat = N.zeros((batchSize,n))
Hv_mat = N.zeros((batchSize, numFilters))
for qi in xrange(0,numFilters):
W_mat[:,qi] = self.W.value[qi,:,:,:,:].reshape((n))
Hv_mat[:,qi] = Hv[:,0,0,0,qi]
for qi in xrange(0,batchSize):
V_mat[qi,:] = self.V.value[qi,:,:,:,:].reshape((n))
H_mat = N.dot(V_mat,W_mat) + self.b.value
tol = 1e-5
if floatX == 'float32':
tol = 1e-4
if N.abs(H_mat-Hv_mat).max() > tol and not N.allclose(H_mat,Hv_mat):
print H_mat
print Hv_mat
print 'max error: '+str(N.abs(H_mat-Hv_mat).max())
W.value[W.value != 0] += 1.0
print 'min non-zero kernel mag: '+str(N.abs(W.value).min())
assert False
def test_c_against_mat_transp_mul(self):
#Use a filter of the same size as the image, so the convolution is just a dense matrix multiply
#Check that dense matrix multiplication by the transpose of the matrix gives the same result as ConvTransp
batchSize = self.rng.randint(1,10)
videoDur = self.rng.randint(3,15)
videoWidth = self.rng.randint(3,15)
videoHeight = self.rng.randint(3,15)
filterWidth = videoWidth
filterHeight = videoHeight
filterDur = videoDur
numFilters = self.rng.randint(1,15)
inputChannels = self.rng.randint(1,15)
self.d.value[0] = self.rng.randint(1,15)
self.d.value[1] = self.rng.randint(1,15)
self.d.value[2] = self.rng.randint(1,15)
self.W.value = self.random_tensor(numFilters,filterHeight,filterWidth,filterDur,inputChannels)
self.b.value = self.random_tensor(numFilters)
self.V.value = self.random_tensor(batchSize,videoHeight,videoWidth,videoDur,inputChannels)
self.rb.value = self.random_tensor(inputChannels)
H_shape = self.H_shape_func()
assert H_shape[1] == 1
assert H_shape[2] == 1
assert H_shape[3] == 1
Hv = self.random_tensor( * H_shape )
Vv = self.transp_func(Hv,[videoHeight,videoWidth,videoDur])
n = inputChannels * videoHeight * videoWidth * videoDur
rbim = N.zeros((videoHeight,videoWidth,videoDur,inputChannels))
for qi in xrange(0,inputChannels):
rbim[:,:,:,qi] = self.rb.value[qi]
rbv = rbim.reshape((n))
W_mat = N.zeros((numFilters, n))
Vv_mat = N.zeros((n, batchSize))
Hv_mat = N.zeros((numFilters,batchSize))
for qi in xrange(0,numFilters):
W_mat[qi,:] = self.W.value[qi,:,:,:,:].reshape((n))
Hv_mat[qi,:] = Hv[:,0,0,0,qi]
for qi in xrange(0,batchSize):
Vv_mat[:,qi] = Vv[qi,:,:,:,:].reshape((n))
V_mat = (N.dot(W_mat.transpose(),Hv_mat).transpose() + rbv).transpose()
if N.abs(V_mat-Vv_mat).max() > 1e-5:
print V_mat
print Vv_mat
for qq in xrange(V_mat.shape[0]):
for qqq in xrange(Vv_mat.shape[1]):
if abs(V_mat[qq,qqq]-Vv_mat[qq,qqq]) > 1e-5:
print 'wrong at '+str((qq,qqq))+': '+str((V_mat[qq,qqq],Vv_mat[qq,qqq]))
assert False
def test_c_against_sparse_mat_transp_mul(self):
#like test_c_against_mat_transp_mul but using a sparse matrix and a kernel that is smaller than the image
batchSize = self.rng.randint(1,3)
filterWidth = self.rng.randint(1,8)
filterHeight = self.rng.randint(1,8)
filterDur = self.rng.randint(1,8)
self.d.value[0] = self.rng.randint(1,3)
self.d.value[1] = self.rng.randint(1,3)
self.d.value[2] = self.rng.randint(1,3)
dr = self.d.value[0]
dc = self.d.value[1]
dt = self.d.value[2]
numFilters = self.rng.randint(1,3)
row_steps = self.rng.randint(1,4)
col_steps = self.rng.randint(1,4)
time_steps = self.rng.randint(1,4)
print (row_steps,col_steps,time_steps)
videoDur = (time_steps-1)*dt+filterDur + self.rng.randint(0,3)
videoWidth = (col_steps-1)*dc+filterWidth + self.rng.randint(0,3)
videoHeight = (row_steps-1)*dr+filterHeight + self.rng.randint(0,3)
inputChannels = self.rng.randint(1,15)
self.W.value = self.random_tensor(numFilters,filterHeight,filterWidth,filterDur,inputChannels)
self.b.value = self.random_tensor(numFilters)
#just needed so H_shape works
self.V.value = self.random_tensor(batchSize,videoHeight,videoWidth,videoDur,inputChannels)
self.rb.value = self.random_tensor(inputChannels)
H_shape = self.H_shape_func()
#make index maps
h = N.zeros( H_shape[1:])
r = N.zeros( H_shape[1:])
c = N.zeros( H_shape[1:])
t = N.zeros( H_shape[1:])
for qi in xrange(0,H_shape[4]):
h[:,:,:,qi] = qi
for qi in xrange(0,H_shape[1]):
r[qi,:,:,:] = qi
for qi in xrange(0,H_shape[2]):
c[:,qi,:,:] = qi
for qi in xrange(0,H_shape[3]):
t[:,:,qi,:] = qi
hn = H_shape[1] * H_shape[2] * H_shape[3] * H_shape[4]
h = h.reshape((hn))
r = r.reshape((hn))
c = c.reshape((hn))
t = t.reshape((hn))
Hv = self.random_tensor( * H_shape )
Vv = self.transp_func(Hv,[videoHeight,videoWidth,videoDur])
n = inputChannels * videoHeight * videoWidth * videoDur
rbim = N.zeros((videoHeight,videoWidth,videoDur,inputChannels))
for qi in xrange(0,inputChannels):
rbim[:,:,:,qi] = self.rb.value[qi]
rbv = rbim.reshape((n))
W_mat = N.zeros((hn,n))
Vv_mat = N.zeros((n, batchSize))
Hv_mat = N.zeros((hn,batchSize))
for qi in xrange(0,hn):
hi = h[qi]
ri = r[qi]
ci = c[qi]
ti = t[qi]
placed_filter = N.zeros(self.V.value.shape[1:])
placed_filter[ri*dr:ri*dr+self.W.value.shape[1],ci*dc:ci*dc+self.W.value.shape[2],ti*dt:ti*dt+self.W.value.shape[3],:] = self.W.value[hi,:,:,:,:]
W_mat[qi,:] = placed_filter.reshape((n))
Hv_mat[qi,:] = Hv[:,ri,ci,ti,hi]
for qi in xrange(0,batchSize):
Vv_mat[:,qi] = Vv[qi,:,:,:,:].reshape((n))
W_mat_T = sparse.csr_matrix(W_mat.transpose())
temp = W_mat_T.matmat(Hv_mat)
V_mat = (temp.transpose() + rbv).transpose()
if N.abs(V_mat-Vv_mat).max() > 1e-5:
print 'mul'
print V_mat
print 'conv'
print Vv_mat
for i in xrange(0,n):
for j in xrange(0,batchSize):
if abs(V_mat[i,j] - Vv_mat[i,j]) > 1e-5:
print 'wrong at %d,%d: %f mul versus %f conv' % (i,j,V_mat[i,j],Vv_mat[i,j])
assert False
def test_infer_shape(self):
self.randomize()
Hv = self.H_func()
H_shape = self.H_shape_func()
assert N.all(Hv.shape == H_shape)
gradients = self.gradientsFunc(self.V.value.shape[1:4])
dCdWv = gradients[0]
dCdW_shape = self.dCdW_shape_func(self.V.value.shape[1:4])
assert N.all(dCdWv.shape == dCdW_shape)
Rv = self.R_func(self.V.value.shape[1:4])
R_shape = self.R_shape_func(self.V.value.shape[1:4])
assert N.all(Rv.shape == R_shape)
def test_gradient(self):
self.randomize()
rng, V,W,b,d,rb = self.rng, self.V, self.W, self.b, self.d, self.rb
dCdH = shared(self.random_tensor( *self.H_shape_func() ))
testsPerDir = 2
theano.tests.unittest_tools.verify_grad(DummyConv3D(rng, (V,W,b), d), [0.0], n_tests=testsPerDir)
theano.tests.unittest_tools.verify_grad(DummyConvTransp3D(rng, (W,rb,dCdH), d,V.value.shape[1:4]), [0.0], n_tests=testsPerDir)
theano.tests.unittest_tools.verify_grad(DummyConvGrad3D(rng, (V,dCdH), d, W.value.shape), [0.0], n_tests=testsPerDir)
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论