public void train() {
		Arrays.fill(theta, 0.0);
		theta0 = 0.0;
		
		double error = getCEError();
		
		double deltaEta = DELTA_ETA;
		if(lambda >= 100) {
			deltaEta /= lambda;
		}
		
		while(true) {
			double[] delta = new double[trainSet.nFeatures];
			double delta0 = 0.0;
			
			for(int i = 0; i < trainSet.m; i++) {
				Map<Integer, Double> x = trainSet.data.get(i);
				
				double h = modelOutput(x, theta0, theta);
				double diff = h - trainSet.classification[i];
				
				delta0 += diff;
				
				for(Entry<Integer, Double> pair : x.entrySet()) {
					delta[pair.getKey()] += diff * pair.getValue();
				}
			}
			
			double eta = 0.0;
			double currentError = error;
			
			while(true) {
				eta += deltaEta;
				
				if(eta >= 1.0) {
					eta = 1.0;
					break;
				}
				
				double[] thetai = Arrays.copyOf(theta, theta.length);
				mulArray(thetai, 1.0 - eta * lambda);
				thetai = scaleSubArrays(theta, delta, eta);
				
				double theta0i = theta0 - eta * delta0;
				
				double nextError = getCEError(theta0i, thetai);
				
				if(nextError > currentError) {
					eta -= deltaEta;
					break;
				}
				
				currentError = nextError;
			}
			
			mulArray(theta, 1.0 - eta * lambda);
			theta = scaleSubArrays(theta, delta, eta);
			theta0 -= eta * delta0;
			
			if(error - currentError <= 0.001) {
				break;
			}
			
			error = currentError;
		}
	}