Понимание различий между похожими методами сглаживания - PullRequest
0 голосов
/ 31 марта 2019

Я выполняю домашнее задание на 2-ю неделю первого курса в сертификате deeplearning.ai на Coursera.

Одной из первых задач является выравнивание изображения (209, 64, 64, 3).Вы можете сделать это тремя способами (или я так думаю):

  1. X.reshape (X.shape [0], - 1) .T
  2. X.flatten ().reshape (12288, 209)
  3. X.reshape (12288, 209)

В этом упражнении я обнаружил, что только первый вариант правильно изменяет изображение, но я понятия не имею, почему,Любая помощь приветствуется.

Ответы [ 3 ]

2 голосов
/ 31 марта 2019

Во-первых, отметим, что мы можем думать о reshape как о «вытягивании» массива из длинной строки элементов, а затем о «повторной укладке» их, заполняя оси в определенном порядке. Рассмотрим следующий массив:

array = np.arange(48).reshape(6, 4, 2)

Этот массив будет содержать элементы от 0 до 47 и иметь форму (6, 4, 2). Эта форма может быть интерпретирована просто как порядок, в котором элементы были размещены на каждой оси .

Например:

>>> print(array[0, :, :])
[[0 1]
 [2 3]
 [4 5]
 [6 7]]

Длина первой оси 48 / 4 / 2 = 8, и, следовательно, этот срез должен иметь 8 элементов. Поскольку это первая ось, она просто состоит из первых 8 элементов источника в рабочем порядке.

Далее нам нужно решить, как эти 8 элементов будут заполнять другие 2 оси. Эти 8 элементов можно рассматривать как составляющие их собственный массив с формой (4, 2). Поскольку первая ось (в подмассиве) должна быть заполнена первой, мы ожидаем, что она будет содержать пары элементов в рабочем порядке:

>>> for i in range(array.shape[1]):
...    print(array[0, i, :])
[0 1]
[2 3]
[4 5]
[6 7]

Сравните это с последней осью:

>>> for i in range(array.shape[2]):
...     print(array[0, :, i])
[0 2 4 6]
[1 3 5 7]

Второй срез, array[1, :, :], будет содержать следующие 8 элементов или 8-15, причем этот процесс повторяется до тех пор, пока не останется больше элементов.

Теперь обратите внимание, что шаг "вытягивания" похож на flatten(). Поэтому неудивительно, что 2 и 3 являются одинаковыми :

X = np.random.rand(209, 64, 64, 3)
print(X.flatten().reshape(12288, 209) == X.reshape(12288, 209)).all(axis=None)

Выход:

True

Беглое сравнение с 1. покажет, что 1. является нечетным. Обратите внимание, что X.shape[0] равно 209 (длина первой оси X). Следовательно, 1. эквивалентно X.reshape(209, -1).T (-1 - сокращение для вывода последней оси, а .T транспонирует массив).

Таким образом, они различаются не по форме , а по порядку , в котором элементы были размещены в осях . 2. и 3. начинаются с одной и той же точки: сплющенный массив, состоящий из элементов в первом ряду, затем во втором, затем в третьем и т. Следовательно, (0, 0) содержит первый оригинальный элемент, а затем (0, 1), (0, 2) ...

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

0 голосов
/ 31 марта 2019

(209, 64, 64, 3) выглядит как форма массива изображений, каждый из 209 изображений (64,64,3). Изменение формы должно сохранять эти элементы изображения вместе и в порядке.

Иллюстрируя на небольшом примере:

In [845]: arr = np.arange(24).reshape(4,2,3)                                    
In [846]: arr                                                                   
Out[846]: 
array([[[ 0,  1,  2],
        [ 3,  4,  5]],

       [[ 6,  7,  8],
        [ 9, 10, 11]],

       [[12, 13, 14],
        [15, 16, 17]],

       [[18, 19, 20],
        [21, 22, 23]]])
In [847]: arr[1]                                                                
Out[847]: 
array([[ 6,  7,  8],
       [ 9, 10, 11]])

Наивный вид:

In [848]: x = arr.reshape(6,4)                                                  
In [849]: x                                                                     
Out[849]: 
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15],
       [16, 17, 18, 19],
       [20, 21, 22, 23]])
In [850]: x[:,1]                                                                
Out[850]: array([ 1,  5,  9, 13, 17, 21])

Выбор столбца приводит к другому набору чисел, чем в Out[847]. [6,7,8] теперь разделен между строками 2 и 3. И [1,5,9...] взяты со всех концов arr.

Изменение формы с последующим транспонированием: (4,2,3) => (4, (2 * 3)) => (4,6) => (6,4):

In [851]: x = arr.reshape(4,6).T                                                
In [852]: x                                                                     
Out[852]: 
array([[ 0,  6, 12, 18],
       [ 1,  7, 13, 19],
       [ 2,  8, 14, 20],
       [ 3,  9, 15, 21],
       [ 4, 10, 16, 22],
       [ 5, 11, 17, 23]])
In [853]: x[:,1]                                                                
Out[853]: array([ 6,  7,  8,  9, 10, 11])
In [855]: x[:,1].reshape(2,3)                                                   
Out[855]: 
array([[ 6,  7,  8],
       [ 9, 10, 11]])

Формально reshape просто требует, чтобы общее количество элементов не менялось. Но, как показано здесь, подгруппы измерений также должны оставаться такими же, (4,2,3) => (4,6) или (8,3), а не (6,4). В противном случае вы рискуете перегруппировать значения.

С помощью только изменения формы и транспонирования x по-прежнему является view, разделяя буфер данных с arr. Но order отличается. Дальнейшее изменение формы (например, ravel) может привести к получению копии.

In [859]: arr.__array_interface__['data']                                       
Out[859]: (36072624, False)
In [860]: x.__array_interface__['data']                                         
Out[860]: (36072624, False)
In [861]: x.ravel()                                                             
Out[861]: 
array([ 0,  6, 12, 18,  1,  7,...])
In [862]: x.ravel(order='F')                                                    
Out[862]: 
array([ 0,  1,  2,  3,  4,  5, ...])
0 голосов
/ 31 марта 2019

Конечный результат всех трех операций будет одинаковым.Три метода - это всего лишь три разных способа достижения одного и того же результата.Но должен быть какой-то улов, верно?Да, есть.

  1. X.reshape (X.shape [0], - 1) .T : Когда вы передаете -1 в качестве оси операции изменения формы, вы говорите Hey, here is my array. I am giving you the first dimension(X.shape[0] in this case), figure out yourself what the second dimesnion should be!.Поскольку reshape - это просто еще один способ упорядочения элементов, numpy возьмет все остальные измерения и получит произведение форм для второго измерения.
  2. X.flatten ().reshape (12288, 209) : здесь вы говорите, что Yes, I know the shape of the ndarray I want, но вместо того, чтобы напрямую изменить его, вы сначала flattened вычеркните его, а затем переставите элементы.
  3. X.reshape(12288, 209) : это то же самое, что и второй вариант, но известно, что вы не выполняете операцию redundant flatten для изменения формы ndarray.

Что еще ?: Время потрачено

a = np.random.rand(2,3,4) 
%timeit d = a.reshape(a.shape[0], -1)
382 ns ± 8.94 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit b = a.flatten().reshape(2,12)
963 ns ± 11.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit c = a.reshape(2,12)
272 ns ± 4.61 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

print(b.shape, c.shape, d.shape)
(2, 12) (2, 12) (2, 12)

print((a.flatten()==b.flatten()).all())
True

print((a.flatten()==c.flatten()).all())
True

print((a.flatten()==d.flatten()).all())
True


...