Dieser Artikel ist [hier](https://aiotadiary.wp.xdomain.jp/2020/03/01/google-cloud-platform%E3%82%92%E4%BD%BF%E3%81%A3% E3% 81% A6% E8% B6% 85% E8% A7% A3% E5% 83% 8F% E5% BA% A6% E5% 8C% 96api% E3% 82% 92% E5% 85% AC% E9% 96% 8B% E3% 81% 97% E3% 81% A6% E3% 81% BF% E3% 82% 8B /) Dies ist eine Neufassung des Blog-Artikels für Qiita. Bitte schauen Sie sich auch diesen an. Dieses Mal möchte ich die API mithilfe der Google Cloud Platform veröffentlichen, die allgemein als GCP bekannt ist. Ich habe GCP vor einiger Zeit verwendet, daher möchte ich diesmal daran denken und die Erfahrung machen, den Dienst selbst bereitzustellen. Die API ist übrigens ein Super-Resolution-Modell, das das zuvor implementierte ESPCN verwendet. Was ich dieses Mal ansprechen möchte, ist eine Funktion namens Cloud Run in GCP.
Um sich für GCP zu registrieren, können Sie normalerweise auf den Google-Leitfaden oder andere Artikel verweisen. Es ist kein Problem, wenn Sie der Startanleitung folgen. Der folgende Artikel wird hilfreich sein. GCP (GCE), um von nun an zu beginnen, verwenden Sie den freien Frame sicher Grundsätzlich registrieren Sie sich in der folgenden Reihenfolge.
Die Registrierung ist jetzt abgeschlossen. Mit der kostenlosen GCP-Stufe können Sie kostenlos eine GCE-Instanz von f1-micro erstellen. Verwenden Sie sie daher bitte. Übrigens können Sie in der kostenlosen Testversion den Service im Wert von 300 $ für 12 Monate kostenlos nutzen, sodass Sie möglicherweise verschiedene Dinge ausprobieren möchten.
Cloud Run ist ein Dienst, mit dem Sie Docker-Container bereitstellen können, und der den Dienst im Wesentlichen mithilfe des in die Container-Registrierung hochgeladenen Docker-Images bereitstellt. Derzeit scheint es eine Möglichkeit zu geben, den Container zu erstellen und bereitzustellen, der durch das Ziehen nach Github ausgelöst wird. Diesmal scheint dies jedoch etwas schwierig zu sein. Daher möchte ich diesmal das lokal erstellte Image hochladen.
Da ich einen Mac verwende, ist die folgende Erklärung für niemanden außer Mac hilfreich. Laden Sie zunächst das folgende Archiv herunter.
google-cloud-sdk-245.0.0-darwin-x86_64.tar.gz
Führen Sie dann den folgenden Befehl aus.
$ ./google-cloud-sdk/install.sh
Initialisieren Sie als Nächstes das SDK.
$ gcloud init
Möchten Sie sich danach anmelden? Es wird eine Meldung angezeigt, die besagt, dass Sie Y eingeben müssen. Wählen Sie danach das Projekt aus, zu dem eine Verbindung hergestellt werden soll. Stellen Sie jedoch sicher, dass nur ein Projekt ausgewählt wird, wenn es vorhanden ist. Danach können Sie den Befehl verwenden, wenn Sie ihn entsprechend eingeben. ↓ ist die offizielle Einrichtungsmethode.
Schnellstart für macOS Schnellstart für Windows Schnellstart für Linux
Hier werde ich das zuletzt implementierte ESPCN-Modell verwenden und den Code verwenden, der die Auflösung verdoppelt und als API ausgibt. Ich benutze Flask und Gunicorn.
Als Ablauf der gesamten Operation
So einfach ist das. Erstellen Sie zunächst ein Git-Repository für die Espcn-API (Klicken Sie hier, um das Repository aufzurufen). Die Verzeichnisstruktur sieht wie folgt aus.
ESPCN-API
├model
│ └pre_trained_mode.pth
├api.py
├requirements.txt
├Dockerfile
└networks.py
test
├test.py
└test.png
Beginnen wir mit dem Testcode. Im Test müssen zwei Dinge überprüft werden.
・ Eine Antwort wird zurückgegeben (status_code ist 200) ・ Die Größe des Bildes wurde verdoppelt Wir werden die Operation implementieren, um diese zu bestätigen.
import sys
import requests
import json
from io import BytesIO
from PIL import Image
import base64
def image_file_to_base64(file_path):
with open(file_path, "rb") as image_file:
data = base64.b64encode(image_file.read())
return 'data:image/png;base64,' + data.decode('utf-8')
def base64_to_img(img_formdata):
img_base64 = img_formdata.split(',')[1]
input_bin = base64.b64decode(img_base64)
with BytesIO(input_bin) as b:
img = Image.open(b).copy().convert("RGB")
return img
if __name__ == "__main__":
source_image_path = "test.png "
source_image = Image.open(source_image_path)
source_width, source_height = source_image.size
print("source_width :", source_width)
print("source_height :", source_height)
host_url = "http://0.0.0.0:8000"
data = {"srcImage":image_file_to_base64(source_image_path)}
json_data = json.dumps(data)
response = requests.post(host_url, json_data, headers={'Content-Type': 'application/json'})
assert response.status_code == 200, "validation error status code should be 200"
res_json = response.json()
res_image = base64_to_img(res_json["sresoImage"])
sreso_width, sreso_height = res_image.size
print("sreso_width :", sreso_width)
print("sreso_height :", sreso_height)
assert sreso_width == source_width * 2 and sreso_height == source_height * 2 , \
"validation error image size should be 2 times of input image"
res_image.show()
print("OK")
Als nächstes werden wir die Haupt-api.py implementieren. Zunächst haben wir das Grundgerüst der gesamten Implementierung erstellt. Der Inhalt der Funktion ist überhaupt nicht implementiert.
from flask import Flask, request, jsonify
from networks import Espcn
import os
from io import BytesIO
import base64
from torchvision import transforms
from torch import load
import torch
import json
from PIL import Image
device = "cpu"
net = Espcn(upscale=2)
net.load_state_dict(torch.load(opt.model_path, map_location="cpu"))
net.to(device)
net.eval()
def b64_to_PILImage(b64_string):
"""
process convert base64 string to PIL Image
input: b64_string: base64 string : data:image/png;base64,{base64 string}
output: pil_img: PIL Image
"""
pass
def PILImage_to_b64(pil_img):
"""
process convert PIL Image to base64
input: pil_img: PIL Image
output: b64_string: base64 string : data:image/png;base64,{base64 string}
"""
pass
def expand(src_image, model=net, device=device):
pass
@app.route("/", methods=["POST"])
def superResolution():
pass
if __name__ == "__main__":
app.run(host="0.0.0.0", port=os.environ.get("PORT", 8000))
Ich denke an eine Struktur, in der die Erweiterungsfunktion die Haupt-Superauflösung ausführt und sie in der SuperResolution ausführt, die für die Anforderung ausgeführt wird. Durch die Definition des Modells, das für die Superauflösung außerhalb der Funktion verwendet wird, ist es meines Erachtens möglich, die Probleme beim Laden des Modells zu vermeiden, wenn der Worker mehrere Anforderungen verarbeitet.
Implementieren Sie also zunächst zwei ~ to ~ -Funktionen, die eingegeben und ausgegeben werden.
def b64_to_PILImage(b64_string):
"""
process convert base64 string to PIL Image
input: b64_string: base64 string : data:image/png;base64,{base64 string}
output: pil_img: PIL Image
"""
b64_split = b64_string.split(",")[1]
b64_bin = base64.b64decode(b64_split)
with BytesIO(b64_bin) as b:
pil_img = Image.open(b).copy().convert('RGB')
return pil_img
def PILImage_to_b64(pil_img):
"""
process convert PIL Image to base64
input: pil_img: PIL Image
output: b64_string: base64 string : data:image/png;base64,{base64 string}
"""
with BytesIO() as b:
pil_img.save(b, format='png')
b.seek(0)
img_base64 = base64.b64encode(b.read()).decode('utf-8')
img_base64 = 'data:image/png;base64,' + img_base64
return img_base64
Ich hatte Schwierigkeiten mit BytesIO, weil es unerwartet schwierig war. Als nächstes folgen die Erweiterungsfunktion und die Hauptfunktion superResolution.
def tensor_to_pil(src_tensor):
src_tensor = src_tensor.mul(255)
src_tensor = src_tensor.add_(0.5)
src_tensor = src_tensor.clamp_(0, 255)
src_tensor = src_tensor.permute(1, 2, 0)
src_tensor = src_tensor.to("cpu", torch.uint8).numpy()
return Image.fromarray(src_tensor)
def expand(src_image, model=net, device=device):
src_tensor = transforms.ToTensor()(src_image).to(device)
if src_tensor.dim() == 3:
src_tensor = src_tensor.unsqueeze(0)
srezo_tensor = model(src_tensor).squeeze()
srezo_img = tensor_to_pil(srezo_tensor)
return srezo_img
@app.route("/", methods=["POST"])
def superResolution():
req_json = json.loads(request.data)
src_b64 = req_json["srcImage"]
# main process
src_img = b64_to_PILImage(src_b64)
srezo_img = expand(src_img)
srezo_b64 = PILImage_to_b64(srezo_img)
results = {"sresoImage":srezo_b64}
return jsonify(results)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=os.environ.get("PORT", 8000))
Zuerst wollte ich es in zwei Funktionen einpassen, aber die Konvertierung von torch.tensor zu PIL Image wurde bei der Verwendung von transforms.ToPILImage merkwürdig, daher habe ich beschlossen, es separat zu definieren. Hat. In Bezug auf das Argument zum Zeitpunkt des letzten app.run scheint es außerdem erforderlich zu sein, die Umgebungsvariable PORT zu lesen, da jedem Docker-Container aufgrund der Spezifikationen des Cloud-Laufs automatisch ein anderer Port zugewiesen wird.
Erstellen Sie nun diese lokal überprüften Vorgänge als Docker-Image.
$ docker build -t gcr.io/[project id]/espcn-api:0 .
Laden Sie dann das Bild mit dem Befehl gcloud in die Containerregistrierung hoch.
$ gcloud docker -- push gcr.io/[project id]/espcn-api:0
Sie können das Bild tatsächlich in der Container-Registrierung auf GCP sehen. Stellen Sie dann das hochgeladene Image bereit.
$ gcloud beta run deploy SR-API --image=gcr.io/[project id]/espcn-api:0
Der Teil, in dem Sie nach der Bereitstellung die SR-API eingeben, ist der Name des Dienstes, sodass Sie ihn nach Belieben hinzufügen können. Es scheint auch, dass die Beta-Komponente installiert wird, wenn sie zum ersten Mal ausgeführt wird. Wenn Sie zu diesem Zeitpunkt nicht auf der Konsole eingeben (--platform verwaltet)
[1] Cloud Run (fully managed)
[2] Cloud Run for Anthos deployed on Google Cloud
[3] Cloud Run for Anthos deployed on VMware
[4] cancel
Please enter your numeric choice: _
Sie werden aufgefordert, 1 einzugeben. Geben Sie also 1 ein. Es wird schwierig sein, kostenlos zu arbeiten, wenn es nicht vollständig verwaltet wird. Dann werden Sie nach der Region gefragt.
[1] asia-east1
[2] asia-northeast1
[3] europe-north1
[4] europe-west1
[5] europe-west4
[6] us-central1
[7] us-east1
[8] us-east4
[9] us-west1
[10] cancel
Please enter your numeric choice: _
Hier wählen wir "us- *". In Cloud Run ist das Downlink-Netzwerk in Nordamerika bis zu 1 GB kostenlos. Wenn Sie dies also über Cloud-Funktionen aufrufen (da Cloud-Funktionen das Downlink-Netzwerk überall bis zu 5 GB kostenlos ist), können Sie es fast kostenlos verwenden.
Danach werden Sie gefragt, ob Sie nicht authentifizierten Zugriff zulassen möchten. Im Moment besteht der Zweck darin, ihn zu verschieben. Setzen Sie ihn also auf "Ja". Wenn Sie wie oben eingeben, wird der Dienst bereitgestellt und die folgende Nachricht ausgegeben.
Deploying container to Cloud Run service [espcn-api] in project [studied-brace-261908] region [us-central1]
✓ Deploying new service... Done.
✓ Creating Revision...
✓ Routing traffic...
✓ Setting IAM Policy...
Done.
Service [espcn-api] revision [espcn-api-00001-guj] has been deployed and is serving 100 percent of traffic at https://espcn-api-~~~-uc.a.run.app
Schließlich wird eine Meldung angezeigt, in der angegeben ist, wo und wo die URL bereitgestellt wurde. Als ich hier versuchte, mit test.py eine Anfrage zu stellen, konnte ich bestätigen, dass die Antwort ordnungsgemäß zurückgegeben wurde.
Es gab eine Sache, die zu diesem Zeitpunkt stecken blieb, aber als ich die Anfrage zum ersten Mal stellte, wurde keine Antwort zurückgegeben. Wenn Sie also das Cloud Run-Protokoll überprüfen,
Memory limit of 244M exceeded with 272M used. Consider increasing the memory limit, see https://cloud.google.com/run/docs/configuring/memory-limits
Ich habe eine Nachricht erhalten, dass nicht genügend Speicher vorhanden ist. Es scheint, dass der zu verwendende Speicher im Cloud-Lauf festgelegt wird und die Größe des Speichers, der angegeben werden kann, 128 MB ~ 2 GB beträgt und standardmäßig 256 MB zugewiesen sind. Da dieser Fehler bei einem 512 x 512-Bild aufgetreten ist, kann er anscheinend durch Zuweisen von 512 MB usw. behoben werden. Wenn Sie jedoch zu viel Speicher zuweisen, wird der freie Frame anscheinend bald überschritten. Verwenden Sie übrigens den folgenden Befehl, wenn Sie den der Anwendung zugewiesenen Speicher ändern möchten.
$ gcloud beta run services update [service name] --memory 512M
Wir haben eine API erstellt und auf Cloud Run veröffentlicht. Im aktuellen Status kann von überall auf diese API zugegriffen werden, und sie ist nicht sicherheits- oder brieftaschenfreundlich. Nachdem Sie den Zugriff auf Cloud Run aus GCP heraus eingeschränkt haben, verwenden Sie dazu die Cloud-Funktionen. Ich möchte zu einer Struktur wechseln, an die Anfragen gesendet werden. Ich möchte eines Tages auf die kontinuierliche Bereitstellung eingehen.
Recommended Posts