sairika commited on
Commit
eb66ca3
Β·
verified Β·
1 Parent(s): f4afa30

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +415 -120
app.py CHANGED
@@ -1,179 +1,474 @@
1
-
2
  import gradio as gr
3
- import pickle
4
  import pandas as pd
5
  import numpy as np
6
- from surprise import SVD
 
 
 
7
  import warnings
8
  warnings.filterwarnings('ignore')
9
 
10
- # Load models and data
11
- print("Loading models...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  with open('svd_model.pkl', 'rb') as f:
13
  svd_model = pickle.load(f)
14
 
 
 
 
 
 
 
15
  with open('movies.pkl', 'rb') as f:
16
  movies = pickle.load(f)
17
 
18
  with open('ratings.pkl', 'rb') as f:
19
  ratings = pickle.load(f)
20
 
21
- print("Models loaded successfully!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- def recommend_movies(user_id, num_recommendations, min_rating):
24
- """
25
- Generate movie recommendations for a user
26
- """
27
- try:
28
- user_id = int(user_id)
29
- num_recommendations = int(num_recommendations)
30
- min_rating = float(min_rating)
31
-
32
- # Check if user exists
33
- if user_id not in ratings['userId'].values:
34
- return f"⚠️ User ID {user_id} not found in database. Please try a different user ID (1-{ratings['userId'].max()})."
35
-
36
- # Get all movies
37
- all_movie_ids = movies['movieId'].unique()
38
-
39
- # Get movies the user has already rated
40
- rated_movies = ratings[ratings['userId'] == user_id]['movieId'].values
41
-
42
- # Get movies the user hasn't rated
43
- movies_to_predict = [mid for mid in all_movie_ids if mid not in rated_movies]
44
-
45
- # Predict ratings
46
- predictions = []
47
- for movie_id in movies_to_predict:
48
  pred = svd_model.predict(user_id, movie_id)
49
  if pred.est >= min_rating:
50
  predictions.append({
51
  'movieId': movie_id,
52
  'predicted_rating': pred.est
53
  })
54
-
55
- if not predictions:
56
- return f"No movies found with predicted rating >= {min_rating}. Try lowering the minimum rating."
57
-
58
- # Sort and get top N
59
- predictions_df = pd.DataFrame(predictions)
60
- predictions_df = predictions_df.sort_values('predicted_rating', ascending=False)
61
- top_recommendations = predictions_df.head(num_recommendations)
62
-
63
- # Merge with movie details
64
- recommendations = top_recommendations.merge(movies, on='movieId')
65
- recommendations['predicted_rating'] = recommendations['predicted_rating'].round(2)
66
-
67
- # Format output
68
- output = f"🎬 Top {len(recommendations)} Movie Recommendations for User {user_id}\n\n"
69
-
70
- for idx, row in recommendations.iterrows():
71
- output += f"{idx + 1}. **{row['title']}**\n"
72
- output += f" ⭐ Predicted Rating: {row['predicted_rating']}/5.0\n"
73
- output += f" 🎭 Genres: {row['genres']}\n\n"
74
-
75
- return output
76
 
77
- except Exception as e:
78
- return f"❌ Error: {str(e)}"
 
 
 
 
 
 
 
79
 
80
- def get_user_history(user_id):
81
- """
82
- Get user's rating history
83
- """
84
  try:
85
  user_id = int(user_id)
86
 
 
87
  if user_id not in ratings['userId'].values:
88
- return f"⚠️ User ID {user_id} not found."
89
 
90
- user_ratings = ratings[ratings['userId'] == user_id].merge(movies, on='movieId')
91
- user_ratings = user_ratings.sort_values('rating', ascending=False).head(10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- output = f"πŸ“Š User {user_id}'s Top Rated Movies:\n\n"
 
94
 
95
- for idx, row in user_ratings.iterrows():
96
- output += f"β€’ **{row['title']}** - ⭐ {row['rating']}/5.0\n"
97
- output += f" Genres: {row['genres']}\n\n"
98
 
99
- return output
100
-
101
  except Exception as e:
102
- return f"❌ Error: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  # Create Gradio interface
105
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
106
  gr.Markdown(
107
  """
108
- # 🎬 MovieLens Recommendation System
109
- ### Powered by SVD Matrix Factorization
 
110
 
111
- Get personalized movie recommendations based on collaborative filtering!
 
 
 
 
112
  """
113
  )
114
 
115
- with gr.Tab("🎯 Get Recommendations"):
116
  with gr.Row():
117
- with gr.Column():
118
  user_id_input = gr.Number(
119
- label="User ID",
120
- value=1,
121
- info=f"Enter a user ID (1 to {ratings['userId'].max()})"
 
122
  )
123
- num_rec_input = gr.Slider(
124
- minimum=5,
125
- maximum=20,
126
- value=10,
 
 
 
 
 
 
127
  step=1,
128
- label="Number of Recommendations"
 
129
  )
130
- min_rating_input = gr.Slider(
131
- minimum=1.0,
132
- maximum=5.0,
133
- value=3.5,
134
  step=0.5,
135
- label="Minimum Predicted Rating"
 
 
 
 
 
 
 
 
 
136
  )
137
- recommend_btn = gr.Button("🎬 Get Recommendations", variant="primary")
138
-
139
- with gr.Column():
140
- recommendations_output = gr.Markdown(label="Recommendations")
141
 
 
 
 
 
 
 
 
 
142
  recommend_btn.click(
143
- fn=recommend_movies,
144
- inputs=[user_id_input, num_rec_input, min_rating_input],
145
  outputs=recommendations_output
146
  )
 
 
 
 
 
 
147
 
148
- with gr.Tab("πŸ“Š User History"):
 
149
  with gr.Row():
150
- with gr.Column():
151
- history_user_id = gr.Number(
152
- label="User ID",
153
- value=1,
154
- info="Enter a user ID to see their rating history"
155
- )
156
- history_btn = gr.Button("πŸ“Š View History", variant="primary")
157
-
158
- with gr.Column():
159
- history_output = gr.Markdown(label="User History")
160
 
161
- history_btn.click(
162
- fn=get_user_history,
163
- inputs=history_user_id,
164
- outputs=history_output
 
 
 
 
 
 
 
 
 
 
 
 
165
  )
166
 
167
- gr.Markdown(
168
- """
169
- ---
170
- ### πŸ“ˆ Model Information
171
- - **Algorithm**: SVD (Singular Value Decomposition)
172
- - **Dataset**: MovieLens Small (100K ratings)
173
- - **Evaluation Metrics**: RMSE, Precision@K, Recall@K, NDCG@K
174
- - **Best Performance**: Lowest RMSE and Highest NDCG among tested models
175
- """
176
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
 
178
  if __name__ == "__main__":
179
- demo.launch()
 
 
1
  import gradio as gr
 
2
  import pandas as pd
3
  import numpy as np
4
+ import pickle
5
+ import torch
6
+ import torch.nn as nn
7
+ from surprise import SVD, KNNBasic
8
  import warnings
9
  warnings.filterwarnings('ignore')
10
 
11
+ # ============================================================================
12
+ # NEURAL COLLABORATIVE FILTERING MODEL
13
+ # ============================================================================
14
+ class NeuralCollaborativeFiltering(nn.Module):
15
+ def __init__(self, n_users, n_items, embedding_dim=64, hidden_layers=[128, 64, 32]):
16
+ super(NeuralCollaborativeFiltering, self).__init__()
17
+
18
+ # GMF Embeddings
19
+ self.gmf_user_embedding = nn.Embedding(n_users, embedding_dim)
20
+ self.gmf_item_embedding = nn.Embedding(n_items, embedding_dim)
21
+
22
+ # MLP Embeddings
23
+ self.mlp_user_embedding = nn.Embedding(n_users, embedding_dim)
24
+ self.mlp_item_embedding = nn.Embedding(n_items, embedding_dim)
25
+
26
+ # MLP Layers
27
+ mlp_layers = []
28
+ input_size = embedding_dim * 2
29
+ for hidden_size in hidden_layers:
30
+ mlp_layers.append(nn.Linear(input_size, hidden_size))
31
+ mlp_layers.append(nn.ReLU())
32
+ mlp_layers.append(nn.Dropout(0.2))
33
+ input_size = hidden_size
34
+
35
+ self.mlp = nn.Sequential(*mlp_layers)
36
+
37
+ # Final prediction layer
38
+ self.output = nn.Linear(embedding_dim + hidden_layers[-1], 1)
39
+
40
+ def forward(self, user_ids, item_ids):
41
+ gmf_user = self.gmf_user_embedding(user_ids)
42
+ gmf_item = self.gmf_item_embedding(item_ids)
43
+ gmf_vector = gmf_user * gmf_item
44
+
45
+ mlp_user = self.mlp_user_embedding(user_ids)
46
+ mlp_item = self.mlp_item_embedding(item_ids)
47
+ mlp_vector = torch.cat([mlp_user, mlp_item], dim=-1)
48
+ mlp_vector = self.mlp(mlp_vector)
49
+
50
+ combined = torch.cat([gmf_vector, mlp_vector], dim=-1)
51
+ output = self.output(combined)
52
+ return output.squeeze()
53
+
54
+ # ============================================================================
55
+ # HYBRID RECOMMENDER CLASS
56
+ # ============================================================================
57
+ class HybridRecommender:
58
+ def __init__(self, ncf_model, svd_model, item_mapping, reverse_item_mapping,
59
+ ratings, movies, ncf_weight=0.65, svd_weight=0.35):
60
+ self.ncf_model = ncf_model
61
+ self.svd_model = svd_model
62
+ self.item_mapping = item_mapping
63
+ self.reverse_item_mapping = reverse_item_mapping
64
+ self.ratings = ratings
65
+ self.movies = movies
66
+ self.ncf_weight = ncf_weight
67
+ self.svd_weight = svd_weight
68
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
69
+ self.ncf_model.to(self.device)
70
+ self.ncf_model.eval()
71
+
72
+ def recommend_movies(self, user_id, N=10, min_rating=3.5):
73
+ all_movie_ids = self.movies['movieId'].unique()
74
+ rated_movies = self.ratings[self.ratings['userId'] == user_id]['movieId'].values
75
+ movies_to_predict = [mid for mid in all_movie_ids if mid not in rated_movies]
76
+
77
+ predictions = []
78
+ with torch.no_grad():
79
+ for movie_id in movies_to_predict:
80
+ # NCF prediction
81
+ if movie_id in self.reverse_item_mapping:
82
+ user_tensor = torch.LongTensor([user_id - 1]).to(self.device)
83
+ item_tensor = torch.LongTensor([self.reverse_item_mapping[movie_id]]).to(self.device)
84
+ ncf_pred = self.ncf_model(user_tensor, item_tensor).item()
85
+ ncf_pred = max(0.5, min(5.0, ncf_pred))
86
+ else:
87
+ ncf_pred = 3.0
88
+
89
+ # SVD prediction
90
+ try:
91
+ svd_pred = self.svd_model.predict(user_id, movie_id).est
92
+ except:
93
+ svd_pred = 3.0
94
+
95
+ # Hybrid prediction
96
+ hybrid_pred = (self.ncf_weight * ncf_pred + self.svd_weight * svd_pred)
97
+
98
+ if hybrid_pred >= min_rating:
99
+ predictions.append({
100
+ 'movieId': movie_id,
101
+ 'predicted_rating': hybrid_pred,
102
+ 'ncf_rating': ncf_pred,
103
+ 'svd_rating': svd_pred
104
+ })
105
+
106
+ if not predictions:
107
+ return pd.DataFrame()
108
+
109
+ predictions_df = pd.DataFrame(predictions)
110
+ predictions_df = predictions_df.sort_values('predicted_rating', ascending=False).head(N)
111
+ recommendations = predictions_df.merge(self.movies, on='movieId')
112
+ recommendations['predicted_rating'] = recommendations['predicted_rating'].round(2)
113
+ recommendations['ncf_rating'] = recommendations['ncf_rating'].round(2)
114
+ recommendations['svd_rating'] = recommendations['svd_rating'].round(2)
115
+
116
+ return recommendations[['title', 'genres', 'predicted_rating', 'ncf_rating', 'svd_rating']]
117
+
118
+ # ============================================================================
119
+ # LOAD MODELS AND DATA
120
+ # ============================================================================
121
+ print("Loading models and data...")
122
+
123
+ # Load saved models and data
124
  with open('svd_model.pkl', 'rb') as f:
125
  svd_model = pickle.load(f)
126
 
127
+ with open('item_based_cf.pkl', 'rb') as f:
128
+ item_based_cf = pickle.load(f)
129
+
130
+ with open('user_based_cf.pkl', 'rb') as f:
131
+ user_based_cf = pickle.load(f)
132
+
133
  with open('movies.pkl', 'rb') as f:
134
  movies = pickle.load(f)
135
 
136
  with open('ratings.pkl', 'rb') as f:
137
  ratings = pickle.load(f)
138
 
139
+ # Load NCF model if exists
140
+ try:
141
+ # Prepare item mapping
142
+ ratings['movieId_cat'] = ratings['movieId'].astype('category')
143
+ item_mapping = dict(enumerate(ratings['movieId_cat'].cat.categories))
144
+ reverse_item_mapping = {v: k for k, v in item_mapping.items()}
145
+
146
+ n_users = ratings['userId'].nunique()
147
+ n_items = ratings['movieId'].nunique()
148
+
149
+ ncf_model = NeuralCollaborativeFiltering(n_users, n_items)
150
+ ncf_model.load_state_dict(torch.load('ncf_model_best.pth', map_location='cpu'))
151
+ ncf_model.eval()
152
+
153
+ # Create hybrid recommender
154
+ hybrid_recommender = HybridRecommender(
155
+ ncf_model=ncf_model,
156
+ svd_model=svd_model,
157
+ item_mapping=item_mapping,
158
+ reverse_item_mapping=reverse_item_mapping,
159
+ ratings=ratings,
160
+ movies=movies
161
+ )
162
+ use_hybrid = True
163
+ print("βœ“ Hybrid model loaded successfully!")
164
+ except Exception as e:
165
+ print(f"⚠ Could not load NCF model: {e}")
166
+ print("Using SVD model only...")
167
+ use_hybrid = False
168
 
169
+ # ============================================================================
170
+ # RECOMMENDATION FUNCTIONS
171
+ # ============================================================================
172
+ def get_user_history(user_id):
173
+ """Get user's rating history"""
174
+ user_ratings = ratings[ratings['userId'] == user_id].merge(movies, on='movieId')
175
+ user_ratings = user_ratings.sort_values('rating', ascending=False).head(10)
176
+
177
+ if len(user_ratings) == 0:
178
+ return pd.DataFrame({"Message": ["No rating history found for this user"]})
179
+
180
+ return user_ratings[['title', 'genres', 'rating', 'timestamp']]
181
+
182
+ def recommend_with_svd(user_id, n_recommendations, min_rating):
183
+ """Generate recommendations using SVD model"""
184
+ all_movie_ids = movies['movieId'].unique()
185
+ rated_movies = ratings[ratings['userId'] == user_id]['movieId'].values
186
+ movies_to_predict = [mid for mid in all_movie_ids if mid not in rated_movies]
187
+
188
+ predictions = []
189
+ for movie_id in movies_to_predict:
190
+ try:
 
 
 
191
  pred = svd_model.predict(user_id, movie_id)
192
  if pred.est >= min_rating:
193
  predictions.append({
194
  'movieId': movie_id,
195
  'predicted_rating': pred.est
196
  })
197
+ except:
198
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
+ if not predictions:
201
+ return pd.DataFrame({"Message": ["No recommendations found with these criteria"]})
202
+
203
+ predictions_df = pd.DataFrame(predictions)
204
+ predictions_df = predictions_df.sort_values('predicted_rating', ascending=False).head(n_recommendations)
205
+ recommendations = predictions_df.merge(movies, on='movieId')
206
+ recommendations['predicted_rating'] = recommendations['predicted_rating'].round(2)
207
+
208
+ return recommendations[['title', 'genres', 'predicted_rating']]
209
 
210
+ def get_recommendations(user_id, n_recommendations, min_rating, model_type):
211
+ """Main recommendation function"""
 
 
212
  try:
213
  user_id = int(user_id)
214
 
215
+ # Check if user exists
216
  if user_id not in ratings['userId'].values:
217
+ return pd.DataFrame({"Error": [f"User ID {user_id} not found. Please enter a valid user ID (1-610)"]})
218
 
219
+ # Get recommendations based on model type
220
+ if model_type == "Hybrid (NCF + SVD)" and use_hybrid:
221
+ recommendations = hybrid_recommender.recommend_movies(
222
+ user_id,
223
+ N=n_recommendations,
224
+ min_rating=min_rating
225
+ )
226
+ elif model_type == "SVD (Matrix Factorization)":
227
+ recommendations = recommend_with_svd(user_id, n_recommendations, min_rating)
228
+ elif model_type == "Item-Based CF":
229
+ # Use item-based CF for recommendations
230
+ all_movie_ids = movies['movieId'].unique()
231
+ rated_movies = ratings[ratings['userId'] == user_id]['movieId'].values
232
+ movies_to_predict = [mid for mid in all_movie_ids if mid not in rated_movies]
233
+
234
+ predictions = []
235
+ for movie_id in movies_to_predict:
236
+ try:
237
+ pred = item_based_cf.predict(user_id, movie_id)
238
+ if pred.est >= min_rating:
239
+ predictions.append({
240
+ 'movieId': movie_id,
241
+ 'predicted_rating': pred.est
242
+ })
243
+ except:
244
+ continue
245
+
246
+ if predictions:
247
+ predictions_df = pd.DataFrame(predictions)
248
+ predictions_df = predictions_df.sort_values('predicted_rating', ascending=False).head(n_recommendations)
249
+ recommendations = predictions_df.merge(movies, on='movieId')
250
+ recommendations['predicted_rating'] = recommendations['predicted_rating'].round(2)
251
+ recommendations = recommendations[['title', 'genres', 'predicted_rating']]
252
+ else:
253
+ recommendations = pd.DataFrame({"Message": ["No recommendations found"]})
254
+ else: # User-Based CF
255
+ all_movie_ids = movies['movieId'].unique()
256
+ rated_movies = ratings[ratings['userId'] == user_id]['movieId'].values
257
+ movies_to_predict = [mid for mid in all_movie_ids if mid not in rated_movies]
258
+
259
+ predictions = []
260
+ for movie_id in movies_to_predict:
261
+ try:
262
+ pred = user_based_cf.predict(user_id, movie_id)
263
+ if pred.est >= min_rating:
264
+ predictions.append({
265
+ 'movieId': movie_id,
266
+ 'predicted_rating': pred.est
267
+ })
268
+ except:
269
+ continue
270
+
271
+ if predictions:
272
+ predictions_df = pd.DataFrame(predictions)
273
+ predictions_df = predictions_df.sort_values('predicted_rating', ascending=False).head(n_recommendations)
274
+ recommendations = predictions_df.merge(movies, on='movieId')
275
+ recommendations['predicted_rating'] = recommendations['predicted_rating'].round(2)
276
+ recommendations = recommendations[['title', 'genres', 'predicted_rating']]
277
+ else:
278
+ recommendations = pd.DataFrame({"Message": ["No recommendations found"]})
279
 
280
+ if len(recommendations) == 0:
281
+ return pd.DataFrame({"Message": ["No recommendations found with these criteria. Try lowering the minimum rating."]})
282
 
283
+ return recommendations
 
 
284
 
285
+ except ValueError:
286
+ return pd.DataFrame({"Error": ["Please enter a valid user ID (integer)"]})
287
  except Exception as e:
288
+ return pd.DataFrame({"Error": [f"An error occurred: {str(e)}"]})
289
+
290
+ def search_movies(query):
291
+ """Search for movies by title"""
292
+ if not query:
293
+ return movies[['movieId', 'title', 'genres']].head(20)
294
+
295
+ mask = movies['title'].str.contains(query, case=False, na=False)
296
+ results = movies[mask][['movieId', 'title', 'genres']].head(20)
297
+
298
+ if len(results) == 0:
299
+ return pd.DataFrame({"Message": [f"No movies found matching '{query}'"]})
300
+
301
+ return results
302
+
303
+ # ============================================================================
304
+ # GRADIO INTERFACE
305
+ # ============================================================================
306
+
307
+ # Model options
308
+ model_options = ["SVD (Matrix Factorization)", "Item-Based CF", "User-Based CF"]
309
+ if use_hybrid:
310
+ model_options.insert(0, "Hybrid (NCF + SVD)")
311
 
312
  # Create Gradio interface
313
+ with gr.Blocks(theme=gr.themes.Soft(), title="MovieLens Recommender System") as demo:
314
  gr.Markdown(
315
  """
316
+ # 🎬 MovieLens Movie Recommendation System
317
+
318
+ Get personalized movie recommendations using state-of-the-art collaborative filtering algorithms!
319
 
320
+ **Available Models:**
321
+ - πŸš€ **Hybrid (NCF + SVD)**: Combines Neural Collaborative Filtering with Matrix Factorization
322
+ - πŸ“Š **SVD**: Singular Value Decomposition (Matrix Factorization)
323
+ - 🎯 **Item-Based CF**: Recommends based on similar movies
324
+ - πŸ‘₯ **User-Based CF**: Recommends based on similar users
325
  """
326
  )
327
 
328
+ with gr.Tab("Get Recommendations"):
329
  with gr.Row():
330
+ with gr.Column(scale=1):
331
  user_id_input = gr.Number(
332
+ label="User ID",
333
+ value=1,
334
+ precision=0,
335
+ info="Enter a user ID (1-610)"
336
  )
337
+ model_selector = gr.Dropdown(
338
+ choices=model_options,
339
+ value=model_options[0],
340
+ label="Recommendation Model",
341
+ info="Choose the algorithm to generate recommendations"
342
+ )
343
+ n_recs = gr.Slider(
344
+ minimum=5,
345
+ maximum=50,
346
+ value=10,
347
  step=1,
348
+ label="Number of Recommendations",
349
+ info="How many movies to recommend"
350
  )
351
+ min_rating_slider = gr.Slider(
352
+ minimum=0.5,
353
+ maximum=5.0,
354
+ value=3.5,
355
  step=0.5,
356
+ label="Minimum Predicted Rating",
357
+ info="Only show movies with predicted rating above this threshold"
358
+ )
359
+ recommend_btn = gr.Button("🎬 Get Recommendations", variant="primary", size="lg")
360
+
361
+ with gr.Column(scale=2):
362
+ recommendations_output = gr.Dataframe(
363
+ label="Recommended Movies",
364
+ wrap=True,
365
+ height=500
366
  )
 
 
 
 
367
 
368
+ gr.Markdown("### πŸ“Š User's Rating History")
369
+ user_history_output = gr.Dataframe(
370
+ label="Top Rated Movies by This User",
371
+ wrap=True,
372
+ height=300
373
+ )
374
+
375
+ # Connect buttons
376
  recommend_btn.click(
377
+ fn=get_recommendations,
378
+ inputs=[user_id_input, n_recs, min_rating_slider, model_selector],
379
  outputs=recommendations_output
380
  )
381
+
382
+ user_id_input.change(
383
+ fn=get_user_history,
384
+ inputs=user_id_input,
385
+ outputs=user_history_output
386
+ )
387
 
388
+ with gr.Tab("Search Movies"):
389
+ gr.Markdown("### πŸ” Search for Movies in Database")
390
  with gr.Row():
391
+ search_input = gr.Textbox(
392
+ label="Search Query",
393
+ placeholder="Enter movie title...",
394
+ info="Search for movies by title"
395
+ )
396
+ search_btn = gr.Button("Search", variant="primary")
 
 
 
 
397
 
398
+ search_output = gr.Dataframe(
399
+ label="Search Results",
400
+ wrap=True,
401
+ height=500
402
+ )
403
+
404
+ search_btn.click(
405
+ fn=search_movies,
406
+ inputs=search_input,
407
+ outputs=search_output
408
+ )
409
+
410
+ search_input.submit(
411
+ fn=search_movies,
412
+ inputs=search_input,
413
+ outputs=search_output
414
  )
415
 
416
+ with gr.Tab("About"):
417
+ gr.Markdown(
418
+ """
419
+ ## πŸ“– About This System
420
+
421
+ This recommendation system was built using the MovieLens dataset and implements multiple collaborative filtering algorithms:
422
+
423
+ ### Models
424
+
425
+ 1. **Hybrid Model (NCF + SVD)** πŸš€
426
+ - Combines Neural Collaborative Filtering with SVD
427
+ - Best performance: RMSE improvement over baseline
428
+ - Uses deep learning to capture non-linear patterns
429
+
430
+ 2. **SVD (Singular Value Decomposition)** πŸ“Š
431
+ - Matrix factorization technique
432
+ - Learns latent factors for users and items
433
+ - Excellent for sparse data
434
+
435
+ 3. **Item-Based Collaborative Filtering** 🎯
436
+ - Recommends movies similar to what you've liked
437
+ - Based on item-item similarity
438
+ - Good for users with consistent preferences
439
+
440
+ 4. **User-Based Collaborative Filtering** πŸ‘₯
441
+ - Recommends based on users similar to you
442
+ - User-user similarity approach
443
+ - Effective for discovering diverse content
444
+
445
+ ### Dataset
446
+ - **MovieLens Small Dataset**: 100,000+ ratings
447
+ - **610 users** and **9,724 movies**
448
+ - Rating scale: 0.5 to 5.0 stars
449
+
450
+ ### Performance Metrics
451
+ The models were evaluated using:
452
+ - RMSE (Root Mean Square Error)
453
+ - Precision@10
454
+ - Recall@10
455
+ - NDCG@10 (Normalized Discounted Cumulative Gain)
456
+
457
+ ### How to Use
458
+ 1. Enter a User ID (1-610)
459
+ 2. Select a recommendation model
460
+ 3. Choose number of recommendations
461
+ 4. Set minimum rating threshold
462
+ 5. Click "Get Recommendations"
463
+
464
+ ---
465
+
466
+ Built with ❀️ using Gradio, PyTorch, and Surprise
467
+ """
468
+ )
469
+
470
+ print("βœ“ Gradio interface ready!")
471
 
472
+ # Launch the app
473
  if __name__ == "__main__":
474
+ demo.launch(share=True)