File size: 19,168 Bytes
e2119ad ede6adc 23dfb62 f94b6a2 23dfb62 2f1a64f 23dfb62 f94b6a2 23dfb62 2f1a64f 23dfb62 2f1a64f 77e28c5 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 23dfb62 77e28c5 23dfb62 2f1a64f 23dfb62 2f1a64f 77e28c5 2f1a64f 77e28c5 23dfb62 77e28c5 23dfb62 92b5f25 23dfb62 92b5f25 23dfb62 92b5f25 23dfb62 92b5f25 23dfb62 92b5f25 2f1a64f 77e28c5 2f1a64f 77e28c5 92b5f25 77e28c5 92b5f25 77e28c5 2f1a64f 23dfb62 2f1a64f 23dfb62 92b5f25 23dfb62 92b5f25 23dfb62 92b5f25 23dfb62 1a6868a 2f1a64f 1a6868a 23dfb62 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 1a6868a 2f1a64f 1a6868a 2f1a64f 23dfb62 2f1a64f 23dfb62 2f1a64f 1a6868a 2f1a64f 1a6868a 2f1a64f 1a6868a 2f1a64f 1a6868a 2f1a64f 1a6868a 23dfb62 2f1a64f 1a6868a 2f1a64f 1a6868a 2f1a64f 1a6868a 2f1a64f 23dfb62 77e28c5 f94b6a2 2f1a64f 23dfb62 2f1a64f 1a6868a 2f1a64f 1a6868a 23dfb62 2f1a64f 23dfb62 f94b6a2 23dfb62 2f1a64f f94b6a2 23dfb62 2f1a64f f94b6a2 23dfb62 2f1a64f f94b6a2 1a6868a 23dfb62 f94b6a2 23dfb62 2f1a64f 1a6868a 23dfb62 2f1a64f 1a6868a 23dfb62 f94b6a2 23dfb62 f94b6a2 2f1a64f 23dfb62 f94b6a2 2f1a64f 23dfb62 2f1a64f 23dfb62 f94b6a2 2f1a64f f94b6a2 23dfb62 f94b6a2 23dfb62 f94b6a2 23dfb62 2f1a64f 23dfb62 f94b6a2 23dfb62 2f1a64f 23dfb62 77e28c5 2f1a64f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | import pandas as pd
import numpy as np
import gradio as gr
import os
from sentence_transformers import SentenceTransformer, util
import torch
import re
print("--- Film Öneri Sistemi Başlatılıyor ---")
csv_file_name = "imdb-top-rated-movies-user-rated.csv"
file_path = os.path.join(".", csv_file_name)
if not os.path.exists(file_path):
print(f"HATA: '{csv_file_name}' dosyası bulunamadı.")
exit(1)
try:
df = pd.read_csv(file_path)
print(f"'{csv_file_name}' başarıyla yüklendi. Toplam {len(df)} film bulundu.")
except Exception as e:
print(f"HATA: CSV dosyası yüklenirken hata oluştu: {e}")
exit(1)
df_filtered = df[['Title', 'IMDb Rating', 'Tags', 'Director', 'Stars', 'Votes', 'Description', 'Poster URL']].copy()
df_filtered['Stars'].fillna('', inplace=True)
df_filtered['Description'].fillna('', inplace=True)
df_filtered['Poster URL'].fillna('', inplace=True)
genre_mapping = {
'action': ['action', 'action epic', 'gun fu', 'one-person army action', 'car action', 'kung fu', 'martial arts', 'martial-arts'],
'adventure': ['adventure', 'adventure epic', 'desert adventure', 'animal adventure', 'space adventure', 'swashbuckler'],
'comedy': ['comedy', 'romantic comedy', 'buddy comedy', 'sitcom', 'black comedy', 'satire', 'spoof', 'parody', 'slapstick', 'screwball comedy', 'dark comedy', 'body swap comedy'],
'drama': ['drama', 'period drama', 'cop drama', 'legal drama', 'medical drama', 'teen drama', 'psychological drama', 'melodrama', 'historical drama', 'biography', 'romantic drama', 'showbiz drama', 'tragedy'],
'thriller': ['thriller', 'crime thriller', 'spy thriller', 'psychological thriller', 'mystery thriller', 'political thriller', 'conspiracy thriller', 'erotic thriller', 'cyber thriller', 'suspense'],
'sci-fi': ['sci-fi', 'space sci-fi', 'dystopian sci-fi', 'cyberpunk', 'alien invasion', 'mutant', 'robot', 'post-apocalyptic', 'time travel'],
'fantasy': ['fantasy', 'dark fantasy', 'sword & sorcery', 'fairy tale', 'epic fantasy'],
'horror': ['horror', 'slasher', 'supernatural horror', 'body horror', 'zombie', 'monster', 'vampire', 'werewolf', 'ghost'],
'mystery': ['mystery', 'suspense mystery', 'cozy mystery', 'whodunnit', 'detective', 'police procedural'],
'crime': ['crime', 'gangster', 'heist', 'mob', 'true crime'],
'romance': ['romance', 'romantic comedy', 'romantic drama'],
'animation': ['animation', 'adult animation', 'anime', 'computer animation', 'drawn animation', 'stop-motion animation'],
'family': ['family', 'kids'],
'western': ['western', 'classic western', 'neo-western'],
'war': ['war', 'war drama'],
'history': ['history', 'historical drama', 'biography'],
'music': ['music', 'musical', 'classic musical', 'concert'],
'documentary': ['documentary', 'docudrama', 'mockumentary']
}
reverse_genre_map = {}
for main_genre, sub_genres in genre_mapping.items():
for sub_genre in sub_genres:
reverse_genre_map[sub_genre] = main_genre
def map_to_main_genres(tag_list):
main_genres = set()
for tag in tag_list:
if tag in reverse_genre_map:
main_genres.add(reverse_genre_map[tag])
return list(main_genres)
def clean_and_split(text_series):
if pd.isna(text_series):
return []
item = str(text_series)
item = item.replace('"', '').replace("'", '').strip()
item = item.replace('sci, fi', 'sci-fi')
split_items = [s.strip().lower() for s in item.split(',') if s.strip()]
return split_items
df_filtered['Tags_cleaned_raw'] = df_filtered['Tags'].apply(clean_and_split)
df_filtered['Director_cleaned'] = df_filtered['Director'].apply(clean_and_split)
df_filtered['Stars_cleaned'] = df_filtered['Stars'].apply(clean_and_split)
df_filtered['Tags_cleaned'] = df_filtered['Tags_cleaned_raw'].apply(map_to_main_genres)
def convert_votes_to_numeric(votes_str):
if isinstance(votes_str, str):
votes_str = votes_str.replace(",", "")
if 'K' in votes_str:
return float(votes_str.replace('K', '')) * 1000
elif 'M' in votes_str:
return float(votes_str.replace('M', '')) * 1_000_000
try:
return float(votes_str)
except ValueError:
return np.nan
df_filtered['Votes_numeric'] = df_filtered['Votes'].apply(convert_votes_to_numeric)
df_filtered.drop('Votes', axis=1, inplace=True)
df_filtered.dropna(subset=['Votes_numeric'], inplace=True)
df_filtered['Combined_Text'] = df_filtered['Title'] + ". " + \
df_filtered['Description'] + ". " + \
df_filtered['Tags_cleaned'].apply(lambda x: ", ".join(x)) + ". " + \
df_filtered['Director_cleaned'].apply(lambda x: ", ".join(x)) + ". " + \
df_filtered['Stars_cleaned'].apply(lambda x: ", ".join(x))
model_name = 'sentence-transformers/all-MiniLM-L6-v2'
try:
sentence_model = SentenceTransformer(model_name)
print(f"'{model_name}' modeli başarıyla yüklendi.")
except Exception as e:
print(f"HATA: Sentence Transformer modeli yüklenirken hata oluştu: {e}")
exit(1)
embeddings_file_path = os.path.join(".", "film_embeddings.npy")
if not os.path.exists(embeddings_file_path):
print(f"HATA: '{embeddings_file_path}' dosyası bulunamadı.")
exit(1)
try:
film_embeddings = torch.from_numpy(np.load(embeddings_file_path))
print("Film embedding'leri başarıyla yüklendi.")
except Exception as e:
print(f"HATA: film_embeddings.npy yüklenirken hata oluştu: {e}")
exit(1)
director_popularity = {}
for index, row in df_filtered.iterrows():
for director in row['Director_cleaned']:
director_popularity[director] = director_popularity.get(director, 0) + row['Votes_numeric']
star_popularity = {}
for index, row in df_filtered.iterrows():
for star in row['Stars_cleaned']:
star_popularity[star] = star_popularity.get(star, 0) + row['Votes_numeric']
all_tags = sorted(list(set([tag for sublist in df_filtered['Tags_cleaned'] for tag in sublist if tag in genre_mapping])))
all_directors = sorted(list(director_popularity.keys()), key=lambda d: director_popularity[d], reverse=True)
all_stars = sorted(list(star_popularity.keys()), key=lambda s: star_popularity[s], reverse=True)
def get_movie_recommendations(selected_tags, selected_directors, selected_stars, min_imdb_rating_slider, num_recommendations_slider, search_text=""):
selected_tags_list = list(selected_tags) if selected_tags else []
selected_directors_list = list(selected_directors) if selected_directors else []
selected_stars_list = list(selected_stars) if selected_stars else []
recommendations_df = df_filtered.copy()
recommendations_df = recommendations_df[recommendations_df['IMDb Rating'] >= min_imdb_rating_slider]
if selected_tags_list:
recommendations_df = recommendations_df[
recommendations_df['Tags_cleaned'].apply(lambda x: any(tag in x for tag in selected_tags_list))
]
if selected_directors_list:
recommendations_df = recommendations_df[
recommendations_df['Director_cleaned'].apply(lambda x: any(director in x for director in selected_directors_list))
]
if selected_stars_list:
recommendations_df = recommendations_df[
recommendations_df['Stars_cleaned'].apply(lambda x: any(star in x for star in selected_stars_list))
]
if search_text and len(recommendations_df) > 0:
try:
query_embedding = sentence_model.encode(search_text, convert_to_tensor=True)
filtered_indices = recommendations_df.index.tolist()
current_film_embeddings = film_embeddings[filtered_indices]
cosine_scores = util.cos_sim(query_embedding, current_film_embeddings)[0]
recommendations_df['Similarity_Score'] = cosine_scores.cpu().numpy()
recommendations_df = recommendations_df.sort_values(
by=['Similarity_Score', 'IMDb Rating', 'Votes_numeric'],
ascending=[False, False, False]
).reset_index(drop=True)
except Exception as e:
return "Benzerlik hesaplanırken bir hata oluştu."
if not search_text:
recommendations_df = recommendations_df.sort_values(
by=['IMDb Rating', 'Votes_numeric'],
ascending=[False, False]
).reset_index(drop=True)
top_recommendations = recommendations_df.head(num_recommendations_slider)
if top_recommendations.empty:
return """
<div style="text-align: center; padding: 60px 20px; background: linear-gradient(135deg, #1a1a1a 0%, #2d1810 100%); border-radius: 16px; border: 2px solid #ff6b35;">
<div style="font-size: 64px; margin-bottom: 20px;">🎬</div>
<h2 style="color: #ff6b35; margin-bottom: 10px; font-size: 28px;">Sonuç Bulunamadı</h2>
<p style="color: #d4d4d4; font-size: 16px;">Seçtiğiniz kriterlere uygun film bulunamadı. Filtreleri değiştirerek tekrar deneyin.</p>
</div>
"""
else:
html_output = ""
for idx, row in top_recommendations.iterrows():
directors_str = ", ".join([d.title() for d in row['Director_cleaned']])
stars_str = ", ".join([s.title() for s in row['Stars_cleaned']])
tags_str = ", ".join([t.title() for t in row['Tags_cleaned']])
similarity_info = ""
if 'Similarity_Score' in row and search_text:
sim_percentage = int(row['Similarity_Score'] * 100)
similarity_info = f"""
<div style="display: inline-block; margin-left: 12px; padding: 6px 12px; background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); border-radius: 8px;">
<span style="color: white; font-weight: 700; font-size: 13px;">🎯 Eşleşme: %{sim_percentage}</span>
</div>
"""
rating_color = "#4ade80" if row['IMDb Rating'] >= 8.0 else "#fbbf24" if row['IMDb Rating'] >= 7.5 else "#fb923c"
poster_html = f"""
<div style="width: 160px; height: 240px; background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); border-radius: 12px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; color: #888; font-size: 0.9em; line-height: 1.4; padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.4); border: 2px solid #3a3a3a;">
<div style="font-size: 48px; margin-bottom: 15px;">🎬</div>
<span style="font-weight: 600; color: #ddd; margin-bottom: 8px;">{row['Title'][:40]}...</span>
<span style="color: #999; font-size: 0.85em;">Poster Yok</span>
</div>
"""
html_output += f"""
<div style="display: flex; margin-bottom: 24px; border: 2px solid #3a3a3a; padding: 20px; border-radius: 16px; background: linear-gradient(135deg, #1a1a1a 0%, #252525 100%); box-shadow: 0 8px 24px rgba(0,0,0,0.3); transition: all 0.3s ease; position: relative; overflow: hidden;">
<div style="position: absolute; top: 0; left: 0; width: 6px; height: 100%; background: linear-gradient(180deg, #ff6b35 0%, #ff8c42 100%);"></div>
<div style="flex-shrink: 0; margin-right: 24px; margin-left: 6px;">
{poster_html}
</div>
<div style="flex-grow: 1;">
<div style="margin-bottom: 12px;">
<h3 style="margin: 0; color: #ff8c42; font-size: 26px; font-weight: 700; display: inline-block;">{row['Title']}</h3>
<div style="display: inline-block; margin-left: 12px; background: {rating_color}; padding: 6px 14px; border-radius: 8px;">
<span style="font-size: 16px;">⭐</span>
<span style="color: #1a1a1a; font-weight: 700; font-size: 16px;">{row['IMDb Rating']:.1f}</span>
</div>
{similarity_info}
</div>
<div style="margin-bottom: 14px;">
<div style="display: inline-block; background: rgba(255, 107, 53, 0.15); padding: 8px 14px; border-radius: 8px; border: 1px solid rgba(255, 107, 53, 0.3);">
<span style="color: #ff8c42; font-weight: 600;">🗳️ {int(row['Votes_numeric']):,} Oy</span>
</div>
</div>
<div style="margin-bottom: 12px;">
<span style="color: #ff8c42; font-weight: 600; font-size: 15px;">🎬 Yönetmen:</span>
<span style="color: #d4d4d4; font-size: 15px; margin-left: 8px;">{directors_str if directors_str else 'Bilinmiyor'}</span>
</div>
<div style="margin-bottom: 14px;">
<span style="color: #ff8c42; font-weight: 600; font-size: 15px;">⭐ Oyuncular:</span>
<span style="color: #d4d4d4; font-size: 15px; margin-left: 8px;">{stars_str if stars_str else 'Bilinmiyor'}</span>
</div>
<div style="display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px;">
{''.join([f'<span style="background: linear-gradient(135deg, #ff6b35 0%, #ff8c42 100%); color: white; padding: 6px 14px; border-radius: 20px; font-size: 13px; font-weight: 600; box-shadow: 0 2px 8px rgba(255, 107, 53, 0.3);">{tag.title()}</span>' for tag in row['Tags_cleaned']])}
</div>
</div>
</div>
"""
return html_output
with gr.Blocks(theme=gr.themes.Soft(), css="""
.gradio-container { max-width: 1200px !important; font-family: 'Segoe UI', sans-serif; }
h1 { color: #f39c12; text-align: center; }
h3 { color: #eee; }
.gr-button.gr-button-primary { background-color: #f39c12 !important; border-color: #f39c12 !important; }
.gr-button.gr-button-primary:hover { background-color: #e67e22 !important; border-color: #e67e22 !important; }
.gr-checkbox-group label { color: #ccc; }
.gr-dropdown, .gr-slider, .gr-textbox { background-color: #2c2c2c; color: #eee; border-color: #555; }
.gr-dropdown-item { color: #eee; }
.gr-checkbox-group input[type='checkbox']:checked + label {
background-color: #f39c12 !important;
border-color: #f39c12 !important;
color: #1a1a1a !important;
}
.gr-checkbox-group input[type='checkbox'] + label {
background-color: #333;
color: #eee;
border: 1px solid #555;
}
.gr-checkbox-group input[type='checkbox'] + label:hover {
background-color: #444;
}
.gr-dropdown-item.selected {
background-color: #f39c12 !important;
color: #1a1a1a !important;
}
.gr-dropdown-item:hover {
background-color: #e67e22 !important;
color: #1a1a1a !important;
}
input[type="range"]::-webkit-slider-thumb {
background-color: #f39c12 !important;
}
input[type="range"]::-moz-range-thumb {
background-color: #f39c12 !important;
}
""") as demo:
gr.Markdown(
"""
# 🎬 Film Öneri Sistemi
Favori film özelliklerinizi seçin, yüksek IMDb puanına sahip filmleri keşfedin!
İstediğiniz bir film veya konu hakkında yazın, benzerlerini de bulalım.
"""
)
with gr.Row():
with gr.Column(scale=2):
gr.Markdown("### Önerilen Filmler:")
output_html = gr.HTML(label="Önerileriniz burada listelenecektir.", value="<p style='text-align: center; color: #bbb;'>Henüz bir öneri yapılmadı. Özellikleri seçip butona tıklayın!</p>")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Film Özelliklerini Seçin:")
tags_input = gr.CheckboxGroup(
label="Film Türleri",
choices=all_tags,
value=['action', 'drama'],
interactive=True
)
directors_input = gr.Dropdown(
label="Yönetmenler",
choices=all_directors,
multiselect=True,
allow_custom_value=False,
interactive=True
)
stars_input = gr.Dropdown(
label="Oyuncular",
choices=all_stars,
multiselect=True,
allow_custom_value=False,
interactive=True
)
min_imdb_rating_slider = gr.Slider(
minimum=df_filtered['IMDb Rating'].min(),
maximum=df_filtered['IMDb Rating'].max(),
step=0.1,
value=7.6,
label="Minimum IMDb Puanı"
)
num_recommendations_slider = gr.Slider(
minimum=1,
maximum=20,
step=1,
value=10,
label="Öneri Sayısı"
)
search_text_input = gr.Textbox(
label="Film Adı veya Konu Hakkında Ara (NLP Tabanlı Benzerlik)",
placeholder="Örneğin: Batman, uzay filmi, zamanda yolculuk..."
)
recommend_btn = gr.Button("🚀 Film Önerilerini Getir", variant="primary", size="lg")
recommend_btn.click(
fn=get_movie_recommendations,
inputs=[tags_input, directors_input, stars_input, min_imdb_rating_slider, num_recommendations_slider, search_text_input],
outputs=output_html
)
gr.Examples(
examples=[
[['action'], [], [], 7.6, 5, ""],
[['comedy', 'drama'], [], [], 7.8, 3, ""],
[[], ['christopher nolan'], [], 8.0, 5, ""],
[[], [], ['leonardo dicaprio'], 7.8, 3, ""],
[[], [], [], 8.0, 5, "kahramanlık ve bilim kurgu"],
[['action', 'sci-fi'], [], [], 7.8, 5, "uzaylı istilası ve kaçış"],
],
inputs=[tags_input, directors_input, stars_input, min_imdb_rating_slider, num_recommendations_slider, search_text_input],
outputs=output_html,
fn=get_movie_recommendations,
label="Örnek Önerileri Deneyin"
)
gr.Markdown(
"""
---
### ℹ️ Nasıl Kullanılır?
1. **Film Türleri, Yönetmenler ve Oyuncular** bölümlerinden istediğiniz filtreleri seçin (birden fazla seçim yapabilirsiniz).
2. **Minimum IMDb Puanı** ve **Öneri Sayısı** çubuklarını ayarlayın.
3. İsterseniz **"Film Adı veya Konu Hakkında Ara"** kutucuğuna bir film adı, konu veya anahtar kelime yazın.
4. **"🚀 Film Önerilerini Getir"** butonuna tıklayın.
5. Öneriler üst panelde görünecektir!
"""
)
demo.launch(share=True)
print("Gradio Web Arayüzü Başlatıldı.") |