반응형

WSL에서 vLLM을 사용해보자.

 

■ WSL을 설치한다. 운영체제는 우분투 24.04를 설치한다.

우분투 26.04는 파이썬 3.14가 설치되어 있는데 현재 파이토치가 파이썬 3.13까지만 지원하기 때문에 파이토치 설치가 귀찮아진다.

 

■ pip3를 설치한다.

sudo apt update

sudo apt upgrade

sudo apt install python3-pip

pip3 --version (정상 설치 확인)

 

■ 파이썬 가상환경을 만들어 주는 venv를 설치한다.

sudo apt install python3-venv

 

가상환경 생성 및 확인

 

가상환경 활성화

■ venv-vllm 디렉토리에 가상환경을 생성하고 활성화한다.

python3 -m venv venv-vllm

source venv-vllm/bin/activate

 

 

■ 파이토치를 설치한다. (CUDA 12.4)
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

 

vllm을 설치하면 많은 프로그램이 설치된다.

■ vLLM을 설치한다.

pip3 install vllm

 

■ vLLM을 실행하기 위해 CUDA Toolkit을 설치한다. (Cuda Toolkit 12.4)

wget https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_550.54.14_linux.run

sudo sh cuda_12.4.0_550.54.14_linux.run

위 명령을 실행하면 잠시 멈춘 것처럼 시간이 좀 걸린다. 그리고 accept를 입력하고 다음 화면에서 CUDA Toolkit 12.4가 선택된 상태에서 Install을 선택한다. Driver는 옵션에 없었지만 있다면 선택해제한다. Driver를 설치하는 것이 아니다.

 

설치가 끝나면 아래와 같이 환경 변수를 등록한다.

nano ~/.bashrc 실행하고 파일 끝에 아래 내용 추가

export PATH=/usr/local/cuda-12.4/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

source ~/.bashrc 실행 (이 명령을 실행하고 나면 파이썬 가상환경이 풀린다)
nvcc --version 명령을 실행하면 버전이 표시된다.

 

 

■ vLLM에서 LGAI EXAON 모델을 사용하는 예

import os

# vLLM에서 FlashInfer 기반의 고속 셈플링(Sampling) 기능을 끄고, 안정적인 PyTorch 네이티브(기본)
# 셈플링 방식으로 되돌리기(Fallback).
os.environ["VLLM_USE_FLASHINFER_SAMPLER"] = "0"
# vLLM은 내부적으로 가속 연산을 위해 FlashInfer라는 라이브러리를 사용하는데 이 라이브러리는
# NVIDIA Turing 아키텍처(Compute Capability sm75) 이상의 GPU에서만 작동한다. 사용하는 그래픽카드가
# 소형 모델 구동용(VRAM 8GB 수준)이면서 sm75보다 낮은 구형 아키텍처(예: GTX 10시리즈인 Pascal 아키텍처 sm61 등)
# 이면 문제가 발생한다.
# 해결 방법: vllm 패키지를 로드하기 전에 os.environ을 통해 FlashInfer 가속 비활성화하기

from vllm import LLM, SamplingParams

def main():
    # 모델 로드
    llm = LLM(
        model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ", 
        trust_remote_code=True, 
        gpu_memory_utilization=0.7
    )
# vLLM은 기본적으로 GPU 메모리의 90%(0.9)를 미리 할당하기 때문에, 낮은 성능의 GPU에서는
# OOM(메모리 부족) 에러를 방지하거나 다른 프로세스와 GPU를 나누어 쓰려면 gpu_memory_utilization
# 옵션이 필수적이다.

    # 파라미터 설정
    sampling_params = SamplingParams(
        temperature=0.0,
        top_p=0.95,
        max_tokens=1024,
        repetition_penalty=1.1
    )

    # 질문 리스트
    raw_questions = [
        "대한민국의 수도는 어디인가요?",
        "인공지능은 무엇인가요?"
    ]

    # vLLM 공식 Chat Template 적용
    prompts = []
    tokenizer = llm.get_tokenizer()
    for q in raw_questions:
        messages = [{"role": "user", "content": q}]
        formatted_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
        prompts.append(formatted_prompt)

    # 텍스트 생성
    outputs = llm.generate(prompts, sampling_params)

    # 결과 출력 (내장함수로 태그 뒤쪽만 추출)
    for i, output in enumerate(outputs):
        prompt = raw_questions[i]
        generated_text = output.outputs[0].text
        
        # </thought> 태그가 존재한다면 그 태그 뒷부분([1])만 추출한다
        if "</thought>" in generated_text:
            clean_answer = generated_text.split("</thought>")[1]
        else:
            clean_answer = generated_text

        print(f"==========================================")
        print(f"질문: {prompt}")
        print(f"답변: {clean_answer.strip()}")
        print(f"==========================================\n")

if __name__ == "__main__":
    main()

 

EXAON 모델이 다운로드되어 있지 않다면 다운로드하느라 이 화면에서 시간이 좀 걸린다.

위 코드에서는 토큰 없이 허가되지 않은 접근을 하고 있기 때문에 다운로드가 느리다고 한다. 토큰을 받고 빠르게 다운로드하는 방법은 맨 아래 내용을 참고하자.

 

최종 결과 화면

 

 

다시 실행하면 모델을 다운로드하는 과정이 없다.

 

import os
os.environ["VLLM_USE_FLASHINFER_SAMPLER"] = "0"

from vllm import LLM, SamplingParams

def main():
    llm = LLM(
        model="LGAI-EXAONE/EXAONE-Deep-2.4B-AWQ", 
        trust_remote_code=True, 
        gpu_memory_utilization=0.7
    )

    sampling_params = SamplingParams(
        temperature=0.0,
        top_p=0.95,
        max_tokens=1024,
        repetition_penalty=1.1
    )

    raw_questions = [
        "대한민국의 수도는 어디인가요?",
        "인공지능에 대해 한 문장으로 요약해줘."
    ]

    prompts = []
    tokenizer = llm.get_tokenizer()
    for q in raw_questions:
        messages = [{"role": "user", "content": q}]
        formatted_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
        prompts.append(formatted_prompt)

    outputs = llm.generate(prompts, sampling_params)

    for i, output in enumerate(outputs):
        prompt = raw_questions[i]
        generated_text = output.outputs[0].text
        
        if "</thought>" in generated_text:
            clean_answer = generated_text.split("</thought>")[1]
        else:
            clean_answer = generated_text

        print(f"==========================================")
        print(f"질문: {prompt}")
        print(f"답변: {clean_answer.strip()}")
        print(f"==========================================\n")

    print(outputs)

if __name__ == "__main__":
    main()

 

짧은 답변이 나오도록 질문을 바꾸고 전체 결과가 출력되도록 print(outputs) 명령을 추가했다.

 

마지막에 outputs의 내용이 출력되었다.

 

[RequestOutput(request_id=0, prompt='[|system|][|endofturn|]\n[|user|]대한민국의 수도는 어디인가요?\n[|assistant|]<thought>\n', prompt_token_ids=[420, 453, 47982, 453, 422, 361, 560, 420, 453, 14719, 453, 422, 36437, 730, 8952, 657, 4083, 798, 7799, 392, 560, 420, 453, 1167, 8659, 453, 422, 389, 52040, 391, 560], encoder_prompt=None, encoder_prompt_token_ids=None, prompt_logprobs=None, outputs=[CompletionOutput(index=0, text='\n</thought>\n\n 대한민국의 수도는 서울(Seoul)입니다.', token_ids=[560, 2240, 52040, 391, 560, 560, 9971, 730, 8952, 657, 2879, 369, 8078, 10103, 370, 10996, 375, 361], routed_experts=None, cumulative_logprob=None, logprobs=None, finish_reason=stop, stop_reason=None)], finished=True, metrics=None, lora_request=None, num_cached_tokens=0), RequestOutput(request_id=1, prompt='[|system|][|endofturn|]\n[|user|]인공지능에 대해 한 문장으로 요약해줘.\n[|assistant|]<thought>\n', prompt_token_ids=[420, 453, 47982, 453, 422, 361, 560, 420, 453, 14719, 453, 422, 41595, 22427, 2373, 2409, 764, 13742, 13456, 16399, 999, 15887, 375, 560, 420, 453, 1167, 8659, 453, 422, 389, 52040, 391, 560], encoder_prompt=None, encoder_prompt_token_ids=None, prompt_logprobs=None, outputs=[CompletionOutput(index=0, text='\n</thought>\n\n인간의 intelligence를 computer로 구현하는 것.', token_ids=[560, 2240, 52040, 391, 560, 560, 25284, 730, 13887, 4605, 6458, 715, 19495, 1130, 657, 924, 375, 361], routed_experts=None, cumulative_logprob=None, logprobs=None, finish_reason=stop, stop_reason=None)], finished=True, metrics=None, lora_request=None, num_cached_tokens=0)]

 

그런데 이렇게 진행하는 모델은 내부에 config.json 파일이 있어야 한다.

예를 들어 EXAON은 config.json 파일이 있지만 Bllossom은 없다. 그래서 Bllossom은 사용할 수 없다. (다른 방법이 있겠지?)

 

config.json 파일이 없다.

 

config.json 파일이 있다.

 

config.json 파일이 있다.

 

 

※ 참고

■ Hugging Face에서 (Bllossom) 모델 다운로드하기

hf 명령어를 사용한다. huggingface-cli는 더 이상 사용하지 않는다. 위 과정을 진행했다면 hf(Hugging Face Hub CLI)는 이미 설치되어 있다.

 

hf auth login
토큰을 선택하고 Profile - Settings - Access Tokens에서 생성한 토큰을 붙여넣는다.
로그인이 완료된다.

로그인 확인하기
hf auth whoami

Bllossom 모델 다운로드하기
hf download Bllossom/llama-3.2-Korean-Bllossom-3B-gguf-Q4_K_M
로그인하지 않은 상태에서 다운로드하면 느리게 진행되지만 로그인을 했으므로 빠르게 진행된다.

모델 다운로드 위치
/home/sean/.cache/huggingface/hub/
예) /home/sean/.cache/huggingface/hub/models--Bllossom--llama-3.2-Korean-Bllossom-3B-gguf-Q4

 

※ 참고

파일 익스플로러 주소창에 \\wsl$를 입력하면 WSL에 설치된 운영체제의 파일에 접근할 수 있다. 물론 왼쪽 패널에 Linux - Ubuntu-24.04로 접근해도 된다.

 

※ 참고

vLLM Documentation

vLLM GitHub

 

반응형

'AI, ML, DL' 카테고리의 다른 글

[YOLO] YOLO-World  (0) 2026.06.16
[Ollama] Ollama Backend Serve 백엔드 실행  (0) 2026.06.14
[Ollama] llava 동영상 분석  (0) 2026.06.14
[Ollama] Hugging Face 모델 설치 (Bllossom)  (0) 2026.06.14
[Ollama] Ollama with Python 2  (0) 2026.06.14
Posted by J-sean
: