{ "cells": [ { "cell_type": "markdown", "metadata": { "cellView": "form", "id": "tuOe1ymfHZPu" }, "source": [ "````{admonition} Copyright 2020 The TensorFlow Authors.\n", "```\n", "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License.\n", "```\n", "````" ] }, { "cell_type": "markdown", "metadata": { "id": "VZ-KA8k5kybx" }, "source": [ "# 张量切片简介" ] }, { "cell_type": "markdown", "metadata": { "id": "MfBg1C5NB3X0" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
在 TensorFlow.org 上查看\n", "在 Google Colab 中运行 在 GitHub 上查看源代码\n", " 下载笔记本
" ] }, { "cell_type": "markdown", "metadata": { "id": "AixIdVeRk3CO" }, "source": [ "在处理目标检测和 NLP 等机器学习应用时,有时需要使用张量的子部分(切片)。例如,如果您的模型架构包含路由,则其中一层可能控制将哪个训练样本路由到下一层。在这种情况下,可以使用张量切片运算将张量拆分并以正确的顺序将它们重新组合在一起。\n", "\n", "在 NLP 应用中,可以在训练时使用张量切片来执行单词遮盖。例如,可以通过在每个句子中选择要遮盖的单词索引,将单词作为标签,然后使用遮盖词例替换选中的单词,以从句子列表中生成训练数据。\n", "\n", "在本指南中,您将学习如何使用 TensorFlow API 执行以下操作:\n", "\n", "- 从张量中提取切片\n", "- 在张量中的特定索引处插入数据\n", "\n", "本指南假定您熟悉张量索引。在开始学习本指南之前,请阅读[张量](https://tensorflow.google.cn/guide/tensor#indexing)和 [TensorFlow NumPy](https://tensorflow.google.cn/guide/tf_numpy#indexing) 指南的索引部分。" ] }, { "cell_type": "markdown", "metadata": { "id": "FcWhWYn7eXkF" }, "source": [ "## 安装\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from set_env import temp_dir" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "m6uvewqi0jso" }, "outputs": [], "source": [ "import tensorflow as tf\n", "import numpy as np" ] }, { "cell_type": "markdown", "metadata": { "id": "K-muS4ej5zoN" }, "source": [ "## 提取张量切片\n", "\n", "使用 `tf.slice` 执行类似 NumPy 的张量切片。\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "id": "wZep0cjs0Oai" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([1 2 3], shape=(3,), dtype=int32)\n" ] } ], "source": [ "t1 = tf.constant([0, 1, 2, 3, 4, 5, 6, 7])\n", "\n", "print(tf.slice(t1,\n", " begin=[1],\n", " size=[3]))" ] }, { "cell_type": "markdown", "metadata": { "id": "Vh3xI3j0DRJ2" }, "source": [ "或者,您也可以使用更具 Python 风格的语法。请注意,张量切片在开始-停止范围内均匀分布。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "P1MtEyKuWuDD" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([1 2 3], shape=(3,), dtype=int32)\n" ] } ], "source": [ "print(t1[1:4])" ] }, { "cell_type": "markdown", "metadata": { "id": "cjq1o8D2wKKs" }, "source": [ "" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "UunuLTIuwDA-" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([5 6 7], shape=(3,), dtype=int32)\n" ] } ], "source": [ "print(t1[-3:])" ] }, { "cell_type": "markdown", "metadata": { "id": "EHvRB-XTwRTd" }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "id": "SW1zFFTnUpCQ" }, "source": [ "对于二维张量,可以使用以下代码:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "id": "kThZhmpAVAQw" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[ 1 2]\n", " [ 6 7]\n", " [11 12]], shape=(3, 2), dtype=int32)\n" ] } ], "source": [ "t2 = tf.constant([[0, 1, 2, 3, 4],\n", " [5, 6, 7, 8, 9],\n", " [10, 11, 12, 13, 14],\n", " [15, 16, 17, 18, 19]])\n", "\n", "print(t2[:-1, 1:3])" ] }, { "cell_type": "markdown", "metadata": { "id": "xA5Xt4OdVUui" }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "id": "iJPggqsH15fI" }, "source": [ "您也可以在更高维度的张量上使用 `tf.slice`。" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "id": "Re5eX1OXnKOZ" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([[[25 27]]], shape=(1, 1, 2), dtype=int32)\n" ] } ], "source": [ "t3 = tf.constant([[[1, 3, 5, 7],\n", " [9, 11, 13, 15]],\n", " [[17, 19, 21, 23],\n", " [25, 27, 29, 31]]\n", " ])\n", "\n", "print(tf.slice(t3,\n", " begin=[1, 1, 0],\n", " size=[1, 1, 2]))" ] }, { "cell_type": "markdown", "metadata": { "id": "x-O5FNV9qOJK" }, "source": [ "还可以使用 `tf.strided_slice` 通过在张量维度上“跨步”来提取张量切片。" ] }, { "cell_type": "markdown", "metadata": { "id": "b9FhvrOnJsJb" }, "source": [ "使用 `tf.gather` 从张量的单个轴中提取特定索引。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "TwviZrrIj2h7" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([0 3 6], shape=(3,), dtype=int32)\n" ] }, { "data": { "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(tf.gather(t1,\n", " indices=[0, 3, 6]))\n", "\n", "# This is similar to doing\n", "\n", "t1[::3]" ] }, { "cell_type": "markdown", "metadata": { "id": "oKyjGi2zyzEC" }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "id": "obrjeKy1WfTN" }, "source": [ "`tf.gather` 不要求索引均匀分布。" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "id": "LjJcwcZ0druw" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([b'c' b'a' b't' b's'], shape=(4,), dtype=string)\n" ] } ], "source": [ "alphabet = tf.constant(list('abcdefghijklmnopqrstuvwxyz'))\n", "\n", "print(tf.gather(alphabet,\n", " indices=[2, 0, 19, 18]))" ] }, { "cell_type": "markdown", "metadata": { "id": "mSHmUXIyeaJG" }, "source": [ "" ] }, { "cell_type": "markdown", "metadata": { "id": "XsxMx49SOaVu" }, "source": [ "要从张量的多个轴中提取切片,请使用 `tf.gather_nd`。当您想要收集矩阵的元素而不仅仅是它的行或列时,这非常有用。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "id": "mT52NFWVdiTe" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[2 7]\n", " [3 8]\n", " [0 5]], shape=(3, 2), dtype=int32)\n" ] } ], "source": [ "t4 = tf.constant([[0, 5],\n", " [1, 6],\n", " [2, 7],\n", " [3, 8],\n", " [4, 9]])\n", "\n", "print(tf.gather_nd(t4,\n", " indices=[[2], [3], [0]]))" ] }, { "cell_type": "markdown", "metadata": { "id": "87NN7YQhh2-a" }, "source": [ "" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "id": "_z6F2WcPJ9Rh" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([ 0 16], shape=(2,), dtype=int64)\n" ] } ], "source": [ "t5 = np.reshape(np.arange(18), [2, 3, 3])\n", "\n", "print(tf.gather_nd(t5,\n", " indices=[[0, 0, 0], [1, 2, 1]]))" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "id": "gyIjhm7cV2N0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[[ 0 1 2]\n", " [ 6 7 8]]\n", "\n", " [[ 9 10 11]\n", " [15 16 17]]], shape=(2, 2, 3), dtype=int64)\n" ] } ], "source": [ "# Return a list of two matrices\n", "\n", "print(tf.gather_nd(t5,\n", " indices=[[[0, 0], [0, 2]], [[1, 0], [1, 2]]]))" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "id": "368D4ciDWB3r" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[ 0 1 2]\n", " [ 6 7 8]\n", " [ 9 10 11]\n", " [15 16 17]], shape=(4, 3), dtype=int64)\n" ] } ], "source": [ "# Return one matrix\n", "\n", "print(tf.gather_nd(t5,\n", " indices=[[0, 0], [0, 2], [1, 0], [1, 2]]))" ] }, { "cell_type": "markdown", "metadata": { "id": "od51VzS2SSPS" }, "source": [ "## 将数据插入张量\n", "\n", "使用 `tf.scatter_nd` 在张量的特定切片/索引处插入数据。请注意,您将值插入的张量是用零初始化的。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "id": "jlALYLWm1KhN" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor([ 0 2 0 4 0 6 0 8 0 10], shape=(10,), dtype=int32)\n" ] } ], "source": [ "t6 = tf.constant([10])\n", "indices = tf.constant([[1], [3], [5], [7], [9]])\n", "data = tf.constant([2, 4, 6, 8, 10])\n", "\n", "print(tf.scatter_nd(indices=indices,\n", " updates=data,\n", " shape=t6))" ] }, { "cell_type": "markdown", "metadata": { "id": "CD5vd-kxksW7" }, "source": [ "像 `tf.scatter_nd` 这样需要零初始化张量的方法类似于稀疏张量初始值设定项。可以使用 `tf.gather_nd` 和 `tf.scatter_nd` 来模拟稀疏张量运算的行为。\n", "\n", "考虑一个将这两种方法结合使用来构造稀疏张量的示例。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "id": "xyK69QgRmrlW" }, "outputs": [], "source": [ "# Gather values from one tensor by specifying indices\n", "\n", "new_indices = tf.constant([[0, 2], [2, 1], [3, 3]])\n", "t7 = tf.gather_nd(t2, indices=new_indices)" ] }, { "cell_type": "markdown", "metadata": { "id": "_7V_Qfa4qkdn" }, "source": [ "" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "id": "QWT1E1Weqjx2" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[ 0 0 2 0 0]\n", " [ 0 0 0 0 0]\n", " [ 0 11 0 0 0]\n", " [ 0 0 0 18 0]], shape=(4, 5), dtype=int32)\n" ] } ], "source": [ "# Add these values into a new tensor\n", "\n", "t8 = tf.scatter_nd(indices=new_indices, updates=t7, shape=tf.constant([4, 5]))\n", "\n", "print(t8)" ] }, { "cell_type": "markdown", "metadata": { "id": "NUyYjnvCn_vu" }, "source": [ "这类似于:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "id": "LeqFwUgroE4j" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SparseTensor(indices=tf.Tensor(\n", "[[0 2]\n", " [2 1]\n", " [3 3]], shape=(3, 2), dtype=int64), values=tf.Tensor([ 2 11 18], shape=(3,), dtype=int32), dense_shape=tf.Tensor([4 5], shape=(2,), dtype=int64))\n" ] } ], "source": [ "t9 = tf.SparseTensor(indices=[[0, 2], [2, 1], [3, 3]],\n", " values=[2, 11, 18],\n", " dense_shape=[4, 5])\n", "\n", "print(t9)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "id": "5MaF6RlJot33" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[ 0 0 2 0 0]\n", " [ 0 0 0 0 0]\n", " [ 0 11 0 0 0]\n", " [ 0 0 0 18 0]], shape=(4, 5), dtype=int32)\n" ] } ], "source": [ "# Convert the sparse tensor into a dense tensor\n", "\n", "t10 = tf.sparse.to_dense(t9)\n", "\n", "print(t10)" ] }, { "cell_type": "markdown", "metadata": { "id": "4sf3F3Xk56Bt" }, "source": [ "要将数据插入到具有既有值的张量中,请使用 `tf.tensor_scatter_nd_add`。" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "id": "mte2ifOb6sQO" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[2 7 6]\n", " [9 5 1]\n", " [4 3 8]], shape=(3, 3), dtype=int32)\n" ] } ], "source": [ "t11 = tf.constant([[2, 7, 0],\n", " [9, 0, 1],\n", " [0, 3, 8]])\n", "\n", "# Convert the tensor into a magic square by inserting numbers at appropriate indices\n", "\n", "t12 = tf.tensor_scatter_nd_add(t11,\n", " indices=[[0, 2], [1, 1], [2, 0]],\n", " updates=[6, 5, 4])\n", "\n", "print(t12)" ] }, { "cell_type": "markdown", "metadata": { "id": "2dQYyROU09G6" }, "source": [ "类似地,可以使用 `tf.tensor_scatter_nd_sub` 从具有既有值的张量中减去值。" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "id": "ac6_i6uK1EI6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[1 0 0]\n", " [0 1 0]\n", " [0 0 1]], shape=(3, 3), dtype=int32)\n" ] } ], "source": [ "# Convert the tensor into an identity matrix\n", "\n", "t13 = tf.tensor_scatter_nd_sub(t11,\n", " indices=[[0, 0], [0, 1], [1, 0], [1, 1], [1, 2], [2, 1], [2, 2]],\n", " updates=[1, 7, 9, -1, 1, 3, 7])\n", "\n", "print(t13)" ] }, { "cell_type": "markdown", "metadata": { "id": "B_2DuzRRwVc8" }, "source": [ "使用 `tf.tensor_scatter_nd_min` 将逐元素最小值从一个张量复制到另一个。" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "id": "T_4FrHrHlkHK" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[-2 -7 -6]\n", " [-9 -5 1]\n", " [-4 -3 -8]], shape=(3, 3), dtype=int32)\n" ] } ], "source": [ "t14 = tf.constant([[-2, -7, 0],\n", " [-9, 0, 1],\n", " [0, -3, -8]])\n", "\n", "t15 = tf.tensor_scatter_nd_min(t14,\n", " indices=[[0, 2], [1, 1], [2, 0]],\n", " updates=[-6, -5, -4])\n", "\n", "print(t15)" ] }, { "cell_type": "markdown", "metadata": { "id": "PkaiKyrF0WtX" }, "source": [ "类似地,使用 `tf.tensor_scatter_nd_max` 将逐元素最大值从一个张量复制到另一个。" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "id": "izJu0nXi0GDq" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "tf.Tensor(\n", "[[-2 -7 6]\n", " [-9 5 1]\n", " [ 4 -3 -8]], shape=(3, 3), dtype=int32)\n" ] } ], "source": [ "t16 = tf.tensor_scatter_nd_max(t14,\n", " indices=[[0, 2], [1, 1], [2, 0]],\n", " updates=[6, 5, 4])\n", "\n", "print(t16)" ] }, { "cell_type": "markdown", "metadata": { "id": "QAffUOa-85lF" }, "source": [ "## 补充阅读和资源\n", "\n", "在本指南中,您学习了如何使用 TensorFlow 提供的张量切片运算来更好地控制张量中的元素。\n", "\n", "- 查看 TensorFlow NumPy 提供的切片运算,例如 `tf.experimental.numpy.take_along_axis` 和 `tf.experimental.numpy.take`。\n", "\n", "- 另请查看[张量指南](https://tensorflow.google.cn/guide/tensor)和[变量指南](https://tensorflow.google.cn/guide/variable)。" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "tensor_slicing.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "xxx", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }