view pylearn/algorithms/exponential_mean.py @ 1371:98d4232df1d8

comment on OD idea
author Razvan Pascanu <r.pascanu@gmail.com>
date Mon, 15 Nov 2010 16:28:02 -0500
parents b4aa46f856c1
children
line wrap: on
line source

"""Modules for maintaining statistics based on exponential decay"""
__docformat__ = "restructuredtext en"

import copy
import numpy
import theano
import theano.tensor

class ExponentialMean(theano.Module):
    """Maintain an exponentially-decaying estimate of the mean

    This module computes the exact mean of the first `max_denom` values of `x`.
    After the first `max_denom` values, it tracks the mean using the formula:

    :math:`self.curval = (1.0 - (1.0/max_denom)) * self.old_curval + (1.0/max_denom) * x`


    The symbolic buffer containing the running mean is called `old_curval`.  (This has a value
    in the ModuleInstance).

    The symbolic variable for the updated running mean is called `curval`.

    """

    max_denom = None
    """The average will be updated as if the current estimated average was estimated from at
    most `max_denom-1` values."""

    ival = None
    """The initial value for the estimated average."""

    def __init__(self, x, max_denom, ival):
        """
        :param x: track the mean of this Variable

        :param max_denom: see `self.max_denom`
    
        :param ival: This should be a tensor of zeros with a shape that matches `x`'s runtime
        value.

        """
        super(ExponentialMean, self).__init__()

        self.max_denom = max_denom
        self.ival = ival

        if len(ival.shape) == 0:
            x_type = theano.tensor.dscalar
        elif len(ival.shape) == 1:
            x_type = theano.tensor.dvector
        elif len(ival.shape) == 2:
            x_type = theano.tensor.dmatrix
        else:
            #TODO: x_type = theano.tensor.TensorType(...)
            raise NotImplementedError()

        self.old_curval = (x_type())
        # TODO: making this an lscalar caused different optimizations, followed by integer
        # division somewhere were i wanted float division.... and the wrong answer.
        self.denom = (theano.tensor.dscalar())

        alpha = 1.0 / self.denom
        self.curval = (1.0 - alpha) * self.old_curval + alpha * x

    def updates(self):
        """
        :returns: the symbolic updates necessary to refresh the mean estimate
        :rtype: dict Variable -> Variable
        """
        return {
                self.old_curval: self.curval,
                self.denom: theano.tensor.smallest(self.denom + 1, self.max_denom)
                }

    def _instance_initialize(self, obj):
        obj.denom = 1
        obj.old_curval = copy.copy(self.ival)

def exp_mean(x, x_shape, max_denom=100):
    """Return an `ExponentialMean` to track a Variable `x` with given shape
    
    :type x: Variable
    :type x_shape: tuple
    :type max_denom: int

    :rtype: ExponentialMean instance
    """
    return ExponentialMean(x, 
            max_denom=max_denom,
            ival=numpy.zeros(x_shape))

def exp_mean_sqr(x, x_shape, max_denom=100):
    """Return an `ExponentialMean` to track a Variable `x`'s square with given shape
    
    :type x: Variable
    :type x_shape: tuple
    :type max_denom: int

    :rtype: ExponentialMean instance
    """
    return ExponentialMean(x**2, 
            max_denom=max_denom,
            ival=numpy.zeros(x_shape))

class exp_var(theano.Module):
    """Module with interface similar to `ExponentialMean` for tracking elementwise variance

    """

    mean = None
    """`ExponentialMean`: current estimate of mean of `x` """

    mean_sqr = None
    """`ExponentialMean`: current estimate of mean sqr of `x` """

    curval = None
    """Variable: Current estimate of the variance of `x` """

    def __init__(self, x, x_shape, max_denom=100):
        """
        :param x: track the variance of this Variable

        :param max_denom: see `self.max_denom`
    
        :param ival: This should be a tensor of zeros with a shape that matches `x`'s runtime
        value.

        """
        super(exp_var,self).__init__()

        self.mean = exp_mean(x, x_shape, max_denom)
        self.mean_sqr = exp_mean_sqr(x, x_shape, max_denom)

        self.curval = self.mean_sqr.curval - self.mean.curval**2

    def updates(self):
        """
        :returns: the symbolic updates necessary to refresh the variance estimate
        :rtype: dict Variable -> Variable
        """
        rval = {}
        rval.update(self.mean.updates())
        rval.update(self.mean_sqr.updates())
        return rval

    def _instance_initialize(self, obj):
        obj.mean.initialize()
        obj.mean_sqr.initialize()

class DynamicNormalizer(theano.Module):
    """
    Normalizes `input` using geometric-decaying estimates of the mean and variance.  The
    `output` should mean near zero, and variance near 1.
    """
    def __init__(self, input, input_shape, max_denom=100, eps=1.0e-8):
        super(DynamicNormalizer, self).__init__()
        self.input = input
        self.d_mean = exp_mean(input, input_shape, max_denom=max_denom)
        self.d_var = exp_var(input, input_shape, max_denom=max_denom)
        self.output = (input - self.d_mean.curval) / theano.tensor.sqrt(self.d_var.curval+eps)

    def updates(self):
        rval = {}
        rval.update(self.d_mean.updates())
        rval.update(self.d_var.updates())
        return rval

    def _instance_initialize(self, obj):
        obj.d_mean.initialize()
        obj.d_var.initialize()