티스토리 뷰

반응형

요즘은 완전히 GPU 전성시대이다. 돈이 있다고 해도 GPU를 살 수 있으려면 기간조차도 매우 많이 드는 게 현실이다.

간단한 모델을 돌려보려고 해도, 클라우드 컴퓨팅은 세팅도 번거로울 뿐더러, 초심자가 접근하기에는 힘들다.

코랩은 또 무료 버전은 시간제한이 있다.

 

한마디로 GPU 값은 매우 비싸다.

그런데 여기, 미쳤다고 무려 16GB의 GPU를 일주일에 30시간이나 빌려주는 곳이 있다.

그것도 공짜로.

 

그곳은 바로 데이터 사이언스인들의 성지. Kaggle이다.

 

https://www.kaggle.com/ 

 

필자는 이 kaggle에서 정말 다양하고 다양한 실험을 해왔다.

파이썬 새로 깔아보기, 해당 도커 버전 바꿔보기, 오픈소스 버그 수정 등등.. 

거의 살을 발라내고 사골까지 우려먹었다고 해도 과언이 아니다. 

그러나 기본적으로 Docker 이미지인 만큼 한계가 있고, 해당하는 방법들도 막히는 경우가 있긴 하다.

 

따라서 오늘은 가장 실용성 있는 사용법 중 하나인 GPU 임시서버로서의 kaggle을 소개하고자 한다.

nginx도 써보긴 했으나, 현재까지는 ngrok을 이용한 방법이 초보자도 이용하기에는 가장 간단하기에 소개한다.

 

ngrok auto키 발급받기

 

 

우선 ngrok이 뭔지에 대해서 설명하자면, 인터넷에서는 공유 ip와 내부 ip라는 개념이 있는데, 내부 ip는 흔히 말하는 우리 집 안과 공유기의 아이피 정도라고 생각하면 된다. 그리고 공유 ip는 말 그대로 전 세계로 나가는 ip라고 생각하면 편하다.

 

그리고 이 내부 ip와 공유 ip 사이에는 방화벽이 쳐져 있어서, 매우 복잡한 보안 프로토콜을 거쳐야지만 외부에서 접속 가능하고 통과가 가능하도록 해놓았다.

 

비유하자면 작은 강(내부 ip)과 큰 강(외부 ip) 사이의 거름망(방화벽)이라고 보면 되겠다.

 

그래서 이 내부 ip는 외부 사람이 함부로 접근하지 못하고, 항상 막히게 되는 것이다. 그런데 이 과정을 매우 편하게 만드는, 그러니까 내부 ip를 외부 ip로 잠시동안 공유도 하면서, 보안 설정까지 할 수 있게 해주는 게 바로 이 ngrok이다.

 

따라서 이 키를 발급 받고 해당하는 세팅을 해준다면 집 컴에서 돌리는 서버가 외부에서도 접속 가능하게 되는 것이다.

 

https://dashboard.ngrok.com/get-started/your-authtoken

 

참고로 api 키가 아니라, 위 경로를 통해 auth 키를 발급받아야 한다.

미리 조심하자.

Fastapi 설정 세팅하기

!pip install fastapi uvicorn pyngrok

 

기본적으로 다음과 같이 다운받아 준다.

 

이제 해당하는 키들을 넣어주자.

 

import os
from kaggle_secrets import UserSecretsClient
from pyngrok import ngrok
user_secrets = UserSecretsClient()
secret_value_0 = user_secrets.get_secret("NGROK_API_KEY")

# ngrok authtoken 설정
ngrok.set_auth_token(secret_value_0)

 

필자의 경우에는 secret으로 넣어주었다.

ngrok.set_auth_token의 괄호 안에 ' '를 붙여서 아예 박아넣어도 사실 상관 없다.

 

이제 간단한 모델 예시를 보자.

import os
from fastapi import FastAPI, File, UploadFile
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import torch
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
import uvicorn
from pyngrok import ngrok
import threading

# FastAPI 앱 생성
app = FastAPI()

# CORS 미들웨어 추가
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 모든 오리진 허용 (프로덕션에서는 특정 오리진만 허용하도록 변경)
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# CUDA 사용 가능 여부 확인
device = "cuda:0" if torch.cuda.is_available() else "cpu"
torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32

# 모델 및 프로세서 초기화
model_id = "openai/whisper-large-v3"
model = AutoModelForSpeechSeq2Seq.from_pretrained(
    model_id, torch_dtype=torch_dtype, low_cpu_mem_usage=True, use_safetensors=True
)
model.to(device)
processor = AutoProcessor.from_pretrained(model_id)

# 파이프라인 설정
pipe = pipeline(
    "automatic-speech-recognition",
    model=model,
    tokenizer=processor.tokenizer,
    feature_extractor=processor.feature_extractor,
    torch_dtype=torch_dtype,
    device=device,
)

@app.post("/transcribe")
async def transcribe_audio(file: UploadFile = File(...)):
    # 파일을 메모리에 로드하고 STT 수행
    audio_bytes = await file.read()
    with open("temp.wav", "wb") as f:
        f.write(audio_bytes)
    
    generate_kwargs = {
        "return_timestamps": True,
        "language": 'korean'
    }
    # 오디오 파일을 텍스트로 변환
    result = pipe("temp.wav", generate_kwargs=generate_kwargs)
    
    # 임시 파일 삭제
    os.remove("temp.wav")
    
    # 결과를 JSON 형식으로 반환
    return JSONResponse(content={"filename": file.filename, "transcription": result['text']})

def run_server():
    uvicorn.run(app, host="0.0.0.0", port=8000)

if __name__ == "__main__":
    # ngrok을 통해 8000 포트를 외부에 노출
    ngrok_tunnel = ngrok.connect(8000)
    print(f"Public URL: {ngrok_tunnel.public_url}")
    
    # 서버 실행
    server_thread = threading.Thread(target=run_server)
    server_thread.start()

 

위 코드는 huggingface에 있는 whisper model large를 사용할 수 있게 만든 것이다..

cors 토큰 설정은 해줘야 fastapi 커넥션이 가능하다.

 

그렇게 하고, 해당 코드를 실행하면 url이 떴을 것이다.

해당 url에 요청하기 위해서, 클라이언트 코드를 짜주자.

import requests
import time

# ngrok URL을 여기에 입력하세요 (서버 실행 시 출력되는 URL)
url = "https://b18a-104-198-7-16.ngrok-free.app/transcribe"

# 파일 경로가 올바른지 확인하세요
file_path = '08580.wav'

try:
    with open(file_path, 'rb') as file:
        files = {'file': file}
        
        # 시작 시간 기록
        start_time = time.time()
        
        response = requests.post(url, files=files)
        
        # 종료 시간 기록
        end_time = time.time()
        
        # 총 소요 시간 계산
        elapsed_time = end_time - start_time
        
    if response.status_code == 200:
        result = response.json()
        print("Transcription result:")
        print(result)
        print(f"\nTotal time taken: {elapsed_time:.2f} seconds")
    else:
        print(f"Error: {response.status_code}")
        print(response.text)
except FileNotFoundError:
    print(f"File not found: {file_path}")
except requests.exceptions.RequestException as e:
    print(f"Request failed: {e}")

 

해당 코드는 stt기에 wav 파일을 보내고, json text를 받는다.

이런 예시와 같이 클라이언트 코드를 짜주면 된다.

이제 요청을 해본다면?

 

다음과 같이 json 파일로 명확하게 stt의 result를 받을 수 있다.

 

이제 이걸 응용해서 kaggle로 임시 GPU 서버를 사용해보자.

이게 임시 GPU 서버인 이유는, kaggle은 세션이 12시간 이상 지속되어 버리면 아예 접속이 끊긴다.

이점은 참고하자.

반응형