Quick Info

Category: Machine Learning
Time Complexity: O(n × m)
Space Complexity: O(n)
Input Type: Numerical Data

Linear Regression

Description

Linear Regression is a fundamental machine learning algorithm used for predicting a continuous output variable based on one or more input features. It works by finding the best-fitting linear relationship between the input and output variables.

How It Works

  1. Model Representation:
    • y = mx + b (Simple Linear Regression)
    • y = β₀ + β₁x₁ + β₂x₂ + ... + βₙxₙ (Multiple Linear Regression)
    • where y is the predicted value, x are features, and β are coefficients
  2. Cost Function:
    • Mean Squared Error (MSE): J(θ) = (1/2m)Σ(hᵢ - yᵢ)²
    • where h is the prediction and y is the actual value
  3. Gradient Descent:
    • Update coefficients: θⱼ = θⱼ - α∂J/∂θⱼ
    • where α is the learning rate

Complexity Analysis

Time Complexity

  • Training: O(n × m × i)
    • n: number of features
    • m: number of training examples
    • i: number of iterations
  • Prediction: O(n)
    • Linear time with respect to number of features

Space Complexity

  • Model Storage: O(n)
    • Coefficients: O(n)
    • Intermediate calculations: O(m)

Visualization

Click 'Generate Data' to begin

Implementation


import numpy as np

class LinearRegression:
    def __init__(self, learning_rate=0.01, n_iterations=1000):
        self.learning_rate = learning_rate
        self.n_iterations = n_iterations
        self.weights = None
        self.bias = None
        self.history = {'loss': []}

    def fit(self, X, y):
        # Initialize parameters
        n_samples, n_features = X.shape
        self.weights = np.zeros(n_features)
        self.bias = 0
        
        # Gradient descent
        for _ in range(self.n_iterations):
            # Forward pass
            y_predicted = self._predict(X)
            
            # Calculate gradients
            dw = (1/n_samples) * np.dot(X.T, (y_predicted - y))
            db = (1/n_samples) * np.sum(y_predicted - y)
            
            # Update parameters
            self.weights -= self.learning_rate * dw
            self.bias -= self.learning_rate * db
            
            # Calculate and store loss
            loss = self._mse(y, y_predicted)
            self.history['loss'].append(loss)

    def predict(self, X):
        return self._predict(X)

    def _predict(self, X):
        return np.dot(X, self.weights) + self.bias

    def _mse(self, y_true, y_pred):
        return np.mean((y_true - y_pred) ** 2)

# Example usage
if __name__ == "__main__":
    # Generate sample data
    np.random.seed(0)
    X = 2 * np.random.rand(100, 1)
    y = 4 + 3 * X + np.random.randn(100, 1)

    # Create and train model
    model = LinearRegression(learning_rate=0.01, n_iterations=1000)
    model.fit(X, y)

    # Make predictions
    X_test = np.array([[0], [2]])
    y_pred = model.predict(X_test)

    print("Weights:", model.weights)
    print("Bias:", model.bias)
    print("Predictions:", y_pred)
                                        

#include <vector>
#include <iostream>
#include <cmath>
#include <random>

class LinearRegression {
private:
    double learning_rate;
    int n_iterations;
    std::vector<double> weights;
    double bias;

    double predict(const std::vector<double>& x) {
        double y_pred = bias;
        for (size_t i = 0; i < x.size(); ++i) {
            y_pred += weights[i] * x[i];
        }
        return y_pred;
    }

public:
    LinearRegression(double lr = 0.01, int iterations = 1000) 
        : learning_rate(lr), n_iterations(iterations) {}

    void fit(const std::vector<std::vector<double>>& X, 
            const std::vector<double>& y) {
        int n_samples = X.size();
        int n_features = X[0].size();

        // Initialize parameters
        weights = std::vector<double>(n_features, 0.0);
        bias = 0.0;

        // Gradient descent
        for (int iter = 0; iter < n_iterations; ++iter) {
            double dw_sum = 0.0;
            std::vector<double> dw(n_features, 0.0);
            double db = 0.0;

            // Calculate gradients
            for (int i = 0; i < n_samples; ++i) {
                double y_pred = predict(X[i]);
                double error = y_pred - y[i];

                for (int j = 0; j < n_features; ++j) {
                    dw[j] += error * X[i][j];
                }
                db += error;
            }

            // Update parameters
            for (int j = 0; j < n_features; ++j) {
                weights[j] -= learning_rate * dw[j] / n_samples;
            }
            bias -= learning_rate * db / n_samples;
        }
    }

    std::vector<double> predict(const std::vector<std::vector<double>>& X) {
        std::vector<double> predictions;
        for (const auto& x : X) {
            predictions.push_back(predict(x));
        }
        return predictions;
    }

    std::vector<double> get_weights() const { return weights; }
    double get_bias() const { return bias; }
};

int main() {
    // Generate sample data
    std::random_device rd;
    std::mt19937 gen(rd());
    std::normal_distribution<double> noise(0, 0.1);

    std::vector<std::vector<double>> X;
    std::vector<double> y;

    for (int i = 0; i < 100; ++i) {
        double x = i * 0.02;
        std::vector<double> x_i = {x};
        double y_i = 2 * x + 1 + noise(gen);
        X.push_back(x_i);
        y.push_back(y_i);
    }

    // Create and train model
    LinearRegression model(0.01, 1000);
    model.fit(X, y);

    // Print results
    std::cout << "Weight: " << model.get_weights()[0] << std::endl;
    std::cout << "Bias: " << model.get_bias() << std::endl;

    return 0;
}
                                        

using System;
using System.Linq;
using System.Collections.Generic;

public class LinearRegression
{
    private double[] weights;
    private double bias;
    private readonly double learningRate;
    private readonly int nIterations;

    public LinearRegression(double learningRate = 0.01, int nIterations = 1000)
    {
        this.learningRate = learningRate;
        this.nIterations = nIterations;
    }

    public void Fit(double[][] X, double[] y)
    {
        int nSamples = X.Length;
        int nFeatures = X[0].Length;

        // Initialize parameters
        weights = new double[nFeatures];
        bias = 0.0;

        // Gradient descent
        for (int iter = 0; iter < nIterations; iter++)
        {
            // Forward pass
            var predictions = X.Select(x => Predict(x)).ToArray();

            // Calculate gradients
            var dw = new double[nFeatures];
            double db = 0.0;

            for (int i = 0; i < nSamples; i++)
            {
                double error = predictions[i] - y[i];
                for (int j = 0; j < nFeatures; j++)
                {
                    dw[j] += error * X[i][j];
                }
                db += error;
            }

            // Update parameters
            for (int j = 0; j < nFeatures; j++)
            {
                weights[j] -= learningRate * dw[j] / nSamples;
            }
            bias -= learningRate * db / nSamples;
        }
    }

    public double Predict(double[] X)
    {
        return X.Zip(weights, (x, w) => x * w).Sum() + bias;
    }

    public double[] GetWeights() => weights;
    public double GetBias() => bias;

    public static void Main()
    {
        // Generate sample data
        var random = new Random(0);
        var X = Enumerable.Range(0, 100)
            .Select(i => new[] { i * 0.02 })
            .ToArray();
        var y = X.Select(x => 2 * x[0] + 1 + random.NextDouble() * 0.1)
            .ToArray();

        // Create and train model
        var model = new LinearRegression(0.01, 1000);
        model.Fit(X, y);

        // Print results
        Console.WriteLine($"Weight: {model.GetWeights()[0]}");
        Console.WriteLine($"Bias: {model.GetBias()}");

        // Make predictions
        var X_test = new[] { new[] { 0.0 }, new[] { 1.0 } };
        foreach (var x in X_test)
        {
            Console.WriteLine($"Prediction for x={x[0]}: {model.Predict(x)}");
        }
    }
}
                                        

Advantages and Disadvantages

Advantages

  • Simple and easy to understand
  • Fast training and prediction
  • Works well for linearly separable data
  • Provides interpretable results

Disadvantages

  • Assumes linear relationship between variables
  • Sensitive to outliers
  • May underfit complex patterns
  • Assumes independence of features