İçeriğe geç

Kitaplar / Yöntem ile Makine Öğrenmesi / Bölüm 2 — Topluluk yöntemleri ve destek vektör makinesi

Bölüm 2 — Topluluk yöntemleri ve destek vektör makinesi

Tek bir ağaç ya ezberler ya da çok basit kalır. Topluluk yöntemleri birçok küçük modeli birleştirir: torbalama (rastgele orman) birbirinden bağımsız ağaçların oyunu alarak dalgalanmayı azaltır; artırma (gradyan artırma, AdaBoost, XGBoost) modelleri sırayla ekler, her biri öncekilerin hatasını düzeltir. Destek vektör makinesi ise başka bir yoldan gider: sınıflar arasına olabildiğince geniş bir boşluk bırakan sınırı arar.

6. Rastgele orman

Fikir. Aynı veriden birçok farklı ağaç kurarız. Farklılığı iki rastgelelik sağlar: her ağaç verinin geri koyarak çekilmiş bir kopyasını (bootstrap) görür ve her düğümde özniteliklerin yalnızca bir kısmı denenir. Tek tek ağaçlar zayıftır ama hataları farklı yerlerdedir; oy çokluğu hataları birbirine söndürür.

ŷ(x)=çoğunluk{h1(x),h2(x),…,hT(x)}\hat{y}(x) = \operatorname{çoğunluk}\{\, h_1(x), h_2(x), \ldots, h_T(x) \,\}

Yöntem
program RastgeleOrman
{ Tek bir derin karar ağacı ile 25 ağaçlık rastgele ormanı karşılaştırır.
  Her ağaç, verinin geri koyarak çekilmiş bir örneğiyle (bootstrap) ve her düğümde
  rastgele seçilen özniteliklerle kurulur; tahmin, ağaçların oy çokluğudur. }
kullan Matematik

tip
  Düğüm = sınıf
    yaprak: mantıksal
    sınıfı, öznitelik: tamsayı
    eşik: ondalık
    sol, sağ: Düğüm
  bitir

değişken
  X, Xt: ondalık dizisi dizisi
  Y, Yt: tamsayı dizisi
  orman: Düğüm dizisi
  tek: Düğüm
  i, t: tamsayı

{ Veri: bir çemberin içi (1) ve dışı (0); etiketlerin %10'u bilerek yanlış (gürültü) }
prosedür Üret(n: tamsayı; X: ondalık dizisi dizisi; Y: tamsayı dizisi)
değişken a, b: ondalık; k, j: tamsayı
başla
  1'den n'e kadar her j için
  başla
    a := Rastgele * 2 - 1; b := Rastgele * 2 - 1
    k := eğer a * a + b * b < 0.5 ise 1 değilse 0
    eğer Rastgele < 0.1 ise k := 1 - k
    X.Ekle([a, b]); Y.Ekle(k)
  bitir
bitir

fonksiyon Gini(sıra: tamsayı dizisi): ondalık
değişken p: ondalık; j: tamsayı
başla
  eğer Uzunluk(sıra) = 0 ise dön 0
  p := 0
  sıra içindeki her j için p := p + Y[j]
  p := p / Uzunluk(sıra)
  sonuç := 2 * p * (1 - p)
bitir

fonksiyon Çoğunluk(sıra: tamsayı dizisi): tamsayı
değişken s, j: tamsayı
başla
  s := 0
  sıra içindeki her j için s := s + Y[j]
  sonuç := eğer 2 * s >= Uzunluk(sıra) ise 1 değilse 0
bitir

{ öznitelikSayısı = 1 ise her düğümde tek bir rastgele öznitelik denenir (ormanın "rastgele" kısmı) }
fonksiyon Ağaç(sıra: tamsayı dizisi; derinlik, öznitelikSayısı: tamsayı): Düğüm
değişken
  d: Düğüm
  f, k, j, enF: tamsayı
  e, puan, enPuan, enE: ondalık
  sol, sağ, adaylar: tamsayı dizisi
başla
  d := Düğüm.Oluştur
  eğer derinlik = 0 veya Gini(sıra) = 0 ise
  başla
    d.yaprak := doğru; d.sınıfı := Çoğunluk(sıra); dön d
  bitir
  adaylar := eğer öznitelikSayısı = 2 ise [0, 1] değilse [Rastgele(2)]
  enPuan := 9; enF := 0; enE := 0
  adaylar içindeki her f için
    sıra içindeki her k için
    başla
      e := X[k][f]
      sol := Süz(sıra, fonksiyon(j: tamsayı) => X[j][f] < e)
      sağ := Süz(sıra, fonksiyon(j: tamsayı) => X[j][f] >= e)
      puan := (Uzunluk(sol) * Gini(sol) + Uzunluk(sağ) * Gini(sağ)) / Uzunluk(sıra)
      eğer puan < enPuan ise başla enPuan := puan; enF := f; enE := e bitir
    bitir
  d.öznitelik := enF; d.eşik := enE
  sol := Süz(sıra, fonksiyon(j: tamsayı) => X[j][enF] < enE)
  sağ := Süz(sıra, fonksiyon(j: tamsayı) => X[j][enF] >= enE)
  eğer Uzunluk(sol) = 0 veya Uzunluk(sağ) = 0 ise
  başla
    d.yaprak := doğru; d.sınıfı := Çoğunluk(sıra); dön d
  bitir
  d.sol := Ağaç(sol, derinlik - 1, öznitelikSayısı)
  d.sağ := Ağaç(sağ, derinlik - 1, öznitelikSayısı)
  sonuç := d
bitir

fonksiyon Tahmin(d: Düğüm; x: ondalık dizisi): tamsayı
başla
  eğer d.yaprak ise dön d.sınıfı
  eğer x[d.öznitelik] < d.eşik ise dön Tahmin(d.sol, x)
  sonuç := Tahmin(d.sağ, x)
bitir

fonksiyon OrmanTahmini(x: ondalık dizisi): tamsayı
değişken oy: tamsayı; a: Düğüm
başla
  oy := 0
  orman içindeki her a için oy := oy + Tahmin(a, x)
  sonuç := eğer 2 * oy > Uzunluk(orman) ise 1 değilse 0
bitir

fonksiyon Doğruluk(tekMi: mantıksal): ondalık
değişken j, isabet, p: tamsayı
başla
  isabet := 0
  0'dan Uzunluk(Xt) - 1'e kadar her j için
  başla
    p := eğer tekMi ise Tahmin(tek, Xt[j]) değilse OrmanTahmini(Xt[j])
    eğer p = Yt[j] ise isabet := isabet + 1
  bitir
  sonuç := isabet / Uzunluk(Xt)
bitir

başla
  Tohum(3)
  X := []; Y := []; Xt := []; Yt := []
  Üret(150, X, Y)
  Üret(300, Xt, Yt)

  tek := Ağaç(Aralık(Uzunluk(X)), 8, 2)
  yaz(Biçimle('Tek derin ağaç (derinlik 8)  → test doğruluğu: {:.3}', Doğruluk(doğru)), 1)

  orman := []
  1'den 25'e kadar her t için
  başla
    { bootstrap: n örneği geri koyarak çek }
    orman.Ekle(Ağaç(Eşle(Aralık(Uzunluk(X)), fonksiyon(j: tamsayı) => Rastgele(Uzunluk(X))), 8, 1))
    eğer t = 1 veya t = 5 veya t = 25 ise
      yaz(Biçimle('Rastgele orman, {:2} ağaç       → test doğruluğu: {:.3}', t, Doğruluk(yanlış)), 1)
  bitir
bitir.

Programın çıktısı şöyledir:

Çıktı
Tek derin ağaç (derinlik 8)  → test doğruluğu: 0.803
Rastgele orman,  1 ağaç       → test doğruluğu: 0.720
Rastgele orman,  5 ağaç       → test doğruluğu: 0.783
Rastgele orman, 25 ağaç       → test doğruluğu: 0.840

Ne öğrendik. Ormanın tek ağacı (%72), tüm veriyle kurulan derin ağaçtan (%80) kötüdür; 25 ağaç bir araya gelince %84’e çıkıyor. Etiketlerin %10’u gürültü olduğu için ulaşılabilecek en iyi doğruluk %90 civarıdır. Doğruluğu her zaman eğitimde görülmemiş bir test kümesinde ölçmek gerekir: derin ağaç eğitim verisinde %100’e yakın görünür.

Alıştırma. Ağaç sayısını 100’e çıkarınız ve doğruluğun nerede durduğuna bakınız. Bootstrap’ı kaldırıp her ağacı tüm veriyle kurarsanız ne olur?

7. Gradyan artırma

Fikir. Basit bir tahminle başlarız (ortalama). Sonra sırayla küçük ağaçlar ekleriz; her yeni ağaç o ana kadarki toplamın artıklarını (gerçek − tahmin) öğrenir. Her katkı bir öğrenme oranıyla (η) küçültülür, böylece hiçbir ağaç tek başına fazla söz sahibi olmaz.

Fm(x)=Fm−1(x)+ηhm(x)F_m(x) = F_{m-1}(x) + \eta \, h_m(x)

Yöntem
program GradyanArtırma
{ Gürültülü bir sinüs eğrisini, art arda eklenen küçük "karar kütükleri"yle öğrenir.
  Her yeni kütük, o ana kadarki toplam modelin hatalarını (artıkları) tahmin etmeye çalışır:
  F_m(x) = F_(m-1)(x) + η · h_m(x)    η: öğrenme oranı }
kullan Matematik

tip
  Kütük = kayıt eşik, sol, sağ: ondalık; bitir      { x < eşik ise sol, değilse sağ değerini verir }

değişken
  X, Y, F, artık: ondalık dizisi
  kütükler: Kütük dizisi
  i, m, n: tamsayı
  eta, okh: ondalık
  k: Kütük

fonksiyon Ortalama(L: ondalık dizisi): ondalık
başla
  sonuç := eğer Uzunluk(L) = 0 ise 0.0 değilse Toplam(L) / Uzunluk(L)
bitir

{ Artıklara en iyi uyan tek bölmeli ağacı bulur: her olası eşiği dener, kare hatası en küçük olanı seçer }
fonksiyon KütükUydur(r: ondalık dizisi): Kütük
değişken
  j, q: tamsayı
  sol, sağ: ondalık dizisi
  hata, enHata: ondalık
başla
  enHata := 1e9
  1'den n - 1'e kadar her j için
  başla
    sol := []; sağ := []
    0'dan n - 1'e kadar her q için eğer X[q] < X[j] ise sol.Ekle(r[q]) değilse sağ.Ekle(r[q])
    hata := 0
    sol içindeki her e için hata := hata + (e - Ortalama(sol)) ** 2
    sağ içindeki her e için hata := hata + (e - Ortalama(sağ)) ** 2
    eğer hata < enHata ise
    başla
      enHata := hata
      sonuç := Kütük.Oluştur(X[j], Ortalama(sol), Ortalama(sağ))
    bitir
  bitir
bitir

başla
  Tohum(1)
  n := 40
  X := []; Y := []
  0'dan n - 1'e kadar her i için
  başla
    X.Ekle(i * 6.28 / n)
    Y.Ekle(Sin(i * 6.28 / n) + Normal(0, 0.15))
  bitir
  eta := 0.3
  F := Eşle(Y, fonksiyon(v: ondalık) => Ortalama(Y))   { başlangıç: herkese ortalamayı tahmin et }
  kütükler := []
  1'den 60'a kadar her m için
  başla
    artık := []
    0'dan n - 1'e kadar her i için artık.Ekle(Y[i] - F[i])
    k := KütükUydur(artık)
    kütükler.Ekle(k)
    okh := 0
    0'dan n - 1'e kadar her i için
    başla
      F[i] := F[i] + eta * (eğer X[i] < k.eşik ise k.sol değilse k.sağ)
      okh := okh + (Y[i] - F[i]) ** 2 / n
    bitir
    eğer m <= 3 veya m mod 20 = 0 ise
      yaz(Biçimle('{:2}. kütük: x < {:.2} ise {:6.3}, değilse {:6.3}   →  OKH = {:.4}', m, k.eşik, k.sol, k.sağ, okh), 1)
  bitir
  yaz(Biçimle('x = 1.57 için tahmin {:.3} (gerçek sin = 1.000)', F[10]), 1)
bitir.

Programın çıktısı şöyledir:

Çıktı
 1. kütük: x < 3.30 ise  0.599, değilse -0.662   →  OKH = 0.2739
 2. kütük: x < 3.61 ise  0.386, değilse -0.522   →  OKH = 0.1713
 3. kütük: x < 2.98 ise  0.351, değilse -0.318   →  OKH = 0.1144
20. kütük: x < 5.97 ise -0.011, değilse  0.200   →  OKH = 0.0229
40. kütük: x < 4.24 ise  0.019, değilse -0.039   →  OKH = 0.0096
60. kütük: x < 2.04 ise  0.022, değilse -0.010   →  OKH = 0.0055
x = 1.57 için tahmin 0.888 (gerçek sin = 1.000)

Ne öğrendik. İlk kütük eğriyi kabaca ikiye böldü; sonrakiler giderek küçülüp ince ayar yaptı. 20. kütükte OKH, verideki gürültünün varyansına (0,15² ≈ 0,0225) ulaştı. Ondan sonraki düşüş, model artık gürültüyü ezberlediği için yanıltıcıdır. Gerçek projelerde kaç ağaç ekleneceğine ayrı bir doğrulama kümesine bakarak karar verilir.

Alıştırma. eta değerini 1,0 ve 0,05 yapınız. Aynı OKH’ye kaç kütükte ulaşılıyor?

8. Destek vektör makinesi (SVM)

Fikir. İki sınıfı ayıran pek çok doğru vardır; SVM, iki taraftaki en yakın noktalara en uzak olanı seçer. Bu en yakın noktalara destek vektörleri denir: sınır yalnızca onlara bağlıdır, diğer noktaları silseniz sonuç değişmez. Doğrusal olmayan sınırlar için “çekirdek hilesi” kullanılır.

minw,bλ2∥w∥2+1n∑imax(0,1−yi(w⊤xi+b))\min_{w,b} \; \frac{\lambda}{2}\lVert w \rVert^2 + \frac{1}{n}\sum_i \max\bigl(0,\; 1 - y_i (w^\top x_i + b)\bigr)

Yöntem
program DestekVektörMakinesi
{ İki sınıfı ayıran ve iki sınıfa da en uzak duran doğruyu (en geniş marjı) bulur.
  Kayıp (menteşe kaybı): λ/2·‖w‖² + ortalama(max(0, 1 − y·(w·x + b)))
  Doğru tarafta ve marjın dışında kalan noktalar kayba hiç katkı yapmaz. }
kullan Matematik

değişken
  X: ondalık dizisi dizisi
  Y: tamsayı dizisi      { +1 ya da −1 }
  w0, w1, b, lam, eta, m, g0, g1, gb: ondalık
  i, t, n: tamsayı

fonksiyon Puan(x: ondalık dizisi): ondalık
başla
  sonuç := w0 * x[0] + w1 * x[1] + b
bitir

başla
  X := [[1, 2], [2, 3], [2, 1], [3, 2], [1.5, 1.5],       { −1 sınıfı }
        [5, 5], [6, 4], [5, 6], [7, 5], [6, 6.5], [4.5, 4]] { +1 sınıfı }
  Y := [-1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1]
  n := Uzunluk(X)
  w0 := 0; w1 := 0; b := 0; lam := 0.01

  1'den 5000'e kadar her t için
  başla
    eta := 1 / (lam * t + 100)        { zamanla küçülen adım }
    g0 := lam * w0; g1 := lam * w1; gb := 0
    0'dan n - 1'e kadar her i için
      eğer Y[i] * Puan(X[i]) < 1 ise    { marjın içinde ya da yanlış tarafta: düzelt }
      başla
        g0 := g0 - Y[i] * X[i][0] / n
        g1 := g1 - Y[i] * X[i][1] / n
        gb := gb - Y[i] / n
      bitir
    w0 := w0 - eta * g0; w1 := w1 - eta * g1; b := b - eta * gb
  bitir

  yaz(Biçimle('Karar sınırı: {:.3}·x + {:.3}·y {} {:.3} = 0', w0, w1, eğer b < 0 ise '-' değilse '+', Mutlak(b)), 1)
  yaz(Biçimle('Marj genişliği: 2/‖w‖ = {:.3}', 2 / Kök(w0 * w0 + w1 * w1)), 1)
  yaz('Noktalar ve y·(w·x + b) değerleri:', 1)
  0'dan n - 1'e kadar her i için
  başla
    m := Y[i] * Puan(X[i])
    yaz(Biçimle('  ({:3}, {:3}) sınıf {:2}  → {:6.3}{}', X[i][0], X[i][1], Y[i], m,
      eğer m < 1.05 ise '   ← destek vektörü' değilse ''), 1)
  bitir
  yaz(Biçimle('Yeni nokta (3.5, 3.5) → sınıf {}', eğer Puan([3.5, 3.5]) >= 0 ise '+1' değilse '-1'), 1)
bitir.

Programın çıktısı şöyledir:

Çıktı
Karar sınırı: 0.581·x + 0.536·y - 3.780 = 0
Marj genişliği: 2/‖w‖ = 2.529
Noktalar ve y·(w·x + b) değerleri:
  (  1,   2) sınıf -1  →  2.126
  (  2,   3) sınıf -1  →  1.008   ← destek vektörü
  (  2,   1) sınıf -1  →  2.081
  (  3,   2) sınıf -1  →  0.963   ← destek vektörü
  (1.5, 1.5) sınıf -1  →  2.103
  (  5,   5) sınıf  1  →  1.808
  (  6,   4) sınıf  1  →  1.853
  (  5,   6) sınıf  1  →  2.345
  (  7,   5) sınıf  1  →  2.971
  (  6, 6.5) sınıf  1  →  3.194
  (4.5,   4) sınıf  1  →  0.981   ← destek vektörü
Yeni nokta (3.5, 3.5) → sınıf +1

Ne öğrendik. 11 noktadan yalnızca 3’ü marjın kenarındadır (değeri 1’e yakındır): sınırı onlar belirler. Diğerlerinin değeri 1’den büyüktür, yani kayba katkıları sıfırdır. Özniteliklerin ölçeği burada da önemlidir; farklı birimlerdeki öznitelikler önce standartlaştırılmalıdır.

Alıştırma. lam değerini 1 yapınız. Marj genişliği ve destek vektörü sayısı nasıl değişiyor? (Büyük λ, geniş marj için birkaç hataya göz yumar.)

14. AdaBoost

Fikir. Her tur, yalnızca tek bir eşik kullanan çok zayıf bir sınıflandırıcı (karar kütüğü) eğitir. Yanlış sınıflanan örneklerin ağırlığı büyütülür; sonraki kütük zorunlu olarak onlara odaklanır. Sonunda her kütük, isabet oranına göre belirlenen bir ağırlıkla (α) oy verir.

$$\alpha_t = \tfrac{1}{2}\ln\frac{1-\varepsilon_t}{\varepsilon_t}, \qquad F(x) = \operatorname{işaret}\Bigl(\sum_t \alpha_t h_t(x)\Bigr)$$

Yöntem
program AdaBoost
{ Zayıf öğrenici (tek eşikli "karar kütükleri") ağırlıklı oylamayla güçlü bir sınıflandırıcıya dönüşür.
  Her turda yanlış sınıflanan örneklerin ağırlığı artar; sonraki kütük onlara odaklanır.
  α_t = ½ · ln((1 − hata) / hata)     F(x) = işaret(Σ α_t · h_t(x)) }
kullan Matematik

tip
  Kütük = kayıt eşik: ondalık; yön: tamsayı; alfa: ondalık; bitir   { h(x) = yön, x < eşik ise; −yön değilse }

değişken
  X, D: ondalık dizisi
  Y: tamsayı dizisi
  kütükler: Kütük dizisi
  i, t, n, isabet: tamsayı
  k, enK: Kütük
  hata, enHata, z: ondalık

fonksiyon H(k: Kütük; x: ondalık): tamsayı
başla
  sonuç := eğer x < k.eşik ise k.yön değilse -k.yön
bitir

fonksiyon Güçlü(x: ondalık): tamsayı
değişken s: ondalık; j: tamsayı
başla
  s := 0
  0'dan Uzunluk(kütükler) - 1'e kadar her j için s := s + kütükler[j].alfa * H(kütükler[j], x)
  sonuç := eğer s >= 0 ise 1 değilse -1
bitir

başla
  { Tek bir eşik bu veriyi ayıramaz: + + + − − − − + + + }
  X := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  Y := [1, 1, 1, -1, -1, -1, -1, 1, 1, 1]
  n := Uzunluk(X)
  D := Eşle(X, fonksiyon(x: ondalık) => 1.0 / n)       { başlangıçta tüm örnekler eşit ağırlıkta }
  kütükler := []
  1'den 4'e kadar her t için
  başla
    enHata := 2
    { en düşük ağırlıklı hatayı veren eşik ve yönü ara }
    0'dan n'e kadar her i için
      [1, -1] içindeki her yön için
      başla
        k := Kütük.Oluştur(i + 0.5, yön, 0)
        hata := 0
        0'dan n - 1'e kadar her j için eğer H(k, X[j]) <> Y[j] ise hata := hata + D[j]
        eğer hata < enHata ise başla enHata := hata; enK := k bitir
      bitir
    k := enK
    k.alfa := 0.5 * Ln((1 - enHata) / enHata)
    kütükler.Ekle(k)
    { ağırlıkları güncelle: yanlışlar ×e^α, doğrular ×e^−α, sonra toplam 1 olacak şekilde ölçekle }
    0'dan n - 1'e kadar her j için D[j] := D[j] * Üstel(-k.alfa * Y[j] * H(k, X[j]))
    z := Toplam(D)
    0'dan n - 1'e kadar her j için D[j] := D[j] / z
    isabet := 0
    0'dan n - 1'e kadar her j için eğer Güçlü(X[j]) = Y[j] ise isabet := isabet + 1
    yaz(Biçimle('Tur {}: x < {:4} ise {:2}   hata = {:.3}  α = {:.3}   topluluk doğruluğu: {}/{}',
      t, k.eşik, k.yön, enHata, k.alfa, isabet, n), 1)
    yaz('        ağırlıklar: ' + Eşle(D, fonksiyon(d: ondalık) => Yuvarla(d, 2)), 1)
  bitir
bitir.

Programın çıktısı şöyledir:

Çıktı
Tur 1: x <  3.5 ise  1   hata = 0.300  α = 0.424   topluluk doğruluğu: 7/10
        ağırlıklar: [0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.07, 0.17, 0.17, 0.17]
Tur 2: x <  7.5 ise -1   hata = 0.214  α = 0.650   topluluk doğruluğu: 7/10
        ağırlıklar: [0.17, 0.17, 0.17, 0.05, 0.05, 0.05, 0.05, 0.11, 0.11, 0.11]
Tur 3: x <  0.5 ise -1   hata = 0.182  α = 0.752   topluluk doğruluğu: 10/10
        ağırlıklar: [0.1, 0.1, 0.1, 0.12, 0.12, 0.12, 0.12, 0.06, 0.06, 0.06]
Tur 4: x <  3.5 ise  1   hata = 0.194  α = 0.711   topluluk doğruluğu: 10/10
        ağırlıklar: [0.06, 0.06, 0.06, 0.08, 0.08, 0.08, 0.08, 0.17, 0.17, 0.17]

Ne öğrendik. İlk kütük sağdaki üç noktayı kaçırdı; ağırlıkları 0,07’den 0,17’ye çıktı ve ikinci kütük tam onları doğru sınıflandırdı. Tek başına en fazla %70 isabet eden üç kütük, birlikte 10 noktanın hepsini doğru sınıflandırıyor. Gürültülü etiketler AdaBoost’un zayıf noktasıdır: yanlış etiketli bir örneğin ağırlığı her turda büyür.

Alıştırma. Y listesindeki bir etiketi bilerek ters çeviriniz ve 10 tur çalıştırınız. O örneğin ağırlığını izleyiniz.

15. XGBoost

Fikir. XGBoost, gradyan artırmanın hızlı ve düzenlileştirilmiş bir sürümüdür. Kaybın yalnızca eğimini (g) değil eğriselliğini de (h, ikinci türev) kullanır; böylece her yaprağın en iyi değeri tek bir formülle bulunur. λ yaprak değerlerini küçülterek aşırı uyumu engeller; γ yeterince kazanç getirmeyen bölmeleri yasaklar.

$$w^* = -\frac{G}{H + \lambda}, \qquad \text{kazanç} = \tfrac{1}{2}\Bigl[\frac{G_L^2}{H_L+\lambda} + \frac{G_R^2}{H_R+\lambda} - \frac{G^2}{H+\lambda}\Bigr] - \gamma$$

Yöntem
program XGBoost
{ XGBoost'un çekirdeği: kaybın birinci (g) ve ikinci (h) türevini kullanan, düzenlileştirilmiş artırma.
  Lojistik kayıpta p = σ(F) için:  g = p − y,  h = p·(1 − p)  (kodda grad ve hess dizileri)
  Yaprak ağırlığı: w = −G / (H + λ)          Bölmenin kazancı:
  kazanç = ½ [G_L²/(H_L+λ) + G_R²/(H_R+λ) − G²/(H+λ)] − γ   (kazanç ≤ 0 ise bölme yapılmaz) }
kullan Matematik

değişken
  X: ondalık dizisi
  Y: tamsayı dizisi
  i: tamsayı

prosedür Eğit(lam, gama: ondalık)
değişken
  F, grad, hess: ondalık dizisi
  t, j, n, enEşik: tamsayı
  GL, HL, GR, HR, G, H, kazanç, enKazanç, wL, wR, kayıp, p: ondalık
başla
  n := Uzunluk(X)
  F := Eşle(X, fonksiyon(x: ondalık) => 0.0)
  yaz(Biçimle('λ = {}, γ = {}', lam, gama), 1)
  1'den 4'e kadar her t için
  başla
    grad := []; hess := []
    0'dan n - 1'e kadar her j için
    başla
      p := Sigmoid(F[j]); grad.Ekle(p - Y[j]); hess.Ekle(p * (1 - p))
    bitir
    G := Toplam(grad); H := Toplam(hess)
    enKazanç := 0; enEşik := 0
    1'den n - 1'e kadar her i için    { eşik: X[i-1] ile X[i] arası }
    başla
      GL := 0; HL := 0
      0'dan i - 1'e kadar her j için başla GL := GL + grad[j]; HL := HL + hess[j] bitir
      GR := G - GL; HR := H - HL
      kazanç := 0.5 * (GL * GL / (HL + lam) + GR * GR / (HR + lam) - G * G / (H + lam)) - gama
      eğer kazanç > enKazanç ise başla enKazanç := kazanç; enEşik := i bitir
    bitir
    eğer enKazanç <= 0 ise
    başla
      yaz('  Tur ' + t + ': hiçbir bölme γ cezasını karşılamıyor, ağaç büyümüyor', 1)
      kır
    bitir
    GL := 0; HL := 0
    0'dan enEşik - 1'e kadar her j için başla GL := GL + grad[j]; HL := HL + hess[j] bitir
    wL := -GL / (HL + lam); wR := -(G - GL) / (H - HL + lam)
    kayıp := 0
    0'dan n - 1'e kadar her j için
    başla
      F[j] := F[j] + 0.5 * (eğer j < enEşik ise wL değilse wR)       { 0.5: küçültme (shrinkage) }
      p := Sigmoid(F[j])
      kayıp := kayıp - (Y[j] * Ln(p) + (1 - Y[j]) * Ln(1 - p)) / n
    bitir
    yaz(Biçimle('  Tur {}: x < {:4.1} | kazanç {:.3} | yapraklar {:6.3} / {:6.3} | kayıp {:.4}',
      t, (X[enEşik - 1] + X[enEşik]) / 2, enKazanç, wL, wR, kayıp), 1)
  bitir
bitir

başla
  { Müşterinin yaşı (sıralı) ve ürünü satın alıp almadığı }
  X := [18, 22, 25, 28, 31, 35, 38, 42, 46, 50, 55, 60]
  Y := [0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1]
  Eğit(0, 0)
  Eğit(5, 0)
  Eğit(5, 0.8)
bitir.

Programın çıktısı şöyledir:

Çıktı
λ = 0.0, γ = 0.0
  Tur 1: x < 26.5 | kazanç 2.722 | yapraklar -2.000 /  1.111 | kayıp 0.5110
  Tur 2: x < 33.0 | kazanç 1.252 | yapraklar -1.023 /  0.957 | kayıp 0.4290
  Tur 3: x < 26.5 | kazanç 0.493 | yapraklar -1.221 /  0.439 | kayıp 0.3962
  Tur 4: x < 52.5 | kazanç 0.318 | yapraklar -0.208 /  1.285 | kayıp 0.3755
λ = 5.0, γ = 0.0
  Tur 1: x < 33.0 | kazanç 0.580 | yapraklar -0.240 /  0.370 | kayıp 0.6428
  Tur 2: x < 26.5 | kazanç 0.476 | yapraklar -0.245 /  0.309 | kayıp 0.6023
  Tur 3: x < 33.0 | kazanç 0.395 | yapraklar -0.214 /  0.285 | kayıp 0.5696
  Tur 4: x < 26.5 | kazanç 0.324 | yapraklar -0.217 /  0.239 | kayıp 0.5431
λ = 5.0, γ = 0.8
  Tur 1: hiçbir bölme γ cezasını karşılamıyor, ağaç büyümüyor

Ne öğrendik. λ = 0 iken yaprak değerleri ±2’ye kadar çıktı ve 4. turda model 50 yaşındaki tek “hayır” için ayrı bir bölme açtı: gürültüyü kovalamaya başladı. λ = 5 yaprakları ±0,3 civarında tuttu ve model yalnızca iki anlamlı eşiği (26,5 ve 33) kullandı. γ = 0,8 ise hiçbir bölmenin bu kadar kazanç getirmediğine karar verdi. Gerçek XGBoost bunlara ek olarak derin ağaçlar, özellik örnekleme ve paralel hesap kullanır; fikir aynıdır.

Alıştırma. Eğit(1, 0.1) ekleyiniz ve sonucu üç çalıştırmayla karşılaştırınız. Hangi ayar, verideki gerçek deseni (yaş ~30’dan sonra satın alım) en iyi yakalıyor?