Написание кода для обобщенных линейных машин для 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.