В этом задании вы будете делать примерно то же, что я каждую неделю – в Mail.Ru Group: обучать модели на выборке в несколько гигабайт. Задание можно выполнить и на Windows с Python, но я рекомендую поработать под *NIX-системой (например, через Docker) и активно использовать язык bash. Немного снобизма (простите, но правда): если вы захотите работать в лучших компаниях мира в области ML, вам все равно понадобится опыт работы с bash под UNIX.
Веб-форма для ответов.
Для выполнения задания понадобится установленный Vowpal Wabbit (уже есть в докер-контейнере курса, см. инструкцию в Wiki репозитория нашего курса) и примерно 70 Гб дискового пространства. Я тестировал решение не на каком-то суперкомпе, а на Macbook Pro 2015 (8 ядер, 16 Гб памяти), и самая тяжеловесная модель обучалась около 12 минут, так что задание реально выполнить и с простым железом. Но если вы планируете когда-либо арендовать сервера Amazon, можно попробовать это сделать уже сейчас.
Материалы в помощь:
Имеются 10 Гб вопросов со StackOverflow – скачайте и распакуйте архив.
Формат данных простой:
Здесь TAB – это символ табуляции. Пример первой записи в выборке:
In [1]:
!head -1 hw8_data/stackoverflow.10kk.tsv
In [1]:
!head -1 hw8_data/stackoverflow_10mln.tsv
Здесь у нас текст вопроса, затем табуляция и теги вопроса: css, css3 и css-selectors. Всего в выборке таких вопросов 10 миллионов.
In [2]:
%%time
!wc -l stackoverflow_10mln.tsv
In [2]:
%%time
!wc -l hw8_data/stackoverflow.10kk.tsv
Обратите внимание на то, что такие данные я уже не хочу загружать в оперативную память и, пока можно, буду пользоваться эффективными утилитами UNIX – head, tail, wc, cat, cut и прочими.
Давайте выберем в наших данных все вопросы с тегами javascript, java, python, ruby, php, c++, c#, go, scala и swift и подготовим обучающую выборку в формате Vowpal Wabbit. Будем решать задачу 10-классовой классификации вопросов по перечисленным тегам.
Вообще, как мы видим, у каждого вопроса может быть несколько тегов, но мы упростим себе задачу и будем у каждого вопроса выбирать один из перечисленных тегов либо игнорировать вопрос, если таковых тегов нет.
Но вообще VW поддерживает multilabel classification (аргумент --multilabel_oaa).
Реализуйте в виде отдельного файла preprocess.py код для подготовки данных. Он должен отобрать строки, в которых есть перечисленные теги, и переписать их в отдельный файл в формат Vowpal Wabbit. Детали:
label | text, где label – число от 1 до 10 (1 - javascript, ... 10 – swift). Пропускаем те строки, где интересующих тегов больше или меньше одного
In [9]:
import os
from tqdm import tqdm
from time import time
import numpy as np
from sklearn.metrics import accuracy_score
Должно получиться вот такое число строк – 4389054. Как видите, 10 Гб у меня обработались примерно за полторы минуты.
In [4]:
!python preprocess.py hw8_data/stackoverflow.10kk.tsv hw8_data/stackoverflow.vw
In [2]:
!wc -l hw8_data/stack.vw
In [4]:
!python preprocess.py stackoverflow_10mln.tsv stackoverflow.vw
Поделите выборку на обучающую, проверочную и тестовую части в равной пропорции - по 1463018 в каждый файл. Перемешивать не надо, первые 1463018 строк должны пойти в обучающую часть stackoverflow_train.vw, последние 1463018 – в тестовую stackoverflow_test.vw, оставшиеся – в проверочную stackoverflow_valid.vw.
Также сохраните векторы ответов для проверочной и тестовой выборки в отдельные файлы stackoverflow_valid_labels.txt и stackoverflow_test_labels.txt.
Тут вам помогут утилиты head, tail, split, cat и cut.
In [3]:
#!head -1463018 hw8_data/stackoverflow.vw > hw8_data/stackoverflow_train.vw
#!tail -1463018 hw8_data/stackoverflow.vw > hw8_data/stackoverflow_test.vw
#!tail -n+1463018 hw8_data/stackoverflow.vw | head -n+1463018 > hw8_data/stackoverflow_valid.vw
#!split -l 1463018 hw8_data/stackoverflow.vw hw8_data/stack
!mv hw8_data/stackaa hw8_data/stack_train.vw
!mv hw8_data/stackab hw8_data/stack_valid.vw
!mv hw8_data/stackac hw8_data/stack_test.vw
!cut -d '|' -f 1 hw8_data/stack_valid.vw > hw8_data/stack_valid_labels.txt
!cut -d '|' -f 1 hw8_data/stack_test.vw > hw8_data/stack_test_labels.txt
Обучите Vowpal Wabbit на выборке stackoverflow_train.vw 9 раз, перебирая параметры passes (1,3,5), ngram (1,2,3).
Остальные параметры укажите следующие: bit_precision=28 и seed=17. Также скажите VW, что это 10-классовая задача.
Проверяйте долю правильных ответов на выборке stackoverflow_valid.vw. Выберите лучшую модель и проверьте качество на выборке stackoverflow_test.vw.
In [3]:
%%time
for p in [1,3,5]:
for n in [1,2,3]:
!vw --oaa 10 \
-d hw8_data/stack_train.vw \
--loss_function squared \
--passes {p} \
--ngram {n} \
-f hw8_data/stack_model_{p}_{n}.vw \
--bit_precision 28 \
--random_seed 17 \
--quiet \
--c
print ('stack_model_{}_{}.vw is ready'.format(p,n))
In [4]:
%%time
for p in [1,3,5]:
for n in [1,2,3]:
!vw -i hw8_data/stack_model_{p}_{n}.vw \
-t -d hw8_data/stack_valid.vw \
-p hw8_data/stack_valid_pred_{p}_{n}.txt \
--quiet
print ('stack_valid_pred_{}_{}.txt is ready'.format(p,n))
In [10]:
%%time
with open('hw8_data/stack_valid_labels.txt') as valid_labels_file :
valid_labels = [float(label) for label in valid_labels_file.readlines()]
scores=[]
best_valid_score=0
for p in [1,3,5]:
for n in [1,2,3]:
with open('hw8_data/stack_valid_pred_'+str(p)+'_'+str(n)+'.txt') as pred_file:
valid_pred = [float(label) for label in pred_file.readlines()]
#if (n,p) in [(2,3),(3,5),(2,1),(1,1)]:
acc_score=accuracy_score(valid_labels, valid_pred)
scores.append(((n,p),acc_score))
if acc_score>best_valid_score:
best_valid_score=acc_score
print(n,p,round(acc_score,4))
In [16]:
scores.sort(key=lambda tup: tup[1],reverse=True)
print(scores)
In [18]:
best_valid_scoret_valid_score
Out[18]:
Вопрос 1. Какое сочетание параметров дает наибольшую долю правильных ответов на проверочной выборке stackoverflow_valid.vw?
Проверьте лучшую (по доле правильных ответов на валидации) модель на тестовой выборке.
In [17]:
!vw -i hw8_data/stack_model_1_2.vw \
-t -d hw8_data/stack_test.vw \
-p hw8_data/stack_test_pred_1_2.txt \
--quiet
In [19]:
%%time
with open('hw8_data/stack_test_labels.txt') as test_labels_file :
test_labels = [float(label) for label in test_labels_file.readlines()]
with open('hw8_data/stack_test_pred_1_2.txt') as pred_file:
test_pred = [float(label) for label in pred_file.readlines()]
test_acc_score=accuracy_score(test_labels, test_pred)
print(round(test_acc_score,4))
In [29]:
100*round(test_acc_score,4)-100*round(best_valid_score,4)
Out[29]:
Вопрос 2. Как соотносятся доли правильных ответов лучшей (по доле правильных ответов на валидации) модели на проверочной и на тестовой выборках? (здесь % – это процентный пункт, т.е., скажем, снижение с 50% до 40% – это на 10%, а не 20%).
Обучите VW с параметрами, подобранными на проверочной выборке, теперь на объединении обучающей и проверочной выборок. Посчитайте долю правильных ответов на тестовой выборке.
In [30]:
!cat hw8_data/stack_train.vw hw8_data/stack_valid.vw > hw8_data/stack_merged.vw
In [31]:
%%time
!vw --oaa 10 \
-d hw8_data/stack_merged.vw \
--loss_function squared \
--passes 1 \
--ngram 2 \
-f hw8_data/stack_model_merged.vw \
--bit_precision 28 \
--random_seed 17 \
--quiet \
-c
In [32]:
%%time
!vw -i hw8_data/stack_model_merged.vw \
-t -d hw8_data/stack_test.vw \
-p hw8_data/stack_test_pred_merged.txt \
--quiet
In [33]:
%%time
with open('hw8_data/stack_test_labels.txt') as test_labels_file :
test_labels = [float(label) for label in test_labels_file.readlines()]
with open('hw8_data/stack_test_pred_merged.txt') as pred_file:
test_pred = [float(label) for label in pred_file.readlines()]
merged_acc_score=accuracy_score(test_labels, test_pred)
print(round(merged_acc_score,4))
In [34]:
100*round(merged_acc_score,4)-100*round(test_acc_score,4)
Out[34]:
Вопрос 3. На сколько процентных пунктов повысилась доля правильных ответов модели после обучения на вдвое большей выборке (обучающая stackoverflow_train.vw + проверочная stackoverflow_valid.vw) по сравнению с моделью, обученной только на stackoverflow_train.vw?
В этом задании мы только познакомились с Vowpal Wabbit. Что еще можно попробовать:
multilabel_oaa) – формат данных тут как раз под такую задачуinitial_t и power_t). Также можно потестировать разные функции потерь – обучать логистическую регресиию или линейный SVMlrq)