KeivanR commited on
Commit
748a976
·
1 Parent(s): c4ad33b

readme and shared global model and tokenizer

Browse files
Files changed (4) hide show
  1. README.md +88 -11
  2. app.py +7 -1
  3. qwen_classifier/evaluate.py +8 -7
  4. qwen_classifier/predict.py +7 -10
README.md CHANGED
@@ -1,11 +1,88 @@
1
- ---
2
- title: Qwen Classifier Demo
3
- emoji: 🏢
4
- colorFrom: green
5
- colorTo: gray
6
- sdk: docker
7
- pinned: false
8
- short_description: Fine tuned Qwen to classify coding exercizes
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Qwen Multi-label Text Classifier
2
+
3
+ ## Overview
4
+ A multi-label text classifier based on Qwen-1.5B, fine-tuned for coding exercise classification. Supports:
5
+ - Local CPU/GPU inference
6
+ - Hugging Face API deployment
7
+ - Batch evaluation
8
+ - REST API via FastAPI
9
+ - Docker deployment
10
+
11
+ ## Features
12
+ - **9 Label Classification**: Predicts multiple tags per text
13
+ - **CLI Interface**: Run predictions/evaluations from terminal
14
+ - **Dual Backend**: Choose between local or HF inference
15
+ - **GPU Optimized**: CUDA support via Docker
16
+
17
+ ## Installation
18
+ ```bash
19
+ git clone https://github.com/your-username/qwen-classifier
20
+ cd qwen-classifier
21
+ python3 -m venv .venv
22
+ source .venv/bin/activate
23
+ pip install -e .
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ ### CLI Prediction
29
+ ```bash
30
+ # Local inference
31
+ qwen-clf predict "Your coding exercise text" --backend local
32
+
33
+ # HF Space inference
34
+ qwen-clf predict "Your text" --backend hf --hf-token YOUR_TOKEN
35
+ ```
36
+
37
+ ### Batch Evaluation
38
+ ```bash
39
+ qwen-clf evaluate dataset.zip --backend local
40
+ ```
41
+
42
+ ### API Server
43
+ ```bash
44
+ uvicorn app:app --host 0.0.0.0 --port 7860
45
+ ```
46
+
47
+ #### API Endpoints
48
+ | Endpoint | Method | Description |
49
+ |----------|--------|-------------|
50
+ | `/` | GET | Documentation |
51
+ | `/predict` | POST | Single text prediction |
52
+ | `/evaluate` | POST | Batch evaluation (ZIP) |
53
+ | `/health` | GET | Service status |
54
+
55
+ ## Docker Deployment
56
+ ```bash
57
+ # Build with GPU support
58
+ docker build -t qwen-classifier .
59
+
60
+ # Run container
61
+ docker run -p 7860:7860 --gpus all qwen-classifier
62
+ ```
63
+
64
+ ## Project Structure
65
+ ```
66
+ .
67
+ ├── app.py # FastAPI entry point
68
+ ├── Dockerfile # GPU-optimized container setup
69
+ ├── qwen_classifier/ # Core package
70
+ │ ├── cli.py # Command line interface
71
+ │ ├── model.py # Qwen classifier implementation
72
+ │ ├── predict.py # Inference logic
73
+ │ └── evaluate.py # Batch evaluation
74
+ └── requirements.txt # Python dependencies
75
+ ```
76
+
77
+ ## Configuration
78
+ Edit `qwen_classifier/config.py` to set:
79
+ - `TAG_NAMES`: List of 9 classification tags
80
+ - `HF_REPO`: Default Hugging Face model repo
81
+ - `DEVICE`: Auto-detected CUDA/CPU
82
+
83
+ ## Hugging Face Space
84
+ Live demo:
85
+ [![HF Space](https://img.shields.io/badge/🤗%20Hugging%20Face-Space-blue)](https://huggingface.co/spaces/KeivanR/qwen-classifier-demo)
86
+
87
+ ## License
88
+ Apache 2.0 © Keivan Razban
app.py CHANGED
@@ -7,12 +7,15 @@ from fastapi import FastAPI
7
  from fastapi.responses import HTMLResponse
8
  from qwen_classifier.predict import predict_single # Your existing function
9
  from qwen_classifier.evaluate import evaluate_batch # Your existing function
 
10
  import torch
 
11
  from huggingface_hub import login
12
  from qwen_classifier.model import QwenClassifier
13
  from qwen_classifier.config import HF_REPO
14
  from pydantic import BaseModel
15
 
 
16
  app = FastAPI(title="Qwen Classifier")
17
  hf_repo = os.getenv("HF_REPO")
18
  if not hf_repo:
@@ -41,6 +44,7 @@ def home():
41
 
42
  @app.on_event("startup")
43
  async def load_model():
 
44
  # Warm up GPU
45
  torch.zeros(1).cuda()
46
  # Read HF_TOKEN from Hugging Face Space secrets
@@ -52,9 +56,11 @@ async def load_model():
52
  login(token=hf_token)
53
 
54
  # Load model (will cache in /home/user/.cache/huggingface)
55
- app.state.model = QwenClassifier.from_pretrained(
 
56
  hf_repo,
57
  )
 
58
  print("Model loaded successfully!")
59
 
60
 
 
7
  from fastapi.responses import HTMLResponse
8
  from qwen_classifier.predict import predict_single # Your existing function
9
  from qwen_classifier.evaluate import evaluate_batch # Your existing function
10
+ from qwen_classifier.globals import model, tokenizer
11
  import torch
12
+ from transformers import AutoTokenizer
13
  from huggingface_hub import login
14
  from qwen_classifier.model import QwenClassifier
15
  from qwen_classifier.config import HF_REPO
16
  from pydantic import BaseModel
17
 
18
+
19
  app = FastAPI(title="Qwen Classifier")
20
  hf_repo = os.getenv("HF_REPO")
21
  if not hf_repo:
 
44
 
45
  @app.on_event("startup")
46
  async def load_model():
47
+ global model, tokenizer
48
  # Warm up GPU
49
  torch.zeros(1).cuda()
50
  # Read HF_TOKEN from Hugging Face Space secrets
 
56
  login(token=hf_token)
57
 
58
  # Load model (will cache in /home/user/.cache/huggingface)
59
+
60
+ model = QwenClassifier.from_pretrained(
61
  hf_repo,
62
  )
63
+ tokenizer = AutoTokenizer.from_pretrained(hf_repo)
64
  print("Model loaded successfully!")
65
 
66
 
qwen_classifier/evaluate.py CHANGED
@@ -10,6 +10,7 @@ from datasets import Dataset
10
  from torch.utils.data import DataLoader
11
  import requests
12
  from .config import TAG_NAMES, DEVICE, SPACE_URL
 
13
 
14
  def load_data(test_data_path):
15
  # zip file handler
@@ -72,15 +73,15 @@ def evaluate_batch(file_path, hf_repo, backend="local", hf_token=None):
72
  raise ValueError(f"Unknown backend: {backend}")
73
 
74
  def _evaluate_local(test_data_path, hf_repo):
75
- global local_model, local_tokenizer
76
 
77
  # Lazy-loading to avoid slow startup
78
- if local_model is None:
79
  from .model import QwenClassifier
80
  from transformers import AutoTokenizer
81
 
82
- local_model = QwenClassifier.from_pretrained(hf_repo).eval()
83
- local_tokenizer = AutoTokenizer.from_pretrained(hf_repo)
84
  df = load_data(test_data_path)
85
  df = preprocessing(df)
86
 
@@ -88,7 +89,7 @@ def _evaluate_local(test_data_path, hf_repo):
88
 
89
  # Then apply tokenization
90
  def tokenize_function(examples):
91
- return local_tokenizer(examples["text"], padding="max_length", truncation=True, max_length=512)
92
 
93
  dataset = hf_dataset.map(tokenize_function, batched=True)
94
 
@@ -97,7 +98,7 @@ def _evaluate_local(test_data_path, hf_repo):
97
  dataloader = DataLoader(dataset, batch_size=8, shuffle=True)
98
 
99
 
100
- local_model.eval()
101
  all_preds = []
102
  all_labels = []
103
 
@@ -106,7 +107,7 @@ def _evaluate_local(test_data_path, hf_repo):
106
  batch = {k: v.to(DEVICE) for k, v in batch.items()}
107
  labels = batch["labels"].type(torch.float32)
108
 
109
- logits = local_model(batch["input_ids"], batch["attention_mask"])
110
 
111
  preds = torch.sigmoid(logits).cpu().numpy() > 0.5
112
  labels = labels.cpu().numpy()
 
10
  from torch.utils.data import DataLoader
11
  import requests
12
  from .config import TAG_NAMES, DEVICE, SPACE_URL
13
+ from .globals import model, tokenizer
14
 
15
  def load_data(test_data_path):
16
  # zip file handler
 
73
  raise ValueError(f"Unknown backend: {backend}")
74
 
75
  def _evaluate_local(test_data_path, hf_repo):
76
+ global model, tokenizer
77
 
78
  # Lazy-loading to avoid slow startup
79
+ if model is None:
80
  from .model import QwenClassifier
81
  from transformers import AutoTokenizer
82
 
83
+ model = QwenClassifier.from_pretrained(hf_repo).eval()
84
+ tokenizer = AutoTokenizer.from_pretrained(hf_repo)
85
  df = load_data(test_data_path)
86
  df = preprocessing(df)
87
 
 
89
 
90
  # Then apply tokenization
91
  def tokenize_function(examples):
92
+ return tokenizer(examples["text"], padding="max_length", truncation=True, max_length=512)
93
 
94
  dataset = hf_dataset.map(tokenize_function, batched=True)
95
 
 
98
  dataloader = DataLoader(dataset, batch_size=8, shuffle=True)
99
 
100
 
101
+ model.eval()
102
  all_preds = []
103
  all_labels = []
104
 
 
107
  batch = {k: v.to(DEVICE) for k, v in batch.items()}
108
  labels = batch["labels"].type(torch.float32)
109
 
110
+ logits = model(batch["input_ids"], batch["attention_mask"])
111
 
112
  preds = torch.sigmoid(logits).cpu().numpy() > 0.5
113
  labels = labels.cpu().numpy()
qwen_classifier/predict.py CHANGED
@@ -1,10 +1,7 @@
1
  import torch
2
  import requests
3
  from .config import TAG_NAMES, SPACE_URL
4
-
5
- # Local model setup (only load if needed)
6
- local_model = None
7
- local_tokenizer = None
8
 
9
  def predict_single(text, hf_repo, backend="local", hf_token=None):
10
  if backend == "local":
@@ -15,19 +12,19 @@ def predict_single(text, hf_repo, backend="local", hf_token=None):
15
  raise ValueError(f"Unknown backend: {backend}")
16
 
17
  def _predict_local(text, hf_repo):
18
- global local_model, local_tokenizer
19
 
20
  # Lazy-loading to avoid slow startup
21
- if local_model is None:
22
  from .model import QwenClassifier
23
  from transformers import AutoTokenizer
24
 
25
- local_model = QwenClassifier.from_pretrained(hf_repo).eval()
26
- local_tokenizer = AutoTokenizer.from_pretrained(hf_repo)
27
 
28
- inputs = local_tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
29
  with torch.no_grad():
30
- logits = local_model(**inputs)
31
  return _process_output(logits)
32
 
33
  def _predict_hf_api(text, hf_token=None):
 
1
  import torch
2
  import requests
3
  from .config import TAG_NAMES, SPACE_URL
4
+ from .globals import model, tokenizer
 
 
 
5
 
6
  def predict_single(text, hf_repo, backend="local", hf_token=None):
7
  if backend == "local":
 
12
  raise ValueError(f"Unknown backend: {backend}")
13
 
14
  def _predict_local(text, hf_repo):
15
+ global model, tokenizer
16
 
17
  # Lazy-loading to avoid slow startup
18
+ if model is None:
19
  from .model import QwenClassifier
20
  from transformers import AutoTokenizer
21
 
22
+ model = QwenClassifier.from_pretrained(hf_repo).eval()
23
+ tokenizer = AutoTokenizer.from_pretrained(hf_repo)
24
 
25
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
26
  with torch.no_grad():
27
+ logits = model(**inputs)
28
  return _process_output(logits)
29
 
30
  def _predict_hf_api(text, hf_token=None):