diff pylearn/algorithms/logistic_regression.py @ 537:b054271b2504

new file structure layout, factories, etc.
author James Bergstra <bergstrj@iro.umontreal.ca>
date Wed, 12 Nov 2008 21:57:54 -0500
parents algorithms/logistic_regression.py@c7ce66b4e8f4
children 85d3300c9a9c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pylearn/algorithms/logistic_regression.py	Wed Nov 12 21:57:54 2008 -0500
@@ -0,0 +1,176 @@
+import sys, copy
+import theano
+from theano import tensor as T
+from theano.tensor import nnet
+from theano.compile import module
+from theano import printing, pprint
+from theano import compile
+
+import numpy as N
+
+from ..datasets import make_dataset
+from .minimizer import make_minimizer
+from .stopper import make_stopper
+
+class LogRegN(module.FancyModule):
+
+    def __init__(self, 
+            n_in=None, n_out=None,
+            input=None, target=None, 
+            w=None, b=None, 
+            l2=None, l1=None):
+        super(LogRegN, self).__init__() #boilerplate
+
+        self.n_in = n_in
+        self.n_out = n_out
+
+        self.input = input if input is not None else T.matrix()
+        self.target = target if target is not None else T.lvector()
+
+        self.w = w if w is not None else module.Member(T.dmatrix())
+        self.b = b if b is not None else module.Member(T.dvector())
+
+        #the params of the model are the ones we fit to the data
+        self.params = [p for p in [self.w, self.b] if p.owner is None]
+        
+        #the hyper-parameters of the model are not fit to the data
+        self.l2 = l2 if l2 is not None else module.Member(T.dscalar())
+        self.l1 = l1 if l1 is not None else module.Member(T.dscalar())
+
+        #here we actually build the model
+        self.linear_output = T.dot(self.input, self.w) + self.b
+        if 0:
+            self.softmax = nnet.softmax(self.linear_output)
+
+            self._max_pr, self.argmax = T.max_and_argmax(self.linear_output)
+            self._xent = self.target * T.log(self.softmax)
+        else:
+            (self._xent, self.softmax, self._max_pr, self.argmax) =\
+                    nnet.crossentropy_softmax_max_and_argmax_1hot(
+                    self.linear_output, self.target)
+
+        self.unregularized_cost = T.sum(self._xent)
+        self.l1_cost = self.l1 * T.sum(abs(self.w))
+        self.l2_cost = self.l2 * T.sum(self.w**2)
+        self.regularized_cost = self.unregularized_cost + self.l1_cost + self.l2_cost
+        self._loss_zero_one = T.mean(T.neq(self.argmax, self.target))
+
+        # METHODS
+        if 0: #TODO: PENDING THE BETTER IMPLEMENTATION ABOVE
+            self.predict = module.Method([self.input], self.argmax)
+            self.label_probs = module.Method([self.input], self.softmax)
+        self.validate = module.Method([self.input, self.target], 
+                [self._loss_zero_one, self.regularized_cost, self.unregularized_cost])
+
+    def _instance_initialize(self, obj):
+        obj.w = N.zeros((self.n_in, self.n_out))
+        obj.b = N.zeros(self.n_out)
+        obj.__pp_hide__ = ['params']
+
+def logistic_regression(n_in, n_out, l1, l2, minimizer=None):
+    if n_out == 2:
+        raise NotImplementedError()
+    else:
+        rval = LogRegN(n_in=n_in, n_out=n_out, l1=l1, l2=l2)
+        rval.minimizer = minimizer([rval.input, rval.target], rval.regularized_cost,
+                rval.params)
+        return rval.make()
+
+#TODO: grouping parameters by prefix does not play well with providing defaults. Think...
+class _fit_logreg_defaults(object):
+    minimizer_algo = 'dummy'
+    #minimizer_lr = 0.001
+    dataset = 'MNIST_1k'
+    l1 = 0.0
+    l2 = 0.0
+    batchsize = 8
+    verbose = 1
+
+from ..datasets import MNIST
+import sgd #TODO:  necessary to add it to factory list
+# consider pre-importing each file in algorithms, datasets (possibly with try/catch around each
+# import so that this import failure is ignored)
+
+def fit_logistic_regression_online(state, channel=lambda *args, **kwargs:None):
+    #use stochastic gradient descent
+    state.use_defaults(_fit_logreg_defaults)
+
+    dataset = make_dataset(**state.subdict(prefix='dataset_'))
+    train = dataset.train
+    valid = dataset.valid
+    test = dataset.test
+
+    logreg = logistic_regression(
+            n_in=train.x.shape[1],
+            n_out=dataset.n_classes,
+            l2=state.l2,
+            l1=state.l1,
+            minimizer=make_minimizer(**state.subdict(prefix='minimizer_')))
+
+    batchsize = state.batchsize
+    verbose = state.verbose
+    iter = [0]
+
+    def step():
+        # step by making a pass through the training set
+        for j in xrange(0,len(train.x)-batchsize+1,batchsize):
+            cost_j = logreg.minimizer.step_cost(train.x[j:j+batchsize], train.y[j:j+batchsize])
+            if verbose > 1:
+                print 'estimated train cost', cost_j
+        #TODO: consult iter[0] for periodic saving to cwd (model, minimizer, and stopper)
+
+    def check():
+        validate = logreg.validate(valid.x, valid.y)
+        if verbose > 0: 
+            print 'iter', iter[0], 'validate', validate
+            sys.stdout.flush()
+        iter[0] += 1
+        return validate[0]
+
+    def save():
+        return copy.deepcopy(logreg)
+
+    stopper = make_stopper(**state.subdict(prefix='stopper_'))
+    stopper.find_min(step, check, save)
+
+    state.train_01, state.train_rcost, state.train_cost = logreg.validate(train.x, train.y)
+    state.valid_01, state.valid_rcost, state.valid_cost = logreg.validate(valid.x, valid.y)
+    state.test_01, state.test_rcost, state.test_cost = logreg.validate(test.x, test.y)
+
+    state.n_train = len(train.y)
+    state.n_valid = len(valid.y)
+    state.n_test = len(test.y)
+
+class LogReg2(module.FancyModule):
+    def __init__(self, input=None, targ=None, w=None, b=None, lr=None, regularize=False):
+        super(LogReg2, self).__init__() #boilerplate
+
+        self.input = input if input is not None else T.matrix('input')
+        self.targ = targ if targ is not None else T.lcol()
+
+        self.w = w if w is not None else module.Member(T.dmatrix())
+        self.b = b if b is not None else module.Member(T.dvector())
+        self.lr = lr if lr is not None else module.Member(T.dscalar())
+
+        self.params = [p for p in [self.w, self.b] if p.owner is None]
+
+        output = nnet.sigmoid(T.dot(self.x, self.w) + self.b)
+        xent = -self.targ * T.log(output) - (1.0 - self.targ) * T.log(1.0 - output)
+        sum_xent = T.sum(xent)
+
+        self.output = output
+        self.xent = xent
+        self.sum_xent = sum_xent
+        self.cost = sum_xent
+
+        #define the apply method
+        self.pred = (T.dot(self.input, self.w) + self.b) > 0.0
+        self.apply = module.Method([self.input], self.pred)
+
+        #if this module has any internal parameters, define an update function for them
+        if self.params:
+            gparams = T.grad(sum_xent, self.params)
+            self.update = module.Method([self.input, self.targ], sum_xent,
+                                        updates = dict((p, p - self.lr * g) for p, g in zip(self.params, gparams)))
+
+