---
title: 'Boru Hatları'
source: 'https://academia.sh/tr/kurslar/oznitelik-muhendisligi/boru-hatlari'
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'
---

# Boru Hatları

Hazırlama zincirinin bir nesneye bağlanması ve boru hattı olmadan kaç adımın sızdığının sayılması: dört adımlı zincir elle uygulandığında dördü de bütün kümeden öğrenilir ve sınama sayısı 0,7817'den 0,8294'e çıkar, şişme 0,0476. Doldurma ve ölçekleme sızdığında sayı hiç değişmez, hedef temelli kodlama sızdığında 0,0397, seçim de sızdığında 0,0476 şişer. Boru hattı 131 sayı öğrenir ve bu sayıların hepsi eğitim kümesinden gelir; ölçekleme adımının ağaç modelindeki katkısı tam sıfır, hedef temelli kodlama adımının katkısı ise eksi 0,0317'dir.

Dört derste bir cümle tekrarlandı: bu adım yalnız eğitim kümesinden öğrenilir. Bölge ortancası,
ölçek ortalaması, seçilen sütun kümesi, bileşen yönleri — dördü de veriden okunmuş sayılardır ve
dördünün de doğru kümeden okunması gerekir. Cümle tekrarlandıkça bir başka şey görünür oldu: bu
adımların hepsi aynı biçime sahiptir. Her birinin bir **öğrenme** yüzü vardır — veriden bir sayı
ya da bir eşleme çıkarır — ve bir **uygulama** yüzü — o sayıyı verilen satırlara işler.

Bu biçim bir nesne tanımıdır. Adımlar tek tek elle çağrıldığında sıra karışabilir, bir adım yanlış
kümeden öğrenebilir, sınama zamanı bir adım unutulabilir. Adımları tek bir nesnede toplayan yapıya
**boru hattı** (pipeline) denir: boru hattının da bir öğrenme yüzü vardır ve yalnız eğitim
kümesini görür, bir uygulama yüzü vardır ve her kümeye aynı öğrenilmiş sayıları uygular. Bu ders
boru hattını yazar ve tek bir soru sorar: aynı zincir elle uygulandığında kaç adım sızar, sayı ne
kadar şişer.

- **OS30.** Zincir dört adımdır: eksik değer doldurma, ölçekleme, hedef temelli kodlama, öznitelik
  seçimi. Beşinci sırada model vardır ve değişmez: derinlik 6 karar ağacı.
- **OS31.** Eksik memnuniyet değerleri ve `tarife_kodu` sütunu **kurgudur**, aynı tohumdan üretilir.
  Tarife kodu bölge ve hane büyüklüğüne bağlıdır, yani etiketle gerçek bir ilişki taşır.
- **OS32.** Hedef temelli kodlama, tarife kodunun her düzeyi için şüpheli oranını öğrenir.
  **Görülmemiş kategori** genel orana düşer.
- **OS33.** Sızıntı ölçümünde ilk `k` adım bütün kümeden öğrenir, kalanlar eğitim kümesinden;
  model her durumda yalnız eğitim kümesinden eğitilir.
- **OS34.** Şişme, boru hattının sınama kümesindeki sayısına göre farktır. Taban çizgisi 0,7579.
- **OS35.** M27/K01'in sızıntı dersi dört sızıntı türünü tek tek saymıştı; buradaki kanal onun
  birinci türüdür — ayrımdan önce hazırlama — ve şişme sayıları tekrarlanmaz.

## Adımın İki Yüzü

```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)
```

Dört adımın dördü de aynı iki yöntemi taşır. `ogren` yalnız kendisine verilen satırlara bakar ve
öğrendiğini `nesne` alanına yazar; `uygula` o nesneyi verilen satırlara işler ve hiçbir şey
öğrenmez. Boru hattı bu ikiliği zincirin tamamına taşır.

```python
for x in VERI:
    r = uretec(TOHUM + 61000 + x["no"])
    r(), r()
    x["a_muhur"] = int(r() < (0.42 if x["supheli"] else 0.10))
    r = uretec(TOHUM + 21000 + x["no"])
    x["tarife_kodu"] = (24 * [b[0] for b in BOLGE].index(x["bolge_ad"])
                        + 4 * (x["hane"] - 1) + int(r() * 4))
    x["memnuniyet"] = None if r() < 0.08 else x["memnuniyet"]
HAVUZ = TABAN_ALAN + ["a_muhur", "tarife_hedef"]


class Doldurma:
    ad = "doldurma"

    def ogren(self, s):
        v = sorted(x["memnuniyet"] for x in s if x["memnuniyet"] is not None)
        self.nesne = {"ortanca": v[len(v) // 2]}

    def uygula(self, s):
        for x in s:
            if x["memnuniyet"] is None:
                x["memnuniyet"] = self.nesne["ortanca"]


class Olcekleme:
    ad = "olcekleme"

    def ogren(self, s):
        self.nesne = {}
        for a in ("ort_tuketim", "oynaklik", "kisi_basi"):
            o = sum(x[a] for x in s) / len(s)
            self.nesne[a] = (o, max(1e-9, (sum((x[a] - o) ** 2 for x in s)
                                           / len(s)) ** 0.5))

    def uygula(self, s):
        for x in s:
            for a, (o, d) in self.nesne.items():
                x[a] = round((x[a] - o) / d, 4)


class HedefKodlama:
    ad = "hedef kodlama"

    def ogren(self, s):
        g, self.genel = {}, sum(x["supheli"] for x in s) / len(s)
        for x in s:
            g.setdefault(x["tarife_kodu"], []).append(x["supheli"])
        self.nesne = {k: sum(v) / len(v) for k, v in g.items()}

    def uygula(self, s):
        for x in s:
            x["tarife_hedef"] = round(self.nesne.get(x["tarife_kodu"], self.genel), 4)


class Secim:
    ad = "secim"

    def __init__(self, havuz):
        self.havuz = havuz

    def ogren(self, s):
        g0, self.nesne = gini(s), []
        for a in self.havuz:
            d, en = sorted({x[a] for x in s}), 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 s if x[a] < v]
                sag = [x for x in s if x[a] >= v]
                if min(len(sol), len(sag)) >= 20:
                    en = max(en, g0 - (len(sol) * gini(sol)
                                       + len(sag) * gini(sag)) / len(s))
            if en >= 0.004:
                self.nesne.append(a)

    def uygula(self, s):
        pass


class BoruHatti:
    def __init__(self, adimlar, derinlik=6):
        self.adimlar, self.derinlik = adimlar, derinlik

    def ogren(self, egt, sizinti_kaynagi=None, sizan=0):
        egt = [dict(x) for x in egt]
        kirli = [dict(x) for x in (sizinti_kaynagi or [])]
        for i, a in enumerate(self.adimlar):
            if i < sizan:
                a.ogren(kirli)
                a.uygula(kirli)
            else:
                a.ogren(egt)
            a.uygula(egt)
        self.model = agac(egt, self.derinlik, self.adimlar[-1].nesne)
        self.egitimde = dogruluk(self.model, egt)
        return self

    def olc(self, s):
        s = [dict(x) for x in s]
        for a in self.adimlar:
            a.uygula(s)
        return dogruluk(self.model, s)


def zincir(havuz=None):
    return [Doldurma(), Olcekleme(), HedefKodlama(), Secim(havuz or HAVUZ)]


BH = BoruHatti(zincir()).ogren(EGT)
print(f"{'kurulum':<26} {'sizan':>5} {'sutun':>5} {'egitimde':>9} {'ayrilmis':>9} "
      f"{'sisme':>8}")
print(f"{'boru hatti':<26} {0:>5} {len(BH.adimlar[-1].nesne):>5} {BH.egitimde:>9.4f} "
      f"{BH.olc(SIN):>9.4f}")
for k, ad in ((1, "elle: doldurma"), (2, "elle: + olcekleme"),
              (3, "elle: + hedef kodlama"), (4, "elle: + secim")):
    b = BoruHatti(zincir()).ogren(EGT, K, k)
    print(f"{ad:<26} {k:>5} {len(b.adimlar[-1].nesne):>5} {b.egitimde:>9.4f} "
          f"{b.olc(SIN):>9.4f} {b.olc(SIN) - BH.olc(SIN):>+8.4f}")
print(f"\ntaban cizgisi {taban(EGT, SIN):.4f}; tarife kodu "
      f"{len({x['tarife_kodu'] for x in VERI})} duzey, eksik memnuniyet "
      f"{sum(1 for x in VERI if x['memnuniyet'] is None)} satir")
```

```
kurulum                    sizan sutun  egitimde  ayrilmis    sisme
boru hatti                     0     8    0.8955    0.7817
elle: doldurma                 1     8    0.8955    0.7817  +0.0000
elle: + olcekleme              2     8    0.8955    0.7817  +0.0000
elle: + hedef kodlama          3     8    0.9021    0.8214  +0.0397
elle: + secim                  4     9    0.9008    0.8294  +0.0476

taban cizgisi 0.7579; tarife kodu 119 duzey, eksik memnuniyet 103 satir
```

Zincir elle uygulandığında **dört adımın dördü de** bütün kümeden öğrenilebilir, çünkü dördü de
bir `ogren` çağrısıdır ve o çağrıya hangi listenin verildiği kodun başka bir yerinde yazılıdır.
Boru hattında bu karar tek bir yerde durur: `ogren` yöntemi eğitim kümesini alır ve zincirin
tamamı o kümeden öğrenir. Adım eklemek sızıntı riskini artırmaz.

Şişme sütunu adım adım okunmalıdır. İlk iki satır **sıfır**: doldurma değeri bütün kümeden
alınınca sayı değişmiyor, ölçek bütün kümeden alınınca da değişmiyor. Bu, o iki adımın sızmadığı
anlamına gelmez — sızmıştır, ölçülen sayı yalnız bu kümede ve bu modelde değişmemiştir. Karar
ağacı bölmelerini sıralamaya göre yapar ve standartlaştırma sıralamayı değiştirmez; ortanca ile
doldurma ise 103 satırı etkiler ve iki ortanca birbirine yakındır. Sızıntının görünmemesi
yokluğunun kanıtı değildir.

Üçüncü satır hedef temelli kodlamadır ve sayı 0,0397 şişiyor: on abone. Neden açık — bu adım
etiketi doğrudan kullanır. Tarife kodunun 119 düzeyi vardır ve her düzeye ortalama on satır düşer;
kodlama bütün kümeden öğrenildiğinde bir sınama satırının kendi etiketi, o satırın göreceği
sayının onda birini oluşturur. Dördüncü satırda seçim de bütün kümeden yapılıyor, seçilen sütun
sayısı sekizden dokuza çıkıyor ve toplam şişme 0,0476'ya, on iki aboneye ulaşıyor.

Kursun ikinci kuralı bu tabloda tamamlanıyor: **her hazırlama adımı yalnız eğitim kümesinden
öğrenilir ve ayrılmış kümeye uygulanır.** Boru hattı bu kuralı bir yordama çevirir; kural yazılı
bir uyarı olmaktan çıkıp nesnenin arayüzü olur.

## Boru Hattının Ölçtüğü

Boru hattının ikinci yararı sızıntıyı önlemek değil, adımları **ölçülebilir** kılmaktır. Zincirden
bir adım çıkarılıp aynı ölçüm tekrarlandığında o adımın dürüst katkısı okunur.

```python
DAR = TABAN_ALAN + ["a_muhur"]
print("boru hattinin ogrendigi nesneler: " + ", ".join(
    f"{a.ad} {len(a.nesne)}" for a in BH.adimlar))
gor = set(BH.adimlar[2].nesne)
print(f"tarife kodunun {len({x['tarife_kodu'] for x in VERI})} duzeyinden {len(gor)} "
      f"tanesi egitimde goruldu; sinamada gorulmemis kategoriye dusen "
      f"{sum(x['tarife_kodu'] not in gor for x in SIN)} satir\n")
print(f"{'zincir':<22} {'adim':>4} {'sutun':>5} {'egitimde':>9} {'ayrilmis':>9} "
      f"{'katki':>8}")
print(f"{'dort adim':<22} {4:>4} {len(BH.adimlar[-1].nesne):>5} {BH.egitimde:>9.4f} "
      f"{BH.olc(SIN):>9.4f}")
for ad, al in (("olcekleme cikarildi", [Doldurma(), HedefKodlama(), Secim(HAVUZ)]),
               ("hedef kodlama cikti", [Doldurma(), Olcekleme(), Secim(DAR)]),
               ("yalniz doldurma+secim", [Doldurma(), Secim(DAR)])):
    b = BoruHatti(al).ogren(EGT)
    print(f"{ad:<22} {len(al):>4} {len(b.adimlar[-1].nesne):>5} {b.egitimde:>9.4f} "
          f"{b.olc(SIN):>9.4f} {b.olc(SIN) - BH.olc(SIN):>+8.4f}")
```

```
boru hattinin ogrendigi nesneler: doldurma 1, olcekleme 3, hedef kodlama 116, secim 8
tarife kodunun 119 duzeyinden 116 tanesi egitimde goruldu; sinamada gorulmemis kategoriye dusen 1 satir

zincir                 adim sutun  egitimde  ayrilmis    katki
dort adim                 4     8    0.8955    0.7817
olcekleme cikarildi       3     8    0.8955    0.7817  +0.0000
hedef kodlama cikti       3     7    0.8730    0.8135  +0.0317
yalniz doldurma+secim     2     7    0.8730    0.8135  +0.0317
```

Boru hattı toplam 131 sayı öğreniyor: bir ortanca, üç sütun için ikişer sayı, 116 tarife düzeyi
için birer oran ve sekiz sütunluk bir liste. Bu 131 sayının hepsi eğitim kümesinin 756 satırından
gelir ve hiçbiri sınama kümesine bakmaz. Sızıntı tartışması bu sayının ne kadarının hangi kümeden
geldiği tartışmasıdır.

Tarife kodunun 119 düzeyinin 116'sı eğitim kümesinde görülmüş; kalan üç düzeyden biri sınama
kümesinde bir satırla karşımıza çıkıyor ve **görülmemiş kategori** olarak genel orana düşüyor.
Elle yazılmış bir hazırlama zincirinde bu satır tipik olarak bir hataya ya da sessiz bir boş
değere dönüşür; boru hattında adımın uygulama yüzü kararı bir kez verir ve her kümede aynı kararı
verir.

Alt tablo iki adımın dürüst katkısını okuyor. Ölçekleme çıkarıldığında sayı **hiç** değişmiyor:
katkı tam sıfır, çünkü karar ağacı sıralamaya bakar ve doğrusal bir ölçek dönüşümü sıralamayı
değiştirmez. Adım yanlış yapıldığında da bir şey değiştirmediği için sessizce yanlış kalabilir.
Hedef temelli kodlama çıkarıldığında ise sayı 0,7817'den 0,8135'e **yükseliyor**: adımın dürüst
katkısı eksi 0,0317'dir. Yüksek kardinaliteli bir sütunu hedefle kodlamak, doğru kümeden
öğrenilse bile bu kümede zarar veriyor; bütün kümeden öğrenildiğinde ise 0,0397 kazandırıyor
görünüyordu. Aynı adım, sızdırıldığında en kârlı, dürüst uygulandığında en zararlı adımdır.

Bu dersin karar öncesi ve karar sonrası sayıları bu yüzden ters yönde okunur. Elle uygulanan
zincir 0,8294, boru hattı 0,7817 veriyor; katkı eksi 0,0476. Eksi işaret burada kaybı değil,
**kaybedilen sayının öğrenilmiş olmadığını** gösterir. Boru hattının kattığı şey sayı değil,
sayının doğruluğudur.

## Özet

- Hazırlama adımı iki yüzlü bir nesnedir: `ogren` yalnız eğitim kümesini görür ve öğrendiğini
  saklar, `uygula` hiçbir şey öğrenmeden o sayıları her kümeye işler.
- Zincir elle uygulandığında dört adımın dördü de bütün kümeden öğrenilebilir; boru hattında karar
  tek bir yerde durur ve adım eklemek sızıntı riskini artırmaz.
- Şişme adım adım 0,0000, 0,0000, 0,0397 ve 0,0476 çıkıyor; ilk iki adımın sızması sayıyı
  değiştirmiyor, ve sızıntının görünmemesi yokluğunun kanıtı değildir.
- Boru hattı 131 sayı öğreniyor ve hepsi eğitim kümesinin 756 satırından geliyor; tarife kodunun
  119 düzeyinden 116'sı görülmüş, sınamada bir satır görülmemiş kategoriye düşüyor.
- Aynı adım sızdırıldığında 0,0397 kazandırıyor, dürüst uygulandığında 0,0317 kaybettiriyor;
  boru hattının katkısı eksi 0,0476'dır ve bu eksi, kaybedilen sayının öğrenilmiş olmadığını
  gösterir.

## Sonraki Adım

Boru hattı zincirin tamamını tek bir kümeden öğrenmeye bağladı ve ölçüm tek bir bölmeden okundu:
756 satır eğitim, 252 satır sınama. O 252 satır bir kez kullanıldı ve verdiği sayı tek bir sayıdır;
başka bir bölmede başka bir sayı çıkardı. Ölçümü birden çok bölme üzerinden almanın adı çapraz
doğrulamadır ve hazırlama zinciri buraya girince yeni bir soru doğar: zincir kat dışında bir kez
mi öğrenilir, yoksa her katın içinde yeniden mi. İki yordam iki ayrı sayı verir ve sonraki ders
hangisinin ayrılmış kümedeki gerçek sayıya yakın çıktığını ölçer.
