Numpy V.S. PyTorch
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 na = np.array([(1 , 2 , 3 ), (4 , 5 , 6 )]) na = np.zeros((3 , 4 ), order='F' ) na = np.full((3 , 5 ), 3.14 , order='C' ) na = np.ones((2 ,3 ,4 ), dtype=np.int16) na = np.identity(4 ) na = np.arange(0 , 3 , 0.1 ) ta = torch.tensor([[1 , 2 , 3 ], [4 , 5 , 6 ]]) ta = torch.FloatTensor([(1 , 2 ), (3 , 4 )]) ta = torch.full((2 , 3 ), 3.14 ) ta = torch.ones(2 , 3 , 4 , device='cuda' ) ta = torch.arange(0 , 3 , 0.1 ) ta = torch.eye(4 ) ta = torch.randn(2 , 3 ) ta = torch.empty(5 , 5 ) lst = na/ta.tolist() nb/tb = na/ta.clone()
索引
索引很灵活,可以用 :
和 ,
分隔,...
表示这一维度不要求,也可以是数组下标、布尔和表达式。
1 2 3 4 5 6 7 8 a = np/torch.array([[1 , 2 , 3 ], [4 , 5 , 6 ], [7 , 8 , 9 ]]) b = a[1 , ...] b = a[..., 1 :] b = a[[0 , 2 ]] b = a[[0 , 1 , 2 ], [0 , 1 , 0 ]] a[a < 0.5 ] = 0 np/torch.where(a > 0.5 , a, 0 ) np/torch.where(a > b, a, b)
维度变换
操作
numpy
torch
原维度拼接
np.concatenate((a, b,...), axis=0)
torch.cat([a, b], dim=0)
创建新维度拼接
np.stack([a, b], axis=0)
torch.stack([a, b], dim=0)
维度分割
np.split(a, size/indices, axis=0)
支持固定大小或不均匀分割
torch.split(a, size, dim=0)
只能按固定大小均匀分割,末块可能偏小
维度分块
np.array_split(a, chunks/indices, axis=0)
支持固定块数或不均匀分割
torch.chunk(input, chunks, dim=0)
只能按块数均匀分割,末块可能偏小
压缩维度
np.squeeze(a, axis=None)
torch.squeeze(a, dim=None)
a.squeeze(dim=None)
增加维度
np.expand_dims(a, axis)
torch.unsqueeze(a, dim)
a.unsqueeze(dim=None)
维度平铺
a.flatten(order='C')
torch.flatten(a)/a.flatten()
每维块填充
np.tile(a, reps)
torch.tile(a, reps)/ a.tile(reps)
元素填充
np.repeat(a, repeats, axis=None)
只支持操作某一维,但元素可不规则复制
a.repeat(*sizes)
支持多维复制,每维仅支持元素等倍复制
重排所有维
np.transpose(a, axes)
仅支持全量维度
torch.permute(a, dims)
a.permute(dims)
可支持部分维度
交换两维
np.swapaxes(a, axis1, axis2)
torch.transpose(a, dim0, dim1)
a.transpose(dim0, dim1)
调整形状
np.reshape(a, newshape)
a.reshape(newshape)
torch.reshape(a, newshape)
a.reshape(newshape)
调整形状
a.reshape(newshape)
input.view(*shape)
要求内存连续
内存连续性
a.flags['C_CONTIGUOUS']
检查
input.contiguous()
强制连续
爱因斯坦求和约定
爱因斯坦求和约定(einsum)提供了一套既简洁又优雅的规则,可实现包括但不限于内积、外积、矩阵乘法、转置等张量操作。第一个参数是个带有 → \rightarrow → 的字符串,第二个参数是所有参与输入的张量列表。字符串里用 26 26 26 个英文小写字母来索引张亮,箭头左边表示输入张量(以逗号分割),箭头右边表示输出张量。
表示维度的字符只能是 26 26 26 个英文字母 [a-z] \text{[a-z]} [a-z] 。
箭头左边不同输入之间重复出现的索引表示把输入张量沿着该维度做乘法操作。
只出现在箭头左边的索引在计算中间结果时需要在这个维度求和,被称为求和索引(Summation Indices )。
右边的索引顺序可以是任意的,会导致输出结果发生转置;右边的输出张量可以省略,按照默认规则推导。
当某个索引在右边出现且在左边部分出现时,会遵照多维矩乘在求和前进行这一维度的广播。
字符串里支持省略号,表示用户并不关心的索引。
numpy 和 pytorch 的 einsum 函数接口完全一致,下面以 torch 举例:
1 2 3 4 5 6 7 torch.einsum("ik,kj->ij" , [a, b]) torch.einsum('ij->ji' , [a]) torch.einsum('...ij->...ji' , [a]) torch.einsum('ij->' , [a]) torch.einsum('ij->j' , [a]) torch.einsum('ik,k->i' /'ik,k' , [a, b]) torch.einsum('bhmd,hmnd->bhmn' , [a, b])
函数计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 np.random.choice(na, size, replace, p) np.unique(na, return_index=True ) np.argsort(na, axis = []) sort(na, axis=-1 , kind='quicksort' ) np.min ()/max ()/mean()/sum () np.sin()/cos()/tan()/log()/exp() np.multiply() / na * nb np.power(x1, x2) np.matmul(na, nb) / na @ nb np.dot(na, nb) np.linalg.det(na) np.linalg.inv(na) / na.I na.transpose() / na.T ta.min (dim=1 ) ta.max (dim=0 , keepdim=True ) ta.mean(dim=(0 , 2 )) ta.sum () torch.amin(ta, dim=1 ) torch.sin(ta)/cos(ta)/exp(ta) ta * tb torch.mm(ta, tb) torch.matmul(ta, tb) torch.dot(v1, v2) torch.norm(ta, p=2 , dim=1 ) torch.multinomial(ta, num_samples=2 ) torch.randperm(10 )
周边操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ny.save(file, na, allow_pickle=True ) np.savez("runoob.npz" , *args, **kwds) nb = np.load(file) np.savetxt(file, na, fmt="%d" ) np.loadtxt(file, delimiter=' ' ) ta.to('cuda' ) ta.requires_grad_(True ) ta.detach() ta.cpu().numpy() na = ta.numpy() ta = torch.from_numpy(na)
基础语法
变量是将名字和对象关联起来,变量名是对象的引用而不是对象本身。
可变对象 的内存地址可以改变,也可以修改当前指向的对象的值,例如列表、字典和集合等;
不可变对象 的内存地址永远不会改变,无法修改其值,例如整数、浮点数和字符串等。
1 2 3 4 a, b = [0 ] * 10 , [{}] * 10 print (id (a[0 ]) == id (a[1 ])) print (type (a[0 ]), type (b[0 ])) a = b; a = b[:]
数值类型和字符串类型的基础运算
1 2 3 4 5 6 7 8 9 10 11 12 0x(1 ), 0o(1 ), 0b(1 ) hex (), oct (), bin () int (a, base = 10 ) a = 2 +3j print (a.real, a.imag) print (10 > 6 < 8 ) print ("{0:.2f}{1:20s}" .format (1.2 , "123" )) print ("{0:a<10d}" .format (1 )) print (r"hello\nworld" ) '2' * 3 print (ord ('我' ), chr (25105 ))
关于列表和迭代器
1 2 3 4 5 6 7 8 9 10 a = [True , False ] any (a)/all (a)/sum (a)/max (a)... for id , element in enumerate (a) i, _, j = [1 , 2 , 3 ] i, *j = [1 , 2 , 3 ] a.insert(index, x) a.pop(index); del a[index] a.remove(val) a = b[:]; a = b.copy() sorted (iterable[,key[, reverse]])
集合的元素和字典的键必须是不可变对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 a.issubset(b); a<b; a.issuperset(b); a>b a.union(b); a|b a.intersection(b); a&b a.difference(b); a-b a.symmertric_difference(b); a^b mailto = ['cc' , 'bbbb' , 'afa' , 'sss' , 'bbbb' , 'cc' , 'shafa' ] addr_to = list (set (mailto)) addr_to.sort(key = mailto.index) fac={1 :"00" , 2 :"00" } fac=dict (math="01" , python="02" ) max (dict , key = vote.get) del a[key]; a.pop(key) a.keys(), a.values(), a.items() d = dict (zip (d.values(), d.keys()))
函数的参数调用分成位置参数、关键字参数、带默认值参数、可变数量参数四种。
1 2 3 4 5 6 7 def init (arg, result=[] ): result.append(arg) print (result) init('a' ), init('b' ) def sum (*a ): def sum (**d ):
lambda 函数会 绑定全局变量地址而非其值 。以下代码的解决方案 见此 。
1 2 3 4 funcs = [] for i in range (10 ): funcs.append(lambda x: x + i)
类
除了最常规的实例方法外,python 还有用 @staticmethod
修饰的静态方法和 @classmethod
修饰的类方法。实例方法依赖实例 self,类方法依赖类 cls 或实例 self,静态方法不依赖类和实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Example : static_attributes = "a" def object_method (self, name ): print (name) @classmethod def class_method (cls ): print (cls.n1) @staticmethod def static_method (): print (Example.attributes) e = Example() e.object_method("b" ) Example.static_method() e.static_method() Example.class_method() e.class_method()
类的继承用 class B(A)
,如果想定义抽象类则需引入 abc 库。
1 2 3 4 5 6 from abc import ABC, abstractmethodclass MyAbstractClass (ABC ): @abstractmethod def my_abstract_method (self ): pass
Python 的类不存在严格意义上的私有成员,保护成员和私有成员常用 _
和 __
作为前缀。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Example : __n1 = "a" def __init__ (self ): self .__2 = "b" def __n3 (self ): print (self .__n2) def _n4 (self ): pass @property def n2 (self ): return self .__n2 @n2.setter def n2 (self, n2 ): self .__n2 = n2 print (e.__n2) print (e._Example__n2)
其中,__xxx__
是系统定义的特殊的类方法。
类或函数的方法
效果
dir()
类或对象的所有属性和方法(列表形式)
__dict__
类或对象中的所有属性和值(字典形式)
__doc__
描述信息,即定义开头长注释里的字符串;可用于模块、函数、类。
__new__
,__init__
前者用于类对象的创建(静态方法),后者用于对象的初始化(实例方法)。前者必须要给出返回值(对象),作为后者的第一个参数。
__del__
类的析构函数
__module__
,__class__
前者表示对象所在模块,后者表示对象所属的类
__str__
类需要转为字符串时(比如打印该对象)触发
__call__
当对象后面接 ()
后会触发
__getitem__
当对象后面接 []
后会触发
__setitem__
,__delitem__
连同上面的 __getitem__
,使类像字典一样使用
__iter__
, __next__
使该类变得可迭代,可以用 for ... in ...
遍历
标准库
argparse 参数解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import argparseparser = argparse.ArgumentParser(description="..." ) parser.add_argument('param1' , type =str , required=True , allow_abbrev=False ) parser.add_argument("-v" , "--verbosity" , help ="help_doc" , action="store_true" ) parser.add_argument("-v" , "--verbosity" , type =int , choices=[0 , 1 , 2 ], default=0 ) parser,add_argument("-v" , "--verbosity" , action="count" , default=0 ) group = parser.add_mutually_exclusive_group() group.add_argument("-v" , "--verbose" , action="store_true" ) group.add_argument("-q" , "--quiet" , action="store_true" ) args = parser.parse_args()
collections 拓展数据结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ''' namedtuple 具名数组,支持用属性名去访问元组 ''' import collections.namedtuple User = namedtuple('User' , ['name' , 'age' , 'id' ]) User = namedtuple('User' , 'name age id' ) user = User('tester' , '22' , '001' ) user = User._make(['Runoob' , 'male' , 12 ]) user = user._replace(age = 22 ) print (user) print (user._asdict) print (user._fields) ''' Counter 是 dict 的子类,用于计数可哈希对象 ''' from collections import Counterocc = dict (Counter(lst)) num = max (occ, key = occ.get) Counter(lst).most_common(k = top_k)
1 2 3 4 5 6 7 8 9 10 11 12 13 import functoolsdef multiply (x, y ): return x * ydouble = functools.partial(multiply, y=2 ) print (double(1 ))from functools import cmp_to_keydef compare (ele1, ele2 ): return ele2 - ele1 a = [2 , 3 , 1 ] print (sorted (a, key=functools.cmp_to_key(compare)))
itertools 的所有方法都实现了生成器函数,高效而节省内存。
1 2 3 4 from itertools import permutations, combinations, productpermutations(list , r = len (list )) combinations(list , r) product(list1, list2, ...)
json & pickle 序列化和反序列化
json 兼容标准的 JavaScript Object Notation 协议。
1 2 3 4 json.load(open ('demo.json' ,'r' )) json.loads('{' a':' 1111 ',' b':' 2222 '}' ) json.dump(dict_or_list, open ('demo.json' , 'w' ), indent = 2 ) a_str = json.dumps(a_dict)
pickle 是 Python 专有基于二进制的序列化和反序列化。对不信任的数据进行 unpickling 是不安全的。
1 2 pickle.load(open ('a.txt' , 'rb' )) pickle.dump(a, open ('a.txt' , 'wb' ))
random 随机
1 2 3 4 5 6 7 8 9 from random import *random.seed(seed) random() uniform(a, b) randint(a, b) choice(seq) shuffle(seq) sample(seq, k)
re 正则表达式
1 2 3 4 5 6 import rere.compile (pattern[, flags]) re.search(pattern, string) re.match (pattern, string) re.findall(pattern, string) re.sub(pattern, repl, string)
第三方库
pyparsing 定制解析器
tesserocr & pytesseract 图片文本检测
对给定图片进行文本检测,并返回识别后的字符串。
注意不太适合很小的图(比如只包括一个字符),进行适当的缩放和重复会有更好的识别效果。
Sympy 数学表达式运算
比较详细的中文介绍 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 x = sympy.Symbol('x' ) fx = 5 * x + 4 fx.free_symbols y1 = fx.evalf(subs={x:6 }) y = sympy.pi * sympy.Symbol('y' ) dx = sympy.diff(fx, x) sympy.integrate(fx, (x,0 ,1 )) ''' 解方程;方程列表,未知数列表(可不写) dict = True:返回的一个 list 记录多解信息,list 里的每个元素是一个 dict 解集 manual = True:手动解,加上会比较稳定、比较快,但是 = False 更适合求联立的方程 rational = None/False/True:None 表示全用浮点数解,False/True 表示里面用分数解,返回值是浮点数/分数。 ''' sympy.solve([fx, 2 *y-1 ], (x, y), dict = True )
Jupyter Notebook
即写即用,一直缓存结果,适合 python 开发。不推荐原生工具,VSCode 更好用。
在 notebook 里展示图片:
用 cv2.imshow("WindowsName", a)
无法正常显示图片,需要再后面加 cv2.waitKey(delay)
,如 delay=0
则跳出一张图片(鼠标关闭后才会继续运行)。
用 plt.imshow(a)
可以画出 a
,但是会在代码运行完后再画。
用 plt.imshow(a); plt.show()
后可以立即输出图片(也可以连续画出多张图片)。