В начале года я опубликовал статью Прививаем машине музыкальный вкус: фильтруем плейлист на основе предпочтений
Результат был хорош, но далеко не идеален - обучение занимало много времени, качество незначительно росло, хотя размеченных данных стало заметно больше. Да и ощущение, что решение должно быть красивее, но я его не вижу в силу отсуствия экспертизы сохранялось
Поэтому спустя полгода после релиза первой версии я дал исходный контекст задачи кодагенту. Без ссылки на код проекта и без подсказок стека и каких бы то ни было подходов к реализации - только цель и ограничения, а именно:
Над решением успели поработать opus 4.8 (основная архитектура решения), fable 5 (ревью архитектуры; да-да, надо было наоборот, но архитектура была собрана до выхода fable) glm-5.2 (прикладные планы реализации задач) и 5.3 (прикладные планы+доведение пайплайна до рабочего состояния)
По архитектуре железный друг:
предложил тот же подход, что я использовал, но выкинул polars, вместо которого решил использовать duckdb. Когда он понял, что его не получится нормально развернуть на nfs, мы сошлись на хранении фич файлами parquet, а duckdb-базу билдить локально при запуске обучения - уж очень удобно ее использовать
отделил пайплайны извлечения фич от обучения. Изменение довольно капитанское, но так реально гораздо быстрее проводить эксперименты и улучшать модель. Появляются четкие артефакты с сырыми фичами, которые можно анализировать сами по себе
для процессинга агент сам предложил взять ту же essentia, но вместо навеса моделей на нее - предложил взять panns
примитивы обучения взял те же: mean k-NN distance+GMM log-likelihood+IsotonicRegression. Предложил обучать по одной single-class модели на include_liked и exclude_disliked, но делать это за один проход. На метрику какой из моделей смотреть - решаем на уровне подписки
Когда мы собрали решение - ничего не заработало. Качество детекции было едва выше случайного. И вот тут реально LLM оказались суперполезны, т.к. в анализе данных они понимают сильно больше меня и компенсировали пробелы в экспертизе:
модель подкинула критерий, который позволяет выделить фичи наиболее различные в датасетах. Не просто PCA, а выбрать те фичи, за счет которых мы различаем liked/disliked. 4k фич сжались до 64х - это значительно повысило точность ML-моделей за счет размерности
но даже после этого exclude_disliked продолжала сильно страдать false positive'ами - эксклюдила что нужно и что не нужно. И тут агент предложил балансировать вывод этой модели выводом include_liked классификатора. Т.е. если exclude_liked считает, что трек надо выкинуть, то прежде чем вернуть это решение мы смотрим на include_liked скор этого трека. Если он высокий - трек остается. С таким подходом качество модели стало выше, мой исходный подход показывал когда-либо
В итоге получилось решение, которое работает проще и быстрее, при этом качество его выше, чем было у меня в первой итерации. Время извлечения фич на датасете из 2k треков сократилось с нескольких дней до нескольких часов, а модель стала весить на порядки меньше
Единственной сохранившейся проблемой остается довольно высокое потребление памяти при процессинге - каждый воркер, извлекающий фичи из аудио, потребляет 1.5-2ГБ. Но размен памяти на процессинг на CPU - вполне приемлем при локальном использовании