wiki:Advanced

Version 2 (modified by 231166, 45 hours ago) ( diff )

--

Напредна тема: Систем за семантички препораки на содржини базиран на NLP Embeddings

Оваа страница го опишува развојот и имплементацијата на напредниот хибриден подсистем за препораки во проектот VidiDB. Наместо да се потпреме исклучиво на класични оцена-базирани алгоритми, во овој модул имплементиравме обработка на природен јазик (NLP) за анализа на контекстот и емоциите скриени зад текстуалните коментари на корисниците.

---

1. Вовед и Мотивација

Во современите стриминг платформи, квалитетот на препораките директно влијае врз задржувањето на корисниците на платформата. Класичните пристапи често паѓаат во замка кога две содржини со ист рејтинг (на пр. 8.5) имаат тотално различна емотивна конотација.

Преку генерирање на векторски вгнездувања (Embeddings) базирани на корисничките коментари (Review), нашиот подсистем овозможува „читање помеѓу редови“. Целта е да се анализира емоцијата и контекстот што корисниците ги оставаат во текстуална форма, со што базата може да препознае скриени семантички врски меѓу филмовите и сериите кои не се видливи само преку чистите бројки.

---

2. Избор на алгоритам и јазичен модел

За генерирање на густите семантички вектори е избран моделот all-MiniLM-L6-v2 преку библиотеката SentenceTransformers. Ова решение беше преферирано поради следниве клучни предности:

  1. Длабоко семантичко разбирање (NLP): Моделот не бара само совпаѓање на клучни зборови (keyword matching), туку го разбира значењето на речениците. На пример, фразите "Masterpiece of cinema" и "Absolutely amazing" математички ќе ги мапира блиску една до друга во повеќедимензионалниот простор бидејќи рефлектираат ист контекст.
  2. Ефикасност на локална архитектура (Lightweight): Моделот е оптимизиран за брза егзекуција на процесорско ниво (CPU Execution), што овозможува брзо локално тренирање и мапирање без потреба од скапа GPU инфраструктура.
  3. Димензионална адаптација: Иако моделот иницијално генерира вектори со 384 димензии, со цел да се оптимизира просторот во базата и да се задоволат реалните капацитети, извршивме димензионално кратење на првите 4 клучни димензии (vector(4)), кои ја задржуваат најгустата семантичка информација.

---

3. Процесирање на податоците (Data Pipeline)

Архитектурата на обработка и трансформација на податоците се одвиваше локално низ три клучни фази:

Фаза 1: Паметна SQL Агрегација на податоци

Бидејќи базата содржи огромна табела со коментари (Review со околу 10 милиони записи реално распределени низ содржините), извршувањето на индивидуален UPDATE за секој коментар поединечно би предизвикало огромен влезно-излезен застој (I/O Bottleneck). Затоа, со помош на упитот:

SELECT r.ContentContentID, m.title, STRING_AGG(r.Comment, ' ') as all_reviews ...

направивме тешка текстуална агрегација на ниво на база. За секој филм/серија, сите кориснички коментари беа споени во еден заеднички мега-текст кој го претставува колективниот впечаток на публиката.

Фаза 2: Генерирање на вектори (Python & PyCharm)

Податоците во пакет (Batch) беа повлечени во Python скриптата. За секој филм, се конструираше нов контекст од насловот и агрегираниот текст (combined_context = f"{title}. Reviews: {all_reviews}"). Овој текст се проследува до model.encode(), кој генерира низа од броеви (вектор) што претставува семантички отпечаток на тој филм.

Фаза 3: Перзистенција и ажурирање на базата

Преку psycopg2 драјверот, скриптата секој генериран вектор автоматски го запишуваше во колоната embedding во табелата Media за соодветното ContentID. Промените се потврдуваат трансакциски со conn.commit().

---

4. Имплементација на ниво на База на Податоци (SQL)

Подолу е прикажан SQL кодот со кој се активира векторската поддршка, се прави корекција/структурирање на табелата Media со додавање на колоната embedding vector(4), и се извршува масовна популација (10 милиони записи) со случајни реални коментари за симулација на реално оптоварување.

-- 1. Ја активираме векторската екстензија во базата
CREATE EXTENSION IF NOT EXISTS vector;

-- 2. Ја додаваме вистинската векторска колона во табелата media по направената структурна измена
ALTER TABLE media
ADD COLUMN embedding vector(4);

-- тука требаше да направиме мала промена во табелата Review

-- Чистење на претходни тест податоци
TRUNCATE TABLE "review" RESTART IDENTITY;

-- 3. Анонимен PL/pgSQL блок за масовно полнење на 10 милиони коментари во табелата Review
DO $$
DECLARE
    user_ids int[] := ARRAY(SELECT UserID FROM "User");
    media_ids int[] := ARRAY(SELECT ContentID FROM "media");
    u_count int := cardinality(user_ids);
    m_count int := cardinality(media_ids);
BEGIN
    INSERT INTO "review" (Comment, ReviewDate, UserUserID, ContentContentID)
    SELECT
      (ARRAY[
        'Absolutely amazing, one of the best!',
        'Great story, highly recommend.',
        'A bit slow at the start but gets better.',
        'Masterpiece of cinema.',
        'Not what I expected but still enjoyable.',
        'Incredible performances all around.',
        'The ending blew my mind.',
        'Would watch again without hesitation.',
        'Overrated in my opinion.',
        'A classic that stands the test of time.',
        'Great cinematography and direction.',
        'The plot had some holes but overall good.',
        'One of the best series ever made.',
        'Keeps you on the edge of your seat.',
        'The writing is absolutely top-notch.',
        'Brilliant acting from start to finish.',
        'A must watch for everyone.',
        'Did not live up to the hype.',
        'Absolutely loved every minute of it.',
        'A cinematic masterpiece.',
        'This man is my worst nightmare. 10/10',
        'No.',
        'The portrayal of toxic relationships is very good.'
      ])[ceil(random() * 23)::int],
      CURRENT_DATE - (random() * 730)::int,
      -- Директно влечење од низата со нов случаен индекс за секој ред
      user_ids[ceil(random() * u_count)::int],
      media_ids[ceil(random() * m_count)::int]
    FROM
      generate_series(1, 10000000) AS gs;
END $$;

---

5. Имплементација на ETL Векторскиот Процес (Python)

Оваа скрипта претставува мостот помеѓу базата и NLP моделот. Ги презема суровите коментари, ги трансформира во семантички вектори преку HuggingFace моделот и ги зачувува назад во базата.

import psycopg2
from sentence_transformers import SentenceTransformer

# 1. Поврзување со локалната база vidi_db
try:
    conn = psycopg2.connect(
        dbname="vidi_db",
        user="postgres",
        password="...",  # Поставете ја вашата системска лозинка
        host="localhost",
        port="5432"
    )
    cursor = conn.cursor()
    print("Успешно поврзување со локалната vidi_db!")
except Exception as e:
    print(f"Грешка при поврзување со базата: {e}")
    exit()

# 2. Вчитување на NLP моделот за генерација на вектори
print("Се вчитува моделот за embeddings...")
model = SentenceTransformer('all-MiniLM-L6-v2')

try:
    print("Ги влечам и ги групирам коментарите по содржина од локалната база...")
    # SQL упит за паметна агрегација со STRING_AGG
    query = """
        SELECT r.ContentContentID, m.title, STRING_AGG(r.Comment, ' ') as all_reviews
        FROM "review" r
        JOIN "media" m ON m.contentid = r.ContentContentID
        GROUP BY r.ContentContentID, m.title;
    """
    cursor.execute(query)
    rows = cursor.fetchall()

    if not rows:
        print("Нема нови филмови со коментари кои чекаат за вектор.")
    else:
        print(f"Пронајдени се {len(rows)} филмови за обработка на коментари...")

        updated_count = 0
        for row in rows:
            content_id = row[0]
            title = row[1]
            all_reviews = row[2] if row[2] else ""

            # Комбинирање на контекстот: наслов + сите текстуални впечатоци
            combined_context = f"{title}. Reviews: {all_reviews}"

            # Енкодирање во вектор и димензионално кратење до 4 димензии за vector(4)
            embedding_vector = model.encode(combined_context).tolist()
            truncated_vector = embedding_vector[:4] 

            # Ажурирање на соодветниот медиум
            cursor.execute(
                'UPDATE "media" SET embedding = %s WHERE contentid = %s;',
                (truncated_vector, content_id)
            )
            updated_count += 1

            if updated_count % 100 == 0:
                print(f"  Процесирани {updated_count} филмови локално...")

        # Зачувување на промените во базата
        conn.commit()
        print(f"\nУспешно ажурирани {updated_count} записи во локалната табела Media!")

except Exception as e:
    print(f"Настана грешка при работа со локалната база: {e}")
    conn.rollback()
finally:
    cursor.close()
    conn.close()
    print("Врската со локалниот Postgres е затворена.")

---

6. Користење на податоците во продукција (Косинусно растојание)

Откако табелата Media е целосно пополнета со вектори, пребарувањето на слични содржини е екстремно брзо и се извршува со чист SQL преку векторскиот оператор за косинусно растојание (<=>), кој е дел од pgvector екстензијата.

Кога корисникот прегледува одредена содржина, системот во реално време препорачува топ 5 најсемантички слични содржини со следниов оптимизиран упит:

SELECT contentid, title, 
       embedding <=> (SELECT embedding FROM "Media" WHERE contentid = :momentalen_film) AS distance
FROM "Media"
WHERE contentid != :trenuten_film AND embedding IS NOT NULL
ORDER BY distance ASC
LIMIT 5;

Заклучок

Со ова напредно решение, платформата Vidi разви интелигентен механизам за семантички препораки. Доколку корисниците во коментарите за два различни филма независно пишуваат дека содржат *„неочекуван пресврт на крајот што го пореметува умот“* или *„одличен приказ на токсични врски“*, јазичниот модел математички ќе ги препознае овие фрази како блиски и ќе ги препорача тие содржини заедно, правејќи го корисничкото искуство супериорно.

Тука може да ги погледнете SQL кодот и Python кодот: generate_embeddings.py console_14.sql

Attachments (2)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.