Данный проект имеет цель на элементарной задаче потренироваться ставить воспроизводимые эксперименты с моделями машинного обучения. В проекте используется DVC для версионирования данных и моделей и хранения их в удаленном хранилище. Для CI/CD используется github actions. Эксперименты запускаются в docker контейнере, что позволяет легко переносить их на другие машины и воспроизводить эксперименты в одинаковых условиях.
В качестве датасета был использован Connectionist Bench (Sonar, Mines vs. Rocks). Датасет содержит 208 объектов, каждый из которых описывается 60 признаками. Признаки - амплитуды частот звукового сигнала, отраженного от объекта. Каждый объект принадлежит к одному из двух классов: R
- камень, M
- мина (металлический цилиндр).
В качестве модели был выбран многослойный перцптрон с одним скрытым слоем.
Результат обучения:
Epochs: 100%|██████| 80/80 [00:01<00:00, 48.04it/s, epoch 79 train: accuracy: 0.923, f1_score: 0.922, loss: 0.409 val: accuracy: 0.827, f1_score: 0.819, loss: 0.507]
В notebooks/classification_rocks_vs_mines.ipynb произведено обучение модели на представленном датасете. В данном блокноте код написан максимльно просто, его цель - "схематично" продемонстрировать пайплайн минимальными средставами.
Этот блокнот был переписан в виде множества скриптов, которые находятся в папке src
- train.py - обучение модели
- logger.py - определение класса Logger. Основной его метод - get_logger, который возвращает логгер с заданным именем. "Под капотом" вызывается logging.getLogger и производится настройка логгера.
- model.py - определение класса модели
- prepare_data.py - определение класса DataPreparer, основной метод которого - split_data, который разбивает данные на тренировочную и тестовую выборки и сохраняет путик ним в
config.ini
- dataset.py - определение класса SonarDataset (наследник torch.utils.data.Dataset). Получает путь к X.csv и y.csv. При образении по индексу возвращает признаки и метки в виде torch.Tensor
- functional_test.py - функциональное тестирование. Для каждого теста из ./tests/ измеряет accuracy модели. Записывает в директории с названиями вида
./experiments/exp_{имя_теста_из_директория_tests}_{дата_и_время}
лог теста и yaml файл с параметрами модели использованной модели.
Unit тесты реализованы с помощью библиотеки unittest
. Тесты находятся в директории ./src/unit_tests. Некоторые проверки:
- Детермнированность работы DataPreparer.split_data()
- Детерменированность forward() модели
- Корректность типов данных всех элементов тренировочного и тестового датасетов
Name Stmts Miss Cover Missing
-------------------------------------------------------------------
src\dataset.py 18 2 89% 20, 26
src\logger.py 26 0 100%
src\model.py 7 0 100%
src\prepare_data.py 111 49 56% 50-61, 67-72, 84-110, 169-170, 174-178, 205-208, 219-229
src\unit_tests\test_dataset.py 25 0 100%
src\unit_tests\test_model.py 23 0 100%
src\unit_tests\test_prepare_data.py 29 0 100%
-------------------------------------------------------------------
TOTAL 239 51 79%
В config.ini хранятся:
- Гиперпараметры модели. Записываются в скрипте train.py.
- Пути к разделенным данным. Записываются в результате работы скрипта prepare_data.py. Используются в скрипте train.py
- Параметры скрипта prepare_data.py. Каждый параметр равен последнему значению, которое переданному через командную строку. Если параметр не был передан, то он берется из config.ini. Если очистить config.ini и не передавать параметры через командную строку, то скрипт prepare_data.py закончится ошибкой.
DVC используется для версионирования исходных данных data/sonar.all-data
и модели experiments/mlp_adam_ce.pkl
.
В качестве remote хранилища была создана папка на google drive с id 1lh_wUfw88ceVCL04UtT0e0zQCoFpqLxY
. Всем в интернете был дан доступ на чтение google drive папки, благодаря чему любой авторизованный в google drive пользователь может производить команды dvc get . data/sonar.all-data
и dvc pull
. DVC автоматически генерирует ссылку на Google OAuth2 при первом выполнении одной из данных команд.
DVC команды, которые были использованы для добавления удаленного хранилища и сохранения на нем первых версий отслеживаемых файлов:
dvc init
dvc remote add -d myremote gdrive://1lh_wUfw88ceVCL04UtT0e0zQCoFpqLxY
dvc remote modify myremote gdrive_acknowledge_abuse true
dvc add data/sonar.all-data
dvc add experiments/mlp_adam_ce.pkl
dvc push
git add data/sonar.all-data.dvc data/.gitignore experiments/mlp_adam_ce.pkl experiments/.gitignore
Для CI/CD был создан Google Cloud проект, в котором был создан service account. Service account'у были даны права на редактирование папки, являющейся remote хранилищем. Json ключ от service accaunt'а был закодирован base64 и записан в github secrets.
Более подробно с произведенной настройкой можно ознакомиться в официальной документации (разделы Using a custom Google Cloud project (recommended)
и Using service accounts
)
CI и CD являются job'ами одного github actions workflow'а. Workflow находится в .github/workflows/CI CD.yml. Workflow запускается при pull request'е в ветку main и при push'е в ветку development (push в ветку main запрещен). CD job запускается после успешного прохождения CI job.
На этапе CI производится сборка docker образа и его отправка в docker hub. В docker образ не включены файлы, отслеживаемые dvc.
На этапе CD на runner'е создается файл с ключом от google service аккаунта, производится dvc pull, который скачивает файл data/sonar.all-data
, выполнются docker-compose pull
и docker-compose up
, в результате чего запускаются скрипты и тесты (соответствующая комманда прописана в docker-compose.yml
). Файл data/sonar.all-data
оказывается доступен внутри контейнера благодаря volume монтированию.
Чтобы запустить проект локально необходимо:
- Склонировать репозиторий
- Установить dvc
- Установить docker
- Получить docker образ либо собрав его локально:
docker build -t proshian/mle-mines-vs-rocks:latest .
либо скачав его с docker hub:
docker pull proshian/mle-mines-vs-rocks:latest
- Произвести dvс pull и пройти аутентификаицю в google drive, если она не была произведена ранее (OAuth2 ссылка будет сгенерирована автоматически).
dvc pull
- Выполнить docker-compose up:
docker-compose up