Mercurial > pylearn
changeset 390:efb797c5efc0
First non-crashing draft of LinearRegression
author | Yoshua Bengio <bengioy@iro.umontreal.ca> |
---|---|
date | Tue, 08 Jul 2008 17:49:44 -0400 |
parents | a474341861fa |
children | b4015b07ab17 |
files | linear_regression.py |
diffstat | 1 files changed, 66 insertions(+), 52 deletions(-) [+] |
line wrap: on
line diff
--- a/linear_regression.py Tue Jul 08 02:27:00 2008 -0400 +++ b/linear_regression.py Tue Jul 08 17:49:44 2008 -0400 @@ -4,16 +4,35 @@ the use of theano. """ -from pylearn import OfflineLearningAlgorithm +from pylearn.learner import OfflineLearningAlgorithm from theano import tensor as T +from theano.others_ops import prepend_1_to_each_row from theano.scalar import as_scalar from common.autoname import AutoName +import theano +import numpy class LinearRegression(OfflineLearningAlgorithm): """ Implement linear regression, with or without L2 regularization (the former is called Ridge Regression and the latter Ordinary Least Squares). + Usage: + + linear_regressor=LinearRegression(L2_regularizer=0.1) + linear_predictor=linear_regression(training_set) + all_results_dataset=linear_predictor(test_set) # creates a dataset with "output" and "squared_error" field + outputs = linear_predictor.compute_outputs(inputs) # inputs and outputs are numpy arrays + outputs, errors = linear_predictor.compute_outputs_and_errors(inputs,targets) + errors = linear_predictor.compute_errors(inputs,targets) + mse = linear_predictor.compute_mse(inputs,targets) + + + + The training_set must have fields "input" and "target". + The test_set must have field "input", and needs "target" if + we want to compute the squared errors. + The predictor parameters are obtained analytically from the training set. Training can proceed sequentially (with multiple calls to update with different disjoint subsets of the training sets). After each call to @@ -52,12 +71,24 @@ - 'squared_error' (optionally produced by learned model if 'target' is provided) = example-wise squared error """ - def __init__(self, L2_regularizer=0): - self.predictor = LinearPredictor(None,None + def __init__(self, L2_regularizer=0,minibatch_size=10000): self.L2_regularizer=L2_regularizer - self._XtX = T.matrix('XtX') - self._XtY = T.matrix('XtY') - self._extended_input = T.prepend_one_to_each_row(self._input) + self.equations = LinearRegressionEquations() + self.minibatch_size=1000 + + def __call__(self,trainset): + first_example = trainset[0] + n_inputs = first_example['input'].size + n_outputs = first_example['target'].size + XtX = numpy.zeros((n_inputs+1,n_inputs+1)) + XtY = numpy.zeros((n_inputs+1,n_outputs)) + for i in xrange(n_inputs): + XtX[i+1,i+1]=self.L2_regularizer + mbs=min(self.minibatch_size,len(trainset)) + for inputs,targets in trainset.minibatches(["input","target"],minibatch_size=mbs): + XtX,XtY=self.equations.update(XtX,XtY,inputs,targets) + theta=numpy.linalg.solve(XtX,XtY) + return LinearPredictor(theta) class LinearPredictorEquations(AutoName): inputs = T.matrix() # minibatchsize x n_inputs @@ -76,22 +107,37 @@ def fn(input_vars,output_vars): return staticmethod(theano.function(input_vars,output_vars, linker=linker)) - cls.compute_outputs = fn([inputs,theta],[outputs]) - cls.compute_errors = fn([outputs,targets],[squared_errors]) + cls.compute_outputs = fn([cls.inputs,cls.theta],[cls.outputs]) + cls.compute_errors = fn([cls.outputs,cls.targets],[cls.squared_errors]) cls.__compiled = True - def __init__(self) + def __init__(self): self.compile() class LinearRegressionEquations(LinearPredictorEquations): P = LinearPredictorEquations XtX = T.matrix() # (n_inputs+1) x (n_inputs+1) XtY = T.matrix() # (n_inputs+1) x n_outputs - extended_input = T.prepend_scalar_to_each_row(1.,P.inputs) - new_XtX = add_inplace(XtX,T.dot(extended_input.T,extended_input)) - new_XtY = add_inplace(XtY,T.dot(extended_input.T,P.targets)) - new_theta = T.Cholesky_solve_inplace(P.theta,XtX,XtY) # solve linear system XtX theta = XtY + extended_input = prepend_1_to_each_row(P.inputs) + new_XtX = T.add_inplace(XtX,T.dot(extended_input.T,extended_input)) + new_XtY = T.add_inplace(XtY,T.dot(extended_input.T,P.targets)) + + __compiled = False + + @classmethod + def compile(cls,linker='c|py'): + if cls.__compiled: + return + def fn(input_vars,output_vars): + return staticmethod(theano.function(input_vars,output_vars, linker=linker)) + + cls.update = fn([cls.XtX,cls.XtY,cls.P.inputs,cls.P.targets],[cls.new_XtX,cls.new_XtY]) + + cls.__compiled = True + + def __init__(self): + self.compile() class LinearPredictor(object): """ @@ -103,15 +149,18 @@ self.theta=theta self.n_inputs=theta.shape[0]-1 self.n_outputs=theta.shape[1] - self.predict_equations = LinearPredictorEquations() + self.equations = LinearPredictorEquations() def compute_outputs(self,inputs): - return self.predict_equations.compute_outputs(inputs,self.theta) + return self.equations.compute_outputs(inputs,self.theta) def compute_errors(self,inputs,targets): - return self.predict_equations.compute_errors(self.compute_outputs(inputs),targets) + return self.equations.compute_errors(self.compute_outputs(inputs),targets) def compute_outputs_and_errors(self,inputs,targets): outputs = self.compute_outputs(inputs) - return [outputs,self.predict_equations.compute_errors(outputs,targets)] + return [outputs,self.equations.compute_errors(outputs,targets)] + def compute_mse(self,inputs,targets): + errors = self.compute_errors(inputs,targets) + return numpy.sum(errors)/errors.size def __call__(self,dataset,output_fieldnames=None,cached_output_dataset=False): assert dataset.hasFields(["input"]) @@ -137,38 +186,3 @@ return ds - self._XtX = T.matrix('XtX') - self._XtY = T.matrix('XtY') - self._extended_input = T.prepend_one_to_each_row(self._input) - self._output = T.dot(self._input,self._W.T) + self._b # (n_examples , n_outputs) matrix - self._squared_error = T.sum_within_rows(T.sqr(self._output-self._target)) # (n_examples ) vector - self._regularizer = self._L2_regularizer * T.dot(self._W,self._W) - self._new_XtX = add_inplace(self._XtX,T.dot(self._extended_input.T,self._extended_input)) - self._new_XtY = add_inplace(self._XtY,T.dot(self._extended_input.T,self._target)) - self._new_theta = T.solve_inplace(self._theta,self._XtX,self._XtY) - - def allocate(self,dataset): - dataset_n_inputs = dataset["input"].shape[1] - dataset_n_outputs = dataset["target"].shape[1] - if not self._n_inputs: - self._n_inputs = dataset_n_inputs - self._n_outputs = dataset_n_outputs - self.XtX = numpy.zeros((1+self._n_inputs,1+self._n_inputs)) - self.XtY = numpy.zeros((1+self._n_inputs,self._n_outputs)) - self.theta = numpy.zeros((self._n_outputs,1+self._n_inputs)) - self.forget() - elif self._n_inputs!=dataset_n_inputs or self._n_outputs!=dataset_n_outputs: - # if the input or target changes dimension on the fly, we resize and forget everything - self.forget() - - def forget(self): - if self._n_inputs and self._n_outputs: - self.XtX.resize((1+self.n_inputs,1+self.n_inputs)) - self.XtY.resize((1+self.n_inputs,self.n_outputs)) - self.XtX.data[:,:]=0 - self.XtY.data[:,:]=0 - numpy.diag(self.XtX.data)[1:]=self.L2_regularizer - - def __call__(self,dataset): - -