Тензор с неопределенным размером в тензорном потоке
Я играю с тензорным потоком и столкнулся с проблемой со следующим кодом:
def _init_parameters(self, input_data, labels):
# the input shape is (batch_size, input_size)
input_size = tf.shape(input_data)[1]
# labels in one-hot format have shape (batch_size, num_classes)
num_classes = tf.shape(labels)[1]
stddev = 1.0 / tf.cast(input_size, tf.float32)
w_shape = tf.pack([input_size, num_classes], 'w-shape')
normal_dist = tf.truncated_normal(w_shape, stddev=stddev, name='normaldist')
self.w = tf.Variable(normal_dist, name='weights')
(Я использую tf.pack
, как было предложено в этом вопросе, так как я получал ту же ошибку)
Когда я запускаю его (из более крупного script, который вызывает этот), я получаю эту ошибку:
ValueError: initial_value must have a shape specified: Tensor("normaldist:0", shape=TensorShape([Dimension(None), Dimension(None)]), dtype=float32)
Я попытался реплицировать процесс в интерактивной оболочке. В самом деле, размеры normal_dist
не определены, хотя имеющиеся значения существуют:
In [70]: input_size.eval()
Out[70]: 4
In [71]: num_classes.eval()
Out[71]: 3
In [72]: w_shape.eval()
Out[72]: array([4, 3], dtype=int32)
In [73]: normal_dist.eval()
Out[73]:
array([[-0.27035281, -0.223277 , 0.14694688],
[-0.16527176, 0.02180306, 0.00807841],
[ 0.22624688, 0.36425814, -0.03099642],
[ 0.25575709, -0.02765726, -0.26169327]], dtype=float32)
In [78]: normal_dist.get_shape()
Out[78]: TensorShape([Dimension(None), Dimension(None)])
Это странно. Тензорный поток генерирует вектор, но не может сказать его формы. Я что-то делаю неправильно?
Ответы
Ответ 1
Как говорит Ишамаэль, все тензоры имеют статическую форму, которая известна во время построения графика и доступна с помощью Tensor.get_shape()
; и динамическая форма, которая известна только во время выполнения и доступна путем извлечения значения тензора или передачи его оператору типа tf.shape
. Во многих случаях статические и динамические формы одинаковы, но они могут быть разными - статическая форма может быть частично определена - чтобы динамическая форма менялась от одного шага к следующему.
В вашем коде normal_dist
есть частично заданная статическая форма, потому что w_shape
- вычисленное значение. (TensorFlow иногда пытается оценить
эти вычисленные значения при времени построения графика, но он застревает в tf.pack
.) Он отображает форму TensorShape([Dimension(None), Dimension(None)])
, что означает "матрица с неизвестным количеством строк и столбцов", потому что она известна тем, что w_shape
является вектор длины 2, поэтому результирующий normal_dist
должен быть двумерным.
У вас есть два варианта решения этой проблемы. Вы можете установить статическую форму, как предполагает Ishamael, но для этого вам нужно знать форму при построении графика. Например, может работать следующее:
normal_dist.set_shape([input_data.get_shape()[1], labels.get_shape()[1]])
В качестве альтернативы вы можете передать validate_shape=False
в конструктор tf.Variable
. Это позволяет создать переменную с частично определенной формой, но она ограничивает количество информации о статической форме, которая может быть выведена позже на графике.
Ответ 2
Переменная может иметь динамическую форму. get_shape()
возвращает статическую форму.
В вашем случае у вас есть тензор, который имеет динамическую форму и в настоящее время имеет значение 4 × 3 (но в некоторый другой момент он может удерживать значение с другой формой - поскольку форма является динамической). Чтобы установить статическую форму, используйте set_shape(w_shape)
. После этого будет применена установленная вами форма, и тензор будет действительным initial_value
.
Ответ 3
Подобный вопрос хорошо объяснен в Часто задаваемые вопросы TF:
В TensorFlow тензор имеет как статическую (предполагаемую) форму, так и динамическая (истинная) форма. Статическая форма может быть прочитана с помощью tf.Tensor.get_shape
: эта форма выводится из операций которые были использованы для создания тензора и могут быть частично завершены. Если статическая форма не полностью определена, динамическая форма тензора t может быть определено путем оценки tf.shape(t)
.
Итак tf.shape()
возвращает вам тензор, всегда будет иметь размер shape=(N,)
и может быть рассчитан в сеансе:
a = tf.Variable(tf.zeros(shape=(2, 3, 4)))
with tf.Session() as sess:
print sess.run(tf.shape(a))
С другой стороны, вы можете извлечь статическую форму с помощью x.get_shape().as_list()
, и это можно вычислить где угодно.