openwebui/qwen/SETUP_MLX.md
MyeonghoeLee 3660556a72 기존 Qwen 파일 qwen/ 디렉터리로 이동, Gemma 4 (31B-IT) Ollama 셋업 추가
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 16:29:32 +09:00

405 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Qwen3.5 + Open WebUI — vllm-mlx (Mac 최적화)
> 환경: MacBook Pro M4 Pro 48GB / Docker Desktop / Python 3.10+
---
# 목차
1. [환경 준비](#1-환경-준비)
2. [서버 시작](#2-서버-시작)
3. [Open WebUI 연결](#3-open-webui-연결)
4. [종료 / 재시작](#4-종료--재시작)
5. [파라미터 레퍼런스](#5-파라미터-레퍼런스)
6. [트러블슈팅](#6-트러블슈팅)
---
# 1. 환경 준비
> ⚠️ conda 환경에서 실행하면 MPICH와 MLX가 충돌합니다. 반드시 별도 venv에서 실행하세요.
```bash
conda deactivate
python3 -m venv ~/mlx-env
source ~/mlx-env/bin/activate
pip install git+https://github.com/waybarrios/vllm-mlx.git
```
---
# 2. 서버 시작
모델은 첫 시작 시 HuggingFace에서 자동 다운로드됩니다.
## 텍스트 전용
```bash
source ~/mlx-env/bin/activate
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
--port 8090 \
--max-tokens 8192 \
--default-temperature 0.7 \
--default-top-p 0.9
```
## 텍스트 + 이미지 (멀티모달)
```bash
source ~/mlx-env/bin/activate
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
--port 8090 \
--max-tokens 8192 \
--default-temperature 0.7 \
--default-top-p 0.9 \
--mllm
```
> `--mllm` 플래그 하나로 이미지 입력이 활성화됩니다.
>
> 포그라운드로 실행됩니다. **새 터미널 탭**을 열어서 다음 단계를 진행하세요.
## 용도별 추천 설정
```bash
# 문서 생성 (보수적)
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
--port 8090 --max-tokens 8192 \
--default-temperature 0.2 --default-top-p 0.95 --mllm
# 대화 / 창의적 응답
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
--port 8090 --max-tokens 8192 \
--default-temperature 1.0 --default-top-p 0.8 --mllm
# 동시 요청 최적화 (여러 사용자)
# ⚠️ --continuous-batching은 단일 사용자일 때 오히려 느려질 수 있음
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
--port 8090 --max-tokens 8192 \
--default-temperature 0.7 --mllm --continuous-batching
```
---
# 3. Open WebUI 연결
### 3-1. 서버 동작 확인
```bash
curl http://localhost:8090/v1/models
curl http://localhost:8090/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "mlx-community/Qwen3.5-35B-A3B-4bit",
"max_tokens": 8192,
"messages": [{"role": "user", "content": "안녕하세요"}]
}'
```
### 3-2. Open WebUI 실행
```bash
cd ~/PyCharmMiscProject/openwebui
docker compose -f docker-compose.mlx.yml up -d
```
> `docker-compose.mlx.yml`이 없으면 [부록](#부록-docker-composemlxyml-생성)을 참고하세요.
### 3-3. 브라우저 접속
```
http://localhost:3000
```
1. 첫 접속 시 회원가입 (로컬 전용, 첫 계정 = admin)
2. **설정 → Connections** → OpenAI API 연결 확인
- URL: `http://host.docker.internal:8090/v1`
- API Key: `none`
3. 상단 모델 선택 후 채팅 시작
---
# 4. 종료 / 재시작
### 종료
```bash
# vllm-mlx 서버: Ctrl+C
# Open WebUI
cd ~/PyCharmMiscProject/openwebui
docker compose -f docker-compose.mlx.yml down
```
### 재시작
```bash
# 터미널 1: 서버
source ~/mlx-env/bin/activate
vllm-mlx serve mlx-community/Qwen3.5-35B-A3B-4bit \
--port 8090 --max-tokens 8192 \
--default-temperature 0.7 --default-top-p 0.9 --mllm
# 터미널 2: Open WebUI
cd ~/PyCharmMiscProject/openwebui
docker compose -f docker-compose.mlx.yml up -d
```
---
# 5. 파라미터 레퍼런스
## 서버 시작 플래그
### 토큰 제한
| 구분 | 의미 | 설정 방법 |
|------|------|-----------|
| **max_tokens (출력)** | 모델이 생성하는 최대 토큰 수 | `--max-tokens 8192` |
| **context length (입력)** | 모델이 받을 수 있는 최대 입력 토큰 수 | 모델 자체 설정 (Qwen3.5: 최대 262K) |
> 입력 길이를 제한하려면 `--use-paged-cache`와 `--max-cache-blocks`를 조합합니다.
> `max-cache-blocks × 64(블록 사이즈) = 최대 입력 토큰 수`
>
> 예: 입력 8192 토큰 제한 → `--use-paged-cache --max-cache-blocks 128`
### 생성 파라미터
| 플래그 | 기본값 | 설명 |
|--------|--------|------|
| `--max-tokens` | 32768 | 최대 **출력** 토큰 수 (thinking + 응답 합산) |
| `--default-temperature` | 모델 기본값 | Temperature (0.0~2.0) |
| `--default-top-p` | 모델 기본값 | Nucleus Sampling |
| `--reasoning-parser qwen3` | off | thinking 내용을 별도 필드로 추출 |
### 모달리티
| 플래그 | 기본값 | 설명 |
|--------|--------|------|
| `--mllm` | off | 멀티모달 (이미지 입력) 활성화 |
### 성능
| 플래그 | 기본값 | 설명 |
|--------|--------|------|
| `--continuous-batching` | off | 동시 요청 배칭 (단일 사용자 시 오히려 느림) |
| `--prefill-step-size` | 2048 | 프리필 처리 단위 |
| `--stream-interval` | 1 | 스트리밍 배치 토큰 수 (1=부드러움, 클수록 처리량↑) |
### 메모리
| 플래그 | 기본값 | 설명 |
|--------|--------|------|
| `--kv-cache-quantization` | off | KV 캐시 양자화 활성화 (메모리 절약) |
| `--kv-cache-quantization-bits` | 8 | KV 캐시 양자화 비트 (4 또는 8) |
| `--cache-memory-percent` | 0.20 | RAM의 몇 %를 캐시에 할당 |
### 서버
| 플래그 | 기본값 | 설명 |
|--------|--------|------|
| `--port` | - | 서버 포트 |
| `--host` | - | 서버 호스트 |
| `--api-key` | - | API 키 (미설정 시 인증 없음) |
| `--rate-limit` | 0 | 분당 요청 제한 (0 = 무제한) |
| `--timeout` | 300 | 요청 타임아웃 (초) |
## 각 파라미터가 하는 일
### `--default-temperature` (Temperature)
모델의 출력 랜덤성을 조절합니다.
```
0.0 ←──────────────────────────→ 2.0
결정적 랜덤
(같은 질문 = 같은 답) (창의적, 횡설수설 가능)
```
- `0.0` → 항상 가장 확률 높은 토큰 선택
- `0.7` → 일반 대화에 적합
- `1.0+` → 창의적 글쓰기
### `--default-top-p` (Nucleus Sampling)
누적 확률 상위 p%에 드는 토큰만 후보로 남깁니다.
```
예: top-p = 0.9
[토큰A: 50%] [토큰B: 30%] [토큰C: 10%] | [토큰D: 5%] [토큰E: 3%] ...
─────────── 상위 90% (후보) ────────── ──── 제외 ────
```
- `0.9` → 희귀한 토큰 배제, 안정적
- `1.0` → 필터 없음
### `--max-tokens` (출력 토큰 제한)
한 요청에서 모델이 **생성(출력)**할 수 있는 최대 토큰 수입니다.
- Qwen3.5는 **thinking 토큰 + 응답 토큰이 합산**됩니다
- 너무 크면 thinking이 끝없이 돌 수 있음
- **8192 권장**
> 입력 토큰 제한(context length)은 별도 플래그가 없으며, 모델의 context window(Qwen3.5: 최대 262K)를 따릅니다. 메모리가 부족하면 `--kv-cache-quantization`이나 `--cache-memory-mb`로 간접 제한하세요.
### `--mllm`
멀티모달 모드를 활성화합니다. 이 플래그가 없으면 텍스트만 처리합니다.
### `--continuous-batching`
여러 요청을 동시에 처리합니다. **단일 사용자일 때는 오히려 느려지므로**, 동시 사용자가 있을 때만 사용하세요.
### `--reasoning-parser qwen3`
Qwen3.5의 `<think>...</think>` thinking 내용을 별도 `reasoning_content` 필드로 추출합니다. Open WebUI에서 thinking 과정을 보고 싶을 때 유용합니다.
---
# 6. 트러블슈팅
## 빠른 참조
| 증상 | 원인 | 해결 |
|------|------|------|
| thinking만 하고 응답 없음 | `--max-tokens`가 너무 큼 | 8192로 설정 후 서버 재시작 |
| conda에서 abort / MPI 에러 | MPICH 충돌 | `conda deactivate` 후 venv에서 실행 |
| 이미지 입력 안 됨 | `--mllm` 미설정 | 서버 시작 시 `--mllm` 추가 |
| 모델이 안 보임 | 서버 미실행 | `curl http://localhost:8090/v1/models` 확인 |
| 컨테이너 안 뜸 | Docker 문제 | `docker logs open-webui-mlx` 확인 |
| 포트 충돌 | 이미 사용 중 | `lsof -i :3000` / `lsof -i :8090` |
| 요청 타임아웃 | 기본 300초 초과 | `--timeout 600` 으로 늘리기 |
| 이미지 입력 시 PyTorch 텐서 에러 | transformers fast image processor 호환성 문제 | 아래 **[이미지 프로세서 호환성 문제](#이미지-프로세서-호환성-문제-transformers--mlx_vlm)** 참고 |
| 서버가 Ctrl+C로 안 꺼짐 | 프로세스가 응답 중 | `lsof -ti :8090 \| xargs kill -9` |
---
## 이미지 프로세서 호환성 문제 (transformers + mlx_vlm)
### 증상
`--mllm` 모드에서 이미지를 입력하면 아래 에러가 발생합니다:
```
ValueError: Failed to process inputs with error: Only returning PyTorch tensors is currently supported.
```
또는 Open WebUI에서:
```
Response payload is not completed: <TransferEncodingError: 400, message='Not enough data to satisfy transfer length header.'>
```
### 원인
`vllm-mlx`는 내부적으로 `mlx_vlm`을 사용하여 이미지를 처리합니다.
`mlx_vlm``transformers` 라이브러리의 이미지 프로세서를 호출하는데,
`transformers >= 5.x`에서는 **Fast Image Processor**가 기본으로 로드됩니다.
```
요청 흐름:
Open WebUI → vllm-mlx → mlx_vlm → transformers (이미지 프로세서) → 에러
```
문제의 핵심:
| | Fast 버전 | Slow 버전 |
|---|---|---|
| 파일 | `image_processing_qwen2_vl_fast.py` | `image_processing_qwen2_vl.py` |
| 반환 형식 | **PyTorch 텐서만** 지원 | numpy 등 다양한 형식 지원 |
| mlx_vlm 호환 | ❌ | ✅ |
`transformers`는 자동으로 fast 버전을 우선 로드하지만,
`mlx_vlm`은 PyTorch 텐서가 아닌 numpy/MLX 배열을 기대하므로 충돌이 발생합니다.
### 해결 방법
fast 파일을 slow 클래스로 리다이렉트합니다.
**1. fast 파일 위치 확인:**
```bash
# venv 경로에 따라 다를 수 있음
FAST_FILE="$(python -c "import transformers; import os; print(os.path.join(os.path.dirname(transformers.__file__), 'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))")"
echo "$FAST_FILE"
```
**2. 원본 백업:**
```bash
cp "$FAST_FILE" "${FAST_FILE}.bak"
```
**3. fast 파일을 slow 버전으로 리다이렉트:**
```bash
cat > "$FAST_FILE" << 'EOF'
"""
Fast Image processor class for Qwen2-VL.
Patched: mlx_vlm 호환을 위해 slow 버전으로 폴백합니다.
원본: image_processing_qwen2_vl_fast.py.bak
"""
from .image_processing_qwen2_vl import Qwen2VLImageProcessor as Qwen2VLImageProcessorFast
__all__ = ["Qwen2VLImageProcessorFast"]
EOF
```
**4. 서버 재시작 후 이미지 테스트**
### 복원 방법
패치를 되돌리려면:
```bash
FAST_FILE="$(python -c "import transformers; import os; print(os.path.join(os.path.dirname(transformers.__file__), 'models/qwen2_vl/image_processing_qwen2_vl_fast.py'))")"
cp "${FAST_FILE}.bak" "$FAST_FILE"
```
### 언제 이 패치가 필요 없어지나
- `mlx_vlm`이 fast image processor를 지원하게 업데이트되면
- `transformers`가 PyTorch 외의 텐서 반환을 지원하면
- `vllm-mlx`가 자체 이미지 프로세싱을 구현하면
`pip install --upgrade mlx-vlm` 후 이미지 테스트가 정상이면 패치를 복원해도 됩니다.
---
# 부록: docker-compose.mlx.yml 생성
> `~/PyCharmMiscProject/openwebui/docker-compose.mlx.yml`이 없을 때만 실행하세요.
```bash
mkdir -p ~/PyCharmMiscProject/openwebui
cd ~/PyCharmMiscProject/openwebui
cat > docker-compose.mlx.yml << 'EOF'
services:
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui-mlx
ports:
- "3000:8080"
environment:
- OPENAI_API_BASE_URL=http://host.docker.internal:8090/v1
- OPENAI_API_KEY=none
- OLLAMA_BASE_URL=
volumes:
- open-webui-mlx-data:/app/backend/data
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
volumes:
open-webui-mlx-data:
EOF
```