FlaskとHerokuを使用したPyTorchモデルの導入

最初のディープラーニングアプリを作成してデプロイします。このPyTorchチュートリアルでは、FlaskとHerokuを使用してPyTorchモデルをデプロイする方法を学習します。結果をjsonデータとして返すRESTAPIを使用して単純なFlaskアプリを作成し、それをHerokuにデプロイします。PytTorchアプリの例として、数字の分類を行い、最後に独自の数字を描画して、ライブ実行アプリで予測することができます。

設定

仮想環境でプロジェクトを作成します(Windowsではコマンドが若干異なる場合があります)。

$ mkdir pytorch-deploy
$ cd pytorch-deploy
$ python3 -m venv venv

それをアクティブにします

$ . venv/bin/activate

またはWindowsの場合

venv\Scripts\activate

FlaskとPyTorchをインストールします

$ pip install Flask
$ pip install torch torchvision

アプリを作成する

新しいディレクトリアプリを作成し、このmain.pyファイル内に以下を挿入します。

from flask import Flask, jsonify
app = Flask(__name__)

@app.route('/predict', methods=['POST'])
def predict():
    if request.method == 'POST':
        return jsonify({'test': 'test_result'})

必要なエンドポイントは1つだけです@app.route('/predict')。ここでは、画像を受け取り、PyTorchモデルで予測して、結果をjsonデータとして返します。今のところ、Flaskのjsonifyメソッドでダミーのjsonデータのみを返します。

アプリを実行してテストします

走る

$ export FLASK_APP=app/main.py
$ export FLASK_ENV=development
$ flask run

テストフォルダーtestを作成し、test.pyファイル内に挿入します。

import requests 

resp = requests.post("http://localhost:5000/predict")

print(resp.text)

2番目のターミナルでtest.pyファイルを実行します(リクエストをインストールする必要がある場合があります:)pip install requests。すべてが正しく機能している場合、これによりダミーのjsonデータが出力されます{'test': 'test_result'}

モデルをトレーニングして保存します。

ここで私のPyTorchコースからコードを取得します。これは、MNISTデータセットでトレーニングされ、数字の分類に使用される単純なフィードフォワードニューラルネットです。私たちが行う唯一の変更は、別のデータセット変換を追加することです。これは、アプリでも同じ変換が必要であることを示したいためです。したがって、最初にこの変換を使用して、データセットに適用します。

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

# MNIST dataset 
train_dataset = torchvision.datasets.MNIST(root='./data', 
                                           train=True, 
                                           transform=transform,  
                                           download=True)

test_dataset = torchvision.datasets.MNIST(root='./data', 
                                          train=False, 
                                          transform=transform)

これは、MNISTデータセットのグローバル平均と標準偏差による正規化にすぎません。

ファイルの最後に、この行を挿入して、トレーニング後にモデルを保存します。

torch.save(model.state_dict(), "mnist_ffn.pth")

次に、このファイルを実行します。これにより、モデルがトレーニングされ、高精度で印刷され、指定されたファイルにモデルが保存されます。このファイルを取得して、アプリフォルダーにコピーします。

PyTorchユーティリティ関数を作成する

appフォルダー内に新しいファイルtorch_utils.pyを作成します。ここでは、次の3つのことを行います。

  • モデルをロードします
  • 画像を前処理し、トーチテンソルに変換します
  • 予測を行う

モデルをロードします

保存と読み込みについては、こちらのPyTorchコースもご覧ください。元のファイルと同じモデルを作成し、状態ディクショナリをロードして、evalモードに設定します。ここではCPUバージョンのみを使用します。それ以外の場合、パッケージはHerokuには大きすぎます。したがって、GPUでトレーニングした場合は、正しくロードするようにしてください。このコードは、保存/読み込みのチュートリアルにもあります。.to(device)コードからすべての呼び出しを削除できます。心配はいりません。CPUは、このアプリケーションでモデルを推論するのに十分な速度で動作します。

import io
import torch 
import torch.nn as nn 
import torchvision.transforms as transforms 
from PIL import Image


class NeuralNet(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(NeuralNet, self).__init__()
        self.input_size = input_size
        self.l1 = nn.Linear(input_size, hidden_size) 
        self.relu = nn.ReLU()
        self.l2 = nn.Linear(hidden_size, num_classes)  
    
    def forward(self, x):
        out = self.l1(x)
        out = self.relu(out)
        out = self.l2(out)
        # no activation and no softmax at the end
        return out

input_size = 784 # 28x28
hidden_size = 500 
num_classes = 10
model = NeuralNet(input_size, hidden_size, num_classes)

PATH = "mnist_ffn.pth"
model.load_state_dict(torch.load(PATH))
model.eval()

画像を読み込んで変換する

ここでは、テンソルがMNISTデータセットと同じプロパティを持っていることを確認します。したがって、元のファイルと同じ変換を適用します。さらに、画像のサイズを(28,28)に変更し、グレースケール画像に変換します。この関数をtorch_utils.pyファイルに追加します。

def transform_image(image_bytes):
    transform = transforms.Compose([transforms.Grayscale(num_output_channels=1),
                                    transforms.Resize((28,28)),
                                    transforms.ToTensor(),
                                    transforms.Normalize((0.1307,),(0.3081,))])

    image = Image.open(io.BytesIO(image_bytes))
    return transform(image).unsqueeze(0)

予測

ここで、元のファイルと同じコードを使用して画像を予測し、新しいヘルパー関数で予測を返します。

def get_prediction(image_tensor):
    images = image_tensor.reshape(-1, 28*28)
    outputs = model(images)
        # max returns (value ,index)
    _, predicted = torch.max(outputs.data, 1)
    return predicted

すべてをまとめる

main.pyファイル、これらのヘルパー関数をインポートして、一緒にすべてをかけるpredict方法。さらに、いくつかのエラーチェックも含まれており、特定のファイルのみを許可します。

from flask import Flask, request, jsonify

from torch_utils import transform_image, get_prediction

app = Flask(__name__)

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
def allowed_file(filename):
    # xxx.png
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/predict', methods=['POST'])
def predict():
    if request.method == 'POST':
        file = request.files.get('file')
        if file is None or file.filename == "":
            return jsonify({'error': 'no file'})
        if not allowed_file(file.filename):
            return jsonify({'error': 'format not supported'})

        try:
            img_bytes = file.read()
            tensor = transform_image(img_bytes)
            prediction = get_prediction(tensor)
            data = {'prediction': prediction.item(), 'class_name': str(prediction.item())}
            return jsonify(data)
        except:
            return jsonify({'error': 'error during prediction'})

このデータセットpredictionclass_nameは、とは同じであることに注意してください。通常、ここでインデックスから実際のクラス名へのマッピングを行う必要があります。

モデルをテストする

次に、いくつかのサンプル画像を取得するか、簡単なペイントプログラムを使用して独自の画像を描画します。私の場合、Macでペイントブラシを使用し、サイズ100x100の新しい画像を作成し、背景を黒で塗りつぶし、数字を白で描画しました。pngまたはjpgで保存し、ファイルをテストフォルダーにコピーします。次に、この画像を投稿リクエストに含めます。たとえば、eight.pngという画像を使用します。

import requests 

resp = requests.post("http://localhost:5000/predict", files={'file': open('eight.png', 'rb')})

print(resp.text)

これは印刷する必要があります{'prediction': 8, 'class_name': 8}。おめでとう!これで、実行中のPyTorchWebアプリができました。最後のステップとして、Herokuにデプロイします

Herokuにデプロイする

本番環境では、適切なWebサーバーが必要なので、gunicornをインストールします

$ pip install gunicorn

以下のすべてのファイルをベースディレクトリに追加する必要があります。まず、wsgi.pyファイルを作成し、この行を挿入します

from app.main import app

Procfileを作成し、これを挿入します。

web: gunicorn wsgi:app

パス名を変更して、アプリパッケージをベースにします。

# in the main.py file:
from app.torch_utils import get_prediction, transform_image

# in the torch_utils.py file
PATH = "app/mnist_ffn.pth"

runtime.txtを作成し、使用しているPythonバージョンを挿入します。

python-3.8.3

パッケージのルートフォルダに再度いることを確認してください。次に、以下を使用してすべてのパッケージをrequirements.txtファイルに追加しますpip freeze

$ pip freeze > requirements.txt

CPUバージョンしか使用できないため、このようにファイルを変更して、PyTorchのCPUのみのバージョンを使用します。CPUのみのバージョンのコマンドは、こちらのPyTorchインストールガイドから取得できます。Linux、pip、およびCUDAなしを選択します。ダウンロードコマンドは、requirements.txtファイルの最初の行として追加できます。

-f https://download.pytorch.org/whl/torch_stable.html
torch==1.6.0+cpu
torchvision==0.7.0+cpu

.gitignoreを追加します。このバージョンのPythonは、GitHubから入手できます。また、テストフォルダーを追加して、ファイルの最初の行に次の行が含まれるようにします。

test/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
...

Herokuアプリを作成します。このためには、HerokuCLIをインストールする必要があります。ここで入手できます。ログインして、目的の名前で新しいアプリを作成します。

$ heroku login -i
$ heroku create your-app-name

アプリをローカルでテストします。

$ heroku local

次に、gitリポジトリを追加し、すべてのファイルをコミットして、Herokuにプッシュします。

git init
heroku git:remote -a your-app-name
git add .
git commit -m "initial commit"
git push heroku master

これにより、アプリがHerokuにデプロイされ、ライブで実行されているアプリへのリンクが表示されます。次に、このURLをtest.pyファイルで次のように使用しましょう。

import requests

resp = requests.post("https://your-app-name.herokuapp.com/predict",
                     files={"file": open('eight.png','rb')})

print(resp.text)

おめでとう!これで、数字の分類を実行できるPyTorchモデルを備えたライブ実行アプリができました。無料利用枠のみを使用する場合、Herokuは最初にアプリをウェイクアップする必要があるため、最初にリクエストを送信するときは数秒かかる場合があることに注意してください。

このチュートリアルを楽しんでいただけたでしょうか。

リンク: https://www.python-engineer.com/posts/pytorch-model-deployment-with-flask/

#pytorch #flask #heroku 

What is GEEK

Buddha Community

FlaskとHerokuを使用したPyTorchモデルの導入

FlaskとHerokuを使用したPyTorchモデルの導入

最初のディープラーニングアプリを作成してデプロイします。このPyTorchチュートリアルでは、FlaskとHerokuを使用してPyTorchモデルをデプロイする方法を学習します。結果をjsonデータとして返すRESTAPIを使用して単純なFlaskアプリを作成し、それをHerokuにデプロイします。PytTorchアプリの例として、数字の分類を行い、最後に独自の数字を描画して、ライブ実行アプリで予測することができます。

設定

仮想環境でプロジェクトを作成します(Windowsではコマンドが若干異なる場合があります)。

$ mkdir pytorch-deploy
$ cd pytorch-deploy
$ python3 -m venv venv

それをアクティブにします

$ . venv/bin/activate

またはWindowsの場合

venv\Scripts\activate

FlaskとPyTorchをインストールします

$ pip install Flask
$ pip install torch torchvision

アプリを作成する

新しいディレクトリアプリを作成し、このmain.pyファイル内に以下を挿入します。

from flask import Flask, jsonify
app = Flask(__name__)

@app.route('/predict', methods=['POST'])
def predict():
    if request.method == 'POST':
        return jsonify({'test': 'test_result'})

必要なエンドポイントは1つだけです@app.route('/predict')。ここでは、画像を受け取り、PyTorchモデルで予測して、結果をjsonデータとして返します。今のところ、Flaskのjsonifyメソッドでダミーのjsonデータのみを返します。

アプリを実行してテストします

走る

$ export FLASK_APP=app/main.py
$ export FLASK_ENV=development
$ flask run

テストフォルダーtestを作成し、test.pyファイル内に挿入します。

import requests 

resp = requests.post("http://localhost:5000/predict")

print(resp.text)

2番目のターミナルでtest.pyファイルを実行します(リクエストをインストールする必要がある場合があります:)pip install requests。すべてが正しく機能している場合、これによりダミーのjsonデータが出力されます{'test': 'test_result'}

モデルをトレーニングして保存します。

ここで私のPyTorchコースからコードを取得します。これは、MNISTデータセットでトレーニングされ、数字の分類に使用される単純なフィードフォワードニューラルネットです。私たちが行う唯一の変更は、別のデータセット変換を追加することです。これは、アプリでも同じ変換が必要であることを示したいためです。したがって、最初にこの変換を使用して、データセットに適用します。

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

# MNIST dataset 
train_dataset = torchvision.datasets.MNIST(root='./data', 
                                           train=True, 
                                           transform=transform,  
                                           download=True)

test_dataset = torchvision.datasets.MNIST(root='./data', 
                                          train=False, 
                                          transform=transform)

これは、MNISTデータセットのグローバル平均と標準偏差による正規化にすぎません。

ファイルの最後に、この行を挿入して、トレーニング後にモデルを保存します。

torch.save(model.state_dict(), "mnist_ffn.pth")

次に、このファイルを実行します。これにより、モデルがトレーニングされ、高精度で印刷され、指定されたファイルにモデルが保存されます。このファイルを取得して、アプリフォルダーにコピーします。

PyTorchユーティリティ関数を作成する

appフォルダー内に新しいファイルtorch_utils.pyを作成します。ここでは、次の3つのことを行います。

  • モデルをロードします
  • 画像を前処理し、トーチテンソルに変換します
  • 予測を行う

モデルをロードします

保存と読み込みについては、こちらのPyTorchコースもご覧ください。元のファイルと同じモデルを作成し、状態ディクショナリをロードして、evalモードに設定します。ここではCPUバージョンのみを使用します。それ以外の場合、パッケージはHerokuには大きすぎます。したがって、GPUでトレーニングした場合は、正しくロードするようにしてください。このコードは、保存/読み込みのチュートリアルにもあります。.to(device)コードからすべての呼び出しを削除できます。心配はいりません。CPUは、このアプリケーションでモデルを推論するのに十分な速度で動作します。

import io
import torch 
import torch.nn as nn 
import torchvision.transforms as transforms 
from PIL import Image


class NeuralNet(nn.Module):
    def __init__(self, input_size, hidden_size, num_classes):
        super(NeuralNet, self).__init__()
        self.input_size = input_size
        self.l1 = nn.Linear(input_size, hidden_size) 
        self.relu = nn.ReLU()
        self.l2 = nn.Linear(hidden_size, num_classes)  
    
    def forward(self, x):
        out = self.l1(x)
        out = self.relu(out)
        out = self.l2(out)
        # no activation and no softmax at the end
        return out

input_size = 784 # 28x28
hidden_size = 500 
num_classes = 10
model = NeuralNet(input_size, hidden_size, num_classes)

PATH = "mnist_ffn.pth"
model.load_state_dict(torch.load(PATH))
model.eval()

画像を読み込んで変換する

ここでは、テンソルがMNISTデータセットと同じプロパティを持っていることを確認します。したがって、元のファイルと同じ変換を適用します。さらに、画像のサイズを(28,28)に変更し、グレースケール画像に変換します。この関数をtorch_utils.pyファイルに追加します。

def transform_image(image_bytes):
    transform = transforms.Compose([transforms.Grayscale(num_output_channels=1),
                                    transforms.Resize((28,28)),
                                    transforms.ToTensor(),
                                    transforms.Normalize((0.1307,),(0.3081,))])

    image = Image.open(io.BytesIO(image_bytes))
    return transform(image).unsqueeze(0)

予測

ここで、元のファイルと同じコードを使用して画像を予測し、新しいヘルパー関数で予測を返します。

def get_prediction(image_tensor):
    images = image_tensor.reshape(-1, 28*28)
    outputs = model(images)
        # max returns (value ,index)
    _, predicted = torch.max(outputs.data, 1)
    return predicted

すべてをまとめる

main.pyファイル、これらのヘルパー関数をインポートして、一緒にすべてをかけるpredict方法。さらに、いくつかのエラーチェックも含まれており、特定のファイルのみを許可します。

from flask import Flask, request, jsonify

from torch_utils import transform_image, get_prediction

app = Flask(__name__)

ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
def allowed_file(filename):
    # xxx.png
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


@app.route('/predict', methods=['POST'])
def predict():
    if request.method == 'POST':
        file = request.files.get('file')
        if file is None or file.filename == "":
            return jsonify({'error': 'no file'})
        if not allowed_file(file.filename):
            return jsonify({'error': 'format not supported'})

        try:
            img_bytes = file.read()
            tensor = transform_image(img_bytes)
            prediction = get_prediction(tensor)
            data = {'prediction': prediction.item(), 'class_name': str(prediction.item())}
            return jsonify(data)
        except:
            return jsonify({'error': 'error during prediction'})

このデータセットpredictionclass_nameは、とは同じであることに注意してください。通常、ここでインデックスから実際のクラス名へのマッピングを行う必要があります。

モデルをテストする

次に、いくつかのサンプル画像を取得するか、簡単なペイントプログラムを使用して独自の画像を描画します。私の場合、Macでペイントブラシを使用し、サイズ100x100の新しい画像を作成し、背景を黒で塗りつぶし、数字を白で描画しました。pngまたはjpgで保存し、ファイルをテストフォルダーにコピーします。次に、この画像を投稿リクエストに含めます。たとえば、eight.pngという画像を使用します。

import requests 

resp = requests.post("http://localhost:5000/predict", files={'file': open('eight.png', 'rb')})

print(resp.text)

これは印刷する必要があります{'prediction': 8, 'class_name': 8}。おめでとう!これで、実行中のPyTorchWebアプリができました。最後のステップとして、Herokuにデプロイします

Herokuにデプロイする

本番環境では、適切なWebサーバーが必要なので、gunicornをインストールします

$ pip install gunicorn

以下のすべてのファイルをベースディレクトリに追加する必要があります。まず、wsgi.pyファイルを作成し、この行を挿入します

from app.main import app

Procfileを作成し、これを挿入します。

web: gunicorn wsgi:app

パス名を変更して、アプリパッケージをベースにします。

# in the main.py file:
from app.torch_utils import get_prediction, transform_image

# in the torch_utils.py file
PATH = "app/mnist_ffn.pth"

runtime.txtを作成し、使用しているPythonバージョンを挿入します。

python-3.8.3

パッケージのルートフォルダに再度いることを確認してください。次に、以下を使用してすべてのパッケージをrequirements.txtファイルに追加しますpip freeze

$ pip freeze > requirements.txt

CPUバージョンしか使用できないため、このようにファイルを変更して、PyTorchのCPUのみのバージョンを使用します。CPUのみのバージョンのコマンドは、こちらのPyTorchインストールガイドから取得できます。Linux、pip、およびCUDAなしを選択します。ダウンロードコマンドは、requirements.txtファイルの最初の行として追加できます。

-f https://download.pytorch.org/whl/torch_stable.html
torch==1.6.0+cpu
torchvision==0.7.0+cpu

.gitignoreを追加します。このバージョンのPythonは、GitHubから入手できます。また、テストフォルダーを追加して、ファイルの最初の行に次の行が含まれるようにします。

test/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
...

Herokuアプリを作成します。このためには、HerokuCLIをインストールする必要があります。ここで入手できます。ログインして、目的の名前で新しいアプリを作成します。

$ heroku login -i
$ heroku create your-app-name

アプリをローカルでテストします。

$ heroku local

次に、gitリポジトリを追加し、すべてのファイルをコミットして、Herokuにプッシュします。

git init
heroku git:remote -a your-app-name
git add .
git commit -m "initial commit"
git push heroku master

これにより、アプリがHerokuにデプロイされ、ライブで実行されているアプリへのリンクが表示されます。次に、このURLをtest.pyファイルで次のように使用しましょう。

import requests

resp = requests.post("https://your-app-name.herokuapp.com/predict",
                     files={"file": open('eight.png','rb')})

print(resp.text)

おめでとう!これで、数字の分類を実行できるPyTorchモデルを備えたライブ実行アプリができました。無料利用枠のみを使用する場合、Herokuは最初にアプリをウェイクアップする必要があるため、最初にリクエストを送信するときは数秒かかる場合があることに注意してください。

このチュートリアルを楽しんでいただけたでしょうか。

リンク: https://www.python-engineer.com/posts/pytorch-model-deployment-with-flask/

#pytorch #flask #heroku