Spec-Zone.ru › NumPy 1.20

Масштабирование массивов в 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-мерного массива совпадает с количеством столбцов 2-мерного массива.

Matrix-Vector-with-error

Рисунок 3

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

Масштабирование обеспечивает удобный способ вычисления внешнего произведения (или любой другой внешней операции) двух массивов. Следующий пример показывает внешнюю операцию сложения двух 1-мерных массивов, которая дает тот же результат, что и Пример 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

Векторизация J. Makhoul, S. Roucos и H. Gish, «Векторизация в кодировании речи», Proc. IEEE, т. 73, стр. 1551-1587, ноябрь 1985 г.

2

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

Примечание

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

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

Spec-Zone.ru

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