partially unsupervised
6.52K subscribers
20 photos
2 files
154 links
@arsenyinfo пишет про software engineering и machine learning
Download Telegram
Я в последнее время часто играю в GeoGuessr - это игра, в которой по картинкам из Google Street View за ограниченное время нужно угадывать локацию. Естественно, к этой игре уже сделали 100500 deep learning читов, которые угадывают сильно лучше среднего игрока. Но я хотел поделиться другим наблюдением: успех в GeoGuessr похож на успех в классических ML проектах. Т.е. для победы нужно придумать фичи и собрать датасет.

Примеры фичей: с какой стороны дороги едут машины, на каком языке написаны вывески и знаки (наконец-то мне пригодилось умение отличать польский от чешского, а вот сербский с болгарским пока иногда путаю), что растет на полях (конечно, с ребятами из OneSoil в этом соревноваться нельзя), видны ли в окрестностях море или горы, насколько разбита дорога, насколько просматривается солнце через облака, какое распределение машин/мопедов...

Но без достаточного датасета (желательно настоящих путешествий, а не наигранных матчей) фичи не помогают. Например, я никогда не был в южном полушарии, и потому едва ли могу отличать страны в Латинской Америке и Африке.
Читаю "Бобо в раю. Откуда берется новая элита" - про социальный класс bourgeois-bohemian, "буржуазной богемы" (в наших широтах похожих людей обычно называют не совсем точно хипстерами), и хочу выдернуть оттуда цитату:

Ваша карьера должна отражать изменчивые запросы бобо-этики. В 1950-х считалось, что деньги лучше всего получать по наследству. В среде сегодняшней бобо-элиты считается, что лучше всего разбогатеть по стечению обстоятельств. Деньги просто случилось заработать на пути к реализации творческой задачи. Это значит, что наиболее престижные профессии совмещают в себе артистическое самовыражение с высоким доходом. Писатель, зарабатывающий миллион долларов в год, куда престижнее банкира, получающего пятьдесят. Программист с опционом на акции стоимостью в несколько миллионов престижнее девелопера, чей пакет оценивается в десятки миллионов.

Думаю, эта цитата хорошо описывает, почему X денег, заработанных в Сбере или, например, на перепродаже спизженного кирпича - не так круто, как такие же X денег от продажи стартапа, призовых на Kaggle или гонораров за лекции.
В любом ML проекте, в котором я оказываюсь, довольно быстро заводятся минимум две функции: get_relative_path и ensure_local.

Первая совсем простая и используется для того, чтобы тесты и скрипты не зависели от того, откуда их вызывают:

fixture = get_relative_path("../fixtures/file.csv", __name__)

Вторая оборачивает все доступные хранилища данных (обычно начинается с s3, но потом могут добавиться другие облака, SSH-машины и черт знает что еще) и используется так:

with ensure_local("s3://secret-bucket/kurwa_dzik.jpg") as fd:
img = cv2.imread(fd)

С течением времени из ensure_local обычно вырастает отдельный модуль io; все это пишется абсолютно бессознательно. А поскольку осознанность - это важно, расскажите в комментариях, какой либой можно удержать меня copilot от написания этого в очередной раз и какие utils пишете вы, врываясь в новый проект?
Видел недавно такой оптимистичный прогноз: 2022 - последний, в котором большую часть текстов не пишет AI. Наверное, это слишком оптимистичная отсечка, но светлое будущее в этом вопросе неизбежно (если, конечно, человечество сейчас не закидает себя ядреными бонбами слишком эффективно).

Примерно четверть кода за меня пишет Copilot. Впрочем, недавно я готовил слайды к небольшой технической презентации, а слайды я обычно пишу в Markdown и конвертирую с помощью Marp. И, оказалось, что Copilot неплох и для такой задачи.

Еще сильнее я удивился, когда Copilot помог найти похожие компании. В коде был список сайтов из определенной ниши, и Copilot советовал добавить еще несколько компаний; сходил к ним на сайты - действительно, делают очень похожие продукты.

В написании "обычных" текстов сейчас помогают Wordtune и Grammarly, и, конечно, до уровня Copilot им еще очень далеко - не в последнюю очередь из-за UX, это скорее постпроцессинг текста, а не подсказки в формате автодополнения. В Google Docs тоже есть автодополнение, но пока слабоватое, обычно подсказывает буквально пару слов для завершения идиомы.

Бывший коллега рассказывал, что еще в пару лет назад, когда только появилась GPT-3, писал с ее помощью всякие продуктовые спеки, и настолько успешно, что большие боссы ставили эти доки всем в пример. Это все еще гиковские забавы, не мейнстрим, но обязательно им станет.

Короче, I'm bullish on large language models для широкого применения.
Недавно нужно было наколхозить простую тулзу для поиска семплов в большом датасете. Условно есть 200М семплов, доступных через AWS Athena, и к ним делаются запросы в духе select * from big_table where blah like '%blah%' and foo like '%bar%' limit 10;.

Делать все через Athena очень просто, но медленно (запросы редко занимают меньше 10 секунд) и небесплатно (получалось что-то вроде $0.05 за запрос, некритично, но не приятно). Напрашивается решение с частичным кэшированием: возьмем сабсет на 5% данных, положим его локально и будем обращаться к нему, а если не нашли достаточно семплов в локальном сабсете, тогда уже пойдем в Athena с медленным запросом. На практике оказалось, что 95%+ запросов можно удовлетворить сабсетом, все довольно быстро (до полутора секунды на запрос) и бесплатно.

Так как я ленивый, сперва уложил локальный сабсет в SQLite, чтобы не возиться с поднятием более серьезной базы. В принципе, этого было достаточно, но в познавательных целях решил потрогать DuckDB - это embedded OLAP (т.е. колоночная) SQL база данных. DuckDB ставится через pip install (впрочем, есть поддержка и для других языков), плотно интегрирована с pandas и parquet. Замена SQLite на DuckDB потребовала всего нескольких строк и ускорила поиск почти в два с половиной раза до малозаметных 600 мс.

Жду комментариев, что Настоящий Мужик бы поднял кластер Clickhouse, а не это все.
Потестил howto - мелкую (120 строк на go) CLI утилиту, написанную @dlinnlp. howto подсказывает консольные же команды по запросу на естественном языке.

Под капотом OpenAI Codex, потому для работы нужен API ключ OpenAI. Впрочем, сложно представить, что кто-то надергает подсказок больше, чем на пару десятков центов.
Вот примеры работы:

➜  ~ howto push my git branch ignoring conflicts
git push -f origin my_branch

➜ ~ howto cache model from huggingface
python3 -m transformers.convert_graph_to_onnx --model_type bert --model_name bert-base-uncased --framework pytorch --output_dir /tmp/onnx/ --input_shape [1,512] --opset 11 --do_lower_case --save_mode model

➜ ~ howto add dependency with poetry
poetry add <package>

➜ ~ howto remove unused docker artifacts
docker system prune -a --volumes

➜ ~ howto pull logs from cloudwatch and filter by word
aws logs filter-log-events --log-group-name /aws/lambda/my-lambda-function --filter-pattern "ERROR" --start-time 1555555555555 --end-time 1555555555555 --output text --limit 100 --query events[*].message | tr -d '"' | grep -i "my-word"

➜ ~ howto install micro
curl https://getmic.ro | bash

➜ ~ howto install python 3.12 ubuntu 16.04
sudo apt-get install python3.12

➜ ~ howto install python 3.14 ubuntu 16.04
sudo apt-get install python3.4

➜ ~ howto compile python code with rust to make it fast
rustc -C opt-level=3 -C lto -C target-cpu=native -C target-feature=+avx2 -C target-feature=+fma -C target-feature=+sse4.2 -C target-feature=+ssse3 -C target-feature=+avx -C target-feature=+aes -C target-feature=+rdrnd -C target-feature=+rdseed -C target-feature=+pclmul -C target-feature=+lzcnt -C target-feature=+popcnt -C target-feature=+bmi2 -C target-feature=+avx512f -C target-feature=+avx512cd -C target-feature=+avx512er -C target-feature=+avx512pf -C target-feature=+avx512bw -C target-feature=+avx512dq -C target-feature=+avx512vl -C target-feature=+avx512ifma -C target-feature=+avx512vbmi -C target-feature=+avx512vb

Как видно, пока не все идеально, но в целом может быть полезно. К сожалению, пока частенько падает с signal SIGSEGV, но верю в скорый фикс.

UPD: кажется, уже починили, спасибо уважаемому подписчику.
Хороший пример intelligence amplification - еще один сыроватый, но полезный инструмент на базе large language models: explainpaper.com.

Суть такая: загружаешь статью (например, свежую state of the art), в процессе чтения выделяешь непонятные куски и получаешь объяснение, что это вообще такое. Если объяснения оказывается недостаточно, можно задавать наводящие вопросы.

Работает прям неплохо: может использоваться и просто как напоминалка базовых понятий ("черт, я забыл, что такое self-attention") и для объяснения концепций из статьи, т.е. в контексте.

С контекстными объяснениями пока не все идеально, с наводящими вопросами тоже. Например, частый failure mode - просто пересказать сказанное выше, не добавляя новой информации. Но в целом выглядит как хороший помощник для тех, кому иногда нужно читать статьи и не хочется закапываться в глубину, чтобы разобраться на достаточном уровне.
Еще со школы я казуально интересовался экономикой. В мои школьные годы еще не выветрился флер экономиста как престижной профессии, и потому гимназия, в которой я учился, в погоне за статусом (и пожертвованиями родителей некоторых моих богатых одноклассников) ввела обязательный спецкурс по экономике. Уставшая от жизни женщина пересказывала нам несвежее издание Economics, я с любопытством слушал. Потом меня отправили на районную олимпиаду по экономике, где все призовые места, как всегда, забрала другая гимназия, называвшая себя "экономической" - видимо, там аналогичный учебник пересказывали дважды в неделю.

Потом был семестр экономики на первом курсе. О качестве преподавания там говорит тот факт, что школьных знаний хватило на сдачу экзамена на нетипичные для меня 9 баллов. На этом академический аспект закончился, а я просто периодически почитывал разные популярные книжки про экономику - от сверхпопсовой "Фрикономики" до относительно немодной "The Worldly Philosophers".

Это я все к чему? Хочу порекомендовать Economics: The User's Guide (на русском "Как устроена экономика") хотя бы за ее четвертую главу, в которой систематизированы десять экономических школ. Да и в остальных главах автор стремится показать многообразие, не пытаясь показать то или иное течение как единственно верное.

Как писали в одном из отзывов, [it guides] how to think (as opposed to what to think). Например, поможет разобраться, почему Хайек - напомню, прославившийся трудом с пафосным названием “Дорога к рабству” - в свое время одобрял диктатуру Пиночета в Чили. И, конечно, более явно замечать bias в той или иной околоэкономической точке зрения.
На выходных достаточно угорел, чтобы пощупать Nim - компилируемый в C язык, на верхнем уровне по синтаксису напоминающий Python (впрочем, желающие могут лезть вглубь и обмазываться макросами, темплейтами и работой с указателями).

На Nim можно легко компилировать нативные модули для дальнейшего вызова из Python. Собственно, такое упражнение я для себя и выбрал: взял пять функций для работы со строками, которые относительно недавно использовал, и реализовал их в виде nim-библиотеки, которую можно импортировать в python-код. Выбрал строки, потому что в числодробилках тягаться с numpy-экосистемой было бы сложно.

Гипотеза такая: учитывая простоту языка сабсета языка, на него можно будет переписывать узкие места кода на питоне (например, какой-нибудь препроцессинг), не прибегая к тяжелым наркотикам вроде Rust 🦀

TL;DR:
- писать примитивные штуки действительно несложно, билдить под питон - тоже;
- слишком примитивные функции ускорить не получится;
- чуть более сложные куски приятно ускоряются;
- тягаться с оптимизированным C кодом, обернутым в питон, тоже нельзя.

В общем, в прод не потяну, а для какого-нибудь оффлайн процессинга вполне можно пробовать.
Чуть больше деталей и исходники на Github 👨‍💻
Про Galactica - "AI trained on humanity's scientific knowledge" - написали уже многие. Вкратце: это decoder-only (т.е. похожая на семейство GPT) языковая модель, обученная на корпусе научных текстов, как следствие, способная решать всякие околонаучные задачи - например, пересказывать научные статьи или упрощать математические выкладки. Больше примеров можно посмотреть на сайте.

Но больше, чем результат, меня впечатлила статья, а точнее, одна из идей - см. 3.1.1 Working Memory Token.

Our solution is a working memory token we call <work>. ... Where a computation is performed that a human could not do internally,
we offload by writing and executing a Python script.

Иными словами, пусть модель генерит python код, выполнение которого даст ответ. К сожалению, пока это только используется в обучении, но не в инференсе:

Importantly, we do not have to turn this on, and the model can also predict the output from running a program. For our experiments, we did not find the need to turn Python offloading on, and leave this aspect to future work.

Эффективное объединение ML и какие-то внешних систеи (например, баз данных) - нерешенная в общем виде проблема, и такие идеи мне кажутся полезными и важными в долгосрочной перспективе.
Вчера выгуливал собаку, и встретил уважаемого читателя, который выкатил справедливую предъяву - где контент в канале?! Придется исправляться.

В последнее время несколько раз разные люди спрашивали меня как активного пользователя Copilot (пользуюсь больше года) о впечатлениях. Я уже много писал, но не поленюсь повторить: очень доволен. Copilot очень ускоряет написание бойлерплейта, тестов и прочего простого кода. сам пишет простые функции, достаточно их нормально называть. Лучше меня знает незнакомые языки или фреймворки, может написать простые куски документации или инфраструктурного кода (например, развернуть в докерфайле комментарий "install X" в цепочку из добавления репозитория, apt update и собственно установки). Самый любопытный инсайт в том, что он слегка меняет паттерн написания кода: т.е. я со временем стал автоматически декомпозировать в духе “ну здесь вынесу простую функцию с такой-то сигнатурой, копайлот допишет”.

Главный недостаток: регулярно подсказывает несуществующие методы. Непонятно, почему нельзя фильтровать это автоматически на уровне плагина к IDE. Еще пацаны (и пацанесса) из Стэнфорда пишут, что есть вопросики к секьюрити, но тут мне сказать нечего - наверное, писать потенциально атакуемый софт копайлотом я бы и сам не рискнул.

Недавно закрылся стартап Kite, который начал делать AI coding assistant еще в 2014. Их CEO утверждает, что корень проблемы такой - разработчики не хотят платить: “Our diagnosis is that individual developers do not pay for tools,” said Smith. На мой взгляд, это так себе отмазка - я с удовольствием плачу за Copilot, а вот Kite в свое время снес на следующий день, он был медленный и бесполезный.

Если кому-то совсем не подходит Copilot из соображений приватности, обнадежу, что уже можно собрать аналог на коленке, используя готовые веса - всего-то и нужно, что написать обвязку, быстрый инференс и купить свежий Geforce.

Кстати, следующий пост как раз будет про быстрый инференс, stay tuned.
Рубрика "мои кенты - мое богатство". 👬

Я обещал написать про быстрый инференс, и вот подвернулся случай. У меня есть два предпочтения, которым я предпочитаю следовать в дизайне инференс-сервисов:
- никаких динамических графов, все должно быть сконвертировано в ONNX, даже легкие scikit-learn модели, и потом гоняться в ONNXRuntime. Это и минимизирует ошибки с одной стороны, и позволяет дешево сменить core model, да и запускать можно одинаково хоть локально, хоть на сервере, только бэкенд подмени;
- если можно что-то вынести на serverless (например, в AWS Lambda), надо выносить - это простой способ сглаживать нагрузку.

У лямбд есть несколько проблем:
- неидеальное масштабирование (с нуля до многих тысяч параллельных запусков мгновенно не вырастешь, что бы там ни говорили маркетинговые описания);
- медленный cold start (в эту сторону есть подвижки);
- нет GPU, и потому инференс жирных моделей скорее затруднителен, да и экономически не очень выгоден.

Так вот, мои старые кореша Андрей и Игорь решили починить одну из этих проблем и пилят платформу everinfer.ai, которая прям соответствует моим представлениям о прекрасном:

from everinfer import Client

client = Client('my_secret_key')
pipeline = client.register_pipeline('my_model_name', ['onnx/model.onnx'])
runner = client.create_engine(pipeline['uuid'])
preds = runner.predict([inputs])

Внутри ONNXRuntime, Rust 🦀, ScyllaDB и прочие модные технологии, благодаря чему инференс получается довольно быстрым. Слегка потестировал, получилось чуть быстрее локального запуска ONNXRuntime на CPU, даже с учетом сетевых издержек.

Платформа только-только открывается для внешних пользователей и предлагает первым тестерам бесплатное железо для инференса и помощь в запуске (хотя API простой как табуретка, вряд ли понадобится много помощи). Можете писать сразу @andrey_kiselev и просить доступ.
Встретил в нескольких местах метафору про то, что все эти новые инструменты (и текстовые на базе LLM, и картиночные с диффузией) - это новое электричество, и аналогично изменит нашу жизнь и работу. Не отрицая пафос, прикинусь мамкиным футуристом и предложу другую метафору про другую смену парадигмы - конвейерное производство, как на заводах Форда.

Рабочие на заводах Форда были гораздо более продуктивны, зарабатывали сильно больше, чем на других производствах (The gains in productivity allowed Ford to increase worker pay from $1.50 per day to $5.00 per day once employees reached three years of service on the assembly line), но монотонность и темп работы делал ее значительно более тяжелой. Использование кардинально новых инструментов (copilot, explainpaper, chat GPT) может в разы увеличить эффективность любого белого воротничка, но требует определенной адаптации. Я знаю несколько крутых программистов, которые утверждают, что в их задачах copilot не поможет, хотя они и не пробовали; это нормальная инерция. Начать использовать больше инструментов - это большая ментальная нагрузка, и больше непривычных переключений контекста. Там где раньше человек решал одну задачу за единицу времени (будь то написание софта, маркетингового текста или презентации для клиента), с мощными инструментами он сделает две или три, но вникать в контекст каждой задачи все еще придется, и это зачастую сложнее, чем собственно написать сто строк говнокода. Вдобавок инстрмуенты пока сыроваты, за ними нужно приглядывать. В общем, повышение эффективности будет неизбежно, но небесплатно.

А в более долгосрочной перспективе этими инструментами начнут пользоваться все, кто не готов жить под мостом или идти работать баристой, и окно возможностей, открытое сейчас у early adopters, закроется.
Досталась мне на работе система, за которую до недавних пор отвечал умный, но неопытный PhD. Задача по сути сводится к text classification, внутри некий трансформер и все по классике: много кастомного, вычурный оптимайзер, дубликаты в данных и так далее. И, конечно, все надо улучшать - от точности до скорости.

В комплекте с системой полагался въедливый коллега, который с радостью согласился пойти разгребать авгиевы конюшни датасетов. А я взялся за инженерную часть. Кое-какая инфраструктура уже была: тесты, CI, обучение в докере - до этого мы с другим коллегой занимались переносом этого хозяйства из jupyter ноутбуков во что-то воспроизводимое. Так что надо можно было более или менее смело лезть в сам training pipeline.

Обучение занимало ~10-11 часов на одной A100, что в целом приемлемо, но, судя по низкой нагрузке и CPU, и GPU, можно было сделать лучше. Перенес часть препроцессинга из __getitem__ в __init__, избавился от pandas, выкинул лишние данные из памяти, что-то закэшировал, увеличил количество воркеров для датасетов, увеличил батчи - и GPU стала загружаться на ~95-98%, а обучение стало втрое быстрее. С такими скоростями уже можно быстро итерироваться.

Основная модель весила больше гигабайта. Я посмотрел на граф и обнаружил, что больше половины весов - это жирная мультиязычная embedding матрица инпута. Пошел в Athena и добыл неразмеченный датасет вида SELECT * FROM DATA WHERE THINGS ARE NOT LIKE FULL GARBAGE LIMIT OVERDOHOOYA, прогнал его через токенайзер, подтвердил гипотезу, что реально используется <50% токенов. Значит, можно переучить токенайзер и заменить эмбеддинг слой на значительно меньший, предварительно скопировав предобученные веса полезных токенов. Это уменьшает размер модели примерно до 60% от оригинального (правда, без заметного эффекта на скорости инференса). Потребление памяти важно для рантайма, ведь можно держать в памяти одного инстанса больше моделей, там как раз был боттлнек.

Кстати, раз у нас есть большой неразмеченный датасет, это звучит как повод устроить pretraining. Адаптировал masked language pretraining пайплайн с huggingface 🤗, и оставил новую, уже уменьшенную модель учиться на недельку. И, наконец, заменил дефолтные веса в основном пайплайне на результат претрейна на этом неразмеченном датасете. Это не только улучшило точность (на разных тестовых датасетах от 10% до 20%) и вторичные метрики вроде калибровки, но и ускорило сходимость, т.е. можно безболезненно уменьшить количество эпох еще на треть.

Итого: за пару недель работы обучение ускорено, потребление памяти упало, точность выросла. Важно подчеркнуть, что ничего из перечисленного не содержало никаких сложных алгоритмов. Если ты не OpenAI, то просто нормально делай - нормально будет.
Я когда-то писал про residential proxies, и вот случайно наткнулся на еще один способ, как такие компании набирают себе пул прокси.

Захотел поставить на телевизор Apple TV, пошел в LG app store, глаз зацепился за приложение Xmas Tree в блоке рекомендаций. Как нетрудно догадаться, приложение показывает елку на фоне горящего камина, но в настройках есть чекбокс "Режим HD" с любопытной подписью мелким шрифтом типа "Разрешить bright data ltd использовать вашу сеть". Bright data - один из крупных поставщиков прокси, на одной из прошлых работ использовали их как раз для парсинга цен конкурентов.

Киберпанк, который мы заслужили: рождественская OLED елка, которая в фоне что-то скрапит.

P.S. Всех с рождеством! 🎄🎅
Для тех, кто предпочитает аудиовизуальный контент, а не эту всю писанину: поговорили с Антоном, одним из самых крутых инженеров и спикеров в русскоязычной computer vision тусовке. Обсудили Copilot, chat GPT и прочие LLM-based инструменты, и как они могут повлиять на околоDS карьеры.
Увидел очередную статью про определение людей и их поз по WiFi-сигналу, и нахлынули воспоминания.

Идея "видеть сквозь стены через wifi" не отпускает ресерчеров уже давно, не меньше 10 лет. На этот раз к ней подошли через любимый мной densepose (я пару раз пытался применять его в работе, но всегда выживал какой-нибудь другой подход), и вроде даже работает. Я склонен верить картинкам и метрикам, потому что Fernando De la Torre - чувак довольно авторитетный!

В умеренно далеком 2019, когда я рисовал AR-кроссовки, на CVPR меня познакомили с Фернандо, который на тот момент был менеджером AR-ресерча в Facebook. Прекрасный шанс запитчить наши наработки и попасть на радар к Facebook, который тогда еще активно покупал стартапы. И вот, после нескольких минут хвастовства, какое у нас технологически охуенное приложение, он задал мне простой вопрос: "окей, приложение хорошее, а в чем ваша команда по-настоящему, на мировом уровне, сильна?". Я растерялся, пробубнил что-то невнятное, и Фернандо мгновенно утратил к нам интерес. Так я просрал шанс, но и он упустил шанс получить промо, недорого прикупив клевых белорусских гребцов.

Кстати, вопрос "в чем ты крут на мировом уровне" мне задавали с тех пор еще один раз, и я снова ответил уклончиво. Хороший повод для карьерной рефлексии!
Применил на работе прием, который считал общеизвестным, но, судя по реакции коллег, это не совсем так. Потому расскажу и здесь!

Предположим, для какой-то ML задачи нужна ручная разметка данных, и расходы сколько-то заметны💰 (а значит, в 2023 их наверняка предложат урезать 🔪). В такой ситуации хочется хотя бы приблизительно понимать, как эти инвестиции в разметку окупаются.

Мое сколько-то наивное решение такое:
- делим тренировочный датасет на бакеты так, минимизируя разницу размеров бакетов и некоторое сходство между семплами разных бакетов (например, все семплы одного пользователя попадают в один бакет, который определяется на базе хэша его id);
- фиксируем вычислительный бюджет (вне зависимости от размера датасета учимся на N батчей);
- учим модель на сабсетах в диапазоне от малой части датасета до целого датасета, обеспечивая кумулятивного увеличение датасета (например, если некий семпл X был в обучении на 10% сабсете, то он обязательно будет и в обучении на 20% датасета);
- для каждой обученной модели смотрим ключевую метрику и рисуем график: по оси X - размер датасета, по оси Y - улучшение метрики;
- включаем воображение и оцениваем с точностью до порядка, сколько данных нужно досыпать, чтобы выжать следующий 1% метрики.

Точность такой экстраполяции оставляет желать лучшего (например, совершенно не учитывает штуки типа concept drift), но она значительно лучше, чем "хер его знает!", и сильно упрощает принятие решений типа "что выгоднее: отправить джуна подбирать гиперпараметры или нанять десять разметчиков и дальше заваливать модель данными".
Сделал вчера неочевидный баг в простом коде и какое-то время тупил, это ли не повод оставить задачку для уважаемых подписчиков?

Задача такая: из некоторого списка тегов отбираем наиболее важные по словарю рангов, формируем промпт и отправляем в модель. Что не так с этим кодом?

import typing as t


def predict_from_tags(model: BertModel, tags: t.Sequence[str]):
prompt = prompt_from_tags(tags, model.tag_ranks)
return model.predict(prompt)


def prompt_from_tags(tags: t.Sequence[str],
tag_ranks: t.Dict[str, float],
max_tags: int = 20,
allow_duplicates: bool = False,
) -> str:

if not allow_duplicates:
tags = list(set(tags))

sorted_tags = sorted(tags, key=lambda x: tag_ranks.get(x, float("inf")))
return " ".join(sorted_tags[:max_tags])

Автор первого правильного ответа в комментариях не получит ничего, кроме респекта от таких же любопытных, которым зачем-то интересно просто так искать баги в чужом коде.
Много лет назад я пошел работать в средних размеров компанию. Достаточно большую, чтобы там были сисадмины, который поддерживали свой емейл-сервер и почтовые клиенты на компьютерах сотрудников, недостаточно большую, чтобы этим слишком сильно заморачиваться. На практике это позволяло script kiddies отправлять емейлы внутренним адресатам, указывая любую строку в качестве отправителя, а почтовый клиент ее наивно показывал, не сверяя с фактическим сервером отправителя. Пранк был неизбежен.

Будучи еще достаточно молодым человеком, необремененным достаточным количеством тормозов, я отправил на всю соседнюю команду письмо, подписанное HR, с просьбой написать объяснительные по поводу "регулярного распития алкоголя в офисе и иных проявлений аморального образа жизни". После невнятного шума за стенкой, их директор выбежал из кабинета, со сложным лицом прибежал к HR-директору, через какое-то время они дошли до админов, наконец, к концу дня история дошла до моего босса.

Мой босс был отличным мужиком, который в т.ч. ценил шутки. И вместо того, чтобы выдать мне заслуженных пиздюлей, он отсмеялся и начал диктовать мне следующий пранк-емейл своему другу и коллеге: "Уважаемый Н.Г.! Просьба явиться завтра в 13:00 по адресу [главное управление КГБ Беларуси] для проведения оперативно-розыскных мероприятий…".

Уважаемый Н.Г. вышел из соседней двери, окликнул моего босса и грустным голосом сказал, что их завтрашний обед придется отменить. Конечно, тут мы ему и сказали, что на допрос идти необязательно.

Грустная ирония всплыла в новостях спустя 12 лет. С подачи КГБ уважаемый Н.Г. внесен в список лиц, причастных к «финансировании террористической деятельности», что в переводе с белорусского мусорского новояза чаще всего значит что-то очень достойное.
Давно ничего не писал про прогресс с книгой, а ведь он есть!

Позавчера созванивались с новым редактором книги по ML System Design - предыдущий уволился после пяти глав, интересно, насколько велик наш вклад в его решение. Новый редактор оказался приятным и толковым дядькой, хотя его linkedin сначала вызвал у меня скепсис: например, до работы в издательстве он долго работал в одной компании на позициях типа Senior XML Architect 🤯. Но большe меня удивило то, что он одновременно работает над 18 (!) книгами. Я бы свихнулся от такого переключения контекстов.

А вообще мы обсуждали early access: продажи книги Chip Huyen ярко подтвердили интерес к теме; и мы, и издательство хотим зарелизить первые главы до окончания всей книги. Сейчас в работе седьмая глава из семнадцати запланированных, в ранний доступ пока планируется выложить пять глав, и добавлять примерно по главе в месяц.

Писать книгу оказалось сложно: явно ощущается разница между "интутивно умею решать такие задачи по ситуации" и "настолько глубоко понимаю тему, что могу предложить общее решение, понятное случайному читателю". Следующий уровень - "сделать так, чтобы это общее решение было не слишком тривиальным, и продвинутые читатели тоже что-то для себя вынесли". И, конечно, сложно понять, когда нужно остановиться с доработками и перейти к следующей главе: это не прод, катнуть фикс следующим пуллреквестом не получится.