目录
〇 、前言
一 、数组(Array)与矩阵(Matrix)
*讲讲一维数据(一行或一列数据)
Example1
Example2
numpy.matrix()
二 、各种运算
0、数组或矩阵的每个元素都乘(加减除···)某个数
1、矩阵的点积(加减)运算
2、数组间对应位元素的运算
①、两数组的shape完全一样
②、不同shape的就broadcast
三、后记
最近这段时间开始了我的机器学习的python踩坑之旅,毕竟之前主要在matlab上coding的,而matlab对矩阵的处理那是非常非常方便的,导致我被惯坏了,在转到用python写框架时还保持着matlab时的思维,全线飘红,不过主要是自己太菜,对numpy的一些机制不了解,经过不断search后,写下这篇笔记。
首先我们得弄清楚numpy中数组与矩阵的区别:Matrix必须是2维的,但是Array可以是多维的,一维二维到N维,Matrix是Array的一个小的分支,包含于Array。所以Matrix 拥有Array的所有特性。对于刚接触机器学习的我们很容易会被迷惑住双眼,因为我们平时接触到的数据维度基本停留在二维,又由于Matrix 拥有Array的所有特性,两者也都都可以进行加减乘除的运算,当我们在处理其他维度的数据时就很容易混用,导致出错。
import numpy as np
coding经常出错的地方是对一维数据的处理,我将给出几个例子,大家很容易就能理解。
# Input:
a = np.ones((1, 5)) #要有括号括起来,这就是它的shape
print(a)
print(a.shape)
b = np.zeros(5)
print(b)
print(b.shape)
# Output:
a:
[[1. 1. 1. 1. 1.]]
a.shape:
(1, 5)
b:
[0. 0. 0. 0. 0.]
b.shape:
(5,)
可以看到,我们都只是想构造一个可以存储5个数据的容器,但两者的shape是不一样的。此时a是二维数组(也可以说他就是个Matrix,这种情况下Array和Matrix确实是没区别的),a是一个有1个数据“宽度”,有5个数据“长度”的“面”;而此时b是一维数组,通过shape也可以看出,b是一条只有5个数据的“长度”的“线”,没有“宽度”,因此它不是Matrix,是不能对其进行转置求逆等操作的。
特别注意:一维数组不是向量,一维数组是不能转置的!!!
虽然在我们看来存的同样都是一个维度的数据,但两者的机制是不一样的,shape也不一样,numpy运算对容器之间的shape会有严格的要求,所以需要特别注意维度问题。
# Input:
d = np.ones((3, 5))
k = d[:, 3] #取d的第4列
print(k)
print(k.shape)
# Output :
k:
[1. 1. 1.]
k.shape:
(3,)
这个例子是用冒号取d的第4列数据k,此时k的shape是(3,),k是一维数组,不是列向量,拿去做其他运算的时候shape会有很大问题。
不仅仅是这个例子,有很多函数他们返回的也是一维数组,也会造成很多纬度上的问题出现,大家要提前了解好要用的函数返回的是什么类型的数据。
如果想让k是一个列向量,此时就得用numpy.matrix()来将数组转化为Matrix。
# Input :
d = np.ones((3, 5))
k_matrix = np.matrix(d[:, 3])
print(k_matrix)
print(k_matrix.shape)
# Output :
k_matrix:
[[1. 1. 1.]]
k_matrix.shape:
(1, 3)
可以看到转成Matrix后shape并不是我们想要的列向量的(3,1),虽然我们脑子里认为取的是一列,但电脑不这么认为,因为d[:, 3]返回的就是“一行数据”(一维数组),转成Matrix后是行向量,所以需要把它加一个转置变成我们想要的列向量。这一点也是很多freshman很容易忽略的。
# Input :
k_matrix = np.matrix(d[:, 3]).T
# 也可以用transpose
# np.transpose(k_matrix)
# Output :
k_matrix:
[[1.]
[1.]
[1.]]
k_matrix.shape:
(3, 1)
下面我将从运算目的和数据维度将各种常用的运算分好类别,大家看菜吃饭。这里都是拿一维和二维的数据讲解,把这个弄懂,更高维度的也是很容易理解的。其他比较少用运算的大家要善用Google,看看文档。
这里numpy会将这单个数广播(broadcast)成和前面那个数组或矩阵一样shape的数组或矩阵,再进行元素级别的运算
# Input :
e = np.ones((3, 5)) * 0.5
print(e)
# Output :
e:
[[0.5 0.5 0.5 0.5 0.5]
[0.5 0.5 0.5 0.5 0.5]
[0.5 0.5 0.5 0.5 0.5]]
维度要求就是按照线性代数的维度要求,点积要求“前列等于后行”,加减要求整个shape都一样。
# Input :
e = np.matrix(np.ones((3, 5)) + 0.5)
f = np.matrix(np.zeros((5, 2)) + 0.5)
print(e * f)
# Output :
e * f:
[[3.75 3.75]
[3.75 3.75]
[3.75 3.75]]
这里要注意如果直接用 “*” 来点积的话,e和f的类型必须至少有一个是Matrix类型,要用numpy.matrix(),或者用np.dot()。
g = np.ones((3, 5)) + 0.5
h = np.zeros((5, 2)) + 0.5
print(np.dot(g, h))
如果g(ndarray)*h(ndarray)的话numpy会认为你是在做数组对应位元素间的运算,会自动broadcast,但维度不符合broadcast规则就报错,就算维度刚刚好没问题,但做的也不是我们想要的点积运算。
易错总结:
如果都是ndarray类型:“+” “-” “*” “/” “**” 等就是对应元素的运算。想要点积就np.dot()。
如果至少有一个是matrix类型:“+” “-” “*” “**”就是矩阵运算。
# Input :
i = np.ones((5, 3)) + 0.5
j = np.zeros((5, 3)) + 0.5
print(i*j)
# Output :
i * j:
[[0.75 0.75 0.75]
[0.75 0.75 0.75]
[0.75 0.75 0.75]
[0.75 0.75 0.75]
[0.75 0.75 0.75]]
我个人理解广播的操作就是通过对某个数组沿其长度为1的维度“拉伸复制”(有点像Excel拉单元格的右下角),使其shape和另一个数组原本或广播后的shape一样,那么就可以广播。
所以简单来讲要满足broadcast规则的必要条件(至少得满足)是:两数组中至少有一个是一维数组或shape是(1,n)或(n,1)的二维数组,否则肯定会错。
对于广播机制,用文字说的话感觉会越说越晕,毕竟情况还是很多的,建议大家还是走传送门看大佬讲,动手做几次就能feel到它是怎么操作的,其实还是很好理解的。
python处理矩阵和数组还是比matlab麻烦不少,但主要会出错搞乱的地方就是一维数组和(二、1)这里,会因为shape不匹配造成很多运算维度出错问题,只要自己清楚这个容器是什么类型,shape是多少,配合类型转换和转置等操作,就能解决问题了。
如果我说的有纰漏,希望大佬们指出错误。