Optimizations

#1
Files changed (3) hide show
  1. .DS_Store +0 -0
  2. Dockerfile +20 -18
  3. 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
- # 1) Configure HF cache locations up front
 
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) Create non-root user
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
- python3 python3-pip python3-dev \
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) Prep model cache dir and HF cache, set ownership
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) **Switch to non-root user** and pre-download your model
31
  USER user
32
  RUN python3 - <<EOF
33
- from huggingface_hub import snapshot_download
34
  import os
 
35
  snapshot_download(
36
- repo_id="numind/NuExtract-1.5", # <-- your HF model ID
37
- local_dir="/home/user/app/model_cache", # <-- where your FastAPI will load from
38
- cache_dir=os.getenv("HF_HUB_CACHE"), # <-- HF’s own cache
39
- resume_download=True
40
  )
41
  EOF
42
 
43
- # 7) Copy your code and launch
44
  COPY --chown=user . .
45
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
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
- # MODEL_CACHE = "/home/user/app/model_cache"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- print ("Model Running ", model_name)
 
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
- # bnb_4bit_compute_dtype=torch.float16
 
 
 
 
 
 
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. N’inclure que les permis explicitement mentionnés dans le texte. S’il n’y a aucune mention de permis, renvoyer une liste vide.\n"
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, num_beams=1, use_cache=False)
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