/// <summary>
 /// Predicts the target for a new batch of instances of the data using the algorithm's trained model.
 /// </summary>
 /// <param name="instances">New instances</param>
 /// <returns>Predictions</returns>
 public List<double> Predict(InsightMatrix instances)
 {
     instances = instances.InsertColumn(0, 1);
     return (instances * Theta.ToColumnMatrix()).Column(0).ToList();
 }
        /// <summary>
        /// Performs linear regression on the input data.
        /// </summary>
        /// <param name="data">Training data</param>
        /// <param name="alpha">The learning rate for the algorithm</param>
        /// <param name="lambda">The regularization weight for the algorithm</param>
        /// <param name="iters">The number of training iterations to run</param>
        /// <returns>Tuple containing the parameter and error vectors</returns>
        private Tuple<InsightVector, InsightVector> PerformLinearRegression(InsightMatrix data, double alpha,
            double lambda, int iters)
        {
            // First add a ones column for the intercept term
            data = data.InsertColumn(0, 1);

            // Split the data into training data and the target variable
            var X = data.RemoveColumn(data.ColumnCount - 1);
            var y = data.Column(data.ColumnCount - 1);

            // Initialize several variables needed for the computation
            var theta = new InsightVector(X.ColumnCount);
            var temp = new InsightVector(X.ColumnCount);
            var error = new InsightVector(iters);

            // Perform gradient descent on the parameters theta
            for (int i = 0; i < iters; i++)
            {
                var delta = (X * theta.ToColumnMatrix()) - y.ToColumnMatrix();

                for (int j = 0; j < theta.Count; j++)
                {
                    var inner = delta.Multiply(X.SubMatrix(0, X.RowCount, j, 1));

                    if (j == 0)
                    {
                        temp[j] = theta[j] - ((alpha / X.RowCount) * inner.Column(0).Sum());
                    }
                    else
                    {
                        var reg = (2 * lambda) * theta[j];
                        temp[j] = theta[j] - ((alpha / X.RowCount) * inner.Column(0).Sum()) + reg;
                    }
                }

                theta = temp.Clone();
                error[i] = ComputeError(X, y, theta, lambda);
            }

            return new Tuple<InsightVector, InsightVector>(theta, error);
        }
 /// <summary>
 /// Classifies a new batch of instances of the data using the algorithm's trained model.
 /// </summary>
 /// <param name="instances">New instances</param>
 /// <returns>Classifications</returns>
 public List<int> Classify(InsightMatrix instances)
 {
     instances = instances.InsertColumn(0, 1);
     var probability = Sigmoid((instances * Theta.ToColumnMatrix()).Column(0));
     return probability.Select(x => x >= 0.5 ? 1 : 0).ToList();
 }