Aspiring Data Science
316 subscribers
382 photos
10 videos
5 files
1.31K links
Заметки экономиста о программировании, прогнозировании и принятии решений, научном методе познания.
Контакт: @fingoldo

I call myself a data scientist because I know just enough math, economics & programming to be dangerous.
Download Telegram
#gpu #cupy #codegems

How to write CPU/GPU agnostic code
CuPy’s compatibility with NumPy makes it possible to write CPU/GPU agnostic code. For this purpose, CuPy implements the cupy.get_array_module() function that returns a reference to cupy if any of its arguments resides on a GPU and numpy otherwise. Here is an example of a CPU/GPU agnostic function that computes log1p:

# Stable implementation of log(1 + exp(x))
def softplus(x):
xp = cp.get_array_module(x) # 'xp' is a standard usage in the community
print("Using:", xp.__name__)
return xp.maximum(0, x) + xp.log1p(xp.exp(-abs(x)))


https://docs.cupy.dev/en/stable/user_guide/basic.html
#featureselection #diogenes #optimisation #gpu #cuda #cupy

Докладываю новости о проекте Диоген. Вчера добавил бюджет времени, переписал функции покрасивее, потестировал все ветки. С указанием допустимого времени работает просто отлично.

Сегодня ещё добавил функциональность random seed. Выяснилось, что нумба не подтягивает его из нампай "обычного" режима, надо в njit-нутой функции его ещё раз выставлять.

Отпрофилировал код на глубинах 1-2 взаимодействий признаков. Оказалось, что 80% времени кушает оценка кандидатов, и 20% времени - расчёт надёжности для лучших кандидатов. Из этих 80% основные опять же ~80% тратятся в прямой оценке MI кандидата и таргета.

Дальше они распадаются так: 20% на слияние многомерного вектора, 70% на перемешивание таргета (np.random.shuffle), 10% на сам расчёт MI из совместного распределения.

Очевидно, выгоднее всего оптимизировать шаффл таргета. Решил попробовать cupy как замену numpy: просто перенести таргет в память gpu и шаффлить прямо там. Улучшения таймингов это не дало, т.к. перемешанный, пусть и быстро, массив 120k элементов всё равно надо было копировать обратно в RAM для подсчёта MI.

К счастью, подумал я, cupy позволяет писать и запускать произвольные ядра Cuda в C++ прямо из питона. Почему бы не выполнить расчёт гистограммы и MI прямо на GPU? Капец я с этим намучался ) Я же совсем забыл, что программирование для GPU - это сущий ад после "обычного", в поточной парадигме: ваша программа выполняется сразу на тысячах потоков, куча ограничений и условий, разделяемая память,синхронизация, атомики, блоки... Плюс я совсем забыл С++ (даже то немногое, что знал). Да ещё cupy иногда радовал перлами типа невозможности подсчитать сумму массива без Visual Studio 2019 (хотя максимум считался нормально).

В итоге всё же добил эту идею, и сейчас у меня есть реализация критической части на Cuda )

Что же по скорости?

%timeit показывает, что массив 120k элементов ноутбучная 1050Ti шаффлит в 4 раза быстрее, чем i7-7700HQ. То есть я могу ожидать, что 0.8*0.8*0.7=40% всего времени исполнения сократится в 4 раза. Если поиск на CPU идёт 15 минут, то можно ожидать, что на GPU это будет 15*0.6+15*0,4/4=11 минут.

Напрямую сравнить CPU и GPU не получается, т.к. у разных библ разные сиды и порядок выполнения операций, а длительность всего поиска зависит от случайных решений. Пара запусков дала новое время 12.5-13.0 минут вместо 15. Возможно, где-то налажал, буду ещё профилировать. Но timeit шаффла массива 1M элементов даёт выигрыш уже не в 4, а в 40 раз, т.е., по идее, GPU будет ещё выгоднее для больших датасетов.
#sklearn #cupy

Вот пример выполнения LDA на GPU с применением этого экспериментального Array API.

from sklearn.datasets import make_classification
from sklearn import config_context
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
import cupy

X_np, y_np = make_classification(random_state=0)
X_cu = cupy.asarray(X_np)
y_cu = cupy.asarray(y_np)
X_cu.device

with config_context(array_api_dispatch=True):
lda = LinearDiscriminantAnalysis()
X_trans = lda.fit_transform(X_cu, y_cu)
X_trans.device


А еще теперь можно напрямую работать с тензорами Pytorch:

import torch
X_torch = torch.asarray(X_np, device="cuda", dtype=torch.float32)
y_torch = torch.asarray(y_np, device="cuda", dtype=torch.float32)

with config_context(array_api_dispatch=True):
lda = LinearDiscriminantAnalysis()
X_trans = lda.fit_transform(X_torch, y_torch)
type(X_trans)
X_trans.device.type


Пока далеко не все модули это поддерживают, но сама идея чудесная.

https://scikit-learn.org/stable/modules/array_api.html#array-api