个人吐槽区:上一篇文章的学习是纯看书学的,后来发现这样有些看不进去,于是在B站上找了网课.......
Element-wise operations(逐点运算)
逐点运算,顾名思义,也就是两个同等规模的张量进行运算时,相同位置的数值进行同样的运算。
举个栗子:
import numpy as np
>>> x = np.array([ 1, 2, 5, 3])
>>> y = np.array([ 2, 1, 0, 2])
>>> z = x + y
>>> z
array([3, 3, 5, 5])
同样的加、减、乘、除、与、或、异或、取余等等都可以。
瞎搞的时候弄了一个 ~
>>> x = np.array([1,2])
>>> z = ~x
>>> z
array([-2, -3], dtype=int32)
我以前一个~是非,然后发这里~的作用是取反-1.
Broadcasting(广播)
广播的作用是,当两个shape不同的张量进行运算时,会将维度较小的张量以复制的方式扩充得与另一个张量shape相同,再进行计算
比如
>>> x = np.random.randint(4,size=(2,4)) #产生一组大小为[0,4],size = (2,4)的张量
>>> x
array([[2, 2, 0, 0],
[2, 1, 3, 3]])
>>> y = np.random.randint(4,size=(4)) #与上同理
>>> y
array([0, 2, 0, 0])
>>> z = x+y
>>> z
array([[2, 4, 0, 0],
[2, 3, 3, 3]])
将两者进行加法运算:
>>> z = x+y
>>> z
array([[2, 4, 0, 0],
[2, 3, 3, 3]])
x = array([[2, 2, 0, 0],
[2, 1, 3, 3]])
这里相当于把y变为了:
y = array([[0, 2, 0, 0],
[0, 2, 0, 0]])
再进行逐点相加。
但这里有一点需要注意的是:并不是任何情况都能扩充。
比如一下shape的情况就不行:
- (2,3,4)与(2)、(3)
- (2,3,4)与(2,4)、(2,3)
但与之相反,以下情况是可以的:
- (2,3,4)与(4)
- (2,3,4)与(3,4)
- (2,3,4,5)与(3,4,5)
也就是说:只有小shape与大shape的末尾几位相同时,才能扩充,等于中间的或者前面的都不行。
下面是一些小拓展(针对前面用到的随机数函数)
随机数方法:
random() 方法返回随机生成的一个实数,它在[0,1)范围内。参数填size
语法:
import numpy as np
random.random()
uniform()方法在指令范围内生成随机数。参数填[a,b],size
语法:
import numpy as np
random.uniform()
randint()方法在指令范围内生成随机整数。参数填[a,b],size
语法:
import numpy as np
random.randint()
等等.....想看更多可以看这篇博客
玩一玩
针对上面的知识我们来做一个小应用,问题描述如下:
随机给定一组(3,4)的整数张量,我们需要算出每一个数据在所在列中的占比
比如, array([[1,1],
[1,1]])
对应结果:array([[0.5, 0.5],
[0.5, 0.5]])
思路很简单:
- 每一列都进行累加,得到张量y
- 再用原张量对得到的z进行除法
代码如下:
#对x进行定义和初始化
>>> x = np.random.randint(10,size=(3,4))
>>> x
array([[3, 2, 4, 7],
[9, 3, 5, 4],
[5, 3, 9, 8]])
#axis=0 按列求和
#axis=1 按行求和
>>> y = x.sum(axis=0)
>>> y
array([17, 8, 18, 19])
>>> z = x/y
>>> z
array([[0.17647059, 0.25 , 0.22222222, 0.36842105],
[0.52941176, 0.375 , 0.27777778, 0.21052632],
[0.29411765, 0.375 , 0.5 , 0.42105263]])
一个人瞎bb:我试了一下让axis=1,使得y得到x按行求和的结果:array([16, 21, 25]),如果想求出x中每一个数在所在列中的占比时,就不能直接除了(因为此时是shape分别为(3,4)和(3)进行的运算),那如果想实现这个目的该怎么做呢?(把x转置?)
查百度发现还真有转置函数
transpose()
>>> i = x.sum(axis=1)
>>> i
array([16, 21, 25])
>>> x = np.transpose(x) #把矩阵x转置
>>> x
array([[3, 9, 5],
[2, 3, 3],
[4, 5, 9],
[7, 4, 8]])
>>> z = x/i
>>> z
array([[0.1875 , 0.42857143, 0.2 ],
[0.125 , 0.14285714, 0.12 ],
[0.25 , 0.23809524, 0.36 ],
[0.4375 , 0.19047619, 0.32 ]])
(PS:我这段时间同时也在准备CCF,但我目前还是习惯C++写题,如果遇见一个需要我实现转置的题,那该怎么写呢?)
其实这也思路很简单:索引对换
伪代码如下:
for i:0->n
for j:0->m
cin >> array[i][j];
result[j][i] = array[i][j];//这里存的便是转置后的结果
Tensor dot(张量点积)
这一部分主要是讲到了函数 dot() 的运用,分为了多种情况:向量之间的、矩阵与向量、矩阵与矩阵。
Vector dot(向量点积)
向量点积的运算过程就是将相同位置的元素进行相乘,再将所有结果求和,得到一个值。
>>> import numpy as np
>>> x = np.random.randint(5,size=(3))
>>> x
array([2, 1, 3])
>>> y = np.random.randint(5,size=(3))
>>> y
array([3, 3, 2])
>>> z = np.dot(x,y)
>>> z
15
(注意,向量长度应当一致)
Matrix_vector dot(矩阵与向量点积)
需要矩阵的列数与向量长度相等,做的事情也就是:将矩阵的一整行与向量做点积(像向量与向量一样),然后得到的结果,每一行放上矩阵对应行和向量的点积结果。(这里有点“难看”没关系,我后面放一张图就好理解了)
>>> x = np.random.randint(5,size = (3,5))
>>> x
array([[4, 0, 4, 1, 4],
[2, 0, 4, 2, 3],
[0, 1, 2, 0, 2]])
>>> y = np.random.randint(5,size = (5,))
>>> y
array([0, 1, 4, 2, 2])
>>> z = np.dot(x,y)
>>> z
array([26, 26, 13])
Maxtrix dot(矩阵点积)
这个就是矩阵运算规律(向量也可以看成是 n*1 的矩阵)
>>> x = np.random.randint(5,size = (2,3))
>>> x
array([[2, 0, 2],
[0, 3, 2]])
>>> y = np.random.randint(5,size = (3,2))
>>> y
array([[1, 2],
[4, 4],
[2, 1]])
>>> z = np.dot(x,y)
>>> z
array([[ 6, 6],
[16, 14]])
More generally(更多情况)
这里主要是考虑下面两种情况的点积:
- (a,b,c,d) . (d, ) -> (a,b,c)
- (a,b,c,d) . (d,e) -> (a,b,c,e)
>>> x = np.random.randint(5,size = (2,3,4,5))
>>> y = np.random.randint(5,size = (5))
>>> z = np.dot(x,y)
>>> z.shape
(2, 3, 4)
>>> y = np.random.randint(5,size = (5,9))
>>> z = np.dot(x,y)
>>> z.shape
(2, 3, 4, 9)
其实也可以当成是y和x的末尾几个轴做点积,比如
- case1 : x末尾的4*5矩阵和向量y(5*1)做点积之后,得到的张量shape为(4*1),这个1是可以省略的,所以结果是(2,3,4)
- case2 : 与上同理,(4,5)*(5,9)得到(4,9),所以结果是(2,3,4,9)
Tensor reshaping
rashape() :
np.reshape(a, newshape, order='C')
#Gives a new shape to an array without changing its data.
他的作用是,在不增减,不改变原有数据的情况下,改变原张量的形状。
>>> x = np.random.randint(5,size=(2,3))
>>> x
array([[4, 2, 3],
[4, 2, 0]])
>>> x = x.reshape((6,1))
>>> x
array([[4],
[2],
[3],
[4],
[2],
[0]])
他做法就是按顺序挨个取出来,然后再挨个摆放。
书中和视频上还提到了转置函数(就是我上面用的那个),以及一些我不知道的用法和理解。
- 如果对超过2维的张量进行转置,会如何进行呢?
- 能否进行部分转置呢?
先回答前者:
在shape上:比如z.shape=(2,3,4,5),转置后z.shape=(5,4,3,2),也就是“倒过来”
在运算结果上,新张量每一个位置的值,其实和原张量对应位置的值的关系,也是“倒过来”
再回答后者:
>>> x = np.arange(12)
>>> x = x.reshape((2,3,2))
>>> x
array([[[ 0, 1],
[ 2, 3],
[ 4, 5]],
[[ 6, 7],
[ 8, 9],
[10, 11]]])
>>> z = np.transpose(x,(0,2,1))#这里只对最后两个轴进行调换,1号轴和2号轴,两者交换
>>> z
array([[[ 0, 2, 4],
[ 1, 3, 5]],
[[ 6, 8, 10],
[ 7, 9, 11]]])