changeset 719:573e3370d0fa

Merged
author Olivier Delalleau <delallea@iro>
date Mon, 25 May 2009 11:45:37 -0400
parents 88f5b75a4afe (diff) fb9fb142098f (current diff)
children 0594cba02fa8
files pylearn/algorithms/sandbox/DAA_inputs_groups.py pylearn/sandbox/scan_inputs_groups.py
diffstat 2 files changed, 100 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/pylearn/algorithms/sandbox/DAA_inputs_groups.py	Sun May 24 17:16:38 2009 -0400
+++ b/pylearn/algorithms/sandbox/DAA_inputs_groups.py	Mon May 25 11:45:37 2009 -0400
@@ -46,12 +46,26 @@
     def __init__(self, input = None, auxinput = None,
                 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,**init):
+                reconstruction_cost_function=cost.cross_entropy, interface = True,
+                ignore_missing=False,
+                **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 will be computed only on non missing
+            components.
+            If False, 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,
+            in the current implementation, auxiliary inputs cannot be used when
+            this option is True.
+            Another side effect of the current crappy way it is implemented is
+            that the reconstruction cost is not properly computed.
         :todo: Default noise level for all daa levels
         """
         print '\t\t**** DAAig.__init__ ****'
@@ -72,12 +86,19 @@
         self.tie_weights = tie_weights
         self.reconstruction_cost_function = reconstruction_cost_function
         self.interface = interface
+        self.ignore_missing = ignore_missing
         
         assert hid_fn in ('sigmoid_act','tanh_act','softsign_act')
         self.hid_fn = eval(hid_fn)
         
         ### 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)
+            self.input = no_missing[0]  # Missing values replaced by zeros.
+            self.input_missing_mask = no_missing[1] # Missingness pattern.
+        else:
+            self.input_missing_mask = None
         self.noisy_input = None
         self.auxinput = auxinput
         self.idx_list = T.ivector('idx_list') if self.auxinput is not None else None
@@ -242,6 +263,18 @@
         return self.random.binomial(T.shape(self.input), 1, 1 - self.noise_level) * self.input
     
     def reconstruction_costs(self, rec):
+        if self.ignore_missing and self.input is not None:
+            # Note: the following code is very ugly. It is just a hack to
+            # ensure that the gradient w.r.t. missing coordinates is (close to)
+            # zero. It is neither efficient nor elegant.
+            # The idea is to put a very big negative value in the
+            # reconstruction for these missing inputs (whose target is 0), so
+            # that the gradient is 1/(1 - rec) ~= 0.
+            # This will in particular screw up the cost computations.
+            zero = rec * 0
+            rec = (rec * T.neq(self.input_missing_mask, zero) +
+                    (zero - 1e100) * T.eq(self.input_missing_mask, zero))
+            
         if (self.input is not None) and (self.auxinput is not None):
             return self.reconstruction_cost_function(T.join(1,self.input,scaninputs(self.idx_list,self.auxinput)), rec)
         if self.input is not None:
--- a/pylearn/sandbox/scan_inputs_groups.py	Sun May 24 17:16:38 2009 -0400
+++ b/pylearn/sandbox/scan_inputs_groups.py	Mon May 25 11:45:37 2009 -0400
@@ -560,3 +560,69 @@
 
 scanmaskenc=ScanMask(True)
 scanmaskdec=ScanMask(False)
+
+# TODO The classes FillMissing and MaskSelect below should probably be moved
+# to another (more appropriate) file.
+class FillMissing(Op):
+    """
+    Given an input, output two elements:
+        - a copy of the input where missing values (NaN) are replaced by some
+        constant (zero by default)
+        - a boolean (actually int8) mask of the same size as input, where each
+        element is True (i.e. 1) iff the corresponding input is not missing
+    """
+
+    def __init__(self, constant_val=0):
+        super(Op, self).__init__()
+        self.constant_val = constant_val
+
+    def __eq__(self, other):
+        return type(self) == type(other) and (self.constant_val == other.constant_val)
+
+	def __hash__(self):
+		return hash(type(self))^hash(self.constant_val)
+	
+    def make_node(self, input):
+        return Apply(self, [input], [input.type(), T.bmatrix()])
+
+    def perform(self, node, inputs, output_storage):
+        input = inputs[0]
+        out = output_storage[0]
+        out[0] = input.copy()
+        out = out[0]
+        mask = output_storage[1]
+        mask[0] = numpy.ones(input.shape, dtype = numpy.int8)
+        mask = mask[0]
+        for (idx, v) in numpy.ndenumerate(out):
+            if numpy.isnan(v):
+                out[idx] = self.constant_val
+                mask[idx] = 0
+
+fill_missing_with_zeros = FillMissing(0)
+
+class MaskSelect(Op):
+    """
+    Given an input x and a mask m (both vectors), outputs a vector that
+    contains all elements x[i] such that bool(m[i]) is True.
+    """
+
+    def __eq__(self, other):
+        return type(self) == type(other)
+
+	def __hash__(self):
+		return hash(type(self))
+	
+    def make_node(self, input, mask):
+        return Apply(self, [input, mask], [input.type()])
+
+    def perform(self, node, (input, mask), (output, )):
+        select = []
+        for (i, m) in enumerate(mask):
+            if bool(m):
+                select.append(i)
+        output[0] = numpy.zeros(len(select), dtype = input.dtype)
+        out = output[0]
+        for (i, j) in enumerate(select):
+            out[i] = input[j]
+
+mask_select = MaskSelect()