---
title: 'Öznitelik Seçimi'
source: 'https://academia.sh/tr/kurslar/oznitelik-muhendisligi/oznitelik-secimi'
course: 'Veri Hazırlama ve Öznitelik Mühendisliği'
language: tr
updated: '2026-08-17T18:10:11+00:00'
license: 'CC BY-SA 4.0'
---

# Öznitelik Seçimi

Süzgeç, sarmalayıcı ve gömülü yöntemlerin aynı 25 aday sütundan kaç öznitelik seçtiğinin ve seçimin kendisinin ürettiği şişmenin sayılması: seçimsiz 25 sütun sınamada 0,8135 verirken süzgeç 10 sütunla 0,8056, sarmalayıcı 4 sütunla 0,7579, gömülü yöntem 14 sütunla 0,8254 veriyor. Sarmalayıcı puanını okuduğu doğrulama kümesinde 0,8413 raporlarken sınamada 0,7579'a düşüyor; şişme 0,0834. Bin iki yüz gürültü sütunundan seçim bütün kümeden yapıldığında ayrılmış küme sayısı 0,0198, 0,0040 ve 0,0714 şişiyor, üç ölçümde de aynı işaretle.

İki derste sütun sayısı on birden yirmi beşe çıktı ve eleme her seferinde elle yapıldı: bir sütun
eklendi, ayrılmış kümedeki sayı okundu, katkısı yoksa çıkarıldı. Bu yordam yirmi beş adayda
işler, iki yüz adayda işlemez. Eleme kararının kendisi bir yordama bağlanabilir.

Üç aile vardır. **Süzgeç yöntemi** (filter) her sütunu tek başına etiketle karşılaştırır ve
modelden bağımsız bir puana göre eler. **Sarmalayıcı yöntem** (wrapper) modeli tekrar tekrar
eğitir ve sütun kümesini bir başarım sayısına göre büyütür. **Gömülü yöntem** (embedded) modelin
öznitelik önemini okur. Bu ders üçünü aynı havuzda çalıştırır ve ikinci bir soru sorar: seçim
yordamının kendisi, ayrılmış kümedeki sayıyı ne kadar şişirir.

- **OS15.** Aday havuzu 25 sütundur: M27/K01'in on bir sütunu ve önceki iki dersin ürettiği on
  dört sütun. Karar öncesi çizgi **seçimsiz** kurulumdur, yani yirmi beş sütunun hepsi.
- **OS16.** Model değişmez: derinlik 6 karar ağacı. Taban çizgisi 0,7579'dur.
- **OS17.** Süzgeç puanı, sütunun tek bir bölmede sağladığı en büyük saflık kazancıdır ve eşik
  0,004'tür; her iki yan en az 20 satır içermelidir. Eşik **önceden** seçilir.
- **OS18.** Sarmalayıcı ileri seçim yapar: her turda en çok kazandıran sütunu ekler, kazanç
  kalmayınca durur, en çok sekiz tur çalışır. Puanını **doğrulama kümesinden** okur.
- **OS19.** Gömülü yöntem derinlik 8 bir ağaç kurar, her bölmenin kazancını bölünen satır sayısıyla
  ağırlıklandırıp sütuna yazar ve toplam önemin yüzde birinden fazlasını taşıyan sütunları seçer.
- **OS20.** Üç yöntemin üçü de kararını **yalnız eğitim ve doğrulama kümelerinden** verir; sınama
  kümesi hiçbir seçime katılmaz. M26/K05'in çoklu karşılaştırma hesabı tekrarlanmaz.
- **OS21.** Şişme ölçümünde 1.200 sütunluk bir gürültü havuzu kullanılır. Sütunlar aynı tohumdan
  abone numarasına bağlı üretilir ve etiketle hiçbir ilişkileri yoktur; katkıları bilinen sıfırdır.

## Üç Yöntem, Üç Öznitelik Kümesi

```python
# ORTAK — MODELDIR. M27/K01'in KURGU abone tablosu ayni tohumla yeniden uretilir;
# model derinlik 6 karar agacidir. Onceki dersten kalan tek yeni sutun kisi_basi.
import math

TOHUM, HAM, M32 = 20260218, 1400, 0xFFFFFFFF
BOLGE = [("kuzey", 0.28, 21, 0.00), ("guney", 0.22, 17, -0.10),
         ("dogu", 0.18, 26, 0.30), ("bati", 0.14, 14, -0.05),
         ("merkez", 0.18, 23, 0.05)]


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


def ayrik(u, w):
    t = 0.0
    for i, x in enumerate(w):
        t += x
        if u < t:
            return i
    return len(w) - 1


ABONE, VERI = [], []
for i in range(HAM):
    r = uretec(TOHUM + i)
    b = BOLGE[ayrik(r(), [x[1] for x in BOLGE])]
    hane = 1 + ayrik(r(), [0.06, 0.24, 0.30, 0.24, 0.11, 0.05])
    memnun = 1 + ayrik(r(), [0.08, 0.14, 0.27, 0.34, 0.17])
    if r() >= 0.046:
        ABONE.append({"no": 10001 + i, "bolge": b, "hane": hane, "memnuniyet": memnun})
for k in ABONE:
    r, v = uretec(TOHUM + 7000 + k["no"]), []
    for d in range(ayrik(r(), [0.05, 0.12, 0.21, 0.62])):
        v.append(0.0 if r() < 0.038 else math.floor(
            k["bolge"][2] * math.exp((r() + r() + r() - 1.5) * 0.62)
            * (1 - d * 0.05) * 100 + 0.5) / 100)
        r()
    if not v:
        continue
    ort, oyn, r = sum(v) / len(v), max(v) - min(v), uretec(TOHUM + 51000 + k["no"])
    z = (0.052 * (ort - 20) + 0.85 * (min(v) == 0.0) + 0.026 * oyn
         + 0.24 * (k["hane"] >= 5) - 0.20 * (k["memnuniyet"] >= 4) + k["bolge"][3]
         + 0.9 * (k["no"] - 10001) / 1399 + (r() + r() + r() - 1.5) * 1.30)
    x = {"no": k["no"], "hane": k["hane"], "memnuniyet": k["memnuniyet"],
         "donem": len(v), "ort_tuketim": round(ort, 2), "oynaklik": round(oyn, 2),
         "sifir_okuma": int(min(v) == 0.0), "supheli": int(z > 1.35),
         "bolge_ad": k["bolge"][0]}
    for b in BOLGE:
        x["b_" + b[0]] = int(k["bolge"][0] == b[0])
    VERI.append(x)

TEMEL = ["ort_tuketim", "oynaklik", "hane", "memnuniyet", "donem", "sifir_okuma",
         "b_kuzey", "b_guney", "b_dogu", "b_bati", "b_merkez"]
TABAN_ALAN = TEMEL + ["kisi_basi"]
for x in VERI:
    x["kisi_basi"] = round(x["ort_tuketim"] / x["hane"], 3)


def karistir(veri, tohum):
    r, s = uretec(tohum), list(range(len(veri)))
    for i in range(len(s) - 1, 0, -1):
        j = int(r() * (i + 1))
        s[i], s[j] = s[j], s[i]
    return [veri[i] for i in s]


K = karistir(VERI, TOHUM + 90000)
EGT, DOG, SIN = K[:756], K[756:1008], K[1008:]


def gini(s):
    p = sum(x["supheli"] for x in s) / len(s) if s else 0.0
    return 2 * p * (1 - p)


def agac(s, derinlik, alanlar, enaz=2):
    p, en = sum(x["supheli"] for x in s) / len(s), None
    if derinlik and len(s) >= 2 * enaz and 0.0 < p < 1.0:
        for a in alanlar:
            d = sorted({x[a] for x in s})
            for v in (d[1:] if len(d) < 10 else
                      [d[int(i * len(d) / 10)] for i in range(1, 10)]):
                sol = [x for x in s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) < enaz:
                    continue
                k = gini(s) - (len(sol) * gini(sol) + len(sag) * gini(sag)) / len(s)
                if en is None or k > en[0]:
                    en = (k, a, v, sol, sag)
    if en is None or en[0] <= 1e-9:
        return {"tahmin": int(p > 0.5)}
    return {"alan": en[1], "esik": en[2],
            "sol": agac(en[3], derinlik - 1, alanlar, enaz),
            "sag": agac(en[4], derinlik - 1, alanlar, enaz)}


def tahmin(d, x):
    while "tahmin" not in d:
        d = d["sol"] if x[d["alan"]] < d["esik"] else d["sag"]
    return d["tahmin"]


def dogruluk(d, s):
    return sum(tahmin(d, x) == x["supheli"] for x in s) / len(s)


def taban(egt, s):
    c = int(sum(x["supheli"] for x in egt) * 2 > len(egt))
    return sum(x["supheli"] == c for x in s) / len(s)


def olc(alanlar, derinlik=6):
    m = agac(EGT, derinlik, alanlar)
    return dogruluk(m, EGT), dogruluk(m, SIN)
```

Aday havuzu önceki iki dersin bütün üretimini içerir — katkısı ölçülüp eksi çıkanlar da dahil,
çünkü seçim yordamının işi tam olarak onları elemektir.

```python
import datetime

BAS = datetime.date(2024, 1, 1)
KOK = ["sayac erisimi saglandi", "okuma normal", "abone evde degildi",
       "kapak kirli", "olcum tekrarlandi", "vana kontrol edildi"]
EK = ["arac park halinde", "kapi zili calismiyor", "bahce kapisi acik"]
ORTANCA = {}
for b in BOLGE:
    v = sorted(x["ort_tuketim"] for x in EGT if x["bolge_ad"] == b[0])
    ORTANCA[b[0]] = v[len(v) // 2]
for x in VERI:
    x["hane_memnuniyet"] = x["hane"] * x["memnuniyet"]
    x["esik_25"] = int(x["ort_tuketim"] > 25)
    x["bolge_ustu"] = int(x["ort_tuketim"] > ORTANCA[x["bolge_ad"]])
    r = uretec(TOHUM + 61000 + x["no"])
    g = BAS + datetime.timedelta(days=int(r() * 1460))
    x["ay"], x["ceyrek"] = g.month, (g.month - 1) // 3 + 1
    x["hafta_gunu"], x["yil_ici_gun"] = g.weekday(), g.timetuple().tm_yday
    x["sozlesme_yasi"] = 1460 - (g - BAS).days
    p = [KOK[int(r() * len(KOK))]]
    if r() < (0.42 if x["supheli"] else 0.10):
        p.append("muhur izi var")
    if r() < (0.30 if x["donem"] < 3 else 0.08):
        p.append("erisim saglanamadi")
    if r() < 0.25:
        p.append(EK[int(r() * len(EK))])
    n = ", ".join(p)
    x["not_uzunluk"], x["not_sozcuk"] = len(n), len(n.split())
    x["a_muhur"] = int("muhur" in n)
    x["a_erisim"] = int("erisim saglanamadi" in n)
    x["a_arac"] = int("arac" in n)

URETILEN = ["kisi_basi", "bolge_ustu", "esik_25", "hane_memnuniyet", "ay", "ceyrek",
            "hafta_gunu", "yil_ici_gun", "sozlesme_yasi", "not_uzunluk", "not_sozcuk",
            "a_muhur", "a_erisim", "a_arac"]
ADAY = TEMEL + URETILEN
ELLE = TABAN_ALAN + ["a_muhur"]


def puan(kaynak, a):
    d, g0, en = sorted({x[a] for x in kaynak}), gini(kaynak), 0.0
    for v in (d[1:] if len(d) < 10 else [d[int(i * len(d) / 10)] for i in range(1, 10)]):
        sol = [x for x in kaynak if x[a] < v]
        sag = [x for x in kaynak if x[a] >= v]
        if min(len(sol), len(sag)) >= 20:
            en = max(en, g0 - (len(sol) * gini(sol) + len(sag) * gini(sag)) / len(kaynak))
    return en


def suzgec(havuz, kaynak, esik=0.004):
    return [a for a in havuz if puan(kaynak, a) >= esik]


def sarmalayici(havuz, egt, olcum, tur=8):
    secili, en_iyi = [], 0.0
    for _ in range(tur):
        aday = max((dogruluk(agac(egt, 6, secili + [a]), olcum), a)
                   for a in havuz if a not in secili)
        if aday[0] <= en_iyi:
            break
        en_iyi = aday[0]
        secili.append(aday[1])
    return secili, en_iyi


def gomulu(havuz, egt, derinlik=8, pay=0.01):
    onem = dict.fromkeys(havuz, 0.0)

    def gez(s, d):
        p = sum(x["supheli"] for x in s) / len(s)
        if not d or len(s) < 4 or not 0.0 < p < 1.0:
            return
        en = None
        for a in havuz:
            dd = sorted({x[a] for x in s})
            for v in (dd[1:] if len(dd) < 10 else
                      [dd[int(i * len(dd) / 10)] for i in range(1, 10)]):
                sol = [x for x in s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) < 2:
                    continue
                k = gini(s) - (len(sol) * gini(sol) + len(sag) * gini(sag)) / len(s)
                if en is None or k > en[0]:
                    en = (k, a, sol, sag)
        if en is None or en[0] <= 1e-9:
            return
        onem[en[1]] += en[0] * len(s)
        gez(en[2], d - 1)
        gez(en[3], d - 1)
    gez(egt, derinlik)
    t = sum(onem.values())
    return [a for a in havuz if onem[a] / t >= pay], onem, t


S1 = suzgec(ADAY, EGT)
S2, RAPOR = sarmalayici(ADAY, EGT, DOG)
S3, ONEM, TOP = gomulu(ADAY, EGT)
ONCE = olc(ADAY)
print(f"{'yontem':<14} {'sutun':>5} {'egitimde':>9} {'ayrilmis':>9} {'katki':>8}")
print(f"{'secim yok':<14} {len(ADAY):>5} {ONCE[0]:>9.4f} {ONCE[1]:>9.4f}")
for ad, s in (("suzgec", S1), ("sarmalayici", S2), ("gomulu", S3), ("elle olculmus", ELLE)):
    e, sn = olc(s)
    print(f"{ad:<14} {len(s):>5} {e:>9.4f} {sn:>9.4f} {sn - ONCE[1]:>+8.4f}")
print(f"\nsarmalayicinin dogrulama kumesinde raporladigi sayi {RAPOR:.4f}, "
      f"sinamada {olc(S2)[1]:.4f}")
print("gomulu onem sirasi:", ", ".join(
    f"{a} {ONEM[a] / TOP:.3f}" for _, a in sorted((-ONEM[a], a) for a in ADAY)[:5]))
```

```
yontem         sutun  egitimde  ayrilmis    katki
secim yok         25    0.8968    0.8135
suzgec            10    0.8823    0.8056  -0.0079
sarmalayici        4    0.8241    0.7579  -0.0556
gomulu            14    0.8995    0.8254  +0.0119
elle olculmus     13    0.8876    0.8254  +0.0119

sarmalayicinin dogrulama kumesinde raporladigi sayi 0.8413, sinamada 0.7579
gomulu onem sirasi: oynaklik 0.216, a_muhur 0.204, ort_tuketim 0.145, sozlesme_yasi 0.076, not_uzunluk 0.056
```

Üç yöntem üç ayrı büyüklükte küme seçiyor: 10, 4 ve 14 sütun. Katkıları da üç ayrı işaret
taşıyor. Gömülü yöntem 0,0119 katıyor ve on dört sütunla 0,8254'e ulaşıyor — tam olarak önceki iki
dersin elle ölçerek bulduğu on üç sütunluk kümenin sayısı. Sütun kümeleri birebir aynı değil ama
ayrılmış kümedeki sayı aynı.

Süzgeç yönteminin on sütunu 0,0079 kaybettiriyor. Nedeni yöntemin tanımındadır: her sütuna
**tek başına** bakar, bu yüzden ancak birlikte bilgi taşıyan sütunları göremez ve birbirinin
kopyası olan sütunları birlikte tutar. Seçtiği on sütunda hem `not_uzunluk` hem `not_sozcuk` var;
ikisi de aynı bilginin bulanık kopyasıdır. Süzgeç ucuzdur — yirmi beş sütun için yirmi beş puan
hesaplanır — ve ucuzluğunun bedeli budur.

## Seçimin Kendi Sayısı

Sarmalayıcı satırı iki ayrı sayı taşıyor ve kursun bu dersteki asıl konusu o ikisinin arasındadır.
Yöntem dört sütun seçti ve durdu; seçimi yaparken her adımda **doğrulama kümesindeki** doğruluğa
baktı ve son adımda 0,8413 gördü. Aynı dört sütunla kurulan model sınama kümesinde 0,7579 veriyor,
yani taban çizgisinin tam üstünde duruyor. Fark 0,0834'tür.

Bu fark bir hata değil, yordamın tanımının sonucudur. İleri seçim ilk turda yirmi beş, ikinci
turda yirmi dört model kurar; kırk dokuz modelin doğrulama kümesindeki en yükseğini almak, o
kümedeki rastlantısal dalgalanmanın en yükseğini almaktır. Doğrulama kümesi seçime katıldığı anda
ayrılmış olmaktan çıkar ve üzerinde okunan sayı artık ölçüm değil, seçimin kendi hedefidir.

Sonuç, bu kursun ikinci kuralının seçim için okunmuş hâlidir: **seçilen öznitelik kümesi de
eğitilmiş bir nesnedir.** Ölçek gibi, kategori eşlemesi gibi, o da veriden öğrenilir ve hangi
veriden öğrenildiği yazılmak zorundadır.

Gömülü yöntemin önem sırası son satırda duruyor: en önemli üç sütun oynaklık, mühür göstergesi ve
ortalama tüketim, toplam önemin 0,565'ini taşıyor. Dördüncü sıradaki sözleşme yaşı ise etiketle
hiç ilişkisi olmayan bir sütundur ve yine de önemin 0,076'sını almıştır. Öznitelik önemi bir
sıralamadır, kanıt değildir.

## Seçim Bütün Kümeden Yapılırsa

Seçimin şişirdiğini temiz ölçmek için katkısı **bilinen sıfır** olan bir havuz gerekir. Aşağıdaki
kod 1.200 gürültü sütunu üretiyor; hiçbirinin etiketle ilişkisi yok. Süzgeç bu havuzdan en çok
ayıran birkaç sütunu seçiyor, bir kez yalnız eğitim kümesine bakarak, bir kez bütün kümeye bakarak.

```python
GURULTU = ["g" + str(j) for j in range(1200)]
for x in VERI:
    r = uretec(TOHUM + 41000 + x["no"])
    for a in GURULTU:
        x[a] = round(r(), 4)


def ayirma(kaynak, a):
    v1 = [x[a] for x in kaynak if x["supheli"]]
    v0 = [x[a] for x in kaynak if not x["supheli"]]
    return abs(sum(v1) / len(v1) - sum(v0) / len(v0))


def en_iyi_k(kaynak, k):
    return [a for _, a in sorted((-ayirma(kaynak, a), a) for a in GURULTU)[:k]]


print(f"{'secilen':>8} {'kaynak':<12} {'egitimde':>9} {'ayrilmis':>9} {'sisme':>8}")
for k in (2, 4, 8):
    e1, s1 = olc(en_iyi_k(EGT, k))
    e2, s2 = olc(en_iyi_k(K, k))
    print(f"{k:>8} {'egitim':<12} {e1:>9.4f} {s1:>9.4f}")
    print(f"{k:>8} {'butun kume':<12} {e2:>9.4f} {s2:>9.4f} {s2 - s1:>+8.4f}")
print(f"\ntaban cizgisi {taban(EGT, SIN):.4f}; havuzda {len(GURULTU)} gurultu sutunu var")
```

```
 secilen kaynak        egitimde  ayrilmis    sisme
       2 egitim          0.8320    0.7262
       2 butun kume      0.8069    0.7460  +0.0198
       4 egitim          0.8307    0.7421
       4 butun kume      0.8241    0.7460  +0.0040
       8 egitim          0.8704    0.6667
       8 butun kume      0.8479    0.7381  +0.0714

taban cizgisi 0.7579; havuzda 1200 gurultu sutunu var
```

Şişme sütununun üç değeri de aynı işaretli: +0,0198, +0,0040, +0,0714. M27/K01'in sızıntı dersinde
dört sızıntı türünün şişmesi de aynı işaretliydi. Buradaki kanal da aynı: seçim bütün kümeye
baktığında sınama kümesinin 252 satırının etiketleri hangi sütunların seçileceğine katkıda bulunur,
seçilen sütunlar o satırlarda daha uyumlu çıkar ve ölçüm yükselir. Fark sütun sayısıyla büyüyor;
sekiz sütunda 0,0714, yani on sekiz abone.

Bir ayrıntı bu tabloyu tehlikeli kılıyor: şişmiş sayıların hiçbiri **taban çizgisini geçmiyor**.
Gürültüden model çıkmıyor, sızıntı yalnız sayıyı yukarı çekiyor. Gerçek bir kümede aynı kanal,
tabanı zaten geçen bir modelin sayısına eklenir ve tabloda sıradan bir iyileşme gibi görünür.
Seçimin yalnız eğitim kümesinden yapılması, sayının küçük çıkması pahasına doğru çıkmasını
sağlayan tek düzenlemedir.

## Özet

- Aynı 25 aday sütunda süzgeç 10, sarmalayıcı 4, gömülü yöntem 14 sütun seçti; seçimsiz kurulum
  0,8135 verirken sırasıyla 0,8056, 0,7579 ve 0,8254 verdiler.
- Gömülü yöntem 0,0119 katkıyla iki dersin elle ölçerek bulduğu sayıya, 0,8254'e, tek yordamla
  ulaştı; süzgeç birbirinin kopyası olan iki sütunu birlikte tuttuğu için 0,0079 kaybettirdi.
- Sarmalayıcı puanını okuduğu doğrulama kümesinde 0,8413 raporladı, sınamada 0,7579 verdi; şişme
  0,0834'tür ve nedeni kırk dokuz model arasından o kümedeki en yükseği seçmiş olmasıdır.
- Seçilen öznitelik kümesi eğitilmiş bir nesnedir; hangi kümeden öğrenildiği yazılmadıkça
  üzerinde okunan sayı ölçüm değildir.
- Katkısı bilinen sıfır olan 1.200 gürültü sütununda seçim bütün kümeden yapıldığında şişme
  +0,0198, +0,0040 ve +0,0714 çıktı; üçü de aynı işaretli ve hiçbiri taban çizgisini geçmiyor.

## Sonraki Adım

Seçim, sütunları teker teker tutup atıyor ve tuttuklarının adı okunabiliyor. Sütun sayısını
azaltmanın başka bir yolu daha var — sütunları atmak yerine birkaç yeni **bileşene** dönüştürmek. Bu yolda her yeni sütun eski sütunların bir
karışımıdır ve karışımın adı yoktur. Kaç bileşende ham özniteliklerin ayrılmış küme sayısına
yetişiliyor ve öznitelik önemi listesi okunabilirliğini yitirdiğinde ne kaybediliyor — sonraki
ders bunu ölçer.
