Skip to content

Commit 0726996

Browse files
feat: add distilbert sentiment classification model (#3)
* feat: add distilbert sentiment classification model - Bake DistilBERT sentiment model into Docker image - Add transformers library dependency (4.40.0+) - Implement sentiment analysis pipeline with local_files_only=True - Document both model download methods: transformers pipeline and wget - Add example inference code for sentiment classification - Model loads from cache without network access at runtime - Support CUDA GPU acceleration or CPU fallback - Include wget alternative for custom/hosted models * docs: clarify use cases and options in dockerfile and main.py - reorganize model baking options (transformers vs wget) under USE CASE 1 - reorganize startup/entrypoint options (jupyter+ssh, hybrid, app-only) under USE CASE 2 - add explicit 'Use this for', 'Behavior', and 'Pros/Cons' sections - clarify which options match which use cases - make it obvious which option is default and why - improve readability for future maintainers * style: remove trailing whitespace in main.py
1 parent 45f9ebb commit 0726996

File tree

4 files changed

+128
-38
lines changed

4 files changed

+128
-38
lines changed

Dockerfile

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,45 +21,63 @@ COPY requirements.txt /app/
2121
RUN pip install --no-cache-dir --upgrade pip && \
2222
pip install --no-cache-dir -r requirements.txt
2323

24+
# ============================================================================
25+
# USE CASE 1: BAKE MODEL INTO IMAGE
26+
# ============================================================================
27+
# Pre-download and cache the model in the image
28+
# Using DistilBERT for sentiment classification - small and efficient
29+
ENV HF_HOME=/app/models
30+
ENV HF_HUB_ENABLE_HF_TRANSFER=0
31+
32+
# MODEL BAKING OPTION 1: Automatic via transformers (DEFAULT)
33+
# Pros: Simple, clean, automatic caching
34+
# Cons: Requires network during build
35+
RUN python -c "from transformers import pipeline; pipeline('sentiment-analysis', model='distilbert-base-uncased-finetuned-sst-2-english')"
36+
37+
# MODEL BAKING OPTION 2: Manual via wget (Alternative)
38+
# Pros: Explicit control, works with custom/hosted models, offline-friendly
39+
# Cons: Need to manually list all model files
40+
# To use: Uncomment below and disable MODEL BAKING OPTION 1 above
41+
# Required files: config.json, model.safetensors, tokenizer_config.json, vocab.txt
42+
# RUN mkdir -p /app/models/distilbert-model && \
43+
# cd /app/models/distilbert-model && \
44+
# wget -q https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english/resolve/main/config.json && \
45+
# wget -q https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english/resolve/main/model.safetensors && \
46+
# wget -q https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english/resolve/main/tokenizer_config.json && \
47+
# wget -q https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english/resolve/main/vocab.txt
48+
2449
# Copy application files
2550
COPY . /app
2651

2752
# ============================================================================
28-
# OPTION 1: Keep everything from base image (Jupyter, SSH, entrypoint) - DEFAULT
53+
# USE CASE 2: SERVICE STARTUP & ENTRYPOINT
2954
# ============================================================================
30-
# The base image already provides everything:
31-
# - Entrypoint: /opt/nvidia/nvidia_entrypoint.sh (handles CUDA setup)
32-
# - Default CMD: /start.sh (starts Jupyter/SSH automatically based on template settings)
33-
# - Jupyter Notebook (starts if startJupyter=true in template)
34-
# - SSH access (starts if startSsh=true in template)
35-
#
55+
# Choose how the container starts and what services run
56+
57+
# STARTUP OPTION 1: Keep everything from base image (DEFAULT - Jupyter + SSH)
58+
# Use this for: Interactive development, remote access, Jupyter notebook
59+
# Behavior:
60+
# - Entrypoint: /opt/nvidia/nvidia_entrypoint.sh (CUDA setup)
61+
# - CMD: /start.sh (starts Jupyter/SSH based on template settings)
3662
# Just don't override CMD - the base image handles everything!
3763
# CMD is not set, so base image default (/start.sh) is used
3864

39-
# ============================================================================
40-
# OPTION 2: Override CMD but keep entrypoint and services
41-
# ============================================================================
42-
# If you want to run your own command but still have Jupyter/SSH start:
43-
# - Keep the entrypoint (CUDA setup still happens automatically)
44-
# - Use the provided run.sh script which starts /start.sh in background,
45-
# then runs your application commands
46-
#
47-
# Edit run.sh to customize what runs after services start, then uncomment:
65+
# STARTUP OPTION 2: Run app after services (Jupyter + SSH + Custom app)
66+
# Use this for: Keep services running + run your application in parallel
67+
# Behavior:
68+
# - Entrypoint: /opt/nvidia/nvidia_entrypoint.sh (CUDA setup)
69+
# - CMD: Runs run.sh which starts /start.sh in background, then your app
70+
# To use: Uncomment below
4871
# COPY run.sh /app/run.sh
4972
# RUN chmod +x /app/run.sh
5073
# CMD ["/app/run.sh"]
51-
#
52-
# The run.sh script:
53-
# 1. Starts /start.sh in background (starts Jupyter/SSH)
54-
# 2. Waits for services to initialize
55-
# 3. Runs your application commands
56-
# 4. Waits for background processes
5774

58-
# ============================================================================
59-
# OPTION 3: Override everything - no Jupyter, no SSH, just your app
60-
# ============================================================================
61-
# If you don't want any base image services, override both entrypoint and CMD:
62-
#
63-
# ENTRYPOINT [] # Clear entrypoint
75+
# STARTUP OPTION 3: Application only (No Jupyter, no SSH)
76+
# Use this for: Production serverless, minimal overhead, just your app
77+
# Behavior:
78+
# - No Jupyter, no SSH, minimal services
79+
# - Direct app execution
80+
# To use: Uncomment below
81+
# ENTRYPOINT []
6482
# CMD ["python", "/app/main.py"]
6583

docs/context.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,40 @@ The Dockerfiles demonstrate three approaches for handling the base image's entry
111111
- `PYTHONUNBUFFERED=1` ensures Python output is immediately visible in logs
112112
- The base image entrypoint (`/opt/nvidia/nvidia_entrypoint.sh`) handles CUDA initialization
113113

114+
## Pre-Baked Model
115+
116+
This template includes a pre-downloaded DistilBERT sentiment classification model baked into the Docker image:
117+
118+
- **Model**: `distilbert-base-uncased-finetuned-sst-2-english`
119+
- **Task**: Sentiment analysis (POSITIVE/NEGATIVE classification)
120+
- **Size**: ~268MB (small and efficient)
121+
- **Input**: Plain text strings
122+
- **Location**: Cached in `/app/models/` within the image
123+
- **Usage**: Load with `pipeline('sentiment-analysis', model=...)` in Python
124+
125+
The model runs on GPU if available (via CUDA) or falls back to CPU. See `main.py` for example inference code.
126+
127+
### Model Download Methods
128+
129+
**Option A: Automatic (Transformers Pipeline)**
130+
- Downloads via `transformers` library during build
131+
- Model cached automatically in `HF_HOME` directory
132+
- Requires network access during build
133+
- See commented "OPTION A" in Dockerfile
134+
135+
**Option B: Manual (wget)**
136+
- Download specific model files directly via `wget`
137+
- Useful for custom/hosted models or when you need explicit control
138+
- Set `HF_HOME` to point to downloaded directory
139+
- See commented "OPTION B" in Dockerfile with example wget commands
140+
- To use: Uncomment the RUN commands in Dockerfile and update `main.py` to load from local path
141+
114142
## Customization Points
115143

116144
- **Base Image**: Change `FROM` line to use other Runpod base images
117145
- **System Packages**: Add to `apt-get install` section
118146
- **Python Dependencies**: Update `requirements.txt`
119147
- **Application Code**: Replace or extend `main.py`
120148
- **Entry Point**: Modify `CMD` in Dockerfile
149+
- **Model Selection**: Replace model ID in Dockerfile and main.py to use different Hugging Face models
121150

main.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,83 @@
11
"""
2-
Example template application.
3-
This demonstrates how to extend a Runpod PyTorch base image.
2+
Example template application with DistilBERT sentiment classification model.
3+
This demonstrates how to extend a Runpod PyTorch base image and use a baked-in model.
44
"""
55

66
import sys
77
import torch
88
import time
99
import signal
10+
from transformers import pipeline
11+
1012

1113
def main():
1214
print("Hello from your Runpod template!")
1315
print(f"Python version: {sys.version.split()[0]}")
1416
print(f"PyTorch version: {torch.__version__}")
1517
print(f"CUDA available: {torch.cuda.is_available()}")
16-
18+
1719
if torch.cuda.is_available():
1820
print(f"CUDA version: {torch.version.cuda}")
1921
print(f"GPU device: {torch.cuda.get_device_name(0)}")
20-
21-
print("\nContainer is running. Add your application logic here.")
22-
print("Press Ctrl+C to stop.")
23-
22+
23+
# Initialize the sentiment analysis model (already cached in the image)
24+
print("\nLoading sentiment analysis model...")
25+
device = 0 if torch.cuda.is_available() else -1
26+
27+
# ========================================================================
28+
# USE CASE 1: LOAD MODEL
29+
# ========================================================================
30+
31+
# MODEL LOADING OPTION 1: From Hugging Face Hub cache (DEFAULT)
32+
# Use this when: Using transformers pipeline for model baking
33+
# Behavior: Loads from cache, requires local_files_only=True
34+
classifier = pipeline(
35+
"sentiment-analysis",
36+
model="distilbert-base-uncased-finetuned-sst-2-english",
37+
device=device,
38+
model_kwargs={"local_files_only": True},
39+
)
40+
41+
# MODEL LOADING OPTION 2: From local directory (Alternative)
42+
# Use this when: Using wget for model baking (uncomment in Dockerfile)
43+
# Behavior: Loads directly from /app/models/distilbert-model
44+
# To use: Uncomment below and disable MODEL LOADING OPTION 1
45+
# classifier = pipeline('sentiment-analysis',
46+
# model='/app/models/distilbert-model',
47+
# device=device)
48+
49+
print("Model loaded successfully!")
50+
51+
# Example inference
52+
test_texts = [
53+
"This is a wonderful experience!",
54+
"I really don't like this at all.",
55+
"The weather is nice today.",
56+
]
57+
58+
print("\n--- Running sentiment analysis ---")
59+
for text in test_texts:
60+
result = classifier(text)
61+
print(f"Text: {text}")
62+
print(f"Result: {result[0]['label']} (confidence: {result[0]['score']:.4f})\n")
63+
64+
print("Container is running. Press Ctrl+C to stop.")
65+
2466
# Keep container running
2567
def signal_handler(sig, frame):
2668
print("\nShutting down...")
2769
sys.exit(0)
28-
70+
2971
signal.signal(signal.SIGINT, signal_handler)
3072
signal.signal(signal.SIGTERM, signal_handler)
31-
73+
3274
# Keep running until terminated
3375
try:
3476
while True:
3577
time.sleep(60)
3678
except KeyboardInterrupt:
3779
signal_handler(None, None)
3880

81+
3982
if __name__ == "__main__":
4083
main()
41-

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
# Add your packages here
33
numpy>=1.24.0
44
requests>=2.31.0
5+
transformers>=4.40.0
56

0 commit comments

Comments
 (0)