Оптимізація гиперпараметров в Vowpal Wabbit з допомогою нового модуля vw-hyperopt

Привіт, Хабр! У цій статті мова піде про таке не дуже приємне аспекті машинного навчання, як оптимізація гиперпараметров. Два тижні тому в дуже відомий і корисний проект Vowpal Wabbit був влитий модуль vw-hyperopt.py, вміє знаходити хороші конфігурації гиперпараметров моделей Vowpal Wabbit в просторах великої розмірності. Модуль був розроблений всередині DCA (Data-Centric Alliance).


Для пошуку хороших конфігурацій vw-hyperopt використовує алгоритми з питоновской бібліотеки Hyperopt і може оптимізувати гиперпараметры адаптивно з допомогою методу Tree-Structured Parzen Estimators (TPE). Це дозволяє знаходити найкращі оптимуми, ніж простий grid search, при рівній кількості ітерацій.

Ця стаття буде цікава всім, хто має справу з Vowpal Wabbit, і особливо тим, хто досадував на відсутність у вихідному коді способів тюнінгу численних ручок моделей, і або тюнил їх вручну, або кодил оптимізацію самостійно.

Гиперпараметры
Що таке гиперпараметры? Це все «ступеня свободи» алгоритму, які він безпосередньо не оптимізує, але від яких залежить результат. Іноді результат залежить зовсім трохи, і тоді, якщо це не kaggle, можна обійтися дефолтними значеннями або підібрати вручну. Але іноді невдала конфігурація може все зіпсувати: алгоритм або сильно переобучится, або, навпаки, не зможе використовувати більшу частину інформації.

У вузькому сенсі під гиперпараметрами часто розуміють лише регуляризацию та інші «очевидні» налаштування методів машинного навчання. Проте в широкому сенсі, гиперпараметры — це взагалі будь-які маніпуляції з даними, які можуть вплинути на результат: інжиніринг фіч, зважування спостережень, undersampling і т. д.

Grid Search
Звичайно, було б непогано мати алгоритм, який крім оптимізації параметрів, оптимізував б і гиперпараметры. Ще краще, якщо б ми могли довіряти цим алгоритмом більше, ніж інтуїції. Якісь кроки в цьому напрямку, звичайно, давно зроблені. У багато бібліотек машинного навчання вбудовані наївні методи: grid search — прохід по сітці, або random search — семплірування точок з фіксованого розподілу (найбільш відомі екземпляри — це GridSearchCV і RandomizedGridSearchCV в sklearn). Перевага проходу по сітці в тому, що його легко закодить самому і легко розпаралелити. Однак у нього є і серйозні недоліки:

  • Він перебирає багато свідомо невдалих точок. Припустимо, вже є набір якихось конфігурацій з результатами або інша якась інформація. Людина може зрозуміти, які конфігурації точно дадуть відстійний результат, і не здогадається перевіряти зайвий раз ці регіони. Grid search так робити не вміє.

  • Якщо гиперпараметров багато, то розмір «клітинки» доводиться робити занадто великим, і можна упустити хороший оптимум. Таким чином, якщо включити в простір пошуку багато зайвих гиперпараметров, ніяк не впливають на результат, то grid search буде працювати набагато гірше при тому числі ітерацій. Втім, для random search це справедливо в меншій мірі:

Байесовские методи
Для того, щоб зменшити кількість ітерацій до знаходження хорошою конфігурації, придумані адаптивні байесовские методи. Вони вибирають наступну точку для перевірки, враховуючи результати на вже перевірених точках. Ідея полягає в тому, щоб на кожному кроці знайти компроміс між (а) дослідженням регіонів поруч із самими вдалими точками серед знайдених і (б) дослідженням регіонів з великою невизначеністю, де можуть знаходитися ще більш вдалі точки. Це часто називають дилемою explore-exploit або «learning vs earning». Таким чином, в ситуаціях, коли перевірка кожної нової точки коштує дорого (в машинному навчанні перевірка = навчання + валідація), можна наблизитися до глобального оптимуму за набагато меншу кількість кроків.

Подібні алгоритми в різних варіаціях реалізовані в інструментах MOE, Spearmint, SMAC, BayesOpt і Hyperopt. На останньому ми зупинимося докладніше, оскільки
vw-hyperopt
— це обгортка над Hyperopt, але спочатку треба трохи написати про Vowpal Wabbit.

Vowpal Wabbit
Багато хто з вас напевно використовували цей інструмент або хоча б чули про нього. Коротенько, це одна з найбільш швидких (якщо не найшвидша) в світі бібліотека машинного навчання. Натренувати модель для нашого CTR-предиктора (бінарна класифікація) на 30 мільйонів спостережень і десятки мільйонів фичей займає всього кілька гігабайт оперативної пам'яті і 6 хвилин на одному ядрі. У Vowpal Wabbit реалізовані кілька онлайнових алгоритмів:

  • Стохастичний градієнтний спуск з різними наворотами;
  • FTRL-Proximal, про який можна почитати тут;
  • Онлайнове подобу SVM;
  • Онлайновий бустинг;
  • Факторизационные машини.
Окрім цього, у ньому реалізовані feed-forward нейронні мережі, батч-оптимізація (BFGS) і LDA. Можна запускати Vowpal Wabbit у фоновому режимі та приймати на вхід потік даних, або дообучаясь на них, або просто видаючи передбачення.

FTRL і SGD можуть вирішувати завдання регресії, так і класифікації, це регулюється тільки функцією втрат. Ці алгоритми лінійні щодо фичей, але нелінійність може легко бути досягнута за допомогою поліноміальних фичей. Є дуже корисний механізм ранньої зупинки, щоб уберегтися від перенавчання, якщо вказав занадто велика кількість епох.

Також Vowpal Wabbit знаменитий своїм хешуванням фичей, яке виконує роль додаткової регуляризації, якщо фичей дуже багато. Завдяки цьому вдається навчатися на категоріальних фичах з мільярдами рідко зустрічаються категорій, вміщуючи модель в оперативну пам'ять без шкоди для якості.

Vowpal Wabbit вимагає спеціальний вхідний формат даних, але в ньому легко розібратися. Він природним чином розріджений і займає мало місця. В оперативну пам'ять в будь-який момент часу завантажена лише одне спостереження (або декілька, для LDA). Навчання найпростіше запускати через консоль.

Зацікавлені можуть прочитати туторіал та інші приклади і статті в їх репозиторії, а також презентації. Про нутрощі Vowpal Wabbit можна докладно прочитати в публікаціях Джона Лэнгфорда і блог. На Хабре теж є підходящий пост. Список аргументів можна отримати через
vw --help
або почитати докладний опис. Як видно з опису, десятки аргументів, і багато з них можуть вважатися гиперпараметрами, які можна оптимізувати.

У Vowpal Wabbit є модуль vw-hypersearch, який вміє підбирати один який-небудь гиперпараметр методом золотого перерізу. Однак при наявності декількох локальних мінімумів цей метод, швидше за все, виявить далеко не кращий варіант. До того ж, часто потрібно оптимізувати відразу багато гиперпараметров, і цього немає в vw-hypersearch. Пару місяців тому я спробував написати багатовимірний метод золотого перерізу, але кількість кроків, які потрібні йому для збіжності, перевершило будь-grid search, так що цей варіант відпав. Було вирішено використовувати Hyperopt.

Hyperopt
У цій бібліотеці, написана на пітоні, реалізований алгоритм оптимізації Tree-Structured Parzen Estimators (TPE). Перевага його в тому, що він може працювати з дуже «незграбними» просторами: коли один гиперпараметр безперервний, інший категоріальний; третій дискретний, але сусідні значення його кореговані один з одним; нарешті, деякі комбінації значень параметрів можуть просто не мати сенсу. TPE приймає на вхід ієрархічне простір пошуку з апріорними ймовірностями, і на кожному кроці змішує їх з Гауссовим розподілом з центром в новій точці. Його автор Джеймс Бергстра стверджує, що цей алгоритм досить добре вирішує проблему explore-exploit і працює краще як grid search-а, так і експертного перебору, принаймні, для завдань глибокого навчання, де гиперпараметров особливо багато. Детальніше про це можна почитати тут і тут. Про сам алгоритм TPE можна почитати тут. Можливо, в майбутньому вдасться написати про нього докладний пост.

Хоча Hyperopt не був вбудований у вихідний код відомих бібліотек машинного навчання, багато хто використовує його. Наприклад, ось чудовий туторіал по hyperopt+sklearn. Ось застосування hyperopt+xgboost. Весь мій внесок — ця схожа обгортка для Vowpal Wabbit, більш-менш стерпний синтаксис для визначення простору пошуку та запуску всього цього з командного рядка. Так як в Vowpal Wabbit ще не було подібного функціоналу, мій модуль сподобався Лэнгфорду, і він його влив. Насправді, кожен може спробувати Hyperopt для свого улюбленого інструменту машинного навчання: це нескладно зробити, і все необхідне є в це туториале.

vw-hyperopt
Перейдемо до використання модуля
vw-hyperopt
. Спочатку потрібно встановити останню версію Vowpal Wabbit з гитхаба. Модуль знаходиться в папці utl.

Увага! Останні зміни (зокрема, новий синтаксис команди) поки що (на 15 грудня) не влиті в основний репозиторій. У найближчі дні, сподіваюся, проблема вирішиться, а поки що можете користуватися останньою версією коду з моєї гілки.

Приклад використання:
./vw-hyperopt.py --train ./train_set.vw --holdout ./holdout_set.vw --max_evals 200 --outer_loss_function logistic --vw_space '--algorithms=ftrl,sgd --l2=1e-8..1e-1~LO --l1=1e-8..1e-1~LO -l=0.01..10~L --power_t=0.01..1 --ftrl_alpha=5e-5..8e-1~L --ftrl_beta=0.01..1 --passes=1..10~I --loss_function=logistic -q=SE+SZ+DR,SE~O --ignore=T~O' --plot 

На вхід модуля потрібно навчальна і валидационная вибірки, а також апріорні розподілу гиперпараметров (закавыченные всередині
--vw_space
). Можна задавати цілочисельні, безперервні або категоріальні гиперпараметры. Для всіх, крім категоріальних, можна задавати рівномірне або лог-рівномірне розподілення. Простір пошуку з прикладу перетворюється всередині
vw-hyperopt
приблизно у такий об'єкт
Hyperopt
(якщо ви пройшли туторіал по
Hyperopt
, ви зрозумієте це):

from hyperopt import hp
prior_search_space = hp.choice('algorithm', [
{'type': 'sgd',
'--l1': hp.choice('sgd_l1_outer', ['empty', hp.loguniform('sgd_l1', log(1e-8), log(1e-1))]),
'--l2': hp.choice('sgd_l2_outer', ['empty', hp.loguniform('sgd_l2', log(1e-8), log(1e-1))]),
'-l': hp.loguniform('sgd_l', log(0.01), log(10)),
'--power_t': hp.uniform('sgd_power_t', 0.01, 1),
'q': hp.choice('sgd_q_outer', ['emtpy', hp.choice('sgd_q', ['q SE -q SZ -q DR', '-q SE'])]),
'--loss_function': hp.choice('sgd_loss', ['logistic']),
'--passes': hp.quniform('sgd_passes', 1, 10, 1),
},

{'type': 'ftrl',
'--l1': hp.choice('ftrl_l1_outer', ['emtpy', hp.loguniform('ftrl_l1', log(1e-8), log(1e-1))]),
'--l2': hp.choice('ftrl_l2_outer', ['emtpy', hp.loguniform('ftrl_l2', log(1e-8), log(1e-1))]),
'-l': hp.loguniform('ftrl_l', log(0.01), log(10)),
'--power_t': hp.uniform('ftrl_power_t', 0.01, 1),
'q': hp.choice('ftrl_q_outer', ['emtpy', hp.choice('ftrl_q', ['q SE -q SZ -q DR', '-q SE'])]),
'--loss_function': hp.choice('ftrl_loss', ['logistic']),
'--passes': hp.quniform('ftrl_passes', 1, 10, 1),
'--ftrl_alpha': hp.loguniform('ftrl_alpha', 5e-5, 8e-1),
'--ftrl_beta': hp.uniform('ftrl_beta', 0.01, 1.)
}
])

Опціонально можна змінити функцію втрат на валідаційної вибірці і максимальну кількість ітерацій (
--outer_loss_function
, за замовчуванням
logistic
та
--max_evals
, 100). Також можна зберігати результати кожної ітерації і будувати графіки за допомогою
--plot
, якщо встановлено
matplotlib
та, бажано,
seaborn
:



Документація
Так як на хабрахабр не прийнято викладати докладну документацію, обмежуся посиланням на неї. Про всю семантику ви можете почитати в російськомовній вікі в моєму форке або дочекатися англомовній версії в головному репозиторії Vowpal Wabbit.

Плани
У майбутньому в модуль планується додати:

  1. Підтримку завдань регресії і мультиклассовой класифікації.

  2. Підтримку «теплого старту»: видати Hyperopt заздалегідь оцінені точки, і почати оптимізацію вже з урахуванням результатів на них.

  3. Опцію оцінки помилки на кожному кроці на ще одній тестовій вибірці (але без оптимізації гиперпараметров на ній). Це потрібно, щоб краще оцінити узагальнюючу здатність — не переобучились ми.

  4. Підтримку бінарних параметрів, які не приймають ніяких значень, таких як
    --lrqdropout, --normalized, --adaptive
    і т. д. Зараз можна, в принципі, писати
    --adaptive=\ ~O
    , але це неинтуитивно зовсім. Можна зробити щось на зразок
    --adaptive=~B
    або
    --adaptive=~BO
    .
Буду дуже радий, якщо хто-небудь скористається модулем, і комусь він допоможе. Буду радий будь-яким пропозиціям, ідей або виявленим багам. Можете писати сюди або створювати issue на гітхабі.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.