@Override
	public void train(List<Instance> instances) {
		this.D = this.getFeautreDimension(instances);
		
		// initialize w and eta
		double[] w = new double[this.D];
		double[] eta = new double[this.D];
		double[] I = new double[this.D];
		for(int i=0; i<this.D; i++){
			w[i] = 0;
			eta[i] = this._eta0;
			I[i] = 1;
		}
		
		// populate sum variables
		int N = instances.size();
		
		
		
		// stochastic gradient descent iteration
		double[] sum_f2 = new double[this.D];	// sum of f^2
		for(int i=0; i<this.D; i++){
			sum_f2[i] = 0;
		}
		
		//System.out.println(D);
		double[] dl = new double[this.D];
		
		for(int iter=0; iter < this._iter * N; iter++){
			Instance curInstance = instances.get(iter%N);
			
			// extract y from sample
			int y = ((ClassificationLabel)curInstance.getLabel()).getValue();
			
			// extract x from sample
			double[] x = new double[D];
			FeatureVector fv = curInstance.getFeatureVector();
			Set<Integer> fIndexes = fv.getFeatureIndexes();
			for(int fi : fIndexes){
				x[fi-1] = fv.get(fi);	// feature index is 1-based
			}
			
			
			double wx = 0;
			for(int d=0; d<this.D; d++){
				wx += w[d]*x[d];
			}
			
			for(int d=0; d<this.D; d++){
				// calculate gradient of likelihood for each dimension
				dl[d] = y * g(-wx) * x[d] + (1-y) * g(wx) * (-x[d]);
				
				// update sum_f2
				sum_f2[d] = sum_f2[d] + dl[d]*dl[d];
				
				// update eta
				eta[d] = this._eta0 / (Math.sqrt(I[d] + sum_f2[d]));
				
				// update w
				w[d] = w[d] + eta[d] * dl[d];
			}
			
		}
		
		// save weights of the model
		this._w = new double[this.D];
		for(int d=0; d<this.D; d++)
			this._w[d] = w[d];
		
//		for(int d=0; d<this.D; d++)
//			System.out.print(this._fmt.format(w[d]) + "\t");
//		System.out.println();
	}