Mercurial > pylearn
changeset 862:882b4169e2b1
added Rust2005Conv
author | James Bergstra <bergstrj@iro.umontreal.ca> |
---|---|
date | Tue, 03 Nov 2009 15:26:01 -0500 |
parents | 07a06c2f9408 |
children | e6d2cb493754 |
files | pylearn/shared/layers/rust2005.py |
diffstat | 1 files changed, 148 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/pylearn/shared/layers/rust2005.py Tue Nov 03 15:25:08 2009 -0500 +++ b/pylearn/shared/layers/rust2005.py Tue Nov 03 15:26:01 2009 -0500 @@ -4,6 +4,10 @@ This layer implements a model of simple and complex cell firing rate responses. + +:TODO: implement full model with variable exponents. The current implementation fixes internal +exponents to 2 and the external exponent to 1/2. + """ import numpy @@ -18,25 +22,31 @@ from theano.compile.sandbox import shared from theano.sandbox.softsign import softsign from theano.tensor.nnet import softplus +from theano.sandbox.conv import ConvOp from .util import update_locals, add_logging -def rust2005_act_from_filters(linpart, E_quad, S_quad): +def rust2005_act_from_filters(linpart, E_quad, S_quad, eps): + """Return rust2005 activation from linear filter responses, as well as E and S terms + + :param linpart: a single tensor of linear filter responses + :param E_quad: a list of tensors of linear filter responses + :param S_quad: a list of tensors of linear filter responses + :param eps: a scalar to add to the sum of squares before the sqrt + + """ + if isinstance(E_quad, theano.Variable): + raise TypeError('E_quad should be a list of Variables, not a Variable itself') + if isinstance(S_quad, theano.Variable): + raise TypeError('E_quad should be a list of Variables, not a Variable itself') sqrt = theano.tensor.sqrt softlin = theano.tensor.nnet.softplus(linpart) - E = sqrt(sum([E_quad_i**2 for E_quad_i in E_quad] + [1e-8, softlin**2])) - S = sqrt(sum([S_quad_i**2 for S_quad_i in S_quad] + [1e-8])) - return (E-S) / (1+E+S) + E = sqrt(sum([E_quad_i**2 for E_quad_i in E_quad] + [softlin**2], eps)) + S = sqrt(sum([S_quad_i**2 for S_quad_i in S_quad], eps)) + return (E-S) / (1+E+S), E, S class Rust2005(object): - """ - shared variable version. - - w is 3-tensor n_in x n_out x (1+n_E_quadratic + n_S_quadratic) - - w - - """ + """Energy-like complex cell activation function described in Rust et al. 2005 """ #logging methods come from the add_logging() call below # _info, _debug, _warn, _error, _fatal @@ -99,7 +109,7 @@ b = shared(numpy.zeros((n_out,), dtype=dtype)) return cls(input, w, b, n_out, n_E, n_S, epsilon, filter_shape, [w,b]) - def img_from_weights(self, rows=12, cols=24, row_gap=1, col_gap=1, eps=1e-4): + def img_from_weights(self, rows=12, cols=25, row_gap=1, col_gap=1, eps=1e-4, triplegap=0): """ Return an image that visualizes all the weights in the layer. The current implentation returns a tiling in which every triple of columns is a logical @@ -110,9 +120,12 @@ """ if cols % 3: #because there are three kinds of filters: linear, excitatory, inhibitory raise ValueError("cols must be multiple of 3") + + n_triples = cols / 3 + filter_shape = self.filter_shape height = rows * (row_gap + filter_shape[0]) - row_gap - width = cols * (col_gap + filter_shape[1]) - col_gap + width = cols * (col_gap + filter_shape[1]) - col_gap + (n_triples-1) * triplegap out_array = numpy.zeros((height, width, 3), dtype='uint8') @@ -123,10 +136,14 @@ for r in xrange(rows): out_r_low = r*(row_gap + filter_shape[0]) out_r_high = out_r_low + filter_shape[0] + extra_col_gap = 0 # a counter we'll use for the triplegap for c in xrange(cols): - out_c_low = c*(col_gap + filter_shape[1]) + if c and (c%3==0): + extra_col_gap += triplegap + out_c_low = c*(col_gap + filter_shape[1]) + extra_col_gap out_c_high = out_c_low + filter_shape[1] out_tile = out_array[out_r_low:out_r_high, out_c_low:out_c_high,:] + assert out_tile.shape[1] == filter_shape[1] if c % 3 == 0: # linear filter if w_col < w.shape[1]: @@ -148,3 +165,119 @@ w_col += self.n_S_quadratic return Image.fromarray(out_array, 'RGB') add_logging(Rust2005) + +class Rust2005Conv(object): + """Convolutional version of `Rust2005` + + :note: + The layer doesn't contain an option for downsampling. It makes sense to downsample the output + using DownsampleMaxPool, but downsampling is orthogonal to the behaviour of this + layer so it is not included. + + """ + + l1 = 0.0 + l2_sqr = 0.0 + + eps=1e-6 # default epsilon to prevent sqrt(0) in quadratic filters + + image_shape = None + filter_shape = None + output_shape = None + output_channels = None + output_examples = None + + def __init__(self, linpart, Es, Ss, params, eps=eps): + """ + """ + eps = numpy.asarray(self.eps, linpart.dtype) + output, E, S = rust2005_act_from_filters(linpart, Es, Ss, eps) + + update_locals(self, locals()) + + + @classmethod + def new(cls, rng, input, image_shape, filter_shape, n_examples, n_filters, n_E, n_S, + n_channels=1, + eps=1.0e-6, dtype=None, conv_mode='valid', + w_range=None, + q_range=None + ): + """ Return Rust2005Conv layer + + layer.output will be 4D tensor with shape (n_examples, n_filters, R, C) where R and C + depend on the image_shape, the filter_shape and the convolution mode. + + + :param rng: generator for randomized initial filters + :param input: symbolic input (4D tensor) + :type input: 4D tensor with shape (n_examples, n_channels, image_shape[0], image_shape[1]) + + :param image_shape: rows, cols of every channel of every image + :param filter_shape: rows, cols of every filter + :param bsize: number of images to be treated + :param n_filters: number of filters (output will have this many channels) + :param n_channels: number of channels in each image and filter + :param n_E: number of squared exciting terms + :param n_S: number of squared inhibition terms + :param eps: epsilon to add to sum-of-squares in sqrt + :param dtype: dtype to use for new variables (Default: input.dtype) + :param conv_mode: convolution mode + :param w_range: linear weights will be drawn uniformly from this range + :type w_range: pair (lower_bound, upper_bound + :param q_range: quadratic weights will be drawn uniformly from this range + :type q_range: pair (lower_bound, upper_bound + """ + if dtype is None: + dtype = input.dtype + + irows, icols = image_shape + krows, kcols = filter_shape + + conv = ConvOp((n_channels,irows, icols), (krows, kcols), n_filters, n_examples, + dx=1, dy=1, output_mode=conv_mode) + + w_shp = (n_filters, n_channels, krows, kcols) + b_shp = (n_filters,) + + if w_range is None: + w_low = -2.0/numpy.sqrt(image_shape[0] * image_shape[1]) + w_high = 2.0/numpy.sqrt(image_shape[0] * image_shape[1]) + else: + w_low, w_high = w_range + + if q_range is None: + q_low, q_high = w_low, w_high + else: + q_low, q_high = w_range + + w = shared(numpy.asarray(rng.uniform(low=w_low, high=w_high, size=w_shp), dtype=dtype)) + b = shared(numpy.asarray(rng.uniform(low=w_low, high=w_low, size=b_shp), dtype=dtype)) + + E_w = [ + shared(numpy.asarray(rng.uniform(low=q_low, high=q_high, size=w_shp), dtype=dtype)) + for i in xrange(n_E) + ] + S_w = [ + shared(numpy.asarray(rng.uniform(low=q_low, high=q_high, size=w_shp), dtype=dtype)) + for i in xrange(n_S) + ] + + rval = cls( + linpart=conv(input, w) + b.dimshuffle(0,'x','x'), + Es=[conv(input, e) for e in E_w], + Ss=[conv(input, s) for s in S_w], + params=[w,b]+E_w + S_w, + eps=eps) + + # ignore bias in l1 (Yoshua's habit) + rval.l1 = sum(abs(p) for p in ([w]+E_w+S_w)) + rval.l2_sqr = sum(p**2 for p in ([w]+E_w+S_w)) + rval.image_shape = image_shape + rval.filter_shape = filter_shape + rval.output_shape = conv.outshp + rval.output_channels = n_filters # how many channels of *output* + rval.output_examples = n_examples + + return rval +add_logging(Rust2005Conv) # _debug, _info, _warn, _error, _fatal