justKevv commited on
Commit
456ba4c
·
1 Parent(s): 46610a8

Enhance model loading and caching: add support for .pkl files, set cache directories, and improve error handling

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ models/*.pkl filter=lfs diff=lfs merge=lfs -text
Dockerfile CHANGED
@@ -4,6 +4,14 @@ FROM python:3.9-slim
4
  # Set the working directory in the container
5
  WORKDIR /app
6
 
 
 
 
 
 
 
 
 
7
  # Copy the requirements file and install dependencies first to leverage caching
8
  COPY requirements.txt .
9
  RUN pip install --no-cache-dir -r requirements.txt
@@ -16,5 +24,5 @@ COPY . .
16
  EXPOSE 7860
17
 
18
  # --- CORRECTED COMMAND ---
19
- # This now correctly points to the 'app' variable inside the 'api/index.py' file
20
  CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
4
  # Set the working directory in the container
5
  WORKDIR /app
6
 
7
+ # Create cache directories with proper permissions
8
+ RUN mkdir -p /tmp/transformers_cache /tmp/hf_home && \
9
+ chmod 777 /tmp/transformers_cache /tmp/hf_home
10
+
11
+ # Set environment variables for transformers cache
12
+ ENV TRANSFORMERS_CACHE=/tmp/transformers_cache
13
+ ENV HF_HOME=/tmp/hf_home
14
+
15
  # Copy the requirements file and install dependencies first to leverage caching
16
  COPY requirements.txt .
17
  RUN pip install --no-cache-dir -r requirements.txt
 
24
  EXPOSE 7860
25
 
26
  # --- CORRECTED COMMAND ---
27
+ # This correctly points to the 'app' variable inside the 'app/main.py' file
28
  CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"]
api/index.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from app.main import app
2
+
3
+ # This file is required for Vercel deployment
4
+ # It imports the FastAPI app from app.main
app/core/models.py CHANGED
@@ -4,20 +4,30 @@ import os
4
 
5
  MODEL_DIR = "models"
6
 
 
 
 
 
 
 
7
  try:
8
  tfidf_vectorizer = joblib.load(os.path.join(MODEL_DIR, "tfidf_vectorizer.pkl"))
9
  le = joblib.load(os.path.join(MODEL_DIR, "label_encoder.pkl"))
10
  rf_model = joblib.load(os.path.join(MODEL_DIR, "random_forest_model.pkl"))
11
  print("Classification models loaded.")
12
 
13
- sentence_model = SentenceTransformer('all-MiniLM-L6-v2')
 
 
 
 
14
  print("SentenceTransformer model loaded.")
15
 
16
  except FileNotFoundError as e:
17
  print(f"MODEL LOADING ERROR: {e}")
18
  print("Make sure the .pkl files are in the 'models' directory.")
19
- # In a real production app, you might want the app to exit or handle this more gracefully.
20
- tfidf_vectorizer, le, rf_model, sentence_model = None, None, None, None
21
 
22
  except Exception as e:
23
  print(f"An unexpected error occurred during model loading: {e}")
 
 
4
 
5
  MODEL_DIR = "models"
6
 
7
+ # Initialize variables
8
+ tfidf_vectorizer = None
9
+ le = None
10
+ rf_model = None
11
+ sentence_model = None
12
+
13
  try:
14
  tfidf_vectorizer = joblib.load(os.path.join(MODEL_DIR, "tfidf_vectorizer.pkl"))
15
  le = joblib.load(os.path.join(MODEL_DIR, "label_encoder.pkl"))
16
  rf_model = joblib.load(os.path.join(MODEL_DIR, "random_forest_model.pkl"))
17
  print("Classification models loaded.")
18
 
19
+ # Set cache directory to a writable location for Hugging Face Spaces
20
+ os.environ['TRANSFORMERS_CACHE'] = '/tmp/transformers_cache'
21
+ os.environ['HF_HOME'] = '/tmp/hf_home'
22
+
23
+ sentence_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
24
  print("SentenceTransformer model loaded.")
25
 
26
  except FileNotFoundError as e:
27
  print(f"MODEL LOADING ERROR: {e}")
28
  print("Make sure the .pkl files are in the 'models' directory.")
29
+ raise e # Re-raise to prevent the application from starting with None models
 
30
 
31
  except Exception as e:
32
  print(f"An unexpected error occurred during model loading: {e}")
33
+ raise e # Re-raise to prevent the application from starting with None models
app/main.py CHANGED
@@ -1,14 +1,14 @@
1
- from fastapi import FastAPI
2
- from .api.routes import recommendations
3
-
4
- app = FastAPI(
5
- title="Student Recommendation API",
6
- description="An API that uses machine learning to predict job categories and recommend internships.",
7
- version="1.0.0"
8
- )
9
-
10
- app.include_router(recommendations.router, prefix="/api/v1")
11
-
12
- @app.get("/", tags=["Root"])
13
- def read_root():
14
- return {"message": "Welcome to the Student Recommendation API"}
 
1
+ from fastapi import FastAPI
2
+ from .api.routes import recommendations
3
+
4
+ app = FastAPI(
5
+ title="Student Recommendation API",
6
+ description="An API that uses machine learning to predict job categories and recommend internships.",
7
+ version="1.0.0"
8
+ )
9
+
10
+ app.include_router(recommendations.router, prefix="/api/v1")
11
+
12
+ @app.get("/", tags=["Root"])
13
+ def read_root():
14
+ return {"message": "Welcome to the Student Recommendation API"}
app/services/ranking.py CHANGED
@@ -55,6 +55,9 @@ def geo_coords(city_name: str) -> tuple | None:
55
 
56
  def get_category_prediction(profile_text: str) -> str:
57
  """Processes text and predicts the job category."""
 
 
 
58
  cleaned_text = profile_text.lower()
59
  vectorized_text = tfidf_vectorizer.transform([cleaned_text])
60
  prediction_encoded = rf_model.predict(vectorized_text)[0]
@@ -63,6 +66,8 @@ def get_category_prediction(profile_text: str) -> str:
63
 
64
  def get_ranked_internships(request: RecommendationRequest) -> list[int]:
65
  """Performs two-stage ranking with dynamic geocoding."""
 
 
66
 
67
  profile_text_to_encode = request.profile_text
68
 
 
55
 
56
  def get_category_prediction(profile_text: str) -> str:
57
  """Processes text and predicts the job category."""
58
+ if not all([tfidf_vectorizer, le, rf_model]):
59
+ raise RuntimeError("Classification models are not properly loaded")
60
+
61
  cleaned_text = profile_text.lower()
62
  vectorized_text = tfidf_vectorizer.transform([cleaned_text])
63
  prediction_encoded = rf_model.predict(vectorized_text)[0]
 
66
 
67
  def get_ranked_internships(request: RecommendationRequest) -> list[int]:
68
  """Performs two-stage ranking with dynamic geocoding."""
69
+ if not sentence_model:
70
+ raise RuntimeError("SentenceTransformer model is not properly loaded")
71
 
72
  profile_text_to_encode = request.profile_text
73
 
requirements.txt CHANGED
@@ -2,8 +2,8 @@
2
  fastapi
3
  uvicorn
4
 
5
- # Machine Learning & Data
6
- scikit-learn
7
  joblib
8
  sentence-transformers
9
  torch
 
2
  fastapi
3
  uvicorn
4
 
5
+ # Machine Learning & Data - Pin scikit-learn version to match your models
6
+ scikit-learn==1.5.1
7
  joblib
8
  sentence-transformers
9
  torch