Что делает `tf.strided_slice()` do?
Мне интересно, что действительно делает оператор tf.strided_slice()
.
doc говорит,
В первый порядок эта операция извлекает срез конца конца - начинается с тензорного ввода, начиная с места, указанного в начале. Срез продолжается добавлением шага к индексу begin, пока все размеры не будут меньше конца. Обратите внимание, что компоненты шага могут быть отрицательными, что вызывает обратный срез.
И в образце
# 'input' is [[[1, 1, 1], [2, 2, 2]],
# [[3, 3, 3], [4, 4, 4]],
# [[5, 5, 5], [6, 6, 6]]]
tf.slice(input, [1, 0, 0], [2, 1, 3], [1, 1, 1]) ==> [[[3, 3, 3]]]
tf.slice(input, [1, 0, 0], [2, 2, 3], [1, 1, 1]) ==> [[[3, 3, 3],
[4, 4, 4]]]
tf.slice(input, [1, 1, 0], [2, -1, 3], [1, -1, 1]) ==>[[[4, 4, 4],
[3, 3, 3]]]
Итак, в моем понимании документа первый образец (tf.slice(input, begin=[1, 0, 0], end=[2, 1, 3], strides=[1, 1, 1])
),
- итоговый размер
end - begin = [1, 1, 3]
. Результат образца показывает [[[3, 3, 3,]]]
, что форма [1, 1, 3]
, кажется ОК.
- первый элемент результата находится в
begin = [1, 0, 0]
. Первый элемент результата выборки 3
, который равен input[1,0,0]
, кажется ОК.
- фрагмент продолжается добавлением шага к индексу begin. Таким образом, второй элемент результата должен быть
input[begin + strides] = input[2, 1, 1] = 6
, но образец показывает, что второй элемент 3
.
Что strided_slice()
делает?
(Примечание: имена методов в образцах, а последний пример неверен.)
Ответы
Ответ 1
Ошибка в вашем аргументе заключается в том, что вы непосредственно добавляете элементы strides
и begin
по элементам. Это сделает функцию намного менее полезной. Вместо этого он увеличивает список begin
по одному измерению за раз, начиная с последнего измерения.
Позвольте решить часть первого примера по частям. begin = [1, 0, 0]
и end = [2, 1, 3]
. Кроме того, все strides
являются 1
. Работайте в обратном направлении, начиная с последнего измерения.
Начните с элемента [1,0,0]
. Теперь увеличивайте последнее измерение только на его количество шагов, давая вам [1,0,1]
. Продолжайте делать это, пока не достигнете предела. Что-то вроде [1,0,2]
, [1,0,3]
(конец цикла). Теперь в вашей следующей итерации начните с возрастания второго до последнего измерения и сброса последнего измерения, [1,1,0]
. Здесь второе до последнего измерения равно end[1]
, поэтому переходим к первому измерению (от третьего к последнему) и reset остальные, предоставляя вам [2,0,0]
. Опять же, вы находитесь в первом пределе измерения, поэтому выходите из цикла.
Следующий код представляет собой рекурсивную реализацию того, что я описал выше,
# Assume global `begin`, `end` and `stride`
def iterate(active, dim):
if dim == len(begin):
# last dimension incremented, work on the new matrix
# Note that `active` and `begin` are lists
new_matrix[active - begin] = old_matrix[active]
else:
for i in range(begin[dim], end[dim], stride[dim]):
new_active = copy(active)
new_active[dim] = i
iterate(new_active, dim + 1)
iterate(begin, 0)
Ответ 2
Я немного экспериментировал с этим методом, который дал мне некоторые идеи, которые, я думаю, могут быть полезны. скажем, у нас есть тензор.
a = np.array([[[1, 1.2, 1.3], [2, 2.2, 2.3], [7, 7.2, 7.3]],
[[3, 3.2, 3.3], [4, 4.2, 4.3], [8, 8.2, 8.3]],
[[5, 5.2, 5.3], [6, 6.2, 6.3], [9, 9.2, 9.3]]])
shape = (3,3,3)
strided_slice()
требуется 4 требуемых аргумента input_, begin, end, strides
, в которых мы приводим наш a
как аргумент input_
.
Как и в случае с методом tf.slice()
, аргумент begin
основан на нулевом значении и остальном аргументе на основе аргументов. Однако в документах begin
и end
оба относятся к нулевому.
Функциональность метода довольно проста:
Он работает как итерация по петле, где begin
- это местоположение элемента в тензоре, от которого начинается цикл, и end
находится там, где он останавливается.
tf.strided_slice(a, [0, 0, 0], [3, 3, 3], [1, 1, 1])
output = the tensor itself
tf.strided_slice(a, [0, 0, 0], [3, 3, 3], [2, 2, 2])
output = [[[ 1. 1.3]
[ 7. 7.3]]
[[ 5. 5.3]
[ 9. 9.3]]]
strides
являются шагами, по которым цикл повторяется, здесь [2,2,2]
делает метод для получения значений, начиная с (0,0,0), (0,0,2), (0,2,0), (0,2,2), (2,0,0), (2,0,2)..... в тензоре a
.
tf.strided_slice(input3, [1, 1, 0], [2, -1, 3], [1, 1, 1])
будет производить выход, аналогичный tf.strided_slice(input3, [1, 1, 0], [2, 2, 3], [1, 1, 1])
, поскольку тензор a
имеет shape = (3,3,3)
.
Ответ 3
Концептуализация, которая действительно помогла мне понять это, заключалась в том, что эта функция эмулирует поведение индексации массивов numpy.
Если вы знакомы с массивами numpy, вы узнаете, что вы можете делать фрагменты с помощью input[start1:end1:step1, start2:end2:step2, ... startN:endN:stepN]
. В принципе, очень сжатый способ записи циклов for
для получения определенных элементов массива.
(Если вы знакомы с индексацией python, вы знаете, что вы можете захватить срез массива через input[start:end:step]
. Массивы Numpy, которые могут быть вложенными, используют вышеуказанный набор объектов среза.)
Ну, strided_slice
просто позволяет вам делать эту причудливую индексацию без синтаксического сахара. Пример numpy сверху просто становится
# input[start1:end1:step1, start2:end2:step2, ... startN:endN:stepN]
tf.strided_slice(input, [start1, start2, ..., startN],
[end1, end2, ..., endN], [step1, step2, ..., stepN])
Документация немного сбивает с толку об этом в том смысле, что:
a) begin
- end
не является строго формой возвращаемого значения:
Документация утверждает иначе, но это справедливо только в том случае, если ваши шаги - все.
Примеры:
rank1 = tf.constant(list(range(10)))
# The below op is basically:
# rank1[1:10:2] => [1, 3, 5, 7, 9]
tf.strided_slice(rank1, [1], [10], [2])
# [10,10] grid of the numbers from 0 to 99
rank2 = tf.constant([[i+j*10 for i in range(10)] for j in range(10)])
# The below op is basically:
# rank2[3:7:1, 5:10:2] => numbers 30 - 69, ending in 5, 7, or 9
sliced = tf.strided_slice(rank2, [3, 5], [7, 10], [1, 2])
# The below op is basically:
# rank2[3:7:1] => numbers 30 - 69
sliced = tf.strided_slice(rank2, [3], [7], [1])
b) он утверждает, что "begin
, end
и strides
будет всей длиной n, где n вообще не совпадает с размерностью input
"
Похоже, что размерность означает ранг здесь, но input
должен быть тензором, по крайней мере, ранга-n; он не может быть ниже (см. выше пример ранга 2).
N.B. Я ничего не сказал/не изучил функцию маскировки, но это выходит за рамки вопроса.
Ответ 4
tf.strided_slice() используется для нарезки стиля numpy тензорной переменной.
Он имеет 4 параметра в целом: ввод, начало, конец, шаг. Срез продолжается добавлением шага к индексу начала, пока все размеры не будут меньше конца. Например:
Возьмем тензорную константу с названием "образец" размеров: [3,2,3]
import tensorflow as tf
sample = tf.constant(
[[[11, 12, 13], [21, 22, 23]],
[[31, 32, 33], [41, 42, 43]],
[[51, 52, 53], [61, 62, 63]]])
slice = tf.strided_slice(sample, begin=[0,0,0], end=[3,2,3], strides=[2,2,2])
with tf.Session() as sess:
print(sess.run(slice))
Теперь выход будет:
[[[11 13]]
[[51 53]]]
Это связано с тем, что шаг начинается с [0,0,0] и переходит в [2,1,2], отбрасывая любые несуществующие данные, такие как:
[[0,0,0], [0,0,2], [0,2,0], [0,2,2],
[2,0,0], [2,0,2], [2,2,0], [2,2,2]]
Если вы используете [1,1,1] в качестве шага, тогда он просто распечатает все значения.