Последствия ручной настройки скудной разреженной формы матрицы - PullRequest
0 голосов
/ 22 мая 2018

Мне нужно пройти онлайн-тренинг по модели TF-IDF.Я обнаружил, что TfidfVectorizer scipy не поддерживает обучение по онлайн-моде, поэтому я реализую свой собственный CountVectorizer для поддержки онлайн-обучения, а затем использую TfidfTransformer scipy для обновления значений tf-idf после предварительно определенного числадокументы были введены в корпус.

Я обнаружил здесь , что вам не следует добавлять строки или столбцы в массивные массивы, так как все данные необходимо будет скопировать, чтобы они сохранялись в смежных блокахпамяти.

Но потом я также обнаружил, что на самом деле, используя скудную разреженную матрицу, вы можете вручную изменить форму матрицы .

Numpy изменить форму документов говорит:

Не всегда возможно изменить форму массива без копирования данных.Если вы хотите, чтобы при копировании данных возникала ошибка, вам следует присвоить новую форму атрибуту shape массива

Поскольку «изменение формы» разреженной матрицы выполняется путем назначенияновая форма, безопасно ли говорить, что данные не копируются?Каковы последствия этого?Эффективен ли он?

Пример кода:

matrix = sparse.random(5, 5, .2, 'csr') # Create (5,5) sparse matrix
matrix._shape = (6, 6) # Change shape to (6, 6)
# Modify data on new empty row

Я также хотел бы расширить свой вопрос, чтобы спросить о таких методах, как vstack, которые позволяют добавить массивы друг к другу (аналогично добавлению строки).vstack копирует все данные, чтобы они сохранялись как непрерывные блоки памяти, как указано в моей первой ссылке?Как насчет hstack?


РЕДАКТИРОВАТЬ: Итак, следуя этому вопросу Я реализовал метод для изменения значений строки в разреженномmatrix.

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

matrix = sparse.random(5, 3, .2, 'csr')
matrix._shape = (6, 3)
# Update indptr to let it know we added a row with nothing in it.
matrix.indptr = np.hstack((matrix.indptr, matrix.indptr[-1]))

# New elements on data, indices format
new_elements = [1, 1]
elements_indices = [0, 2] 

# Set elements for new empty row
set_row_csr_unbounded(matrix, 5, new_elements, elements_indices)

Я запускаю приведенный выше код несколькораз во время одного и того же выполнения и не получил ошибки.Но как только я пытаюсь добавить новый столбец (тогда нет необходимости изменять indptr), я получаю ошибку, когда пытаюсь изменить значения.Любое указание на то, почему это происходит?

Ну, поскольку set_row_csr_unbounded использует numpy.r_ снизу, я предполагаю, что лучше использовать lil_matrix.Даже если все элементы, однажды добавленные, не могут быть изменены.Я прав?

Я думаю, что lil_matrix будет хуже, потому что я предполагаю, что numpy.r_ копирует данные.

1 Ответ

0 голосов
/ 22 мая 2018

In numpy reshape означает изменить shape таким образом, чтобы сохранить одинаковые числовые элементы.Таким образом, произведение терминов формы не может измениться.

Самый простой пример - это что-то вроде

np.arange(12).reshape(3,4)

Метод присваивания:

x = np.arange(12)
x.shape = (3,4)

method (или np.reshape(...)) возвращает новый массив.Назначение shape работает на месте.

Примечание к документации, которое вы цитируете, вступает в игру, когда вы делаете что-то вроде

x = np.arange(12).reshape(3,4).T
x.reshape(3,4)   # ok, but copy
x.shape = (3,4)  # raises error

Чтобы лучше понять, что здесь происходит, распечатайте массив на разных этапах и посмотрите, как оригинал 0,1,2,... смежные изменения.(это оставлено как упражнение для читателя, так как оно не является центральным в более широком вопросе.)

Существует функция и метод resize, но он не используется много, и его поведение относительнодля просмотра и копирования сложно.

np.concatenate (и варианты, такие как np.stack, np.vstack), создают новые массивы и копируют все данные из входных данных.

Список (и массив dtype объекта) содержит указатели на элементы (которые могут быть массивами) и поэтому не требуют копирования данных.

Разреженные матрицы хранят свои данные (и индексы строк / столбцов) в различных атрибутах, которые различаются междуформаты.coo, csr и csc имеют 3 1d массива.lil имеет 2 массива объектов, содержащих списки.dok - это подкласс словаря.

lil_matrix реализует метод reshape.Других форматов нет.Как и в случае np.reshape, произведение размеров не может измениться.

Теоретически разреженная матрица может быть «встроена» в большую матрицу с минимальным копированием данных, поскольку все новые значения будут использоваться по умолчанию.0 и не занимают места.Но детали этой операции не были разработаны ни для одного из форматов.

sparse.hstack и sparse.vstack (не используйте версии numpy на разреженных матрицах) работают путем объединения coo атрибуты входов (через sparse.bmat).Так что да, они создают новые массивы (data, row, col).

Минимальный пример создания более разреженной матрицы:

In [110]: M = sparse.random(5,5,.2,'coo')
In [111]: M
Out[111]: 
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 5 stored elements in COOrdinate format>
In [112]: M.A
Out[112]: 
array([[0.        , 0.80957797, 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.23618044, 0.        , 0.91625967, 0.8791744 ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.7928235 , 0.        ]])
In [113]: M1 = sparse.coo_matrix((M.data, (M.row, M.col)),shape=(7,5))
In [114]: M1
Out[114]: 
<7x5 sparse matrix of type '<class 'numpy.float64'>'
    with 5 stored elements in COOrdinate format>
In [115]: M1.A
Out[115]: 
array([[0.        , 0.80957797, 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.23618044, 0.        , 0.91625967, 0.8791744 ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.7928235 , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.        , 0.        , 0.        ]])
In [116]: id(M1.data)
Out[116]: 139883362735488
In [117]: id(M.data)
Out[117]: 139883362735488

M иM1 имеют одинаковый атрибут data (тот же идентификатор массива).Но большинство операций с этими матрицами потребует преобразования в другой формат (например, csr для математики или lil для изменения значений) и будет включать копирование и изменение атрибутов.Таким образом, эта связь между двумя матрицами будет нарушена.

Когда вы создаете разреженную матрицу с функцией, подобной coo_matrix, и не предоставляете параметр shape, она выводит форму из предоставленных координат,Если вы предоставляете shape, он использует это.Эта форма должна быть как минимум такой же большой, как подразумеваемая форма.С помощью lildok) вы можете выгодно создать «пустую» матрицу с большой формой, а затем задать значения итеративно.Вы не хотите делать это с csr.И вы не можете напрямую установить значения coo.

Канонический способ создания разреженных матриц - это создание массивов или списков data, row и col итеративно из различных частей -составьте список добавлений / расширений или объединений массивов и создайте из него массив формата coo (или csr).Таким образом, вы делаете все «растущие» перед созданием матрицы.

изменение _shape

Создайте матрицу:

In [140]: M = (sparse.random(5,3,.4,'csr')*10).astype(int)
In [141]: M
Out[141]: 
<5x3 sparse matrix of type '<class 'numpy.int64'>'
    with 6 stored elements in Compressed Sparse Row format>
In [142]: M.A
Out[142]: 
array([[0, 6, 7],
       [0, 0, 6],
       [1, 0, 5],
       [0, 0, 0],
       [0, 6, 0]])

In [144]: M[1,0] = 10
... SparseEfficiencyWarning)
In [145]: M.A
Out[145]: 
array([[ 0,  6,  7],
       [10,  0,  6],
       [ 1,  0,  5],
       [ 0,  0,  0],
       [ 0,  6,  0]])

ваш новый метод формы (убедитесь, что dtype из indptr не изменяется):

In [146]: M._shape = (6,3)
In [147]: newptr = np.hstack((M.indptr,M.indptr[-1]))
In [148]: newptr
Out[148]: array([0, 2, 4, 6, 6, 7, 7], dtype=int32)
In [149]: M.indptr = newptr
In [150]: M
Out[150]: 
<6x3 sparse matrix of type '<class 'numpy.int64'>'
    with 7 stored elements in Compressed Sparse Row format>
In [151]: M.A
Out[151]: 
array([[ 0,  6,  7],
       [10,  0,  6],
       [ 1,  0,  5],
       [ 0,  0,  0],
       [ 0,  6,  0],
       [ 0,  0,  0]])
In [152]: M[5,2]=10
... SparseEfficiencyWarning)
In [153]: M.A
Out[153]: 
array([[ 0,  6,  7],
       [10,  0,  6],
       [ 1,  0,  5],
       [ 0,  0,  0],
       [ 0,  6,  0],
       [ 0,  0, 10]])

Добавление столбца также работает:

In [154]: M._shape = (6,4)
In [155]: M
Out[155]: 
<6x4 sparse matrix of type '<class 'numpy.int64'>'
    with 8 stored elements in Compressed Sparse Row format>
In [156]: M.A
Out[156]: 
array([[ 0,  6,  7,  0],
       [10,  0,  6,  0],
       [ 1,  0,  5,  0],
       [ 0,  0,  0,  0],
       [ 0,  6,  0,  0],
       [ 0,  0, 10,  0]])
In [157]: M[5,0]=10
.... SparseEfficiencyWarning)
In [158]: M[5,3]=10
.... SparseEfficiencyWarning)
In [159]: M
Out[159]: 
<6x4 sparse matrix of type '<class 'numpy.int64'>'
    with 10 stored elements in Compressed Sparse Row format>
In [160]: M.A
Out[160]: 
array([[ 0,  6,  7,  0],
       [10,  0,  6,  0],
       [ 1,  0,  5,  0],
       [ 0,  0,  0,  0],
       [ 0,  6,  0,  0],
       [10,  0, 10, 10]])

совместное использование атрибутов

Я могу сделатьновая матрица из существующей:

In [108]: M = (sparse.random(5,3,.4,'csr')*10).astype(int)
In [109]: newptr = np.hstack((M.indptr,6))
In [110]: M1 = sparse.csr_matrix((M.data, M.indices, newptr), shape=(6,3))

data приписывает общую, по крайней мере, в смысле представления:

In [113]: M[0,1]=14
In [114]: M1[0,1]
Out[114]: 14

Но если я изменю M1, добавив ненулевойзначение:

In [117]: M1[5,0]=10
...
  SparseEfficiencyWarning)

Разрыв связи между матрицами:

In [120]: M[0,1]=3
In [121]: M1[0,1]
Out[121]: 14
...