사용자의 모델을 ThanoSQL에서 사용하기¶
- 튜토리얼 난이도: ★★☆☆☆
 - 읽는데 걸리는 시간: 10분
 - 사용 언어: SQL (50%), Python (50%)
 - 실행 파일 위치: tutorial/thanosql_ml/udm_tutorial.ipynb
 - 참고 문서: Beans 데이터 세트
 
튜토리얼 소개¶
해당 기능은 유료 버전에서 원할하게 작동합니다.
ThanoSQL에서는 사용자가 만들거나 가지고 있는 모델에 대하여 ThanoSQL 워크스페이스와 데이터베이스에 올리고 예측에 사용할 수 있는 기능을 제공하고 있습니다.
본 튜토리얼에서는
👉 beans 데이터 세트는 우간다의 농업 연구를 담당하는 국가 기관인 국립 작물 자원 연구소(NaCRRI)와 협력하여 Makerere AI 연구소가 우간다의 여러 지역에서 현장에서 촬영한 잎 이미지입니다. 데이터는 총 3개의 클래스로 구성되어 있습니다. 2개의 질병 클래스와 건강 클래스이고, 질병은 각각 세균모무늬병(Angular leaf spot)과 콩 녹병(Bean rust)입니다. 본 튜토리얼에서는 이미지 분류 모델을 학습시켜서 사용자 모델을 만든 후 ThanoSQL에 올리고 예측해 봅니다.
#. 파이썬을 이용한 데이터 세트 및 모델 준비¶
데이터 세트 준비¶
데이터 다운로드 및 압축풀기¶
import os
from shutil import unpack_archive
from urllib.request import urlretrieve
url = "https://storage.googleapis.com/ibeans"
for split in ["train", "validation", "test"]:
    urlretrieve(f"{url}/{split}.zip", f"{split}.zip")
    unpack_archive(f"{split}.zip", ".")
    os.remove(f"{split}.zip")
패키지 설치¶
!pip install torch torchvision
from torch.utils.data import DataLoader
from torchvision import transforms as T
from torchvision.datasets import ImageFolder
data_transforms = {
    "train": T.Compose(
        [
            T.RandomResizedCrop(224),
            T.RandomHorizontalFlip(),
            T.ToTensor(),
            T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
        ]
    ),
    "validation": T.Compose(
        [
            T.Resize(224),
            T.CenterCrop(224),
            T.ToTensor(),
            T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
        ]
    ),
}
image_datasets = {
    split: ImageFolder(split, data_transforms[split])
    for split in ["train", "validation"]
}
dataloaders = {
    split: DataLoader(image_datasets[split], batch_size=8, shuffle=split == "train")
    for split in ["train", "validation"]
}
dataset_sizes = {split: len(image_datasets[split]) for split in ["train", "validation"]}
모델 준비¶
모델 학습 코드 작성하기¶
import time
import copy
import torch
def train_model(model, criterion, optimizer, num_epochs=3):
    start_time = time.time()
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    model.to(device)
    best_model_weights = copy.deepcopy(model.state_dict())
    best_acc = 0.0
    for epoch in range(num_epochs):
        print(f"Epoch {epoch}/{num_epochs - 1}")
        print("-" * 10)
        # 각 에폭(epoch)은 학습 단계와 검증 단계를 갖습니다.
        for phase in ["train", "validation"]:
            if phase == "train":
                model.train()
            else:
                model.eval()
            running_loss = 0.0
            running_corrects = 0
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)
                # 매개변수 경사도를 0으로 설정
                optimizer.zero_grad()
                # 순전파
                # 학습 단계에서만 연산 기록을 추적
                with torch.set_grad_enabled(phase == "train"):
                    outputs = model(inputs)
                    preds = torch.argmax(outputs, dim=1)
                    loss = criterion(outputs, labels)
                    # 학습 단계인 경우에만 역전파
                    if phase == "train":
                        loss.backward()
                        optimizer.step()
                # 통계
                running_loss += loss.item() * inputs.size(0)
                running_corrects += torch.sum(preds == labels.data)
            epoch_loss = running_loss / dataset_sizes[phase]
            epoch_acc = running_corrects / dataset_sizes[phase]
            print(f"{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}")
            # 모델의 정확도가 기존의 최고 정확도보다 높다면 저장
            if phase == "validation" and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_weights = copy.deepcopy(model.state_dict())
        print()
    time_elapsed = time.time() - start_time
    print(f"Training complete in {time_elapsed // 60:.0f}m {time_elapsed % 60:.0f}s")
    print(f"Best val Acc: {best_acc:4f}")
    # 가장 나은 모델 가중치를 불러옴
    model.load_state_dict(best_model_weights)
    return model
모델 불러오기¶
mobilevit v2를 사용합니다. 작고 가볍지만 정확도는 높아 빠른 튜토리얼에 적합합니다.
model = torch.hub.load("rwightman/pytorch-image-models", "mobilevitv2_050", pretrained=True, num_classes=3)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = torch.nn.CrossEntropyLoss()
모델 훈련 및 저장¶
trained_model = train_model(model, criterion, optimizer, num_epochs=1)
Epoch 0/0 ---------- train Loss: 0.5792 Acc: 0.8153 validation Loss: 0.1912 Acc: 0.9323 Training complete in 0m 58s Best val Acc: 0.932331
torch.save(trained_model, "trained_model.pth")
ThanoSQL에 입력할 데이터프레임 생성¶
import numpy as np
import pandas as pd
test_dataset = ImageFolder("test", data_transforms["validation"])
data = np.stack([img.numpy() for img, _ in test_dataset])
df = pd.DataFrame(pd.Series(data.tolist()), columns=["image"])  # column 이름을 "image"로 지정해야 합니다.
df.to_pickle("test_data.pkl")
0. 데이터 세트 준비¶
ThanoSQL의 쿼리 구문을 사용하기 위해서는 ThanoSQL 워크스페이스에서 언급된 것처럼 API 토큰을 생성하고 아래의 쿼리를 실행해야 합니다.
%load_ext thanosql
%thanosql API_TOKEN=<발급받은_API_TOKEN>
데이터 세트 준비¶
%%thanosql
COPY beans_test
OPTIONS (if_exists='replace')
FROM 'test_data.pkl'
Success
쿼리 세부 정보
- "COPY" 쿼리 구문을 사용하여 데이터베이스에 저장 할 테이블명을 지정합니다.
 - "OPTIONS" 쿼리 구문을 통해 COPY에 사용할 옵션을 지정합니다.
        
- "if_exists": 동일 이름의 테이블이 존재하는 경우 처리하는 방법 설정. 오류 발생, 기존 테이블에 추가, 기존 테이블 대체 (str, optional, 'fail'|'replace'|'append', default: 'fail')
 
 
1. 데이터 세트 확인¶
본 튜토리얼을 진행하기 위해 ThanoSQL 워크스페이스 데이터베이스에 저장되어 있는 beans_test 테이블을 사용합니다. 아래의 쿼리 구문을 실행하고 테이블의 내용을 확인합니다.
%%thanosql
SELECT *
FROM beans_test
LIMIT 5
| image | |
|---|---|
| 0 | [[[-0.028684020042419434, -0.04580877348780632... | 
| 1 | [[[-0.0629335269331932, -0.0629335269331932, -... | 
| 2 | [[[1.9577873945236206, 1.8721636533737183, 1.7... | 
| 3 | [[[0.21106265485286713, 0.0569397434592247, -0... | 
| 4 | [[[-1.3815395832061768, -1.432913899421692, -1... | 
데이터 테이블 이해하기
beans_test 테이블은 아래와 같은 정보를 담고 있습니다.
- image: numpy 형식으로 저장한 이미지
 
2. 사용자 모델 업로드¶
이전 단계에서 파이썬을 이용하여 사용자가 만든 모델을 아래의 쿼리 구문을 실행하여 beans_mobilevit이라는 이름의 모델을 업로드합니다.
%%thanosql
UPLOAD MODEL beans_mobilevit
OPTIONS (
    framework='pytorch',
    overwrite=True
    )
FROM 'trained_model.pth'
Success
쿼리 세부 정보
- "UPLOAD MODEL" 쿼리 구문을 사용하여 beans_mobilevit이라는 모델을 업로드 시킵니다.
 - "OPTIONS" 쿼리 구문을 통해 모델 업로드에 사용할 옵션을 지정합니다.
        
- "framework": 모델의 프레임워크 (str, default: 'pytorch')
 - "overwrite": 동일 이름의 모델이 존재하는 경우 덮어쓰기 가능 여부 설정. True일 경우 기존 모델은 새로운 모델로 변경됨 (bool, optional, True|False, default: False)
 
 
현재 ThanoSQL은 pytorch 형식의 모델만 지원합니다.
3. 사용자 모델을 사용하여 예측¶
이전 단계에서 업로드한 사용자 모델을 사용해 콩의 클래스를 예측해 봅니다.
%%thanosql
PREDICT USING beans_mobilevit
OPTIONS (
    result_col='predicted'
    )
AS (
    SELECT *
    FROM beans_test
    ORDER BY RANDOM()
    LIMIT 5
    )
| image | predicted | |
|---|---|---|
| 0 | [[[-0.7821731567382812, -0.9362959265708923, -... | [0.464764267206192, 2.2284131050109863, -2.832... | 
| 1 | [[[0.7248052358627319, 0.8104289770126343, 0.8... | [-0.7518391609191895, 2.296389102935791, -1.67... | 
| 2 | [[[0.1083141341805458, 0.31381115317344666, 0.... | [2.892843008041382, -1.4041131734848022, -1.36... | 
| 3 | [[[-0.23418104648590088, -0.38830381631851196,... | [-1.4550528526306152, 2.636288642883301, -1.30... | 
| 4 | [[[-1.809658408164978, -1.689785122871399, -1.... | [-1.589335560798645, -0.9054973125457764, 2.46... | 
쿼리 세부 정보
- "PREDICT USING" 쿼리 구문을 사용하여 beans_mobilevit 모델을 예측에 사용합니다.
 - "OPTIONS" 쿼리 구문을 통해 예측에 사용할 옵션을 지정합니다.
        
- "result_col": 데이터 테이블에서 예측 결과를 담을 컬럼 이름 (str, optional, default: 'predict_result')
 
 
pred_df = _  # 가장 마지막에 사용된 객체를 불러옵니다.
pred_df["predicted"] = pred_df["predicted"].apply(np.argmax)
pred_df["predicted"] = pred_df["predicted"].apply(test_dataset.classes.__getitem__)
pred_df
| image | predicted | |
|---|---|---|
| 0 | [[[-0.7821731567382812, -0.9362959265708923, -... | bean_rust | 
| 1 | [[[0.7248052358627319, 0.8104289770126343, 0.8... | bean_rust | 
| 2 | [[[0.1083141341805458, 0.31381115317344666, 0.... | angular_leaf_spot | 
| 3 | [[[-0.23418104648590088, -0.38830381631851196,... | bean_rust | 
| 4 | [[[-1.809658408164978, -1.689785122871399, -1.... | healthy | 
4. 튜토리얼을 마치며¶
이번 튜토리얼에서는 사용자 정의 모델을 ThanoSQL에 올린 후 beans 데이터 세트를 사용하여 사용자 모델을 예측에 사용해 보았습니다. 해당 튜토리얼을 참고하여 다양한 모델들을 ThanoSQL에 올리고 사용할 수 있습니다.
나만의 서비스를 위한 모델 배포 관련 문의
ThanoSQL을 활용해 나만의 모델을 만들거나, 나의 서비스에 적용하는데 어려움이 있다면 언제든 아래로 문의주세요😊
사용자 정의 모델 구축 관련 문의: contact@smartmind.team