PyTorch框架使用了Python作为基本编程语言,因此读者也需要具备一定的Python基础。
Linux基础
Linux诞生于1991年,是一个免费使用与自由传播的类UNIX操作系统,凭借其稳定的性能与开放的社区,在手机、电脑、超级计算机等各种计算机设备中都能看到Linux的身影。
由于Linux系统知识树很大,不可能对所有知识点都覆盖到,因此这里将简要介绍基本目录结构和环境变量这两个知识点。
1.基本目录结构
Linux的首要思想是“一切都是文件”,因此对于Linux系统对Linux根目录做了统一的规范,必须包含boot、lib、home、usr和opt等文件。下面对几个主要的目录进行说明:
• boot:Linux系统启动时用到的文件,建议单独分区,大小512MB即可。
• lib:系统使用到的函数库目录,协助系统中程序的执行,比较重要的有lib/modules目录,存放着内存文件。
• bin:可执行的文件目录,包含了如ls、mv和cat等常用的命令。
• home:默认的用户目录,包含了所有用户的目录与数据,建议设置较大的磁盘空间。
• usr:应用程序存放的目录,其中,usr/local目录下存放一些软件升级包,如Python、CUDA等,usr/lib目录下存放一些不能直接运行但却是其他程序不可或缺的库文件,usr/share目录下存放一些共享的数据。
• opt:额外安装的软件所在的目录,例如常用的ROS可执行文件,一般就存放在opt/ros目录下。
2.环境变量
环境变量,通俗讲是指操作系统执行程序时默认设定的参数,如各种可执行文件、库的路径等。我们将会用到Python、PyTorch和CUDA等多个库,具体使用时调用的是哪一个版本的库,需要设定环境变量。
环境变量有系统级与用户级之分。系统级的环境变量是每一个登录到系统的用户都要读取的变量,可通过以下两个文件进行设置:
• /etc/environment:用于为所有进程设置环境变量,是系统登录时读取的第一个文件,与登录用户无关,一般要重启系统才会生效。
• /etc/profile:用于设置针对系统所有用户的环境变量,是系统登录时读取的第二个文件,与登录用户有关。
用户级环境变量是指针对当前登录用户设定的环境变量,也可以通过两个文件进行设置:
• ~/.profile:对应当前用户的profile文件,用于设置当前用户的工作环境,默认执行一次。
• ~/.bashrc:对应当前用户的bash初始化文件,每打开一个终端,就会被执行一次。我们可以在终端中使用echo来查看当前的环境变量,例如:
echo $PYTHONPATH
如果想永久性地设置环境变量,可以在上述的多种文件中添加环境变量。例如我们想指定使用的Python路径,可以修改~/.bashrc文件,在最下方添加:
export PYTHONPATH=/home/yourpythonpath:$PYTHONPATH
在终端中执行source ~/.bashrc即可生效。
如果仅仅是临时设置环境变量,可以在终端中使用set或者export命令,这种方式只在当前终端中生效,对其他终端无效。
export PYTHONPATH=/home/yourpythonpath
Python基础
Python也是PyTorch深度学习框架的第一语言。
1.变量与对象
>> a = 1 # 这里1为对象,a是指向对象1的变量>> a = 'hello' # 变量a可以指向任意的对象,没有类型限制>> a'hello'
• 不可变对象:对象对应内存中的值不会变,因此如果指向该对象的变量被改变了,Pyhton则会重新开辟一片内存,变量再指向这个新的内存,包括int、float、str、tuple等。
• 可变对象:对象对应内存中的值可以改变,因此变量改变后,该对象也会改变,即原地修改,如list、dict、set等。对于不可变对象,所有指向该对象的变量在内存中共用一个地址:
>> a = 1>> b = 1>> c = a + 0>> id(a) == id(b) and id(a) == id(c) # 使用id()函数来查看3个变量的内存地址True
Python中的变量也存在深拷贝与浅拷贝的区别,不可变对象无论深/浅拷贝,其地址都是一样的,而可变对象则存在3种情况,下面以list为例。
• 直接赋值:仅仅拷贝了引用,因此前后变量没有任何隔离,原list改变,拷贝的变量也会发生变化。
• 浅拷贝:使用copy()函数,拷贝了list最外围,而list内部的对象仍然是引用。
• 深拷贝:使用deepcopy()函数,list内外围均为拷贝,因此前后的变量完全隔离,而非引用。
实现拷贝需要首先引入copy模块>> import copy>> a = [1, 2, [1, 2]]>> b = a # 直接赋值,变量前后没有隔离>> c = copy.copy(a) # 浅拷贝>> d = a[:] # 相当于浅拷贝,与c相同>> e = copy.deepcopy(a) # 深拷贝,前后两个变量完全隔离>> a.append(3)>> a[2].append(3)>> a, b([1, 2, [1, 2, 3], 3], [1, 2, [1, 2, 3], 3])>> c[1, 2, [1, 2, 3]]>> d[1, 2, [1, 2, 3]]>> e[1, 2, [1, 2]]
2.作用域
a = 1 # a为全局变量def local(): # local也是全局变量,在全局作用域中b = 2 # b为局部变量
a = 1def local():a = 2 # 由于Python不需要预先声明,因此在局部作用域引入了新的变量,而没有修改全局local()print(a) #这里a的值仍为1
要实现局部修改全局变量,通常有两种办法,增加globa等关键字,或者使用list和dict等可变对象的内置函数。
a = 1b = [1]def local():global a # 使用global关键字,表明在局部使用的是全局的a变量a = 2b.append(2) #对于可变对象,使用内置函数则会修改全局变量local()print(a) #这里a的值已经被改变为2print(b) #这里输出的是[1, 2]
Python的作用域从内而外,可以分为Local(局部)、Enclosed(嵌套)、Global(全局)及Built-in(内置)4种,如图所示。变量的搜索遵循LEGB原则,如果一直搜索不到则会报错。
Python的LEGB原则
这4种作用域的含义如下:
• 局部:在函数与类中,每当调用函数时都会创建一个局部作用域,局部变量域像一个栈,仅仅是暂时存在,依赖于创建该局部作用域的函数是否处于活动的状态。
• 嵌套:一般出现在函数中嵌套了一个函数时,在外围函数中的作用域称为嵌套作用域,主要目的是为了实现闭包。
• 全局:模型文件顶层声明的变量具有全局作用域,从外部看来,模块的全局变量就是一个模块对象的属性,全局作用域仅限于单个模块的文件中。
• 内置:系统内解释器定义的变量。这种变量的作用域是解释器在则在,解释器亡则亡。
3.高阶函数
在编程语言中,高阶函数是指接受函数作为输入或者输出的函数。对于Python而言,函数是一等对象,即可以赋值给变量、添加到集合中、传参到函数中,也可以作为函数的返回值。下面介绍map()、reduce()、filter()和sorted()这4种常见的高阶函数。
>> f = abs>> f(-1) # 这里的f(-1)等同于abs(-1)1
map()函数可以将一个函数映射作用到可迭代的序列中,并返回函数输出的序列:
>>> def f(x): return x * x>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # 将定义的函数f依次作用于列表的各个元素[1, 4, 9, 16, 25, 36, 49, 64, 81]>>> map(str, range(4))['0', '1', '2', '3']
reduce()函数与map()函数不同,其输入的函数需要传入两个参数。reduce()的过程是先使用输入函数对序列中的前两个元素进行操作,得到的结果再和第三个元素进行运算,直到最后一个元素。
# reduce的计算过程:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)>>> from functools import reduce # 需要从functools引入reduce函数>>> def f(x, y): return x*10+y>>> reduce(f, [1, 3, 5, 7, 9])13579
filter()函数的作用主要是通过输入函数对可迭代序列进行过滤,并返回满足过滤条件的可迭代序列。
>>> def is_odd(n): return n % 2 == 0>>> filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]) # 对奇偶过滤,保留偶数[2, 4, 6, 10]
sorted()函数可以完成对可迭代序列的排序。与列表本身自带的sort()函数不同,这里的sorted()函数返回的是一个新的列表。sorted()函数可以传入关键字key来指定排序的标准,参数reverse代表是否反向。
sorted([3, 5, -87, 0, -21], key=abs, reverse=True) # 绝对值排序,并且为反序[-87, -21, 5, 3, 0]
对于一些简单逻辑函数,可以使用lambda匿名表达式来取代函数的定义,这样可以节省函数名称的定义,以及简化代码的可读性等。
>> add = lambda x, y : x+y # 使用lamda方便地实现add函数>> add(1,2)3>> map(lambda x: x+1, [1, 2, 3, 4, 5, 6, 7, 8, 9]) # lamda实现元素加1的操作[2, 3, 4, 5, 6, 7, 8, 9, 10]
4.迭代器与生成器
迭代器(Iterator)与生成器(Generator)是Python最强大的功能之一,尤其是在处理大规模数据序列时,会带来诸多便利。在检测模型训练时,图像与标签数据的加载通常就是利用迭代生成器实现的。
迭代器不要求事先准备好整个迭代过程中所有的元素,可以使用next()来访问元素。Python中的容器,如list、dict和set等,都属于可迭代对象,对于这些容器,我们可以使用iter()函数封装成迭代器。
>>> x = [1, 2, 3]>>> y = iter(x)>>> z = iter(x)>>> next(y), next(y), next(z) # 迭代器之间相互独立(1, 2, 1)
使用生成器还可以便捷地实现斐波那契数列的生成,如下:
def fibonacci():a = [1, 1]while True:a.append(sum(a)) # 往列表里添加下一个元素yield a.pop(0) # 取出第0个元素,并停留在当前执行点for x in fibonacci():print(x)if x > 50:break # 仅打印小于50的数字

