webolavo commited on
Commit
dd81747
ยท
verified ยท
1 Parent(s): 8dc9360

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +43 -0
  2. app.py +129 -0
  3. requirements.txt +11 -0
Dockerfile ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # โ”€โ”€โ”€ Base Image โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
2
+ FROM python:3.10-slim
3
+
4
+ # โ”€โ”€โ”€ ู…ุชุบูŠุฑุงุช ุงู„ุจูŠุฆุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
5
+ ENV PYTHONDONTWRITEBYTECODE=1 \
6
+ PYTHONUNBUFFERED=1 \
7
+ HF_HOME=/app/.cache/huggingface \
8
+ TRANSFORMERS_CACHE=/app/.cache/huggingface \
9
+ PORT=7860
10
+
11
+ # โ”€โ”€โ”€ ู…ุฌู„ุฏ ุงู„ุนู…ู„ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
12
+ WORKDIR /app
13
+
14
+ # โ”€โ”€โ”€ ุชุซุจูŠุช dependencies ุงู„ู†ุธุงู… โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
15
+ RUN apt-get update && apt-get install -y \
16
+ git \
17
+ wget \
18
+ libglib2.0-0 \
19
+ libsm6 \
20
+ libxext6 \
21
+ libxrender-dev \
22
+ libgomp1 \
23
+ && rm -rf /var/lib/apt/lists/*
24
+
25
+ # โ”€โ”€โ”€ ู†ุณุฎ requirements ุฃูˆู„ุงู‹ (cache optimization) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
26
+ COPY requirements.txt .
27
+
28
+ # โ”€โ”€โ”€ ุชุซุจูŠุช Python packages โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
29
+ RUN pip install --no-cache-dir --upgrade pip && \
30
+ pip install --no-cache-dir -r requirements.txt
31
+
32
+ # โ”€โ”€โ”€ ู†ุณุฎ ุงู„ูƒูˆุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
33
+ COPY app.py .
34
+
35
+ # โ”€โ”€โ”€ ุฅู†ุดุงุก ู…ุฌู„ุฏ ุงู„ูƒุงุด โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
36
+ RUN mkdir -p /app/.cache/huggingface && \
37
+ chmod -R 777 /app/.cache
38
+
39
+ # โ”€โ”€โ”€ ุงู„ู…ู†ูุฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
40
+ EXPOSE 7860
41
+
42
+ # โ”€โ”€โ”€ ุชุดุบูŠู„ ุงู„ุชุทุจูŠู‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
43
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # โ”€โ”€โ”€ flash_attn Mock โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
2
+ import sys
3
+ import types
4
+ import importlib.util
5
+
6
+ flash_mock = types.ModuleType("flash_attn")
7
+ flash_mock.__version__ = "2.0.0"
8
+ flash_mock.__spec__ = importlib.util.spec_from_loader("flash_attn", loader=None)
9
+ sys.modules["flash_attn"] = flash_mock
10
+ sys.modules["flash_attn.flash_attn_interface"] = types.ModuleType("flash_attn.flash_attn_interface")
11
+ sys.modules["flash_attn.bert_padding"] = types.ModuleType("flash_attn.bert_padding")
12
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
+
14
+ import io
15
+ import time
16
+ import torch
17
+ from PIL import Image
18
+ from transformers import AutoProcessor, AutoModelForCausalLM
19
+ from fastapi import FastAPI, HTTPException, UploadFile, File
20
+ from contextlib import asynccontextmanager
21
+
22
+ MODEL_ID = "microsoft/Florence-2-large-ft"
23
+
24
+ # โ”€โ”€โ”€ ุงู„ุณุคุงู„ ุงู„ุฃุตู„ูŠ + ุชุฃูƒูŠุฏ ุนู„ู‰ ุงู„ูŠุฏ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
25
+ VQA_QUESTION = (
26
+ "Is there a woman or any part of a woman's body in this image? Answer yes or no only."
27
+ )
28
+
29
+ MODEL_DATA = {}
30
+
31
+ @asynccontextmanager
32
+ async def lifespan(app: FastAPI):
33
+ print(f"๐Ÿ“ฅ Loading {MODEL_ID}...")
34
+ start = time.time()
35
+ MODEL_DATA["processor"] = AutoProcessor.from_pretrained(
36
+ MODEL_ID, trust_remote_code=True
37
+ )
38
+ MODEL_DATA["model"] = AutoModelForCausalLM.from_pretrained(
39
+ MODEL_ID,
40
+ torch_dtype=torch.float32,
41
+ trust_remote_code=True,
42
+ attn_implementation="eager"
43
+ ).eval()
44
+ print(f"โœ… Model ready in {time.time()-start:.1f}s")
45
+ yield
46
+ MODEL_DATA.clear()
47
+
48
+ app = FastAPI(
49
+ title="Female Detection API - VQA",
50
+ description="Florence-2-large-ft | VQA",
51
+ version="4.3.0",
52
+ lifespan=lifespan
53
+ )
54
+
55
+ @app.get("/health")
56
+ def health():
57
+ return {"status": "ok", "model_loaded": "model" in MODEL_DATA}
58
+
59
+ def decide(answer: str) -> tuple[str, str]:
60
+ """
61
+ - "no" โ†’ allow โœ…
62
+ - "yes" โ†’ block ๐Ÿ”ด
63
+ - ุฃูŠ ุดูŠุก ุขุฎุฑ โ†’ block ๐Ÿ”ด ู„ู„ุฃู…ุงู†
64
+ """
65
+ a = answer.strip().lower()
66
+ if a == "no" or a.startswith("no"):
67
+ return "allow", "model_answered_no"
68
+ elif "yes" in a:
69
+ return "block", "model_answered_yes"
70
+ else:
71
+ return "block", "unexpected_answer_blocked_for_safety"
72
+
73
+ @app.post("/analyze")
74
+ async def analyze_image(file: UploadFile = File(...)):
75
+
76
+ if not file.content_type.startswith("image/"):
77
+ raise HTTPException(status_code=400, detail="ุงู„ู…ู„ู ู„ูŠุณ ุตูˆุฑุฉ")
78
+
79
+ try:
80
+ image = Image.open(io.BytesIO(await file.read())).convert("RGB")
81
+ except Exception as e:
82
+ raise HTTPException(status_code=400, detail=f"ุฎุทุฃ ููŠ ู‚ุฑุงุกุฉ ุงู„ุตูˆุฑุฉ: {str(e)}")
83
+
84
+ try:
85
+ processor = MODEL_DATA["processor"]
86
+ model = MODEL_DATA["model"]
87
+
88
+ task = "<VQA>"
89
+ prompt = f"{task}{VQA_QUESTION}"
90
+
91
+ inputs = processor(text=prompt, images=image, return_tensors="pt")
92
+
93
+ start_time = time.time()
94
+ with torch.no_grad():
95
+ generated_ids = model.generate(
96
+ input_ids=inputs["input_ids"],
97
+ pixel_values=inputs["pixel_values"],
98
+ max_new_tokens=10,
99
+ num_beams=3,
100
+ do_sample=False
101
+ )
102
+
103
+ generated_text = processor.batch_decode(generated_ids, skip_special_tokens=False)[0]
104
+ parsed = processor.post_process_generation(
105
+ generated_text,
106
+ task=task,
107
+ image_size=(image.width, image.height)
108
+ )
109
+ elapsed = round(time.time() - start_time, 2)
110
+
111
+ answer = parsed.get(task, "").strip()
112
+ decision, reason = decide(answer)
113
+
114
+ return {
115
+ "decision": decision,
116
+ "reason": reason,
117
+ "vqa_answer": answer,
118
+ "question": VQA_QUESTION,
119
+ "execution_time": elapsed,
120
+ "status": "success"
121
+ }
122
+
123
+ except Exception as e:
124
+ raise HTTPException(status_code=500, detail=str(e))
125
+
126
+
127
+ if __name__ == "__main__":
128
+ import uvicorn
129
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ transformers==4.40.0
2
+ timm
3
+ einops
4
+ pillow
5
+ requests
6
+ fastapi
7
+ uvicorn
8
+ pydantic
9
+ torch
10
+ torchvision
11
+ python-multipart