DeathBlade020's picture
update app.py
1b8c42a verified
Raw
History Blame Contribute Delete
5.82 kB
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.neighbors import NearestNeighbors
from sentence_transformers import SentenceTransformer
import numpy as np
import streamlit as st
import os
from rapidfuzz import process
import re
def load_data(filepath):
"""Load and deduplicate the dataset."""
data = pd.read_csv(filepath)
data = data.drop_duplicates(subset=['book_name'], keep='first').reset_index(drop=True)
return data
def preprocess_data(data):
"""Preprocess the dataset by cleaning text fields."""
data['book_name'] = data['book_name'].fillna('').str.lower()
data['summaries'] = data['summaries'].fillna('').str.lower()
data['categories'] = data['categories'].fillna('').str.lower()
data['combined_text'] = (
data['book_name'] + ' ' +
data['summaries'] * 2 + ' ' +
data['categories']
)
return data
def feature_engineering(data):
"""Generate features using TF-IDF and SentenceTransformer embeddings."""
# TF-IDF Vectorization
vectorizer = TfidfVectorizer(stop_words='english')
tfidf_matrix = vectorizer.fit_transform(data['combined_text'])
# SentenceTransformer embeddings
model = SentenceTransformer('paraphrase-MiniLM-L3-v2')
embeddings_file = "semantic_embeddings.npy"
if not os.path.exists(embeddings_file):
data['semantic_embeddings'] = data['combined_text'].apply(lambda x: model.encode(x))
np.save(embeddings_file, np.vstack(data['semantic_embeddings']))
else:
semantic_embeddings = np.load(embeddings_file)
data['semantic_embeddings'] = [embedding for embedding in semantic_embeddings]
# Fit NearestNeighbors model
nn_model = NearestNeighbors(metric='cosine', algorithm='auto')
nn_model.fit(np.vstack(data['semantic_embeddings']))
return vectorizer, tfidf_matrix, model, nn_model
def calculate_similarity(input_title, data, vectorizer, tfidf_matrix, model, nn_model, n_recommendations=5, min_similarity=0.3):
"""Calculate similarities and generate recommendations."""
try:
# Compute TF-IDF similarity
input_vector = vectorizer.transform([input_title.lower()])
cosine_similarities = cosine_similarity(input_vector, tfidf_matrix).flatten()
min_similarity = max(0.1, cosine_similarities.mean() - cosine_similarities.std())
# Compute semantic similarity using NearestNeighbors
input_embedding = model.encode(input_title.lower())
distances, indices = nn_model.kneighbors([input_embedding], n_neighbors=len(data))
# Combine similarities
recommended_books = []
seen_titles = set()
for idx, dist in zip(indices[0], distances[0]):
book_title = data['book_name'].iloc[idx]
round_sim_value = round(1 - dist, 2) # Convert distance to similarity
if book_title not in seen_titles and round_sim_value >= min_similarity and str(book_title).lower() != str(input_title).lower():
recommended_books.append((book_title, round_sim_value))
seen_titles.add(book_title)
if len(recommended_books) == n_recommendations:
break
# If no meaningful recommendations, return fallback message
if not any(score >= min_similarity for _, score in recommended_books):
return [("No meaningful recommendations found.", 0.0)]
return recommended_books
except Exception as e:
return [(f"An error occurred: {str(e)}", 0.0)]
def validate_input(input_title, data, threshold=70):
"""Validate input title using fuzzy matching against dataset titles."""
titles = data['book_name'].tolist()
closest_match, score, _ = process.extractOne(input_title, titles)
if score >= threshold:
return closest_match
return None
def deploy_ui(data, vectorizer, tfidf_matrix, model, nn_model):
"""Streamlit-based UI for interacting with the recommendation system."""
st.title("Content-Based Book Recommender")
st.write("Enter a book title to get up to 5 similar recommendations.")
book_title = st.text_input("Enter a book title")
if st.button("Submit"):
# Input validation
if len(book_title.strip()) < 3:
st.write("Input is too short. Please enter a more meaningful book title.")
return
closest_title = validate_input(book_title, data)
if closest_title is None:
st.write("No close match found. Please try a different title.")
return
if closest_title:
st.write(f"Did you mean: {closest_title}?")
book_title = closest_title
# Generate recommendations
recommendations = calculate_similarity(book_title, data, vectorizer, tfidf_matrix, model, nn_model, min_similarity=0.3)
if "error" in recommendations[0][0]:
st.write("Error occured. Please try again later")
elif recommendations is None or len(recommendations) == 0 or recommendations[0][0] == "No meaningful recommendations found.":
st.write("No meaningful recommendations found for the input. Please try again with a different title.")
else:
st.write("Recommended Books:")
idx = 1
for book, score in recommendations:
cleaned_book = re.sub(r"[^A-Za-z0-9]+$", "", book)
st.write(f"{idx}: {cleaned_book}")
idx += 1
if __name__ == "__main__":
filepath = "books_summary.csv"
data = load_data(filepath)
data = preprocess_data(data)
vectorizer, tfidf_matrix, model, nn_model = feature_engineering(data)
deploy_ui(data, vectorizer, tfidf_matrix, model, nn_model)