Написание кода для обобщенных линейных машин для Shogun на C++.

Обзор

И документация, и код в значительной степени вдохновлены pyGLMnet.:



Первое, что я сделал, это разделил все вычисления на новые функции в другом классе с именем GLMCostFunction. Сначала я получил этот класс от FirstOrderCostFunction class, но позже я избавился от этого наследования, поскольку оно оказалось большим препятствием.

Исходный класс GLM имел дело с градиентным спуском, который фактически минимизировал функцию потерь. Я сделал так, чтобы он производился от класса Iterative Machine, что делает его действительно удобным для написания любых итерационных алгоритмов. Есть две функции init_model() и iteration(), которые вам нужно переопределить, и ваша работа будет выполнена. train_machine(), как определено в классе IterativeMachine, заботится обо всем, сначала вызывая init_model(), а затем iteration() фиксированное количество раз, если алгоритм не сходится.

Это был основной запрос на извлечение, который я открыл:



В будущем этот GLM должен иметь все виды распределений, такие как BINOMIAL, GAMMA, SOFTPLUS, PROBIT, POISSON.

Я сделал перечисление для этого, которое решит, какое распределение происходит:

Я научился нескольким вещам, например, использованию cmath вместо math.h или множеству способов улучшить код C++, например заставить параметризованные конструкторы вызывать непараметризованные конструкторы. помочь с абстракцией.

Сначала я использовал библиотеку «random» для генерации случайных чисел, но, как заметил Виктор, дистрибутив неодинаков для различных реализаций stdc++. Поэтому мне пришлось сделать так, чтобы класс GLM также наследовался от RandomMixin, что решило проблемы, связанные с генерацией случайных чисел.

Кроме того, классы оптимизации Shogun оказались очень полезными при оптимизации алгоритма. Существуют целые классы для таких задач, как обновления градиентного спуска или для управления постоянной и переменной скоростью обучения, даже IterativeMachine в этом отношении. Все это действительно снимает нагрузку с плеч программиста, и он/она может сосредоточиться на том, чтобы заставить код работать.

Еще одна интересная вещь в Shogun — linalg. Поскольку в C++ нет прямых библиотек для линейной алгебры, использование linalg упрощает задачу без увеличения времени вычислений за счет выполнения циклов при запуске Eigen3 и ViennaCL в фоновом режиме.

Следует отметить, что Shogun использует матрицы, в которых каждая строка соответствует одному признаку, а каждый столбец — примеру. Это называется упорядочивание по столбцам, что является полной противоположностью по сравнению с большинством библиотек Python.

Посмотрим, как работает код:

init_model()

Существует простая функция init_model(), которая запускается один раз перед началом итераций. Все, что он делает, это заботится обо всех инициализациях весов и смещения.

Здесь мы устанавливаем веса как случайные значения, взятые из случайного распределения. Вот тут-то и появляется RandomMixin, который используется при наследовании класса LinearMachine.

итерация()

Давайте посмотрим на функцию итерации. Эта функция выполняет простой градиентный спуск:

Вам может быть интересно, откуда мы берем наши gradient_w и gradient_bias, вот откуда вся математика.

Во-первых, я определил несколько функций, чтобы улучшить абстракцию кода.

вычислить_z()

Функция вычисления_z:

не_линейность()

Функция для реализации нелинейности:

градиент_не_линейность()

И, наконец, градиент этой нелинейности:

Убрав эти простые функции, давайте перейдем к фактическому вычислению градиента. Мы будем использовать выражение, которое мы получили в предыдущем сообщении в блоге.

получить_градиент()

Я написал аналогичный код для нахождения градиента относительно смещения, просто нам не нужно беспокоиться о всей сложной линейной алгебре, и мы можем просто иметь дело со скалярами.

применить_регрессия ()

Наконец, наступает этап, когда вы должны применить модель к набору данных после ее обучения. Это делается с помощью метода apply_regression().

Вот как я реализовал регрессию Пуассона в Shogun.