Initial commit

This commit is contained in:
b24503@hanmaceng.co.kr
2025-01-07 09:11:27 +09:00
commit fc3ead893a
14 changed files with 752 additions and 0 deletions

32
.gitattributes vendored Executable file
View File

@@ -0,0 +1,32 @@
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text

166
.gitignore vendored Executable file
View File

@@ -0,0 +1,166 @@
#output, model, etc
output
cache
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
.vscode/
flagged/
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

32
Dockerfile Executable file
View File

@@ -0,0 +1,32 @@
# Base image
FROM pytorch/pytorch:2.1.2-cuda11.8-cudnn8-devel
# Set timezone and working directory
ARG DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Seoul
WORKDIR /opt/workspace
# Use a faster mirror for apt-get
RUN sed -i 's/archive.ubuntu.com/mirror.kakao.com/g' /etc/apt/sources.list
# Install system dependencies (including dependencies for Hugging Face login)
RUN apt-get update && apt-get install -y \
git \
vim \
curl \
&& apt-get clean
# Upgrade pip and install Python dependencies, including Hugging Face CLI
COPY requirements.txt .
RUN pip install --upgrade pip && \
pip install huggingface_hub && \
pip install -r requirements.txt
# Copy project files into the container
COPY . .
# Set Python to output logs in real time
ENV PYTHONUNBUFFERED=1
# Default command to run both uvicorn server and worker
# CMD ["/bin/bash", "-c", "uvicorn main:app --host 0.0.0.0 --port 8000 & python /opt/workspace/worker.py"]

21
LICENSE Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Lectom
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

60
README.md Executable file
View File

@@ -0,0 +1,60 @@
# FastAPI LLM Inference Framework
## 개요
### 목적
* FastAPI를 이용한 대규모 언어 모델(LLM) 서빙 및 추론 환경 제공
### 특징
* **LLM**을 활용하여 입력 텍스트 기반의 추론 수행
* **다중 모델 지원:** 여러 모델에 대해 순차적으로 추론 가능
* **유연한 배치 처리:** 대용량 CSV 데이터를 분할하여 효율적으로 처리
* **Redis + RQ 큐**를 활용한 비동기 작업 관리 및 확장성 제공
---
## 사용하기
### 빌드
```
docker compose build
```
Dockerfile 상 변경은 없으나 디펜던시 변경 또는 환경 설정 업데이트가 있는 경우, `--no-cache` 옵션을 사용하여 캐시 없이 재빌드가 가능합니다.
### 실행
```
docker compose up
```
`docker-compose.yml`의 실행 진입점은 다음과 같습니다:
```
entrypoint: uvicorn main:app --reload --host 0.0.0.0 --port 8000
```
---
## API
### API 문서 확인
FastAPI의 자동 생성 API 문서를 확인하려면:
```
http://localhost:8000/docs
```
### 주요 API 엔드포인트
1. **추론 시작** (`POST /start-inference/`):
- CSV 데이터와 모델 리스트를 업로드하여 추론 작업 시작
- 결과 파일은 자동으로 저장됨
2. **결과 병합** (`GET /merge-results/`):
- `processed` 디렉토리에 저장된 모든 `_result.csv` 파일을 병합하여 하나의 CSV 파일로 통합합니다.
- 병합된 최종 결과는 `processed/final_result.csv`로 저장되며, 이 파일의 경로가 응답으로 반환됩니다.
- **사용 사례**:
- 다중 모델 추론 결과를 한곳에 통합하여 분석하려는 경우.
- 배치별로 나뉜 결과를 단일 파일로 결합하려는 경우.
3. **최신 결과 다운로드** (`GET /download-latest`):
- 가장 최근에 완료된 추론 결과를 다운로드
---

71
docker-compose.yml Executable file
View File

@@ -0,0 +1,71 @@
version: "3.8"
services:
llm-asyncio:
build:
context: .
dockerfile: Dockerfile
shm_size: "1000gb"
volumes:
- ./workspace:/opt/workspace/
- ./cache:/root/.cache/
environment:
PYTORCH_CUDA_ALLOC_CONF: expandable_segments:True
PYTHONPATH: /opt/workspace/
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: "all"
capabilities: [gpu]
container_name: llm-asyncio
ports:
- "8000:8000"
networks:
- llm-network
entrypoint: >
/bin/bash -c "
uvicorn main:app --reload --host 0.0.0.0 --port 8000
"
tty: true
redis:
image: redis:latest
container_name: redis-server
ports:
- "6379:6379"
restart: always
networks:
- llm-network
worker:
build:
context: .
dockerfile: Dockerfile
shm_size: "1000gb"
volumes:
- ./workspace:/opt/workspace/
- ./cache:/root/.cache/
environment:
PYTORCH_CUDA_ALLOC_CONF: expandable_segments:True
PYTHONPATH: /opt/workspace/
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: "all"
capabilities: [gpu]
networks:
- llm-network
entrypoint: >
/bin/bash -c "
python /opt/workspace/worker.py
"
tty: true
scale: 2
networks:
llm-network:
driver: bridge

46
pyproject.toml Executable file
View File

@@ -0,0 +1,46 @@
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
line-length = 120
indent-width = 4
[lint]
# 기본적으로 Pyflakes('F')와 pycodestyle('E') 코드의 하위 집합을 활성화합니다.
select = ["E4", "E7", "E9", "F"]
ignore = []
# 활성화된 모든 규칙에 대한 수정 허용.
fixable = ["ALL"]
unfixable = []
# 밑줄 접두사가 붙은 경우 사용하지 않는 변수를 허용합니다.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
[format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
docstring-code-format = false
docstring-code-line-length = "dynamic"

7
requirements.txt Executable file
View File

@@ -0,0 +1,7 @@
fastapi
uvicorn
pandas
redis
rq
python-multipart
vllm==0.6.2

77
workspace/ADR-0.md Executable file
View File

@@ -0,0 +1,77 @@
# RESTful API 기반의 LLM 추론 아키텍처
FastAPI를 이용해 클라이언트가 RESTful API를 통해 서비스와 상호작용하도록 설계되었습니다.
- **독립성**: 주요 기능(파일 업로드, 모델 추론, 작업 상태 확인)이 각기 독립된 API 엔드포인트로 구현되었습니다.
---
## 아키텍처 구성 요소
### (1) API 계층
- **FastAPI**:
- 클라이언트와 직접 상호작용하는 API 레이어.
- 비동기 요청을 처리하고 데이터 유효성 검사를 수행.
- **주요 엔드포인트**:
- `/start-inference/`: CSV 및 모델 리스트 파일 업로드 후 추론 작업 시작.
- `/merge-results/`: 배치별로 나뉜 결과를 단일 파일로 결합
- `/download-latest/`: 가장 최근에 완료된 결과 파일 다운로드.
### (2) 작업 처리 계층
- **RQ (Redis Queue)**:
- 긴 작업(예: 대규모 LLM 추론)을 처리하기 위한 비동기 태스크 큐.
- 작업 상태를 추적 가능하며, 확장성을 제공.
- **주요 태스크**:
- `run_inference`: 모델 로드 및 배치 기반 추론 처리.
### (3) 모델 및 비즈니스 로직 계층
- **LLM 모듈 (`vllm`)**:
- LLM 모델(예: Llama, EXAONE)을 로드하고 추론을 수행.
- GPU 메모리 최적화 및 모델 동적 로딩 지원.
- **템플릿 포맷팅 모듈 (`template.py`)**:
- 각 모델에 특화된 입력 텍스트 포맷팅.
- 사용자가 정의한 규칙 기반의 텍스트 전처리 지원.
### (4) 데이터 계층
- **파일 저장소**:
- 업로드된 파일은 `/LLM_asyncio/uploaded` 디렉토리에 저장.
- 추론 결과는 `/LLM_asyncio/processed` 디렉토리에 저장.
- **에러 처리 데이터**:
- 추론 실패 또는 에러 발생 행은 `/LLM_asyncio/errors` 디렉토리에 저장하여 추적 가능.
---
## 상태
**제안됨**
---
## 콘텍스트
대규모 텍스트 데이터 추론은 높은 처리 비용과 시간이 소요됩니다.
이를 효율적으로 처리하기 위해 비동기 작업 관리, GPU 자원 활용 최적화, 배치 기반 설계를 도입하였습니다.
---
## 결정
- **성능**: GPU 메모리 효율을 극대화하고 배치 기반 추론으로 처리 시간을 단축.
- **확장성**: Redis Queue와 다중 워커를 활용하여 대규모 요청 처리에 유연하게 대응.
- **안정성**: 오류 행 데이터를 별도로 저장하고 추적 가능하여 처리 안정성을 보장.
- **유지보수성**: 템플릿 기반 설계를 통해 다양한 모델에 대한 호환성을 확보.
---
## 결과
- **사용자 경험 향상**:
- 작업 상태를 실시간으로 확인하고 결과를 쉽게 다운로드할 수 있어 효율적인 워크플로우 제공.
- **확장 가능성**:
- Redis와 RQ 워커를 추가하여 높은 트래픽 상황에도 대응 가능.
---
## 컴플라이언스
- **모델 및 데이터**: 각 모델 사용 규약 및 데이터 처리 가이드라인 준수.
---
## 노트
- **결정자**: 그래픽스개발팀 / 김용연
- **결정 날짜**: [2025.01.06]

183
workspace/main.py Executable file
View File

@@ -0,0 +1,183 @@
import os
import pandas as pd
from fastapi import FastAPI, UploadFile, BackgroundTasks
from fastapi.responses import JSONResponse, FileResponse
from redis import Redis
from rq import Queue
from vllm import LLM, SamplingParams
import logging
import gc
import torch
from tqdm import tqdm
import sys
sys.path.append("/opt/workspace/")
from template import LLMInference
app = FastAPI()
# Redis 설정
redis_conn = Redis(host="redis-server", port=6379, decode_responses=True)
queue = Queue("model_tasks", connection=redis_conn)
# 로깅 설정
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# FastAPI 엔드포인트: CSV 파일 및 모델 리스트 업로드 처리
@app.post("/start-inference/")
async def process_csv(input_csv: UploadFile, model_list_txt: UploadFile, background_tasks: BackgroundTasks):
logger.info(f"file_name: {input_csv},model_list_file: {model_list_txt}")
# 파일 형식 확인 및 저장
if not input_csv.filename.endswith(".csv") or not model_list_txt.filename.endswith(".txt"):
return JSONResponse(content={"error": "Invalid file format."}, status_code=400)
file_path = f"uploaded/{input_csv.filename}"
model_list_path = f"uploaded/{model_list_txt.filename}"
os.makedirs("uploaded", exist_ok=True)
with open(file_path, "wb") as f:
f.write(await input_csv.read())
with open(model_list_path, "wb") as f:
f.write(await model_list_txt.read())
df = pd.read_csv(file_path, encoding="euc-kr")
batch_size = 10
job_ids = []
# 데이터를 batch_size로 나누어 작업 큐에 추가
for i in range(0, len(df), batch_size):
batch_file_path = file_path.replace(".csv", f"_batch_{i}_{i+batch_size}.csv")
df.iloc[i:i+batch_size].to_csv(batch_file_path, index=False, encoding="utf-8")
job = queue.enqueue(run_inference, batch_file_path, model_list_path, job_timeout=1800)
job_ids.append(job.id)
logger.info(f"Jobs enqueued: {job_ids}")
return {"job_ids": job_ids, "status": "queued"}
def chat_formating(input_sentence: str, model_name: str):
if "llama" in model_name:
hidden_prompt = LLMInference.llama_template()
elif "gemma" in model_name:
hidden_prompt = LLMInference.gemma_template()
elif "exaone" in model_name:
hidden_prompt = LLMInference.exaone_template()
else:
raise ValueError("Unknown model name: " + model_name)
formated_sentence = hidden_prompt.format(input_sent=input_sentence)
logger.info(f"Sentence: {formated_sentence}")
return formated_sentence
# 모델 추론 함수
def run_inference(batch_file_path: str, model_list_path: str):
try:
# 워커 ID 확인
worker_id = os.environ.get("HOSTNAME", "Unknown Worker")
logger.info(f"Worker {worker_id} started inference for batch file: {batch_file_path}")
# 모델 리스트 읽기
with open(model_list_path, "r") as f:
model_list = [line.strip() for line in f.readlines()]
if not model_list:
raise ValueError("The model list file is empty.")
# 배치 데이터 읽기
df = pd.read_csv(batch_file_path, encoding="utf-8")
if "input" not in df.columns:
raise ValueError("The input CSV must contain a column named 'input'.")
# 추론 수행
for model in model_list:
logger.info(f"Worker {worker_id} loading model: {model}")
try:
llm = LLM(model)
torch.cuda.empty_cache()
logger.info(f"Worker {worker_id} loaded model {model} successfully.")
except Exception as e:
logger.error(f"Worker {worker_id} error loading model {model}: {e}")
continue
sampling_params = SamplingParams(max_tokens=50, temperature=0.7, top_p=0.9, top_k=50)
responses = []
# tqdm 추가: 워커별 모델 진행 상태 표시
with tqdm(total=len(df), desc=f"[{worker_id}] Model: {model}") as pbar:
model_name = model.split("/")[-1]
for _, row in df.iterrows():
try:
input_text = chat_formating(input_sentence=row["input"], model_name=model_name)
response = llm.generate(input_text, sampling_params)[0].outputs[0].text.strip()
logger.info(f"Model: {model}, Input: {input_text}, Output: {response}")
responses.append(response)
except Exception as e:
logger.error(f"Worker {worker_id} error during inference for model {model}, row {row.name}: {e}")
error_rows = pd.concat([error_rows, pd.DataFrame([row])], ignore_index=True)
responses.append(None)
finally:
pbar.update(1)
# 결과 추가
df[model_name] = responses
del llm
torch.cuda.empty_cache()
gc.collect()
# 배치 결과 저장
output_path = batch_file_path.replace("uploaded", "processed").replace(".csv", "_result.csv")
os.makedirs("processed", exist_ok=True)
df.to_csv(output_path, index=False, encoding="utf-8")
logger.info(f"Worker {worker_id} inference completed for batch. Result saved to: {output_path}")
# 에러 행 저장
if not error_rows.empty:
error_path = batch_file_path.replace("uploaded", "errors").replace(".csv", "_errors.csv")
os.makedirs("errors", exist_ok=True)
error_rows.to_csv(error_path, index=False, encoding="utf-8")
logger.info(f"Error rows saved to: {error_path}")
return output_path
except Exception as e:
logger.error(f"Worker {worker_id} error during inference: {e}")
raise
@app.get("/merge-results/")
def merge_results():
try:
processed_dir = "processed"
all_files = [os.path.join(processed_dir, f) for f in os.listdir(processed_dir) if f.endswith("_result.csv")]
combined_df = pd.concat([pd.read_csv(f, encoding="utf-8") for f in all_files], ignore_index=True)
final_output_path = os.path.join(processed_dir, "final_result.csv")
combined_df.to_csv(final_output_path, index=False, encoding="utf-8")
logger.info(f"Final merged result saved to: {final_output_path}")
return {"final_result_path": final_output_path}
except Exception as e:
logger.error(f"Error during merging results: {e}")
return JSONResponse(content={"error": "Failed to merge results."}, status_code=500)
# 결과 파일 다운로드
@app.get("/download-latest", response_class=FileResponse)
def download_latest_file():
try:
# processed 디렉토리 경로
directory = "LLM_asyncio/processed"
csv_files = [os.path.join(directory, f) for f in os.listdir(directory) if f.endswith(".csv")]
if not csv_files:
return JSONResponse(content={"error": "No CSV files found in the processed directory."}, status_code=404)
latest_file = max(csv_files, key=os.path.getctime)
logger.info(f"Downloading latest file: {latest_file}")
return FileResponse(latest_file, media_type="application/csv", filename=os.path.basename(latest_file))
except Exception as e:
logger.error(f"Error during file download: {e}")
return JSONResponse(content={"error": "Failed to download the latest file."}, status_code=500)

15
workspace/setting.json Executable file
View File

@@ -0,0 +1,15 @@
{
"terminal.integrated.fontFamily": "OxygenMono, D2CodingLigatureMono",
"terminal.integrated.fontWeightBold": "bold",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit",
"editor.insertSpaces": "explicit"
}
}
}

18
workspace/template.py Executable file
View File

@@ -0,0 +1,18 @@
class LLMInference:
def __init__(self):
pass
@staticmethod
def llama_template():
hidden_prompt = "<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n\n친절한 건설안전전문가로서 반드시 [규칙]을 바탕으로 상대방의 요청에 답변을 생성해주세요.\n[규칙]\n1. 한 문장으로 핵심만 요약해서 답변을 생성합니다.\n2. 모든 답변은 반드시 한 문장의 한국어(Korean)으로 작성합니다.\n3. 생성된 답변의 신뢰성은 1(낮음)~5(높음)으로 평가합니다.4. 답변 형식은 다음과 같습니다. 답변.\n(신뢰성:3)<|eot_id|>\n<|start_header_id|>user<|end_header_id|>\n\n{input_sent}<|eot_id|><|start_header_id|>assistant<|end_header_id|>"
return hidden_prompt
@staticmethod
def gemma_template():
hidden_prompt = "<bos><start_of_turn>user\n친절한 건설안전전문가로서 반드시 [규칙]을 바탕으로 상대방의 요청에 답변을 생성해주세요.\n[규칙]\n1. 한 문장으로 핵심만 요약해서 답변을 생성합니다.\n2. 모든 답변은 반드시 한 문장의 한국어(Korean)으로 작성합니다.\n3. 생성된 답변의 신뢰성은 1(낮음)~5(높음)으로 평가합니다.4. 답변 형식은 다음과 같습니다. 답변.\n(신뢰성:3)\n\n{input_sent}<end_of_turn>\n<start_of_turn>model"
return hidden_prompt
@staticmethod
def exaone_template():
hidden_prompt = "[|system|]친절한 건설안전전문가로서 반드시 [규칙]을 바탕으로 상대방의 요청에 답변을 생성해주세요.\n[규칙]\n1. 한 문장으로 핵심만 요약해서 답변을 생성합니다.\n2. 모든 답변은 반드시 한 문장의 한국어(Korean)으로 작성합니다.\n3. 생성된 답변의 신뢰성은 1(낮음)~5(높음)으로 평가합니다.4. 답변 형식은 다음과 같습니다. 답변.\n(신뢰성:3)[|endofturn|]\n\n[|user|]{input_sent}[|endofturn|]\n[|assistant|]"
return hidden_prompt

12
workspace/tests/example.py Executable file
View File

@@ -0,0 +1,12 @@
from vllm import LLM
"""
- max_model_len : 모델이 지원해주는 최대 시퀀스 길이입니다. 더 짧게도 가능하며, 모델이 지원해주는 가장 큰 값으로도 가능합니다. 저는 2048로 임의로 셋팅했습니다.
- tensor_parallel_size : 앞서 vLLM을 소개할 때 vLLM은 분산 추론(distrubuted inference)를 지원합니다. 더 자세히 말하면 분산 텐서 병렬(distributed tenwor parallel)기반 inference 및 serving을 지원하는 것입니다. 이때 vLLM은 Ray를 활용해 분산 런타임을 지원합니다. 따라서 Python Ray가 설치되어 있어야하며, 이를 활용하면 쉽고 간단하게 gpu 등을 병렬로 처리할 수 있습니다. 저는 1이라고 셋팅해서 1개의 gpu를 사용하도록 설정했습니다.
이렇게 올라온 모델을 사용해 이제 텍스트를 생성하는 text generate를 실행해보겠습니다. 다음과 같이 실행하면 됩니다.
"""
llm = LLM(model="yanolja/EEVE-Korean-Instruct-2.8B-v1.0", max_model_len=2048, tensor_parallel_size=1) # 모델로드
requestoutput = llm.generate("안녕하십니까. 기상 캐스터 어시스턴트입니다. 오늘의 날씨는") # 입력문장
print(requestoutput)

12
workspace/worker.py Executable file
View File

@@ -0,0 +1,12 @@
from redis import Redis
from rq import Worker, Queue
# Redis 설정
redis_conn = Redis(host="redis-server", port=6379, decode_responses=False)
# 작업 큐
queue = Queue("model_tasks", connection=redis_conn)
if __name__ == "__main__":
worker = Worker([queue], connection=redis_conn)
worker.work()