# HG changeset patch # User Olivier Delalleau # Date 1243952040 14400 # Node ID fc85ce33b5188094358da956060ee4a8e37068ae # Parent 4d22396678e6003c295d2c12390e3398727f73b0 FillMissing can now impute missing values by an array instead of a single constant diff -r 4d22396678e6 -r fc85ce33b518 pylearn/algorithms/sandbox/DAA_inputs_groups.py --- a/pylearn/algorithms/sandbox/DAA_inputs_groups.py Mon Jun 01 17:44:56 2009 -0400 +++ b/pylearn/algorithms/sandbox/DAA_inputs_groups.py Tue Jun 02 10:14:00 2009 -0400 @@ -7,7 +7,7 @@ from theano.tensor.nnet import sigmoid from pylearn.sandbox.scan_inputs_groups import scaninputs, scandotdec, scandotenc, scannoise, scanbiasdec, \ - scanmaskenc,scanmaskdec, fill_missing_with_zeros, mask_gradient + scanmaskenc,scanmaskdec, FillMissing, mask_gradient from pylearn.algorithms import cost from pylearn.algorithms.logistic_regression import LogRegN @@ -47,18 +47,21 @@ in_size=None, auxin_size= None, n_hid=1, regularize = False, tie_weights = False, hid_fn = 'sigmoid_act', reconstruction_cost_function=cost.cross_entropy, interface = True, - ignore_missing=False, + ignore_missing=None, **init): """ :param regularize: WRITEME :param tie_weights: WRITEME :param hid_fn: WRITEME :param reconstruction_cost: Should return one cost per example (row) - :param ignore_missing: if True, the input will be scanned in order to - detect missing values, and these values will be replaced by zeros. - Also, the reconstruction cost's gradient will be computed only on - non missing components. - If False, the presence of missing values may cause crashes or other + :param ignore_missing: if not None, the input will be scanned in order + to detect missing values, and these values will be replaced. Also, + the reconstruction cost's gradient will be computed only on non + missing components. The value of this parameter indicates how to + replace missing values: + - some numpy.ndarray: value of this array at the same index + - a constant: this same value everywhere + If None, the presence of missing values may cause crashes or other weird and unexpected behavior. Please note that this option only affects the permanent input, not auxilary ones (that should never contain missing values). In fact, @@ -91,8 +94,8 @@ ### DECLARE MODEL VARIABLES and default self.input = input - if self.ignore_missing and self.input is not None: - no_missing = fill_missing_with_zeros(self.input) + if self.ignore_missing is not None and self.input is not None: + no_missing = FillMissing(self.ignore_missing)(self.input) self.input = no_missing[0] # Missing values replaced by zeros. self.input_missing_mask = no_missing[1] # Missingness pattern. else: @@ -152,7 +155,7 @@ container.hidden = self.hid_fn(container.hidden_activation) self.define_propdown(container, idx_list , auxinput) container.rec = self.hid_fn(container.rec_activation) - if self.ignore_missing and self.input is not None: + if self.ignore_missing is not None and self.input is not None: # Apply mask to gradient to ensure we do not backpropagate on the # cost computed on missing inputs (that were replaced with zeros). container.rec = mask_gradient(container.rec, @@ -340,7 +343,7 @@ regularize = False, tie_weights = False, hid_fn = 'sigmoid_act', reconstruction_cost_function=cost.cross_entropy, n_out = 2, target = None, debugmethod = False, totalupdatebool=False, - ignore_missing=False, + ignore_missing=None, **init): super(StackedDAAig, self).__init__() diff -r 4d22396678e6 -r fc85ce33b518 pylearn/sandbox/scan_inputs_groups.py --- a/pylearn/sandbox/scan_inputs_groups.py Mon Jun 01 17:44:56 2009 -0400 +++ b/pylearn/sandbox/scan_inputs_groups.py Tue Jun 02 10:14:00 2009 -0400 @@ -567,23 +567,37 @@ """ Given an input, output two elements: - a copy of the input where missing values (NaN) are replaced by some - constant (zero by default) + other value (zero by default) - a mask of the same size and type as input, where each element is zero iff the corresponding input is missing - Currently, the gradient is computed as if the input value was really zero. - It may be safer to replace the gradient w.r.t. missing values with either - zeros or missing values (?). + The 'fill_with' parameter may either be: + - a scalar: all missing values are replaced with this value + - a Numpy array: a missing value is replaced by the value in this array + at the same position (ignoring the first k dimensions if 'fill_with' + has k less dimensions than the input) + Currently, the gradient is computed as if the input value was really what + it was replaced with. It may be safer to replace the gradient w.r.t. + missing values with either zeros or missing values (?). """ - def __init__(self, constant_val=0): + def __init__(self, fill_with=0): super(Op, self).__init__() - self.constant_val = constant_val + self.fill_with = fill_with + self.fill_with_is_array = isinstance(self.fill_with, numpy.ndarray) def __eq__(self, other): - return type(self) == type(other) and (self.constant_val == other.constant_val) + return (type(self) == type(other) and + self.fill_with_is_array == other.fill_with_is_array and + ((self.fill_with_is_array and + (self.fill_with == other.fill_with).all()) or + self.fill_with == other.fill_with)) - def __hash__(self): - return hash(type(self))^hash(self.constant_val) + def __hash__(self): + if self.fill_with_is_array: + fill_hash = self.fill_with.__hash__() + else: + fill_hash = hash(self.fill_with) + return hash(type(self))^hash(self.fill_with_is_array)^fill_hash def make_node(self, input): return Apply(self, [input], [input.type(), input.type()]) @@ -595,9 +609,15 @@ mask = output_storage[1] mask[0] = numpy.ones(input.shape) mask = mask[0] + if self.fill_with_is_array: + ignore_k = len(out.shape) - len(self.fill_with.shape) + assert ignore_k >= 0 for (idx, v) in numpy.ndenumerate(out): if numpy.isnan(v): - out[idx] = self.constant_val + if self.fill_with_is_array: + out[idx] = self.fill_with[idx[ignore_k:]] + else: + out[idx] = self.fill_with mask[idx] = 0 def grad(self, inputs, (out_grad, mask_grad, )):