首页 > 其他分享 >PyTorch与Serverless架构结合

PyTorch与Serverless架构结合

时间:2023-01-26 16:12:16浏览次数:35  
标签:Serverless loss 架构 nn self torch PyTorch size

PyTorch介绍

2017年1月,FAIR(Facebook AI Research)发布了PyTorch。其标志如下所示。PyTorch是在Torch基础上用Python语言重新打造的一款深度学习框架,Torch是用Lua语言打造的机器学习框架。但是Lua语言较为小众,导致Torch学习成本高,知名度不高。近几年来,PyTorch凭借其易用性、代码简洁灵活等特点逐渐有了超越TensorFlow的趋势。在学术界,PyTorch的地位已经超越TensorFlow,且PyTorch借助ONNX所带来的模型落地能力在工业界大放光彩。

PyTorch标志 

PyTorch如此流行与它的张量和动态计算图有关。和TensorFlow一样,PyTorch也有张量(Tensor)。而与TensorFlow不同的是,PyTorch中的张量是n维数组,类似于Numpy中的Ndarray。Numpy是Python中最主流的数据计算库之一。

PyTorch中的张量几乎是对Ndarray的扩展,且可以运行在GPU上,大大加快了运算速度。

PyTorch官网提供了非常方便的PyTorch框架安装指引,如下所示。

 

PyTorch框架安装指引 

只需要选择不同的PyTorch Build、OS,以及Language等信息,我们就可以生成对应的命令,在本地执行生成的命令就可以进行PyTorch的安装:

pip3 install torch torchvision torchaudio

PyTorch实践:图像分类系统

CIFAR-10是由Hinton的学生Alex Krizhevsky和Ilya Sutskever整理的一个用于识别普适物体的小型数据集。该数据集包含10个类别共60 000张图片,每张图片的大小为32×32,其中训练图像50 000张,测试图像10 000张。下图是一些示例。

CIFAR-10数据集示例 

本案例将基于CIFAR-10数据集快速入门PyTorch框架,并实现一个简单的图像分类系统。

1.开发前准备

在开始实现本案例之前,导入包括PyTorch等在内的依赖库:

import torch

import torch.nn as nn

import torch.nn.functional as F

import torchvision

import torchvision.transforms as transforms

通常在使用PyTorch的时候会用到两个依赖:

·torch是关于运算的包;

·torchvision则集成了常用数据集和经典的神经网络模型,比如ResNet。

在正式开始构建模型之前,准备好训练集和测试集,同时定义好数据预处理操作,这里仅将图像的RGB值归一化至0~1区间:

transform = transforms.Compose( [transforms.ToTensor(),  transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

cifar_train = torchvision.datasets.CIFAR10(root='./data', train=True,  download=True, transform=transform)

cifar_test = torchvision.datasets.CIFAR10(root='./data', train=False,   transform=transform)

PyTorch还提供了数据加载器DataLoader,以便在训练、测试过程中遍历数据集:

trainloader = torch.utils.data.DataLoader(cifar_train, batch_size=32, shuffle=True)

testloader = torch.utils.data.DataLoader(cifar_test, batch_size=32, shuffle=False)

在数据加载器Dataloader中,定义每一步训练使用32个样本,即这里的参数batch_size=32,并在训练时对训练数据集随机洗牌,对测试集不进行洗牌。这里定义一个简单的卷积神经网络模型:

class Net(nn.Module):    
    def __init__(self):        
        super(Net, self).__init__()        
        self.conv1 = nn.Conv2d(3, 6, 5)        
        self.pool = nn.MaxPool2d(2, 2)        
        self.conv2 = nn.Conv2d(6, 16, 5)        
        self.fc1 = nn.Linear(16 * 5 * 5, 120)        
        self.fc2 = nn.Linear(120, 84)        
        self.fc3 = nn.Linear(84, 10)    

    def forward(self, x):        
        x = self.pool(F.relu(self.conv1(x)))        
        x = self.pool(F.relu(self.conv2(x)))        
        x = x.view(-1, 16 * 5 * 5)        
        x = F.relu(self.fc1(x))        
        x = F.relu(self.fc2(x))        
        x = self.fc3(x)        
        return x net = Net()

它包含2个卷积层和3个全连接层,第一层卷积层接收大小为32×32的图像的数据,最后的全连接层产生10个类别的输出结果。

2.模型训练

在目标函数上,选择多分类交叉熵损失函数和随机梯度下降法(Stochastic Gradient Descent,SGD)作为优化器,学习率lr大小为0.001。

criterion = nn.CrossEntropyLoss()

optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

·SGD:梯度是一个矢量,它告诉模型如何改变权重,使损失变化最快。这个过程为梯度下降,因为它使用梯度使损失下降到最小值。随机使用某一批数据进行训练,那么这次训练就是随机的。这就是随机梯度下降法名字的由来。

·学习率:模型每一次梯度下降的跨步大小。其决定着目标函数能否收敛到局部最小值以及何时收敛到最小值。合适的学习率能够使目标函数在合适的时间内收敛到局部最小值。

之后循环遍历数据集,将得到的数据输入模型进行训练:

# 迭代次数为2次

nums_epoch = 2

for epoch in range(nums_epoch):    

# 初始化损失大小为0.0    

  _loss = 0.0    

# 从数据加载器中得到数据集和对应标签    

  for i, (inputs, labels) in enumerate(trainloader, 0):        

# 将数据和标签指定到对应设备,如CPU或GPU,GPU需指定到CUDA       

    inputs, labels = inputs.to(device), labels.to(device)        

# 清空已有的梯度        

optimizer.zero_grad()        

# 训练数据输入模型,做前向传播,得到模型输出        

outputs = net(inputs)        

# 通过模型输出和对应的标签计算损失函数        

loss = criterion(outputs, labels)        

# 梯度反向传播       

loss.backward()        

# 更新优化器参数        

optimizer.step()        

# 累计损失值并打印        

_loss += loss.item()        

# 每2000步打印一次损失值        

if i % 2000 == 1999:            

  print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, _loss / 2000))            

  _loss = 0.0

其中,nums_epoch表示迭代次数,inputs.to(device)和labels.to(device)都表示将数据转换到device指示的硬件设备上,device可以为CPU或者GPU设备。在获取模型的前向输出后计算损失函数的值,直接调用损失函数backward()完成后向传播,并用optimizer.step()更新优化器。下面是训练的日志:

[1, 2000] loss: 1.178

[1, 4000] loss: 1.200

[1, 6000] loss: 1.168

[1, 8000] loss: 1.175

[1, 10000] loss: 1.185

[1, 12000] loss: 1.165

[2, 2000] loss: 1.073

[2, 4000] loss: 1.066

[2, 6000] loss: 1.100

[2, 8000] loss: 1.107

[2, 10000] loss: 1.083

[2, 12000] loss: 1.103

3.模型评估

最后对模型进行评估:

correct, total = 0, 0

with torch.no_grad():    

  for images, labels in testloader:        

    outputs = net(images)        

    _, predicted = torch.max(outputs, 1)        

    total += labels.size(0)        

    correct += (labels == predicted).sum().item()

    print('Accuracy: %d %%' % (100 * correct / total))

输出结果为:

Accuracy: 58 %

与Serverless架构结合:对姓氏进行分类

1.本地开发

参考PyTorch官方案例NLP FROM SCRATCH: CLASSIFYING NAMES WITH A CHARAC-TER-LEVEL RNN,通过PyTorch框架构建并训练基本的字符级RNN来对单词进行分类。训练完成之后,通过Python Web框架将该项目与Flask框架进行结合,并服务化。

首先根据姓氏进行分类的示例代码,在本地进行代码的编写以及项目的基本测试:

f

from __future__ import unicode_literals, print_function, division 
from io import open 
import glob 
import unicodedata 
import string 
import torch 
import torch.nn as nn 
from torch.autograd import Variable 
from flask import Flask, request 
app = Flask(__name__) 
all_letters = string.ascii_letters + " .,;'" 
n_letters = len(all_letters) 
category_lines = {} 
all_categories = [] 
n_hidden = 128 
findFiles = lambda path: glob.glob(path) 
unicodeToAscii = lambda s: ''.join(c for c in unicodedata.normalize('NFD', s) 
if unicodedata.category(c) != 'Mn' and c in all_letters): 
    readLines = lambda filename: [unicodeToAscii(line) for line in open(filename, encoding='utf-8').read().strip().split()] 
    letterToIndex = lambda letter: all_letters.find(letter) 
    for filename in findFiles('data/names/*.txt'):    
        category = filename.split('/')[-1].split('.')[0]    
        all_categories.append(category)    
        lines = readLines(filename)    
        category_lines[category] = lines n_categories = len(all_categories) 

def letterToTensor(letter):    
    tensor = torch.zeros(1, n_letters)    
    tensor[0][letterToIndex(letter)] = 1    
    return tensor 

def lineToTensor(line):    
    tensor = torch.zeros(len(line), 1, n_letters)    
    for li, letter in enumerate(line):        
        tensor[li][0][letterToIndex(letter)] = 1    
        return tensor 

class RNN(nn.Module):    
    def __init__(self, input_size, hidden_size, output_size):        
        super(RNN, self).__init__()        
        self.hidden_size = hidden_size        
        self.i2h = nn.Linear(input_size + hidden_size, hidden_size)        
        self.i2o = nn.Linear(input_size + hidden_size, output_size)        
        self.softmax = nn.LogSoftmax(dim=1)    

def forward(self, input, hidden):        
    combined = torch.cat((input, hidden), 1)        
    hidden = self.i2h(combined)        
    output = self.i2o(combined)        
    return output, hidden    

def initHidden(self):        
    return Variable(torch.zeros(1, self.hidden_size)) 

rnn = RNN(n_letters, n_hidden, n_categories) 

def evaluate(line_tensor):    
    hidden = rnn.initHidden()    
    for i in range(line_tensor.size()[0]):        
        output, hidden = rnn(line_tensor[i], hidden)    
        return output 

def predict(input_line, n_predictions=3):    
    with torch.no_grad():        
        output = evaluate(lineToTensor(input_line))        
        topv, topi = output.topk(n_predictions, 1, True)        
        predictions = [[topv[0][i].item(), all_categories[topi[0][i].item()]] for i in range(n_predictions)]    
        return predictions 


@app.route('/invoke', methods=['POST']) 
def invoke():    
    return {'result': predict(request.get_data().decode("utf-8"))} 

if __name__ == '__main__':    
    app.run(debug=True, host='0.0.0.0', port=9000)

之后,通过Python命令启动该Bottle项目,并通过命令行工具进行相关的测试:

curl --location --request POST 'http://0.0.0.0:9000/invoke' \ --header 'Content-Type: text/plain' \ --data-raw 'bai'

输出的测试结果如下:

{ "result": [ [ 0.09027218818664551,  "Russian" ],  [ 0.07011377066373825,  "Chinese" ],   [ 0.053722310811281204,  "Portuguese" ] ] }

可以看到,当输入一个姓氏之后,系统已经可以按照预期进行相关返回,包括所属国家信息以及相关度信息。

2.部署到Serverless架构

目前,各大云厂商的FaaS平台均支持容器镜像的部署。所以,我们可以将项目打包成镜像,并通过Serverless Devs开发者工具部署到阿里云函数计算。

若通过Serverless Devs开发者工具构建镜像并部署到阿里云函数计算,我们需要准备Doc-kerfile文件与Serverless Devs的资源描述文件。其中,Dockerfile文件参考如下:

FROM python:3.7-slim

WORKDIR /usr/src/app

RUN pip install torch flask numpy

COPY . .

CMD [ "python", "-u", "/usr/src/app/index.py" ]

Serverless Devs的资源描述文件是对部署到线上的资源进行预描述,包括服务相关配置、函数相关配置以及触发器、自定义域名等相关的配置:

edition: 1.0.0 name: container-pytorch access: default vars:    region: cn-shanghai services:    pytorch-demo:        component: devsapp/fc        props:            region: ${vars.region}            service:                name: pytorch-service            function:                name: pytorch-function                timeout: 60                caPort: 9000                memorySize: 1536                runtime: custom-container                customContainerConfig:                    image: 'registry.cn-shanghai.aliyuncs.com/custom-container/pytorch-demo:0.0.1'

完成资源准备之后,通过Serverless Devs开发者工具中FC组件提供的build能力进行镜像的构建,例如执行s build--use-docker命令,即可看到预期的镜像构建效果,如下所示。

镜像构建效果示意图 

镜像构建完成之后,可以通过Serverless Devs开发者工具执行s deploy--push-registry acr-internet--use-local-y进行部署。这里主要包括以下几个动作。

·将构建完成的镜像推送到阿里云镜像服务。

·基于函数计算创建服务。

·基于函数计算创建函数,并指定代码源为指定的容器镜像。

·进行触发器和自定义域名的创建。

部署完成后,可以看到系统返回的测试地址,如下所示。

应用创建示意图 

此时,可以通过该测试地址,利用curl命令行测试工具进行测试:

curl --location --request POST 'http://pytorch-function.pytorch-service.1583208943291465. cn-shanghai.fc.devsapp.net/invoke'\ --header 'Content-Type: text/plain' \ --data-raw 'bai'

之后,可以看到接口已经返回预测结果:

{ "result": [ [  0.1394740492105484,  "Arabic"  ],   [  0.06561967730522156,  "Dutch" ],     [   0.04731455445289612,   "Portuguese" ]  ] }

至此,通过PyTorch完成了一个简单的文本分类功能,并通过部署到Serverless架构,暴露可以对外提供服务的API。

3.项目优化

Serverless架构的发展非常迅速,面临的挑战也有目共睹。尽管本实例采用了更为传统和简单的容器镜像部署方案,即将应用部署到阿里云Serverless平台,但是由于目前Server-less架构发展受限制,仍然存在诸多不足。

·基于自定义镜像的函数计算项目虽然更容易部署和迁移,但是冷启动问题非常严峻。至少目前来看,相对原生的运行时,容器镜像的冷启动问题要严峻不少。若想缓解镜像部署带来的冷启动问题,我们可以考虑使用镜像加速、预留实例等技术。

·PyTorch可以基于GPU实现预测,而且GPU被广泛应用到各行业的人工智能项目中。但是就目前来看,大部分厂商的Serverless架构还不支持GPU实例,所以在Serverless架构下如何使用GPU,以及是否能使用GPU将成为人工智能项目部署到Serverless架构的关键参考指标。目前,阿里云函数计算已经支持GPU实例。在本项目部署过程中,可以考虑GPU实例的技术选型,以提升预测性能。

 

标签:Serverless,loss,架构,nn,self,torch,PyTorch,size
From: https://www.cnblogs.com/muzinan110/p/17067877.html

相关文章

  • TensorFlow与Serverless架构结合
    4.2.1TensorFlow介绍TensorFlow是一个基于数据流编程(DataFlowProgramming)的符号数学系统,被广泛应用于各类机器学习算法的编程实现。其前身是谷歌的神经网络算法库Dist......
  • scikit-learn与Serverless架构结合
    1scikit-learn介绍scikit-learn是一个面向Python的第三方提供的非常强力的机器学习库,简称sklearn,标志如下所示。它建立在NumPy、SciPy和Matplotlib上,包含从数据预处理到......
  • Serverless架构下的AI应用
    近年来,Serverless架构逐渐被更多的开发者所认识、接受,逐渐被应用到了更多领域,其中包括如今非常热门的机器学习领域。与其他领域不同的是,在Serverless架构上进行人工智能相......
  • Serverless架构下的应用开发流程
    UCBerkeley认为Serverless架构的出现过程类似于40多年前从汇编语言转向高级语言的过程,在未来Serverless架构的使用会飙升,或许服务器式云计算不会消失,但是将促进BaaS发展,以......
  • Pytorch LSTM实现中文单词预测(附完整训练代码)
    PytorchLSTM实现中文单词预测(附完整训练代码)目录​​Pytorch LSTM实现中文单词预测(词语预测附完整训练代码)​​​​1、项目介绍​​​​2、中文单词预测方法(N-Gram模......
  • conda 安装pytorch with cuda 失败问题(2023.1.8)
    文章目录​​conda安装pytorchwithcuda失败问题​​​​使用pip安装​​​​使用conda安装pytorchwithcuda​​​​安装完cuda依然无法调用:错误的版本搭配​​​​正......
  • Serverless面临的挑战
    在Serverless架构为使用者提供全新的编程范式的同时,当用户在享受Serverless带来的第一波技术红利的时候,Serverless的缺点也逐渐地暴露了出来,例如函数的冷启动问题,就是如今......
  • Serverless架构下用Python轻松实现图像分类和预测
    Serverless架构下用Python轻松实现图像分类和预测图像分类是人工智能领域的一个热门话题。通俗解释就是,图像分类是一种根据各自在图像信息中所反映的不同特征,把不同类别的......
  • Serverless 触发器和函数赋能自动化运维
    Serverless架构在运维层面有着得天独厚的优势,不仅因为其事件触发可以有针对性地获取、响应一些事件,还因为其轻量化、低运维的特性让很多运维开发者甚是喜爱。在实际生产中......
  • Serverless与监控告警、自动化运维
    通过Serverless架构实现监控告警功能在实际生产中,经常需要编写一些监控脚本来监控网站服务或API服务的健康状况,包括是否可用、响应速度是否足够快等。传统的方法是直接使......