준비는 끝났다! 제일 간단한 예제로 resnet 을 서버에 올려보자
https://pytorch.org/hub/pytorch_vision_resnet/
[추론 코드]
import torch
from threading import Lock
from torchvision import transforms
class Singleton:
_instance = None
_lock = Lock()
def __new__(cls, *args, **kwargs):
with cls._lock:
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
class ResNet(Singleton):
"""Singleton class for ResNet model."""
def __init__(self) -> None:
self.device = "mps"
self.model = torch.hub.load('pytorch/vision:v0.10.0', 'resnet18', pretrained=True).to(self.device)
self.model.eval()
self.preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
with open("imagenet_classes.txt", "r") as f:
self.categories = [s.strip() for s in f.readlines()]
if __name__ == "__main__":
resnet1 = ResNet()
resnet2 = ResNet()
print(f"resnet1 ID: {id(resnet1)}")
print(f"resnet2 ID: {id(resnet2)}")
if id(resnet1) == id(resnet2):
print("싱글턴 패턴이 제대로 적용되었습니다.")
else:
print("싱글턴 패턴이 적용되지 않았습니다.")
우선 추론 코드에 싱글턴 패턴이 제대로 적용 되었는지 확인해보았다

성공👏
설치해둔 가상환경에 fastapi와 uvicorn 을 설치
$ pip install "fastapi[all]" uvicorn
app = FastAPI()
class ResNet(Singleton):
def __init__(self) -> None:
self.device = "mps" if torch.backends.mps.is_available() else "cpu"
self.model = torch.hub.load('pytorch/vision:v0.10.0',
'resnet18',
pretrained=True).to(
self.device)
self.model.eval()
self.preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
with open("imagenet_classes.txt", "r") as f:
self.categories = [s.strip() for s in f.readlines()]
위 싱글턴을 그대로 사용하여 하나의 모델 객체만 생성 될 수 있게 만들어준다.
def predict(self, image_bytes: bytes) -> dict:
input_image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
input_tensor = self.preprocess(input_image).unsqueeze(0).to(
self.device)
with torch.no_grad():
output = self.model(input_tensor)
predict_tensor = torch.nn.functional.softmax(output[0], dim=0)
top5_prob, top5_catid = torch.topk(predict_tensor, 5)
top5_dict = {}
for i in range(top5_prob.size(0)):
top5_dict[self.categories[top5_catid[i]]] = top5_prob[i].item()
return top5_dict
resnet_model = ResNet()
추론은 추론만 하면 될 수 있게 분리.
@app.post("/predict")
async def predict(file: UploadFile = File(...)):
try:
image_bytes = await file.read()
predictions = resnet_model.predict(image_bytes)
return JSONResponse(content=predictions)
except Exception as e:
return JSONResponse(content={"error": str(e)}, status_code=500)
엔드포인트를 만들어주고, 사진은 byte 정보를 post로 받을 수 있게 만들었다
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
$ python app.py
그러고 나서 파일을 실행하면 추론 서버가 켜진다 👍

이제 해당 사진을 요청으로 보내보면,

import requests
def send_image(image_path: str, url: str = "http://127.0.0.1:8000/predict") -> None:
with open(image_path, "rb") as image_file:
files = {"file": image_file}
try:
response = requests.post(url, files=files)
for cls, score in response.json().items():
print(cls, score)
except requests.exceptions.RequestException as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
image_path = "dog.jpg"
send_image(image_path)

사모예드로 분류해냈다
🐶
fast api와 singleton패턴 resnet 을 사용하여 간단하게 비동기 서버에 인공지능 모델을 올리는 과정을 해보았다. 이제 서버 환경에 맞춰서 약간 수정하고, 위 예제에 다른 모델들만 바꿔서 쓰면 될 것 이다