---
title: 'İçerik Temelli Öneri'
source: 'https://academia.sh/tr/kurslar/pekistirmeli-ve-oneri/icerik-temelli-oneri'
course: 'Pekiştirmeli Öğrenme ve Öneri Sistemleri'
language: tr
updated: '2026-08-17T18:10:16+00:00'
license: 'CC BY-SA 4.0'
---

# İçerik Temelli Öneri

Kalemin özniteliklerinden abone profili çıkaran önerinin ölçülmesi: on bir bileşenli ikili öznitelik vektörüyle 360 abonenin 280'ine profil kurulur, 80'i için hiç öneri üretilemez. İlk beş isabet 0,4431 ile 0,6527'lik popülerlik tabanının altında kalır, ama katalog kapsaması 9 yerine 24'tür. Bedeli benzeşmedir: ilk beş listenin iç benzerliği 0,6769 ve listedeki kalemlerin 0,715'i tek bir alandan gelir. Hiç gösterilmemiş yeni bir kalem 280 abonenin tamamında sıralanır, 191'inin ilk onuna girer ve ortalama konumu 25 kalem içinde 11,55'tir; aynı kalem popülerlik sıralamasında sonuncudur.

Önceki dersin tabanı ilk beş isabetini 0,6527'ye çıkardı, ama bunu kataloğun 24 kaleminden
yalnız 9'unu göstererek yaptı ve hiç etkileşimi olmayan 80 aboneye herkese söylediğinin aynısını
söyledi. İki eksik de aynı yerden geliyor: taban yalnız **etkileşim sayılarına** bakıyor,
kalemin ne olduğuna bakmıyor.

Katalogda kalem başına yazılı bilgi var: hangi alana ait olduğu, hangi kanaldan yürüdüğü, ücretli
olup olmadığı, taahhüt ve randevu gerektirip gerektirmediği. Bu ders o sütunlardan bir benzerlik
kurar, aboneyi seçtiği kalemlerin ortalaması olarak tarif eder ve iki sayıyı ölçer: **kaç abone
için hiç öneri üretilemiyor**, ve **üretilen öneriler birbirine ne kadar benziyor**.

- **ON9.** Kurgu ve tohum önceki dersle aynıdır; dizey, ayırma ve popülerlik tabanı aynen yeniden
  kurulur ve aynı sayıları verir.
- **ON10.** Öznitelik vektörü **on bir bileşenlidir**: beş alan, üç kanal ve üç ikili bayrak.
  Bileşenler 0/1'dir ve vektör birim uzunluğa getirilir.
- **ON11.** Abone profili, eğitim kümesinde **seçtiği** kalemlerin öznitelik vektörlerinin
  toplamının birim uzunluğa getirilmiş halidir. Puanlar kullanılmaz; profil örtük sinyalden gelir.
- **ON12.** İki birim vektörün benzerliği iç çarpımlarıdır ve 0 ile 1 arasındadır. Eşitlik
  durumunda katalog sırası bozar; kayan noktalı değerler doğrudan karşılaştırılmaz, sıralama
  anahtarı olarak kullanılır.
- **ON13.** Kapsama **iki ayrı sayıdır**: kaç aboneye öneri üretilebildiği, ve kaç ayrı kalemin
  en az bir abonenin listesine girdiği.
- **ON14.** Bir listenin **iç benzerliği**, listedeki bütün kalem ikililerinin ortalama
  benzerliğidir.
- **ON15.** Sonda katalogda yeni bir kalem vardır: öznitelikleri yazılıdır, hiç gösterilmemiştir
  ve hiçbir etkileşimi yoktur.

## Dizeyi Yeniden Kurmak

Ölçüm aynı veri üstünde yapılmazsa iki dersin sayıları karşılaştırılamaz. Aşağıdaki blok kurguyu,
ayırmayı ve popülerlik tabanını aynı tohumla yeniden kurar; iki satırlık çıktısı önceki dersin
seyreklik ve isabet sayılarıyla birebir aynı olmalıdır.

```python
# ORTAK -- KURGUDUR. Onceki dersin dizeyi ayni tohumla yeniden kurulur; bu kez
# katalogun oznitelik sutunlari da okunacak.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


KALEM = [  # (ad, alan, kanal, ucretli, taahhut, randevu, taban_pay)
    ("otomatik_odeme_talimati", "odeme", "cevrimici", 0, 0, 0, 0.90),
    ("e_fatura_gecisi", "bildirim", "cevrimici", 0, 0, 0, 0.85),
    ("odeme_hatirlatma_sms", "bildirim", "cevrimici", 0, 0, 0, 0.78),
    ("taksitlendirme", "odeme", "sube", 0, 1, 0, 0.62),
    ("uzaktan_okuma_basvurusu", "olcum", "saha", 0, 1, 1, 0.55),
    ("kesinti_uyari_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.50),
    ("borc_yapilandirma", "odeme", "sube", 0, 1, 0, 0.44),
    ("adres_guncelleme", "sozlesme", "cevrimici", 0, 0, 0, 0.40),
    ("sayac_degisimi", "olcum", "saha", 1, 0, 1, 0.36),
    ("tuketim_raporu_aboneligi", "bildirim", "cevrimici", 0, 0, 0, 0.32),
    ("fatura_itiraz_kaydi", "odeme", "sube", 0, 0, 0, 0.28),
    ("abonelik_devri", "sozlesme", "sube", 0, 0, 0, 0.25),
    ("kacak_kontrol_randevusu", "olcum", "saha", 0, 0, 1, 0.22),
    ("tesisat_bakim_paketi", "tesisat", "saha", 1, 1, 1, 0.19),
    ("ikinci_sayac_talebi", "olcum", "saha", 1, 0, 1, 0.16),
    ("isyeri_tarifesine_gecis", "sozlesme", "sube", 0, 1, 0, 0.14),
    ("basinc_duzenleyici_montaji", "tesisat", "saha", 1, 0, 1, 0.12),
    ("bahce_sulama_tarifesi", "sozlesme", "sube", 0, 1, 0, 0.10),
    ("sayac_yeri_degisikligi", "olcum", "saha", 1, 0, 1, 0.09),
    ("ortak_alan_paylasim_kaydi", "sozlesme", "sube", 0, 1, 0, 0.08),
    ("tesisat_uygunluk_raporu", "tesisat", "saha", 1, 0, 1, 0.07),
    ("gecikme_faizi_muafiyeti", "odeme", "sube", 0, 0, 0, 0.06),
    ("mobil_erisim_izni", "bildirim", "cevrimici", 0, 0, 0, 0.05),
    ("ek_hat_tesisati", "tesisat", "saha", 1, 1, 1, 0.04),
]
KESIM = {
    "tasarruf": {"odeme": 0.5, "olcum": 1.7, "sozlesme": 0.6, "bildirim": 1.5, "tesisat": 0.6},
    "odeme":    {"odeme": 1.9, "olcum": 0.5, "sozlesme": 0.6, "bildirim": 1.1, "tesisat": 0.3},
    "isyeri":   {"odeme": 0.7, "olcum": 0.9, "sozlesme": 1.6, "bildirim": 0.7, "tesisat": 1.9},
    "yeni":     {"odeme": 0.8, "olcum": 0.7, "sozlesme": 1.8, "bildirim": 1.3, "tesisat": 0.7},
}
KESIM_AD = list(KESIM)
rk = uretec(TOHUM + 101)
KALITE = [0.25 + 0.70 * rk() for _ in KALEM]

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, ETKILESIM = [], {}
for i in range(N_ABONE):
    r = uretec(TOHUM + 7919 * i)
    a, kesim = 20001 + i, KESIM_AD[int(r() * 4)]
    etkinlik = 0.35 + 1.15 * r() ** 1.6
    ABONE.append(a)
    for j, (ad, alan, kanal, uc, ta, ra, pay) in enumerate(KALEM):
        egilim = min(0.95, KESIM[kesim][alan] * pay * etkinlik * 0.55)
        if r() < min(0.95, pay ** 0.55 * 0.72):     # gosterildi mi
            if r() < egilim:                         # secildi mi
                ETKILESIM[(a, j)] = r()
                if r() < 0.32:
                    r()                              # puan cekimi: burada okunmuyor
        else:
            r(), r(), r()

EGIT, AYRI, GORULEN, kayit = {}, {}, {}, {}
for (a, j), damga in ETKILESIM.items():
    kayit.setdefault(a, []).append((damga, j))
for a, lst in kayit.items():
    lst.sort()
    if len(lst) >= 2:
        AYRI[a] = lst[-1][1]
        lst = lst[:-1]
    for damga, j in lst:
        EGIT[(a, j)] = 1
        GORULEN.setdefault(a, set()).add(j)
pay = [0] * N_KALEM
for (a, j) in EGIT:
    pay[j] += 1
sirali = sorted(range(N_KALEM), key=lambda j: -pay[j])


def isabet(oner, K):
    return sum(1 for a in AYRI if AYRI[a] in oner(a)[:K]) / len(AYRI)


def taban(a):
    return [j for j in sirali if j not in GORULEN.get(a, set())]


print("etkilesim", len(ETKILESIM), " seyreklik",
      round(1 - len(ETKILESIM) / (N_ABONE * N_KALEM), 4))
print("ayrilmis abone", len(AYRI), " taban ilk 5 isabet", round(isabet(taban, 5), 4))
```

```
etkilesim 577  seyreklik 0.9332
ayrilmis abone 167  taban ilk 5 isabet 0.6527
```

İki satır önceki dersle birebir aynı: 577 etkileşim, 0,9332 seyreklik, 167 ayrılmış abone ve
0,6527 taban isabeti. Bu tekrar bir tören değil, ölçümün ön koşulu: iki yöntemin sayısı ancak
aynı ayırma, aynı ayrılmış küme ve aynı taban üstünde okunduğunda karşılaştırılabilir. Kurgu
aynı tohumdan yeniden üretildiği için aradaki her fark yöntemden gelir.

## Öznitelikten Profile

Her kalem on bir bileşenli bir vektöre dönüşür ve birim uzunluğa getirilir; böylece çok
özniteliği olan kalem yalnız bu yüzden öne çıkmaz. Ücretli, taahhütlü ve randevulu bir saha
kalemi altı bileşeninde 1 taşır, ücretsiz bir çevrimiçi kalem yalnız ikisinde; ölçekleme
olmasaydı birincisi her profile daha yakın görünürdü. Bileşenler ikili olduğu için çok sayıda
eşit benzerlik çıkar, ve eşitlik katalog sırasıyla bozulur; bu keyfi bir kuraldır ve sonucu
koşumdan koşuma sabit tutmak için yazılmıştır.

Abonenin profili, seçtiği kalemlerin vektörlerinin toplamıdır: iki saha başvurusu yapmış bir
abonenin profili saha yönünde uzar. Öneri, profile en yakın ve daha önce seçilmemiş kalemleri
sıralamaktır. Abonenin hiç seçimi yoksa toplanacak vektör yoktur ve profil kurulamaz.

```python
import math

ALAN = ["odeme", "olcum", "sozlesme", "bildirim", "tesisat"]
KANAL = ["cevrimici", "saha", "sube"]


def oznitelik(k):
    v = [1.0 if k[1] == x else 0.0 for x in ALAN]
    v += [1.0 if k[2] == x else 0.0 for x in KANAL]
    v += [float(k[3]), float(k[4]), float(k[5])]
    n = math.sqrt(sum(x * x for x in v))
    return [x / n for x in v]


def benzerlik(u, v):
    return sum(x * y for x, y in zip(u, v))


OZ = [oznitelik(k) for k in KALEM]
BOYUT = len(OZ[0])

PROFIL = {}
for a in ABONE:
    gor = GORULEN.get(a, set())
    if not gor:
        continue                                   # profil kurulamaz
    p = [sum(OZ[j][d] for j in gor) for d in range(BOYUT)]
    n = math.sqrt(sum(x * x for x in p))
    PROFIL[a] = [x / n for x in p]


def icerik(a):
    if a not in PROFIL:
        return []                                  # oneri uretilemiyor
    return sorted((j for j in range(len(OZ)) if j not in GORULEN.get(a, set())),
                  key=lambda j: (-benzerlik(PROFIL[a], OZ[j]), j))


def kapsama(oner, K=5):
    return len({j for a in AYRI for j in oner(a)[:K]})


def ic_benzerlik(oner, K=5):
    t = n = 0
    for a in AYRI:
        lst = oner(a)[:K]
        for x in range(len(lst)):
            for y in range(x + 1, len(lst)):
                t += benzerlik(OZ[lst[x]], OZ[lst[y]])
                n += 1
    return t / n


print("profil kurulan abone", len(PROFIL), "/", N_ABONE,
      " kurulamayan", N_ABONE - len(PROFIL))
for K in (3, 5):
    print(f"ilk {K}: icerik {isabet(icerik, K):.4f}  taban {isabet(taban, K):.4f}")
print("katalog kapsamasi   icerik", kapsama(icerik), " taban", kapsama(taban), "/", N_KALEM)
print(f"ilk 5 ic benzerlik  icerik {ic_benzerlik(icerik):.4f}  taban {ic_benzerlik(taban):.4f}")
t = 0
for a in AYRI:
    alanlar = [KALEM[j][1] for j in icerik(a)[:5]]
    t += max(alanlar.count(x) for x in set(alanlar))
print("ilk 5 te en cok gecen alanin ortalama payi", round(t / len(AYRI) / 5, 4))
```

```
profil kurulan abone 280 / 360  kurulamayan 80
ilk 3: icerik 0.2874  taban 0.5509
ilk 5: icerik 0.4431  taban 0.6527
katalog kapsamasi   icerik 24  taban 9 / 24
ilk 5 ic benzerlik  icerik 0.6769  taban 0.3827
ilk 5 te en cok gecen alanin ortalama payi 0.715
```

İçerik temelli öneri tabanın altında kalıyor: ilk beş isabeti 0,4431, tabanın 0,6527'sinin 0,2096
gerisinde; ilk üçte fark daha büyük, 0,2874'e karşı 0,5509. Farkın kaynağı yöntemin ne
gördüğüdür. Bir kalemin seçilme olasılığı kurguda iki çarpandan gelir: abonenin o alana eğilimi,
ve kalemin kendi yaygınlığı. Öznitelik vektörü yalnız birincisini taşır; ikincisi, yani kaç
abonenin o kalemi seçtiği, öznitelik sütunlarında hiç yazılı değil. Yöntem "bu abone neyi
seçer" sorusunu "bu abonenin seçtiklerine ne benzer" sorusuyla değiştiriyor ve yaygınlığı
düşürüyor.

Kapsama sütunu ters yöne bakıyor. Taban 167 abonenin listelerine yalnız 9 kalem sokuyordu;
içerik temelli öneri **24 kalemin tamamını** en az bir listeye sokuyor. Buna karşılık listeler
kendi içlerinde daralıyor: ilk beşin iç benzerliği tabanda 0,3827 iken içerik temelli öneride
0,6769, ve ilk beşteki kalemlerin ortalama 0,715'i tek bir alandan geliyor. Saha başvurusu yapmış
aboneye dört saha başvurusu daha öneriliyor. Katalog düzeyinde çeşitlilik artıyor, abone
düzeyinde azalıyor, ve iki sayı ayrı ayrı yazılmazsa kapsama tek başına yanıltır.

Abone kapsaması kapsamanın öbür yarısı: 360 abonenin 280'ine profil kurulabiliyor, **80'ine hiç
öneri üretilemiyor**. Bu 80 abone bir sonraki dönem de hiçbir şey görmeyecek, hiçbir şey
seçmeyecek ve satırları boş kalmaya devam edecek.

Üç sayı üç ayrı soruya yanıt veriyor ve hiçbiri diğerinin yerine geçmiyor. İsabet "ayrılmış kalem
listede mi" diye soruyor ve içerik temelli yöntem burada kaybediyor. Katalog kapsaması "kaç kalem
hiç görünmüyor" diye soruyor ve burada kazanıyor. İç benzerlik "listedeki beş kalem birbirinin
tekrarı mı" diye soruyor ve burada kaybediyor. Yalnız isabeti yazan bir rapor yöntemi gereksiz
gösterir, yalnız kapsamayı yazan bir rapor onu gereğinden iyi gösterir.

## Yeni Kalem ve Yeni Abone

Soğuk başlangıç iki yönlüdür ve içerik temelli öneri ikisine iki ayrı yanıt verir. Katalogda
hiç gösterilmemiş bir kalem düşünelim: etkileşim sayısı sıfır, ama öznitelikleri katalogla
birlikte yazılmış. Bir de hiç etkileşimi olmayan bir abone düşünelim: öznitelikleri yok, çünkü
öznitelik kalemin, abonenin değil.

```python
# Kataloga hic gosterilmemis yeni bir kalem eklenir: hicbir etkilesimi yok,
# yalniz oznitelikleri var.
YENI = ("mobil_sayac_okuma", "olcum", "cevrimici", 0, 0, 0, 0.0)
OZ.append(oznitelik(YENI))
YJ = len(OZ) - 1

ilk5 = sum(1 for a in PROFIL if YJ in icerik(a)[:5])
ilk10 = sum(1 for a in PROFIL if YJ in icerik(a)[:10])
konum = sum(icerik(a).index(YJ) + 1 for a in PROFIL) / len(PROFIL)
print(f"{YENI[0]}: icerik temelli ilk 5 -> {ilk5} abone, ilk 10 -> {ilk10} abone")
print("ortalama konum", round(konum, 2), "/", len(OZ), " taban siralamasinda konum",
      len(OZ), "(etkilesim sayisi 0)")
print("profilsiz abone", N_ABONE - len(PROFIL), " icerik temelli oneri sayisi",
      sum(len(icerik(a)) for a in ABONE if a not in PROFIL),
      " tabanin verdigi", 5 * (N_ABONE - len(PROFIL)))
```

```
mobil_sayac_okuma: icerik temelli ilk 5 -> 13 abone, ilk 10 -> 191 abone
ortalama konum 11.55 / 25  taban siralamasinda konum 25 (etkilesim sayisi 0)
profilsiz abone 80  icerik temelli oneri sayisi 0  tabanin verdigi 400
```

Yeni kalem 280 abonenin **tamamında** sıralanıyor: hiç gösterilmemiş olmasına rağmen ortalama
konumu 25 kalem içinde 11,55, ve 191 abonenin ilk onuna, 13 abonenin ilk beşine giriyor. Bir
etkileşim sayısına dayanan sıralamada aynı kalem herkes için sonuncudur ve sonuncu kaldığı
sürece gösterilmediği için etkileşim de toplayamaz. Öznitelik bu döngüyü kıran şeydir: kalem
hakkında **etkileşimden önce** bilinen bir bilgi taşır.

Aynı yardım abone tarafında yok. Profilsiz 80 abone için içerik temelli yöntem toplam **sıfır**
öneri üretiyor; kişiselleştirmesiz taban aynı abonelere 400 öneri veriyor, hepsi aynı beş kalem.
Asimetri yöntemin tanımından geliyor: katalogda kalemin öznitelikleri yazılı, abonenin
öznitelikleri değil. Abone hakkında bilinen tek şey geçmiş seçimleri, ve geçmiş seçimi olmayan
abone hakkında bilinen hiçbir şey yok.

Bu dersin toplanan veri sayısı budur: 80 abone hiçbir öneri almıyor, ve alamadıkları için bir
sonraki dönemin etkileşim dizeyine de hiçbir satır eklemiyorlar.

## Özet

- Öznitelik vektörü on bir bileşenlidir; abone profili seçtiği kalemlerin vektörlerinin birim
  uzunluğa getirilmiş toplamıdır ve 360 abonenin 280'ine kurulabilir.
- İçerik temelli önerinin ilk beş isabeti 0,4431, ilk üç isabeti 0,2874'tür; popülerlik tabanı
  aynı ölçülerde 0,6527 ve 0,5509 verir. Öznitelik sütunları kalemin yaygınlığını taşımaz.
- Katalog kapsaması 9'dan 24'e çıkar, ama listelerin iç benzerliği 0,3827'den 0,6769'a yükselir
  ve ilk beşteki kalemlerin 0,715'i tek bir alandan gelir.
- Kapsama iki sayıdır: 80 abone için hiç öneri üretilemez, ve bu abonelere hiçbir şey
  gösterilmediği için satırları boş kalmaya devam eder.
- Hiç gösterilmemiş yeni bir kalem 280 abonenin tamamında sıralanır, 191'inin ilk onuna girer ve
  ortalama konumu 11,55'tir; etkileşim sayısına dayanan sıralamada aynı kalem sonuncudur.

## Sonraki Adım

İçerik temelli öneri iki kalemi, ikisinin öznitelik sütunları benzediği için yakın sayıyor. Bu
yüzden yeni kalemi ilk günden sıralayabiliyor, ve bu yüzden bir aboneye hep aynı alandan beş
kalem öneriyor. Ölçümde hiç kullanılmayan bir şey kaldı: abonelerin **birbirine benzemesi**.
Aynı üç kalemi seçmiş iki abone, öznitelik sütunlarında hiçbir izi olmayan bir ortaklık taşır;
birinin seçip diğerinin henüz seçmediği dördüncü kalem bir öneri adayıdır. Bir sonraki ders
benzerliği kalemin özniteliklerinden değil **etkileşim dizeyinin satır ve sütunlarından** kurar,
komşu sayısının sıralamayı ne kadar oynattığını ölçer, ve seyreklik arttıkça hiç komşu
bulunamayan abone sayısını sayar.
