Использование разреженной матрицы против массива numpy
Я создаю несколько пустых массивов со счетчиками слов в Python: строки - это документы, столбцы - это счетчики для слова X. Если у меня много нулевых счетчиков, люди предлагают использовать разреженные матрицы при дальнейшей обработке, например, в классификаторе. Однако, когда в массив Scikit добавлялся классификатор логистической регрессии
, он представлял собой массив чисел с разреженной матрицей, однако, это не имело большого значения. Так что мне было интересно о трех вещах:
Википедия
говорит
разреженная матрица - это матрица, в которой большинство элементов равно нулю
Это подходящий способ определить, когда использовать разреженную матрицу
формат - как только> 50% значений равны нулю? Или это делает
смысл использовать на всякий случай?
- Насколько разреженная матрица помогает производительности в такой задаче, как моя,
особенно по сравнению с пустым массивом или стандартным списком?
- Пока что я собираю свои данные в массив, а затем преобразую в
csr_matrix в Scipy. Это правильный способ сделать это? я не могу
выяснить, как построить разреженную матрицу с нуля, и что
может быть невозможно.
Любая помощь высоко ценится!
Ответы
Ответ 1
Редкий матричный пакет scipy
и подобные в MATLAB были основаны на идеях, разработанных из задач линейной алгебры, таких как решение больших разреженных линейных уравнений (например, конечных разностных и конечных элементов). Так что вещи, подобные матричному продукту (продукт dot
для массивов numpy) и решатели уравнений хорошо разработаны.
Мой грубый опыт заключается в том, что разреженный матричный продукт csr
должен иметь 1% -ную разреженность быстрее, чем эквивалентная плотная операция dot
- другими словами, одно ненулевое значение для каждых 99 нулей. (но см. тесты ниже)
Но люди также пытаются использовать разреженные матрицы для экономии памяти. Но имейте в виду, что такая матрица должна хранить 3 массива значений (по крайней мере, в формате coo
). Таким образом, разреженность должна быть меньше 1/3, чтобы начать экономить память. Очевидно, что вы не собираетесь сохранять память, если сначала создаете плотный массив и создаете разреженный из них.
Пакет scipy
реализует множество разреженных форматов. Формат coo
проще всего понять и построить. Постройте по документации и посмотрите на ее атрибуты .data
, .row
и .col
(3 1d массивы).
csr
и csc
обычно строятся из формата coo
и немного сжимают данные, что делает их более трудными для понимания. Но у них большая часть математической функциональности.
Также возможно индексировать формат csr
, хотя в целом это медленнее, чем эквивалентный плотный матричный/массивный случай. Другие операции, такие как изменение значений (особенно от 0 до отличных от нуля), конкатенация, прирост роста, также медленнее.
lil
(списки списков) также легко понять и лучше всего подходит для инкрементного построения. dok
- фактически словарь-подкласс.
Ключевым моментом является то, что разреженная матрица ограничена 2d и во многом ведет себя как класс np.matrix
(хотя это не подкласс).
Поиск других вопросов с использованием scikit-learn
и sparse
может быть лучшим способом найти плюсы и минусы использования этих матриц. Я ответил на несколько вопросов, но я знаю "редкую" сторону лучше, чем "учиться". Я думаю, что они полезны, но я понимаю, что подгонка не всегда лучшая. Любая настройка находится на стороне learn
. Пока пакет sparse
не оптимизирован для этого приложения.
Я просто попробовал некоторые тесты на матричные продукты, используя метод sparse.random
для создания разреженной матрицы с указанной разрешающей способностью. Разреженное умножение матрицы выполнялось лучше, чем я ожидал.
In [251]: M=sparse.random(1000,1000,.5)
In [252]: timeit M1=M*M
1 loops, best of 3: 2.78 s per loop
In [253]: timeit Ma=M.toarray(); M2=Ma.dot(Ma)
1 loops, best of 3: 4.28 s per loop
Это проблема размера; для меньшей матрицы плотная dot
быстрее
In [255]: M=sparse.random(100,100,.5)
In [256]: timeit M1=M*M
100 loops, best of 3: 3.24 ms per loop
In [257]: timeit Ma=M.toarray(); M2=Ma.dot(Ma)
1000 loops, best of 3: 1.44 ms per loop
Но сравните индексирование
In [268]: timeit M.tocsr()[500,500]
10 loops, best of 3: 86.4 ms per loop
In [269]: timeit Ma[500,500]
1000000 loops, best of 3: 318 ns per loop
In [270]: timeit Ma=M.toarray();Ma[500,500]
10 loops, best of 3: 23.6 ms per loop
Ответ 2
разреженная матрица - это матрица, в которой большая часть элементов равна нулю Это подходящий способ определить, когда использовать разреженный формат матрицы - как только > 50% значений равны нулю? Или имеет смысл использовать на всякий случай?
Нет общего правила. Это зависит только от вашего точного использования позже. Вы должны вычислить сложность модели, основанной на разреженной матрице и без нее, а затем вы можете найти "сладкое пятно". Это будет зависеть как от количества выборок, так и от размера. В общем, он часто сводится к матричным умножениям формы
X' W
где X - матрица данных N x d, а W - некоторая весовая матрица d x K. Следовательно, "плотное" умножение принимает время NdK
, хотя и разреженное, считая, что ваша средняя разрешающая строка равна p NpdK
. Таким образом, если ваша разреженность составляет 50%, вы можете ожидать почти в 2 раза быстрее работы. Более сложная задача - оценить накладные расходы на разреженный доступ, а не на сильно оптимизированную плотность.
Сколько разреженной матрицы помогает производительность в задаче вроде моей, особенно по сравнению с массивом numpy или стандартным списком?
Для конкретного случая LR это может быть в несколько раз быстрее плотного формата, но для того, чтобы наблюдать разницу, вам нужно много данных ( > 1000) с высоким размером ( > 100).
Пока я собираю свои данные в массив numpy, а затем конвертирую в csr_matrix в Scipy. Это правильный способ сделать это? Я не мог понять, как построить разреженную матрицу с нуля, и это может быть невозможно.
Нет, это не очень хороший подход. Вы можете построить его "с нуля", например, сначала создав словарь, а затем преобразовывая его и т.д. Существует множество способов построить разреженную матрицу без плотной.
Ответ 3
@hpaulj Ваше время неверно, вы получаете медленные результаты, приводящие к отображению sparse.random в массив numpy (он медленный), помня об этом:
M=sparse.random(1000,1000,.5)
Ma=M.toarray()
%timeit -n 25 M1=M*M
352 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 25 loops each)
%timeit -n 25 M2=Ma.dot(Ma)
13.5 ms ± 2.17 ms per loop (mean ± std. dev. of 7 runs, 25 loops each)
Чтобы приблизиться к numpy, нам нужно иметь
M=sparse.random(1000,1000,.03)
%timeit -n 25 M1=M*M
10.7 ms ± 119 µs per loop (mean ± std. dev. of 7 runs, 25 loops each)
%timeit -n 25 M2=Ma.dot(Ma)
11.4 ms ± 564 µs per loop (mean ± std. dev. of 7 runs, 25 loops each)