Kashaf23's picture
Update app.py
41adbac verified
Raw
History Blame Contribute Delete
4.92 kB
# app.py
import gradio as gr
from datasets import load_dataset
import random
import pandas as pd
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer, InputExample, losses
from torch.utils.data import DataLoader
# ---------------------------
# 1️⃣ Load datasets
# ---------------------------
movies = load_dataset("AiresPucrs/movielens-movies", split="train")
ratings = load_dataset("AiresPucrs/movielens-user-ratings", split="train")
# Use full dataset
movies_list = [dict(m) for m in movies]
movie_titles = [m['title'] for m in movies_list]
# Combine title + genre for embeddings
movie_texts = [f"{m['title']}{m['genres']}" for m in movies_list]
# Ratings DataFrame
df_ratings = pd.DataFrame(ratings)
# Safe movieId -> title mapping
movieId_to_title = {m['movieId']: m['title'] for m in movies_list}
# ---------------------------
# 2️⃣ Generate triplets for fine-tuning
# ---------------------------
triplets = []
for m in movies_list:
genres = set(m['genres'].split("|"))
# Positive: movies sharing at least 1 genre
same_genre = [x for x in movies_list if x != m and len(set(x['genres'].split("|")).intersection(genres)) > 0]
if not same_genre:
continue
pos = random.choice(same_genre)
# Negative: movies with no genre overlap
diff_genre = [x for x in movies_list if len(set(x['genres'].split("|")).intersection(genres)) == 0]
if not diff_genre:
continue
neg = random.choice(diff_genre)
triplets.append((
f"{m['title']}{m['genres']}",
f"{pos['title']}{pos['genres']}",
f"{neg['title']}{neg['genres']}"
))
# ---------------------------
# 3️⃣ Fine-tune embeddings (fast: 1 epoch)
# ---------------------------
model = SentenceTransformer('all-MiniLM-L6-v2')
train_examples = [InputExample(texts=[a,p,n]) for a,p,n in triplets]
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)
train_loss = losses.TripletLoss(model=model)
# Fine-tune (1 epoch for testing)
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=1, warmup_steps=50)
# ---------------------------
# 4️⃣ Build FAISS index
# ---------------------------
movie_embeddings = model.encode(movie_texts, show_progress_bar=True)
movie_embeddings = np.array(movie_embeddings).astype("float32")
dim = movie_embeddings.shape[1]
index = faiss.IndexFlatL2(dim)
index.add(movie_embeddings)
# ---------------------------
# 5️⃣ Recommendation functions
# ---------------------------
def recommend_similar_movies(query_title, top_k=5):
movie_row = [m for m in movies_list if m['title'] == query_title]
if len(movie_row) == 0:
return ["Movie not found"]
query_text = f"{movie_row[0]['title']}{movie_row[0]['genres']}"
q_emb = model.encode([query_text]).astype("float32")
# Search more neighbors to ensure enough recommendations
D, I = index.search(q_emb, top_k * 2)
recommendations = []
for idx in I[0]:
title = movie_titles[int(idx)]
if title != query_title and title not in recommendations:
recommendations.append(title)
if len(recommendations) >= top_k:
break
return recommendations
def recommend_for_user(user_id, top_k=5):
user_rated = df_ratings[df_ratings["userId"] == user_id]
liked = user_rated[user_rated["rating"] >= 4]["movieId"].tolist()
similar_users = df_ratings[df_ratings["movieId"].isin(liked)]["userId"].unique()
recs = df_ratings[
(df_ratings["userId"].isin(similar_users)) &
(~df_ratings["movieId"].isin(liked)) &
(df_ratings["rating"] >= 4)
]["movieId"]
top_recs = recs.value_counts().head(top_k).index.tolist()
recommended_titles = [movieId_to_title[m] for m in top_recs if m in movieId_to_title]
return recommended_titles if recommended_titles else ["No recommendations available"]
# ---------------------------
# 6️⃣ Gradio interface
# ---------------------------
def gradio_recommend(input_type, value, top_k=5):
if input_type == "Movie Title":
return recommend_similar_movies(value, top_k)
elif input_type == "User ID":
try:
user_id = int(value)
return recommend_for_user(user_id, top_k)
except:
return ["Invalid User ID"]
else:
return ["Invalid input type"]
iface = gr.Interface(
fn=gradio_recommend,
inputs=[
gr.Dropdown(["Movie Title", "User ID"], label="Input Type"),
gr.Textbox(label="Enter movie title or user ID"),
gr.Slider(1, 10, value=5, step=1, label="Number of recommendations")
],
outputs=gr.JSON(label="Recommendations"), # ✅ JSON ensures correct counts
title="Movie Recommendation System",
description="Content-based and user-personalized movie recommendations"
)
iface.launch()