Ders 03 / 19
Görsel Denetim
Var olan arayüzlerin biçem kaynağından değer envanterinin makineyle çıkarılması, yakın-yinelemelerin eşiklerle kümelemesi, bağlama kuralının sayıya etkisi ve ürün bazında sapma haritası.
İçindekiler
Önceki ders sistemin ne zaman kendini ödediğini hesapladı ve kapsamın en belirleyici karar olduğunu gösterdi. Kapsam, hangi kararların sisteme gireceğini söyler; bunu bilmek için de hangi kararların fiilen tekrarlandığını bilmek gerekir. Tahminle çizilen bir kapsam ya kullanılmayan belirteçler üretir ya da en sık kullanılan kararı dışarıda bırakır.
Görsel denetim (visual audit), var olan arayüzlerin fiilen kullandığı değerlerin sayılmasıdır. Denetimin ayırt edici özelliği, kaynağının bellek değil eser olmasıdır: kimseye “kart iç boşluğu kaç piksel” diye sorulmaz, biçem kaynağından okunur. Bu ders o okumayı yapan hesabı kurar ve envanterin hangi bölümünün gerçek karar, hangi bölümünün karşılıksız çeşitlilik olduğunu ayırır.
Denetim Eserden Okunur
Katalog kurumunun dört ürünü var: katalog arayüzü, yönetim paneli, üye profili ve raf yerleşim ekranı. Dördü ayrı zamanlarda, kısmen ayrı ekiplerce yazıldı. Yinelenme ve Tutarlılık dersinde katalog arayüzünün dört ekranı elle sayılmıştı; burada sayım dört ürünün tamamına genişler ve elle değil, kaynağı ayrıştırarak yapılır.
Elle sayımla makine sayımı arasındaki fark yalnız hız değildir. Elle sayım, sayanın gözüne çarpan değerleri bulur; makine sayımı, kimsenin bakmadığı bildirimleri de bulur. Denetimin değeri tam buradadır: aranan şey zaten bilinen tutarsızlık değil, bilinmeyen tutarsızlıktır.
Sayım beş özellik sınıfı üzerinde yapılır: aralık, yazı boyutu, köşe yarıçapı, kenar kalınlığı ve renk. İlk dördünde yakınlık ölçütü piksel farkıdır ve eşikler Yinelenme ve Tutarlılık dersindeki ayırt edilebilirlik eşikleridir. Renkte ölçüt iki parçalıdır: iki renk hem birbirine karşı kontrast oranı 1.10’un altındaysa hem de en büyük kanal farkı 16’nın altındaysa aynı kararı taşıyor sayılır. Tek başına kontrast oranı yetmez, çünkü farklı tonlardaki iki renk aynı bağıl parlaklığa sahip olabilir.
// denetim.mjs — dort katalog urununun bicem kaynagindan envanter cikarma ve yakin-yineleme kumeleme // Dort urunun bicem kaynagindan alinmis bildirimler. Her satirin onunde urun kodu var: // K katalog, Y yonetim paneli, U uye profili, R raf yerlesim ekrani. const KAYNAK = ` K .arama-alani { padding: 8px 12px; font-size: 14px; border-radius: 4px; border: 1px solid #b7bcc2; color: #212327; } K .arama-dugmesi { padding: 8px 16px; font-size: 14px; border-radius: 4px; background: #275ea5; color: #ffffff; } K .kayit-karti { padding: 16px; border-radius: 8px; border: 1px solid #d3d6d9; background: #ffffff; } K .kayit-basligi { font-size: 20px; color: #212327; margin-bottom: 4px; } K .kayit-yazari { font-size: 16px; color: #5e656e; margin-bottom: 8px; } K .kayit-ustverisi { font-size: 13px; color: #757e8a; } K .kayit-etiketi { padding: 2px 8px; font-size: 12px; border-radius: 12px; background: #dee9f7; color: #15335b; } K .sonuc-listesi { gap: 24px; padding: 32px 24px; } K .sayfalama { gap: 8px; font-size: 14px; padding: 24px 0; } K .onay-katmani { padding: 24px; border-radius: 8px; background: #ffffff; border: 1px solid #d3d6d9; } K .onay-uyarisi { font-size: 14px; color: #ab3321; margin-top: 8px; } Y .panel-baslik { font-size: 24px; color: #1f2226; margin-bottom: 16px; } Y .panel-kart { padding: 14px; border-radius: 6px; border: 1px solid #d5d8db; background: #fdfdfd; } Y .panel-tablo { font-size: 13px; color: #333333; } Y .panel-tablo-basligi { font-size: 12px; color: #6b7280; padding: 6px 10px; } Y .panel-satiri { padding: 10px 12px; border-bottom: 1px solid #e9eaec; } Y .panel-dugmesi { padding: 6px 14px; font-size: 13px; border-radius: 3px; background: #2a5fa8; color: #ffffff; } Y .panel-ikincil-dugme { padding: 6px 14px; font-size: 13px; border-radius: 3px; border: 1px solid #b7bcc2; color: #275ea5; } Y .panel-uyari { font-size: 12px; color: #a83224; padding: 10px; border-radius: 3px; } Y .panel-bolum { gap: 20px; padding: 20px; } U .profil-basligi { font-size: 22px; color: #212327; margin-bottom: 12px; } U .profil-karti { padding: 20px; border-radius: 8px; border: 1px solid #d3d6d9; background: #ffffff; } U .odunc-satiri { padding: 12px 16px; font-size: 15px; color: #22252a; border-bottom: 1px solid #e9eaec; } U .odunc-tarihi { font-size: 13px; color: #5e656e; } U .gecikme-uyarisi { font-size: 13px; color: #ab3321; padding: 8px 12px; border-radius: 4px; background: #fbeae7; } U .profil-dugmesi { padding: 10px 18px; font-size: 15px; border-radius: 6px; background: #275ea5; color: #ffffff; } U .profil-bolumu { gap: 16px; padding: 28px 24px; } R .raf-basligi { font-size: 18px; color: #212327; margin-bottom: 8px; } R .raf-hucresi { padding: 12px; border-radius: 4px; border: 2px solid #b7bcc2; } R .raf-kodu { font-size: 12px; color: #757e8a; } R .raf-dolu { background: #e9eaec; border: 2px solid #969da6; } R .raf-secili { border: 2px solid #3075cf; background: #dee9f7; } R .raf-izgarasi { gap: 6px; padding: 18px; } R .raf-uyarisi { font-size: 13px; color: #b03526; padding: 8px; border-radius: 4px; } `; // --- cikarma ----------------------------------------------------------------- const UZUNLUK_OZELLIK = { aralik: /^(padding|margin-top|margin-bottom|gap)$/, "yazi boyutu": /^font-size$/, "kose yaricapi": /^border-radius$/, }; const SINIFLAR = ["aralik", "yazi boyutu", "kose yaricapi", "kenar kalinligi", "renk"]; const envanter = Object.fromEntries(SINIFLAR.map((s) => [s, []])); for (const satir of KAYNAK.trim().split("\n")) { const urun = satir[0]; const govde = satir.slice(satir.indexOf("{") + 1, satir.lastIndexOf("}")); for (const bildirim of govde.split(";")) { const i = bildirim.indexOf(":"); if (i < 0) continue; const ozellik = bildirim.slice(0, i).trim(); const deger = bildirim.slice(i + 1).trim(); for (const [sinif, kalip] of Object.entries(UZUNLUK_OZELLIK)) { if (!kalip.test(ozellik)) continue; for (const p of deger.split(/\s+/)) { const m = p.match(/^(\d+(?:\.\d+)?)px$/); if (m) envanter[sinif].push({ deger: Number(m[1]), urun }); } } if (/^border(-bottom)?$/.test(ozellik)) { const m = deger.match(/^(\d+(?:\.\d+)?)px/); if (m) envanter["kenar kalinligi"].push({ deger: Number(m[1]), urun }); } for (const m of deger.matchAll(/#([0-9a-f]{6})/g)) { envanter.renk.push({ deger: "#" + m[1], urun }); } } } // --- yakinlik olcutleri ------------------------------------------------------- const ESIK = { aralik: 2, "yazi boyutu": 1, "kose yaricapi": 2, "kenar kalinligi": 0.5 }; const cz = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)); function kanal(v) { const s = v / 255; return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4); } const parlaklik = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b); function kontrast(a, b) { const [x, y] = [parlaklik(a), parlaklik(b)].sort((p, q) => q - p); return (x + 0.05) / (y + 0.05); } const kanalFarki = (a, b) => Math.max(...a.map((v, i) => Math.abs(v - b[i]))); // Iki deger ayni karari tasiyor mu? Renkte olcut hem kontrast orani hem kanal farkidir. function yakin(sinif, a, b) { if (sinif !== "renk") return Math.abs(a - b) <= ESIK[sinif]; return kontrast(cz(a), cz(b)) < 1.1 && kanalFarki(cz(a), cz(b)) < 16; } function sirala(sinif, degerler) { const tekil = [...new Set(degerler)]; return sinif === "renk" ? tekil.sort((a, b) => parlaklik(cz(b)) - parlaklik(cz(a))) : tekil.sort((a, b) => a - b); } // Zincirleme baglama: bir degere yakin tek bir uye bulmak kumeye katilmaya yeter. function zincirleme(sinif, degerler) { const kumeler = []; for (const d of sirala(sinif, degerler)) { const k = kumeler.find((k) => k.some((o) => yakin(sinif, d, o))); if (k) k.push(d); else kumeler.push([d]); } return kumeler; } // Cap sinirli baglama: kumeye katilmak icin kumedeki HER uyeye yakin olmak gerekir. function capSinirli(sinif, degerler) { const kumeler = []; for (const d of sirala(sinif, degerler)) { const k = kumeler.find((k) => k.every((o) => yakin(sinif, d, o))); if (k) k.push(d); else kumeler.push([d]); } return kumeler; } console.log("ozellik kullanim ayri deger zincirleme kume cap sinirli kume"); let toplamKullanim = 0; let toplamAyri = 0; let toplamZincir = 0; let toplamCap = 0; const capKumeleri = {}; for (const sinif of SINIFLAR) { const degerler = envanter[sinif].map((k) => k.deger); const z = zincirleme(sinif, degerler); const c = capSinirli(sinif, degerler); capKumeleri[sinif] = c; const ayri = new Set(degerler).size; toplamKullanim += degerler.length; toplamAyri += ayri; toplamZincir += z.length; toplamCap += c.length; console.log( `${sinif.padEnd(15)} ${String(degerler.length).padStart(9)} ${String(ayri).padStart(11)} ` + `${String(z.length).padStart(16)} ${String(c.length).padStart(17)}` ); } console.log( `${"toplam".padEnd(15)} ${String(toplamKullanim).padStart(9)} ${String(toplamAyri).padStart(11)} ` + `${String(toplamZincir).padStart(16)} ${String(toplamCap).padStart(17)}` ); console.log( `karsiliksiz cesitlilik (cap sinirli): ${toplamAyri - toplamCap} deger, ` + `${((100 * (toplamAyri - toplamCap)) / toplamAyri).toFixed(1)}%` ); console.log("\ncap sinirli kumeler (ayni karar, farkli yazim)"); for (const sinif of SINIFLAR) { for (const k of capKumeleri[sinif]) { if (k.length > 1) console.log(` ${sinif.padEnd(15)} ${k.join(", ")}`); } } // --- urun bazinda sapma ------------------------------------------------------- // Bir kumenin en cok kullanilan uyesi "ana yazim"; digerleri sapma sayilir. const sayac = {}; for (const sinif of SINIFLAR) { for (const kayit of envanter[sinif]) { const a = `${sinif}:${kayit.deger}`; sayac[a] = (sayac[a] || 0) + 1; } } const anaYazim = new Map(); for (const sinif of SINIFLAR) { for (const k of capKumeleri[sinif]) { const en = k.reduce((a, b) => ((sayac[`${sinif}:${b}`] || 0) > (sayac[`${sinif}:${a}`] || 0) ? b : a)); for (const uye of k) anaYazim.set(`${sinif}:${uye}`, en); } } console.log("\nurun kullanim ana yazimdan sapan sapma orani"); for (const urun of ["K", "Y", "U", "R"]) { let toplam = 0; let sapan = 0; for (const sinif of SINIFLAR) { for (const kayit of envanter[sinif].filter((k) => k.urun === urun)) { toplam++; if (anaYazim.get(`${sinif}:${kayit.deger}`) !== kayit.deger) sapan++; } } console.log( `${urun.padStart(4)} ${String(toplam).padStart(9)} ${String(sapan).padStart(19)} ${((100 * sapan) / toplam).toFixed(1).padStart(12)}%` ); }
ozellik kullanim ayri deger zincirleme kume cap sinirli kume aralik 45 13 4 8 yazi boyutu 22 9 5 7 kose yaricapi 14 5 2 3 kenar kalinligi 11 2 2 2 renk 43 23 15 15 toplam 135 52 28 35 karsiliksiz cesitlilik (cap sinirli): 17 deger, 32.7% cap sinirli kumeler (ayni karar, farkli yazim) aralik 2, 4 aralik 6, 8 aralik 10, 12 aralik 14, 16 aralik 18, 20 yazi boyutu 12, 13 yazi boyutu 14, 15 kose yaricapi 3, 4 kose yaricapi 6, 8 renk #ffffff, #fdfdfd renk #e9eaec, #dee9f7 renk #d5d8db, #d3d6d9 renk #b03526, #ab3321, #a83224 renk #2a5fa8, #275ea5 renk #22252a, #212327, #1f2226 urun kullanim ana yazimdan sapan sapma orani K 46 3 6.5% Y 37 20 54.1% U 31 6 19.4% R 21 5 23.8%
Bağlama Kuralı Sayıyı Belirler
Birinci tablonun iki küme sütunu aynı veriden iki farklı sonuç üretiyor: 28 ve 35. Fark küçük görünüyor ama tek bir satırda toplanıyor. Aralık sınıfında zincirleme bağlama 4 küme, çap sınırlı bağlama 8 küme buluyor.
Nedeni, zincirleme bağlamanın kümeye katılmak için tek bir yakın üye aramasıdır. Aralık değerleri 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 biçiminde ikişer ikişer ilerlediği için her değer bir öncekine yakındır ve zincir kopmaz. Sonuçta 2 ile 20 aynı kümeye düşer; oysa 2 piksellik bir boşlukla 20 piksellik bir boşluk aynı karar değildir.
Çap sınırlı bağlama kümeye katılmak için kümedeki her üyeye yakın olmayı şart koşar. Bu, kümenin genişliğini eşikle sınırlar ve zinciri kırar. Aynı veride 2 ile 4 aynı kümede kalır, 6 yeni bir küme açar.
Bu ayrım denetim raporunun güvenilirliğini doğrudan etkiler. Zincirleme sayı, yakın-yineleme sorununu olduğundan büyük gösterir: “on üç aralık değeri aslında dört karar” cümlesi denetimden çıkan en çarpıcı ama en yanlış cümledir. Doğru sayı sekizdir ve karşılıksız çeşitlilik toplamda 52 değerden 17’sidir, yani yüzde 32.7.
Kümeleme Öneri Üretir, Karar Vermez
İkinci blok kümelerin içeriğini gösteriyor ve burada ölçütün sınırı görünür hâle geliyor.
Renk kümelerinin çoğu doğru: #22252a, #212327 ve #1f2226 üç ürünün üç ayrı koyu metin
rengidir ve birbirinden ayırt edilemez. #b03526, #ab3321, #a83224 üçlüsü aynı biçimde
üç ayrı hata kırmızısıdır. #2a5fa8 ile #275ea5 iki ayrı birincil eylem mavisidir.
Bunların hepsi tek bir karar olmalıydı ve üç kez verildi.
Bir küme ise yanlış: #e9eaec ile #dee9f7. Birincisi nötr bir gri, ikincisi soluk bir
mavi zemin. Bağıl parlaklıkları birbirine yakın olduğu ve kanal farkı eşiğin altında kaldığı
için ölçüt bunları aynı sayıyor; oysa ikisi ayrı işler yapıyor — biri dolu raf hücresinin
zemini, diğeri seçili kaydın vurgusu. Ölçüt bunu bilemez, çünkü ölçüt yalnızca değere bakar,
değerin taşıdığı role bakmaz.
Buradan çıkan kural denetimin tamamı için geçerlidir: kümeleme bir öneri listesidir, bir karar listesi değil. Her küme insan tarafından okunur ve iki soru sorulur: bu değerler aynı işi mi yapıyor, ve birleştirilirlerse kaybolan bir ayrım var mı. Birinci soruya hayır yanıtı gelen küme bölünür; ikinci soruya evet yanıtı gelen küme kayda geçirilir.
Sapma Tek Yerde Toplanır
Üçüncü tablo denetimin en doğrudan eyleme dönüşen çıktısı. Her kümenin en çok kullanılan üyesi ana yazım kabul ediliyor; diğer üyeler sapma sayılıyor.
Sonuç eşit dağılmıyor. Katalog arayüzünün 46 kullanımından yalnız 3’ü sapma: yüzde 6.5. Yönetim panelinin 37 kullanımından 20’si sapma: yüzde 54.1. Üye profili yüzde 19.4, raf ekranı yüzde 23.8.
Bu dağılım kapsam ve sıralama kararını birlikte veriyor. Yönetim paneli tek başına toplam sapmanın büyük bölümünü taşıyor; sisteme geçişte önce onun düzeltilmesi, diğer üç üründen daha fazla kazanç sağlar. Aynı zamanda bir soru açıyor: yönetim paneli neden bu kadar uzakta? Yanıt genellikle ya ayrı bir ekipte yazılmış olmasıdır ya da başka bir zamanda — iki durumda da düzeltme yalnız değerleri değiştirmekle bitmez, o ekibin ya da o dönemin kararlarının neden farklılaştığının anlaşılmasını gerektirir.
Katalog arayüzünün düşük sapma oranı da ayrıca okunur: ana yazımlar zaten büyük ölçüde onun değerleridir, çünkü en çok kullanım oradadır. Bu, “en sık kullanılan değer doğrudur” varsayımının denetime gizlice girdiği yerdir. Varsayım her zaman doğru değildir; en sık kullanılan değer, en çok ekran yazılmış ürünün değeridir — kalitesinin değil, hacminin sonucudur.
Denetimin Teslim Ettiği Şey
Bir görsel denetim üç belge üretir ve üçü de sonraki adımların girdisidir.
Değer envanteri: her özellik sınıfı için kullanılan değerler, kullanım sayılarıyla birlikte. Ölçek kurulurken hangi değerin korunacağını bu sayılar belirler.
Küme raporu: yakın-yineleme kümeleri ve her küme için verilmiş insan kararı — birleştirilecek mi, bölünecek mi, kayda geçirilecek mi.
Sapma haritası: hangi ürünün ana yazımdan ne kadar uzakta olduğu. Geçiş sırasını ve maliyet tahminini bu harita verir.
Denetimin teslim etmediği şey ise ölçeğin kendisidir. Envanter neyin kullanıldığını söyler, neyin kullanılması gerektiğini söylemez. İkisi arasındaki geçiş ayrı bir karardır ve iki farklı yoldan yapılabilir.
Özet
- Görsel denetim, arayüzün fiilen kullandığı değerlerin biçem kaynağından makineyle sayılmasıdır; kaynağı bellek değil eserdir ve aradığı şey bilinmeyen tutarsızlıktır.
- Yakın-yineleme ölçütü sınıfa göre değişir: uzunluklarda piksel farkı, renkte hem kontrast oranı hem en büyük kanal farkı birlikte kullanılır.
- Bağlama kuralı sonucu belirler: zincirleme bağlama 13 aralık değerini 4 kümede toplar ve yakın-yinelemeyi abartır; çap sınırlı bağlama 8 küme bulur.
- Ölçülen dört üründe 52 ayrı değerin 17’si karşılıksız çeşitliliktir; yüzde 32.7’lik bu pay bedelsiz kapatılabilir.
- Kümeleme öneri üretir; her küme rolüyle birlikte okunur, çünkü ölçüt değere bakar, değerin işine bakmaz.
- Sapma haritası geçiş sırasını verir: dört üründen biri toplam sapmanın büyük bölümünü taşıyorsa geçiş oradan başlar.
Sonraki Adım
Envanter elde olduğunda iki yol açılır. Birinci yol, ölçeği ilkelerden kurup mevcut değerleri ona oturtmaktır; ikinci yol, mevcut değerlerin en sık kullanılanlarını ölçek ilan edip oradan devam etmektir. İki yolun geçiş maliyeti de, ürettiği ölçeğin kalitesi de farklıdır ve ikisi de hesaplanabilir. Sonraki ders bu iki stratejiyi aynı envanter üzerinde uygular; kaç değerin gözle görülür biçimde değişeceğini, kaç bileşenin dokunulacağını ve türetilen ölçeklerin Aralık Ölçeği dersindeki ölçütleri geçip geçmediğini karşılaştırır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.