Понимание функции numpy dstack
У меня есть некоторые проблемы с пониманием того, что делает функция numpy dstack
. Документация довольно скудная и просто говорит:
Массивы стека в последовательности глубины (вдоль третьей оси).
Принимает последовательность массивов и складывает их вдоль третьей оси для создания единого массива. Перестраивает массивы, деленные на dsplit
. Это простой способ укладывать 2D-массивы (изображения) в один 3D-массив для обработки.
Итак, либо я действительно глуп, и смысл этого очевиден, либо у меня, похоже, есть неправильное представление о терминах "укладка", "в последовательности", "глубина" или "вдоль оси". Тем не менее, у меня создалось впечатление, что я правильно поняла эти термины в контексте vstack
и hstack
.
Возьмем этот пример:
In [193]: a
Out[193]:
array([[0, 3],
[1, 4],
[2, 5]])
In [194]: b
Out[194]:
array([[ 6, 9],
[ 7, 10],
[ 8, 11]])
In [195]: dstack([a,b])
Out[195]:
array([[[ 0, 6],
[ 3, 9]],
[[ 1, 7],
[ 4, 10]],
[[ 2, 8],
[ 5, 11]]])
Прежде всего, a
и b
не имеют третьей оси, поэтому как бы я их складывал по "третьей оси" для начала? Во-вторых, если предположить, что a
и b
являются представлениями 2D-изображений, почему в итоге я получаю три 2D-массива в результате вместо двух 2D-массивов "в последовательности"?
Ответы
Ответ 1
Проще понять, что np.vstack
, np.hstack
и np.dstack
* делают, посмотрев на атрибут .shape
выходного массива.
Используя ваши два примера массивов:
print(a.shape, b.shape)
# (3, 2) (3, 2)
np.vstack
объединяется по первому измерению...
print(np.vstack((a, b)).shape)
# (6, 2)
np.hstack
объединяется по второму измерению...
print(np.hstack((a, b)).shape)
# (3, 4)
и np.dstack
объединяются по третьему измерению.
print(np.dstack((a, b)).shape)
# (3, 2, 2)
Поскольку a
и b
оба являются двумерными, np.dstack
расширяет их, вставляя третье измерение размера 1. Это эквивалентно индексации их в третьем измерении с помощью np.newaxis
(или, альтернативно, None
), например это:
print(a[:, :, np.newaxis].shape)
# (3, 2, 1)
Если c = np.dstack((a, b))
, то c[:, :, 0] == a
и c[:, :, 1] == b
.
Вы можете сделать ту же операцию более явно, используя np.concatenate
, например:
print(np.concatenate((a[..., None], b[..., None]), axis=2).shape)
# (3, 2, 2)
* Импорт всего содержимого модуля в ваше глобальное пространство имен с использованием import *
считается плохой практикой по нескольким причинам. Идиоматический способ заключается в import numpy as np
.
Ответ 2
Пусть x == dstack([a, b])
. Тогда x[:, :, 0]
совпадает с a
, а x[:, :, 1]
идентично b
. В общем случае при dstacking 2D-массивах dstack создает такой вывод, что output[:, :, n]
идентичен n-му входному массиву.
Если мы собираем 3D-массивы, а не 2D:
x = numpy.zeros([2, 2, 3])
y = numpy.ones([2, 2, 4])
z = numpy.dstack([x, y])
тогда z[:, :, :3]
будет идентичным x
, а z[:, :, 3:7]
будет идентичным y
.
Как вы можете видеть, мы должны взять срезы вдоль третьей оси для восстановления входов до dstack
. Вот почему dstack
ведет себя так, как это делает.
Ответ 3
Поскольку вы упоминаете "изображения", я думаю, что этот пример был бы полезен. Если вы используете Keras для обучения 2D-сети свертки с входным X, тогда лучше сохранить X с размером (#images, dim1ofImage, dim2ofImage).
image1 = np.array([[4,2],[5,5]])
image2 = np.array([[3,1],[6,7]])
image1 = image1.reshape(1,2,2)
image2 = image2.reshape(1,2,2)
X = np.stack((image1,image2),axis=1)
X
array([[[[4, 2],
[5, 5]],
[[3, 1],
[6, 7]]]])
np.shape(X)
X = X.reshape((2,2,2))
X
array([[[4, 2],
[5, 5]],
[[3, 1],
[6, 7]]])
X[0] # image 1
array([[4, 2],
[5, 5]])
X[1] # image 2
array([[3, 1],
[6, 7]])
Ответ 4
Я хотел бы попытаться визуально объяснить это (хотя принятый ответ имеет достаточно смысла, мне потребовалось несколько секунд, чтобы объяснить это разумом).
Если представить 2d-массивы в виде списка списков, где 1-я ось дает один из внутренних списков, а 2-я ось дает значение в этом списке, тогда визуальное представление OP-массивов будет таким:
a = [
[0, 3],
[1, 4],
[2, 5]
]
b = [
[6, 9],
[7, 10],
[8, 11]
]
# Shape of each array is [3,2]
Теперь, согласно текущей документации, функция dstack
добавляет третью ось, что означает, что каждый из массивов в конечном итоге будет выглядеть следующим образом:
a = [
[[0], [3]],
[[1], [4]],
[[2], [5]]
]
b = [
[[6], [9]],
[[7], [10]],
[[8], [11]]
]
# Shape of each array is [3,2,1]
Теперь объединение обоих этих массивов в 3-е измерение просто означает, что результат должен выглядеть, как и ожидалось, следующим образом:
dstack([a,b]) = [
[[0, 6], [3, 9]],
[[1, 7], [4, 10]],
[[2, 8], [5, 11]]
]
# Shape of the combined array is [3,2,2]
Надеюсь, это поможет.