API로 whisper를 외부에 오픈하기

FastAPI 를 사용하여 api를 만든후 file을 업로드 받아서 transcribe 를 해서 결과를 보내주는 API를 만들자.

architecture

FastAPI

기본 api만들기

tag : v0.2

Dockerfile에 다음처럼 수정한다.


FROM python:3.9

WORKDIR /code

RUN apt update
RUN apt install ffmpeg -y
RUN pip install --upgrade pip

RUN pip install openai-whisper==20230314
RUN pip install setuptools-rust==1.5.2

RUN pip install fastapi[all]==0.95.1
RUN pip install uvicorn[standard]==0.21.1

COPY ./app /code/app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

새로운 main.py를 만든다.

vi app/main.py
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"Hello": "World"}

docker-compose.yml을 업데이트한다.

version: '3.9'
services:
  whisper:
    build: .
    volumes:
      - ./cache:/root/.cache/whisper:rw
    ports:
      - '80:80'
    # gpu사용하면 주석해제
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: 'nvidia'
    #           capabilities: [gpu]
    #           count: 1

실행하고 테스트하기

docker-compose up --build
curl http://localhost

helloworld가 나오면 성공.

api호출을 통해서 transcribe하기

기존 main코드를 새 main코드에 이동한다.

helloworld부분을 지우고 다음처럼 업데이트한다. tag v0.3을 이용한다.

vi main.py
from fastapi import FastAPI, Path
import whisper
import torch
import time
import os
from threading import Lock

app = FastAPI()

model_name= os.getenv("ASR_MODEL", "base")

@app.get("/")
def read_root():
    print("model:",model_name)
    if torch.cuda.is_available():
        model = whisper.load_model(model_name).cuda()
        print("Using GPU:", torch.cuda.get_device_name(0))
    else:
        model = whisper.load_model(model_name)
        print("Using CPU")
    model_lock = Lock()

    start = time.time()

    file = "app/audio/kr.mp3" #mp3 파일 경로를 이용한다.
    result = model.transcribe(file)

    end = time.time()
    print("The time of execution of above program is :", (end-start))

    return {"content": result["text"],"processing_seconds": (end-start)}

처리 시간을 보기위해 시간 관련 코드를 넣었다.

모델타입을 환경변수로 받을수 있게 해두었다.

현재 도커파일은 다음과 같다.

vi Dockerfile

FROM python:3.9

WORKDIR /code

RUN apt update
RUN apt install ffmpeg -y
RUN pip install --upgrade pip

RUN pip install openai-whisper==20230314
RUN pip install setuptools-rust==1.5.2

RUN pip install fastapi[all]==0.95.1
RUN pip install uvicorn[standard]==0.21.1
RUN pip install gunicorn==20.1.0

COPY ./app /code/app

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

# If running behind a proxy like Nginx or Traefik add --proxy-headers
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"]

docker-compose파일은 다음과 같다.

version: '3.9'
services:
  whisper:
    build: .
    volumes:
      - ./cache:/root/.cache/whisper:rw
      - ./app:/code/app
    ports:
      - '80:80'
    environment:
      - ASR_MODEL=tiny
      # - ASR_MODEL=base
      # - ASR_MODEL=small
      # - ASR_MODEL=medium
      # - ASR_MODEL=large
    # deploy: # gpu사용시 주석해제
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: 'nvidia'
    #           capabilities: [gpu]
    #           count: 1

실행후 테스트해보자.

cpu만 사용시 위 주석 그대로 사용

주석 해제후 GPU사용

cpu는 30초 gpu는 15초 걸림을 알수 있다.

tiny모델의 경우라 그렇고 large모델은 더 많은 시간차이가 난다. gpu사용을 추천한다.

api doc 보기

브라우저에서 http://whipsper/docs를 확인해보자.

나의 경우에 whisper를 서버아이피로 /etc/hosts에서 정의해두었다. 안해두었으면 서버 아이피를 사용하면된다.

/에 대한 내용을 볼수잇다 앞으로 api endpoint를 추가하면 이 Url로 다 확인할수 있다.

health Check url을 추가

from fastapi import FastAPI, File, UploadFile
import whisper
import torch
import ffmpeg
import numpy as np
import os
from threading import Lock
from typing import BinaryIO
from fastapi.responses import StreamingResponse, RedirectResponse

app = FastAPI()

SAMPLE_RATE=16000

model_name= os.getenv("ASR_MODEL", "base")
deviceType=""
deviceName = ""

if torch.cuda.is_available():
    deviceType = "GPU"
    deviceName = torch.cuda.get_device_name(0)
    print("Using GPU:", deviceName)
    model = whisper.load_model(model_name).cuda()
else:
    deviceType = "CPU"
    print("Using CPU")
    model = whisper.load_model(model_name)
model_lock = Lock()

print("Device Type:", deviceType)
if(deviceType == "GPU"):
    print("Device Name:", deviceName)

@app.get("/health")
def health():
    return {"model": model_name,"deviceType": deviceType, "deviceName": deviceName}

브라우저에서 요청해보면 다음처럼 잘 보인다.

mp3파일을 업로드해서 결과 확인

다음 코드를 추가한다.


# asr : Automatic Speech Recognition(자동 음성 인식)
@app.post("/asr")
def transcribe(
                audio_file: UploadFile = File(...),
                ):
    audio = load_audio(audio_file.file)
    with model_lock:
        result = model.transcribe(audio)
    return result["text"]

def load_audio(file: BinaryIO, sr: int = SAMPLE_RATE):
    try:
        out, _ = (
            ffmpeg.input("pipe:", threads=0)
            .output("-", format="s16le", acodec="pcm_s16le", ac=1, ar=sr)
            .run(cmd="ffmpeg", capture_stdout=True, capture_stderr=True, input=file.read())
        )
    except ffmpeg.Error as e:
        raise RuntimeError(f"Failed to load audio: {e.stderr.decode()}") from e

    return np.frombuffer(out, np.int16).flatten().astype(np.float32) / 32768.0

실행해보자.

curl -F "audio_file=@~/Desktop/GitHub/my-whisper/app/audio/kr.mp3" http://whisper/asr

포스트맨으로 실행해보도 확인할수 있다.

업로드한 파일의 언어 확인

tag v0.5를 확인한다.

다음 코드를 추가한다.

@app.post("/lang")
def language_detection(
                audio_file: UploadFile = File(...),
                ):

    # load audio and pad/trim it to fit 30 seconds
    audio = load_audio(audio_file.file)
    audio = whisper.pad_or_trim(audio)

    # make log-Mel spectrogram and move to the same device as the model
    mel = whisper.log_mel_spectrogram(audio).to(model.device)

    # detect the spoken language
    with model_lock:
        _, probs = model.detect_language(mel)
    detected_lang_code = max(probs, key=probs.get)

    result = { "detected_language": tokenizer.LANGUAGES[detected_lang_code],"language_code" : detected_lang_code }

    return result

실행하고 확인해본다.

curl -F "audio_file=@~/Desktop/GitHub/my-whisper/app/audio/kr.mp3" http://whisper/lang

> {"detected_language":"korean","language_code":"ko"}

subscribe 할때 언어를 지정해주자.

tag : v0.6을 확인하자.

언어를 지정하지 않으면 자동으로 확인해서 적당한 언어를 지정해준다. 그런데 언어를 지정해주면 더 정확할것 같다.

def transcribe(
                audio_file: UploadFile = File(...),
                language: Union[str, None] = Query(default=None, enum=LANGUAGE_CODES),
                ):
    audio = load_audio(audio_file.file)
    options_dict = {"language" : language  }

    with model_lock:
        result = model.transcribe(audio, **options_dict)
    return result["text"]

언어를 다르게 지정해주면 다르게 결과가 나오는것을 알수 잇다.

확인해보자.

curl -F "audio_file=@kr.mp3" http://whisper/asr\?language\=en
curl -F "audio_file=@kr.mp3" http://whisper/asr\?language\=ko

일단 인식하고 번역을 해주는것인지 인식을 다르게 하는것인지가 확실치가 않다.

//todo 위 내용 확인

translate

tag : v0.7을 보자.

whisper는 subscribe 나 translate 두가지 task를 할수 있다. 이걸 api에서 쿼리스트링으로 받아보자.

translate을 사용하면 모든 언어를 영어로 바꿔준다.

# asr : Automatic Speech Recognition(자동 음성 인식)
@app.post("/asr")
def transcribe(
                audio_file: UploadFile = File(...),
                language: Union[str, None] = Query(default=None, enum=LANGUAGE_CODES),
                task : Union[str, None] = Query(default="transcribe", enum=["transcribe", "translate"]),
                ):
    audio = load_audio(audio_file.file)
    options_dict = {"language" : language  }
    if task:
        options_dict["task"] = task
    with model_lock:
        result = model.transcribe(audio, **options_dict)
    return result["text"]

task를 받아서 그걸 옵션으로 transcribe에 넣어준다.

확인해보자.

curl -F "audio_file=@kr.mp3" http://whisper/asr\?task\=translate

한글이 영어로 번역되서 보여진다.

한글을 다른 언어로 번역은 안되나?

https://github.com/openai/whisper 에서 보면 영어로만 된다고 되있음

Last updated

Was this helpful?