EasyData
950 subscribers
152 photos
10 videos
22 files
80 links
Добро пожаловать!
Меня зовут Мария Жарова, и это мой блог про науку о данных

Лайфхаки из будней MLщика, полезности по Data Science и ответы на вопросы, которые волнуют новичков и не только🌝

Автор @NaNCat
加入频道
Привет, друзья!
Спешу поделиться и похвастаться тем, как выглядит автограф Владимира Савельева - автора знаменитой книги "Статистика и котики"😍 (кстати, кто ещё не читал - в этом посте можно найти её pdf-версию!)
Такой чудесный и неожиданный подарок прислали коллеги из Отраслевой ИТ-Школы Росатома, с которыми мы вместе вели курс - за что им огромное спасибо☺️
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
16🔥5
Veroyatnosti_i_nepriatnosti_Matematika_povsednevnoy_zhizni_Samoylenko.pdf
14.1 MB
Ну и раз уж речь зашла про математическую литературу, держите в дополнение к статистике ещё одно любопытное издание, посвящённое теории вероятности - книгу Сергея Самойленко "Вероятности и неприятности"🫴

Концентрация математики и формул повыше, чем у В. Савельева, но жизненные примеры и ассоциации не менее интересны!😊

#математика@data_easy
Please open Telegram to view this post
VIEW IN TELEGRAM
16
Всем отличных выходных!
#мем@data_easy
😁15🤣76
Привет, друзья!
Во многих задачах - от классификации и кластеризации до поиска похожих объектов и генерации рекомендаций - важно уметь эффективно находить ближайшие векторы в высокоразмерном пространстве. Но как только число векторов превышает несколько тысяч, простые методы из sklearn и numpy начинают работать непозволительно медленно🐌 При масштабах же в миллионы объектов стоит задуматься уже не только про быстроту, но и про оптимизацию, балансируя между точностью, скоростью и потреблением памяти.

В таких случаях приходят на помощь библиотеки для быстрого KNN в высокоразмерных пространствах. Самая известная, пожалуй, Faiss от Facebook AI Research. Вот её главные преимущества:

🔠 Подкапотная реализация на C++ с SIMD-оптимизациями и продуманным управлением памятью в Python-обёртке. А ещё это значит, что Faiss не блокируется GIL'ом и отлично масштабируется по потокам: можно параллелить поиск через joblib.Parallel или ThreadPoolExecutor.
🔠 Поддержка GPU, что также даёт прирост скорости в десятки раз.
🔠 Гибкость индексов: от точного поиска (Flat) до приближённых структур с квантованием и кластеризацией.

Рассмотрим основные типы индексов:

🔠 IndexFlat - базовая реализация, которая хранит все векторы в RAM и выполняет точный линейный поиск. Работает быстро на малых объемах (<100K векторов), также идеален для отладки или в случаях, если поиск каждый раз нужно осуществлять в разных ограниченных подпространствах векторов (тогда просто запускаете с тем же Parallel:)).
🔠 IndexIVF (Inverted File Index) - предварительно перед поиском он кластеризует всё пространство векторов (например, с помощью k-means).
При поиске сначала выбираются N ближайших кластеров, а затем производится поиск заданной глубины K внутри них.
На моей практике, даже с учётом времени на обучение индекса, IVF ускорил процесс поиска в ~8 раз по сравнению с Flat+Parallel, при этом точность результата по метрикам проявилась только в 5-м знаке после запятой (ещё и в бОльшую сторону! 😎)
Но есть уже озвученное "НО" - такой индекс требует предварительного обучения, и на это нужно время. Поэтому имеет смысл его использовать, если запросы каждый раз делаются из одинакового и фиксированного пространства.
Если захотите воспользоваться IVF, то упомянутые N и K лучше брать как √d и 2–4*(размер топа, который вы ищете) соответственно, где d - количество векторов в базе для поиска.
🔠 IndexIVFPQ (Product Quantization) добавляет к IVF еще одно ускорение - сжимает векторы до компактного кода (например, 8 байт вместо 128 float32). Работает ещё быстрее и экономит память при минимуме потерь в точности. Имеет смысл посмотреть в сторону этого подхода, если у вас порядка миллиарда векторов.
🔠HNSW-семейство индексов (Hierarchical Navigable Small World) - индексы на основе графов малого мира, где каждый вектор представляется как вершина, связанная с соседями в многослойной структуре. Поиск происходит за счёт навигации по графу: сначала на грубом уровне, затем всё точнее.
Такие индексы не требуют предварительного обучения и хорошо подходят для динамических баз, где данные часто добавляются на лету. Отличаются высокой точностью даже при небольшой глубине поиска, но потребляют больше памяти и не поддерживаются на GPU.

Полезные ссылки:
🔠 Официальный репозиторий Faiss.
Там же ссылка на quick start и базу jupyter-ноутбуков с примерами кода на Python
🔠 Ещё пара хороших туториалов с примерами кода тут и тут
⚠️Если решите установить версию с поддержкой GPU, лучше использовать эту

Лёгкой и солнечной недели! ☀️

#mlops@data_easy
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥113🙏1
Всем привет!
Когда в последний раз вы удивлялись Python? Держите несколько коварных вопросов на внимательность и знание тонкостей языка😏

🐍 изменяемые аргументы по умолчанию (1 и 2),
🐍 dict с ключами разного типа (3),
🐍 логика работы логических операторов (4), с and, кстати, всё работает по тому же принципу,
🐍 is и магия чисел (5, 6).

Листайте условия в карточках, в конце недели подведём итоги👀

#python@data_easy
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥71
Привет, друзья!
Пришло время подводить итоги прошлых задачек для разминки по Python. Ниже разбор правильных ответов⚠️

1.
print(magic_func(25)) # [50]
print(magic_func(30)) # [50, 55]

Причина в том, что аргумент lst=[] - изменяемый объект. Он сохраняется между вызовами функции. Поэтому при втором вызове к списку добавляется второй элемент.

2.
print(extend_list(1)) # [1]
print(extend_list(2)) # [1, 2]
print(extend_list(3, [])) # [3]
print(extend_list(4)) # [1, 2, 4]

Демонстрация того же эффекта, что и в прошлом задании: используем изменяемый аргумент по умолчанию - список lst. Он сохраняет своё состояние между вызовами, если не передать другой. В третьем вызове передан новый список, поэтому результат уже не связан с предыдущими:)

3.
d = {1: 1, 1.0: 1.0, '1': '1', True: True}
print(d) # {1: True, '1': '1'}

Ключи 1, 1.0 и True считаются равными в словаре, потому что 1 == 1.0 == True (можете проверить это отдельно). Поэтому остаётся последний по порядку ключ из этой группы (True), и он перезаписывает предыдущие.

4.
print(tricky_or([], 'fallback')) # 'fallback'
print(tricky_or({}, 0)) # 0
print(tricky_or('', None)) # None

Оператор or возвращает первое "истинное" значение или последнее, если все ложные. [], {}, '' считаются ложными, 'fallback' - истинное, 0 и None - тоже ложные, но 0 возвращается, потому что он второй аргумент. По аналогии можете подумать, как ведет себя в подобных ситуациях and.

5.
print(x == y) # True
print(x is y) # False

== сравнивает значения, по этому принципу списки равны. is сравнивает идентичность объектов - а у нас два разных списка в памяти, находящихся по разным адресам.

6.
a = 256
b = 256
print(a is b) # True

a = 257
b = 257
print(a is b) # False

Числа от -5 до 256 кэшируются в Python, и переменные с такими значениями могут ссылаться на один и тот же объект. Значения вне этого диапазона не кэшируются, поэтому a is b даёт False.

Кто верно догадался во всех задачах - ставьте 😇

Успехов в диалогах с питоном!🐍

#python@data_easy
9👍4😇1