/// <summary>
 /// Computes the total error of the solution with parameters theta.
 /// </summary>
 /// <param name="X">Training data</param>
 /// <param name="y">Target variable</param>
 /// <param name="theta">Model parameters</param>
 /// <param name="lambda">Regularization weight</param
 /// <returns>Solution error</returns>
 private double ComputeError(InsightMatrix X, InsightVector y, InsightVector theta, double lambda)
 {
     var inner = ((X * theta.ToColumnMatrix()) - y.ToColumnMatrix()).Power(2);
     var thetaSub = theta.SubVector(1, theta.Count - 1);
     var reg = lambda * thetaSub.Multiply(thetaSub).Sum();
     return (inner.Column(0).Sum() / (2 * X.RowCount)) + reg;
 }
        /// <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>
 /// Computes the total error of the solution with parameters theta.
 /// </summary>
 /// <param name="X">Training data</param>
 /// <param name="y">Target variable</param>
 /// <param name="theta">Model parameters</param>
 /// <param name="lambda">Regularization weight</param
 /// <returns>Solution error</returns>
 private double ComputeError(InsightMatrix X, InsightVector y, InsightVector theta, double lambda)
 {
     var first = y.Multiply(Sigmoid((X * theta.ToColumnMatrix()).Column(0)).Log());
     var second = (1 - y).Multiply(1 - Sigmoid((X * theta.ToColumnMatrix()).Column(0)).Log());
     var thetaSub = theta.SubVector(1, theta.Count - 1);
     var reg = (lambda / 2 * X.RowCount) * thetaSub.Power(2).Sum();
     return (first - second).Sum() / X.RowCount + reg;
 }