文章首发于本人CSDN账号:https://blog.csdn.net/tefuirnever
由于微信不允许外部链接,你需要点击页尾左下角的“阅读原文”,才能访问文中的链接。
1、python中的广播
这是一个不同食物(每100g)中不同营养成分的卡路里含量表格,表格为3行4列,列表示不同的食物种类,从左至右依次为苹果(Apples),牛肉(Beef),鸡蛋(Eggs),土豆(Potatoes)。行表示不同的营养成分,从上到下依次为碳水化合物,蛋白质,脂肪。
那么,现在假设我们想要计算不同食物中不同营养成分中的卡路里百分比,应该怎么做?
以计算苹果中的碳水化合物卡路里百分比含量为例,首先计算苹果(Apples)(100g)中三种营养成分卡路里总和 56+1.2+1.8 = 59
,然后用 56 / 59 = 94.9%
算出结果。可以明显地看出苹果(Apples)中的卡路里大部分来自于碳水化合物(Carb),而牛肉(Beef)则不同。对于其他食物,计算方法类似。首先,按列求和,计算每种食物中(100g)三种营养成分总和,然后分别用不用营养成分的卡路里数量除以总和,计算百分比。
那么,能否在向量化的基础上用代码完成这样的一个计算过程呢?
当然是可以的,假设上图的表格是一个4行3列的矩阵 ,记为 ,接下来使用 Python 的 numpy 库完成这样的计算。使用两行代码就可以完成整个过程,第一行代码对每一列进行求和,第二行代码分别计算每种食物每种营养成分的百分比。
在 jupyter notebook 中输入如下代码,按 Ctrl + Enter 运行,输出如下:
下面再计算每列的和,可以看到输出是每种食物(100g)的卡路里总和。
其中 sum
的参数 axis=0
表示求和运算按列执行,之后会详细解释。
接下来计算百分比,这条指令将 的矩阵 除以一个 的矩阵,得到了一个 的结果矩阵,这个结果矩阵就是要求的百分比含量。
到这里问题就解决了,现在来解释一下 A.sum(axis = 0)
中的参数 axis
。axis用来指明将要进行的运算是沿着哪个轴执行,在numpy中,0轴是垂直的,也就是列,而1轴是水平的,也就是行。 而第二个 A / cal.reshape(1, 4)
指令则调用了 numpy 中的广播机制。这里使用 的矩阵 除以 的矩阵 。技术上来讲,其实并不需要再将矩阵 reshape
(重塑)成 ,因为矩阵 本身已经是 了。但是当我们写代码的过程中出现不确定矩阵维度的时候,通常会对矩阵进行重塑来确保得到想要的列向量或行向量。重塑操作 reshape
是一个常量时间的操作,时间复杂度是 ,它的调用代价极低,所以使用是没问题的,也推荐大家使用。
那么一个 的矩阵是怎么和 的矩阵做除法的呢?来看一些广播的例子:
在 numpy 中,当一个 的列向量与一个常数做加法时,实际上会将常数扩展为一个 的列向量,然后两者做逐元素加法。结果就是右边的这个向量。这种广播机制对于行向量和列向量均可以使用。
再看下一个例子。
用一个 的矩阵和一个 的矩阵相加,其泛化形式是 的矩阵和 的矩阵相加。在执行加法操作时,其实是将 的矩阵复制成为 的矩阵,然后两者做逐元素加法得到结果。针对这个具体例子,相当于在矩阵的第一列全部加100,第二列全部加200,第三列全部加300。这就是在前面例子中计算卡路里百分比的广播机制,只不过那里是除法操作,这里是加法操作(广播机制与执行的运算种类无关)。
下面是最后一个例子。
这里相当于是一个 的矩阵加上一个 的矩阵。在进行运算时,会先将 矩阵水平复制 次,变成一个 的矩阵,然后再执行逐元素加法。
广播机制的一般原则如下:
这里的广播和播音广播是完全不同的,它的要求是什么呢?什么样的条件下可以使用广播?
要求:如果两个数组的后缘维度的轴长度相符或其中一方的轴长度为1,则认为它们是广播兼容的。广播会在缺失维度和轴长度为1的维度上进行。
如何计算后缘维度的轴长度?可以使用代码 A.shape[-1]
即矩阵维度元组中的最后一个位置的值,就是矩阵维度的最后一个维度,比如卡路里计算的例子中,矩阵 后缘维度的轴长度是4,而矩阵 的后缘维度也是4,故满足了后缘维度轴长度相符的条件,可以进行广播。广播会在轴长度为1的维度上进行,轴长度为1的维度对应 axis=0
,即垂直方向,矩阵 沿 axis=0
(垂直方向)复制成为 ,之后两者进行逐元素除法运算。
简单概括总结就是,先变成一样大,再逐元素除法。
矩阵 和矩阵 进行四则运算,后缘维度轴长度相符,符合条件,可以广播,广播沿着轴长度为1的轴进行,即 广播成为 ,之后做逐元素四则运算。
矩阵 和矩阵 进行四则运算,后缘维度轴长度不相符,但其中一方轴长度为1,符合条件,可以广播,广播沿着轴长度为1的轴进行,即 广播成为 ,之后做逐元素四则运算。
矩阵 和常数 进行四则运算,后缘维度轴长度不相符,但其中一方轴长度为1,符合条件,可以广播,广播沿着缺失维度的轴进行,缺失维度就是 axis=0
,轴长度为1的轴是 axis=1
,即 广播成为 ,之后做逐元素四则运算。
最后总结一下 broadcasting
,可以看看下面的图:
2、numpy向量
Python 的特性允许你使用 广播(broadcasting) 功能,这是 Python 的 numpy 程序语言库中最灵活的地方,但这是程序语言的优点,也是缺点。
为了演示 Python-numpy 的一个容易被忽略的效果,特别是怎样在 Python-numpy 中构造向量,来做一个快速示范。
首先设置 ,这样会生成存储在数组 中的5个高斯随机数变量;然后输出 ,从屏幕上可以得知,此时 的 shape(形状) 是一个 的结构同样地, 的 shape 也是这样的。这在 Python 中被称作 一个一维数组。它既不是一个行向量也不是一个列向量,这也导致它有一些不是很直观的效果。
比如 和 的转置阵最终结果看起来一样,shape 也是一样的。但是输出 和 的转置阵的内积,你可能会想, 乘以 的转置,返回的可能会是一个矩阵。但如果这样做,你只会得到一个数。
所以在编写神经网络时,不要使用 shape 为 (5,)
、(n,)
或者其他一维数组的数据结构。相反,设置 为 ,这样就是一个5行1列的向量。在先前的操作里 和 的转置看起来一样,而现在这样的 变成一个新的 的转置,并且它是一个行向量。当输出 的转置时有两对方括号,而之前只有一对方括号,所以这就是 1行5列的矩阵和一维数组的差别。
如果这次再输出 和 的转置的乘积,会返回一个向量的外积,也就是一个矩阵。这就符合我们的预期了,也就是在可控范围内了,因为你知道自己的代码输出是什么了。
除了,输入确定维度的矩阵或向量之外,还有一件事,就是如果你不能完全确定一个向量的维度,建议你扔一个 断言语句(assertion statement) 进去。这样,就可以确保在这种情况下是否是一个 向量了,或者说是一个列向量。
如果不对的话,就会报一个叫做 AssertionError 的错误!!!
3、编程框架
这个我在 大话卷积神经网络CNN(干货满满) 中讲过,目前主流的是 Google的TensorFlow、Facebook的pytorch 还有 百度的paddlepaddle,如果是研究的话,我建议使用TensorFlow,因为它更好理解一下基础原理,而不是单纯的调包侠,不过现在pytorch的使用要更热门一些,社区也更广,推荐学习。