func (l *LeastSquares) Learn() error {
	if l.trainingSet == nil || l.expectedResults == nil {
		err := fmt.Errorf("ERROR: Attempting to learn with no training examples!\n")
		fmt.Fprintf(l.Output, err.Error())
		return err
	}
	examples := len(l.trainingSet)
	if examples == 0 || len(l.trainingSet[0]) == 0 {
		err := fmt.Errorf("ERROR: Attempting to learn with no training examples!\n")
		fmt.Fprintf(l.Output, err.Error())
		return err
	}
	if len(l.expectedResults) == 0 {
		err := fmt.Errorf("ERROR: Attempting to learn with no expected results! This isn't an unsupervised model!! You'll need to include data before you learn :)\n")
		fmt.Fprintf(l.Output, err.Error())
		return err
	}
	fmt.Fprintf(l.Output, "Training:\n\tModel: Logistic (Binary) Classification\n\tOptimization Method: %v\n\tTraining Examples: %v\n\tFeatures: %v\n\tLearning Rate α: %v\n\tRegularization Parameter λ: %v\n...\n\n", l.method, examples, len(l.trainingSet[0]), l.alpha, l.regularization)
	var err error
	if l.method == base.BatchGA {
		err = base.GradientAscent(l)
	} else if l.method == base.StochasticGA {
		err = base.StochasticGradientAscent(l)
	} else {
		err = fmt.Errorf("Chose a training method not implemented for LeastSquares regression")
	}
	if err != nil {
		fmt.Fprintf(l.Output, "\nERROR: Error while learning –\n\t%v\n\n", err)
		return err
	}
	fmt.Fprintf(l.Output, "Training Completed.\n%v\n\n", l)
	return nil
}
Learn takes the struct's dataset and expected results and runs
batch gradient descent on them, optimizing theta so you can
predict based on those results
package linear
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"os"
	"github.com/cdipaolo/goml/base"
)
type LeastSquares struct {
	alpha          float64
	regularization float64
	maxIterations  int
	method base.OptimizationMethod
	trainingSet     [][]float64
	expectedResults []float64
	Parameters []float64 `json:"theta"`
	Output io.Writer
}
func NewLeastSquares(method base.OptimizationMethod, alpha, regularization float64, maxIterations int, trainingSet [][]float64, expectedResults []float64, features ...int) *LeastSquares {
	var params []float64
	if len(features) != 0 {
		params = make([]float64, features[0]+1)
	} else if trainingSet == nil || len(trainingSet) == 0 {
		params = []float64{}
	} else {
		params = make([]float64, len(trainingSet[0])+1)
	}
	return &LeastSquares{
		alpha:          alpha,
		regularization: regularization,
		maxIterations:  maxIterations,
		method: method,
		trainingSet:     trainingSet,
		expectedResults: expectedResults,
		Parameters: params,
		Output: os.Stdout,
	}
}
func (l *LeastSquares) UpdateTrainingSet(trainingSet [][]float64, expectedResults []float64) error {
	if len(trainingSet) == 0 {
		return fmt.Errorf("Error: length of given training set is 0! Need data!")
	}
	if len(expectedResults) == 0 {
		return fmt.Errorf("Error: length of given result data set is 0! Need expected results!")
	}
	l.trainingSet = trainingSet
	l.expectedResults = expectedResults
	return nil
}
func (l *LeastSquares) UpdateLearningRate(a float64) {
	l.alpha = a
}
func (l *LeastSquares) LearningRate() float64 {
	return l.alpha
}
func (l *LeastSquares) Examples() int {
	return len(l.trainingSet)
}
func (l *LeastSquares) MaxIterations() int {
	return l.maxIterations
}
func (l *LeastSquares) Predict(x []float64, normalize ...bool) ([]float64, error) {
	if len(x)+1 != len(l.Parameters) {
		return nil, fmt.Errorf("Error: Parameter vector should be 1 longer than input vector!\n\tLength of x given: %v\n\tLength of parameters: %v\n", len(x), len(l.Parameters))
	}
	if len(normalize) != 0 && normalize[0] {
		base.NormalizePoint(x)
	}
	sum := l.Parameters[0]
	for i := range x {
		sum += x[i] * l.Parameters[i+1]
	}
	return []float64{sum}, nil
}