Как torch.flatten() упорядочивает элементы по сглаженным размерам

#python #pytorch

#python #pytorch

Вопрос:

У меня есть тензор формы 4D [32,64,64,3] , который соответствует [batch, timeframes, frequency_bins, features] и я делаю tensor.flatten(start_dim=2) (в PyTorch). Я понимаю, что затем форма преобразуется в [32,64,64*3] --> [batch,timeframes,frequency_bins*features] — но с точки зрения фактического упорядочения элементов в этом новом сглаженном измерении 64*3 — это первые 64 индекса, относящиеся к тому, что было бы [:,:,:,0] вторым 64 [:,:,:,1] и последним 64 [:,:,:,2] ?

Ответ №1:

Для понимания давайте сначала рассмотрим простейший случай, когда у нас есть тензор ранга 2, то есть регулярная матрица. PyTorch выполняет сглаживание в так называемом порядке следования строк, переходя от «самой внутренней» оси к «самой внешней» оси.

Возьмем простой массив 3×3 ранга 2, назовем его A[3, 3] :

 [[a, b, c],
 [d, e, f],
 [g, h, i]]
 

Сглаживание этого от самых внутренних до самых внешних осей даст вам [a, b, c, d, e, f, g, h, i] . Давайте назовем этот сплющенный массив B[3] .

Связь между соответствующими элементами в A (по индексу [i, j] ) и B (по индексу k ) может быть легко получена как:

 k = A.size[1] * i   j
 

Это потому, что для достижения элемента at [i, j] мы сначала перемещаем i строки вниз, подсчитывая A.size[1] (т. Е. Ширину массива) элементы для каждой строки. Как только мы достигаем строки i , нам нужно перейти к столбцу j , поэтому мы добавляем j , чтобы получить индекс в сглаженном массиве.

Например, элемент e находится по индексу [1, 1] в A . В B , он будет занимать индекс 3 * 1 1 = 4 , как и ожидалось.

Давайте расширим ту же идею до тензора ранга ранга 4, как в вашем случае, где мы сглаживаем только последние две оси.

Опять же, используя простой тензор формы ранга 4 A (2, 2, 2, 2) , как показано ниже:

 A =
[[[[ 1,  2],
   [ 3,  4]],

   [[ 5,  6],
   [ 7,  8]]],


   [[[ 9, 10],
   [11, 12]],

   [[13, 14],
   [15, 16]]]]
 

Давайте найдем связь между индексами A и torch.flatten(A, start_dim=2) (назовем сглаженную версию B ).

 B =
[[[ 0,  1,  2,  3],
  [ 4,  5,  6,  7]],

  [[ 8,  9, 10, 11],
  [12, 13, 14, 15]]]
 

Элемент 12 имеет индекс [1, 1, 0, 0] in A и индекс [1, 1, 0] in B . Обратите внимание, что индексы на осях 0 и 1, т. Е. [1, 1] Остаются неизменными даже после частичного сглаживания. Это связано с тем, что эти оси не сглажены и, следовательно, не затрагиваются.

Это фантастика! Таким образом, мы можем представить преобразование из A в B как

 B[i, j, _] = A[i, j, _, _]
 

Теперь наша задача сводится к нахождению связи между последней осью B и последними 2 осями A . Но A[i, j, _, _] является массивом 2×2, для которого мы уже вывели соотношение k = A.size[1] * i j ,

A.size[1] теперь изменится на A.size[3] 3, поскольку теперь последняя ось. Но общая связь остается.

Заполняя пробелы, мы получаем соотношение между соответствующими элементами в A и B как:

 B[i, j, k] = A[i, j, m, n]
 

где k = A.size[3] * m n .

Мы можем убедиться, что это правильно. Элемент 14 находится [1, 1, 1, 0] внутри A . и переходит в [1, 1, 2 * 1 0] = [1, 1, 2] in B .

РЕДАКТИРОВАТЬ: добавлен пример

Взяв пример массива @ A Molem7b5 с формой (1, 4, 4, 3) из комментариев:

Итерация от внутренней ( dim=3 ) к внешним осям ( dim=2 ) A дает последовательные элементы B . Что я имею в виду под этим:

 // Using relation: A[:, :, i, j] == B[:, :, 3 * i   j]

// i = 0, all j
A[:, :, 0, 0] == B[:, :, 0]
A[:, :, 0, 1] == B[:, :, 1]
A[:, :, 0, 2] == B[:, :, 2]
// (Note the consecutive order in B.)

// i = 1, all j
A[:, :, 1, 0] == B[:, :, 3]
A[:, :, 1, 1] == B[:, :, 4]

// and so on until

A[:, :, 3, 2] == B[:, :, 11]
 

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

Комментарии:

1.Но если у нас есть A[1,4,4,3] и мы делаем A[:,:,:,0] = 1 A[:,:,:,1] = 2 A[:,:,:,2] = 3 , то A[:,:,:,0] = tensor([[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]]) , если мы тогда сделаем B = A.flatten(start_dim=2) B[:,:,:4] = tensor([[[1, 2, 3, 1], [1, 2, 3, 1], [1, 2, 3, 1], [1, 2, 3, 1]]]) это A[:,:,:,0] != B[:,:,:4] , есть ли что-то, что я неправильно понимаю?

2. @Molem7b5 Я внес исправление и использовал ваш пример A[1, 4, 4, 3] . Вы можете убедиться, что это правильно.

Ответ №2:

Да, это правильно, порядок данных сохраняется (как и базовая компоновка памяти). Вот минимальный пример:

 >>> x = torch.rand(1, 1, 12, 3)

>>> x
tensor([[[[0.9942, 0.1458, 0.7069],
          [0.2749, 0.9886, 0.3127],
          [0.8236, 0.9903, 0.0779],
          [0.0385, 0.2587, 0.7001],
          [0.1113, 0.8011, 0.0176],
          [0.1597, 0.1456, 0.4040],
          [0.2168, 0.6588, 0.7472],
          [0.1607, 0.6133, 0.0700],
          [0.6749, 0.2120, 0.4192],
          [0.8809, 0.8893, 0.4950],
          [0.1695, 0.3175, 0.6653],
          [0.7216, 0.5403, 0.8244]]]])

>>> x.flatten()
tensor([0.9942, 0.1458, 0.7069, 0.2749, 0.9886, 0.3127, 0.8236, 0.9903, 0.0779,
        0.0385, 0.2587, 0.7001, 0.1113, 0.8011, 0.0176, 0.1597, 0.1456, 0.4040,
        0.2168, 0.6588, 0.7472, 0.1607, 0.6133, 0.0700, 0.6749, 0.2120, 0.4192,
        0.8809, 0.8893, 0.4950, 0.1695, 0.3175, 0.6653, 0.7216, 0.5403, 0.8244]))