从头开始了解PyTorch的简单实现

选自GitHub上,机器之心编译。
本教程展现了如何从了解张量开始到使用PyTorch训练简单的神经网络,是很是基础的PyTorch入门资源.PyTorch创建在Python和火炬库之上,并提供了一种相似Numpy的抽象方法来表征量(或多维数组),它还能利用GPU来提高性能。本教程的代码并不完整,详情请查看原Jupyter Notebook文档。
PyTorch使入门深度学习变得简单,即便你这方面的背景知识不太充足。至少,知道多层神经网络模型可视为由权重链接的节点图就是有帮助的,你能够基于前向和反向传播,利用优化过程(如梯度计算)从数据中估计权重。
  • 必备知识:该教程假设读者熟悉Python和NumPy。
  • 必备软件:在运行原Jupyter Notebook以前你须要安装PyTorch。原笔记本有代码单元格能够验证你是否作好准备。
  • 必需硬件:你须要安装NVIDIA GPU和CUDA SDK。据报告此举可能实现10-100的加速。固然,若是你没有进行此设置,那么你仍然能够在仅使用CPU的状况下运行PyTorch。可是,记住,在训练神经网络模型时,生命苦短!因此仍是尽量使用GPU吧!

1.必要的PyTorch背景

  • PyTorch是一个创建在Torch库之上的Python包,旨在加速深度学习应用。
  • PyTorch提供一种相似NumPy的抽象方法来表征张量(或多维数组),它能够利用GPU来加速训练。

1.1 PyTorch张量

PyTorch的关键数据结构是张量,即多维数组。其功能与NumPy的ndarray对象相似,以下咱们可使用torch.Tensor()建立张量。
#生成2-d pytorch张量(即,基质)
 pytorch_tensor = torch.Tensor(10,20)
的打印(“类型:” ,类型(pytorch_tensor), “ 和尺寸:”,pytorch_tensor.shape)

复制代码
若是你须要一个兼容NumPy的表征,或者你想从现有的NumPy对象中建立一个PyTorch张量,那么就很简单了。
#将pytorch张量转换为numpy数组:
 numpy_tensor = pytorch_tensor.numpy()
print(“type:”,type(numpy_tensor),“and size:”,numpy_tensor.shape)

#将numpy数组转换为Pytorch Tensor:
 print(“type:”,type(torch.Tensor(numpy_tensor)),“and size:”,torch.Tensor(numpy_tensor).shape)

复制代码

1.2 PyTorch与NumPy

PyTorch并非NumPy的简单替代品,但它实现了不少NumPy功能。其中有一个不便之处是其命名规则,有时它和和NumPy的命名方法至关不一样。咱们来举几个例子说明其中的区别:
1张量建立
T = torch.rand(2,4,3,5)
一个= np.random.rand(2,4,3,5)

复制代码
2张量分割
T = torch.rand(2,4,3,5)
一个= t.numpy()
pytorch_slice = T [ 0,1:3,:,4 ] 
numpy_slice = A [ 0,1:3,:,4 ] 
打印('张量[0,1:3,:1,4]:\ N',pytorch_slice)
打印('NdArray [0,1:3,:1,4]:\ N',numpy_slice)

------- -------------------------------------------------- ---------------- 
张量[ 0,1:3,:,4 ]:

 0.2032   0.1594   0.3114 
 0.9073   0.6497   0.2826
 [torch.FloatTensor大小的2 ×3] 

NdArray [ 0,1:3,:,4 ]:
[[ 0.20322084   0.15935552   0.31143939 ] 
[ 0.90726137   0.64966112   0.28259504 ]]

复制代码
3张量掩蔽
t = t  - 0.5
 a = t.numpy()
pytorch_masked = t [t> 0 ] 
numpy_masked = a [a> 0 ]

复制代码
4张量重塑
pytorch_reshape = t.view([ 6,5,4 ])
numpy_reshape = a.reshape([ 6,5,4 ])

复制代码

1.3 PyTorch变量

  • PyTorch张量的简单封装
  • 帮助创建计算图
  • Autograd(自动微分库)的必要部分
  • 将关于这些变量的梯度保存在.grad中
结构图:
计算图和变量:在PyTorch中,神经网络会使用相互链接的变量做为计算图来表示.PyTorch容许经过代码构造计算图来构建网络模型;以后PyTorch会简化估计模型权重的流程,例如经过自动计算梯度的方式。
举例来讲,假设咱们想构建两层模型,那么首先要输入和输出建立张量变量。咱们能够将PyTorch Tensor包装进变量对象中:
从 torch.autograd 进口可变
进口 torch.nn.functional 做为 ˚F 

X =变数(torch.randn(4,1),requires_grad = 假)
Y =变量(torch.randn(3,1),requires_grad = 假)

复制代码
咱们把requires_grad设置为True,代表咱们想要自动计算梯度,这将用于反向传播中以优化权重。
如今咱们来定义权重:
W1 =变数(torch.randn(5,4),requires_grad = 真)
W2 =变量(torch.randn(3,5),requires_grad = 真)

复制代码
训练模型:
def  model_forward (x):
    return F.sigmoid(w2 @ F.sigmoid(w1 @ x))

 print(w1)
 print(w1.data.shape)
 print(w1.grad)#最初,不存在

 ---- -------------------------------------------------- -------------------
可变含:
  1.6068  -1.3304  -0.6717  -0.6097 
-0.3414  -0.5062  -0.2533   1.0260 
-0.0341  -1.2144  -1.5983  -0.1392 
-0.5473   0.0084   0.4054   0.0970 
 0.3596   0.5987  -0.0324   0.6116
 [torch.Float传感器的大小5 X4的] 

torch.Size([ 5,4 ])
无

复制代码

1.4 PyTorch反向传播

这样咱们有了输入和目标,模型权重,那么是时候训练模型了咱们须要三个组件:
损失函数:描述咱们模型的预测距离目标还有多远;
导入 torch.nn 为 nn 
条件= nn.MSELoss()

复制代码
优化算法:用于更新权重;
导入 torch.optim 做为 optim 
optimizer = optim.SGD([w1,w2],lr = 0.001)

复制代码
反向传播步骤:
对于历元在范围(10):
    损耗=标准(model_forward(x)中,y)的
    optimizer.zero_grad() #零出之前梯度
     loss.backward()#计算新梯度
     optimizer.step() #应用这些梯度


打印( w1)

------------------------------------------------ ------------------------- 
变量包含:
 1.6067  -1.3303  -0.6717  -0.6095 
-0.3414  -0.5062  -0.2533   1.0259 
-0.0340  -1.2145  -1.5983  -0.1396 
-0.5476   0.0085   0.4055   0.0976
 0.3597   0.5986  -0.0324   0.6113
 [火炬。尺寸为5 x4的飞溅传感器 ]

复制代码

1.5 PyTorch CUDA接口

PyTorch的优点之一是为张量和autograd库提供CUDA接口。使用CUDA GPU,你不只能够加速神经网络训练和推断,还能够加速任何映射至PyTorch张量的工做负载。
你能够调用torch.cuda.is_available()函数,检查PyTorch中是否有可用CUDA。
cuda_gpu = torch.cuda.is_available()
if(cuda_gpu):
    print(“Great,you have a GPU!”)
elseprint(“Life is short  -  consider a GPU!”)

复制代码
很好,如今你有GPU了。
.cuda()
以后,使用cuda加速代码就和调用同样简单。若是你在张量上调用.cuda(),则它将执行从CPU到CUDA GPU的数据迁移。若是你在模型上调用.cuda(),则它不只将全部内部储存移到GPU,还将整个计算图映射至GPU。
要想将张量或模型复制回CPU,好比想和NumPy交互,你能够调用.cpu()。
若是 cuda_gpu:
    x = x.cuda()
    printtype(x.data))

x = x.cpu()
printtype(x.data))

--------------- -------------------------------------------------- -------- 
< class ' 火炬。cuda。FloatTensor '> 
< class ' 火炬。FloatTensor '>

复制代码
咱们来定义两个函数(训练函数和测试函数)来使用咱们的模型执行训练和推断任务。该代码一样来自PyTorch官方教程,咱们摘选了全部训练/推断的必要步骤。
对于训练和测试网络,咱们须要执行一系列动做,这些动做可直接映射至PyTorch代码:
1.咱们将模型转换到训练/推断模式;
2.咱们经过在数据集上成批获取图像,以迭代训练模型;
3.对于每个批量的图像,咱们都要加载数据和标注,运行网络的前向步骤来获取模型输出;
4.咱们定义损失函数,计算每一个批量的模型输出和目标之间的损失;
5.训练时,咱们初始化梯度为零,使用上一步定义的优化器和反向传播,来计算全部与损失有关的层级梯度;
6.训练时,咱们执行权重更新步骤。
def  train (model,epoch,criterion,optimizer,data_loader):
     model.train()
     for batch_idx,(data,target) in enumerate(data_loader):
         if cuda_gpu:
            data,target = data.cuda(),target.cuda( )
            model.cuda()
        数据,目标=变量(数据),变量(目标)
        输出=模型(数据)

        optimizer.zero_grad()
        损失=标准(输出,目标)
        loss.backward()
        optimizer.step()
         if( batch_idx + 1)% 400 == 0:
            print'Train Epoch:{} [{} / {}({:.0f}%)] \ tLoss:{:.6f}'. format(
                epoch,(batch_idx + 1)* len(data),len(data_loader.dataset ),
                100 *(batch_idx + 1)/ LEN(data_loader),loss.data [ 0 ]))


DEF  试验(模型,历元,标准,data_loader) :
     model.eval()
    test_loss = 0
     正确= 0 
    为数据,目标在 data_loader中:
        if cuda_gpu:
            data,target = data.cuda(),target.cuda()
            model.cuda()
        数据,target =变量(数据),变量(目标)
        output = model(data)
        test_loss + = criterion(output,target).data [ 0 ] 
        pred = output.data.max(1)[ 1 ] #获取最大对数几率索引
         + = pred.eq(目标数据).cpu()。sum()

    test_loss / = len(data_loader)#失败函数已经在批量大小上取平均值
     acc = correct / len(data_loader.dataset)
    print'\ nTest set:Average loss:{:。 4f},准确度:{} / {}({:.0f }%)\ n'.format(
        test_loss,correct,len(data_loader.dataset),100. * acc))
    return(acc,test_loss)

复制代码
如今介绍完毕,让咱们开始此次数据科学之旅吧!

2.使用PyTorch进行数据分析

  • 使用torch.nn库构建模型
  • 使用torch.autograd库训练模型
  • 将数据封装进torch.utils.data.Dataset库
  • 使用NumPy接口链接你的模型,数据和你最喜欢的工具
在查看复杂模型以前,咱们先来看个简单的:简单合成数据集上的线性回归,咱们可使用sklearn工具生成这样的合成数据集。
从 sklearn.datasets 导入 make_regression 
进口 seaborn 做为 SNS 
进口熊猫做为 PD 
进口 matplotlib.pyplot 做为 PLT 

sns.set()

x_train,y_train,W_target = make_regression(N_SAMPLES次= 100,n_features = 1,噪声= 10,COEF = 真)

DF = pd.DataFrame(data = { 'X':x_train.ravel(),'Y':y_train.ravel()})

sns.lmplot(x = 'X',y = 'Y',data = df,fit_reg = True)
plt.show()

1)x_torch = torch.FloatTensor(x_train)
y_torch = torch.FloatTensor(y_train)
y_torch = y_torch.view(y_torch.size()[ 0 ],1)

复制代码
PyTorch的nn库中有大量有用的模块,其中一个就是线性模块。如名字所示,它对输入执行线性变换,即线性回归。
class  LinearRegression (torch.nn.Module):
    def  __init__ (self,input_size,output_size):
         super(LinearRegression,self).__ init __()
        self.linear = torch.nn.Linear(input_size,output_size)  

     def  forward (self,x ):
        返回 self.linear(x)的

模型=线性回归( 1, 1)

复制代码
要训练线性回归,咱们须要从nn库中添加合适的损失函数。对于线性回归,咱们将使用MSELoss() - 均方差损失函数。

咱们还须要使用优化函数(SGD),并运行与以前示例相似的反向传播。本质上,咱们重复上文定义的train()函数中的步骤。不能直接使用该函数的缘由是咱们实现它的目的是分类而不是回归,以及咱们使用交叉熵损失和最大元素的索引做为模型预测。而对于线性回归,咱们使用线性层的输出做为预测。git

准则= torch.nn.MSELoss()
优化= torch.optim.SGD(model.parameters(),LR = 0.1)   


对于历元在范围(50):
    数据,目标=变量(x_torch),变量(y_torch)
    输出= model(data)

    optimizer.zero_grad()
    loss = criterion(output,target)
    loss.backward()
    optimizer.step()

predict = model(Variable(x_torch))。data.numpy()

复制代码
如今咱们能够打印出原始数据和适合PyTorch的线性回归。
plt.plot(x_train,y_train,'o',label = '原始数据')
plt.plot(x_train,predicted,label = 'Fitted line')
plt.legend()
plt.show()

复制代码
为了转向更复杂的模型,咱们下载了MNIST数据集至「数据集」文件夹中,并测试了一些PyTorch中可用的初始预处理.PyTorch具有数据加载器和处理器,可用于不一样的数据集。下载好后,你能够随时使用。你还能够将数据包装进PyTorch张量,建立本身的数据加载器类别。
批大小(batch size)是机器学习中的术语,指一次迭代中使用的训练样本数量。批大小能够是如下三种之一:
  • batch模式:批大小等于整个数据集,所以迭代和epoch值一致;
  • mini-batch模式:批大小大于1但小于整个数据集的大小。一般,数量能够是能被整个数据集整除的值。
  • 随机模式:批大小等于1.所以梯度和神经网络参数在每一个样本以后都要更新。
从 torchvision 导入数据集,变换

batch_num_size = 64

 train_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data',train = True,download = True,transform = transforms.Compose([ 
        transforms.ToTensor()),
        转换。 Normalize((0.1307,),(0.3081,))
    ])),
    batch_size = batch_num_size,shuffle = True)

test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data',train = False,transform = transforms。撰写([
        transforms.ToTensor(),
        transforms.Normalize((0.1307,),(0.3081,))
    ])),
    batch_size = batch_num_size,shuffle = True)

复制代码

3. PyTorch中的LeNet卷积神经网络(CNN)

如今咱们从头开始建立第一个简单神经网络。该网络要执行图像分类,识别MNIST数据集中的手写数字。这是一个四层的卷积神经网络(CNN),一种分析MNIST数据集的常见架构。该代码来自PyTorch官方教程,你能够在这里(http://pytorch.org/tutorials/)找到更多示例。
咱们将使用torch.nn库中的多个模块:
1.线性层:使用层的权重对输入张量执行线性变换;
2. Conv1和Conv2:卷积层,每一个层输出在卷积核(小尺寸的权重张量)和一样尺寸输入区域之间的点积;
3. Relu:修正线性单元函数,使用逐元素的激活函数max(0,x);
池化层:使用max运算执行特定区域的下采样(一般2x2像素);
5. Dropout2D:随机将输入张量的全部通道设为零。当特征图具有强相关时,dropout2D提高特征图之间的独立性;
6. Softmax:将Log(Softmax(x))函数应用到n维输入张量,以使输出在0到1之间。
类 LeNet (nn.Module) :
    DEF  __init__ (个体):
         超级(LeNet,自我).__ INIT __()
        self.conv1 = nn.Conv2d( 1, 10,kernel_size = 5)
        self.conv2 = nn.Conv2d( 10, 20,kernel_size = 5)
        self.conv2_drop = nn.Dropout2d()
        self.fc1 = nn.Linear( 320, 50)
        self.fc2 = nn.Linear( 50, 10)

     DEF  向前(个体,X) :
         X = F .relu(F.max_pool2d(self.conv1(x)的2))
        X = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(X)),2))
        X = x.view(-1,320)
        X = F.relu(self.fc1(X ))
        x = F.dropout(x,training = self.training)
        x = self.fc2(x)
        return F.log_softmax(x,dim = 1)

复制代码
建立LeNet类后,建立对象并移至GPU:
model = LeNet()
if cuda_gpu:
    model.cuda()

print'MNIST_net model:\ n'print(model)

----------------------- -------------------------------------------------- 
MNIST_net模型:

LeNet(
  (CONV1):Conv2d(1,10,kernel_size =(5,5),跨度=(1,1))
  (CONV2):Conv2d(10,20,kernel_size =(5,5),步幅=(1,1))
  (conv2_drop):Dropout2d(p值=0.5)
  (fc1):线性(in_features = 320,out_features = 50,bias = 真)
  (fc2):线性(in_features = 50,out_features = 10,bias = 真)
)

复制代码
要训练该模型,咱们须要使用带动量的SGD,学习率为0.01,动量为0.5。
criteria = nn.CrossEntropyLoss()   
优化器= optim.SGD(model.parameters(),lr = 0.005,动量= 0.9)

复制代码
仅仅须要5个epoch(一个epoch意味着你使用整个训练数据集来更新训练模型的权重),咱们就能够训练出一个至关准确的LeNet模型。这段代码检查能够肯定文件中是否已有预训练好。的模型有则加载;无则训练一个并保存至磁盘。
import os epochs

 = 5 
if(os.path.isfile('pretrained / MNIST_net.t7')):
    print'Loading model')
    model.load_state_dict(torch.load('pretrained / MNIST_net.t7',map_location = lambda storage ,LOC:存储))
    ACC,损耗=试验(模型,1,标准,test_loader)
不然:
    打印('训练模式')
    用于历元在范围(1,历元+ 1:)
        系(模型,历元,标准,优化,train_loader)
        acc,loss = test(model,1,criterion,test_loader)
    torch.save(model.state_dict(),'pretrained / MNIST_net.t7')

------------------ -------------------------------------------------- ----- 
加载模型
试验组:平均损耗:0.0471,准确度:9859号文件 / 10000(99%)

复制代码
如今咱们来看下模型。首先,打印出该模型的信息。打印函数显示全部层(如Dropout被实现为一个单独的层)及其名称和参数。一样有一个迭代器在模型中全部已命名模块之间运行。当你具有一个包含多个「内部」模型的复杂时,此有所帮助。在全部已命名模块之间的迭代容许咱们建立模型解析器,可读取模型参数,建立与该网络相似的模块。
print'Internal models:'for idx,m in enumerate(model.named_modules()):
    print(idx,' - >',m)
    print'-------------- -------------------------------------------------- ---------”) #输出: 内部模型: 0 - >('',LeNet( (CONV1):Conv2d(1,10,kernel_size =(5,5),跨度=(1,1)) (CONV2):Conv2d(10,20,kernel_size =(5,5),跨度=(1,1)) (conv2_drop):Dropout2d(p值= 0.5) (FC1):线性(in_features = 320,out_features = 50,偏压= 真) (FC2):线性(in_features = 50,out_features = 10,bias = True) )) ----------------------------------------- -------------------------------- 1 - >('CONV1',Conv2d(1,10,kernel_size =(5,5),跨度=(1,1))) -------------------------------------------------- ----------------------- 2 - >('CONV2',Conv2d(10,20,kernel_size =(5,5),跨度=(1,1))) ---------------------------------------------- --------------------------- 3 - >('conv2_drop',Dropout2d(p = 0.5)) -------- -------------------------------------------------- --------------- 4 - >('fc1',Linear(in_features = 320,out_features = 50,bias = True)) -------------------------------------------------- ----------------------- 5 - >('fc2',Linear(in_features = 50,out_features = 10,bias = True)) ---- -------------------------------------------------- ------------------- 复制代码
你可使用.cpu()方法将张量移到CPU(或确保它在那里)。或者,当GPU可用时(torch.cuda。可用),使用.cuda()方法将张量移至GPU。你能够看到张量是否在GPU上,其类型为torch.cuda.FloatTensor。若是张量在CPU上,则其类型为torch.FloatTensor 。
printtype(t.cpu()。data))
if torch.cuda.is_available():
    print(“Cuda is available”)
    printtype(t.cuda()。data))
elseprint(“Cuda is不可用“)

---------------------------------------------- --------------------------- 
< 类 ' 炬。FloatTensor '> 
Cuda的 是 可用的
 < 类 ' 炬。cuda。FloatTensor '>

复制代码
若是张量在CPU上,咱们能够将其转换成NumPy数组,其共享一样的内存位置,改变其中一个就会改变另外一个。
若是 torch.cuda.is_available():
    尝试:
        打印(t.data.numpy()),
    除了 RuntimeError 为 e:
        “你不能将GPU张量转换为numpy nd数组,你必须将你的weight tendor复制到cpu而后获取numpy数组“

 printtype(t.cpu()。data.numpy()))
print(t.cpu()。data.numpy()。shape)
print(t.cpu()。data)。 numpy的())

复制代码
如今咱们了解了如何将张量转换成NumPy数组,咱们能够利用该知识使用matplotlib进行可视化!咱们来打印出第一卷积层的卷积滤波器。
data = model.conv1.weight.cpu()。data.numpy()
print(data.shape)
print(data [:, 0 ] .shape)

kernel_num = data.shape [ 0 ] 

fig,axes = plt.subplots( NCOLS = kernel_num,figsize =(2 * kernel_num,2))

为山口在范围(kernel_num):
    轴[COL] .imshow(数据[COL,0,:,:],CMAP = plt.cm.gray)
PLT。显示()

复制代码
以上是简要的教程资源,还有更多的内容和实验能够查看原项目了解更多。
相关文章
相关标签/搜索