28 第2章神经网络的数学基础 我们来看两个例子。 口人口统计数据集,其中包括每个人的年龄、邮编和收入。每个人可以表示为包含3个值 的向量,而整个数据集包含100000个人,因此可以存储在形状为(100000,3)的2D 张量中。 口文本文档数据集,我们将每个文档表示为每个单词在其中出现的次数(字典中包含 20000个常见单词)。每个文档可以被编码为包含20000个值的向量(每个值对应于 字典中每个单词的出现次数),整个数据集包含500个文档,因此可以存储在形状为 (500,20000)的张量中。 2.2.10时间序列数据或序列数据 当时间(或序列顺序)对于数据很重要时,应该将数据存储在带有时间轴的3D张量中。 每个样本可以被编码为一个向量序列(即2D张量),因此一个数据批量就被编码为一个3D张 量(见图2-3)。 特征 样本 时间步长 图2-3时间序列数据组成的3D张量 根据惯例,时间轴始终是第2个轴(索引为1的轴)。我们来看几个例子。 口股票价格数据集。每一分钟,我们将股票的当前价格、前一分钟的最高价格和前一分钟 的最低价格保存下来。因此每分钟被编码为一个3D向量,整个交易日被编码为一个形 状为(390,3)的2D张量(一个交易日有390分钟),而250天的数据则可以保存在 个形状为(250,390,3)的3D张量中。这里每个样本是一天的股票数据。 口推文数据集。我们将每条推文编码为280个字符组成的序列,而每个字符又来自于128 个字符组成的字母表。在这种情况下,每个字符可以被编码为大小为128的二进制向量 (只有在该字符对应的索引位置取值为1,其他元素都为0)。那么每条推文可以被编码 为一个形状为(280,128)的2D张量,而包含100万条推文的数据集则可以存储在一 个形状为(1000000,280,128)的张量中。 2.2.11图像数据 图像通常具有三个维度:高度、宽度和颜色深度。虽然灰度图像(比如MNST数字图像) 只有一个颜色通道,因此可以保存在2D张量中,但按照惯例,图像张量始终都是3D张量,灰 度图像的彩色通道只有一维。因此,如果图像大小为256×256,那么128张灰度图像组成的批 量可以保存在一个形状为(128,256,256,1)的张量中,而128张彩色图像组成的批量则
28 第 2 章 神经网络的数学基础 我们来看两个例子。 人口统计数据集,其中包括每个人的年龄、邮编和收入。每个人可以表示为包含 3 个值 的向量,而整个数据集包含 100 000 个人,因此可以存储在形状为 (100000, 3) 的 2D 张量中。 文本文档数据集,我们将每个文档表示为每个单词在其中出现的次数(字典中包含 20 000 个常见单词)。每个文档可以被编码为包含 20 000 个值的向量(每个值对应于 字典中每个单词的出现次数),整个数据集包含 500 个文档,因此可以存储在形状为 (500, 20000) 的张量中。 2.2.10 时间序列数据或序列数据 当时间(或序列顺序)对于数据很重要时,应该将数据存储在带有时间轴的 3D 张量中。 每个样本可以被编码为一个向量序列(即 2D 张量),因此一个数据批量就被编码为一个 3D 张 量(见图 2-3)。 ༬ኙ क़օ้ ᄣԨ 图 2-3 时间序列数据组成的 3D 张量 根据惯例,时间轴始终是第 2 个轴(索引为 1 的轴)。我们来看几个例子。 股票价格数据集。每一分钟,我们将股票的当前价格、前一分钟的最高价格和前一分钟 的最低价格保存下来。因此每分钟被编码为一个 3D 向量,整个交易日被编码为一个形 状为 (390, 3) 的 2D 张量(一个交易日有 390 分钟),而 250 天的数据则可以保存在一 个形状为 (250, 390, 3) 的 3D 张量中。这里每个样本是一天的股票数据。 推文数据集。我们将每条推文编码为 280 个字符组成的序列,而每个字符又来自于 128 个字符组成的字母表。在这种情况下,每个字符可以被编码为大小为 128 的二进制向量 (只有在该字符对应的索引位置取值为 1,其他元素都为 0)。那么每条推文可以被编码 为一个形状为 (280, 128) 的 2D 张量,而包含 100 万条推文的数据集则可以存储在一 个形状为 (1000000, 280, 128) 的张量中。 2.2.11 图像数据 图像通常具有三个维度:高度、宽度和颜色深度。虽然灰度图像(比如 MNIST 数字图像) 只有一个颜色通道,因此可以保存在 2D 张量中,但按照惯例,图像张量始终都是 3D 张量,灰 度图像的彩色通道只有一维。因此,如果图像大小为 256×256,那么 128 张灰度图像组成的批 量可以保存在一个形状为 (128, 256, 256, 1) 的张量中,而 128 张彩色图像组成的批量则
书籍下载qg群6089740钉钉群21734177 IT书籍http:/t.cn/RDIAj5D 23神经网络的“齿轮”:张量运算 29 可以保存在一个形状为(128,256,256,3)的张量中(见图2-4)。 颜色通道 2 高度 样本 宽度 图2-4图像数据组成的4D张量(通道在前的约定) 图像张量的形状有两种约定:通道在后(channels-last)的约定(在TensorFlow中使用)和 通道在前(channels--first)的约定(在Theano中使用)。Google的TensorFlow机器学习框架将 颜色深度轴放在最后:(samples,height,width,color_depth)。与此相反,Theano 将图像深度轴放在批量轴之后:(samples,color_depth,height,width)。如果采 用Thean0约定,前面的两个例子将变成(128,1,256,256)和(128,3,256,256)。 Keras框架同时支持这两种格式。 2.2.12视频数据 视频数据是现实生活中需要用到5D张量的少数数据类型之一。视频可以看作一系列帧, 每一帧都是一张彩色图像。由于每一帧都可以保存在一个形状为(height,width,color_ depth)的3D张量中,因此一系列帧可以保存在一个形状为(frames,height,width, co1or_depth)的4D张量中,而不同视频组成的批量则可以保存在一个5D张量中,其形状为 (samples,frames,height,width,color_depth)o 举个例子,一个以每秒4帧采样的60秒YouTube视频片段,视频尺寸为144×256,这个 视频共有240帧。4个这样的视频片段组成的批量将保存在形状为(4,240,144,256,3) 的张量中。总共有106168320个值!如果张量的数据类型(dtype)是f1oat32,每个值都是 32位,那么这个张量共有405MB。好大!你在现实生活中遇到的视频要小得多,因为它们不以 f1oat32格式存储,而且通常被大大压缩,比如MPEG格式。 2.3神经网络的“齿轮”:张量运算 所有计算机程序最终都可以简化为二进制输入上的一些二进制运算(AND、OR、NOR等), 与此类似,深度神经网络学到的所有变换也都可以简化为数值数据张量上的一些张量运算(tensor 电子书寻找看手相钉钉或微信pythontesting
2.3 神经网络的“齿轮”:张量运算 29 1 5 3 7 2 6 4 8 9 可以保存在一个形状为 (128, 256, 256, 3) 的张量中(见图 2-4)。 ڢཚჿ ܈ߛ ܈ ᄣԨ 图 2-4 图像数据组成的 4D 张量(通道在前的约定) 图像张量的形状有两种约定:通道在后(channels-last)的约定(在 TensorFlow 中使用)和 通道在前(channels-first)的约定(在 Theano 中使用)。Google 的 TensorFlow 机器学习框架将 颜色深度轴放在最后:(samples, height, width, color_depth)。与此相反,Theano 将图像深度轴放在批量轴之后:(samples, color_depth, height, width)。如果采 用 Theano 约定,前面的两个例子将变成 (128, 1, 256, 256) 和 (128, 3, 256, 256)。 Keras 框架同时支持这两种格式。 2.2.12 视频数据 视频数据是现实生活中需要用到 5D 张量的少数数据类型之一。视频可以看作一系列帧, 每一帧都是一张彩色图像。由于每一帧都可以保存在一个形状为 (height, width, color_ depth) 的 3D 张量中,因此一系列帧可以保存在一个形状为 (frames, height, width, color_depth) 的 4D 张量中,而不同视频组成的批量则可以保存在一个 5D 张量中,其形状为 (samples, frames, height, width, color_depth)。 举个例子,一个以每秒 4 帧采样的 60 秒 YouTube 视频片段,视频尺寸为 144×256,这个 视频共有 240 帧。4 个这样的视频片段组成的批量将保存在形状为 (4, 240, 144, 256, 3) 的张量中。总共有 106 168 320 个值!如果张量的数据类型(dtype)是 float32,每个值都是 32 位,那么这个张量共有 405MB。好大!你在现实生活中遇到的视频要小得多,因为它们不以 float32 格式存储,而且通常被大大压缩,比如 MPEG 格式。 2.3 神经网络的“齿轮”:张量运算 所有计算机程序最终都可以简化为二进制输入上的一些二进制运算(AND、OR、NOR 等), 与此类似,深度神经网络学到的所有变换也都可以简化为数值数据张量上的一些张量运算(tensor 书籍下载qq群6089740 钉钉群21734177 IT书籍 http://t.cn/RDIAj5D 电子书寻找看手相 钉钉或微信pythontesting
30 第2章神经网络的数学基础 operation),例如加上张量、乘以张量等。 在最开始的例子中,我们通过叠加Dense层来构建网络。Keras层的实例如下所示。 keras.layers.Dense(512,activation='relu') 这个层可以理解为一个函数,输入一个2D张量,返回另一个2D张量,即输入张量的新 表示。具体而言,这个函数如下所示(其中W是一个2D张量,b是一个向量,二者都是该层的 属性)。 output relu(dot (W,input)+b) 我们将上式拆开来看。这里有三个张量运算:输入张量和张量W之间的点积运算(ot)、 得到的2D张量与向量b之间的加法运算(+入、最后的relu运算。relu(x)是max(x,0)。 注意虽然本节的内容都是关于线性代数表达式,但你却找不到任何数学符号。我发现,对于 没有数学背景的程序员来说,如果用简短的Python代码而不是数学方程来表达数学概念, 他们将更容易掌握。所以我们自始至终将会使用Numpy代码。 2.3.1逐元素运算 relu运算和加法都是逐元素(element-wise)的运算,即该运算独立地应用于张量中的每 个元素,也就是说,这些运算非常适合大规模并行实现(向量化实现,这一术语来自于1970一 1990年间向量处理器超级计算机架构)。如果你想对逐元素运算编写简单的Python实现,那么 可以用for循环。下列代码是对逐元素relu运算的简单实现。 def naiverelu(x): assert len(x.shape)==24一x是一个Numpy的2D张量 x=x.copy ( 4一避免覆盖输入张量 for i in range(x.shape[0]): for j in range(x.shape[1]): x[i,]max(x[i,j],0) return x 对于加法采用同样的实现方法。 def naive_add(x,y): assert len(x.shape)==2 x和y是Numpy的2D张量 assert x.shape =y.shape xx.copy ( for i in range(x.shape[0]): 避免覆盖输入张量 for j in range(x.shape[1]): x[i,j]+y[i,j] return x 根据同样的方法,你可以实现逐元素的乘法、减法等。 在实践中处理Numpy数组时,这些运算都是优化好的Numpy内置函数,这些函数将大量
30 第 2 章 神经网络的数学基础 operation),例如加上张量、乘以张量等。 在最开始的例子中,我们通过叠加 Dense 层来构建网络。Keras 层的实例如下所示。 keras.layers.Dense(512, activation='relu') 这个层可以理解为一个函数,输入一个 2D 张量,返回另一个 2D 张量,即输入张量的新 表示。具体而言,这个函数如下所示(其中 W 是一个 2D 张量,b 是一个向量,二者都是该层的 属性)。 output = relu(dot(W, input) + b) 我们将上式拆开来看。这里有三个张量运算:输入张量和张量 W 之间的点积运算(dot)、 得到的 2D 张量与向量 b 之间的加法运算(+)、最后的 relu 运算。relu(x) 是 max(x, 0)。 注意 虽然本节的内容都是关于线性代数表达式,但你却找不到任何数学符号。我发现,对于 没有数学背景的程序员来说,如果用简短的 Python 代码而不是数学方程来表达数学概念, 他们将更容易掌握。所以我们自始至终将会使用 Numpy 代码。 2.3.1 逐元素运算 relu 运算和加法都是逐元素(element-wise)的运算,即该运算独立地应用于张量中的每 个元素,也就是说,这些运算非常适合大规模并行实现(向量化实现,这一术语来自于 1970— 1990 年间向量处理器超级计算机架构)。如果你想对逐元素运算编写简单的 Python 实现,那么 可以用 for 循环。下列代码是对逐元素 relu 运算的简单实现。 def naive_relu(x): assert len(x.shape) == 2 x = x.copy() for i in range(x.shape[0]): for j in range(x.shape[1]): x[i, j] = max(x[i, j], 0) return x 对于加法采用同样的实现方法。 def naive_add(x, y): assert len(x.shape) == 2 assert x.shape == y.shape x = x.copy() for i in range(x.shape[0]): for j in range(x.shape[1]): x[i, j] += y[i, j] return x 根据同样的方法,你可以实现逐元素的乘法、减法等。 在实践中处理 Numpy 数组时,这些运算都是优化好的 Numpy 内置函数,这些函数将大量 x 是一个 Numpy 的 2D 张量 避免覆盖输入张量 避免覆盖输入张量 x 和 y 是 Numpy 的 2D 张量
书籍下载qg群6089740钉钉群21734177 IT书籍http:/t.cn/RDIAj5D 23神经网络的“齿轮”:张量运算 31 运算交给安装好的基础线性代数子程序(BLAS,basic linear algebra subprograms)实现(没装 的话,应该装一个)。BLAS是低层次的、高度并行的、高效的张量操作程序,通常用Fortran 或C语言来实现。 因此,在Numpy中可以直接进行下列逐元素运算,速度非常快。 import numpy as np 2 2=X+y 4一逐元素的相加 z np.maximum(z,0.) 4一逐元素的xelu 2.3.2广播 上一节naive add的简单实现仅支持两个形状相同的2D张量相加。但在前面介绍的 Dense层中,我们将一个2D张量与一个向量相加。如果将两个形状不同的张量相加,会发生 什么? 如果没有歧义的话,较小的张量会被广播(broadcast),以匹配较大张量的形状。广播包含 以下两步。 (1)向较小的张量添加轴(叫作广播轴),使其ndim与较大的张量相同。 (2)将较小的张量沿着新轴重复,使其形状与较大的张量相同。 来看一个具体的例子。假设X的形状是(32,10),y的形状是(10,)。首先,我们给y 添加空的第一个轴,这样y的形状变为(1,10)。然后,我们将y沿着新轴重复32次,这样 得到的张量Y的形状为(32,10),并且Y[i,:】==y for i in range(0,32)。现在, 我们可以将x和¥相加,因为它们的形状相同。 在实际的实现过程中并不会创建新的2D张量,因为那样做非常低效。重复的操作完全是 虚拟的,它只出现在算法中,而没有发生在内存中。但想象将向量沿着新轴重复10次,是一种 很有用的思维模型。下面是一种简单的实现。 def naive add matrix_and_vector(x,y): assert len(x.shape)=2一x是-个Numpy的2D张量 assert1len(y.shape)=14一y是-个Numpy向量 assert x.shape[1]==y.shape[0] xx.copy() 4一避免覆盖输入张量 for i in range(x.shape[0]): for j in range(x.shape[1]): x[i,j]+=y[j】 return x 如果一个张量的形状是(a,b,...n,n+1,...m),另一个张量的形状是(n,n+1, ··m),那么你通常可以利用广播对它们做两个张量之间的逐元素运算。广播操作会自动应用 于从a到n-1的轴。 下面这个例子利用广播将逐元素的maximum运算应用于两个形状不同的张量。 电子书寻找看手相钉钉或微信pythontesting
2.3 神经网络的“齿轮”:张量运算 31 1 5 3 7 2 6 4 8 9 运算交给安装好的基础线性代数子程序(BLAS,basic linear algebra subprograms)实现(没装 的话,应该装一个)。BLAS 是低层次的、高度并行的、高效的张量操作程序,通常用 Fortran 或 C 语言来实现。 因此,在 Numpy 中可以直接进行下列逐元素运算,速度非常快。 import numpy as np z = x + y z = np.maximum(z, 0.) 2.3.2 广播 上一节 naive_add 的简单实现仅支持两个形状相同的 2D 张量相加。但在前面介绍的 Dense 层中,我们将一个 2D 张量与一个向量相加。如果将两个形状不同的张量相加,会发生 什么? 如果没有歧义的话,较小的张量会被广播(broadcast),以匹配较大张量的形状。广播包含 以下两步。 (1) 向较小的张量添加轴(叫作广播轴),使其 ndim 与较大的张量相同。 (2) 将较小的张量沿着新轴重复,使其形状与较大的张量相同。 来看一个具体的例子。假设 X 的形状是 (32, 10),y 的形状是 (10,)。首先,我们给 y 添加空的第一个轴,这样 y 的形状变为 (1, 10)。然后,我们将 y 沿着新轴重复 32 次,这样 得到的张量 Y 的形状为 (32, 10),并且 Y[i, :] == y for i in range(0, 32)。现在, 我们可以将 X 和 Y 相加,因为它们的形状相同。 在实际的实现过程中并不会创建新的 2D 张量,因为那样做非常低效。重复的操作完全是 虚拟的,它只出现在算法中,而没有发生在内存中。但想象将向量沿着新轴重复 10 次,是一种 很有用的思维模型。下面是一种简单的实现。 def naive_add_matrix_and_vector(x, y): assert len(x.shape) == 2 assert len(y.shape) == 1 assert x.shape[1] == y.shape[0] x = x.copy() for i in range(x.shape[0]): for j in range(x.shape[1]): x[i, j] += y[j] return x 如果一个张量的形状是 (a, b, ... n, n+1, ... m),另一个张量的形状是 (n, n+1, ... m),那么你通常可以利用广播对它们做两个张量之间的逐元素运算。广播操作会自动应用 于从 a 到 n-1 的轴。 下面这个例子利用广播将逐元素的 maximum 运算应用于两个形状不同的张量。 逐元素的相加 逐元素的 relu x 是一个 Numpy 的 2D 张量 y 是一个 Numpy 向量 避免覆盖输入张量 书籍下载qq群6089740 钉钉群21734177 IT书籍 http://t.cn/RDIAj5D 电子书寻找看手相 钉钉或微信pythontesting
32 第2章神经网络的数学基础 import numpy as np x=np.random.random(64,3,32,10))4一x是形状为(64,3,32,10)的随机张量 y np.random.random((32,10)) 4一y是形状为(32,10)的随机张量 z=np.maximum(x,y) 4一输出z的形状是(64,3,32,10),与x相同 2.3.3张量点积 点积运算,也叫张量积(tensor product,不要与逐元素的乘积弄混),是最常见也最有用的 张量运算。与逐元素的运算不同,它将输入张量的元素合并在一起。 在Numpy、Keras、Theano和TensorFlow中,都是用*实现逐元素乘积。TensorFlow中的 点积使用了不同的语法,但在Numpy和Keras中,都是用标准的dot运算符来实现,点积。 import numpy as np z np.dot(x,y) 数学符号中的点(.)表示点积运算。 z=x.y 从数学的角度来看,点积运算做了什么?我们首先看一下两个向量×和y的点积。其计算 过程如下。 def naive vector_dot(x,y): assert len(x.shape)==1 assert len(y.shape)==1 x和y都是Numpy向量 assert x.shape[o]=y.shape[0] z=0. for i in range(x.shape[0]): z+=x[i]*y[i] return z 注意,两个向量之间的点积是一个标量,而且只有元素个数相同的向量之间才能做点积。 你还可以对一个矩阵×和一个向量y做点积,返回值是一个向量,其中每个元素是y和x 的每一行之间的点积。其实现过程如下。 import numpy as np def naive_matrix_vector_dot(x,y): x是一个Numpy矩阵 assert len(x.shape)==2 assert len(y.shape)==1 d一y是一个Numpy向量 assert x.shape[1]==y.shape[0] 4一x的第1维和y的第0维大小必须相同 z np.zeros(x.shape[0]) for i in range(x.shape[0]): for j in range(x.shape[1]): 这个运算返回一个全是0的向量, z[i]+=x[i,j】*y[j] 其形状与x.shape[0】相同 return z
32 第 2 章 神经网络的数学基础 import numpy as np x = np.random.random((64, 3, 32, 10)) y = np.random.random((32, 10)) z = np.maximum(x, y) 2.3.3 张量点积 点积运算,也叫张量积(tensor product,不要与逐元素的乘积弄混),是最常见也最有用的 张量运算。与逐元素的运算不同,它将输入张量的元素合并在一起。 在 Numpy、Keras、Theano 和 TensorFlow 中,都是用 * 实现逐元素乘积。TensorFlow 中的 点积使用了不同的语法,但在 Numpy 和 Keras 中,都是用标准的 dot 运算符来实现点积。 import numpy as np z = np.dot(x, y) 数学符号中的点(.)表示点积运算。 z=x.y 从数学的角度来看,点积运算做了什么?我们首先看一下两个向量 x 和 y 的点积。其计算 过程如下。 def naive_vector_dot(x, y): assert len(x.shape) == 1 assert len(y.shape) == 1 assert x.shape[0] == y.shape[0] z = 0. for i in range(x.shape[0]): z += x[i] * y[i] return z 注意,两个向量之间的点积是一个标量,而且只有元素个数相同的向量之间才能做点积。 你还可以对一个矩阵 x 和一个向量 y 做点积,返回值是一个向量,其中每个元素是 y 和 x 的每一行之间的点积。其实现过程如下。 import numpy as np def naive_matrix_vector_dot(x, y): assert len(x.shape) == 2 assert len(y.shape) == 1 assert x.shape[1] == y.shape[0] z = np.zeros(x.shape[0]) for i in range(x.shape[0]): for j in range(x.shape[1]): z[i] += x[i, j] * y[j] return z x 是形状为 (64, 3, 32, 10) 的随机张量 y 是形状为 (32, 10) 的随机张量 输出 z 的形状是 (64, 3, 32, 10),与 x 相同 x 和 y 都是 Numpy 向量 y 是一个 Numpy 向量 x 的第 1 维和 y 的第 0 维大小必须相同 x 是一个 Numpy 矩阵 这个运算返回一个全是 0 的向量, 其形状与 x.shape[0] 相同