#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]))