Joseph Ibochi commited on
Commit
0b6fe9c
·
1 Parent(s): 77566f8

update:model mod for handling NAN

Browse files
Files changed (2) hide show
  1. app/app.py +2 -1
  2. app/model.py +30 -14
app/app.py CHANGED
@@ -22,4 +22,5 @@ def match(request: MatchRequest):
22
  result = matcher.predict(request.current_user, request.other_users)
23
  return {"matches": result}
24
  except Exception as e:
25
- return {"error": str(e)}
 
 
22
  result = matcher.predict(request.current_user, request.other_users)
23
  return {"matches": result}
24
  except Exception as e:
25
+ return {"error": str(e)}
26
+
app/model.py CHANGED
@@ -1,4 +1,3 @@
1
- # model.py
2
  import numpy as np
3
  import pandas as pd
4
  from sklearn.preprocessing import OneHotEncoder, MinMaxScaler
@@ -6,18 +5,33 @@ from sklearn.metrics.pairwise import cosine_similarity
6
  from sentence_transformers import SentenceTransformer
7
  from typing import Dict, List
8
 
 
 
 
 
 
 
 
 
9
  class RoommateMatcher:
10
  def __init__(self):
11
  self.text_model = SentenceTransformer('all-MiniLM-L6-v2')
12
- self.financial_encoder = OneHotEncoder(sparse_output=False, handle_unknown="ignore")
 
 
13
  self.scaler = MinMaxScaler()
14
  self.is_fitted = False
15
 
 
 
 
16
  def predict(self, current_user: Dict, other_users: List[Dict]) -> List[Dict]:
17
  if not self.is_fitted and other_users:
18
  self._fit_encoders(other_users)
19
 
20
  others_df = pd.DataFrame(other_users)
 
 
21
  others_df['combined_text'] = others_df.apply(
22
  lambda x: " ".join(filter(None, [
23
  str(x.get('personal_description', '')),
@@ -25,29 +39,30 @@ class RoommateMatcher:
25
  *[str(s) for s in x.get('social_preference', [])]
26
  ])), axis=1
27
  )
28
-
29
  text_embeds = self.text_model.encode(others_df['combined_text'].tolist())
30
- text_block = text_embeds / np.linalg.norm(text_embeds, axis=1, keepdims=True)
31
 
 
32
  fin_block = self.financial_encoder.transform(others_df[['financials']])
33
- fin_block = fin_block / np.linalg.norm(fin_block, axis=1, keepdims=True)
34
 
 
35
  num_features = np.hstack([
36
  np.array([x for x in others_df['location']]),
37
  others_df[['budget_min', 'budget_max']].values
38
  ])
39
- num_block = self.scaler.transform(num_features)
40
- num_block = num_block / np.linalg.norm(num_block, axis=1, keepdims=True)
41
 
 
42
  current_text = self.text_model.encode(" ".join(filter(None, [
43
  str(current_user.get('personal_description', '')),
44
  str(current_user.get('occupation', '')),
45
  *[str(s) for s in current_user.get('social_preference', [])]
46
  ])))
47
- current_text = current_text / np.linalg.norm(current_text)
48
 
49
  current_fin = self.financial_encoder.transform([[current_user['financials']]])
50
- current_fin = current_fin / np.linalg.norm(current_fin)
51
 
52
  current_num = self.scaler.transform([[
53
  current_user['location'][0],
@@ -55,19 +70,21 @@ class RoommateMatcher:
55
  current_user['budget_min'],
56
  current_user['budget_max']
57
  ]])
58
- current_num = current_num / np.linalg.norm(current_num)
59
 
 
60
  combined_existing = np.hstack([
61
  text_block * 0.6,
62
  fin_block * 0.1,
63
  num_block * 0.3
64
  ])
65
  current_block = np.hstack([
66
- current_text.reshape(1, -1) * 0.6,
67
  current_fin * 0.2,
68
  current_num * 0.2
69
  ])
70
 
 
71
  others_df['similarity'] = np.round(
72
  cosine_similarity(current_block, combined_existing)[0] * 100, 2
73
  )
@@ -75,9 +92,8 @@ class RoommateMatcher:
75
  return others_df.sort_values('similarity', ascending=False).head(10).to_dict('records')
76
 
77
  def _fit_encoders(self, users: List[Dict]):
78
- financials = np.array([u['financials'] for u in users]).reshape(-1, 1)
79
  locations = np.array([u['location'] for u in users])
80
  budgets = np.array([[u['budget_min'], u['budget_max']] for u in users])
81
- self.financial_encoder.fit(financials)
82
- self.scaler.fit(np.hstack([locations, budgets]))
83
  self.is_fitted = True
 
 
1
  import numpy as np
2
  import pandas as pd
3
  from sklearn.preprocessing import OneHotEncoder, MinMaxScaler
 
5
  from sentence_transformers import SentenceTransformer
6
  from typing import Dict, List
7
 
8
+
9
+ def safe_normalize(v: np.ndarray) -> np.ndarray:
10
+ """Avoid division by zero when normalizing vectors."""
11
+ norm = np.linalg.norm(v, axis=1, keepdims=True)
12
+ norm[norm == 0] = 1e-6 # prevent division by 0
13
+ return v / norm
14
+
15
+
16
  class RoommateMatcher:
17
  def __init__(self):
18
  self.text_model = SentenceTransformer('all-MiniLM-L6-v2')
19
+ self.financial_encoder = OneHotEncoder(
20
+ sparse_output=False, handle_unknown="ignore"
21
+ )
22
  self.scaler = MinMaxScaler()
23
  self.is_fitted = False
24
 
25
+ # Fit encoder in advance with known categories to avoid all-zero rows
26
+ self.financial_encoder.fit([["split-rent"], ["single-payment"]])
27
+
28
  def predict(self, current_user: Dict, other_users: List[Dict]) -> List[Dict]:
29
  if not self.is_fitted and other_users:
30
  self._fit_encoders(other_users)
31
 
32
  others_df = pd.DataFrame(other_users)
33
+
34
+ # === TEXT VECTOR ===
35
  others_df['combined_text'] = others_df.apply(
36
  lambda x: " ".join(filter(None, [
37
  str(x.get('personal_description', '')),
 
39
  *[str(s) for s in x.get('social_preference', [])]
40
  ])), axis=1
41
  )
 
42
  text_embeds = self.text_model.encode(others_df['combined_text'].tolist())
43
+ text_block = safe_normalize(text_embeds)
44
 
45
+ # === FINANCIAL VECTOR ===
46
  fin_block = self.financial_encoder.transform(others_df[['financials']])
47
+ fin_block = safe_normalize(fin_block)
48
 
49
+ # === NUMERIC VECTOR ===
50
  num_features = np.hstack([
51
  np.array([x for x in others_df['location']]),
52
  others_df[['budget_min', 'budget_max']].values
53
  ])
54
+ num_block = safe_normalize(self.scaler.transform(num_features))
 
55
 
56
+ # === CURRENT USER VECTORS ===
57
  current_text = self.text_model.encode(" ".join(filter(None, [
58
  str(current_user.get('personal_description', '')),
59
  str(current_user.get('occupation', '')),
60
  *[str(s) for s in current_user.get('social_preference', [])]
61
  ])))
62
+ current_text = safe_normalize(current_text.reshape(1, -1))
63
 
64
  current_fin = self.financial_encoder.transform([[current_user['financials']]])
65
+ current_fin = safe_normalize(current_fin)
66
 
67
  current_num = self.scaler.transform([[
68
  current_user['location'][0],
 
70
  current_user['budget_min'],
71
  current_user['budget_max']
72
  ]])
73
+ current_num = safe_normalize(current_num)
74
 
75
+ # === STACK FEATURES ===
76
  combined_existing = np.hstack([
77
  text_block * 0.6,
78
  fin_block * 0.1,
79
  num_block * 0.3
80
  ])
81
  current_block = np.hstack([
82
+ current_text * 0.6,
83
  current_fin * 0.2,
84
  current_num * 0.2
85
  ])
86
 
87
+ # === SIMILARITY ===
88
  others_df['similarity'] = np.round(
89
  cosine_similarity(current_block, combined_existing)[0] * 100, 2
90
  )
 
92
  return others_df.sort_values('similarity', ascending=False).head(10).to_dict('records')
93
 
94
  def _fit_encoders(self, users: List[Dict]):
 
95
  locations = np.array([u['location'] for u in users])
96
  budgets = np.array([[u['budget_min'], u['budget_max']] for u in users])
97
+ numeric_block = np.hstack([locations, budgets])
98
+ self.scaler.fit(numeric_block)
99
  self.is_fitted = True