Посмотреть на TensorFlow.org | Запустить в Google Colab | Посмотреть исходный код на GitHub | Скачать блокнот |
import tensorflow as tf
import numpy as np
Тензоры — это многомерные массивы с единым типом (называемым dtype
). Вы можете увидеть все поддерживаемые dtypes
в tf.dtypes.DType
.
Если вы знакомы с NumPy , тензоры (вроде) похожи на np.arrays
.
Все тензоры неизменяемы, как числа и строки Python: вы никогда не сможете обновить содержимое тензора, только создадите новый.
Основы
Давайте создадим несколько основных тензоров.
Вот "скалярный" или "ранг-0" тензор. Скаляр содержит одно значение и не содержит «осей».
# This will be an int32 tensor by default; see "dtypes" below.
rank_0_tensor = tf.constant(4)
print(rank_0_tensor)
tf.Tensor(4, shape=(), dtype=int32)
Тензор «вектор» или «ранг 1» похож на список значений. Вектор имеет одну ось:
# Let's make this a float tensor.
rank_1_tensor = tf.constant([2.0, 3.0, 4.0])
print(rank_1_tensor)
tf.Tensor([2. 3. 4.], shape=(3,), dtype=float32)
«Матричный» или тензор «ранга 2» имеет две оси:
# If you want to be specific, you can set the dtype (see below) at creation time
rank_2_tensor = tf.constant([[1, 2],
[3, 4],
[5, 6]], dtype=tf.float16)
print(rank_2_tensor)
tf.Tensor( [[1. 2.] [3. 4.] [5. 6.]], shape=(3, 2), dtype=float16)
Скаляр, форма: [] | Вектор, форма: [3] | Матрица, форма: [3, 2] |
---|---|---|
Тензоры могут иметь больше осей; вот тензор с тремя осями:
# There can be an arbitrary number of
# axes (sometimes called "dimensions")
rank_3_tensor = tf.constant([
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]],
[[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]],
[[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]],])
print(rank_3_tensor)
tf.Tensor( [[[ 0 1 2 3 4] [ 5 6 7 8 9]] [[10 11 12 13 14] [15 16 17 18 19]] [[20 21 22 23 24] [25 26 27 28 29]]], shape=(3, 2, 5), dtype=int32)
Есть много способов визуализировать тензор с более чем двумя осями.
3-осевой тензор, форма: [3, 2, 5] | ||
---|---|---|
Вы можете преобразовать тензор в массив NumPy либо с помощью np.array
, либо с помощью метода tensor.numpy
:
np.array(rank_2_tensor)
array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16)
rank_2_tensor.numpy()
array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16)
Тензоры часто содержат числа с плавающей запятой и целые числа, но имеют много других типов, в том числе:
- комплексные числа
- струны
Базовый класс tf.Tensor
требует, чтобы тензоры были «прямоугольными», то есть вдоль каждой оси все элементы имели одинаковый размер. Однако существуют специализированные типы тензоров, которые могут обрабатывать различные формы:
- Рваные тензоры (см. RaggedTensor ниже)
- Разреженные тензоры (см. SparseTensor ниже)
Вы можете выполнять базовые математические операции с тензорами, включая сложение, поэлементное умножение и матричное умножение.
a = tf.constant([[1, 2],
[3, 4]])
b = tf.constant([[1, 1],
[1, 1]]) # Could have also said `tf.ones([2,2])`
print(tf.add(a, b), "\n")
print(tf.multiply(a, b), "\n")
print(tf.matmul(a, b), "\n")
tf.Tensor( [[2 3] [4 5]], shape=(2, 2), dtype=int32) tf.Tensor( [[1 2] [3 4]], shape=(2, 2), dtype=int32) tf.Tensor( [[3 3] [7 7]], shape=(2, 2), dtype=int32)
print(a + b, "\n") # element-wise addition
print(a * b, "\n") # element-wise multiplication
print(a @ b, "\n") # matrix multiplication
tf.Tensor( [[2 3] [4 5]], shape=(2, 2), dtype=int32) tf.Tensor( [[1 2] [3 4]], shape=(2, 2), dtype=int32) tf.Tensor( [[3 3] [7 7]], shape=(2, 2), dtype=int32)
Тензоры используются во всех видах операций (ops).
c = tf.constant([[4.0, 5.0], [10.0, 1.0]])
# Find the largest value
print(tf.reduce_max(c))
# Find the index of the largest value
print(tf.argmax(c))
# Compute the softmax
print(tf.nn.softmax(c))
tf.Tensor(10.0, shape=(), dtype=float32) tf.Tensor([1 0], shape=(2,), dtype=int64) tf.Tensor( [[2.6894143e-01 7.3105854e-01] [9.9987662e-01 1.2339458e-04]], shape=(2, 2), dtype=float32)
О фигурах
Тензоры имеют форму. Немного лексики:
- Форма : длина (количество элементов) каждой из осей тензора.
- Ранг : Количество осей тензора. Скаляр имеет ранг 0, вектор имеет ранг 1, матрица имеет ранг 2.
- Ось или размерность : конкретная размерность тензора.
- Размер : общее количество элементов в тензоре, вектор формы произведения.
Тензоры и объекты tf.TensorShape
имеют удобные свойства для доступа к ним:
rank_4_tensor = tf.zeros([3, 2, 4, 5])
Тензор ранга 4, форма: [3, 2, 4, 5] | |
---|---|
print("Type of every element:", rank_4_tensor.dtype)
print("Number of axes:", rank_4_tensor.ndim)
print("Shape of tensor:", rank_4_tensor.shape)
print("Elements along axis 0 of tensor:", rank_4_tensor.shape[0])
print("Elements along the last axis of tensor:", rank_4_tensor.shape[-1])
print("Total number of elements (3*2*4*5): ", tf.size(rank_4_tensor).numpy())
Type of every element: <dtype: 'float32'> Number of axes: 4 Shape of tensor: (3, 2, 4, 5) Elements along axis 0 of tensor: 3 Elements along the last axis of tensor: 5 Total number of elements (3*2*4*5): 120
Хотя на оси часто ссылаются по их индексам, вы всегда должны следить за значением каждого из них. Часто оси упорядочиваются от глобальных к локальным: сначала идет пакетная ось, затем пространственные измерения, а в последнюю очередь - функции для каждого местоположения. Таким образом, векторы признаков представляют собой непрерывные области памяти.
Типичный порядок осей |
---|
Индексация
Одноосное индексирование
TensorFlow следует стандартным правилам индексации Python, аналогичным индексации списка или строки в Python , и основным правилам индексации NumPy.
- индексы начинаются с
0
- отрицательные индексы отсчитываются в обратном порядке с конца
- двоеточия,
:
, используются для срезов:start:stop:step
rank_1_tensor = tf.constant([0, 1, 1, 2, 3, 5, 8, 13, 21, 34])
print(rank_1_tensor.numpy())
[ 0 1 1 2 3 5 8 13 21 34]
Индексация со скаляром удаляет ось:
print("First:", rank_1_tensor[0].numpy())
print("Second:", rank_1_tensor[1].numpy())
print("Last:", rank_1_tensor[-1].numpy())
First: 0 Second: 1 Last: 34
Индексирование с помощью фрагмента :
сохраняет ось:
print("Everything:", rank_1_tensor[:].numpy())
print("Before 4:", rank_1_tensor[:4].numpy())
print("From 4 to the end:", rank_1_tensor[4:].numpy())
print("From 2, before 7:", rank_1_tensor[2:7].numpy())
print("Every other item:", rank_1_tensor[::2].numpy())
print("Reversed:", rank_1_tensor[::-1].numpy())
Everything: [ 0 1 1 2 3 5 8 13 21 34] Before 4: [0 1 1 2] From 4 to the end: [ 3 5 8 13 21 34] From 2, before 7: [1 2 3 5 8] Every other item: [ 0 1 3 8 21] Reversed: [34 21 13 8 5 3 2 1 1 0]
Многоосевое индексирование
Тензоры более высокого ранга индексируются путем передачи нескольких индексов.
Те же самые правила, что и в случае с одной осью, применяются к каждой оси независимо.
print(rank_2_tensor.numpy())
[[1. 2.] [3. 4.] [5. 6.]]
При передаче целого числа для каждого индекса результатом является скаляр.
# Pull out a single value from a 2-rank tensor
print(rank_2_tensor[1, 1].numpy())
4.0
Вы можете индексировать, используя любую комбинацию целых чисел и срезов:
# Get row and column tensors
print("Second row:", rank_2_tensor[1, :].numpy())
print("Second column:", rank_2_tensor[:, 1].numpy())
print("Last row:", rank_2_tensor[-1, :].numpy())
print("First item in last column:", rank_2_tensor[0, -1].numpy())
print("Skip the first row:")
print(rank_2_tensor[1:, :].numpy(), "\n")
Second row: [3. 4.] Second column: [2. 4. 6.] Last row: [5. 6.] First item in last column: 2.0 Skip the first row: [[3. 4.] [5. 6.]]
Вот пример с 3-осевым тензором:
print(rank_3_tensor[:, :, 4])
tf.Tensor( [[ 4 9] [14 19] [24 29]], shape=(3, 2), dtype=int32)
Выбор последней функции во всех местоположениях в каждом примере в пакете | |
---|---|
Прочтите руководство по нарезке тензоров, чтобы узнать, как применять индексирование для управления отдельными элементами тензоров.
Управление фигурами
Изменение формы тензора очень полезно.
# Shape returns a `TensorShape` object that shows the size along each axis
x = tf.constant([[1], [2], [3]])
print(x.shape)
(3, 1)
# You can convert this object into a Python list, too
print(x.shape.as_list())
[3, 1]
Вы можете преобразовать тензор в новую форму. Операция tf.reshape
выполняется быстро и дешево, поскольку базовые данные не нужно дублировать.
# You can reshape a tensor to a new shape.
# Note that you're passing in a list
reshaped = tf.reshape(x, [1, 3])
print(x.shape)
print(reshaped.shape)
(3, 1) (1, 3)
Данные сохраняют свой макет в памяти, и создается новый тензор с запрошенной формой, указывающий на те же данные. TensorFlow использует упорядочение памяти по строкам в стиле C, где увеличение самого правого индекса соответствует одному шагу в памяти.
print(rank_3_tensor)
tf.Tensor( [[[ 0 1 2 3 4] [ 5 6 7 8 9]] [[10 11 12 13 14] [15 16 17 18 19]] [[20 21 22 23 24] [25 26 27 28 29]]], shape=(3, 2, 5), dtype=int32)
Если вы сгладите тензор, вы увидите, в каком порядке он расположен в памяти.
# A `-1` passed in the `shape` argument says "Whatever fits".
print(tf.reshape(rank_3_tensor, [-1]))
tf.Tensor( [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29], shape=(30,), dtype=int32)
Обычно единственным разумным использованием tf.reshape
является объединение или разделение соседних осей (или добавление/удаление 1
с).
Для этого тензора 3x2x5 изменение формы до (3x2)x5 или 3x(2x5) является разумным решением, поскольку срезы не смешиваются:
print(tf.reshape(rank_3_tensor, [3*2, 5]), "\n")
print(tf.reshape(rank_3_tensor, [3, -1]))
tf.Tensor( [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19] [20 21 22 23 24] [25 26 27 28 29]], shape=(6, 5), dtype=int32) tf.Tensor( [[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24 25 26 27 28 29]], shape=(3, 10), dtype=int32)
Некоторые хорошие переделки. | ||
---|---|---|
Изменение формы будет «работать» для любой новой формы с тем же общим количеством элементов, но оно не принесет ничего полезного, если вы не соблюдаете порядок осей.
Перестановка осей в tf.reshape
не работает; вам нужен tf.transpose
для этого.
# Bad examples: don't do this
# You can't reorder axes with reshape.
print(tf.reshape(rank_3_tensor, [2, 3, 5]), "\n")
# This is a mess
print(tf.reshape(rank_3_tensor, [5, 6]), "\n")
# This doesn't work at all
try:
tf.reshape(rank_3_tensor, [7, -1])
except Exception as e:
print(f"{type(e).__name__}: {e}")
tf.Tensor( [[[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] [[15 16 17 18 19] [20 21 22 23 24] [25 26 27 28 29]]], shape=(2, 3, 5), dtype=int32) tf.Tensor( [[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17] [18 19 20 21 22 23] [24 25 26 27 28 29]], shape=(5, 6), dtype=int32) InvalidArgumentError: Input to reshape is a tensor with 30 values, but the requested shape requires a multiple of 7 [Op:Reshape]
Какие-то неудачные переделки. | ||
---|---|---|
Вы можете столкнуться с не полностью определенными формами. Либо фигура содержит None
(длина оси неизвестна), либо вся фигура None
(ранг тензора неизвестен).
За исключением tf.RaggedTensor , такие формы будут встречаться только в контексте символических API построения графиков TensorFlow:
Подробнее о DTypes
Чтобы проверить тип данных tf.Tensor
, используйте свойство Tensor.dtype
.
При создании tf.Tensor
из объекта Python вы можете дополнительно указать тип данных.
Если вы этого не сделаете, TensorFlow выбирает тип данных, который может представлять ваши данные. TensorFlow преобразует целые числа Python в tf.int32
и числа Python с плавающей запятой в tf.float32
. В противном случае TensorFlow использует те же правила, что и NumPy при преобразовании в массивы.
Вы можете переходить от типа к типу.
the_f64_tensor = tf.constant([2.2, 3.3, 4.4], dtype=tf.float64)
the_f16_tensor = tf.cast(the_f64_tensor, dtype=tf.float16)
# Now, cast to an uint8 and lose the decimal precision
the_u8_tensor = tf.cast(the_f16_tensor, dtype=tf.uint8)
print(the_u8_tensor)
tf.Tensor([2 3 4], shape=(3,), dtype=uint8)
Вещание
Вещание — это концепция, заимствованная из эквивалентной функции в NumPy . Короче говоря, при определенных условиях меньшие тензоры автоматически «растягиваются» для соответствия большим тензорам при выполнении над ними комбинированных операций.
Самый простой и распространенный случай — это когда вы пытаетесь умножить или добавить тензор к скаляру. В этом случае скаляр передается таким же образом, как и другой аргумент.
x = tf.constant([1, 2, 3])
y = tf.constant(2)
z = tf.constant([2, 2, 2])
# All of these are the same computation
print(tf.multiply(x, 2))
print(x * y)
print(x * z)
tf.Tensor([2 4 6], shape=(3,), dtype=int32) tf.Tensor([2 4 6], shape=(3,), dtype=int32) tf.Tensor([2 4 6], shape=(3,), dtype=int32)
Точно так же оси с длиной 1 могут быть растянуты, чтобы соответствовать другим аргументам. Оба аргумента могут быть растянуты в одном и том же вычислении.
В этом случае матрица 3x1 поэлементно умножается на матрицу 1x4, чтобы получить матрицу 3x4. Обратите внимание, что начальная 1 не является обязательной: форма y — [4]
.
# These are the same computations
x = tf.reshape(x,[3,1])
y = tf.range(1, 5)
print(x, "\n")
print(y, "\n")
print(tf.multiply(x, y))
tf.Tensor( [[1] [2] [3]], shape=(3, 1), dtype=int32) tf.Tensor([1 2 3 4], shape=(4,), dtype=int32) tf.Tensor( [[ 1 2 3 4] [ 2 4 6 8] [ 3 6 9 12]], shape=(3, 4), dtype=int32)
Широковещательное добавление: [3, 1] умножить на [1, 4] дает [3,4] |
---|
Вот та же операция без трансляции:
x_stretch = tf.constant([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
y_stretch = tf.constant([[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]])
print(x_stretch * y_stretch) # Again, operator overloading
tf.Tensor( [[ 1 2 3 4] [ 2 4 6 8] [ 3 6 9 12]], shape=(3, 4), dtype=int32)
В большинстве случаев широковещательная рассылка эффективна как по времени, так и по пространству, поскольку широковещательная операция никогда не материализует расширенные тензоры в памяти.
Вы видите, как выглядит вещание, используя tf.broadcast_to
.
print(tf.broadcast_to(tf.constant([1, 2, 3]), [3, 3]))
tf.Tensor( [[1 2 3] [1 2 3] [1 2 3]], shape=(3, 3), dtype=int32)
Например, в отличие от математической операции, broadcast_to
не делает ничего особенного для экономии памяти. Здесь вы материализуете тензор.
Это может стать еще более сложным. В этом разделе книги Джейка ВандерПласа « Справочник по науке о данных Python» показаны другие приемы вещания (опять же в NumPy).
tf.convert_to_tensor
Большинство операций, таких как tf.matmul
и tf.reshape
принимают аргументы класса tf.Tensor
. Однако вы заметите, что в приведенном выше случае принимаются объекты Python в форме тензоров.
Большинство, но не все операции вызывают convert_to_tensor
для нетензорных аргументов. Существует реестр преобразований, и большинство классов объектов, таких как ndarray NumPy, ndarray
, TensorShape
Python и tf.Variable
, будут конвертироваться автоматически.
См. tf.register_tensor_conversion_function
для более подробной информации, и если у вас есть собственный тип, который вы хотели бы автоматически преобразовать в тензор.
Рваные тензоры
Тензор с переменным числом элементов вдоль некоторой оси называется «рваным». Используйте tf.ragged.RaggedTensor
для рваных данных.
Например, This нельзя представить в виде обычного тензора:
A tf.RaggedTensor , shape: [4, None] |
---|
ragged_list = [
[0, 1, 2, 3],
[4, 5],
[6, 7, 8],
[9]]
try:
tensor = tf.constant(ragged_list)
except Exception as e:
print(f"{type(e).__name__}: {e}")
ValueError: Can't convert non-rectangular Python sequence to Tensor.
Вместо этого создайте tf.RaggedTensor
, используя tf.ragged.constant
:
ragged_tensor = tf.ragged.constant(ragged_list)
print(ragged_tensor)
<tf.RaggedTensor [[0, 1, 2, 3], [4, 5], [6, 7, 8], [9]]>
Форма tf.RaggedTensor
будет содержать несколько осей неизвестной длины:
print(ragged_tensor.shape)
(4, None)
Тензоры струн
tf.string
— это dtype
, то есть вы можете представлять данные в виде строк (байтовых массивов переменной длины) в тензорах.
Строки являются атомарными и не могут быть проиндексированы так, как строки Python. Длина струны не является одной из осей тензора. См. tf.strings
для функций для управления ими.
Вот скалярный тензор струны:
# Tensors can be strings, too here is a scalar string.
scalar_string_tensor = tf.constant("Gray wolf")
print(scalar_string_tensor)
tf.Tensor(b'Gray wolf', shape=(), dtype=string)
И вектор строк:
Вектор строк, форма: [3,] |
---|
# If you have three string tensors of different lengths, this is OK.
tensor_of_strings = tf.constant(["Gray wolf",
"Quick brown fox",
"Lazy dog"])
# Note that the shape is (3,). The string length is not included.
print(tensor_of_strings)
tf.Tensor([b'Gray wolf' b'Quick brown fox' b'Lazy dog'], shape=(3,), dtype=string)
В приведенной выше распечатке префикс b
указывает, что tf.string
dtype — это не строка Unicode, а строка байтов. См. Учебник по Unicode , чтобы узнать больше о работе с текстом Unicode в TensorFlow.
Если вы передаете символы Unicode, они кодируются utf-8.
tf.constant("🥳👍")
<tf.Tensor: shape=(), dtype=string, numpy=b'\xf0\x9f\xa5\xb3\xf0\x9f\x91\x8d'>
Некоторые основные функции со строками можно найти в tf.strings
, включая tf.strings.split
.
# You can use split to split a string into a set of tensors
print(tf.strings.split(scalar_string_tensor, sep=" "))
tf.Tensor([b'Gray' b'wolf'], shape=(2,), dtype=string)
# ...but it turns into a `RaggedTensor` if you split up a tensor of strings,
# as each string might be split into a different number of parts.
print(tf.strings.split(tensor_of_strings))
<tf.RaggedTensor [[b'Gray', b'wolf'], [b'Quick', b'brown', b'fox'], [b'Lazy', b'dog']]>
Разделение на три струны, форма: [3, None] |
---|
И tf.string.to_number
:
text = tf.constant("1 10 100")
print(tf.strings.to_number(tf.strings.split(text, " ")))
tf.Tensor([ 1. 10. 100.], shape=(3,), dtype=float32)
Хотя вы не можете использовать tf.cast
для преобразования строкового тензора в числа, вы можете преобразовать его в байты, а затем в числа.
byte_strings = tf.strings.bytes_split(tf.constant("Duck"))
byte_ints = tf.io.decode_raw(tf.constant("Duck"), tf.uint8)
print("Byte strings:", byte_strings)
print("Bytes:", byte_ints)
Byte strings: tf.Tensor([b'D' b'u' b'c' b'k'], shape=(4,), dtype=string) Bytes: tf.Tensor([ 68 117 99 107], shape=(4,), dtype=uint8)
# Or split it up as unicode and then decode it
unicode_bytes = tf.constant("アヒル 🦆")
unicode_char_bytes = tf.strings.unicode_split(unicode_bytes, "UTF-8")
unicode_values = tf.strings.unicode_decode(unicode_bytes, "UTF-8")
print("\nUnicode bytes:", unicode_bytes)
print("\nUnicode chars:", unicode_char_bytes)
print("\nUnicode values:", unicode_values)
Unicode bytes: tf.Tensor(b'\xe3\x82\xa2\xe3\x83\x92\xe3\x83\xab \xf0\x9f\xa6\x86', shape=(), dtype=string) Unicode chars: tf.Tensor([b'\xe3\x82\xa2' b'\xe3\x83\x92' b'\xe3\x83\xab' b' ' b'\xf0\x9f\xa6\x86'], shape=(5,), dtype=string) Unicode values: tf.Tensor([ 12450 12498 12523 32 129414], shape=(5,), dtype=int32)
tf.string
используется для всех необработанных байтов данных в TensorFlow. Модуль tf.io
содержит функции для преобразования данных в байты и обратно, включая декодирование изображений и разбор csv.
Разреженные тензоры
Иногда ваши данные разрежены, например, очень широкое пространство для встраивания. TensorFlow поддерживает tf.sparse.SparseTensor
и связанные операции для эффективного хранения разреженных данных.
tf.SparseTensor , форма: [3, 4] |
---|
# Sparse tensors store values by index in a memory-efficient manner
sparse_tensor = tf.sparse.SparseTensor(indices=[[0, 0], [1, 2]],
values=[1, 2],
dense_shape=[3, 4])
print(sparse_tensor, "\n")
# You can convert sparse tensors to dense
print(tf.sparse.to_dense(sparse_tensor))
SparseTensor(indices=tf.Tensor( [[0 0] [1 2]], shape=(2, 2), dtype=int64), values=tf.Tensor([1 2], shape=(2,), dtype=int32), dense_shape=tf.Tensor([3 4], shape=(2,), dtype=int64)) tf.Tensor( [[1 0 0 0] [0 0 2 0] [0 0 0 0]], shape=(3, 4), dtype=int32)