Spaces:
Runtime error
Runtime error
Optimizations
#1
by Marcin-XStudio - opened
- .DS_Store +0 -0
- Dockerfile +20 -18
- app.py +37 -36
.DS_Store
DELETED
|
Binary file (8.2 kB)
|
|
|
Dockerfile
CHANGED
|
@@ -1,45 +1,47 @@
|
|
| 1 |
FROM nvidia/cuda:12.0.0-cudnn8-devel-ubuntu22.04
|
| 2 |
|
| 3 |
-
|
|
|
|
| 4 |
ENV HF_HOME="/home/user/.cache/huggingface" \
|
| 5 |
HF_HUB_CACHE="/home/user/.cache/huggingface/hub" \
|
| 6 |
TRANSFORMERS_CACHE="/home/user/.cache/huggingface/transformers"
|
| 7 |
|
| 8 |
-
# 2)
|
| 9 |
RUN useradd -m -u 1000 user
|
| 10 |
|
| 11 |
-
# 3) Install Python & system libs
|
| 12 |
RUN apt-get update && \
|
| 13 |
apt-get install -y --no-install-recommends \
|
| 14 |
-
|
| 15 |
-
build-essential git libpoppler-cpp-dev poppler-utils libmagic-dev && \
|
| 16 |
rm -rf /var/lib/apt/lists/*
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
WORKDIR /home/user/app
|
| 19 |
COPY --chown=user requirements.txt .
|
| 20 |
-
|
| 21 |
-
# 4) Install Python deps
|
| 22 |
RUN pip install --no-cache-dir torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118
|
| 23 |
RUN pip install --no-cache-dir huggingface_hub
|
| 24 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 25 |
|
| 26 |
-
# 5)
|
| 27 |
-
RUN mkdir -p /home/user/app/model_cache $HF_HUB_CACHE
|
| 28 |
-
chown -R user:user /home/user/app /home/user/.cache/huggingface
|
| 29 |
|
| 30 |
-
# 6)
|
| 31 |
USER user
|
| 32 |
RUN python3 - <<EOF
|
| 33 |
-
from huggingface_hub import snapshot_download
|
| 34 |
import os
|
|
|
|
| 35 |
snapshot_download(
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
resume_download=True
|
| 40 |
)
|
| 41 |
EOF
|
| 42 |
|
| 43 |
-
# 7)
|
| 44 |
COPY --chown=user . .
|
| 45 |
-
CMD ["uvicorn",
|
|
|
|
| 1 |
FROM nvidia/cuda:12.0.0-cudnn8-devel-ubuntu22.04
|
| 2 |
|
| 3 |
+
|
| 4 |
+
# 1) Variables HF avant tout
|
| 5 |
ENV HF_HOME="/home/user/.cache/huggingface" \
|
| 6 |
HF_HUB_CACHE="/home/user/.cache/huggingface/hub" \
|
| 7 |
TRANSFORMERS_CACHE="/home/user/.cache/huggingface/transformers"
|
| 8 |
|
| 9 |
+
# 2) Créer l’utilisateur non-root
|
| 10 |
RUN useradd -m -u 1000 user
|
| 11 |
|
|
|
|
| 12 |
RUN apt-get update && \
|
| 13 |
apt-get install -y --no-install-recommends \
|
| 14 |
+
python3 python3-pip python3-dev && \
|
|
|
|
| 15 |
rm -rf /var/lib/apt/lists/*
|
| 16 |
|
| 17 |
+
# 3) Installer dépendances système
|
| 18 |
+
RUN apt-get update && apt-get install -y \
|
| 19 |
+
build-essential git libpoppler-cpp-dev poppler-utils libmagic-dev python3-dev \
|
| 20 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 21 |
+
|
| 22 |
+
# 4) Copier requirements et installer libs Python
|
| 23 |
WORKDIR /home/user/app
|
| 24 |
COPY --chown=user requirements.txt .
|
|
|
|
|
|
|
| 25 |
RUN pip install --no-cache-dir torch torchvision --extra-index-url https://download.pytorch.org/whl/cu118
|
| 26 |
RUN pip install --no-cache-dir huggingface_hub
|
| 27 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 28 |
|
| 29 |
+
# 5) Pré-créer model_cache et HF_HOME, puis chown
|
| 30 |
+
RUN mkdir -p /home/user/app/model_cache $HF_HUB_CACHE \
|
| 31 |
+
&& chown -R user:user /home/user/app /home/user/.cache/huggingface
|
| 32 |
|
| 33 |
+
# 6) Télécharger le modèle au build-time sous l’utilisateur non-root
|
| 34 |
USER user
|
| 35 |
RUN python3 - <<EOF
|
|
|
|
| 36 |
import os
|
| 37 |
+
from huggingface_hub import snapshot_download
|
| 38 |
snapshot_download(
|
| 39 |
+
repo_id="numind/NuExtract-1.5-tiny",
|
| 40 |
+
local_dir="/home/user/app/model_cache",
|
| 41 |
+
cache_dir=os.getenv("HF_HUB_CACHE")
|
|
|
|
| 42 |
)
|
| 43 |
EOF
|
| 44 |
|
| 45 |
+
# 7) Copier le reste du code et démarrer
|
| 46 |
COPY --chown=user . .
|
| 47 |
+
CMD ["uvicorn","app:app","--host","0.0.0.0","--port","7860"]
|
app.py
CHANGED
|
@@ -12,27 +12,52 @@ from supabase import create_client
|
|
| 12 |
from huggingface_hub import snapshot_download
|
| 13 |
from transformers import BitsAndBytesConfig, AutoModelForCausalLM
|
| 14 |
|
|
|
|
|
|
|
| 15 |
load_dotenv()
|
| 16 |
app = FastAPI()
|
| 17 |
|
| 18 |
-
model_name = "numind/NuExtract-1.5"
|
| 19 |
|
| 20 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
|
| 23 |
dtype = torch.float16 if device in ("mps", "cuda") else torch.float32
|
| 24 |
|
| 25 |
print("CUDA available:", torch.cuda.is_available()) # True
|
| 26 |
print("Device name:", torch.cuda.get_device_name(0))
|
| 27 |
-
|
|
|
|
| 28 |
|
| 29 |
# If lower memory usage needed:
|
| 30 |
|
| 31 |
# bnb_config = BitsAndBytesConfig(
|
| 32 |
# load_in_4bit=True,
|
| 33 |
# bnb_4bit_use_double_quant=True,
|
| 34 |
-
# bnb_4bit_quant_type="nf4"
|
| 35 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
# )
|
| 37 |
|
| 38 |
@app.on_event("startup")
|
|
@@ -48,30 +73,27 @@ def startup_supabase():
|
|
| 48 |
def load_model():
|
| 49 |
print("Loading model and tokenizer...", flush=True)
|
| 50 |
global model, tokenizer
|
|
|
|
|
|
|
|
|
|
| 51 |
model = AutoModelForCausalLM.from_pretrained(
|
| 52 |
-
# model_name,
|
| 53 |
-
# cache_dir=MODEL_CACHE,
|
| 54 |
MODEL_CACHE,
|
| 55 |
local_files_only=True,
|
| 56 |
torch_dtype=dtype,
|
| 57 |
trust_remote_code=True,
|
| 58 |
# quantization_config=bnb_config,
|
|
|
|
| 59 |
device_map="auto"
|
| 60 |
).to(device).eval()
|
|
|
|
| 61 |
tokenizer = AutoTokenizer.from_pretrained(
|
| 62 |
-
# model_name,
|
| 63 |
-
# cache_dir=MODEL_CACHE,
|
| 64 |
MODEL_CACHE,
|
| 65 |
local_files_only=True,
|
| 66 |
trust_remote_code=True,
|
| 67 |
device_map="auto"
|
| 68 |
)
|
| 69 |
-
# Check this optimization!
|
| 70 |
-
# if torch.__version__ >= "2.0":
|
| 71 |
-
# model = torch.compile(model)
|
| 72 |
print("✅ Model and tokenizer loaded from", MODEL_CACHE)
|
| 73 |
|
| 74 |
-
|
| 75 |
def predict_NuExtract(texts, template, batch_size=1, max_length=5096, max_new_tokens=1024):
|
| 76 |
print("Starting NuExtract prediction...", flush=True)
|
| 77 |
start_time = time.perf_counter()
|
|
@@ -80,30 +102,9 @@ def predict_NuExtract(texts, template, batch_size=1, max_length=5096, max_new_to
|
|
| 80 |
"<|input|>\n"
|
| 81 |
"### Instruction:\n"
|
| 82 |
"Remplis la template JSON avec les informations extraits du texte.\n"
|
| 83 |
-
"Extraire le nom du candidat tel qu’il apparaît sur la première ligne du document (souvent en majuscules ou en plus gros), et le mettre dans nom. Si le nom n’est pas trouvé, renvoyer une chaîne vide.\n"
|
| 84 |
-
"Si le text contient des mentions de diplomes ou de titres de formations, certificats ou Attestation on les considère comme education, pas experience\n"
|
| 85 |
-
"Pour chaque bloc formation ou expérience où une date est mentionnée (MM/YYYY ou mois/YYYY etc), remplir systématiquement annee_debut et annee_fin.\n"
|
| 86 |
-
"Ne jamais laisser ces champs vides si la date est dans le texte.\n"
|
| 87 |
-
"Si une seule date specifié pour une experience ou une formation met la même date pour start_date et end_date. Exemples : \n"
|
| 88 |
-
"Exemple 1 : \n"
|
| 89 |
-
"Texte : \"...01/2024 – En cours...\"\n"
|
| 90 |
-
"Output : \"start_date\": \"01/2024\", \"end_date\": \"01/2024\"\n"
|
| 91 |
-
"Exemple 2 : \n"
|
| 92 |
-
"Texte : \"...mai 2025...\"\n"
|
| 93 |
-
"Output : \"start_date\": \"05/2025\", \"end_date\": \"05/2025\"\n"
|
| 94 |
-
"Exemple 3 : \n"
|
| 95 |
-
"Texte : \"...mai 2025 – juin 2026...\"\n"
|
| 96 |
-
"Output : \"start_date\": \"05/2025\", \"end_date\": \"06/2026\"\n"
|
| 97 |
-
"Extraction des dates est *très* importante. Ne laisse jamais les dates vides\n"
|
| 98 |
"Exemples types de formations : CAP Boucherie, Licence Pro Métiers de l’Énergétique, Baccalauréat Général\n"
|
| 99 |
"Exemples catégories de formations : Transport, énergie, langues, esthétique\n"
|
| 100 |
-
"Exemples mobilités : permis B, permis C, permis D. si y'a juste la mention de permis on considère que c'est le permis B
|
| 101 |
-
"### Exemple 1 (avec permis) \n"
|
| 102 |
-
"Texte : \"...j’ai obtenu mon permis B en 2015...\"\n"
|
| 103 |
-
"Output : \"types_des_permis_de_conduire\": [\"permis B\"]\n"
|
| 104 |
-
"### Exemple 2 (sans permis) \n"
|
| 105 |
-
"Texte : \"...j’ai étudié à l’Université de Paris...\"\n"
|
| 106 |
-
"Output : \"types_des_permis_de_conduire\": []\n"
|
| 107 |
"Output *only* the completed JSON.\n"
|
| 108 |
"### Template:\n"
|
| 109 |
f"{template_str}\n"
|
|
@@ -125,7 +126,7 @@ def predict_NuExtract(texts, template, batch_size=1, max_length=5096, max_new_to
|
|
| 125 |
max_length=max_length
|
| 126 |
).to(device)
|
| 127 |
print(f"Generating outputs with model for batch {i//batch_size+1}...", flush=True)
|
| 128 |
-
ids = model.generate(**enc, max_new_tokens=max_new_tokens,
|
| 129 |
outputs += tokenizer.batch_decode(ids, skip_special_tokens=True)
|
| 130 |
print("Outputs generated.", flush=True)
|
| 131 |
elapsed = time.perf_counter() - start_time
|
|
|
|
| 12 |
from huggingface_hub import snapshot_download
|
| 13 |
from transformers import BitsAndBytesConfig, AutoModelForCausalLM
|
| 14 |
|
| 15 |
+
|
| 16 |
+
|
| 17 |
load_dotenv()
|
| 18 |
app = FastAPI()
|
| 19 |
|
|
|
|
| 20 |
|
| 21 |
+
# // FOR RUNNING IN SPACES
|
| 22 |
+
model_name = "numind/NuExtract-1.5-tiny"
|
| 23 |
+
# Path inside your container
|
| 24 |
+
# MODEL_PATH = "/app/model_cache/models--numind--NuExtract-1.5-tiny/snapshots/df52efb3109d324cd52b30728f9e3fdedf19f742"
|
| 25 |
+
# If you used local_dir="model", snapshot_download will still create models--… subfolder.
|
| 26 |
+
# You can also symlink or copy it to /app/model directly in Dockerfile.
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# MODEL_PATH = "/app/model_cache"
|
| 30 |
+
# model_cache_path = snapshot_download(
|
| 31 |
+
# repo_id="numind/NuExtract-1.5-tiny",
|
| 32 |
+
# local_dir="/app/model_cache", # <-- direct destination
|
| 33 |
+
# cache_dir="/app/model_cache/hf_cache"
|
| 34 |
+
# )
|
| 35 |
+
|
| 36 |
+
MODEL_CACHE = "/home/user/app/model_cache"
|
| 37 |
+
|
| 38 |
+
print(">>> MODEL CACHE PATH:", MODEL_CACHE, os.listdir(MODEL_CACHE))
|
| 39 |
|
| 40 |
device = "cuda" if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu"
|
| 41 |
dtype = torch.float16 if device in ("mps", "cuda") else torch.float32
|
| 42 |
|
| 43 |
print("CUDA available:", torch.cuda.is_available()) # True
|
| 44 |
print("Device name:", torch.cuda.get_device_name(0))
|
| 45 |
+
# bnb_config = BitsAndBytesConfig(load_in_8bit=True)
|
| 46 |
+
|
| 47 |
|
| 48 |
# If lower memory usage needed:
|
| 49 |
|
| 50 |
# bnb_config = BitsAndBytesConfig(
|
| 51 |
# load_in_4bit=True,
|
| 52 |
# bnb_4bit_use_double_quant=True,
|
| 53 |
+
# bnb_4bit_quant_type="nf4"
|
| 54 |
+
# )
|
| 55 |
+
# model = AutoModelForCausalLM.from_pretrained(
|
| 56 |
+
# MODEL_CACHE,
|
| 57 |
+
# quantization_config=bnb_config,
|
| 58 |
+
# device_map="auto",
|
| 59 |
+
# local_files_only=True,
|
| 60 |
+
# trust_remote_code=True
|
| 61 |
# )
|
| 62 |
|
| 63 |
@app.on_event("startup")
|
|
|
|
| 73 |
def load_model():
|
| 74 |
print("Loading model and tokenizer...", flush=True)
|
| 75 |
global model, tokenizer
|
| 76 |
+
# model = AutoModelForCausalLM.from_pretrained(
|
| 77 |
+
# model_name, torch_dtype=dtype, trust_remote_code=True
|
| 78 |
+
# )
|
| 79 |
model = AutoModelForCausalLM.from_pretrained(
|
|
|
|
|
|
|
| 80 |
MODEL_CACHE,
|
| 81 |
local_files_only=True,
|
| 82 |
torch_dtype=dtype,
|
| 83 |
trust_remote_code=True,
|
| 84 |
# quantization_config=bnb_config,
|
| 85 |
+
# no_split_module_classes=["Block"],
|
| 86 |
device_map="auto"
|
| 87 |
).to(device).eval()
|
| 88 |
+
# tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
|
| 89 |
tokenizer = AutoTokenizer.from_pretrained(
|
|
|
|
|
|
|
| 90 |
MODEL_CACHE,
|
| 91 |
local_files_only=True,
|
| 92 |
trust_remote_code=True,
|
| 93 |
device_map="auto"
|
| 94 |
)
|
|
|
|
|
|
|
|
|
|
| 95 |
print("✅ Model and tokenizer loaded from", MODEL_CACHE)
|
| 96 |
|
|
|
|
| 97 |
def predict_NuExtract(texts, template, batch_size=1, max_length=5096, max_new_tokens=1024):
|
| 98 |
print("Starting NuExtract prediction...", flush=True)
|
| 99 |
start_time = time.perf_counter()
|
|
|
|
| 102 |
"<|input|>\n"
|
| 103 |
"### Instruction:\n"
|
| 104 |
"Remplis la template JSON avec les informations extraits du texte.\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
"Exemples types de formations : CAP Boucherie, Licence Pro Métiers de l’Énergétique, Baccalauréat Général\n"
|
| 106 |
"Exemples catégories de formations : Transport, énergie, langues, esthétique\n"
|
| 107 |
+
"Exemples mobilités : permis B, permis C, permis D. si y'a juste la mention de permis on considère que c'est le permis B\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
"Output *only* the completed JSON.\n"
|
| 109 |
"### Template:\n"
|
| 110 |
f"{template_str}\n"
|
|
|
|
| 126 |
max_length=max_length
|
| 127 |
).to(device)
|
| 128 |
print(f"Generating outputs with model for batch {i//batch_size+1}...", flush=True)
|
| 129 |
+
ids = model.generate(**enc, max_new_tokens=max_new_tokens, use_cache=False)
|
| 130 |
outputs += tokenizer.batch_decode(ids, skip_special_tokens=True)
|
| 131 |
print("Outputs generated.", flush=True)
|
| 132 |
elapsed = time.perf_counter() - start_time
|