![TensorFlow.NET实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/278/46418278/b_46418278.jpg)
第2章
数据类型与张量详解
2.1 数据类型
TensorFlow本质上是一个深度学习的科学计算库,这个库的主要数据类型为张量,所有的运算都是基于张量数据进行的操作,更复杂的网络模型也只是一些基础运算的组合拼接,只有深入理解基本的张量运算,才能在各种深度学习的网络模型中游刃有余,开发出有价值、有创意的算法模型。
TensorFlow中基本的数据类型有数值类型、字符串类型和布尔类型。下面简单举例介绍。
(1)数值类型:var x=tf.Variable(10,name:"x")。
(2)字符串类型:var mammal1=tf.Variable("Elephant",name:"var1",dtype:tf.@string)。
(3)布尔类型:var bo=tf.Variable(true)。
具体数据类型如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_22_1.jpg?sign=1739535318-XkNgESCgXIxOqDtqTuwyH6ydm3m8f2lK-0-37db442723b3e096795d018593514e26)
2.2 张量详解
类似于NumPy中的N维数组对象NDArray,TensorFlow中数据的基本单位为张量(Tensor)。二者都是多维数组的概念。我们可以使用张量表示标量(0维数组)、向量(1维数组)、矩阵(2维数组)。
张量的主要特性为形状、类型和值,可以通过张量的属性shape、dtype和方法numpy()来获取特性。举例如下。
1.形状获取
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_23_1.jpg?sign=1739535318-LyuDBl38hgg1FV6HIFsrSFeEvQ9hA8gl-0-0136974f4bebe4d19dc18d89c63f4356)
上述代码运行后返回结果true,张量的属性shape通过返回整型1维数组的方式来显示张量的形状。
我们也可以直接通过TensorFlow.Binding封装的print()方法输出形状。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_23_2.jpg?sign=1739535318-JDUDasbNYJlZGcCzQ3M24451q5U4hdOa-0-3c85c95d72e9bf7f5a540e5638a62e50)
输出如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_23_3.jpg?sign=1739535318-eMfG1Ul228SRTyvnPmNYTc3ooRZ4qTif-0-47d4545148812cff8e01e2b3b543729b)
2.类型获取
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_23_4.jpg?sign=1739535318-xwfwYn8HktcI8ls6XCQ4BeNNS7d12WWz-0-6d3524bb7e260bb5970bd1a19568ac6c)
输出如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_23_5.jpg?sign=1739535318-rjP81tnf6YEUqurFDDABYZLQptn9Z32T-0-64b48cf817fd7422c367cb756e6d6ec8)
张量的属性dtype返回TF_DataType类型的枚举,可以很方便地通过print()方法进行输出。
3.值获取
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_23_6.jpg?sign=1739535318-4op8mKzaHC21acZxSKTRnnsRJps9CNDJ-0-507d0faf517b749474d99567d9f25258)
输出如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_24_1.jpg?sign=1739535318-BmW6zNg9EgtbvHXQnvqcXvjsVOae5e5X-0-3d22e96ecb44f8f2f9bc484914a10612)
张量的numpy()方法返回NumSharp.NDArray类型的值,内容为张量保存的值内容,可以很方便地通过print()方法进行输出。
4.类型转换
在C#中,可以快速对0维张量进行类型转换,通过在变量前加(type)进行强制类型转换,代码参考如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_24_2.jpg?sign=1739535318-fZLFTXPwKp8aSe4Zf1HneeTI3lm07R76-0-2eebd3341e8a498ca697486bcde742aa)
结果输出如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_24_3.jpg?sign=1739535318-pMMBwyORxuf405gee8sIIXBiVYQSU8pu-0-fd9e3a0f9875ffa29c2873917271f933)
通过上述结果可以看到,原来的张量类型被快速转换成普通的数值类型。
2.3 常量与变量
从行为特性来看,有常量constant和变量Variable两种类型的张量。
常量在计算图中不可以被重新赋值;变量在计算图中可以用assign等算子重新赋值。
1.常量
一般的常量类型如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_24_4.jpg?sign=1739535318-fwG1xsr5zJvdPqnE6yNYLS7qOlrYALhD-0-322d2ec3a01f0bbdd14c4ad23d6f39dc)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_25_1.jpg?sign=1739535318-x0YBGbBZ76rpz3BjVN2oJBg0y1dZGYFC-0-9bb74f5994093bcd9a83005a9d835b81)
输出如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_25_2.jpg?sign=1739535318-hISFxbOZOT6g3MoZEh1kiVikv5FdVUdd-0-3b824b890fd259927f70975107f718c2)
我们来写一个4维的常量,并通过tf.rank函数返回张量的秩。
一般来说,标量为0维张量,向量为1维张量,矩阵为2维张量。
彩色图像有r、g、b三个通道,可以表示为3维张量。
视频增加一个时间维,可表示为4维张量。
代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_25_3.jpg?sign=1739535318-MxZX4L7WV21jm29JeuAjn3z4W7QjWAr3-0-e8d67df1187fb654a6ee199da2f77879)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_26_1.jpg?sign=1739535318-PMbRbyYQvXzxThdYtRaFjHOoWYjpkURJ-0-75d25a0a459f762d0ef54b0ffc1f274a)
代码运行后输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_26_2.jpg?sign=1739535318-hTNNL7vYodO1tmI0V0lzHvM5yuxxWJ9U-0-6044cffe55d48f7b022e93619a01eb08)
2.变量
在深度学习模型中,一般被训练的参数需要定义为变量,变量的值可以在模型训练过程中被修改。
我们简单测试一个2维数组的变量,代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_26_3.jpg?sign=1739535318-TCXJ3APqblKDSQb1Yfp5PK98CJwSbvJd-0-6de69965264b1a6a3d8cd87a666f7544)
代码输出如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_26_4.jpg?sign=1739535318-eTbor2EpyzRSdNpwnYkX5avSTaSzLkPZ-0-95c417e11fbcdb0c408be03caa0d56f4)
接下来我们一起看下常量和变量的差别:常量的值可以参与运算,也可以被重新赋值,但是重新赋值或运算后的结果会开辟新的内存空间;变量的值可以通过assign、assign_add等算子重新赋值。代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_26_5.jpg?sign=1739535318-i5hULW5jwKLaG2l9yKqk4aGqGEBqEOqZ-0-1f5a8102cfc8b5ce5bc7ba63455e9ca0)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_27_1.jpg?sign=1739535318-o0P9sPv151pWu2vqfw7psk60HPgDMhln-0-13c498fa3c48f79bc7c34076b3d1a328)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_27_2.jpg?sign=1739535318-GusXi0NTKkXQm5rw57Dwppnzzq4Dtgz3-0-24b35ff9c0c10fd9517265d8723f4e94)
2.4 字符串常见操作
TensorFlow.NET中有专门的创建、转换和截取等常见的字符串操作。
1.通过byte数组创建字符串
传入Hex十六进制的byte数组,打印输出对应的转换后的字符串,代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_27_3.jpg?sign=1739535318-0XMtD4AVpUwOK4tQ04cDdpR1eOF9Q0pY-0-08344923aa7b02fd3053b8771bb6dece)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_27_4.jpg?sign=1739535318-63Jjdundi0ELNyiwicBGT09JLf0LmXqe-0-3123cae5117c7db9e4e108daa4804255)
也可以通过ToString()方法将张量整体转换为字符串进行输出,代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_27_5.jpg?sign=1739535318-PZjCwW1Kyl6zpjWwi8d894UfsPK47k4h-0-604c4f1b456faa6ea0311ef774b9d5a1)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_28_1.jpg?sign=1739535318-XYEp2oQZC5OzL2sjStRJpokd47mrdHxK-0-9e1c03831e75d35de726cff97589f136)
2.通过numpy()方法转换字符串的值
numpy()方法可以转换字符串的值,代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_28_2.jpg?sign=1739535318-myXlpkvNUew04GYqNzDJVuEvGX8BxdvF-0-51cbbeb51091b328e09ecdfcaac73496)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_28_3.jpg?sign=1739535318-LdtdYxNA0j2csJNtmSr6bdj0AZDvQtar-0-20d75f05c7ad2a8913e2f5d5dec2d456)
3.通过tf.strings.substr()方法截取字符串
tf.strings.substr()方法可以截取字符串。下述代码演示字符串的截取,并对截取后的字符串进行比较,最后输出张量的布尔标量值。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_28_4.jpg?sign=1739535318-xzrw29FUxGH5e69yclu3QWRrPdR7aQks-0-49e637f683b11f935e9ef7a903697250)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_28_5.jpg?sign=1739535318-K4Pl7756TUzWzDz8JVXM3gxU2CyjEpu0-0-938b416c4e4390fcf0ce14dadf5f9a42)
主要参数说明如下。
方法名:tf.strings.substr(Tensor input,int pos,int len,string name=null,string@uint="BYTE")。
参数1:input,类型为Tensor,待截取的输入字符串。
参数2:pos,类型为int,起始位置。
参数3:len,类型为int,截取长度。
返回值:类型为Tensor,返回截取后输出的字符串。
4.字符串数组张量的创建和转换
下述代码演示了字符串数组张量的创建,并可以打印出张量的形状和张量的内容。ToString()方法可以将张量的完整内容转换为string;StringData()方法可以提取出张量的值并转换为string[]数组类型。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_29_1.jpg?sign=1739535318-myam3Rp3wWkJ6JnsiETWZCeL4K97Hh3Y-0-2581122bfc3c4a0220beb0c38b490d42)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_29_2.jpg?sign=1739535318-1yjwDHraxpdR1Margc27E0jbWJcpMYLp-0-00627583128f831b9867c175ca7fa029)
5.本地文件(图像)读取示例
我们可以通过tf.io.read_file()方法从本地读取文件(图像),并打印和测试该文件(图像)的前3个byte数据,代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_29_3.jpg?sign=1739535318-KtBIqysJzlmE93dN5ATtMPW6bve8M7ug-0-c281dcddb392eed7a49e977ad9142d51)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_30_1.jpg?sign=1739535318-WO3id9iRLd3khVtGOf0IhsSEsGPkLE8O-0-973207cad9ebf4aca2b5fedc44bf1394)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_30_2.jpg?sign=1739535318-D0RnsOZ8OdvnR0S6HttzZgTvcFzoQGsw-0-5af42bcb4cef8e1b88895dd725bb72c6)
2.5 基本张量操作
张量是TensorFlow.NET中常用的数据结构,TensorFlow.NET中内置了大量的基础张量操作方法,可以进行张量的创建、索引和修改等。
1.tf.cast改变张量的数据类型
下述例子演示的是将int32类型的值转换为float32类型的值。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_30_3.jpg?sign=1739535318-bGRCZc0UBszHiaP7mJxFdGwQgTDrCGcG-0-6730d6bf8042613fa57061d4e9a3bfbb)
通过tf.cast将int32类型的值转换为float32类型的值,输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_30_4.jpg?sign=1739535318-PjWfbZV58OOEnqYsD0vb53BzrcUDHfTv-0-e1f3fd68960e6ec6f4053047402e2806)
2.tf.range创建区间张量值
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_30_5.jpg?sign=1739535318-4Nlg5SfZedtfIh34qVOW4o7Ot0jbIcwl-0-9c32efa81e13d8c95d5deb52477d2298)
常用参数说明如下。
参数1:start,区间初始值。
参数2:limit,区间限定值,取值<limit(不等于limit)。
参数3:delta,区间值递增的差量。
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_31_1.jpg?sign=1739535318-jQhkcXov33V4obuMcU9GyeirUU9XPLfM-0-9b124fb2f6fbcb50d988de180f382c3e)
3.tf.zeros/tf.ones创建0值和1值的张量
下述例子创建了一个3×4的0值张量和4×5的1值张量,一般可用于张量的初始化。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_31_2.jpg?sign=1739535318-9nl3ZU8ka5y1cYYqVpKhYPF7TbKDbOeX-0-693b1f0469ac77075a8c82df5eca7b42)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_31_3.jpg?sign=1739535318-ygI41IAt8zm9ABeKTk3Pl6FL56BReK2L-0-a442d1b56e6f2447b93b2a2540a65fcd)
4.tf.random生成随机分布张量
tf.random.normal用于随机生成正态分布的张量;tf.random.truncated_normal用于随机生成正态分布的张量,并剔除2倍方差以外的数据。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_31_4.jpg?sign=1739535318-U10AFS7d6pyrmZdStvnWbwO0wF7ftDak-0-4c86557094ff2e0037c72130eca985dd)
常用参数说明如下。
参数1:shape,生成的正态分布张量的形状。
参数2:mean,正态分布的中心值。
参数3:stddev,正态分布的标准差。
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_32_1.jpg?sign=1739535318-fMWxSsx7kegPZY8zJRMcgfbS12xAJBjO-0-2c03e0d912030cdab04c30d8b3a2de64)
5.索引切片
可以通过张量的索引来读取元素;对于变量,可以通过索引对部分元素进行修改。
下述为张量的索引功能的演示。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_32_2.jpg?sign=1739535318-g5PjRasCockyYck4xMgyfezojVLctjej-0-f202a14c0c400464763fd164d6aa617c)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_33_1.jpg?sign=1739535318-u7NaZviVGs57eQeqedz0oe68s5nAiNlw-0-a22b5215026a6b15e5c7118579d49a44)
下述为张量的切片读取功能的演示。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_33_2.jpg?sign=1739535318-8EQ65J8JCpLfZxhIT4stkA0kfiWFWsGM-0-1b4d7879dda30d616a842d35fe698d0f)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_33_3.jpg?sign=1739535318-rLTzWOXx02vSH6qFcdImBWrSNYK9y3C9-0-7cbea395c16d7a5164aa75e0d6b816bf)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_34_1.jpg?sign=1739535318-zBkG9xTD0OnuEJie9BL4XoQNVSlB7l81-0-6f844b999f9839fa1ee918839cea5de0)
下述为张量的切片赋值功能的演示,通过assign算子实现。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_34_2.jpg?sign=1739535318-476k9lUEja2EQa6D7EZ66OK7fu7Psdb8-0-f3905d226468a71bc1b669ca500198ac)
程序运行后,通过assign算子对[":2",":2"]的张量切片进行赋值,结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_34_3.jpg?sign=1739535318-FpQ1QFhRgKRwF3I2mtqzM2I4lcgXcvzs-0-b5b824959a01a721cc5b9be45eab751b)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_35_1.jpg?sign=1739535318-UmrSc4AD01SGsFZmb27F6zrZsxJ7CIHz-0-1e61a66efce1e445a994dc87c9fb6b8b)
6.张量比较
tf.equal可以比较两个张量是否相同;ToScalar可以获取布尔标量值。代码如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_35_2.jpg?sign=1739535318-mBC9Ap2vJUgQfgRGqXxd1JMJkaiJBise-0-0006af07a813f3cb485b2e19357d5c4d)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_35_3.jpg?sign=1739535318-Tt5lnbYztKH8dQeAWZIe1edRTyySrTQm-0-cb2ebed4a0456b516274b6dcdfa0b124)
2.6 维度变换
张量的维度变换操作主要是指改变张量的形状,主要方法有tf.reshape、tf.squeeze、tf.expand_dims和tf.transpose。
1.tf.reshape改变张量的形状
tf.reshape主要改变张量的形状,该操作不会改变张量在内存中的存储顺序,因此速度非常快,并且操作可逆。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_35_4.jpg?sign=1739535318-7RjsCntgqAMVR6dyemevWwuW4xEHIDnD-0-d9656f1f54b229b2fa875458b42ac8df)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_36_1.jpg?sign=1739535318-5zIxSUviiSagvfdmil8Xlxn304IndBze-0-7b55995d702145c17e2f077e1f136f6e)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_36_2.jpg?sign=1739535318-RLHoKUCGIIFLBooRFGyFgBU3K2rNPX9j-0-ab1e5175da469e9902d0fc68c42f0991)
2.tf.squeeze维度压缩简化
tf.squeeze可以消除张量中的单个元素的维度。和tf.reshape一样,该操作不会改变张量在内存中的存储顺序。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_36_3.jpg?sign=1739535318-pwvBEUj6lLwWbHIuAnm01CXinNYmMMcu-0-b408494beb859ceb81ba5041c738fad2)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_36_4.jpg?sign=1739535318-rBxmMKlrh8F8g2VeDwjOexgOMt8mPmhC-0-3fbe593f402251a61b443d09b5c4c28f)
3.tf.expand dims增加维度
tf.squeeze的逆向操作为tf.expand_dims,即往指定的维度中插入长度为1的维度。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_37_1.jpg?sign=1739535318-Y6rBksDicQFDoJ4FZzhQTRxCVAbofNdX-0-f75d9fab03ce393671208ff28d4e38af)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_37_2.jpg?sign=1739535318-bCKIM57hpXuioae73LZNRsgRf2EuFG9j-0-fce1c0b7ed0163e40fb7fcff01d0263f)
4.tf.transpose维度交换
tf.transpose可以交换张量的维度,与tf.reshape不同,它会改变张量在内存中的存储顺序。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_37_3.jpg?sign=1739535318-SMYSli7UfItkUSX00mUjSBbjfTOReNJN-0-f3af864168eef8d5e4703a1f9553c72c)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_37_4.jpg?sign=1739535318-a3LUATRYI765ukSNEldaCbWNy0FbPvbS-0-188695bcd40de29c0ffe59af189c498c)
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_38_1.jpg?sign=1739535318-xazGge2F3oaPodP1qMQKQRY5gWMyeUK6-0-cf62379b4edae883ef48f4fd5e6f8fd5)
tf.transpose维度交换过程示意图如图2-1所示。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_38_2.jpg?sign=1739535318-WkPZII5dbSa3ELHEzqEd0mUcyOhl6rBn-0-d88b80d4bc2cdaa087e2fd8e847e87aa)
图2-1 tf.transpose维度交换过程示意图
2.7 合并分割
张量的合并分割和NumPy类似,其中合并有两种不同的实现方式:tf.concat可以连接不同的张量,在同一设定的维度进行,不会增加维度;tf.stack采用维度堆叠的方式,会增加维度。
1.tf.concat
我们来测试一下使用tf.concat连接3个形状为[2,2]的张量。concatValue1通过在axis:0维度中的张量连接操作,将3个张量合并为1个形状为[6,2]的新张量;concatValue2通过在axis:-1维度中的张量连接操作,将3个张量合并为1个形状为[2,6]的新张量。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_38_3.jpg?sign=1739535318-4LNgdXRopUFgAgk5T5ypqxyUvLUlPElM-0-e40fcaa721fec5411b2f51dd7cccbb8a)
输出如下,正确地实现了张量的连接合并功能。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_39_1.jpg?sign=1739535318-G6rjW4KnT7uqwTceKfMf9fmYji9vnFii-0-41754a3ab3c15af15a96fb087ef122a7)
2.tf.stack
同样是上面的例子,我们将tf.concat替换为tf.stack。可以看到,tf.stack在指定的维度上创建了新的维度,并将输入张量在新维度上进行堆叠操作。通过代码的运行,我们可以看到两种方式的内部机制的差异。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_39_2.jpg?sign=1739535318-p4FkJa18dRJfGVu4xCVRU9JiL2DZSu9a-0-7628292b202726d70558c02c7912aa73)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_39_3.jpg?sign=1739535318-XSiqR5v4rFfzMXZ4V3qvsKJvVEySACLG-0-2d77f561c41abda775bd1d757222aa2d)
上面两个例子演示了张量的合并,接下来我们来测试张量的分割。张量的分割方法tf.split是tf.concat方法的逆操作,可以将张量平均分割或按照指定的形状分割。
3.tf.split
我们利用下述代码首先将a、b、c合并为shape:[3,2,2]的concatValue,然后通过tf.split将concatValue的0维分割,还原为3个shape:[2,2]的张量数组splitValue。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_40_1.jpg?sign=1739535318-raB3uMneNGLA2FnYaxYeI3GOFsU1jIin-0-cc46092b3242651004eb17446e11d7ca)
输出结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_40_2.jpg?sign=1739535318-hUTUddtdfraXI0iaMge5HBYah45opkmV-0-93ddff53b3d288bdbb91900d480b37fe)
2.8 广播机制
本节我们聊聊在NumPy和张量中都很常用并很重要的一个特性:Broadcasting,即广播机制,又称作自动扩展机制。广播是一种十分轻量的张量复制操作,只会在逻辑上扩展张量的形状,而不会直接执行实际存储I/O的复制操作。经过广播后的张量在视图上会体现出复制后的形状。
在进行实际数据运算的时候,广播机制会通过深度学习框架的优化技术,避免实际复制数据而完成逻辑运算。对于用户来说,广播机制和tf.tile复制数据的最终实现效果是相同的,但是广播机制节省了大量的计算资源并自动优化了运算速度。
但是,广播机制并不是任何场合都适用的,下面我们来介绍广播机制的使用规则和实现效果。
(1)如果张量的维度不同,则对维度较小的张量左侧补齐进行扩展,直到两个张量的维度相同。
(2)如果两个张量在某个维度上的长度是相同的,或者其中一个张量在该维度上的长度为1,则我们说这两个张量在该维度上是相容的。
(3)如果两个张量在所有维度上或通过上述(1)的过程扩展后都是相容的,则它们能使用广播机制。这是广播机制的核心思想——普适性。
(4)广播之后,每个维度的长度取两个张量在该维度长度上的较大值。
(5)在任何一个维度上,如果一个张量的长度为1,另一个张量的长度大于1,那么在该维度上,就好像对第一个张量进行了复制。
我们通过图解的方式进一步举例说明。
首先来看可广播的情形:张量B的形状为[w,1],张量A的形状为[b,h,w,c],不同维度的张量相加运算A+B是可以正常运行的,这就是广播机制的作用,张量B通过广播机制扩展为和A相同的形状[b,h,w,c]。正常的广播扩展过程如图2-2所示,分为3步。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_41_1.jpg?sign=1739535318-lMExqwGKgr1DueuBdiifkG9I3OTi2MAV-0-41bc11bc707ac8796de7ce71d0cce522)
图2-2 正常的广播扩展过程
然后来看不可广播的情形:同样是上面这个例子,如果张量B的形状为[w,2],同时张量A的形状为[b,h,w,c],其中c≠2,则这两个张量不符合普适性原则,无法应用广播机制,运行张量相加操作A+B会触发报错机制。无法应用广播机制的内部原理如图2-3所示。
广播机制的实现有两种方式。
1.隐式自动调用
在进行不同形状的张量运算时,隐式地自动调用广播机制,如用“+、-、*、/”等运算,先将参与运算的张量广播成统一的形状,再进行相应的运算。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_42_1.jpg?sign=1739535318-UoVPTq3y18M84BDxZYyuAM8An0uM9X7P-0-8d289e4aaa85170bb9b4aea67aa3f829)
图2-3 无法应用广播机制的内部原理
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_42_2.jpg?sign=1739535318-oXqYjjmrCLn9mvkHxW7xBbYbk2o1vTA6-0-5d4c5b0af5936ba533610966e2b18fc8)
运行结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_42_3.jpg?sign=1739535318-Xhf2QLhwRxxRrZGC0Gq8Hewxka3elA8G-0-31eb0193b33c6a49448a3c6cf0041dd3)
2.显式广播方法
使用tf.broadcast_to显式地调用广播方法,将指定的张量广播至指定的形状。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_43_1.jpg?sign=1739535318-axXEv2ieWjYsijdHnEAYiN2bv0D60Oek-0-4c92cca90108bfe625235ff29476da91)
运行结果如下。
![](https://epubservercos.yuewen.com/E3272D/25638808101564006/epubprivate/OEBPS/Images/44309_43_2.jpg?sign=1739535318-gtGqCUoZp6sNwMJkAfcfv8ftTr2X2HdZ-0-8325c86ce213a2f74c83ba962be438cf)