Big Data на обычном linux, или Big Data по быстрому
Мотивация
Часто нужно подготовить данные для машинного обучения “ИИ” и
- объём этих данных не всегда так велик, что превосходит объём дисков одной рабочей машины
- важен не объём данных (размер выборки) а качество этих данных и наличие полезных признаков.
и удобно просто в консоли что то сделать с данными или скриптом как-то их отфильтровать итд. Однако существующие методы работы с dataset заставляют открывать среду разработки и начинать “программирование”. Это замедляет и без того не быстрый процесс и , например у меня, пропадает запал.
Я хочу проходить путь от идеи и датасета в 1-2 ТБ до обучающего набора ИИ за день и не более.
на финальном этапе если вы используете питон для ИИ обычно используют pandas и однопоточный питон, который загружает массивы. Но это этого этапа еще надо добраться и я тут предложу простые и быстрые способы работы с CSV из линукс консоли.
CSV формат в linux “CLI”
Данные в формате csv это просто текстовый файл, с которым в консоли удобно работать. Часто в этом формате данные откуда - то выгружают, чтобы передать для биг даты или анализа и обучения ИИ.
Есть “аналитические БД” которые могут статистику по данным посчитать уже при импорте, или сделать какую-то простую выборку. Их рассматривать тут не буду - не интересно и есть другие нудобства (кому надо тот знает и так почему я их не рассматриваю ;). Есть способ увеличить скорость обработки в десятки раз и я хочу тут писать о быстрой прямой потоковой обработке. Я буду писать о джадайских приёмах.
Биг дата не помещается в памяти машины (чаще всего) и с ней лучше всего работать в потоковом режиме задействуя все процессоры и скорость IO системы - параллельно.
Мапинг данных в память, чтобы “как бы можно работать в памяти” - это галиматья. Если вы мапите память, то есть соблазн отойти от потоковой обработки и не быть героем - теряете в скорости , очень теряете.
Утилиты для обработки csv “файлов”
Сравнение производителности и возможностей утилит обработки csv файлов
Удобные шаблонные скрипты для потоковой обработки файлов
Конфигурация для map - reduce
1 |
|
config.sh
содержит конфиграцию кластера (slave машины например). он может сохранить ее в удобный файл окружения для скриптов, например так:
1 |
|
Тут
- определение полезных в работе перменных
- MAP_SLAVE_N число рабочих машин
- SLAVES список самих этих машин (ssh доступ)
- сохранение в файл
.env_autobuild
переменных.
в файле .env_autobuild получим :
1 | declare -ax SLAVES=([0]="sshpass -p slavepassword ssh slave@localhost") |
Красота! Запустил скрипт и все настроено - теперь удобно переменные конфигурации использовать так:
1 | BASH_ENV=.env_autobuild bash СКРИПТ КОМАНДА |
Разбиеное одного большого csv файла на slave части и загрузка его на slave машины
1 |
|
Тут ключевая строчка ради которой все считается вот :
1 | tail -n +$startline $SRCFILE | head -n $linecount | pv -s $linecount -l -N "split for map number $i"| gzip -c | ($slave 'cat > map/IN.csv.gz') & |
берем хвост начиная со нужной строки , берем от него начало в нужное число строк , зная общее число строк показываем прогресс (pv) , сжимаем это все для передачи по сети (меньше трафик), запскаем на рабочей машине “сохранялку в указанный файл” и передаем ей сетевой поток сжатыйх данных. и так с каждым диапазоном строк - входной файл быстро разбивается на примерно равное число кусков по строкам, по числу раобчих машин.
Запуск map операций на всех рабочих машинах
скрипт
1 |
|
по списку ssh рабочих машин подключается и там запускает скрипт map обработки в фоне, мы ждем завершения их всех.
Так можно по списку слейвов запустить там любые команды - я вам тут даю рецепт администрирования кластера linux машин, по сути.
аналогично можно получать от рабоичих машин результаты , или так или scp - по всякому.
Таким образом весь map - reduce сводится к операциям обработки csv файлов на одной машине. само распределение и сбор в одном месте - дело довольно простой уже техники.
Полезные приёмы обработки csv
Используйте mawk если это возможно (это awk реализация)
есть по идее bin/csvawk н вроде бы на С , но mawk хоть и не для csv но он очень быстрый, выберите ему один столбец, оно того стоит.
Mawk быстрее gawk с++ решения в 6 раз. доказательство , быстрее perl скрипта в 3 раза, это один из моих серетных инструментов.
использую примерно так, например нужно из url вытащить только домен
1 | # делаем из него домен а не url с мусором |
Все нужные возможности языка awk есть, за мелким исключением, но скорость…. я уже не пытаюсь даже вручную реализовывать , сдался - эта штука быстрее в реальных тестах, чем все что вы скорее всего сами ручками напишете. Используйте.
Есть разные способы работы с регулярками , mawk - быстрее всего, что я пробовал.
берем один столбец или несколько столбцов csvtool (C) csvcut (python)
1 | # берём только столбец "url" |
утилита csvtool (C) - одна из самых быстрых чтобы выбрать нужные столбцы из входного потока (она их читает по имени столбца по номеру).
Однако если нужно поменять столбцы менятами местами , то есть вариант на много быстрее , для этого она не подходит.
Полезные возможности:
выбрать указанные столбцы. (менять порядок столцов не советую - тормозит)
умеет менять разделители
1
csvtool -t TAB -u COMMA cat input.tsv > output.csv
объединение нескольких csv файлов в один , НО есть
paste
- который быстрее1
csvtool paste input1.csv input2.csv > output.csv
умеет примитивный join (по точному значению), но для этого есть тоже получше средства
удачные примеры использования
1 | # два нужны столбца по номеру |
csvtool (C) быстрее чем csvcut (python). замеры скорости.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 #=========тесты скорости разбора csv=========
#slave@mp-company:~$ time pv map/IN.csv | csvcut > /dev/null
#1,43GiB 0:02:54 [8,44MiB/s] [========================================================================================================================================================>] 100%
#real 2m54,070s
#user 2m53,384s
#sys 0m5,936s
#slave@mp-company:~$
# !!сишный инструмент - гораздо быстрее (gnu csvtools)
#slave@mp-company:~$ time pv map/IN.csv | csvtool col 1,3-11,2 - > /dev/null
#1,43GiB 0:00:41 [35,5MiB/s] [========================================================================================================================================================>] 100%
#real 0m41,390s
#user 0m39,750s
#sys 0m1,296s
# среднее значение 20МБ/с но тоже быстро (csvawk из https://github.com/DavyLandman/csvtools)
# но тут awk конкатенация съедает много, csvcut из этого пакета ооочень быстр. см ниже
#slave@mp-company:~$ time pv map/IN.csv | /home/mp/WORK/JOB/v6/csvtools/bin/csvawk '{print $1","$3","$4","$5","$6","$7","$8","$9","$10","$11","$2}' > /dev/null
#D: Done parsing config params
#1,43GiB 0:01:13 [20,0MiB/s] [========================================================================================================================================================>] 100%
#real 1m13,542s
#user 1m15,441s
#sys 0m2,310s
# скорость бешеная вообще
#slave@mp-company:~$ time pv map/IN.csv | /home/mp/WORK/JOB/v6/csvtools/bin/csvcut -D 2 -- 2>/dev/null >/dev/null
#1,43GiB 0:00:07 [ 185MiB/s] [========================================================================================================================================================>] 100%
#real 0m7,902s
#user 0m7,203s
#sys 0m0,845s
Питоновские утилиты не для BigData берите лучшее для своей задачи.
Важное замечание, совет
используйте нормальные утилиты, меньше городите самодеятельность - csv формат коварный, там экранирование строк и разделители … можете все сгубить. надо вам поменять столбцы местами ? - делаёте так:
1 | # меняем порядок с domain,,,, на ,,,domain |
Это хотя бы контролируемо, не пишите отсебятину, оптимизировать потом будуте, если воообще будет надо.
Импортируем данные в SQLITE
Да, sqlite - вещь, для большинства этапов обработки csv с ним можно сделать очень много полезного полностью утилизировав процессоры.
Можно в нем считать выборки и джойны, выводить из него в каналы, считать в них что-то и загружать обратно - очень удобно и просто.
Вот кусок скрипта в котором выполняется импорт и операции
1 | DB='sqlite3 map/index1.db' |
Фокус в том, что вы не покидаете bash окружения и делаете реально полезные вещи, можно параллельно в разных базах… удобно
и он быстрый и очень гибкий. там есть свой prce reg exp загружаемых модулей, но mawk быстрее и удобнее.
пример sqite reg exp неудобного, но он есть
1 | sudo apt-get install sqlite3-pcre |
Исключтить какие то строки grep -v
1 | # исключить нулевые идентификаторы |
вставьте в канал grep и отфильруйте по регулярке, как обычно. (полезно для этого иногда поменять порядок столбцов)
Сортировка csv по указанным столбцам
Встраиваем в csv конвеййер сортировку:
1 | # сортируем по первому столбцу |
linux команду sort и его параметры знают многие, а вот параллельная сортировка со слиянием частей - дело посложнее. тут пример параллельной сортировки со слиянием.
Передача потока на питон скрипт
если надо что то сделать с накоплением (reduce?) то часто надо и ручками что-то написать, скрипт на питон - хороший вариант для прототипа. в контексте bash его можно быстро и накидать:
1 | # отсортировали или просто передали питон скрипту поток csv |
ну или дальше можно канал передать по конвейеру…
Питон удобно использовать еще и для того, чтобы заполнить и сохранить данные в pickle формате или подготовить для ИИ в pandas
paste - соединить два солбца через разделитель
реально быстро работает обычная linux команда paste. Пример использования
1 | time paste -d ';' <( |
Тут используется process substitution для paste - это очень удобный bash приём!
т.е. первый столбец мы формируем скриптом из входного файла (расчётное значение), потом разделитель “;” потом столбцы из входного файла
paste прост как валенок и если надо делать столбцы с экранированием символов - это уже не подходит… используйте csvtool например
Как показать прогресс операции на консоль
PV
есть для консоли утилита pv
вместо cat
- она выводит на терминал прогресс,
Минусы :
- не в файл сохраняет прогресс - тадо иметь конект и она долна знать pty чтобы выводить, если вы отключались от сервера и снова подключились - вы ничего не увидите + процесс от этого может прерваться.
- И она работает с файлами известного размера, ей можно подсунуть “общее число строк”, для подсчета прогресса, но токуда она выводит прогресс - все равноне удобно.
примеры использования утилиты pv
на мастер машине использование утилиты pv удобнее и допустимо т.к, вы на мастере залогиены по ssh и скорее всего не отключетесь во время своих команд
1 | export DEBUG=head |
если $IN это csv.gz файл то надо узнать для pv число строк в файле, тут для этого отдельный подсчёт… он кстати довольно быстрый, и выигруш от такого способа есть и смысл от gzip тоже т.к. процессоров много а IO не быстрый.
DD –progress & mypv
другой способ получить прогресс такой
1 | PROGRESSDD="dd if=/dev/stdin of=/dev/stdout bs=4096 status=progress" |
вот что тут происходит :
- входной файл кидается на канал некоторой команды,
- команда в первой сдвинутой строке тупо передаёт данные со входа на выход блоками 4096 байт и при этом считает число блоков
- информация о прогрессе передаётся моему скрипту
mypv
который расчитывает (зная общий размер данных) прогресс и нормально выводит данные о прогрессе в файл который задан в переменной $STATUS_INDEX - далее можно cat нуть этот файл в нужное место на терминале (хоть по расписанию) и увидеть и текстовое сообщение и сам прогресс и сколько времени осталось до конца.
Чем это хорошо? - тем, что каждый прогресс каждого процесса в котором перекидываются данные известного размера через pipe можно таким способом вывести в отдельные файлы и потом собрать их где надо и выести на терминал красиво в нужном порядке с нужной периордичностью. Это полностью контролируемая информация о прогрессе всех процессов
Вначале инициализируйте эти файлы например так
1 | echo "1/5 compute domain column (~3m/1.5GB)" > $STATUS_INDEX |
чтобы там хоть что то было до первого их обновления
mypv - мой скрипт подсчитывает прогресс, предсказывает время до конца, сохраняет в строку
dd --status
выдает что то типа
1 | 1539135987 bytes (1,5 GB, 1,4 GiB) copied, 8,61819 s, 179 MB/s\n |
это разбирает скрипт на языке lua (LuaJit) , имя файла mypv
, chmod +x mypv
:
1 | #!/usr/bin/luajit |
Шаблон скрипта как показать прогресс процессов
1 |
|
итак, есть файлы в папке map с расширением .status х и будем показывать , там читаем про одной строке
ея была: выводить в разных местах экрана, но сделано проще:
экран чистим и встаём на начало , каждую строку печатаем шириной с кол-вом символов на экране с переносом строки и печатаем их одну за другой, всё. Этого достаточно, чтобы каждую секунду видеть актуальный прогресс.
Заходим на машину запускаем скрипт мониторинга и радуемся тому что данные в консоли меняются и при этом процессы идут фоном.
Полезные bash приемы (типа бонус)
Запуск в параллели любых кусков скрипта
1 | ( |