import numpy as np
from .layers import Layer
from ..base import ParamMixin
from ..parameter import Parameter
import cudarray as ca
[docs]def padding(win_shape, border_mode):
if border_mode == 'valid':
return (0, 0)
elif border_mode == 'same':
return (win_shape[0]//2, win_shape[1]//2)
elif border_mode == 'full':
return (win_shape[0]-1, win_shape[1]-1)
else:
raise ValueError('invalid mode: "%s"' % border_mode)
[docs]class Convolution(Layer, ParamMixin):
def __init__(self, n_filters, filter_shape, weights, bias=0.0,
strides=(1, 1), border_mode='valid'):
self.name = 'conv'
self.n_filters = n_filters
self.filter_shape = filter_shape
self.weights = Parameter.from_any(weights)
self.bias = Parameter.from_any(bias)
pad = padding(filter_shape, border_mode)
self.conv_op = ca.nnet.ConvBC01(pad, strides)
self._tmp_x = None
def _setup(self, x_shape):
n_channels = x_shape[1]
self.weights._setup((self.n_filters, n_channels) + self.filter_shape)
if not self.weights.name:
self.weights.name = self.name + '_weights'
self.bias._setup((1, self.n_filters, 1, 1))
if not self.bias.name:
self.bias.name = self.name + '_bias'
[docs] def fprop(self, x):
self._tmp_x = x
convout = self.conv_op.fprop(x, self.weights.array)
return convout + self.bias.array
[docs] def bprop(self, y_grad):
_, x_grad = self.conv_op.bprop(
self._tmp_x, self.weights.array, y_grad, to_imgs=self.bprop_to_x,
filters_d=self.weights.grad_array
)
ca.sum(ca.sum(y_grad, axis=(2, 3), keepdims=True), axis=0,
keepdims=True, out=self.bias.grad_array)
return x_grad
@property
def _params(self):
return self.weights, self.bias
@_params.setter
def _params(self, params):
self.weights, self.bias = params
[docs] def y_shape(self, x_shape):
return self.conv_op.output_shape(x_shape, self.n_filters,
self.filter_shape)
[docs]class Pool(Layer):
def __init__(self, win_shape=(3, 3), method='max', strides=(1, 1),
border_mode='valid'):
self.name = 'pool'
pad = padding(win_shape, border_mode)
self.pool_op = ca.nnet.PoolB01(win_shape, pad, strides, method)
self.img_shape = None
[docs] def fprop(self, x):
self.img_shape = x.shape[2:]
poolout = self.pool_op.fprop(x)
return poolout
[docs] def bprop(self, y_grad):
x_grad = self.pool_op.bprop(self.img_shape, y_grad)
return x_grad
[docs] def y_shape(self, x_shape):
return self.pool_op.output_shape(x_shape)
[docs]class LocalResponseNormalization(Layer):
def __init__(self, alpha=1e-4, beta=0.75, n=5, k=1):
self.alpha = alpha
self.beta = beta
self.n = n
self.k = k
[docs] def fprop(self, x):
x = ca.lrnorm_bc01(x, N=self.n, alpha=self.alpha, beta=self.beta,
k=self.k)
return x
[docs] def bprop(self, y_grad):
return y_grad
[docs] def y_shape(self, x_shape):
return x_shape
[docs]class LocalContrastNormalization(Layer):
@staticmethod
[docs] def gaussian_kernel(sigma, size=None):
if size is None:
size = int(np.ceil(sigma*2.))
if size % 2 == 0:
size += 1
x = np.linspace(-size/2., size/2., size)
kernel = 1/(np.sqrt(2*np.pi))*np.exp(-x**2/(2*sigma**2))/sigma
return kernel/np.sum(kernel)
def __init__(self, kernel, eps=0.1, strides=(1, 1)):
self.name = 'lcn'
self.eps = eps
if kernel.ndim == 1:
kernel = np.outer(kernel, kernel)
if kernel.shape[-2] % 2 == 0 or kernel.shape[-1] % 2 == 0:
raise ValueError('only odd kernel sizes are supported')
self.kernel = kernel
self.ca_kernel = None
pad = padding(kernel.shape[-2:], 'same')
self.conv_op = ca.nnet.ConvBC01(pad, strides)
def _setup(self, x_shape):
n_channels = x_shape[1]
if self.kernel.ndim == 2:
self.kernel = np.repeat(self.kernel[np.newaxis, np.newaxis, ...],
n_channels, axis=1)
elif self.kernel.ndim == 3:
self.kernel = self.kernel[np.newaxis, :]
self.ca_kernel = ca.array(self.kernel)
[docs] def fprop(self, x):
n_channels = x.shape[1]
# Calculate local mean
tmp = self.conv_op.fprop(x, self.ca_kernel)
if n_channels > 1:
ca.divide(tmp, n_channels, tmp)
# Center input with local mean
centered = ca.subtract(x, tmp)
# Calculate local standard deviation
tmp = ca.power(centered, 2)
tmp = self.conv_op.fprop(tmp, self.ca_kernel)
if n_channels > 1:
ca.divide(tmp, n_channels, tmp)
ca.sqrt(tmp, tmp)
# Scale centered input with standard deviation
return centered / (tmp + self.eps)
[docs] def bprop(self, y_grad):
raise NotImplementedError('LocalContrastNormalization supports only '
'usage as a preprocessing layer.')
[docs] def y_shape(self, x_shape):
return x_shape
[docs]class Flatten(Layer):
def __init__(self):
self.name = 'flatten'
self.x_shape = None
[docs] def fprop(self, x):
self.x_shape = x.shape
return ca.reshape(x, self.y_shape(x.shape))
[docs] def bprop(self, y_grad):
return ca.reshape(y_grad, self.x_shape)
[docs] def y_shape(self, x_shape):
return (x_shape[0], np.prod(x_shape[1:]))