Статьи

Программа для линейной регрессии с градиентным спуском

Я взял программу на Python, которая применяет градиентный спуск к линейной регрессии, и преобразовал ее в Ruby. Но сначала подведем итоги: мы используем линейную регрессию для численного прогнозирования.

Если x является входной (независимой) переменной, а y является выходной (зависимой) переменной, мы приходим к исходной формуле (уравнению), которая показывает математическое соотношение между ними.

Затем мы берем каждое значение x и вычисляем значение y, используя наше исходное уравнение. Разница между вычисленным значением y и фактическим значением y, соответствующим значению x, является ошибкой. Значения ошибок суммируются с помощью которого мы приходим к другому уравнению. Мы минимизируем это уравнение, используя алгоритм градиентного спуска, чтобы мы получили уравнение наилучшего соответствия.

Название изображения

Когда я искал в Интернете эту тему, я наткнулся на эту страницу « Введение в градиентный спуск и линейную регрессию » Мэтта Недрича, на которой он представляет пример Python. Программа находит наиболее подходящую линию для заданного набора данных значений x и y. Хороший улов для меня. Для практики я взял программу Мэтта и переписал ее на Ruby.

Мне понравилась статья в блоге Мэтта, поэтому я даю ее часть ниже, но с моими фрагментами Ruby.

Чтобы вычислить ошибку для данной линии, мы проведем итерацию по каждой точке (x, y) в нашем наборе данных и суммируем квадратные расстояния между значением y каждой точки и значением y линии-кандидата (вычисленным в mx + b).

Формально эта функция ошибок будет выглядеть так:

0.upto points.length-1 do |i|
    x = points[i][0]
    y = points[i][1]
    totalError += (y - (m * x + b)) ** 2
end
return totalError / points.length

Когда мы запустим поиск градиентного спуска, мы начнем с некоторого места на этой поверхности и двинемся вниз, чтобы найти линию с наименьшей ошибкой. Чтобы запустить градиентный спуск для этой функции ошибки, нам сначала нужно вычислить ее градиент. Градиент будет действовать как компас и всегда будет направлять нас вниз.

Чтобы вычислить его, нам нужно будет дифференцировать нашу функцию ошибок. Поскольку наша функция определяется двумя параметрами (m и b), нам потребуется вычислить частную производную для каждого из них. Производные работают так:

0.upto points.length-1 do |i|
    x = points[i][0]
    y = points[i][1]
    m_gradient += -(2/n) * x * (y - ((m_current * x) + b_current))
    b_gradient += -(2/n) * (y - ((m_current * x) + b_current))
end
new_m = m_current - (learningRate * m_gradient)
new_b = b_current - (learningRate * b_gradient)

Наконец, мы подошли к полной программе:

require 'csv'

# y = mx + b
# m is slope, b is y-intercept
def compute_error_for_line_given_points(b, m, points)
    totalError = 0
    0.upto points.length-1 do |i|
        x = points[i][0]
        y = points[i][1]
        totalError += (y - (m * x + b)) ** 2
    end
    return totalError / points.length
end

def step_gradient(b_current, m_current, points, learningRate)
    b_gradient = 0
    m_gradient = 0
    n = points.length + 0.0

    0.upto points.length-1 do |i|
        x = points[i][0]
        y = points[i][1]
        m_gradient += -(2/n) * x * (y - ((m_current * x) + b_current))
        b_gradient += -(2/n) * (y - ((m_current * x) + b_current))
    end
    new_m = m_current - (learningRate * m_gradient)
    new_b = b_current - (learningRate * b_gradient)
    return [new_b, new_m]
end

def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations)
    b = starting_b
    m = starting_m

    0.upto num_iterations-1 do |i|
        b, m = step_gradient(b, m, points, learning_rate)
    end
    return [b, m]
end

def run()
    points = CSV.read('data.csv', converters: :numeric)
    learning_rate = 0.0001
    initial_b = 0 # initial y-intercept guess
    initial_m = 0 # initial slope guess
    num_iterations = 1000    
    puts "Starting gradient descent at b = #{initial_b}, m = #{initial_m}, error = #{compute_error_for_line_given_points(initial_b, initial_m, points)}"
    puts "Running..."
    (b, m) = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations)
    puts "After #{num_iterations} iterations b = #{b}, m = #{m}, error = #{compute_error_for_line_given_points(b, m, points)}"
end

run()

Файл «data.cv», необходимый для запуска этой программы, доступен по этой  ссылке .