深度学习——PyTorch框架
绪论
对于算法科研⼈员来说,熟练掌握并应⽤⼀种深度学习框架是⼀项必备技能。学术研究⼈员关⼼的是研究中算法的迭代速度,其应⽤场景通常是相对较⼩的数据集上,最⼤的限制因素不是性能,⽽是快速实现并验证假设的能⼒,使得学术研究倾向于PyTorch。
⼀、NumPy基础
在机器学习和深度学习中,图像等输⼊数据最终都要转换为数组或矩阵。NumPy能够有效的进⾏数组和矩阵的运算,是数据科学的通⽤语⾔,对PyTorch⽽⾔,其重要性⼗分明显。
Python本⾝含有列表与数组数据结构,但对于⼤数据来说,这些结构与很多不⾜。例如列表的元素可以是任何对象,因此其保存的是对象的指针,这种结构显然浪费CPU和内存等宝贵资源;⾄于数组,其与C相似,但⼜不⽀持⾼维。NumPy的诞⽣弥补了这些不⾜:NumPy提供了ndarray与ufunc⽤于存储与处理⾼维数组。
1.1 ⽣成NumPy数组
NumPy是Python的外部库,不在标准库中,在使⽤之前需要导⼊:
import numpy as np
NumPy封装了⼀个新的数据类型ndarray,该对象封装了许多常⽤的数学运算函数,⽅便我们做数据处理、数据分析等。
1.1.1 转换创建数组
Python的基本数据类型可以直接转换为ndarray:
list=[3.14,2.17,0,1,2]
nd = np.array(list)
上述⽰例的列表换成元组依然成⽴。如果Python的基本数据类型是嵌套的,那么将会⽣成多维ndarray。
1.1.2 np.random⽣成数组
在深度学习中,需要对⼀些参数进⾏初始化,为了更有效训练模型,提⾼模型的性能,有些初始化还需要满⾜⼀定条件,如满⾜正态分布或均匀分布等。np.random提供了常⽤的随机⽣成:
-np.random.random(),⽣成0到1之间的随机数;
-np.random.uniform(),⽣成均匀分布的随机数;
-np.random.randn(),⽣成标准正态的随机数;
-np.random.randint(),⽣成随机整数;
-al(),⽣成正态分布。
1.1.3 指定创建数组
参数初始化时,有时需要⽣成⼀些特殊矩阵,如零矩阵等,可以通过NumPy的函数实现,例如:
-np.zeros(),创建全为0的数组;
-np.ones(),创建全为1的数组;
-np.empty(),创建空数组;
-np.eye(),创建单位矩阵;
1.1.4 其他函数⽣成数组
arange()是NumPy中的函数,其函数格式为
arange([start,] stop,[step,] dtype =None)
其与Python内值的range()相似,并⽣成⼀个包含从start或0,到stop之前元素的,以step或1为步长的ndarray。
linspace()也是NumPy中的函数,其函数格式为
linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
其可以根据输⼊的指定数据范围以及数量⾃动⽣成⼀个线性等分向量,其中endpoint指包含终点,retstep指返回包含ndarray和步长的元组。
此外,NumPy还提供其他的函数⽣成数组,在此不再展开介绍。
1.2 NumPy数据操作
在机器学习和深度学习中,涉及⼤量的数组或矩阵的运算与操作。
1.2.1 NumPy数组运算
np.multiply()⽤于矩阵的元素级乘法,其函数的简单格式为
multiply(x1, x2)
其也可以简单的代替为
x1 * x2
其元素之⼀也可以是标量。
np.dot()⽤于矩阵的矩阵级乘法,其函数的格式为
dot(x1, x2, out=None)
其参数需遵守矩阵乘法法则。
1.2.2 NumPy数组变形
修改指定数组的形状是NumPy的常⽤操作之⼀,常见函数与变量如下:
-shape(),对arr的维度改变⽽不修改向量;
-size(),对arr的维度改变且对向量等⽐改变;
-arr.T,arr的转置;
-arr.ravel(),⽣成将arr展为1维的数组的视图,随着arr的改变⽽改变;
-arr.flatten(),⽣成将arr展为1维的数组的副本,作为新的ndarray;
-arr.squeeze(),对arr进⾏降维,及删除通道数为1的维度;
-anspose(),对arr的⾼维矩阵进⾏轴对换。
1.2.3 NumPy批量处理
深度学习中,由于源数据都⽐较⼤,通常需要采⽤批处理。将⼤数据拆分成批数据需要经过随机打乱数据、指定批⼤⼩并划分的步骤。⼀个简单的⽰例为:
import numpy as np
data = np.random.randn(10000,2,3)
np.random.shuffle(data)
batch_size =100
for i in range(0,len(data), batch_size):
x_batch_sum = np.sum(data[i:i + batch_size])
# some codes to processing x_batch_sum
其中np.random.shuffle()将data的第⼀维度作为数据数量将数据打乱⽽不打乱单个数据点的数据。
1.3 ⼴播机制
NumPy的ufunc对输⼊数组的shape有⼀定的要求,当其不满⾜要求时,会使⽤⼴播机制。⼴播在整个NumPy中⽤于决定如何处理形状迥异的数组,其具有如下规则:
-所有输⼊数组项shape的维度最⾼的对齐,不⾜的维度通过在shape前⾯加⼀个通道数为1维度的补齐;
-输出数组的shape是输⼊数组的shape各轴的最⼤值;
-仅当输⼊数组对应的维度的通道相同或为1时可以⽤于计算;
-当输⼊数组的某个维度通道数为1时,则在运算时按需赋值此通道的值。
考虑⼀个简单的⽰例,对于shape为(4, 1)的矩阵A与shape为(3,)的向量B,其加法操作根据⼴播机制有如下处理:
-B需要向A的维度对齐,其shape变为(1, 3);
-输出数组的shape取A与B的通道最⼤值,shape为(4, 3);
-A复制第2维度的通道形成(4, 3)的shape,B同理,并运算。
⼆、PyTorch基础
PyTorch类似于NumPy,属于动态框架,⽀持快速和灵活的⽅式构建动态神经⽹络,并允许在训练过程中快速更改代码且不妨碍其性能,并⽀持动态图,是快速实验的理想选择。
2.1 Numpy与Tensor
PyTorch与Numpy相似,其共享内存,转换⾮常⽅便。其最⼤的区别是Numpy将ndarray放在CPU中
加速运算,⽽PyTorch将Tensor放在GPU中加速运算。
2.1.1 ⽣成Tensor张量
⽣成Tensor的⽅法很多,可以从列表或者ndarray中构建,也可以指定形状构建。常见的构建⽅法有:
-torch.Tensor(),直接从Python或Numpy构造张量;
-(),创建⼆维对⾓张量;
-s(),创建指定shape的零张量;
还有⼀些类似NumPy的⽣成张量操作,这⾥不再展开叙述。
2.1.2 Tensor张量变形
在处理数据、构建⽹络层等过程中,经常需要了解与操作Tensor的形状。Torch与NumPy对形状的操作类似:
-tensor.numel(),计算tensor中元素的个数;
-tensor.view(),⽣成修改tensor的shape的视图,随着tensor的改变⽽改变;
-tensor.item(),若tensor只有单个元素,则返回Python标量;
还有⼀些类似NumPy的⽣成张量操作,这⾥不再展开叙述。
2.2 参数学习
在神经⽹络中,⼀个重要内容就是进⾏参数学习,⽽参数学习离不开求导。PyTorch为张量上的所有操作提供了⾃动求导功能。
2.2.1 计算图
计算图是⼀种有向⽆环图,其⽤图数据结构表⽰算⼦与变量之间的关系。例如表达式,其中是⾃定义的变量,其不依赖于其他变量,也是图的叶结点,为了计算叶结点的梯度,需要把张量参数requires_grad属性设置为True;⽽是计算得到的变量,作为最终的计算结果,亦是根结点。由变量及算⼦,如加法add(),乘法mul()构成的完整的计算过程,称为前向传播。
深度学习的⽬标是根据叶结点的梯度,根据复合函数的链式法则更新叶节点,这⼀过程称为反向传播。PyTorch调⽤backward()将⾃动计算各结点的梯度,对于叶结点,梯度值会累加到grad属性中,⽽对于⾮叶结点的计算操作会记录在grad_fn属性中,⽽叶结点的该属性值为None。
2.2.2 反向传播
考虑标量算式,那么对于前向及反向传播并⾃动求导,⾸先需要定义叶结点及算⼦结点:linspace numpy
import torch
x = torch .Tensor ([2])
w = torch .randn (1, requires_grad =True )
b = torch .randn (1, requires_grad =True )
z = torch .mul (x , w )
y = torch .add (z , b )
可以通过x.requires_grad、x.is_leaf、x.grad_fn来访问变量x的梯度、叶结点、算⼦结点属性,其他变量同理。由于变量x⽆需更新,是计算图的叶结点,因此上述三个表达式的结果分别为False、True、None。
在定义计算图后,使⽤
y .backward ()
实现梯度的反向传播。
2.3 机器学习
考虑⼀个有关回归的机器学习:给出⼀组数据,并基于表达式与⼀定的噪声形成数据,然后构造⼀个机器学习模型,学习参数与。分别使⽤NumPy和PyTorch完成,来⽐较其之间的异同。
y =wx +b x ,w ,b y y =wx +b x y =3x +22y y =wx +2b w b
NumPy实现及其学习的代码多但透明,有利于理解每步的⼯作原理。
⾸先导⼊库:
import numpy as np
import matplotlib .pyplot as plt
然后设置随机数种⼦,⽣成同⼀份数据与:
np .random .seed (100)
x = np .linspace (-1, 1, 100).reshape (100, 1)
y = 3 * np .power (x , 2) + 2 + 0.2 * np .random .rand (x .size ).reshape (100, 1)
并观察其分布:
plt .scatter (x , y )
plt .show
()
再初始化权重参数:
在这⾥插⼊代码⽚
设批⼤⼩为100,定义损失函数为均⽅误差,那么有
损失函数对参数求导,有利⽤梯度下降法学习参数,学习率为,那么代码实现为
lr = 0.01
for i in range (800):
y_pred = np .power (x , 2) * w + b
w_grad = np .sum ((y_pred - y ) * np .power (x , 2))
b_grad = np .sum ((y_pred - y ))
w -= lr * w_grad
b -= lr * b_grad
使⽤代码可视化
plt .plot (x , y_pred , 'r-', label ='predict')
plt .scatter (x , y , color ='blue', marker ='o', label ='true')
plt .show ()
效果为
⽽学习得到的参数。从结果上看,学习效果尚可。
x y Loss =(wx +21i =1∑100
i 2b −y )i 2
∂Loss /∂w ∂Loss /∂b =(wx +b −y )x i =1∑i 2i i
2=(wx +b −y )i =1∑100
i 2i lr w =2.9913,b =2.0974
使⽤NumPy⼿⼯的构建完成机器学习适⽤于⽐较简单的情况,但如果复杂⼀些,代码量将⼏何级增加。PyTorch可以简洁的完成该任务。
⾸先导⼊库:
import torch
import matplotlib.pyplot as plt
并⽣成训练数据,⽣成与上述⼀致的数据并可视化:
torch.manual_seed(100)
x = torch.unsqueeze(torch.linspace(-1,1,100),1)
y =3* x.pow(2)+2+0.2* torch.rand(x.size())
plt.scatter(x.numpy(), y.numpy())
()
plt.show
再初始化权重:
w = torch.randn(1,1, dtype=torch.float, requires_grad=True)
b = s(1,1, dtype=torch.float, requires_grad=True)
定义损失并训练模型:
lr =0.001
for i in range(800):
y_pred = x.pow(2).mm(w)+ b
loss =0.5*(y_pred - y)**2
loss = loss.sum()
loss.backward()
_grad():
w -= lr * w.grad
b -= lr * b.grad
可视化训练结果:
plt.plot(x.numpy(), y_pred.detach().numpy(),'r-', label='predict')
plt.scatter(x.numpy(), y.numpy(), color='blue', marker='o', label='True')
plt.legend()
()
plt.show
⽽学习到的参数,该结果与NumPy学习的差不多。
三、PyTorch核⼼组件
PyTorch的数据结构与⾃动求导机制,可以⼤⼤的提⾼开发效率。⽽PyTorch的核⼼组件,则可以极⼤的简化构建神经⽹络模型的任务。
3.1 简单神经⽹络实例
神经⽹络看起来很复杂,但核⼼部分并不多,把这些组件确定后,神经⽹络基本就确定了。考虑使⽤神经⽹络完成⼿写数字进⾏识别的实例,来说明借助nn实现⼀个神经⽹络,并对神经⽹络有⼀个直观的了解。
3.1.1 数据处理
⾸先导⼊必要的模块:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论