Spec-Zone.ru › NumPy 1.19

Расширение массивов в Numpy

Давайте рассмотрим более продвинутую концепцию в numpy, называемую расширением массивов. Термин «расширение массивов» описывает, как numpy обрабатывает массивы с различными форматами во время арифметических операций. При соблюдении определенных ограничений меньший массив «расширяется» на больший массив, чтобы они имели совместимые форматы. Расширение массивов обеспечивает способ векторизации операций над массивами, так что циклы выполняются на C, а не на Python. Это делается без необходимости лишних копий данных и обычно приводит к эффективной реализации алгоритмов. Также существуют случаи, когда расширение массивов является плохой идеей, потому что приводит к неэффективному использованию памяти, что замедляет вычисления. Эта статья предоставляет простое введение в расширение массивов с многочисленными примерами, начиная от простых и заканчивая сложными. Она также предоставляет советы о том, когда использовать, а когда не использовать расширение массивов.

Операции numpy обычно выполняются поэлементно, что требует, чтобы два массива имели ровно одинаковую форму:

Пример 1
>>> from numpy import array
>>> a = array([1.0, 2.0, 3.0])
>>> b = array([2.0, 2.0, 2.0])
>>> a * b
array([ 2.,  4.,  6.])

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

Пример 2
>>> from numpy import array
>>> a = array([1.0,2.0,3.0])
>>> b = 2.0
>>> a * b
array([ 2.,  4.,  6.])

Результат эквивалентен предыдущему примеру, где b был массивом. Мы можем представить, что скаляр b растягивается во время арифметической операции в массив с такой же формой, как a. Новые элементы в b, как показано на рисунке Рисунок 1, просто представляют собой копии исходного скаляра. Аналогия с растяжением носит только концептуальный характер. Numpy достаточно умён, чтобы использовать исходное скалярное значение без фактического создания копий, так что операции расширения массивов максимально эффективны с точки зрения памяти и вычислений. Поскольку Пример 2 перемещает меньше данных в памяти (b является скаляром, а не массивом), он примерно на 10% быстрее, чем Пример 1 с использованием стандартного numpy в Windows 2000 с массивами по одному миллиону элементов.

Vector-Scalar multiplication

Рисунок 1

В простейшем примере расширения массивов скаляр ``b`` растягивается, чтобы стать массивом с такой же формой, как ``a``, чтобы формы были совместимы для поэлементного умножения.

Правило, определяющее, совместимы ли два массива для расширения, можно выразить в одном предложении.

Правило расширения массивов

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

Если это условие не выполняется, выбрасывается исключение ValueError('frames are not aligned'), указывающее, что формы массивов несовместимы. Размер результирующего массива, созданного операциями расширения, — это максимальный размер по каждой размерности от входных массивов. Обратите внимание, что правило ничего не говорит о том, что два массива должны иметь одинаковое количество размерностей. Например, если у вас есть массив 256 x 256 x 3 значений RGB, и вы хотите масштабировать каждый цвет на изображении на разное значение, вы можете умножить изображение на одномерный массив с 3 значениями. Сопоставление размеров конечных осей этих массивов в соответствии с правилом расширения показывает, что они совместимы

Изображение

(3d массив)

256 x

256 x

3

Масштаб

(1d массив)

3

Результат

(3d массив)

256 x

256 x

3

В следующем примере оба массива A и B имеют оси с длиной один, которые расширяются до большего размера в операции расширения.

A

(4d массив)

8 x

1 x

6 x

1

B

(3d массив)

7 x

1 x

5

Результат

(4d массив)

8 x

7 x

6 x

5

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

Пример 3
>>> from numpy import array
>>> a = array([[ 0.0,  0.0,  0.0],
...            [10.0, 10.0, 10.0],
...            [20.0, 20.0, 20.0],
...            [30.0, 30.0, 30.0]])
>>> b = array([1.0, 2.0, 3.0])
>>> a + b
array([[  1.,   2.,   3.],
       [ 11.,  12.,  13.],
       [ 21.,  22.,  23.],
       [ 31.,  32.,  33.]])

Как показано на рисунке Рисунок 2, b добавляется к каждой строке a. Когда b длиннее строк a, как на рисунке Рисунок 3, возникает исключение из-за несовместимости форм.

Matrix-Vector

Рисунок 2

Двумерный массив, умноженный на одномерный, приводит к расширению, если количество элементов 1-d массива соответствует количеству столбцов 2-d массива.

Matrix-Vector-with-error

Рисунок 3

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

Расширение массивов предоставляет удобный способ вычисления внешнего произведения (или любой другой внешней операции) двух массивов. Следующий пример показывает внешнюю операцию сложения двух 1-d массивов, которая дает тот же результат, что и пример Пример 3

Пример 4
>>> from numpy import array, newaxis
>>> a = array([0.0, 10.0, 20.0, 30.0])
>>> b = array([1.0, 2.0, 3.0])
>>> a[:,newaxis] + b
array([[  1.,   2.,   3.],
       [ 11.,  12.,  13.],
       [ 21.,  22.,  23.],
       [ 31.,  32.,  33.]])

Здесь оператор newaxis вставляет новую ось в a, превращая его в двумерный массив 4x1. Рисунок Рисунок 4 иллюстрирует растяжение обоих массивов для получения желаемого выходного массива 4x3.

vector-vector with newaxis

Рисунок 4

В некоторых случаях расширение массивов растягивает оба массива, чтобы сформировать выходной массив, больший, чем любой из исходных массивов.

Практический пример: векторное квантование.

Расширение массивов довольно часто встречается в реальных задачах. Типичный пример возникает в алгоритме векторного квантования (VQ), используемом в теории информации, классификации и смежных областях. Основная операция в VQ [#f0] — нахождение ближайшей точки в наборе точек, называемых кодами в терминологии VQ, к заданной точке, называемой наблюдением. В очень простом двумерном случае, показанном на рисунке Рисунок 5, значения в наблюдении описывают вес и рост спортсмена для классификации. Коды представляют разные классы спортсменов. 2 Нахождение ближайшей точки требует вычисления расстояния между наблюдением и каждым из кодов. Самое короткое расстояние обеспечивает наилучшее соответствие. В этом примере codes[0] является ближайшим классом, указывающим на то, что спортсмен, скорее всего, баскетболист.

vector quantitization example

Рисунок 5

Основная операция векторного квантования вычисляет расстояние между объектом, подлежащим классификации (тёмный квадрат), и множеством известных кодов (серые круги). В этом простом случае коды представляют отдельные классы. Более сложные случаи используют несколько кодов на класс.

Примечания

1

Векторное квантование. Дж. Маххул, С. Рукос и Х. Гиш. «Векторное квантование в кодировании речи», Proc. IEEE, т. 73, стр. 1551-1587, ноябрь 1985 г.

2

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

Примечание

Код для создания рисунков является частью книги AstroML

© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/user/theory.broadcasting.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API