بناء نظام توصية أفلام بسيط يعتمد على تشابه المحتوى (Content-Based Filtering)

دقائق القراءة: 6

مقدمة

يُعد بناء نظام توصية أفلام قائم على تشابه المحتوى من أكثر التطبيقات التعليمية فائدة لفهم كيف تتحول البيانات الوصفية إلى قرارات ذكية قابلة للتنفيذ. هذا النوع من الأنظمة لا يعتمد على سلوك المستخدمين الآخرين، بل يركّز على خصائص العنصر نفسه مثل النوع، الكلمات المفتاحية، الملخص، المخرج، أو طاقم التمثيل، ثم يقيس مدى قرب فيلم من آخر داخل فضاء عددي قابل للمقارنة.

إذا كنت قد قرأت مقال أنظمة التوصية (Recommendation Systems): كيف يقترح نتفليكس أو أمازون المنتجات؟ فستعرف أن Content-Based Filtering مناسب عندما تكون لدينا بيانات وصفية غنية عن الأفلام حتى لو كانت تفاعلات المستخدمين قليلة. كما أن نجاحه يعتمد بقوة على جودة تجهيز البيانات، وهي فكرة ترتبط مباشرة بمقال هندسة الميزات (Feature Engineering): كيف تستخرج بيانات جديدة من البيانات الحالية؟.

في هذا المقال سنبني نموذجاً مبسطاً واحترافياً باستخدام Python وPandas وscikit-learn، ثم نوضح كيف يمكن توسيعه لاحقاً ضمن بيئات البيانات الضخمة باستخدام PySpark وخطوط ETL.

ما هو مبدأ التوصية المعتمدة على المحتوى؟

الفكرة الجوهرية بسيطة: إذا كان فيلم ما يحتوي على خصائص مشابهة لفيلم أعجب المستخدم سابقاً، فهناك احتمال مرتفع أن ينال إعجابه أيضاً. نحن لا نسأل: “من شاهد هذا أيضاً؟”، بل نسأل: “ممَّ يتكوّن هذا الفيلم؟”.

عملياً، نحوّل الحقول النصية والوصفية إلى تمثيل رقمي. بعد ذلك نحسب مسافة أو تشابهاً بين المتجهات الناتجة. من أشهر المقاييس المستخدمة هنا Cosine Similarity لأنه يقيس زاوية التشابه بين متجهين بدلاً من الاعتماد على الطول المطلق للنص.

في بيئات الإنتاج، تكون قوة النظام أقل ارتباطاً بالخوارزمية نفسها وأكثر ارتباطاً بجودة تمثيل المحتوى. فيلم ببيانات ناقصة أو كلمات مفتاحية ضعيفة سيؤدي إلى توصيات متدنية حتى لو استخدمت أفضل مكتبات Machine Learning.

البيانات المطلوبة لبناء النموذج

يمكنك البدء بجدول أفلام بسيط يحتوي على الأعمدة التالية:

  • معرف الفيلم movie_id
  • العنوان title
  • الأنواع genres
  • الملخص النصي overview
  • الكلمات المفتاحية keywords
  • المخرج أو الممثلون إن توفرت

قبل بناء النموذج، يجب معالجة القيم المفقودة وتوحيد الصياغة النصية. وهنا ستستفيد عملياً من مفاهيم تنظيف البيانات (Data Cleaning): اكتشاف ومعالجة القيم المفقودة (Missing Values)، وكذلك من التعامل مع البيانات النصية (Text Data): استخراج الكلمات المفتاحية وتحويل النصوص لتصنيفات.

تجهيز البيانات النصية وصناعة الحقول المركبة

أفضل ممارسة في النماذج البسيطة هي دمج الحقول النصية المهمة داخل عمود واحد يمثل “بصمة الفيلم”. هذا الحقل قد يجمع الأنواع والملخص والكلمات المفتاحية في سلسلة نصية واحدة. بعدها نطبّق تقنيات تمثيل نصي مثل TF-IDF.

ميزة TF-IDF أنها لا تكتفي بعدّ الكلمات، بل تخفّض وزن الكلمات الشائعة وترفع وزن الكلمات الأكثر تمييزاً. لذلك تصبح كلمات مثل “space”, “war”, “alien”, “romance” أكثر دلالة من الكلمات العامة المتكررة.

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

# بيانات تجريبية مبسطة
movies = pd.DataFrame({
    "movie_id": [1, 2, 3, 4, 5],
    "title": ["Interstellar", "The Martian", "Inception", "Gravity", "The Notebook"],
    "genres": [
        "Sci-Fi Adventure Drama",
        "Sci-Fi Adventure Survival",
        "Sci-Fi Thriller Mind-Bending",
        "Sci-Fi Space Survival",
        "Romance Drama"
    ],
    "overview": [
        "A team travels through space to save humanity.",
        "An astronaut is stranded on Mars and must survive.",
        "A thief enters dreams to plant an idea.",
        "Astronauts struggle to survive after a space disaster.",
        "A romantic story about love and memory."
    ],
    "keywords": [
        "space future blackhole nasa",
        "mars astronaut survival science",
        "dream subconscious thriller heist",
        "space astronaut orbit survival",
        "love romance relationship memory"
    ]
})

# معالجة القيم المفقودة
for col in ["genres", "overview", "keywords"]:
    movies[col] = movies[col].fillna("")

# إنشاء حقل المحتوى المدمج
movies["content"] = movies["genres"] + " " + movies["overview"] + " " + movies["keywords"]

# تحويل النص إلى تمثيل رقمي
vectorizer = TfidfVectorizer(stop_words="english")
tfidf_matrix = vectorizer.fit_transform(movies["content"])

# حساب مصفوفة التشابه
similarity_matrix = cosine_similarity(tfidf_matrix, tfidf_matrix)

def recommend_movies(movie_title, top_n=3):
    idx_list = movies.index[movies["title"].str.lower() == movie_title.lower()].tolist()
    if not idx_list:
        return f"Movie '{movie_title}' not found."

    idx = idx_list[0]
    similarity_scores = list(enumerate(similarity_matrix[idx]))
    similarity_scores = sorted(similarity_scores, key=lambda x: x[1], reverse=True)

    recommendations = []
    for i, score in similarity_scores[1:top_n+1]:
        recommendations.append({
            "title": movies.iloc[i]["title"],
            "score": round(float(score), 4)
        })

    return pd.DataFrame(recommendations)

print(recommend_movies("Interstellar"))

كيف يعمل الكود خطوة بخطوة؟

1) تحميل البيانات

استخدمنا DataFrame من Pandas لأنه مناسب للنماذج الأولية السريعة. وإذا أردت مراجعة هيكلية الجداول أكثر، فمقال مكتبة Pandas (2): استكشاف هيكل البيانات وفهم DataFrame و Series يشرح هذه النقطة بعمق.

2) تنظيف الحقول النصية

أي قيمة Null قد تكسر عملية الدمج أو تقلل جودة التمثيل النصي. لذلك قمنا بملء القيم الفارغة بسلاسل فارغة، وهي خطوة أساسية ضمن تجهيز البيانات.

3) بناء عمود موحد للمحتوى

أنشأنا العمود content ليجمع كل الإشارات الدلالية المهمة. هذه المرحلة تمثل شكلاً عملياً من Feature Engineering لأننا لا نستخدم الأعمدة الخام كما هي، بل نعيد تشكيلها لتصبح أكثر فائدة للنموذج.

4) التحويل إلى متجهات رقمية

المكتبة TfidfVectorizer تبني قاموساً من الكلمات، ثم تنتج مصفوفة رقمية تمثل أهمية كل كلمة داخل كل فيلم. النتيجة تكون عادة مصفوفة sparse عالية الكفاءة من حيث الذاكرة.

5) حساب التشابه

استخدمنا cosine_similarity لإنتاج مصفوفة تشابه بين جميع الأفلام. كل صف فيها يخبرنا بدرجة قرب فيلم معين من بقية الأفلام.

كيف نطوّر النموذج ليصبح أقوى؟

النموذج السابق ممتاز كبداية، لكنه يصبح أكثر واقعية عند إضافة عناصر أخرى مثل:

  1. إعطاء وزن أعلى للأنواع مقارنة بالملخص.
  2. تكرار الكلمات المفتاحية المهمة لزيادة أثرها في vectorization.
  3. تنظيف الأسماء المركبة مثل science fiction لتوحيدها.
  4. استخدام n-grams لالتقاط العبارات بدل الكلمات المفردة.
  5. إدخال تقييمات المستخدم الشخصية لاحقاً لبناء نظام هجين Hybrid Recommender.

عندما يتجاوز عدد الأفلام مئات الآلاف، فإن حساب مصفوفة تشابه كاملة بين جميع العناصر يصبح مكلفاً جداً. هنا تظهر أهمية تصميم Data Architecture مناسبة، مثل التخزين المرحلي للمتجهات، الفهرسة التقريبية، أو المعالجة الموزعة عبر Spark.

من النموذج المحلي إلى بيئة البيانات الضخمة

في المشاريع الحقيقية، لا تأتي البيانات من ملف واحد صغير، بل من مصادر متعددة: قاعدة أفلام، سجلات مشاهدة، واجهات API، وربما مخزن سحابي. لذلك غالباً نحتاج إلى خط ETL Pipeline منظم، ويمكنك الرجوع إلى بناء خطوط أنابيب البيانات (ETL – Extract, Transform, Load) باستخدام بايثون لفهم هذه البنية.

إذا كانت البيانات ضخمة جداً، فسيصبح من الأنسب نقل مراحل التنظيف والدمج إلى PySpark DataFrames بدلاً من Pandas. ويمكنك التوسع أكثر عبر ما هو Apache Spark؟ ولماذا تتوقف مكتبة Pandas عن العمل مع البيانات الضخمة (Big Data)؟ وقراءة وتحليل ملفات ضخمة (بحجم جيجابايت) في ثوانٍ باستخدام PySpark DataFrames.

from pyspark.sql import SparkSession
from pyspark.sql.functions import concat_ws, col, coalesce, lit

spark = SparkSession.builder.appName("movie-content-recommender").getOrCreate()

df = spark.read.csv("movies_metadata.csv", header=True, inferSchema=True)

df = df.fillna({
    "genres": "",
    "overview": "",
    "keywords": ""
})

df = df.withColumn(
    "content",
    concat_ws(" ", col("genres"), col("overview"), col("keywords"))
)

df.select("title", "content").show(5, truncate=False)

هذا المثال لا يحسب التشابه كاملاً داخل Spark، لكنه يوضح كيف نهيئ البيانات في بيئة موزعة قبل تمريرها إلى مرحلة النمذجة أو التخزين.

قيود هذا النوع من الأنظمة

رغم بساطته وفعاليته، فإن Content-Based Filtering يواجه عدة تحديات:

  • قد يكرر نفس النمط ويحد من التنوع.
  • يعتمد بشدة على جودة البيانات الوصفية.
  • لا يكتشف بسهولة “المفاجآت” التي لا تشبه تفضيلات المستخدم الحالية شكلياً.
  • يتطلب صيانة مستمرة للقواميس النصية والكلمات المفتاحية.

خاتمة

بناء نظام توصية أفلام بسيط يعتمد على تشابه المحتوى هو مدخل ممتاز لفهم التقاء علم البيانات بهندسة البيانات. أنت تبدأ من وصف نصي غير منظم، ثم تمر بمرحلة تنظيف ودمج وتمثيل عددي، وبعدها تستخرج توصيات قابلة للاستخدام الفعلي. هذا المسار يلخص جوهر العمل في المشاريع الذكية: تحويل البيانات الخام إلى قرار.

ومع التوسع، يمكن نقل نفس الفكرة إلى بنى أكثر نضجاً عبر Airflow، وSpark، ومستودعات التحليل الحديثة، مع الحفاظ على المبدأ نفسه: كلما كان تمثيل المحتوى أدق، كانت التوصية أذكى وأكثر إقناعاً.

2 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *