Austing Dong commited on
Commit
3bd29bb
·
1 Parent(s): d582a9c

fixed docker issues

Browse files
Files changed (10) hide show
  1. .dockerignore +17 -0
  2. Dockerfile +50 -31
  3. README.md +28 -1
  4. app.py +6 -2
  5. compose.yaml +28 -0
  6. demo/model_utils.py +31 -13
  7. docker/verify_runtime.py +104 -0
  8. pyproject.toml +16 -18
  9. requirements-gradio.txt +3 -11
  10. requirements.txt +22 -23
.dockerignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .git
2
+ .gitattributes
3
+ .gradio
4
+ .idea
5
+ .vscode
6
+ **/__pycache__
7
+ **/*.py[cod]
8
+ *.egg-info
9
+ **/*.egg-info
10
+ **/.ipynb_checkpoints
11
+ *.ipynb
12
+ results
13
+ .pytest_cache
14
+ .ruff_cache
15
+ .mypy_cache
16
+ .venv
17
+ venv
Dockerfile CHANGED
@@ -1,41 +1,60 @@
1
- FROM python:3.10
 
 
2
 
3
- COPY ./requirements-gradio.txt /code/requirements-gradio.txt
 
4
 
5
- # Install system dependencies and create user
6
- RUN apt-get update && apt-get install -y --no-install-recommends \
7
- && useradd -m -u 1000 user \
8
- && rm -rf /var/lib/apt/lists/*
9
-
10
- # Install OpenGL and other dependencies required for OpenCV
11
- RUN apt-get update && apt-get install -y \
12
- libgl1-mesa-glx \
13
- libglib2.0-0 \
14
- && rm -rf /var/lib/apt/lists/*
15
-
16
- # Switch to "user" before installing dependencies
17
- USER user
18
- ENV HOME=/home/user \
19
- PATH=/home/user/.local/bin:$PATH \
20
- PYTHONPATH=$HOME/app \
21
  PYTHONUNBUFFERED=1 \
 
 
 
 
 
 
 
 
22
  GRADIO_ALLOW_FLAGGING=never \
23
- GRADIO_NUM_PORTS=1 \
24
  GRADIO_SERVER_NAME=0.0.0.0 \
25
- GRADIO_THEME=huggingface \
26
- SYSTEM=spaces
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- WORKDIR $HOME/app
 
 
 
 
 
29
 
30
- # Copy project files as "user" before installing dependencies
31
- COPY --chown=user . $HOME/app
32
- COPY --chown=user ./images /home/user/app/images
33
 
34
- # Install dependencies as "user"
35
- RUN pip install --no-cache-dir --user -e .
36
- RUN pip install --no-cache-dir --user opencv-python
37
- # RUN pip install --no-cache-dir --user -r /code/requirements-gradio.txt
38
- RUN ls -l /home/user/app/images/
 
 
39
 
40
- CMD ["python", "app.py"]
41
 
 
 
 
 
 
 
1
+ # PyTorch 2.9.1 + CUDA 13.0 includes Blackwell (sm_120) support required by
2
+ # GeForce RTX 50-series GPUs. Pin the digest so rebuilds use the same runtime.
3
+ FROM pytorch/pytorch:2.9.1-cuda13.0-cudnn9-runtime@sha256:60f22fb80755fd0b470fb47928dbd55816aa9f847edd95cf43c93253507a9ddf
4
 
5
+ ARG APP_UID=1000
6
+ ARG APP_GID=1000
7
 
8
+ ENV DEBIAN_FRONTEND=noninteractive \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  PYTHONUNBUFFERED=1 \
10
+ PYTHONDONTWRITEBYTECODE=1 \
11
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
12
+ HOME=/home/user \
13
+ PATH=/home/user/.local/bin:/opt/conda/bin:$PATH \
14
+ PYTHONPATH=/home/user/app \
15
+ HF_HOME=/home/user/.cache/huggingface \
16
+ TORCH_HOME=/home/user/.cache/torch \
17
+ MPLCONFIGDIR=/home/user/.cache/matplotlib \
18
  GRADIO_ALLOW_FLAGGING=never \
 
19
  GRADIO_SERVER_NAME=0.0.0.0 \
20
+ GRADIO_SERVER_PORT=7860 \
21
+ GRADIO_SHARE=false \
22
+ REQUIRE_CUDA=1 \
23
+ PYTORCH_ALLOC_CONF=expandable_segments:True
24
+
25
+ RUN apt-get update \
26
+ && apt-get install -y --no-install-recommends \
27
+ ca-certificates \
28
+ fonts-dejavu-core \
29
+ libglib2.0-0 \
30
+ libgomp1 \
31
+ && groupadd --gid "${APP_GID}" user \
32
+ && useradd --create-home --uid "${APP_UID}" --gid "${APP_GID}" --shell /bin/bash user \
33
+ && rm -rf /var/lib/apt/lists/*
34
+
35
+ WORKDIR /home/user/app
36
 
37
+ # Install pinned dependencies before copying the source to preserve the build
38
+ # cache when only application code changes.
39
+ COPY requirements.txt requirements-gradio.txt ./
40
+ RUN python -m pip install --no-cache-dir \
41
+ --requirement requirements.txt \
42
+ --requirement requirements-gradio.txt
43
 
44
+ COPY --chown=user:user . .
 
 
45
 
46
+ RUN mkdir -p \
47
+ /home/user/.cache/huggingface \
48
+ /home/user/.cache/torch \
49
+ /home/user/.cache/matplotlib \
50
+ /home/user/app/results \
51
+ && python -c "import cv2, gradio, spaces, torch, torchvision, transformers; assert torch.__version__.startswith('2.9.1'); assert torch.version.cuda == '13.0'; assert transformers.__version__ == '4.48.2'" \
52
+ && chown -R user:user /home/user/.cache /home/user/app/results
53
 
54
+ EXPOSE 7860
55
 
56
+ # Fail fast when the container was started without GPU access, then replace the
57
+ # checker process with the application so signals reach Gradio correctly. The
58
+ # checker starts as root only long enough to prepare mounted volumes, then drops
59
+ # to the unprivileged "user" account before touching models or running Gradio.
60
+ CMD ["python", "docker/verify_runtime.py", "--", "python", "app.py"]
README.md CHANGED
@@ -10,4 +10,31 @@ pinned: false
10
  license: mit
11
  ---
12
 
13
- Check out the configuration reference at <https://huggingface.co/docs/hub/spaces-config-reference>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  license: mit
11
  ---
12
 
13
+ Check out the configuration reference at <https://huggingface.co/docs/hub/spaces-config-reference>
14
+
15
+ ## Docker GPU runtime
16
+
17
+ The container is pinned to PyTorch 2.9.1 with CUDA 13.0 for NVIDIA
18
+ Blackwell/RTX 50-series compatibility.
19
+
20
+ Requirements:
21
+
22
+ - NVIDIA driver compatible with CUDA 13.0
23
+ - Docker Desktop with the WSL2 backend on Windows
24
+ - NVIDIA GPU access enabled in Docker
25
+
26
+ Build and run:
27
+
28
+ ```shell
29
+ docker compose build
30
+ docker compose up
31
+ ```
32
+
33
+ Open <http://localhost:7860>. The entry point executes a CUDA kernel before
34
+ starting Gradio and exits with an actionable error if the GPU is unavailable.
35
+
36
+ To validate Docker GPU passthrough independently:
37
+
38
+ ```shell
39
+ docker run --rm --gpus all nvidia/cuda:13.0.0-base-ubuntu22.04 nvidia-smi
40
+ ```
app.py CHANGED
@@ -330,5 +330,9 @@ with gr.Blocks() as demo:
330
  outputs=[understanding_output, activation_map_output, understanding_target_token_decoded_output]
331
  )
332
 
333
- demo.launch(share=True)
334
- # demo.queue(concurrency_count=1, max_size=10).launch(server_name="0.0.0.0", server_port=37906, root_path="/path")
 
 
 
 
 
330
  outputs=[understanding_output, activation_map_output, understanding_target_token_decoded_output]
331
  )
332
 
333
+ demo.launch(
334
+ server_name=os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"),
335
+ server_port=int(os.getenv("GRADIO_SERVER_PORT", "7860")),
336
+ share=os.getenv("GRADIO_SHARE", "false").lower() in {"1", "true", "yes", "on"},
337
+ )
338
+ # demo.queue(concurrency_count=1, max_size=10).launch(server_name="0.0.0.0", server_port=37906, root_path="/path")
compose.yaml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ app:
3
+ image: probing-vis-literacy:gpu
4
+ build:
5
+ context: .
6
+ dockerfile: Dockerfile
7
+ ports:
8
+ - "7860:7860"
9
+ environment:
10
+ REQUIRE_CUDA: "1"
11
+ GRADIO_SHARE: "false"
12
+ deploy:
13
+ resources:
14
+ reservations:
15
+ devices:
16
+ - driver: nvidia
17
+ count: all
18
+ capabilities: [gpu]
19
+ shm_size: "8gb"
20
+ volumes:
21
+ - huggingface-cache:/home/user/.cache/huggingface
22
+ - model-cache:/home/user/.cache/torch
23
+ - app-results:/home/user/app/results
24
+
25
+ volumes:
26
+ huggingface-cache:
27
+ model-cache:
28
+ app-results:
demo/model_utils.py CHANGED
@@ -6,17 +6,30 @@ from transformers import AutoConfig, AutoModelForCausalLM, LlavaForConditionalGe
6
  from transformers import CLIPProcessor, CLIPModel
7
  from janus.models import MultiModalityCausalLM, VLChatProcessor
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  @spaces.GPU(duration=120)
10
  def set_dtype_device(model, precision=16, device_map=None):
11
- dtype = (torch.bfloat16 if torch.cuda.is_available() else torch.float16) if precision==16 else (torch.bfloat32 if torch.cuda.is_available() else torch.float32)
12
- cuda_device = 'cuda' if torch.cuda.is_available() else 'cpu'
13
- if torch.cuda.is_available():
14
- model = model.to(dtype)
15
- if not device_map:
16
- model.cuda()
17
- else:
18
- torch.set_default_device("cpu")
19
- model = model.to(dtype)
20
  return model, dtype, cuda_device
21
 
22
 
@@ -64,6 +77,8 @@ class Janus_Utils(Model_Utils):
64
  language_config._attn_implementation = 'eager'
65
  self.vl_gpt = AutoModelForCausalLM.from_pretrained(model_path,
66
  language_config=language_config,
 
 
67
  trust_remote_code=True,
68
  ignore_mismatched_sizes=True,
69
  )
@@ -127,10 +142,13 @@ class LLaVA_Utils(Model_Utils):
127
  self.vl_gpt = LlavaForConditionalGeneration.from_pretrained(model_path,
128
  low_cpu_mem_usage=True,
129
  attn_implementation = 'eager',
 
130
  device_map="auto",
131
  output_attentions=True
132
  )
133
- self.vl_gpt, self.dtype, self.cuda_device = set_dtype_device(self.vl_gpt)
 
 
134
  self.processor = AutoProcessor.from_pretrained(model_path)
135
  self.tokenizer = self.processor.tokenizer
136
 
@@ -140,7 +158,7 @@ class LLaVA_Utils(Model_Utils):
140
  self.processor = AutoProcessor.from_pretrained(model_path)
141
 
142
  self.vl_gpt = LlavaOnevisionForConditionalGeneration.from_pretrained(model_path,
143
- torch_dtype=torch.float16,
144
  device_map="auto",
145
  low_cpu_mem_usage=True,
146
  attn_implementation = 'eager',
@@ -220,7 +238,8 @@ class ChartGemma_Utils(Model_Utils):
220
 
221
  self.vl_gpt = PaliGemmaForConditionalGeneration.from_pretrained(
222
  model_path,
223
- torch_dtype=torch.float16,
 
224
  attn_implementation="eager",
225
  output_attentions=True
226
  )
@@ -294,4 +313,3 @@ def add_title_to_image(image, title, font_size=50):
294
  return combined
295
 
296
 
297
-
 
6
  from transformers import CLIPProcessor, CLIPModel
7
  from janus.models import MultiModalityCausalLM, VLChatProcessor
8
 
9
+ def get_inference_dtype(precision=16):
10
+ cuda_available = torch.cuda.is_available()
11
+ if precision == 16 and cuda_available:
12
+ return (
13
+ torch.bfloat16
14
+ if torch.cuda.is_bf16_supported()
15
+ else torch.float16
16
+ )
17
+
18
+ # Float16 model execution is not consistently supported on CPU.
19
+ return torch.float32
20
+
21
+
22
  @spaces.GPU(duration=120)
23
  def set_dtype_device(model, precision=16, device_map=None):
24
+ dtype = get_inference_dtype(precision)
25
+ cuda_device = "cuda" if torch.cuda.is_available() else "cpu"
26
+
27
+ if device_map:
28
+ # Accelerate owns device placement for dispatched models. Calling
29
+ # model.cuda() or model.to(device) afterwards can invalidate its hooks.
30
+ return model, dtype, cuda_device
31
+
32
+ model = model.to(device=cuda_device, dtype=dtype)
33
  return model, dtype, cuda_device
34
 
35
 
 
77
  language_config._attn_implementation = 'eager'
78
  self.vl_gpt = AutoModelForCausalLM.from_pretrained(model_path,
79
  language_config=language_config,
80
+ torch_dtype=get_inference_dtype(),
81
+ low_cpu_mem_usage=True,
82
  trust_remote_code=True,
83
  ignore_mismatched_sizes=True,
84
  )
 
142
  self.vl_gpt = LlavaForConditionalGeneration.from_pretrained(model_path,
143
  low_cpu_mem_usage=True,
144
  attn_implementation = 'eager',
145
+ torch_dtype=get_inference_dtype(),
146
  device_map="auto",
147
  output_attentions=True
148
  )
149
+ self.vl_gpt, self.dtype, self.cuda_device = set_dtype_device(
150
+ self.vl_gpt, device_map="auto"
151
+ )
152
  self.processor = AutoProcessor.from_pretrained(model_path)
153
  self.tokenizer = self.processor.tokenizer
154
 
 
158
  self.processor = AutoProcessor.from_pretrained(model_path)
159
 
160
  self.vl_gpt = LlavaOnevisionForConditionalGeneration.from_pretrained(model_path,
161
+ torch_dtype=get_inference_dtype(),
162
  device_map="auto",
163
  low_cpu_mem_usage=True,
164
  attn_implementation = 'eager',
 
238
 
239
  self.vl_gpt = PaliGemmaForConditionalGeneration.from_pretrained(
240
  model_path,
241
+ torch_dtype=get_inference_dtype(),
242
+ low_cpu_mem_usage=True,
243
  attn_implementation="eager",
244
  output_attentions=True
245
  )
 
313
  return combined
314
 
315
 
 
docker/verify_runtime.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Fail-fast CUDA validation for the Docker entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ import pwd
8
+ import sys
9
+
10
+ import torch
11
+
12
+
13
+ WRITABLE_DIRECTORIES = (
14
+ Path("/home/user/.cache/huggingface"),
15
+ Path("/home/user/.cache/torch"),
16
+ Path("/home/user/.cache/matplotlib"),
17
+ Path("/home/user/app/results"),
18
+ )
19
+
20
+
21
+ def env_flag(name: str, default: bool) -> bool:
22
+ value = os.getenv(name)
23
+ if value is None:
24
+ return default
25
+ return value.strip().lower() in {"1", "true", "yes", "on"}
26
+
27
+
28
+ def prepare_directories_and_drop_privileges() -> None:
29
+ """Make mounted volumes writable, then permanently become the app user."""
30
+ if os.geteuid() != 0:
31
+ return
32
+
33
+ account = pwd.getpwnam("user")
34
+ for directory in WRITABLE_DIRECTORIES:
35
+ directory.mkdir(parents=True, exist_ok=True)
36
+ for root, directories, files in os.walk(directory):
37
+ os.chown(root, account.pw_uid, account.pw_gid)
38
+ for name in directories:
39
+ os.chown(
40
+ os.path.join(root, name),
41
+ account.pw_uid,
42
+ account.pw_gid,
43
+ follow_symlinks=False,
44
+ )
45
+ for name in files:
46
+ os.chown(
47
+ os.path.join(root, name),
48
+ account.pw_uid,
49
+ account.pw_gid,
50
+ follow_symlinks=False,
51
+ )
52
+
53
+ os.setgroups([])
54
+ os.setgid(account.pw_gid)
55
+ os.setuid(account.pw_uid)
56
+
57
+ if os.geteuid() == 0:
58
+ raise RuntimeError("Failed to drop root privileges.")
59
+
60
+
61
+ def verify_cuda() -> None:
62
+ require_cuda = env_flag("REQUIRE_CUDA", True)
63
+
64
+ if not torch.cuda.is_available():
65
+ message = (
66
+ "CUDA is not visible inside the container. Start it with "
67
+ "`docker compose up` or `docker run --gpus all ...`."
68
+ )
69
+ if require_cuda:
70
+ raise RuntimeError(message)
71
+ print(f"WARNING: {message} Continuing because REQUIRE_CUDA=0.", flush=True)
72
+ return
73
+
74
+ # Executing a real kernel detects runtime/driver/architecture mismatches that
75
+ # torch.cuda.is_available() alone can miss.
76
+ device = torch.device("cuda:0")
77
+ result = (torch.ones(1, device=device) + 1).item()
78
+ torch.cuda.synchronize(device)
79
+ if result != 2:
80
+ raise RuntimeError("CUDA smoke test returned an unexpected result.")
81
+
82
+ properties = torch.cuda.get_device_properties(device)
83
+ capability = torch.cuda.get_device_capability(device)
84
+ print(
85
+ "CUDA ready: "
86
+ f"torch={torch.__version__}, runtime={torch.version.cuda}, "
87
+ f"device={properties.name}, capability=sm_{capability[0]}{capability[1]}",
88
+ flush=True,
89
+ )
90
+
91
+
92
+ def main() -> None:
93
+ prepare_directories_and_drop_privileges()
94
+ verify_cuda()
95
+
96
+ command = sys.argv[1:]
97
+ if command and command[0] == "--":
98
+ command = command[1:]
99
+ if command:
100
+ os.execvp(command[0], command)
101
+
102
+
103
+ if __name__ == "__main__":
104
+ main()
pyproject.toml CHANGED
@@ -10,29 +10,27 @@ authors = [{name = "DeepSeek-AI"}]
10
  license = {file = "LICENSE-CODE"}
11
  urls = {homepage = "https://github.com/deepseek-ai/Janus"}
12
  readme = "README.md"
13
- requires-python = ">=3.8"
14
  dependencies = [
15
- "torch>=2.0.1",
16
- "transformers>=4.38.2",
17
- "timm>=0.9.16",
18
- "accelerate",
19
- "sentencepiece",
20
- "attrdict",
21
- "einops",
 
22
  ]
23
 
24
  [project.optional-dependencies]
25
  gradio = [
26
- "gradio==3.48.0",
27
- "gradio-client==0.6.1",
28
- "mdtex2html==1.3.0",
29
- "pypinyin==0.50.0",
30
- "tiktoken==0.5.2",
31
- "tqdm==4.64.0",
32
- "colorama==0.4.5",
33
- "Pygments==2.12.0",
34
- "markdown==3.4.1",
35
- "SentencePiece==0.1.96"
36
  ]
37
  lint = [
38
  "isort",
 
10
  license = {file = "LICENSE-CODE"}
11
  urls = {homepage = "https://github.com/deepseek-ai/Janus"}
12
  readme = "README.md"
13
+ requires-python = ">=3.10,<3.13"
14
  dependencies = [
15
+ "torch==2.9.1",
16
+ "torchvision==0.24.1",
17
+ "transformers==4.48.2",
18
+ "timm==0.9.16",
19
+ "accelerate==1.3.0",
20
+ "sentencepiece==0.2.1",
21
+ "attrdict3==2.0.2",
22
+ "einops==0.8.0",
23
  ]
24
 
25
  [project.optional-dependencies]
26
  gradio = [
27
+ "gradio==5.20.0",
28
+ "gradio-client==1.7.2",
29
+ "spaces==0.32.0",
30
+ "opencv-python-headless==4.11.0.86",
31
+ "matplotlib==3.10.0",
32
+ "pillow==11.1.0",
33
+ "pydantic==2.10.6",
 
 
 
34
  ]
35
  lint = [
36
  "isort",
requirements-gradio.txt CHANGED
@@ -1,11 +1,3 @@
1
- # for gradio demo
2
- gradio==3.48.0
3
- gradio-client==0.6.1
4
- mdtex2html==1.3.0
5
- pypinyin==0.50.0
6
- tiktoken==0.5.2
7
- tqdm==4.64.0
8
- colorama==0.4.5
9
- Pygments==2.12.0
10
- markdown==3.4.1
11
- SentencePiece==0.1.96
 
1
+ # Matches the Hugging Face Spaces SDK version declared in README.md.
2
+ gradio==5.20.0
3
+ gradio-client==1.7.2
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,25 +1,24 @@
1
- torch>=2.0.1
2
- numpy>=1.21.2
 
 
 
 
3
  transformers==4.48.2
4
- timm>=0.9.16
5
- accelerate
6
- sentencepiece
7
- attrdict
8
- einops
9
- opencv-python
10
- spaces
11
- matplotlib
12
- pillow>=11.1.0
13
- pydantic==2.10.6
14
 
15
- # for gradio demo
16
- # gradio==3.48.0
17
- # gradio-client==0.6.1
18
- # mdtex2html==1.3.0
19
- # pypinyin==0.50.0
20
- # tiktoken==0.5.2
21
- # tqdm==4.64.0
22
- # colorama==0.4.5
23
- # Pygments==2.12.0
24
- # markdown==3.4.1
25
- # SentencePiece==0.1.96
 
 
1
+ # GPU framework versions must match the pinned PyTorch CUDA base image.
2
+ torch==2.9.1
3
+ torchvision==0.24.1
4
+
5
+ # The custom attention implementations in demo/modified_attn.py target the
6
+ # Transformers 4.48 API. Do not upgrade this independently.
7
  transformers==4.48.2
8
+ huggingface-hub==0.28.1
9
+ tokenizers==0.21.0
10
+ safetensors==0.5.2
11
+ accelerate==1.3.0
 
 
 
 
 
 
12
 
13
+ # Model and visualization runtime.
14
+ numpy==2.2.2
15
+ timm==0.9.16
16
+ sentencepiece==0.2.1
17
+ attrdict3==2.0.2
18
+ einops==0.8.0
19
+ opencv-python-headless==4.11.0.86
20
+ spaces==0.32.0
21
+ matplotlib==3.10.0
22
+ pillow==11.1.0
23
+ pydantic==2.10.6
24
+ psutil==5.9.8