İçeriğe geç
academia.sh

Ders 08 / 14

Öneri Problemi

Kurgu abone-kalem etkileşim dizeyinde önerinin ölçülmesi: 360 abone ve 24 hizmet kalemi 8.640 hücrenin yalnız 577'sinde buluşur, seyreklik 0,9332'dir ve 80 abonenin hiçbir etkileşimi yoktur. Taban çizgisi eğitimde en çok seçilen kalemi herkese sunmaktır; ilk beş isabeti 0,6527, rastgele sıranınki 0,2036'dır. Aynı veri örtük okunduğunda ve açık puanla okunduğunda ilk beş kalemin yalnız biri ortaktır, ortalama puana göre öneri 0,1497 isabet verir, 24 kalemin 16'sının on taneden az puanı vardır ve hiç gösterilmemiş 5.638 hücrenin puan sayısı sıfırdır.

İçindekiler

Yedi derste ajan kendi eylemini seçti ve sonucunu ortamdan öğrendi: bir aboneye ekip gönderilince ne olduğu, gönderilmeyince ne olduğu adım adım kayda geçti. Şimdi eylem el değiştiriyor. Sistemin yapabildiği tek şey bir aboneye bir seçenek sunmak, ve sonucu ancak o abone seçerse görünür. Sunulmayan seçeneğin sonucu hiç doğmaz; sunulan ama seçilmeyen seçeneğin niçin seçilmediği kayda geçmez. Toplanan veri, sistemin kendi sunum kararlarının izidir.

Bu ders o düzenin verisini kurar ve iki sayı üretir: herkese aynı kalemi sunan tabanın ilk beş isabeti, ve aynı veriyi örtük ile açık okumanın ürettiği iki sıralamanın ne kadar ayrıldığı. Kursun kuralı burada ilk kez ödenir: gösterilmeyen kalem puanlanmaz, puanlanmayan kalem gösterilmez.

  • ON1. Küme kurgudur, tohum 20260218: 360 abone, 24 hizmet kalemi, 8.640 hücre. Kalemler bölgesel ölçüm ağının hizmet başvurularıdır (tarife değişikliği, sayaç değişimi, taksitlendirme, uzaktan okuma başvurusu gibi).
  • ON2. Üç olay ayrı ayrı üretilir: gösterim, seçim, puan. Bir kalem gösterilmeden seçilemez, seçilmeden puanlanamaz. Zincir kurgunun içine bilerek konmuştur.
  • ON3. Abonenin bir kaleme gerçek eğilimi üretecin içindedir ve yalnız ölçüm için okunur; hiçbir yöntem onu görmez.
  • ON4. Örtük geri bildirim seçimin kendisidir ve 0/1’dir. Açık geri bildirim 1 ile 5 arasında bir puandır ve yalnız seçimlerin bir kısmında bırakılır.
  • ON5. Geçmişte hangi kalemin kime gösterildiği payına göre yanlıdır: sık başvurulan kalem çok, seyrek olan az sunulmuştur. Bu yanlılık kurguya bilerek konmuştur.
  • ON6. Ayrılmış küme: en az iki etkileşimi olan her abonenin son etkileşimi. Kalanı eğitim kümesidir.
  • ON7. Ölçüt ilk k isabetidir: abonenin ayrılmış kalemi ilk k öneri içinde mi. Kesinlik ve duyarlılık tanımları M27/K03’te kuruldu ve burada tekrarlanmaz.
  • ON8. İki taban çizgisi kurulur: rastgele sıra ve eğitimde en çok seçilen kalem sırası. İkincisi asıl tabandır, çünkü hiçbir kişiselleştirme içermez ve yine de kataloğun eğrisinden beslenir.

Etkileşim Dizeyi ve Seyreklik

Dizeyin satırı abone, sütunu hizmet kalemidir. Bir hücrenin dolu olması abonenin o kalemi seçtiğini söyler; boş olması iki ayrı şeyin karışımıdır ve ayırt edilemez: kalem sunulmuş ama seçilmemiş olabilir, ya da hiç sunulmamış olabilir. Kurgu bu ikisini ayrı ayrı üretir, böylece ölçüm sırasında hangisinin ne kadar olduğu sayılabilir.

# ORTAK -- KURGUDUR. Abone x hizmet kalemi etkilesim dizeyi ders icinde kurulur.
# Gosterim, secim ve puan ayri ayri uretilir: bir kalem once GOSTERILIR, sonra
# secilebilir, ancak secilirse puanlanabilir.
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 = {  # kesimin alanlara egilimi; yalniz uretecin icinde kullanilir
    "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]   # kalemin kendi doyuruculugu

N_ABONE, N_KALEM = 360, len(KALEM)
ABONE, GOSTERIM, ETKILESIM, PUAN, EGILIM = [], {}, {}, {}, {}
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)
        EGILIM[(a, j)] = egilim
        if r() < min(0.95, pay ** 0.55 * 0.72):        # sunulmus mu
            GOSTERIM[(a, j)] = 1
            if r() < egilim:                            # secilmis mi
                ETKILESIM[(a, j)] = r()                 # deger: sira damgasi
                if r() < 0.32:                          # puan birakmis mi
                    ham = (0.16 + 0.50 * KESIM[kesim][alan] / 1.9
                           + 0.42 * KALITE[j] + 0.26 * (r() - 0.5))
                    PUAN[(a, j)] = max(1, min(5, int(round(ham * 5))))
        else:
            r(), r(), r()

HUCRE = N_ABONE * N_KALEM
sayi = {}
for (a, j) in ETKILESIM:
    sayi[a] = sayi.get(a, 0) + 1
print("dizey", N_ABONE, "abone x", N_KALEM, "kalem =", HUCRE, "hucre")
for ad, d in (("gosterim", GOSTERIM), ("etkilesim", ETKILESIM), ("puan", PUAN)):
    print(f"  {ad:10s} {len(d):5d}  doluluk {len(d)/HUCRE:.4f}  seyreklik {1-len(d)/HUCRE:.4f}")
print("hic etkilesimi olmayan abone", N_ABONE - len(sayi),
      " abone basina ortalama", round(len(ETKILESIM) / N_ABONE, 3),
      " en cok", max(sayi.values()))
dizey 360 abone x 24 kalem = 8640 hucre
  gosterim    3002  doluluk 0.3475  seyreklik 0.6525
  etkilesim    577  doluluk 0.0668  seyreklik 0.9332
  puan         218  doluluk 0.0252  seyreklik 0.9748
hic etkilesimi olmayan abone 80  abone basina ortalama 1.603  en cok 7

Üç doluluk oranı üç ayrı problemi tarif ediyor. Gösterim dizeyi 0,3475 dolu, seçim dizeyi 0,0668, puan dizeyi 0,0252. Seyreklik seçim düzeyinde 0,9332’dir: her yüz hücrenin altmış yedisi sunulmuş ama seçilmemiş, yirmi altısı hiç sunulmamıştır. Abone başına ortalama 1,603 etkileşim düşer ve 360 abonenin 80’inin hiçbir etkileşimi yoktur. Bu tabloda bir aboneye ne önerileceğine karar vermek, satırların çoğunun neredeyse tümüyle boş olduğu bir dizeyden karar çıkarmaktır.

Herkese Aynı Kalemi Sunmak

Öneri alanının tabanı kişiselleştirmesiz bir yordamdır: eğitim kümesinde en çok seçilmiş kalemleri sıraya dizip herkese aynı listeyi sunmak. Bu taban hiçbir aboneyi diğerinden ayırt etmez, yine de kataloğun eğriliğinden beslenir ve şaşırtıcı biçimde yükseğe çıkar. Yanına ikinci bir taban konur: abonenin görmediği kalemleri rastgele sıraya dizmek.

# Ayirma: en az iki etkilesimi olan her abonenin SON etkilesimi ayrilmis kumeye.
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_populerlik(a):
    return [j for j in sirali if j not in GORULEN.get(a, set())]


rg = uretec(TOHUM + 31)


def taban_rastgele(a):
    aday = [j for j in range(N_KALEM) if j not in GORULEN.get(a, set())]
    for i in range(len(aday) - 1, 0, -1):
        t = int(rg() * (i + 1))
        aday[i], aday[t] = aday[t], aday[i]
    return aday


print("egitim", len(EGIT), "etkilesim / ayrilmis", len(AYRI), "abone")
print("egitimde en cok secilen bes kalem:")
for j in sirali[:5]:
    print(f"  {KALEM[j][0]:26s} {pay[j]:3d}")
for K in (3, 5):
    p, s = isabet(taban_populerlik, K), isabet(taban_rastgele, K)
    print(f"ilk {K}: populerlik {p:.4f}  rastgele {s:.4f}  oran {p/s:.2f}")
print("populerlik tabaninin katalog kapsamasi",
      len({j for a in AYRI for j in taban_populerlik(a)[:5]}), "/", N_KALEM)
egitim 410 etkilesim / ayrilmis 167 abone
egitimde en cok secilen bes kalem:
  odeme_hatirlatma_sms        68
  e_fatura_gecisi             64
  otomatik_odeme_talimati     47
  taksitlendirme              39
  borc_yapilandirma           27
ilk 3: populerlik 0.5509  rastgele 0.1437  oran 3.83
ilk 5: populerlik 0.6527  rastgele 0.2036  oran 3.21
populerlik tabaninin katalog kapsamasi 9 / 24

Kişiselleştirmesiz taban, 167 abonenin 109’unun ayrılmış kalemini ilk beşte yakalıyor: isabet 0,6527. Rastgele sıra aynı ölçüde 0,2036’da kalıyor, aradaki oran 3,21. İlk üçe inildiğinde taban 0,5509 veriyor ve oran 3,83’e çıkıyor. Bu sayı bu kursun geri kalanı için bir eşik: bir öneri yöntemi 0,6527’yi geçmiyorsa, hiçbir abone ayrımı yapmayan bir listeden daha iyi değildir.

Tabanın ödediği bedel dördüncü satırda: 167 abonenin ilk beş listelerinde toplam 9 ayrı kalem geçiyor, katalogda 24 kalem var. On beş kalem hiçbir aboneye hiç önerilmiyor. Taban yüksek isabeti kataloğun on beş kalemini tümüyle görünmez kılarak alıyor, ve görünmeyen kalem bir sonraki dönemin verisine de girmiyor.

Örtük ile Açık Geri Bildirim

Örtük geri bildirim seçimin kendisidir: abone başvurdu ya da başvurmadı. Açık geri bildirim abonenin bıraktığı puandır. İkisi aynı kataloğu ölçüyor gibi görünür, ama farklı şeyleri sayarlar. Örtük sinyal hacmi ölçer ve gösterim sayısını içinde taşır; açık sinyal seçtikten sonraki doyumu ölçer ve yalnız seçenlerin bir kısmından gelir.

gos, sec, adet, toplam = ([0] * N_KALEM for _ in range(4))
for (a, j) in GOSTERIM:
    gos[j] += 1
for (a, j) in ETKILESIM:
    sec[j] += 1
for (a, j), p in PUAN.items():
    adet[j] += 1
    toplam[j] += p
ort = [toplam[j] / adet[j] if adet[j] else 0.0 for j in range(N_KALEM)]

s_ortuk = sorted(range(N_KALEM), key=lambda j: -sec[j])
s_acik = sorted([j for j in range(N_KALEM) if adet[j] > 0], key=lambda j: -ort[j])
print(f"{'kalem':26s} {'gosterim':>8s} {'secim':>6s} {'puan':>5s} {'ortalama':>9s}")
for j in s_ortuk[:3] + s_acik[:3]:
    print(f"{KALEM[j][0]:26s} {gos[j]:8d} {sec[j]:6d} {adet[j]:5d} {ort[j]:9.2f}")
print("ortuk ilk 5 ile acik ilk 5 kesisimi", len(set(s_ortuk[:5]) & set(s_acik[:5])))
print("hic secilmemis kalem", sum(1 for j in range(N_KALEM) if sec[j] == 0),
      " on tane puandan az kalem", sum(1 for j in range(N_KALEM) if adet[j] < 10))
print("hic gosterilmemis hucre", HUCRE - len(GOSTERIM),
      " bunlarin puan sayisi", sum(1 for k in PUAN if k not in GOSTERIM))
print("hic puan birakmamis abone", N_ABONE - len({a for (a, j) in PUAN}))
s_puan = sorted(range(N_KALEM), key=lambda j: -ort[j])
print(f"ilk 5 isabet -- ortalama puana gore {isabet(lambda a: [j for j in s_puan if j not in GORULEN.get(a, set())], 5):.4f}")
kalem                      gosterim  secim  puan  ortalama
e_fatura_gecisi                 215     93    30      3.27
odeme_hatirlatma_sms            236     89    31      3.06
otomatik_odeme_talimati         242     73    24      3.83
adres_guncelleme                158     37    14      4.79
ikinci_sayac_talebi              96      9     3      4.33
isyeri_tarifesine_gecis          91      5     4      4.25
ortuk ilk 5 ile acik ilk 5 kesisimi 1
hic secilmemis kalem 1  on tane puandan az kalem 16
hic gosterilmemis hucre 5638  bunlarin puan sayisi 0
hic puan birakmamis abone 205
ilk 5 isabet -- ortalama puana gore 0.1497

İki sıralamanın ilk beşinde ortak olan kalem sayısı 1. Örtük sıralamanın başındaki üç kalem ikişer yüzü aşan gösterim almış ve ortalama puanları 3,06 ile 3,83 arasında; açık sıralamanın başındaki kalemlerden ikisinin puan sayısı 3 ve 4. Ortalama puana göre öneri yapmak ilk beş isabetini 0,1497’ye düşürüyor, yani rastgele sıranın 0,2036’sının bile altına.

Sayının kaynağı üçüncü ve dördüncü satırda duruyor. Kataloğun 24 kaleminin 16’sının on taneden az puanı var, çünkü az gösterilen kalem az seçiliyor ve az seçilen kalem az puanlanıyor. Hiç gösterilmemiş 5.638 hücrenin puan sayısı sıfır; bu bir bulgu değil, zincirin tanımı. Abonelerin 205’i hiç puan bırakmamış. Açık geri bildirim daha zengin bir sinyal taşır, ama taşıdığı yerde taşır: sistemin daha önce gösterdiği kalemlerde. Gösterilmeyen kalem puanlanmaz, ve ortalama puanla sıralanan bir liste kataloğun görünmeyen kısmı hakkında hiçbir şey söylemez.

Toplanan veri bu dersin üçüncü sayısıdır ve iki parçadan oluşur: hiç gösterilmemiş 5.638 hücre, ve tabanın önerdiği listelerin dışında kalan 15 kalem. Bir öneri yöntemi ölçülürken bu iki sayı yazılmıyorsa, ölçünün hangi kataloğa ait olduğu bilinmez.

Özet

  • Kurgu dizey 360 abone ile 24 hizmet kalemini 8.640 hücrede buluşturur; gösterim doluluğu 0,3475, seçim 0,0668, puan 0,0252’dir. Seçim düzeyinde seyreklik 0,9332 ve 80 abonenin hiç etkileşimi yoktur.
  • Taban çizgisi eğitimde en çok seçilen kalemleri herkese aynı sırayla sunmaktır; ilk beş isabeti 0,6527, ilk üç isabeti 0,5509’dur. Rastgele sıra aynı ölçüde 0,2036 ve 0,1437 verir.
  • Tabanın bedeli kapsamadır: 167 abonenin ilk beş listelerinde yalnız 9 ayrı kalem geçer, on beş kalem hiçbir aboneye önerilmez.
  • Örtük ve açık geri bildirim aynı katalogda iki ayrı sıralama üretir; ilk beşlerinin kesişimi birdir ve ortalama puana göre öneri 0,1497 isabet verir.
  • Açık sinyal seyrektir ve gösterime bağımlıdır: 24 kalemin 16’sının on taneden az puanı vardır, 205 abone hiç puan bırakmamıştır, hiç gösterilmemiş 5.638 hücrenin puan sayısı sıfırdır.

Sonraki Adım

Taban 0,6527 ile bir eşik koydu, ama bunu kataloğun on beş kalemini hiç göstermeyerek yaptı ve hiç etkileşimi olmayan 80 aboneye söyleyecek ayrı bir sözü yok. İlk soru şu: aboneden hiç veri gelmemişken bile kalemin kendisi hakkında bilinen bir şey var mı. Katalogda her kalemin alanı, kanalı, ücretli olup olmadığı, taahhüt ve randevu gerektirip gerektirmediği yazılı. Bir sonraki ders bu özniteliklerden abone başına bir profil çıkarır, önerileri profile benzerlikle sıralar ve iki sayıyı ölçer: kaç abone için hiç öneri üretilemiyor, ve üretilen öneriler birbirine ne kadar benziyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat