#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Explanation: Copyright 2020 The TensorFlow Authors.
End of explanation
"""


import tensorflow as tf
import numpy as np

"""
Explanation: 张量简介
<table class="tfo-notebook-buttons" align="left">
  <td><a target="_blank" href="https://tensorflow.google.cn/guide/tensor"><img src="https://tensorflow.google.cn/images/tf_logo_32px.png">在  TensorFlow.org 上查看</a></td>
  <td><a target="_blank" href="https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/zh-cn/guide/tensor.ipynb"><img src="https://tensorflow.google.cn/images/colab_logo_32px.png">在 Google Colab 中运行 </a></td>
  <td><a target="_blank" href="https://github.com/tensorflow/docs-l10n/blob/master/site/zh-cn/guide/tensor.ipynb">     <img src="https://tensorflow.google.cn/images/GitHub-Mark-32px.png">    在 GitHub 上查看源代码</a></td>
  <td><a href="https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/zh-cn/guide/tensor.ipynb"><img src="https://tensorflow.google.cn/images/download_logo_32px.png">下载笔记本</a></td>
</table>
End of explanation
"""


# This will be an int32 tensor by default; see "dtypes" below.
rank_0_tensor = tf.constant(4)
print(rank_0_tensor)

"""
Explanation: 张量是具有统一类型（称为 dtype）的多维数组。您可以在 tf.dtypes.DType 中查看所有支持的 dtypes。
如果您熟悉 NumPy，就会知道张量与 np.arrays 有一定的相似性。
就像 Python 数值和字符串一样，所有张量都是不可变的：永远无法更新张量的内容，只能创建新的张量。
基础知识
我们来创建一些基本张量。
下面是一个“标量”（或称“0 秩”张量）。标量包含单个值，但没有“轴”。
End of explanation
"""


# Let's make this a float tensor.
rank_1_tensor = tf.constant([2.0, 3.0, 4.0])
print(rank_1_tensor)

"""
Explanation: “向量”（或称“1 秩”张量）就像一个值列表。向量有 1 个轴：
End of explanation
"""


# If we want to be specific, we 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)

"""
Explanation: “矩阵”（或称“2 秩”张量）有 2 个轴：
End of explanation
"""


# 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)

"""
Explanation: <table>
<tr>
  <th>标量，形状：<code>[]</code> </th>
  <th>向量，形状：<code>[3]</code> </th>
  <th>矩阵，形状：<code>[3, 2]</code> </th>
</tr>
<tr>
  <td>    <img src="images/tensor/scalar.png" alt="A scalar, the number 4"> </td>
  <td>    <img src="images/tensor/vector.png" alt="The line with 3 sections, each one containing a number."> </td>
  <td>    <img src="images/tensor/matrix.png" alt="A 3x2 grid, with each cell containing a number."> </td>
</tr>
</table>

张量的轴可能更多，下面是一个包含 3 个轴的张量：
End of explanation
"""


np.array(rank_2_tensor)

rank_2_tensor.numpy()

"""
Explanation: 对于包含 2 个以上的轴的张量，您可以通过多种方式加以呈现。
<table>
<tr>
  <th colspan="3">3 轴张量，形状：<code>[3, 2, 5]</code> </th>
</tr>
<tr>
</tr>
<tr>
  <td>    <img src="images/tensor/3-axis_numpy.png"> </td>
  <td>    <img src="images/tensor/3-axis_front.png"> </td>
  <td>    <img src="images/tensor/3-axis_block.png"> </td>
</tr>
</table>

通过使用 np.array 或 tensor.numpy 方法，您可以将张量转换为 NumPy 数组：
End of explanation
"""


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")

print(a + b, "\n") # element-wise addition
print(a * b, "\n") # element-wise multiplication
print(a @ b, "\n") # matrix multiplication

"""
Explanation: 张量通常包含浮点型和整型数据，但是还有许多其他数据类型，包括：

复杂的数值
字符串

tf.Tensor 基类要求张量是“矩形”——也就是说，每个轴上的每一个元素大小相同。但是，张量有可以处理不同形状的特殊类型。

不规则张量（参阅下文中的 RaggedTensor）
稀疏张量（参阅下文中的 SparseTensor）

您可以对张量执行基本数学运算，包括加法、逐元素乘法和矩阵乘法。
End of explanation
"""


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))

"""
Explanation: 各种运算 (op) 都可以使用张量。
End of explanation
"""


rank_4_tensor = tf.zeros([3, 2, 4, 5])

"""
Explanation: 形状简介
张量有形状。下面是几个相关术语：

形状：张量的每个轴的长度（元素数量）。
秩：张量轴数。标量的秩为 0，向量的秩为 1，矩阵的秩为 2。
轴或维度：张量的一个特殊维度。
大小：张量的总项数，即乘积形状向量

注：虽然您可能会看到“二维张量”之类的表述，但 2 秩张量通常并不是用来描述二维空间。
张量和 tf.TensorShape 对象提供了方便的属性来访问：
End of explanation
"""


print("Type of every element:", rank_4_tensor.dtype)
print("Number of dimensions:", 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())

"""
Explanation: <table>
<tr>
  <th colspan="2">4 秩张量，形状：<code>[3, 2, 4, 5]</code> </th>
</tr>
<tr>
  <td> <img src="images/tensor/shape.png" alt="A tensor shape is like a vector."> </td>
<td> <img src="images/tensor/4-axis_block.png" alt="A 4-axis tensor"> </td>
  </tr>
</table>
End of explanation
"""


rank_1_tensor = tf.constant([0, 1, 1, 2, 3, 5, 8, 13, 21, 34])
print(rank_1_tensor.numpy())

"""
Explanation: 虽然通常用索引来指代轴，但是您始终要记住每个轴的含义。轴一般按照从全局到局部的顺序进行排序：首先是批次轴，随后是空间维度，最后是每个位置的特征。这样，在内存中，特征向量就会位于连续的区域。
<table>
<tr>
<th>典型的轴顺序</th>
</tr>
<tr>
    <td> <img src="images/tensor/shape2.png" alt="Keep track of what each axis is. A 4-axis tensor might be: Batch, Width, Height, Features"> </td>
</tr>
</table>

索引
单轴索引
TensorFlow 遵循标准 Python 索引规则（类似于在 Python 中为列表或字符串编制索引）以及 NumPy 索引的基本规则。

索引从 0 开始编制
负索引表示按倒序编制索引
冒号 : 用于切片 start:stop:step
End of explanation
"""


print("First:", rank_1_tensor[0].numpy())
print("Second:", rank_1_tensor[1].numpy())
print("Last:", rank_1_tensor[-1].numpy())

"""
Explanation: 使用标量编制索引会移除轴：
End of explanation
"""


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())

"""
Explanation: 使用 : 切片编制索引会保留轴：
End of explanation
"""


print(rank_2_tensor.numpy())

"""
Explanation: 多轴索引
更高秩的张量通过传递多个索引来编制索引。
对于高秩张量的每个单独的轴，遵循与单轴情形完全相同的规则。
End of explanation
"""


# Pull out a single value from a 2-rank tensor
print(rank_2_tensor[1, 1].numpy())

"""
Explanation: 为每个索引传递一个整数，结果是一个标量。
End of explanation
"""


# 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")

"""
Explanation: 您可以使用整数与切片的任意组合编制索引：
End of explanation
"""


print(rank_3_tensor[:, :, 4])

"""
Explanation: 下面是一个 3 轴张量的示例：
End of explanation
"""


# Shape returns a `TensorShape` object that shows the size on each dimension
var_x = tf.Variable(tf.constant([[1], [2], [3]]))
print(var_x.shape)

# You can convert this object into a Python list, too
print(var_x.shape.as_list())

"""
Explanation: <table>
<tr>
<th colspan="2">选择批次中每个示例的所有位置的最后一个特征</th>
</tr>
<tr>
    <td> <img src="images/tensor/index1.png" alt="A 3x2x5 tensor with all the values at the index-4 of the last axis selected."> </td>
      <td> <img src="images/tensor/index2.png" alt="The selected values packed into a 2-axis tensor."> </td>
</tr>
</table>

请参阅张量切片指南，了解如何应用索引编制以操作张量中的各个元素。
操作形状
Reshaping a tensor is of great utility.
End of explanation
"""


# We can reshape a tensor to a new shape.
# Note that we're passing in a list
reshaped = tf.reshape(var_x, [1, 3])

print(var_x.shape)
print(reshaped.shape)

"""
Explanation: 通过重构可以改变张量的形状。重构的速度很快，资源消耗很低，因为不需要复制底层数据。
End of explanation
"""


print(rank_3_tensor)

"""
Explanation: 数据在内存中的布局保持不变，同时使用请求的形状创建一个指向同一数据的新张量。TensorFlow 采用 C 样式的“行优先”内存访问顺序，即最右侧的索引值递增对应于内存中的单步位移。
End of explanation
"""


# A `-1` passed in the `shape` argument says "Whatever fits".
print(tf.reshape(rank_3_tensor, [-1]))

"""
Explanation: 如果您展平张量，则可以看到它在内存中的排列顺序。
End of explanation
"""


print(tf.reshape(rank_3_tensor, [3*2, 5]), "\n")
print(tf.reshape(rank_3_tensor, [3, -1]))

"""
Explanation: 一般来说，tf.reshape 唯一合理的用途是用于合并或拆分相邻轴（或添加/移除 1）。
对于 3x2x5 张量，重构为 (3x2)x5 或 3x(2x5) 都合理，因为切片不会混淆：
End of explanation
"""


# 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}")

"""
Explanation: <table>
<th colspan="3">一些正确的重构示例。</th>
<tr>
  <td> <img src="images/tensor/reshape-before.png" alt="A 3x2x5 tensor"> </td>
  <td>   <img src="images/tensor/reshape-good1.png" alt="The same data reshaped to (3x2)x5"> </td>
  <td> <img src="images/tensor/reshape-good2.png" alt="The same data reshaped to 3x(2x5)"> </td>
</tr>
</table>

重构可以处理总元素个数相同的任何新形状，但是如果不遵从轴的顺序，则不会发挥任何作用。
利用 tf.reshape 无法实现轴的交换，要交换轴，您需要使用 tf.transpose。
End of explanation
"""


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, let's cast to an uint8 and lose the decimal precision
the_u8_tensor = tf.cast(the_f16_tensor, dtype=tf.uint8)
print(the_u8_tensor)

"""
Explanation: <table>
<th colspan="3">一些错误的重构示例。</th>
<tr>
  <td> <img src="images/tensor/reshape-bad.png" alt="You can't reorder axes, use tf.transpose for that"> </td>
  <td><img src="images/tensor/reshape-bad4.png" alt="Anything that mixes the slices of data together is probably wrong."></td>
  <td> <img src="images/tensor/reshape-bad2.png" alt="The new shape must fit exactly."> </td>
</tr>
</table>

您可能会遇到非完全指定的形状。要么是形状包含 None（轴长度未知），要么是整个形状为 None（张量的秩未知）。
除了 tf.RaggedTensor 外，这种情况只会在 TensorFlow 的符号化计算图构建 API 环境中出现：

tf.function
Keras 函数式 API。

DTypes 详解
使用 Tensor.dtype 属性可以检查 tf.Tensor 的数据类型。
从 Python 对象创建 tf.Tensor 时，您可以选择指定数据类型。
如果不指定，TensorFlow 会选择一个可以表示您的数据的数据类型。TensorFlow 将 Python 整数转换为 tf.int32，将 Python 浮点数转换为 tf.float32。另外，当转换为数组时，TensorFlow 会采用与 NumPy 相同的规则。
数据类型可以相互转换。
End of explanation
"""


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)

"""
Explanation: 广播
广播是从 NumPy 中的等效功能借用的一个概念。简而言之，在一定条件下，对一组张量执行组合运算时，为了适应大张量，会对小张量进行“扩展”。
最简单和最常见的例子是尝试将张量与标量相乘或相加。在这种情况下会对标量进行广播，使其变成与其他参数相同的形状。
End of explanation
"""


# 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))

"""
Explanation: 同样，可以扩展长度为 1 的轴，使其匹配其他参数。在同一个计算中可以同时扩展两个参数。
在本例中，一个 3x1 的矩阵与一个 1x4 进行元素级乘法运算，从而产生一个 3x4 的矩阵。注意前导 1 是可选的：y 的形状是 [4]。
End of explanation
"""


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

"""
Explanation: <table>
<tr>
  <th>广播相加：<code>[3, 1]</code> 乘以 <code>[1, 4]</code> 的结果是 <code>[3,4]</code> </th>
</tr>
<tr>
  <td> <img src="images/tensor/broadcasting.png" alt="Adding a 3x1 matrix to a 4x1 matrix results in a 3x4 matrix"> </td>
</tr>
</table>

下面是不使用广播的同一运算：
End of explanation
"""


print(tf.broadcast_to(tf.constant([1, 2, 3]), [3, 3]))

"""
Explanation: 在大多数情况下，广播的时间和空间效率更高，因为广播运算不会在内存中具体化扩展的张量。
使用 tf.broadcast_to 可以了解广播的运算方式。
End of explanation
"""


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}")

"""
Explanation: 与数学运算不同，broadcast_to 并不会节省内存。在这个示例中，您将具体化张量。
这可能会变得更复杂。Jake VanderPlas 的<em>《Python 数据科学手册》</em>一书中的这一节介绍了更多广播技巧（同样使用 NumPy）。
tf.convert_to_tensor
大部分运算（如 tf.matmul 和 tf.reshape）会使用 tf.Tensor 类的参数。不过，在上面的示例中，您会发现形状类似于张量的 Python 对象也可以接受。
大部分（但并非全部）运算会在非张量参数上调用 convert_to_tensor。我们提供了一个转换注册表，大多数对象类（如 NumPy 的 ndarray、TensorShape、Python 列表和 tf.Variable）都可以自动转换。
有关更多详细信息，请参阅 tf.register_tensor_conversion_function。如果您有自己的类型，则可能希望自动转换为张量。
不规则张量
如果张量的某个轴上的元素个数可变，则称为“不规则”张量。对于不规则数据，请使用 tf.ragged.RaggedTensor。
例如，下面的例子无法用规则张量表示：
<table>
<tr>
  <th>“tf.RaggedTensor”，形状：<code>[4, None]</code> </th>
</tr>
<tr>
  <td> <img src="images/tensor/ragged.png" alt="A 2-axis ragged tensor, each row can have a different length."> </td>
</tr>
</table>
End of explanation
"""


ragged_tensor = tf.ragged.constant(ragged_list)
print(ragged_tensor)

"""
Explanation: 应使用 tf.ragged.constant 来创建 tf.RaggedTensor：
End of explanation
"""


print(ragged_tensor.shape)

"""
Explanation: tf.RaggedTensor 的形状将包含一些具有未知长度的轴：
End of explanation
"""


# Tensors can be strings, too here is a scalar string.
scalar_string_tensor = tf.constant("Gray wolf")
print(scalar_string_tensor)

"""
Explanation: 字符串张量
tf.string 是一种 dtype，也就是说，在张量中，您可以用字符串（可变长度字节数组）来表示数据。
字符串是原子类型，无法像 Python 字符串一样编制索引。字符串的长度并不是张量的一个轴。有关操作字符串的函数，请参阅 tf.strings。
下面是一个标量字符串张量：
End of explanation
"""


# If we 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)

"""
Explanation: 下面是一个字符串向量：
<table>
<tr>
  <th>字符串向量，形状：<code>[3,]</code> </th>
</tr>
<tr>
  <td> <img src="images/tensor/strings.png" alt="The string length is not one of the tensor's axes."> </td>
</tr>
</table>
End of explanation
"""


tf.constant("🥳👍")

"""
Explanation: 在上面的打印输出中，b 前缀表示 tf.string dtype 不是 Unicode 字符串，而是字节字符串。有关在 TensorFlow 如何使用 Unicode 文本的详细信息，请参阅 Unicode 教程。
如果传递 Unicode 字符，则会使用 utf-8 编码。
End of explanation
"""


# We can use split to split a string into a set of tensors
print(tf.strings.split(scalar_string_tensor, sep=" "))

# ...but it turns into a `RaggedTensor` if we 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))

"""
Explanation: 在 tf.strings 中可以找到用于操作字符串的一些基本函数，包括 tf.strings.split。
End of explanation
"""


text = tf.constant("1 10 100")
print(tf.strings.to_number(tf.strings.split(text, " ")))

"""
Explanation: <table>
<tr>
  <th>三个字符串分割，形状：<code>[3, None]</code> </th>
</tr>
<tr>
  <td> <img src="images/tensor/string-split.png" alt="Splitting multiple strings returns a tf.RaggedTensor"> </td>
</tr>
</table>

以及 tf.string.to_number：
End of explanation
"""


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)

# 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)

"""
Explanation: 虽然不能使用 tf.cast 将字符串张量转换为数值，但是可以先将其转换为字节，然后转换为数值。
End of explanation
"""


# 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")

# We can convert sparse tensors to dense
print(tf.sparse.to_dense(sparse_tensor))

"""
Explanation: tf.string dtype 可用于 TensorFlow 中的所有原始字节数据。tf.io 模块包含在数据与字节类型之间进行相互转换的函数，包括解码图像和解析 csv 的函数。
稀疏张量
在某些情况下，数据很稀疏，比如说在一个非常宽的嵌入空间中。为了高效存储稀疏数据，TensorFlow 支持 tf.sparse.SparseTensor 和相关运算。
<table>
<tr>
  <th>“tf.SparseTensor”，形状：<code>[3, 4]</code> </th>
</tr>
<tr>
  <td> <img src="images/tensor/sparse.png" alt="An 3x4 grid, with values in only two of the cells."> </td>
</tr>
</table>
End of explanation
"""


