Тензоры в TensorFlow

TensorFlow, как видно из названия, является платформой для определения и выполнения вычислений с использованием тензоров. Тензор - это обобщение векторов и матриц на более высокие измерения. Внутри TensorFlow тензоры представлены в виде n-мерных массивов базовых типов данных.

При написании программы TensorFlow основным объектом, которым вы манипулируете и передаете, является tf.Tensor. Объект tf.Tensor представляет собой частично определенное вычисление, которое в итоге дает результат вычислений. Программы TensorFlow работают, сначала создавая граф объектов tf.Tensor и подробно описывая, как вычисляется каждый тензор на основе других доступных тензоров, а затем запуская части этого графа для получения результатов вычисления.

tf.Tensor обладает следующими параметрами:

  • тип данных (float32, int32, или string, например)
  • размеры (shape)

Все элементы тензора имеют одинаковый тип данных, и он всегда известен. Размеры (количество измерений и размер каждого измерения) могут быть известны только частично. Результатом большинства операций являются тензоры с известными размерами, если размеры на входе также полностью известны, но в некоторых случаях узнать размеры тензора можно только во время исполнения графа.

Есть несколько специальных видов тензоров, они будут рассмотрены в других разделах руководства TensorFlow. Основные виды тензоров следующие:

За исключением tf.Variable, значение тензора неизменяемо, т.е. в контексте одного выполнения тензор может иметь только одно значение. Однако вычисление одного и того же тензора дважды может вернуть различные значения; например, тот же тензор может быть результатом чтения данных с диска, или генерации случайного числа.

Ранг

Ранг объекта tf.Tensor это количество его измерений. Синонимами ранга являются порядок, степень, размерность. Обратите внимание, что ранг в TensorFlow это не то же самое, что и ранг матрицы в математике. Как показывает следующая таблица, каждый ранг в Tensorflow соответствует некоторой математической сущности:

Ранг Математическая сущность
0 Скаляр (только величина)
1 Вектор (величина и направление)
2 Матрица (таблица чисел)
3 3-Тензор (куб чисел)
n n-Тензор (ну вы поняли идею)

Ранг 0

Следующий фрагмент демонстрирует создание нескольких переменных ранга 0:

mammal = tf.Variable("Elephant", tf.string)
ignition = tf.Variable(451, tf.int16)
floating = tf.Variable(3.14159265359, tf.float64)
its_complicated = tf.Variable(12.3 - 4.85j, tf.complex64)

Замечание: Строка считается единым объектом в TensorFlow, а не последовательностью символов. Возможно иметь строковые скаляры, векторы строк и т.д.

Ранг 1

Для создания объекта tf.Tensor ранга 1, вы можете передать список элементов в качестве начальных значений. Например:

mystr = tf.Variable(["Hello"], tf.string)
cool_numbers  = tf.Variable([3.14159, 2.71828], tf.float32)
first_primes = tf.Variable([2, 3, 5, 7, 11], tf.int32)
its_very_complicated = tf.Variable([12.3 - 4.85j, 7.5 - 6.23j], tf.complex64)

Ранги более высокого порядка

Ранг 2 объекта tf.Tensor состоит как минимум из одной строки и одного столбца:

mymat = tf.Variable([[7],[11]], tf.int16)
myxor = tf.Variable([[False, True],[True, False]], tf.bool)
linear_squares = tf.Variable([[4], [9], [16], [25]], tf.int32)
squarish_squares = tf.Variable([ [4, 9], [16, 25] ], tf.int32)
rank_of_squares = tf.rank(squarish_squares)
mymatC = tf.Variable([[7],[11]], tf.int32)

Тензоры более высокого ранга, аналогично, состоят из n-мерных массивов. Например, при обработке изображений используется много тензоров ранга 4, с размерностями соответствующими номеру примера в пакете, высоте изображения, ширине изображения, и цветовому каналу.

my_image = tf.zeros([10, 299, 299, 3])  # размер пакета x высота x ширина x количество цветовых каналов

Получение ранга объекта tf.Tensor

Для определения ранга объекта tf.Tensor, вызовите метод tf.rank. Например, следующий метод программно определяет ранг tf.Tensor заданного выше:

r = tf.rank(my_image)
# После запуска графа, r станет равным 4.

Ссылки на срезы tf.Tensor

Поскольку tf.Tensor это n-мерный массив ячеек, для получения доступа к одной ячейке в tf.Tensor вам нужно указать n индексов.

Для тензоров ранга 0 (скаляров), индексы не нужны, поскольку это уже просто число.

Для тензора ранга 1 (вектор), передача единственного индекса даст вам доступ к числу:

my_scalar = my_vector[2]

Заметьте что индекс передаваемый в [] может сам быть скаляром tf.Tensor, если вы хотите динамически выбрать элемент из вектора.

Для тензоров ранга 2 или выше ситуация интереснее. Для tf.Tensor ранга 2, передача двух чисел возвращает как и ожидалось скаляр:

my_scalar = my_matrix[1, 2]

Передача одного числа, однако, возвращает подвектор матрицы следующим образом:

my_row_vector = my_matrix[2]
my_column_vector = my_matrix[:, 3]

Нотация : в синтаксисе выделения подмассива в python используется как "оставьте это измерение в покое". Это полезно в тензорах высокого ранга, поскольку позволяет получить доступ к подвекторам, подматрицам и даже другим подтензорам.

Размеры

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

Документация TensorFlow использует три условных обозначения для описания размерности тензора: ранг, размеры и количество измерений. Следующая таблица показывает как они соотносятся друг с другом:

Ранг Размеры Количество измерений Пример
0 [] 0-D 0-D тензор. Скаляр.
1 [D0] 1-D 1-D тензор размера [5].
2 [D0, D1] 2-D 2-D тензор размера [3, 4].
3 [D0, D1, D2] 3-D 3-D тензор размера [1, 4, 3].
n [D0, D1, ... Dn-1] n-D Тензор размера [D0, D1, ... Dn-1].

Размеры могут быть представлены в виде списков Python/кортежей целых чисел, или с tf.TensorShape.

Получение размера объекта tf.Tensor

Есть два способа получить размеры tf.Tensor. При построении графа часто полезно спросить, что уже известно о размерах тензора. Это можно сделать, прочитав свойство shape объекта tf.Tensor. Этот метод возвращает объект TensorShape, который является удобным способом представления частично определенных размеров (поскольку при построении графа не все размеры могут быть полностью известны).

Также можно получить tf.Tensor который представляет полностью определенные размеры другого tf.Tensor во время выполнения. Это делается вызовом операции tf.shape. Этим способом вы можете построить граф, который манипулирует размерами тензоров строя другие тензоры зависящие от динамических размеров входных tf.Tensor.

Например, так можно сделать вектор нулей того же размера, что и число столбцов данной матрицы:

zeros = tf.zeros(my_matrix.shape[1])

Изменение размеров tf.Tensor

Количество элементов тензора это произведение всех его измерений. Количество элементов скаляра всегда равно 1. Так как много разных размеров могут давать одно и то же число элементов часто удобно менять размеры tf.Tensor, не изменяя его элементы. Это может быть сделано с помощью tf.reshape.

Следующие примеры показывают как изменить размеры тензора:

rank_three_tensor = tf.ones([3, 4, 5])
matrix = tf.reshape(rank_three_tensor, [6, 10])  # Преобразование существущих данных
                                                 # в матрицу 6x10 
matrixB = tf.reshape(matrix, [3, -1])  # Преобразование существующих данных в 
                                       # матрицу 3x20. -1 говорит reshape что нужно
                                       # посчитать размер этого измерения.
matrixAlt = tf.reshape(matrixB, [4, 3, -1])  # Преобразование существующих данных в
                                             # тензор 4x3x5

# Отметим, что число элементов в преобразованных тензорах  должно совпадать
# с изначальным количеством. Поэтому следующий пример породит
# ошибку поскольку нет такого значения для последнего измерения
# при котором совпадет количество элементов.
yet_another = tf.reshape(matrixAlt, [13, 2, -1])  # ERROR!

Типы данных

В дополнение к размерностям, у тензоров есть тип данных. Обратитесь к странице tf.DType для полного списка типов данных.

У конкретного tf.Tensor не может быть более одного типа данных. Однако, возможно сериализовать произвольные структуры данных в string и хранить их в tf.Tensor.

Можно преобразовать tf.Tensor из одного типа данных в другой используя tf.cast:

# Преобразование константного целочисленного тензора в тензор с плавающей запятой.
float_tensor = tf.cast(tf.constant([1, 2, 3]), dtype=tf.float32)

Чтобы посмотреть тип данных tf.Tensor используйте свойство Tensor.dtype.

При создании tf.Tensor из объекта python вы можете опционально указать тип данных. Если вы этого не сделаете, TensorFlow выберет тип данных который может представлять ваши данные. TensorFlow преобраазует целые числа Python в tf.int32, а числа с плавающей запятой в tf.float32. В других случаях TensorFlow использует те же правила что и numpy при конвертации массивов.

Оценка тензоров

Как только был построен вычислительный граф, вы можете запустить вычисление, которое сгенерирует определенный tf.Tensor и извлечь присвоенное ему значение. Это часто полезно для отладки, а также для работы большей части TensorFlow.

Самый простой способ оценить Tensor - использовать метод Tensor.eval. Например:

constant = tf.constant([1, 2, 3])
tensor = constant * constant
print(tensor.eval())

Метод eval работает только когда активнасессия по умолчанию tf.Session (см. Графы и сессии для дополнительной информации).

Tensor.eval возвращает массив numpy с тем же содержанием что и тензор.

Иногда невозможно оценить tf.Tensor без контекста, потому что его значение может зависеть от динамической информации, которая недоступна. Например, тензоры, зависящие от placeholder, не могут быть оценены без предоставления значения для placeholder.

p = tf.placeholder(tf.float32)
t = p + 1.0
t.eval()  # Это не сработает, потому что  placeholder не получил значение.
t.eval(feed_dict={p:2.0})  # Это сработает, потому что мы передает
                           # значение в placeholder.

Обратите внимание, что можно использовать любой tf.Tensor, не только placeholder.

Конструкции других моделей могут усложнить оценивание tf.Tensor. TensorFlow не может оценить напрямую tf.Tensor определенные внутри функций или внутри конструкций потока управления. Если tf.Tensor зависит от значения из очереди, оценка tf.Tensor сработает только когда что-то поместят в очередь; в противном случае, оценка тензора зависнет. При работе с очередями, не забывайте вызвать tf.train.start_queue_runners перед оценкой любого tf.Tensor.