첫 Docker Space: T5 텍스트 생성

첫 Docker Space: T5 텍스트 생성 (Your First Docker Space: Text Generation with T5)

다음 섹션에서 Docker Space 생성, 구성, 코드 배포의 기본을 배웁니다. FastAPI를 서버로 사용해 주어진 입력 텍스트에서 텍스트를 생성하는 google/flan-t5-small 모델을 데모하는 Text Generation Space를 만들 거예요.

출처: 문서

본문

다음 섹션에서 Docker Space 생성, 구성, 코드 배포의 기본을 배웁니다. FastAPI를 서버로 사용해 입력 텍스트가 주어지면 텍스트를 생성하는 google/flan-t5-small 모델을 데모할 Text Generation Space를 만들 거예요.

완성된 버전은 여기에서 호스팅됩니다.

새 Docker Space 만들기 (Create a new Docker Space)

완전히 새로운 Space를 만들고 SDK로 Docker를 선택하는 것으로 시작합니다.

Hugging Face Spaces는 Git 리포지토리이므로 커밋을 푸시하며 Space를 점진적으로(그리고 협업으로) 작업할 수 있습니다. 계속하기 전에 Getting Started with Repositories 가이드에서 파일을 만들고 편집하는 방법을 알아보세요. UI를 선호한다면 브라우저에서 직접 작업해도 됩니다.

새 Space를 만들 때 SDK로 Docker를 선택하면 README.md 파일의 YAML 블록에 sdk 속성을 docker로 설정해 Docker Space를 초기화합니다.

sdk: docker

README.md 파일의 YAML 블록에 app_port 속성을 설정해 Space의 기본 애플리케이션 포트를 변경할 수도 있어요. 기본 포트는 7860입니다.

app_port: 7860

의존성 추가하기 (Add the dependencies)

Text Generation Space에서는 Flan T5라는 텍스트 생성 모델을 보여주는 FastAPI 앱을 만듭니다. 모델 추론에는 🤗 Transformers 파이프라인을 사용할 거예요. 먼저 몇 가지 의존성을 설치해야 합니다. 리포지토리에 requirements.txt 파일을 만들고 다음 의존성을 추가하면 됩니다:

fastapi==0.74.*
requests==2.27.*
sentencepiece==0.1.*
torch==1.11.*
transformers==4.*
uvicorn[standard]==0.17.*

이 의존성들은 나중에 만들 Dockerfile에서 설치됩니다.

앱 만들기 (Create the app)

엔드포인트가 동작하는지 확인하려고 더미 FastAPI 앱으로 시작해 봅시다. 첫 단계로 main.py라는 앱 파일을 만듭니다.

from fastapi import FastAPI

app = FastAPI()

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

Dockerfile 만들기 (Create the Dockerfile)

Docker Space의 핵심 단계는 Dockerfile을 만드는 것입니다. Dockerfiles에 대한 자세한 내용은 여기에서 읽어볼 수 있어요. 이 튜토리얼에서는 FastAPI를 사용하지만, Dockerfiles는 사용자에게 새로운 세대의 ML 데모를 구축할 수 있는 큰 유연성을 줍니다. 우리 앱의 Dockerfile을 작성해 봅시다:

# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
# you will also find guides on how best to write your Dockerfile

FROM python:3.9

# The two following lines are requirements for the Dev Mode to be functional
# Learn more about the Dev Mode at https://huggingface.co/dev-mode-explorers
RUN useradd -m -u 1000 user
WORKDIR /app

COPY --chown=user ./requirements.txt requirements.txt
RUN pip install --no-cache-dir --upgrade -r requirements.txt

COPY --chown=user . /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]

변경 사항을 저장하면 Space가 다시 빌드되고 몇 초 후에 데모가 올라옵니다! 이 시점의 예시 결과는 여기입니다.

로컬 테스트 (Testing locally)

파워 유저를 위한 팁(건너뛰어도 됨): 로컬에서 개발한다면 지금 docker builddocker run으로 로컬 디버그를 할 수 있지만, 변경 사항을 Hub에 푸시하고 어떻게 보이는지 확인하는 것이 더 쉽습니다!

docker build -t fastapi .
docker run  -it -p 7860:7860 fastapi

Secrets이 있다면 docker buildx를 사용해 시크릿을 빌드 인자로 전달할 수 있습니다.

export SECRET_EXAMPLE="my_secret_value"
docker buildx build --secret id=SECRET_EXAMPLE,env=SECRET_EXAMPLE -t fastapi .

그리고 시크릿을 환경 변수로 전달해 docker run으로 실행:

export SECRET_EXAMPLE="my_secret_value"
docker run -it -p 7860:7860 -e SECRET_EXAMPLE=$SECRET_EXAMPLE fastapi

앱에 ML 추가하기 (Adding some ML to our app)

앞서 말했듯이 Flan T5 모델을 텍스트 생성에 사용하는 것이 아이디어입니다. 입력 필드를 위한 HTML과 CSS를 추가하기 위해 static이라는 디렉터리에 index.html, style.css, script.js 파일을 만들겠습니다. 이 시점의 파일 구조는 다음과 같아야 합니다:

/static
/static/index.html
/static/script.js
/static/style.css
Dockerfile
main.py
README.md
requirements.txt

이걸 동작하게 만드는 모든 단계를 살펴봅시다. CSS와 HTML의 세부 사항은 일부 생략할 거예요. 전체 코드는 DockerTemplates/fastapi_t5 Space의 Files and versions 탭에서 찾을 수 있습니다.

  1. 추론하는 FastAPI 엔드포인트 작성하기

transformerspipeline으로 google/flan-t5-small 모델을 불러옵니다. 입력을 받아 추론 호출 결과를 출력하는 infer_t5라는 엔드포인트를 설정합니다.

from transformers import pipeline

pipe_flan = pipeline("text2text-generation", model="google/flan-t5-small")

@app.get("/infer_t5")
def t5(input):
    output = pipe_flan(input)
    return {"output": output[0]["generated_text"]}
  1. 페이지 코드를 포함한 간단한 폼을 위해 index.html 작성하기
<main>
  <section id="text-gen">
    <h2>Text generation using Flan T5</h2>
    <p>
      Model:
      <a
        href="https://huggingface.co/google/flan-t5-small"
        rel="noreferrer"
        target="_blank"
        >google/flan-t5-small
      </a>
    </p>
    <form class="text-gen-form">
      <label for="text-gen-input">Text prompt</label>
      <input
        id="text-gen-input"
        type="text"
        value="German: There are many ducks"
      />
      <button id="text-gen-submit">Submit</button>
      <p class="text-gen-output"></p>
    </form>
  </section>
</main>
  1. main.py에서 정적 파일을 마운트하고 루트 경로에 html 파일 표시하기
app.mount("/", StaticFiles(directory="static", html=True), name="static")

@app.get("/")
def index() -> FileResponse:
    return FileResponse(path="/app/static/index.html", media_type="text/html")
  1. script.js에서 요청을 처리하게 만들기
const textGenForm = document.querySelector(".text-gen-form");

const translateText = async (text) => {
  const inferResponse = await fetch(`infer_t5?input=${text}`);
  const inferJson = await inferResponse.json();

  return inferJson.output;
};

textGenForm.addEventListener("submit", async (event) => {
  event.preventDefault();

  const textGenInput = document.getElementById("text-gen-input");
  const textGenParagraph = document.querySelector(".text-gen-output");

  textGenParagraph.textContent = await translateText(textGenInput.value);
});
  1. 올바른 디렉터리에 권한 부여하기

Permissions 섹션에서 논의했듯이 컨테이너는 사용자 ID 1000으로 실행됩니다. 즉 Space가 권한 문제를 겪을 수 있어요. 예를 들어 transformersHF_HOME 경로 아래에 모델을 다운로드하고 캐시합니다. 가장 쉬운 해결책은 올바른 권한을 가진 사용자를 만들고 그 사용자로 컨테이너 애플리케이션을 실행하는 것입니다. Dockerfile에 다음 줄을 추가하면 됩니다.

# Switch to the "user" user
USER user

# Set home to the user's home directory
ENV HOME=/home/user \
	PATH=/home/user/.local/bin:$PATH

최종 Dockerfile은 이렇게 생겨야 합니다:


# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
# you will also find guides on how best to write your Dockerfile

FROM python:3.9

# The two following lines are requirements for the Dev Mode to be functional
# Learn more about the Dev Mode at https://huggingface.co/dev-mode-explorers
RUN useradd -m -u 1000 user
WORKDIR /app

COPY --chown=user ./requirements.txt requirements.txt
RUN pip install --no-cache-dir --upgrade -r requirements.txt

COPY --chown=user . /app

USER user

ENV HOME=/home/user \
	PATH=/home/user/.local/bin:$PATH

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

성공! 이제 앱이 동작해야 합니다! 최종 결과는 DockerTemplates/fastapi_t5에서 확인하세요.

긴 여정이었죠! Docker Spaces는 많은 자유를 주므로 FastAPI에 제한되지 않는다는 점을 기억하세요. Go Endpoint부터 Shiny App까지, 한계는 달입니다! 공식 예시를 확인해 보세요. 필요하다면 Space를 GPU로 업그레이드할 수도 있습니다 😃

디버깅 (Debugging)

BuildContainer 로그를 확인해 Space를 디버그할 수 있어요. Open Logs 버튼을 클릭해 모달을 여세요.

모든 것이 잘 되었다면 Build 탭에서 Pushing ImageScheduling Space를 볼 수 있을 것입니다.

Container 탭에서는 애플리케이션 상태를 볼 수 있습니다. 이 경우 Uvicorn running on http://0.0.0.0:7860입니다.

추가로 Space에서 Dev Mode를 활성화할 수 있어요. Dev Mode는 실행 중인 Space에 VSCode 또는 SSH로 연결할 수 있게 해줍니다. 자세한 내용: https://huggingface.co/dev-mode-explorers

더 읽기 (Read More)

더 알아보기 (Learn more)

sdk: docker + Dockerfile로 어떤 스택이든(여기선 FastAPI + Flan T5) Space에서 실행할 수 있어요. Dev Mode·GPU 기능과 함께 컨테이너는 사용자 ID 1000으로 돌아가니 USER user / COPY --chown=user로 권한을 맞추세요. 전체 예시는 DockerTemplates/fastapi_t5과 Docker Spaces 문서에서 확인할 수 있습니다.