Javascript etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Javascript etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

27 Ekim 2025 Pazartesi

WebGPU ile Tarayıcıda Yapay Zeka: onnxruntime-web ile Hızlı Görüntü Sınıflandırma (Adım Adım Rehber)

WebGPU nedir ve neden önemli?

WebGPU, modern tarayıcılarda yerel GPU erişimi sunan yeni nesil bir web standardıdır. WebGL’in mirasını devralırken, daha düşük seviyeli ve verimli bir API sağlar. Bu sayede tarayıcı içinde yapay zeka modellerini, özellikle de tensör hesaplamalarını GPU üzerinde çalıştırarak ciddi performans artışları elde edebilirsiniz. Bu yazıda, WebGPU ve onnxruntime-web kullanarak basit bir görüntü sınıflandırma örneğini nasıl hayata geçirebileceğinizi adım adım anlatacağım. Amaç, “kodu kopyala-çalıştır” değil; mantığı kavrayıp kendi projenize uyarlayabilmeniz.

Destekleyen tarayıcılar ve ön koşullar

WebGPU, Chrome 113+ ve Edge gibi Chromium tabanlı tarayıcılarda genel olarak desteklenirken, bazı sistemlerde bayrak (flag) açmanız gerekebilir. Adres çubuğuna chrome://flags/#enable-unsafe-webgpu yazıp etkinleştirip tarayıcıyı yeniden başlatın. Donanım olarak modern bir entegre veya ayrık GPU, güncel sürücüler ve mümkünse 8 GB+ RAM önerilir. Ayrıca, proje dizininde basit bir HTTP sunucu (örneğin live-server veya vite) kullanmanız yerel dosya erişim sorunlarını önler.

Proje iskeleti ve kurulum

Temiz bir klasör oluşturun ve bir paket yöneticisiyle başlangıç yapın. Örnek: “npm init -y” ile bir Node projesi başlatın. Ardından “npm i onnxruntime-web” komutuyla tarayıcı sürümünü ekleyin. Statik dosyalar için index.html, model ve etiket dosyaları için “/public” klasörü, mantık kodu için main.js gibi basit bir yapı işinizi görür. Geliştirme sunucusu için “npx vite” gibi bir araç tercih edebilirsiniz; aksi durumda “npx http-server” da yeterli.

Model seçimi: ONNX ve boyut optimizasyonu

Tarayıcı için düşük gecikme ve sınırlı bellek kritik. Bu nedenle küçük ve verimli görüntü sınıflandırma modelleri (MobileNetV2/V3, EfficientNet-Lite) iyi başlangıç noktalarıdır. ONNX formatında bir model indirin veya dönüştürün. Eğer PyTorch veya TensorFlow modeliniz varsa, ONNX’a export edin. Ardından boyutu ve bellek kullanımını azaltmak için quantization uygulayın. Örnek komut akışı: “pip install onnxruntime-tools” ardından dinamik quantization ile “python -m onnxruntime.quantization.quantize_dynamic model.onnx model-int8.onnx”. Tarayıcıda indirme süresini kısaltmak için 8-bit tamsayı (INT8) genellikle iyi bir dengedir.

WebGPU ile oturum başlatma mantığı

onnxruntime-web, birden fazla çalışma sağlayıcısı (execution provider) sunar. Varsayılan WASM’tan maksimum hız beklemiyorsanız WebGPU sağlayıcısını açın. Temel akış şöyledir: 1) Tarayıcıda navigator.gpu mevcut mu kontrol edin. 2) onnxruntime-web’i import edin. 3) “InferenceSession.create(‘model.onnx’, { executionProviders: [‘webgpu’] })” ile oturumu başlatın. 4) Girdiyi (örneğin 224x224 RGB) ön işleyin: yeniden boyutlandırma, normalize etme (mean/std) ve tensör şeklini NHWC veya NCHW’ye uygun hazırlama. 5) “session.run(feeds)” ile çıkarım yapıp çıktıdan en yüksek olasılıklı sınıfı alın. Çıktıları etiket dosyanızla eşleştirerek kullanıcıya dostu bir sonuç sunun.

Veri ön işleme ve performans nüansları

Tarayıcı tarafında piksel işlemleri için OffscreenCanvas ve ImageBitmap kullanmak, ana iş parçacığını (main thread) bloklamadan dönüşüm yapmanıza yardım eder. Mümkünse işlemleri Web Worker içine taşıyın. Normalize (ör. 0-1 aralığına çekme) ve kanal düzeni dönüşümleri (HWC’den CHW’ye) pahalı olabilir; bu adımları GPU ile yapacak eklentiler henüz yaygın olmasa da, yapılabildiği durumlarda dramatik hızlanma sağlar. Ayrıca “warm-up” için ilk çağrıda boş bir tensörle veya düşük çözünürlüklü bir örnekle modeli çalıştırmak, JIT derlemelerini önden tetikleyerek ilk gecikmeyi azaltır.

Hafıza yönetimi ve kullanıcı deneyimi

Model dosyanız büyükse, indirme süresini iyileştirmek için HTTP/2, CDN ve gzip/br sıkıştırmayı aktif edin. Yükleme sırasında aşamalı geri bildirim (yüzde barı, “model yükleniyor”) sağlayın. Mobil cihazlarda bellek sınırlı olduğu için giriş çözünürlüğünü 160-192 gibi daha küçük değerlere düşürmeyi ve modelin INT8 sürümünü varsayılan yapmayı düşünebilirsiniz. Uzun süren işler için kullanıcıya iptal butonu vermek, UI’ın donduğu algısını ortadan kaldırır.

Geriye dönüş (fallback) stratejisi

Her cihaz WebGPU’yu desteklemeyebilir. Bu nedenle oturum açılışı başarısız olduğunda otomatik olarak WASM sağlayıcısına düşmek iyi bir uygulamadır. Mantık basit: “navigator.gpu” yoksa veya adapter alınamadıysa, “executionProviders: [‘wasm’]” ile yeniden dene. Kullanıcıya küçük bir uyarı vererek performansın sınırlı olabileceğini belirtin. Progressive enhancement yaklaşımıyla en iyi olanı kullanıp, geri kalanları zarifçe destekleyin.

Sık karşılaşılan hatalar

“navigator.gpu is undefined”: Tarayıcınız veya platformunuz WebGPU’yu desteklemiyor; bayrakları kontrol edin, tarayıcıyı güncelleyin veya Edge/Chrome deneyin. “Adapter not found” veya “RequestDevice failed”: Sürücüler eski olabilir, GPU erişimi politikalarla kısıtlanmış olabilir. “Out of memory”: Model boyutu büyük; INT8’e geçin, giriş çözünürlüğünü düşürün, arka planda çalışan sekmeleri kapatın. “CORS hataları”: Model dosyalarını aynı origin’den servis edin veya doğru CORS başlıklarını etkinleştirin.

Gizlilik, offline ve güvenlik boyutu

Tarayıcı içinde yerel çıkarım, verinin cihazi terk etmeden işlenmesini sağlar. Bu, özellikle sağlık veya kişisel görsellerin sınıflandırılması gibi senaryolarda mahremiyet avantajı sunar. Doğru Cache-Control ve Service Worker stratejileriyle model dosyalarını offline kullanılabilir hale getirebilir, ilk kurulumdan sonra çevrimdışı sınıflandırma deneyimi sunabilirsiniz. Güvenlik tarafında ise sadece güvenilir kaynaklardan model ve betik dosyaları alın; tedarik zinciri risklerini azaltmak için bütünlük (integrity) ve imza kontrollerini değerlendirin.

Sonuç ve ileri adımlar

WebGPU, tarayıcıda yapay zekayı pratik ve hızlı kılıyor. onnxruntime-web ile birkaç adımda GPU hızlandırmalı görüntü sınıflandırma çalıştırabilir, küçük cihazlarda dahi akıcı bir deneyim sağlayabilirsiniz. Bir sonraki adım olarak, metinle yönlendirilen görsel arama, gerçek zamanlı kamera akışı üzerinde nesne tespiti veya WebAssembly + WebGPU karma stratejileriyle daha Karmaşık modelleri denemeyi düşünebilirsiniz. Performansı daha da artırmak için model mimarilerini küçültmek, katman füzyonu uygulamak ve akıllı önbellekleme teknikleri kullanmak, projelerinizi üretime hazır hale getirir.

26 Ekim 2025 Pazar

Tarayıcıda Yapay Zeka: ONNX Runtime Web ve WebGPU ile YOLOv8 Nesne Tespiti (Adım Adım)

Giriş

Tarayıcıda çalışan yapay zeka uygulamaları son iki yılda WebGPU’nun yaygınlaşmasıyla ciddi ivme kazandı. Sunucu maliyeti olmadan, gecikmeyi düşürerek ve veriyi cihazda tutarak gerçek zamanlı deneyimler kurmak artık mümkün. Bu yazıda, ONNX Runtime Web ve WebGPU kullanarak tarayıcıda YOLOv8 tabanlı nesne tespiti nasıl çalıştırılır adım adım anlatıyorum. Amacımız, sade bir JavaScript projesinde model dosyasını yüklemek, WebGPU hızlandırmalı çıkarım yapmak ve olası performans tuzaklarını aşmak.

Neden Tarayıcıda Nesne Tespiti?

- Gizlilik: Görselleri veya kamera akışını sunucuya göndermeden cihazda işlemek, KVKK ve benzeri regülasyonlara uyumu kolaylaştırır.

- Düşük gecikme: Ağ gecikmesi ortadan kalktığı için özellikle gerçek zamanlı iş akışlarında (ör. kalite kontrol, interaktif demolar) deneyim belirgin şekilde iyileşir.

- Ölçeklenebilirlik: Her kullanıcı kendi cihaz kaynağını kullanır; sunucu GPU’su tahsis etme ihtiyacı azalır.

Gereksinimler

- Güncel bir Chromium tabanlı tarayıcı (Chrome/Edge 113+ sürümleri veya muadilleri) ya da WebGPU destekli Safari. Bazı eski sürümlerde deneysel bayraklar gerekebilir.

- Node.js ve bir paket yöneticisi (npm veya pnpm).

- onnxruntime-web paketi ve YOLOv8’in ONNX formatında bir sürümü (tercihen küçük bir varyant: yolov8n.onnx).

Proje Kurulumu

1) Hızlı bir iskelet için Vite ile başlangıç oluşturun: npm create vite@latest yolo-webgpu -- --template vanilla

2) Projeye girin ve bağımlılığı kurun: cd yolo-webgpu && npm i onnxruntime-web

3) Model dosyasını (ör. yolov8n.onnx) public/models/ klasörüne koyun. Geliştirme sırasında dosyaya /models/yolov8n.onnx yolu ile erişeceğiz.

4) Geliştirme sunucusunu başlatın: npm run dev. Modeli dosya sistemi üzerinden (file://) çağırmayın; tarayıcı CORS kısıtları nedeniyle engelleyebilir.

Modeli Hazırlama

Eğer hazır bir ONNX modeli kullanmıyorsanız, Ultralytics YOLOv8’i ONNX biçimine dışa aktarabilirsiniz. Tipik dışa aktarma, PyTorch modelinden .onnx üretir. Tarayıcı tarafı için küçük boyutlu (n veya s serisi) ve mümkünse nicemlenmiş (INT8) modeller performans ve bellek açısından avantaj sağlar. ONNX Runtime Web, QLinear tabanlı nicemlenmiş modelleri destekleyebilir; ancak operatör kapsaması modele göre değiştiğinden küçük bir varyantla başlamak mantıklıdır.

InferenceSession Oluşturma

Tarayıcı tarafında ONNX Runtime Web’i yükleyip WebGPU sağlayıcısıyla bir oturum açacağız. Güncel sürümlerde WebGPU kullanılabilir değilse otomatik olarak WASM arka ucuna düşecek şekilde bir dizi sağlayıcı tanımlamak iyi bir pratiktir:

import * as ort from 'onnxruntime-web';

const session = await ort.InferenceSession.create('/models/yolov8n.onnx', { executionProviders: ['webgpu', 'wasm'], graphOptimizationLevel: 'all' });

Bu yapılandırma, WebGPU uygunsa GPU hızlandırmayı kullanır; değilse WASM ile çalışmaya devam eder. WASM tarafında çok çekirdek ve SIMD faydası görmek için sayfanın Cross-Origin-Opener-Policy ve Cross-Origin-Embedder-Policy başlıklarıyla izole sunulması gerekir; Vite ve çoğu modern dev sunucu buna uygundur.

Ön İşleme ve Çıkarım

YOLOv8, girdi olarak tipik biçimde [1, 3, H, W] boyutunda, float32 normalleştirilmiş tensör bekler. Tarayıcıda bir <canvas> üzerinden görüntüyü 640×640’a yeniden boyutlandırıp RGB kanallarını alarak tensöre dönüştürebilirsiniz. Kanal sıralamasını (HWC → CHW) unutmayın ve 0–1 aralığına ölçekleyin.

Örnek çağrı akışı şu şekildedir: const feeds = { images: inputTensor }; ardından const output = await session.run(feeds);. Çoğu YOLOv8 ONNX dışa aktarımında çıktı, kutular ve sınıf skorlarını içeren tek bir tensördür. Bu tensörü NMS (Non-Maximum Suppression) ile filtreleyip skor ve sınıf eşikleri ile sonuçları çizmeniz gerekir.

Sonuçların Görselleştirilmesi

Nesne tespiti sonuçlarını çizmek için ikinci bir <canvas> katmanı kullanın. Her kutu için [x1, y1, x2, y2] koordinatlarını orijinal çözünürlüğe geri ölçekleyin. Sınıf adına göre renk paleti oluşturmak, kullanıcı deneyimini iyileştirir. Yüksek kare hızları elde etmek için çizimi requestAnimationFrame döngüsüyle eşleştirin ve her karede gereksiz bellek tahsisinden kaçının.

Performans İpuçları

- Küçük model seçin: yolov8n veya yolov8s web için daha uygundur.

- İlk çağrı ısınması: Uygulama açılışında küçük bir “boş” çıkarım tetikleyerek WebGPU shader derlemelerini önceden yaptırabilirsiniz; bu, ilk gerçek karedeki gecikmeyi düşürür.

- Nicemleme: INT8 veya dinamik nicemlenmiş modellerin boyutu küçülür ve bant genişliği ihtiyacı azalır. Doğruluk-hız dengesini ürün gereksinimlerinize göre ölçün.

- Asenkron iş akışı: Kamera karesini işlerken UI’ı kilitlememek için çıkarımı bir Web Worker içinde yürütmeyi değerlendirin. OffscreenCanvas desteklenen tarayıcılarda çizimi de iş parçacığına taşıyabilirsiniz.

Yayına Alma ve Güvenlik

Üretime alırken modeli bir CDN üzerinden cache-control başlıklarıyla servis etmek yükleme süresini iyileştirir. Model dosyalarınız büyükse gzip veya brotli sıkıştırmayı etkinleştirin. Gizlilik açısından, tüm çıkarımın cihazda kalması önemli bir artıdır; sunucuya yalnızca analitik metrikleri veya anonimleştirilmiş telemetri göndermek yeterli olabilir. Kamera erişimi gerekiyorsa kullanıcıya net bir izin akışı ve kapatma seçeneği sunun.

Sorun Giderme

- navigator.gpu undefined: Tarayıcı WebGPU’yu desteklemiyor olabilir. Sağlayıcı sırasına 'wasm' eklediğinizden emin olun.

- CORS/ERR_BLOCKED_BY_RESPONSE: Modeli file:// üzerinden değil bir HTTP sunucusundan yükleyin; yerelde Vite dev sunucusu yeterlidir.

- Out of memory: Modeli küçültün, çözünürlüğü düşürün (ör. 640 → 512) ve gereksiz kopyaları engellemek için tensörleri tekrar kullanın.

Sonuç

ONNX Runtime Web ve WebGPU kombinasyonu, tarayıcıda gerçek zamanlı nesne tespiti için pratik ve çağdaş bir yaklaşım sunuyor. Doğru model seçimi, düzenli ön işleme ve akıllı görselleştirme ile sunucu maliyeti olmadan yüksek kaliteli deneyimler oluşturabilirsiniz. Küçükten başlayın, performansı ölçerek iterasyon yapın ve kademeli olarak daha gelişmiş modellere geçin. Böylece hem kullanıcı gizliliğini korur hem de modern web’in sunduğu GPU gücünden en iyi şekilde yararlanmış olursunuz.

15 Ekim 2025 Çarşamba

WebGPU ile Tarayıcıda Nesne Tespiti: ONNX Runtime Web ile Adım Adım Kurulum

Giriş

Tarayıcıda makine öğrenmesi artık bir demodan öte; üretim senaryolarına hazır. WebGPU sayesinde GPU hızlandırmalı hesaplama, Chrome ve Edge gibi modern tarayıcılarda varsayılan olarak açıldı. Bu rehberde, ONNX Runtime Web (ORT Web) kullanarak tarayıcı içinde nesne tespiti (YOLO ailesi gibi) modelini çalıştırmayı ve WebGPU yürütme sağlayıcısı ile performansı nasıl artırabileceğinizi anlatıyorum. Kurulum adımlarını, küçük kod parçacıklarıyla ve pratik ipuçlarıyla beraber bulacaksınız.

Neden WebGPU?

WebGPU, WebGL’in ötesine geçerek daha modern bir grafik ve hesaplama API’si sunar. ML tarafında en kritik fark, tensör hesaplamalarını verimli çalıştırabilmesi ve veri kopyalamayı azaltmasıdır. Sonuç olarak, aynı model WebAssembly (WASM) ile 200-400 ms sürerken, WebGPU ile 60-120 ms aralığına inebilir; bu özellikle canlı kamera akışında kare kaçırmamak için belirleyici bir fark yaratır.

Ön Koşullar ve Tarayıcı Desteği

- Chrome 113+ ve Edge 113+ sürümlerinde WebGPU masaüstünde varsayılan olarak açık. Mac’te Apple Silicon ile daha istikrarlı sonuçlar alırsınız. Firefox’ta Nightly ile about:config altında dom.webgpu.enabled bayrağını etkinleştirmeniz gerekebilir. Safari’de ise Technology Preview önerilir.
- Geliştirme için basit bir statik sunucu kullanın: npx serve, python -m http.server veya VS Code Live Server eklentisi işinizi görür. Dosyaları doğrudan diskten açmak (file://) CORS hatalarına yol açar.

Proje Yapısı

Minimum örnek için şu dosyaları oluşturun: index.html, app.js ve model dosyanız (ör. yolov8n.onnx). Test için bir görüntü dosyası veya webcam akışı kullanabilirsiniz. Kamera ile test edecekseniz, HTTPS üzerinde yayınlamayı unutmayın; tarayıcılar güvenlik nedeniyle kamera erişimini HTTP’de kısıtlar.

ONNX Runtime Web’i Ekleme

CDN ile hızlı başlamak için index.html içine ORT Web’i ekleyebilirsiniz: <script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort-web.min.js"></script>. Alternatif olarak modüler kullanım için paket yöneticileriyle onnxruntime-web kurabilir, bundler ile projeye dahil edebilirsiniz. WebGPU hızlandırma için çalışma zamanı seçeneklerinde executionProviders: ['webgpu'] belirtmeniz yeterli.

Oturum Oluşturma ve WebGPU’yu Açma

Temel başlatma adımı şöyledir: const session = await ort.InferenceSession.create('yolov8n.onnx', { executionProviders: ['webgpu'], graphOptimizationLevel: 'all' });. Eğer cihazda WebGPU yoksa otomatik olarak WASM’a düşmek isteyebilirsiniz; o durumda önce WebGPU ile dene, başarısız olursa executionProviders: ['wasm'] ile yeniden oluştur şeklinde bir sarmalayıcı yazın.

Girdi Hazırlama

Nesne tespiti modelleri tipik olarak BxCxHxW float32 tensörü bekler. Tarayıcı tarafında Canvas üzerinden resmi alıp 640x640 gibi modele uygun boyuta yeniden örnekleyin, ardından ImageData piksellerinden Float32Array oluşturun. Kanal sıralaması (NCHW vs NHWC) ve normalizasyon (0-1 arası ölçek, ortalama-standart sapma) modelinize göre değişir; ONNX ihracını yaparken hangi düzen kullanıldıysa ona sadık kalın. Örnek: const input = new ort.Tensor('float32', floatArray, [1,3,640,640]);

Çıktıların İşlenmesi

YOLO tarzı modeller, her kutu için [x, y, w, h, score, class] veya benzeri bir yapı üretir. Post-processing adımları: skor eşiği uygulama (örn. 0.25), Non-Maximum Suppression ile çakışan kutuları eleme ve sınıf etiketleri ile renklendirme. ORT WebGPU bazı durumlarda çıktı tensörünü GPU belleğinde tutabilir; tarayıcı tarafında çizmek için CPU’ya kopyalamanız gerekir. Performans için tek bir canvas üzerinde hem görüntü hem de kutuları çizmek, her karede gereksiz tahsislerden kaçınmak önemlidir.

Performans İpuçları

- Model boyutu: YOLOv8n gibi küçük varyantlarla başlayın; tarayıcıda latency dramatik şekilde düşer.
- Precision: FP16 desteği olan cihazlarda otomatik kazanç sağlanır. ORT Web, WebGPU arka ucunda uygunsa bunu kullanır.
- İşletim birleştirme: graphOptimizationLevel: 'all' genellikle faydalı. Bazı modellerde ek kazanımlar için ONNX’ı optimize edin (Fusion/Constant folding).
- Batch=1: Akış senaryolarında tekli batch en düşük gecikmeyi verir.
- Sıcak çalışma: İlk çalıştırmadan önce bir kez “ısındırma” inference’ı yapın; shader derlemeleri ve önbellekler sonrası kare süreleri iyileşir.

Güvenlik ve Barındırma

Model dosyanız büyükse (ör. 20-50 MB), ilk yükleme süresi kullanıcı deneyimini etkiler. HTTP istemci önbelleği için uzun Cache-Control süreleri ayarlayın ve sürümleme yapın. Kamera kullanan sayfalar için HTTPS zorunlu. Cross-origin model yüklerken CORS başlıklarını doğru verin (Access-Control-Allow-Origin). Ayrıca, kullanıcı donanımlarında GPU kaynakları sınırlı olabilir; bir hata durumunda kibarca WASM’a düşmek, uygulamanın kırılmasını engeller.

Hata Ayıklama

- navigator.gpu yoksa tarayıcı sürümünüzü ve bayrakları kontrol edin.
- ValidationError görüyorsanız tensör şekilleri/hizalamaları uyuşmuyor olabilir. Girdiyi ve modelin beklediği düzeni tekrar gözden geçirin.
- Performance sekmesiyle CPU/GPU zamanlarını izleyin; çoğu darboğaz, görüntü ön-işlemede ortaya çıkar. Web Workers ile ön-işlemeyi ana iş parçacığından ayırmak sayfa akıcılığını artırır.

Sonuç

WebGPU ve ONNX Runtime Web birleşimi, tarayıcıda ML uygulamaları için yeni bir performans eşiği sunuyor. Sunucuya görüntü yüklemeden, gizlilik dostu ve düşük gecikmeli nesne tespiti mümkün. Bu rehberdeki yapı ile kısa sürede çalışan bir prototip çıkarabilir, ardından model optimizasyonu, önbellekleme ve UI cilasıyla üretim seviyesine taşıyabilirsiniz. Bir kez kurduktan sonra, segmentasyon, yüz mesh’i ya da metin tespiti gibi diğer ONNX modellerini de aynı altyapı üzerinde hızla deneyebilirsiniz.

22 Eylül 2025 Pazartesi

Passkey (WebAuthn) ile Şifresiz Giriş: Modern Web Uygulamalarına Adım Adım Entegrasyon

Passkey nedir ve neden şimdi?

Son yıllarda şifre sızıntıları, kimlik avı saldırıları ve kullanıcı deneyimi sorunları; geliştiricileri “şifresiz giriş” modellerine yöneltti. Passkey, FIDO2 ve WebAuthn standartlarını temel alarak kullanıcıların cihazlarında biyometrik (Face ID, Touch ID, Windows Hello) veya donanımsal güvenlik anahtarıyla güvenli, hızlı ve kimlik avına dayanıklı oturum açmalarını sağlar. Chrome, Safari, Edge ve Firefox’un güncel sürümleri ile iOS, Android ve Windows ekosistemleri passkey’i yerel olarak destekliyor.

Mimariye hızlı bakış

Passkey, sunucu (Relying Party) ve tarayıcı/cihaz (Authenticator) arasında challenge temelli bir akış kullanır. Sunucu, kayıt veya giriş için benzersiz bir challenge üretir; istemci tarafında navigator.credentials.create() (kayıt) veya navigator.credentials.get() (giriş) çağrısı yapılır ve oluşan kriptografik yanıt sunucuya geri gönderilir. Sunucu, bu yanıtı doğrular ve oturum açılır.

Önkoşullar ve en iyi uygulamalar

- Üretimde HTTPS zorunludur (localhost geliştirmede istisna).
- RP ID alan adınız olmalı (örn. example.com). Alt alan adılarında doğrulama için RP ID’yi üst alan adına ayarlayın.
- Kullanıcı kimlikleri ve cihazdan dönen credential ID’leri veritabanında güvenle saklanmalı.
- Kimlik avına dayanıklılık için cross-origin isteklerden kaçının; Origin ve Relying Party ID tutarlı olmalı.

Adım adım: Basit bir Node.js/Express ve WebAuthn entegrasyonu

Örnek, sunucuda @simplewebauthn/server kütüphanesini kullanır. İstemci tarafında ise tarayıcı API’lerini çağırıyoruz. Bu iskelet, tek başına üretime hazır değildir; amaç kavramı göstermektir.

1) Sunucuyu hazırlayın

// server.js
import express from 'express';
import cors from 'cors';
import session from 'express-session';
import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse,
} from '@simplewebauthn/server';

const app = express();
app.use(cors({ origin: 'https://app.example.com', credentials: true }));
app.use(express.json());
app.use(session({ secret: 'change-me', resave: false, saveUninitialized: true }));

// Demo bellek içi depolar
const users = new Map(); // userId -> { id, username, credentials: [] }

const rpID = 'example.com';
const rpName = 'Örnek Uygulama';

// Kayıt: seçenek üret
app.post('/webauthn/register/options', (req, res) => {
  const { username, userId } = req.body;
  let user = users.get(userId);
  if (!user) {
    user = { id: userId, username, credentials: [] };
    users.set(userId, user);
  }
  const options = generateRegistrationOptions({
    rpName,
    rpID,
    userID: user.id,
    userName: user.username,
    attestationType: 'none',
    excludeCredentials: user.credentials.map(c => ({ id: c.id, type: 'public-key' })),
    authenticatorSelection: { residentKey: 'preferred', userVerification: 'preferred' },
  });
  req.session.currentChallenge = options.challenge;
  res.json(options);
});

// Kayıt: doğrula
app.post('/webauthn/register/verify', async (req, res) => {
  const { userId, attResp } = req.body;
  const user = users.get(userId);
  const verification = await verifyRegistrationResponse({
    response: attResp,
    expectedChallenge: req.session.currentChallenge,
    expectedOrigin: 'https://app.example.com',
    expectedRPID: rpID,
  });
  if (verification.verified) {
    const { credentialPublicKey, credentialID, counter } = verification.registrationInfo;
    user.credentials.push({ id: credentialID, publicKey: credentialPublicKey, counter });
  }
  res.json({ ok: verification.verified });
});

// Giriş: seçenek üret
app.post('/webauthn/login/options', (req, res) => {
  const { userId } = req.body;
  const user = users.get(userId);
  const options = generateAuthenticationOptions({
    rpID,
    userVerification: 'preferred',
    allowCredentials: user ? user.credentials.map(c => ({ id: c.id, type: 'public-key' })) : [],
  });
  req.session.currentChallenge = options.challenge;
  res.json(options);
});

// Giriş: doğrula
app.post('/webauthn/login/verify', async (req, res) => {
  const { userId, authResp } = req.body;
  const user = users.get(userId);
  const dbCred = user.credentials.find(c => Buffer.compare(c.id, Buffer.from(authResp.rawId, 'base64url')) === 0);
  const verification = await verifyAuthenticationResponse({
    response: authResp,
    expectedChallenge: req.session.currentChallenge,
    expectedOrigin: 'https://app.example.com',
    expectedRPID: rpID,
    authenticator: dbCred,
  });
  if (verification.verified) {
    dbCred.counter = verification.authenticationInfo.newCounter;
    // Burada oturumu işaretleyin (req.session.userId = userId gibi)
  }
  res.json({ ok: verification.verified });
});

app.listen(3000, () => console.log('Server listening on http://localhost:3000'));

2) İstemci tarafı: Kayıt ve giriş akışı

// register.js (tarayıcı)
async function startRegistration(username, userId) {
  const opts = await fetch('/webauthn/register/options', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ username, userId })
  }).then(r => r.json());

  // ArrayBuffer alanlarını dönüştürün
  opts.challenge = base64urlToBuffer(opts.challenge);
  opts.user.id = new TextEncoder().encode(opts.user.id);

  const cred = await navigator.credentials.create({ publicKey: opts });
  const attResp = credentialToJSON(cred);
  const res = await fetch('/webauthn/register/verify', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ userId, attResp })
  }).then(r => r.json());
  return res.ok;
}

async function startLogin(userId) {
  const opts = await fetch('/webauthn/login/options', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ userId })
  }).then(r => r.json());

  opts.challenge = base64urlToBuffer(opts.challenge);
  opts.allowCredentials = (opts.allowCredentials || []).map(a => ({ ...a, id: base64urlToBuffer(a.id) }));

  const assertion = await navigator.credentials.get({ publicKey: opts });
  const authResp = credentialToJSON(assertion);
  const res = await fetch('/webauthn/login/verify', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ userId, authResp })
  }).then(r => r.json());
  return res.ok;
}

// Yardımcılar
function base64urlToBuffer(b64url) {
  const pad = '='.repeat((4 - (b64url.length % 4)) % 4);
  const b64 = (b64url + pad).replace(/-/g, '+').replace(/_/g, '/');
  const raw = atob(b64);
  const buf = new ArrayBuffer(raw.length);
  const view = new Uint8Array(buf);
  for (let i = 0; i < raw.length; ++i) view[i] = raw.charCodeAt(i);
  return buf;
}

function credentialToJSON(cred) {
  return JSON.parse(JSON.stringify(cred, (k, v) => v instanceof ArrayBuffer
    ? bufferToBase64url(v)
    : v));
}

function bufferToBase64url(buf) {
  const bytes = new Uint8Array(buf);
  let binary = '';
  for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
  return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/,'');
}

Güvenlik, UX ve üretim tavsiyeleri

- Kullanıcı adısız (usernameless) giriş için “resident key” tercih edin ve uygun akışı tasarlayın.
- Birden fazla passkey kaydına izin vererek cihaz kaybına karşı dayanıklılık sağlayın.
- Platform passkey (cihazda) ile çapraz platform passkey (iCloud Keychain, Google Password Manager) farklarını kullanıcılara açıklayın.
- Hata yönetimini özenle yapın: tarayıcıda NotAllowedError vb. hataları kullanıcı dostu mesajlara çevirin.
- Oturum yönetiminde kısa ömürlü JWT veya güvenli, HttpOnly, SameSite=strict cookie tercih edin.

Sorun giderme

- Origin ve RP ID uyuşmazlığı en sık hatadır: “https://app.example.com” ile rpID “example.com” olmalı.
- Sunucunuz ve istemci cihazınızın saat farkları challenge doğrulamasını bozabilir; NTP senkronizasyonunu sağlayın.
- Geliştirme aşamasında sadece localhost özel izni vardır; kendi alan adınızda test ederken mutlaka HTTPS kullanın.
- Kurumsal ortamlarda WebAuthn politikaları kısıtlanmış olabilir; tarayıcı ve OS politikalarını kontrol edin.

Sonuç

Passkey/WebAuthn, hem güvenlik hem de kullanıcı deneyimi tarafında anlamlı bir sıçrama sunuyor. Doğru RP ID, sağlam challenge yönetimi ve tutarlı origin politikasıyla kurulum basit hale geliyor. Üretime geçişte çoklu cihaz desteği, kullanıcı eğitimleri ve geri dönüş (fallback) stratejileri planlandığında, şifresiz geleceğe hazır bir kimlik doğrulama katmanı elde edebilirsiniz.

18 Eylül 2025 Perşembe

Tarayıcıda Yüksek Performanslı Görüntü İşleme: WebGPU ve WGSL ile Adım Adım Başlangıç Rehberi

WebGPU Nedir ve Neden Önemli?

WebGPU, tarayıcıda düşük seviyeli GPU erişimi sağlayan modern bir web standardıdır. WebGL’in yerini hedefleyen bu API, yalnızca çizim için değil, genel amaçlı hesaplama (compute) iş yükleri için de GPU gücünü kullanmanızı mümkün kılar. Bu sayede yüksek çözünürlüklü görüntü filtreleri, gerçek zamanlı video işleme veya makine öğrenmesi çıkarımı gibi ağır işleri tarayıcıda akıcı biçimde gerçekleştirebilirsiniz. Bu rehberde, WebGPU ve WGSL (WebGPU Shading Language) kullanarak basit bir görüntü filtresini nasıl kurgulayacağınızı, performans ve uyumluluk detaylarıyla anlatıyorum.

Önkoşullar ve Uyumluluk

WebGPU, Chrome 113+ ve Edge’de varsayılan olarak etkin, Safari’de 17+ sürümlerde geniş ölçüde destekleniyor. Firefox tarafında Nightly sürümünde bayrakla test edilebiliyor. Geliştirme sırasında sayfanızın HTTPS üzerinden servis edilmesi önerilir; yerelde localhost istisna kabul edilir. Uygulama başlangıcında if ('gpu' in navigator) ile özellik tespiti yaparak destek yoksa zarif bir geri dönüş (örneğin Canvas 2D veya WebGL) uygulayın.

Temel Akış: Adaptör, Cihaz ve Bağlam

İlk adımda navigator.gpu.requestAdapter() ile bir adaptör talep edilir; ardından adapter.requestDevice() ile GPU cihazı oluşturulur. Görüntü çıktınız bir <canvas> üzerindeyse, canvas.getContext('webgpu') çağrısı ile bir bağlam alınır ve uygun formatla (genellikle navigator.gpu.getPreferredCanvasFormat()) yapılandırılır. En iyi verim için cihaz ve bağlamı tek sefer oluşturup tüm etkileşimlerde yeniden kullanın.

Veri Temsili: Buffer ve Texture Seçimleri

Görüntü işleme söz konusu olduğunda veriyi çoğunlukla GPUTexture olarak tutmak gerekir. Statik resimleri ImageBitmap üzerinden yükleyip copyExternalImageToTexture ile GPU tarafına taşıyabilirsiniz. Filtre uygulanacak hedef için storage izinli bir kaplama (örneğin rgba8unorm formatında) tanımlayın. Parametreler (örneğin kernel boyutu, yoğunluk) için küçük bir GPUBuffer yeterli olacaktır. Farklı geçişlerde (compute vs render) aynı texture’ı hem storage hem texture-binding haklarıyla oluşturmayı planlayın.

WGSL ile Basit Bir Filtre Tasarlama

WGSL, WebGPU’nun yerel gölgelendirici dilidir. Örnek olarak gri tonlama filtresi düşünelim: Her piksel için RGB bileşenlerinin ağırlıklı ortalamasını alıp aynı değeri tüm kanallara yazarsınız. Compute yaklaşımında, bir @compute shader ile iş parçacıklarını 2B çalışma gruplarına böler, giriş kaplamasından örnek alır ve çıktıyı storage kaplamaya yazarsınız. Performans için @workgroup_size(8, 8) veya ekran kartınıza uygun başka bir boyut seçip test edin; farklı boyutlar, cihaz ve çözünürlüğe bağlı olarak ciddi fark yaratabilir.

Render Pass mı, Compute Pass mı?

Basit bir tonlama veya tek dokunuşlu efektlerde fragment shader’lı bir render pass yeterli olabilir; bir ekran dolgu üçgeni veya dörtgen ile giriş dokuyu örnekleyip sonuç doğrudan ekrana çıkarılır. Çok adımlı bulanıklaştırma (örneğin Gaussian blur), kenar algılama veya konvolüsyon temelli filtrelerde compute pass genellikle daha esnek ve performanslıdır. İki yaklaşımı da prototiplemek ve profil ile kıyaslamak iyi bir pratiktir.

Komut Akışı ve Kaynak Yönetimi

Her karede yeni bir komut kodlayıcı (commandEncoder) oluşturur, gerekli geçişleri kaydedip device.queue.submit() ile çalıştırırsınız. Maliyetli işlemler: tekrar tekrar buffer/texture oluşturmak, çok sayıda küçük submit yapmak ve gereksiz mapAsync çağrılarıdır. Bunları en aza indirin. Bind group’ları ve pipeline’ları mümkün olduğunca önceden oluşturup yeniden kullanın, yalnızca parametre değiştiğinde güncelleyin.

Performans İpuçları

- Yüksek çözünürlükte çalışmak yerine, görüntüyü ölçekli (örneğin 0.75x) işlemeyi deneyin ve sonuçta tekrar ölçekleyin; çoğu filtrede görsel kalite farkı az, hız kazancı yüksektir.

- Uygunsa rgba8unorm gibi 8 bit formatları tercih edin. Yarı hassas (16 bit float) gereksinimi yoksa daha hafif formatlar bellek bant genişliğini azaltır.

- Zaman ölçümü için CPU tarafında performance.now() kullanmak başlangıç için yeterli; daha ileri düzeyde cihazınız destekliyorsa timestamp-query özelliğiyle GPU süresi ölçebilirsiniz.

- Dizüstü bilgisayarlarda entegre ve ayrık GPU arasında fark büyük olabilir. requestAdapter sırasında powerPreference: 'high-performance' ipucunu verip sahada A/B testleri yapın.

Hata Ayıklama ve Geliştirici Deneyimi

WGSL hata mesajları başta göz korkutabilir. Shader’ları küçük, tek amaçlı fonksiyonlara bölmek ve her adımı ayrı çalıştırıp görselleştirmek (örneğin ara sonuçları ekrana basmak) sorun tespitini kolaylaştırır. Chrome’da chrome://gpu sayfası ve konsol uyarıları donanım/driver durumunu anlamaya yardımcı olur. Ayrıca pipeline oluştururken cihaz özelliklerini ve format desteklerini sorgulayıp koşullu yollar izlemek (örn. device.features) saha stabilitesini artırır.

Güvenlik ve Üretim Hazırlığı

WebGPU, tasarımı gereği güvenli; ancak yüksek frekansta veri aktarımı ve CORS kısıtları gibi web’in temel dinamikleri hala geçerli. Harici görsel yüklerken CORS başlıklarını doğru ayarlayın, kullanıcı verilerini işliyorsanız istemci tarafı GPU hesaplamalarının gizlilik etkilerini göz önünde bulundurun. Üretimde, hatalı cihazları belirlemek için basit bir “öz tanılama” ekranı (desteklenen formatlar, maksimum texture boyutu, başarısız pipeline sayısı) değerli geri bildirim sağlar.

Adım Adım Özet

1) Özellik tespiti yapın ve zarif bir geri dönüş belirleyin. 2) Adaptör ve cihazı oluşturun, canvas bağlamını yapılandırın. 3) Giriş görüntüsünü texture’a kopyalayın, çıktı için storage texture hazırlayın. 4) WGSL ile filtre shader’ını yazın (compute veya fragment). 5) Bind group/pipeline oluşturun, komut akışını kodlayın. 6) Performans ölçüp darboğazları azaltın. 7) Uyumluluk, hata ayıklama ve güvenlik kontrollerini ekleyin.

Sonuç

WebGPU, tarayıcıda görüntü işleme projelerini profesyonel sınıfa taşıyor. Doğru veri temsilini seçip WGSL ile minimal, okunabilir shader’lar yazdığınızda 4K çözünürlükte bile akıcı sonuçlar elde edebilirsiniz. İlk uygulamada basit bir gri tonlama veya kutu bulanıklaştırma ile başlayın; ardından çok geçişli Gaussian, kenar algılama ve renk uzayı dönüştürme gibi filtrelere genişletin. Elde edeceğiniz hız kazanımı ve taşınabilirlik, modern web uygulamalarınız için oyunu değiştirecek.

14 Eylül 2025 Pazar

WebGPU ile Tarayıcıda Yapay Zeka: ONNX Runtime Web ile Hızlı Görüntü Sınıflandırma Rehberi

Giriş

Tarayıcıda yapay zeka çalıştırmak artık bir deney olmaktan çıktı; WebGPU ve ONNX Runtime Web gibi teknolojiler sayesinde ciddi performans elde etmek mümkün. Bu rehberde, WebGPU hızlandırmasıyla bir ONNX görüntü sınıflandırma modelini tamamen tarayıcı içinde, ek bir sunucu tarafı altyapı olmadan nasıl koşturabileceğinizi adım adım anlatıyorum. Hedefimiz düşük gecikme, daha iyi gizlilik (veri cihazı terk etmez) ve çevrimdışı çalışma imkânı.

Neden WebGPU?

WebGPU, WebGL’in ötesine geçen, modern GPU mimarilerinden doğrudan faydalanmayı kolaylaştıran bir tarayıcı API’sidir. Hesaplama (compute) kabiliyetleri, daha düşük sürücü ek yükü ve daha öngörülebilir performans sunar. ONNX Runtime Web de bu yeteneklerden yararlanarak modeli GPU üzerinde çalıştırabilir; GPU desteklenmiyorsa otomatik olarak WASM (CPU) yoluna düşer.

Önkoşullar ve Ortam

• Tarayıcı: Chrome/Edge 113+ sürümlerinde WebGPU varsayılan olarak açıktır. Safari’de kısmi, Firefox’ta Nightly ve bayrak ile destek var. En güncel Chrome önerilir.
• Temel JS/HTML bilgisi ve statik bir sunucu (ör. npx serve veya Vite).
• Model dosyası: ONNX Model Zoo’dan MobileNetV2-7 küçük ve hızlı bir başlangıçtır. Alternatif olarak SqueezeNet veya EfficientNet-Lite tercih edilebilir.

Proje İskeleti

Basit bir index.html dosyasıyla başlayın. Aşağıdaki minimal iskelet, bir dosya girişi, bir tuş ve sonucu göstermek için alan içerir:

<!doctype html>
<html lang="tr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>WASM + WebGPU Sınıflandırma</title>
</head>
<body>
  <input id="file" type="file" accept="image/*">
  <button id="run">Çalıştır</button>
  <canvas id="canvas" width="224" height="224"></canvas>
  <div id="out"></div>
  <script type="module" src="./app.js"></script>
</body>
</html>

ONNX Runtime Web’i Entegre Etme

CDN üzerinden ORT’yi kullanabilirsiniz. app.js dosyasında ORT’yi içe alın ve WebGPU’yu tercih eden bir oturum oluşturun. WASM fallback’i yedekte kalsın:

import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js";

// WASM dosyalarının yolu (CDN veya yerel) - doğru ayarlayın
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/";
// Daha yüksek performans için çoklu iş parçacığı (COOP/COEP gerektirebilir)
// ort.env.wasm.numThreads = navigator.hardwareConcurrency || 4;

const preferGPU = !!navigator.gpu;
const session = await ort.InferenceSession.create("./models/mobilenetv2-7.onnx", {
  executionProviders: preferGPU ? ["webgpu", "wasm"] : ["wasm"],
  graphOptimizationLevel: "all"
});

Ön İşleme: Görseli Tensore Dönüştürme

MobileNet türü modeller genelde 224x224 giriş, RGB kanal sırası ve kanal başta (NCHW) düzeni bekler. Pixel değerlerini 0-1 aralığına getirip normalize etmek gerekir. Örnek bir dönüştürme:

async function imageToTensor(file) {
  const img = new Image();
  img.src = URL.createObjectURL(file);
  await img.decode();
  const canvas = document.getElementById("canvas");
  const ctx = canvas.getContext("2d");
  ctx.drawImage(img, 0, 0, 224, 224);
  const { data } = ctx.getImageData(0, 0, 224, 224);
  const chw = new Float32Array(1 * 3 * 224 * 224);
  const mean = [0.485, 0.456, 0.406];
  const std = [0.229, 0.224, 0.225];
  let p = 0;
  for (let y = 0; y < 224; y++) {
    for (let x = 0; x < 224; x++) {
      const i = (y * 224 + x) * 4;
      const r = data[i] / 255;
      const g = data[i + 1] / 255;
      const b = data[i + 2] / 255;
      // NCHW: önce tüm R, sonra G, sonra B kanalı
      chw[0 * 224 * 224 + p] = (r - mean[0]) / std[0];
      chw[1 * 224 * 224 + p] = (g - mean[1]) / std[1];
      chw[2 * 224 * 224 + p] = (b - mean[2]) / std[2];
      p++;
    }
  }
  return new ort.Tensor("float32", chw, [1, 3, 224, 224]);
}

Çalıştırma ve Sonuçları Yorumlama

Girdi tensörünü oluşturduktan sonra oturumu çalıştırın. Çoğu sınıflandırma modelinde çıktı tek bir tensördür; en büyük olasılığı seçip etikete dönüştürürsünüz. ImageNet etiketlerini küçük bir JSON dosyası olarak projeye ekleyebilirsiniz:

import labels from "./labels-imagenet.json" assert { type: "json" };

document.getElementById("run").addEventListener("click", async () => {
  const file = document.getElementById("file").files[0];
  if (!file) return;
  const input = await imageToTensor(file);
  const feeds = { input: input }; // Modelin gerçek girdi ismini kontrol edin
  const out = await session.run(feeds);
  const outputName = session.outputNames[0];
  const data = out[outputName].data;
  let idx = 0, max = -Infinity;
  for (let i = 0; i < data.length; i++) { if (data[i] > max) { max = data[i]; idx = i; } }
  document.getElementById("out").textContent = labels[idx] + " (" + max.toFixed(3) + ")";
});

Performans İpuçları

• İlk yüklemede bir “ısındırma” (warm-up) çağrısı yapın; modelin derlenmesi ve GPU kaynaklarının hazırlanması süre alabilir.
• WebGPU aktifleştirilmişse IOBinding ile giriş/çıkışları GPU üzerinde tutmak ek hız kazandırır (ileri kullanım).
• Modellerin quantized (INT8) sürümlerini tercih ederek dosya boyutunu ve gecikmeyi azaltın.
• Model dosyasını Brotli/Gzip ile sıkıştırıp uygun cache header’ları ayarlayın; tarayıcı önbelleğini ve Service Worker ile Cache Storage’ı kullanın.
• Görüntü ön işlemesini OffscreenCanvas veya Web Worker içinde yaparak ana iş parçacığını serbest bırakın.

Güvenlik ve Uyumluluk Notları

WASM tarafında çoklu iş parçacığı ve SharedArrayBuffer kullanmak istiyorsanız COOP/COEP başlıklarını etkinleştirmeniz gerekir. Bu mümkün değilse tek iş parçacığı da gayet iş görür; WebGPU varsa GPU hızlandırması zaten ciddi bir fark yaratır. Tarayıcı uyumluluğunda problem yaşıyorsanız, geçici olarak executionProviders: ["wasm"] ile çalışmaya devam edebilirsiniz.

Sonuç

Bu rehberle, yalnızca statik dosyalardan oluşan basit bir web projesinde ONNX Runtime Web ve WebGPU kullanarak gerçek zamanlıya yakın görüntü sınıflandırma kurdunuz. Avantajları açık: düşük gecikme, veri gizliliği ve dağıtım kolaylığı. Bir sonraki adım olarak kamera akışı (getUserMedia) ile canlı sınıflandırma, farklı model mimarileri (ResNet, EfficientNet, ViT), otomatik etiketleme veya yerleşik önbellekle çevrimdışı kullanım gibi özellikleri ekleyebilirsiniz. WebGPU ekosistemi hızla olgunlaşıyor; bugün attığınız adımlar, yarının tarayıcı tabanlı yapay zeka uygulamalarının temelini oluşturacak.

3.

13 Eylül 2025 Cumartesi

Passkey (WebAuthn) ile Şifresiz Giriş Entegrasyonu: Adım Adım Rehber

Passkey nedir ve neden önemli?

Geleneksel parolalar hem kullanıcı deneyimi açısından sorunlu hem de güvenlik zaafları nedeniyle verimsizdir. Passkey, FIDO2 ve WebAuthn standartlarına dayalı, phishing’e dayanıklı ve cihaz tabanlı bir kimlik doğrulama yöntemidir. Kullanıcılar artık “123456” gibi zayıf parolalarla uğraşmak yerine, platformlarının (iOS, Android, Windows, macOS) yerleşik biyometrik doğrulamasını veya donanım anahtarını kullanır. Bu yazıda, modern bir web uygulamasına passkey ile şifresiz girişin nasıl ekleneceğini, mimari prensipleri ve dikkat edilmesi gereken güvenlik detaylarını anlatıyorum.

Temel mimari ve kavramlar

WebAuthn iki temel akıştan oluşur: Kayıt (Registration) ve Giriş (Authentication). Kayıt sırasında tarayıcı bir anahtar çifti üretir ve sunucuya sadece public key ve credentialId gibi meta veriler iletilir. Girişte, sunucu tarafından üretilen tek kullanımlık challenge imzalanarak doğrulama yapılır.

Önemli alanlar: rpId (Relying Party ID, genellikle alan adınız), origin (tam köken, ör. https://app.siteniz.com), userHandle (kullanıcının uygulama içi benzersiz kimliği), signCount (anti-rollback sayacı). Her doğrulamada challenge rastgele üretilir ve tek seferliktir.

Önkoşullar ve teknoloji seçimi

Üretimde HTTPS zorunludur. Geliştirme için localhost istisnası vardır. Sunucu tarafında doğrulama için hazır kütüphaneler tercih edin: Node.js için SimpleWebAuthn, Go için duo-labs/webauthn, Java için WebAuthn4J, Python için webauthn. İstemci tarafında tarayıcı API’si navigator.credentials üzerinden erişilir. Veritabanında her kullanıcı için birden fazla credential saklamaya hazır olun (cihaz kaybı senaryosu).

Kayıt (Registration) akışı

1. Sunucu hazırlığı: Kullanıcı hesabı oluşturulduğunda veya “Passkey ekle” tıklandığında sunucu bir PublicKeyCredentialCreationOptions üretir. İçerik: rp (name, id), user (id, name, displayName), challenge (kriptopratik rastgele dizi), pubKeyCredParams (genelde -7 ES256), authenticatorSelection (ör. residentKey: "preferred", userVerification: "required"), attestation ("none" çoğu senaryo için yeterli).

2. İstemci isteği: Tarayıcıda navigator.credentials.create({ publicKey: options }) çağrılır. Kullanıcı, biyometrik veya PIN ile onaylar. Tarayıcı size attestation yanıtını döner.

3. Sunucu doğrulaması: İstemciden dönen cevabı sunucuda kütüphane ile doğrulayın. Origin ve rpId eşleşmeli, challenge doğru olmalı. Başarılıysa şu verileri saklayın: credentialId (base64url), publicKey, signCount, transports (ör. "internal", "usb", "ble"), userId. attestation politikanıza göre sertifika zinciri doğrulamasını atlayabilir veya sıkı modda açabilirsiniz.

Giriş (Authentication) akışı

1. Sunucu challenge üretimi: Giriş sayfasında kullanıcı e-posta veya kullanıcı adını girince sunucu PublicKeyCredentialRequestOptions döner: challenge, rpId, allowCredentials (ilgili credentialId listesi) ve userVerification ("required" önerilir).

2. İstemci doğrulaması: navigator.credentials.get({ publicKey: options }) çağrılır. Tarayıcı kullanıcıdan biyometrik onay ister ve assertion döner.

3. Sunucu doğrulaması: İmza ve authenticatorData kontrol edilir; rpIdHash ve origin eşleşmeli. signCount daha büyükse güncellenir, geriye düşüyorsa potansiyel klon/hatalı cihaz işareti olarak reddedilir. Başarılıysa güvenli bir session veya token üretin.

Kullanıcı deneyimi: Conditional UI ve otomatik doldurma

Conditional UI (Chromium) ile kullanıcı adını sormadan doğrudan mediation: "conditional" parametresiyle passkey önerisini tarayıcıda tetikleyebilirsiniz. iOS ve Safari tarafında sistem otomatik doldurma panelindeki “Anahtarlar” akışı devreye girer. Parola yerine “Devam etmek için cihazınızı doğrulayın” gibi net, güven veren metinler kullanın ve OTP/eposta doğrulamayı sadece kurtarma senaryosu olarak bırakın.

Güvenlik ve en iyi uygulamalar

- HTTPS zorunlu; Secure, HttpOnly, SameSite cookie bayraklarını doğru ayarlayın. CSRF için same-site veya token stratejisi uygulayın.

- rpId alan adınızla birebir ilişkili olmalı. Üretimde app.example.com kullanıyorsanız origin tam olarak bu olmalı; ters proxy kullanıyorsanız X-Forwarded-Proto/Host başlıklarını doğru iletin.

- Discoverable credentials (resident keys) açıldığında kullanıcı adı girmeden giriş mümkün olur. Ancak veritabanında kullanıcı başına çoklu credential yönetimini kurgulayın.

- Cihaz kaybı ve kurtarma: Kullanıcının birden fazla passkey ekleyebilmesini sağlayın (telefon, laptop, güvenlik anahtarı). Eski credential’ları listeden kaldırma (revoke) imkanı verin.

- Hata yönetimi: Timeout, NotAllowedError, InvalidStateError gibi hataları kullanıcı dostu mesajlarla ele alın. Geri dönüş yolunda asla parola istemeyin; kurtarma akışını ayrı doğrulamalarla çalıştırın.

Test, uyumluluk ve dağıtım

Geliştirme sırasında Chrome DevTools’taki Virtual Authenticator Environment ile farklı platformları simüle edin. iOS 17+, Android 14+, Windows Hello ve macOS/iCloud Keychain ile pratik testler yapın. Google Password Manager ve iCloud senkronizasyon farklarını (cihazlar arası passkey paylaşımı) göz önünde bulundurun.

Üretime geçerken alan adı stratejinizi belirleyin: rpId olarak kök alan adını (example.com) seçip alt alanlarda tutarlılık sağlayın. CDN ve WAF arkasında origin doğru raporlanıyor mu kontrol edin. İzleme için kayıt/giriş başarı oranı, kullanıcı başına credential sayısı, hataya düşen challenge sayısı gibi metrikler tutun.

Sonuç

Passkey (WebAuthn) ile şifresiz giriş, hem güvenliği yükselten hem de kullanıcı deneyimini sadeleştiren bir yaklaşım. Doğru rpId/origin eşlemesi, sıkı challenge doğrulaması ve sağlam bir oturum yönetimiyle phishing’e dirençli bir kimlik doğrulama katmanı elde edersiniz. Üstelik conditional UI ve discoverable credentials sayesinde “parola” kavramını kullanıcıdan tamamen gizleyerek tek dokunuşla giriş deneyimi sunabilirsiniz. Küçük bir POC ile başlayıp, çoklu credential ve kurtarma senaryolarını da kapsayacak şekilde adım adım genellemek en pragmatik yol olacaktır.

4 Eylül 2025 Perşembe

Passkey (WebAuthn) ile Parolasız Giriş: Node.js ve SimpleWebAuthn ile Adım Adım Entegrasyon

Passkey (WebAuthn) ile Parolasız Giriş: Neden ve Nasıl?

Parolasız giriş dünyasında Passkey teknolojisi (WebAuthn/FIDO2) hızla standart hâline geliyor. Kimlik avı direnci, cihazlar arası senkronizasyon ve hızlı kullanıcı deneyimi sayesinde klasik parolaların yerini alıyor. Bu yazıda, Node.js ve @simplewebauthn kütüphanesini kullanarak bir web uygulamasına Passkey tabanlı kimlik doğrulamayı nasıl entegre edeceğinizi adım adım anlatıyorum. Rehber, modern tarayıcılar (Chrome, Safari, Edge) ve platformlar (iOS, Android, macOS, Windows) ile uyumludur.

Ön Koşullar ve Genel Mimari

Başlamadan önce Node.js 18+ sürümü, HTTPS ile çalışan bir alan adı (localhost için self-signed sertifika), temel Express.js bilgisi ve veri saklamak için bir veritabanı (ör. PostgreSQL, MongoDB veya basit bir bellek deposu) gerekli. Mimari olarak iki akış vardır: Kayıt (Registration) ve Giriş (Authentication). Kayıtta sunucu, kullanıcıya bir challenge üretir ve tarayıcı navigator.credentials.create() ile güvenli bir anahtar çifti oluşturur. Girişte ise sunucu yeni bir challenge üretir ve tarayıcı navigator.credentials.get() ile imza üretip doğrulatır.

Proje Kurulumu

mkdir passkey-demo && cd passkey-demo komutlarıyla klasörü oluşturun. Ardından npm init -y ve npm i express @simplewebauthn/server cors cookie-session komutlarını çalıştırın. Geliştirme için npm i -D typescript ts-node @types/express @types/cookie-session ekleyebilirsiniz. HTTPS için bir ters proxy ya da self-signed sertifika kullanın; WebAuthn çoğu senaryoda güvenli köken (https) ister.

Sunucu Tarafı: Temel Ayarlar

Express uygulamasında kök alan adınızı rpID olarak tanımlayın (ör. example.com). origin değeri tam protokol ve alan adını içermeli (ör. https://example.com). Kullanıcı oturumunda veya Redis gibi bir depoda challenge saklayın.

import express from 'express';
import { generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
import session from 'cookie-session';

const app = express();
app.use(express.json());
app.use(session({ name: 'sess', keys: ['secret'], maxAge: 600000 }));

const rpID = 'example.com';
const origin = 'https://example.com';

Kayıt Akışı (Registration)

1) Options uç noktası: Kullanıcı kayıt olurken sunucu challenge üretir ve istemciye gönderir. Kullanıcı tanımlayıcısını (user.id) kalıcı bir değerden oluşturun.

app.post('/register/options', async (req, res) => {
  const { username, displayName } = req.body;
  const userId = 'user-' + username; // Örnek amaçlı
  const options = await generateRegistrationOptions({
    rpName: 'Passkey Demo',
    rpID,
    userID: userId,
    userName: username,
    userDisplayName: displayName || username,
    attestationType: 'none',
  });
  req.session.challenge = options.challenge;
  res.json(options);
});

2) Doğrulama uç noktası: İstemci, navigator.credentials.create() sonucunu bu uç noktaya gönderir. Sunucu doğrular, credential’ı veritabanına kaydeder.

app.post('/register/verify', async (req, res) => {
  const body = req.body; // client response
  const expectedChallenge = req.session.challenge;
  const verification = await verifyRegistrationResponse({
    response: body,
    expectedChallenge,
    expectedOrigin: origin,
    expectedRPID: rpID,
  });
  if (!verification.verified) return res.status(400).json({ ok: false });
  // credential kaydet: id, publicKey, counter, transports
  res.json({ ok: true });
});

Giriş Akışı (Authentication)

1) Options uç noktası: Sunucu, kullanıcıya bağlı mevcut credential’lara göre allowCredentials ile bir challenge üretir.

app.post('/login/options', async (req, res) => {
  const { username } = req.body;
  const userCreds = await loadUserCredentials(username); // DB'den çekin
  const options = await generateAuthenticationOptions({
    rpID,
    allowCredentials: userCreds.map(c => ({ id: c.id, type: 'public-key' })),
  });
  req.session.challenge = options.challenge;
  res.json(options);
});

2) Doğrulama uç noktası: İstemciden gelen imzayı doğrulayın, sayaç değerini güncelleyin ve oturumu başlatın.

app.post('/login/verify', async (req, res) => {
  const body = req.body;
  const expectedChallenge = req.session.challenge;
  const user = await findUserByCredentialId(body.rawId);
  const verification = await verifyAuthenticationResponse({
    response: body,
    expectedChallenge,
    expectedOrigin: origin,
    expectedRPID: rpID,
    authenticator: user.authenticator, // publicKey & counter
  });
  if (!verification.verified) return res.status(401).json({ ok: false });
  // counter güncelle, session başlat
  res.json({ ok: true });
});

İstemci Tarafı: WebAuthn API Kullanımı

Kayıt sırasında sunucudan aldığınız PublicKeyCredentialCreationOptions nesnesini navigator.credentials.create() içine verin. Tarayıcı, platform anahtarı (ör. iCloud Anahtar Zinciri, Google Password Manager) veya güvenlik anahtarı (YubiKey) ile cihaz üzerinde anahtar üretir.

const opts = await fetch('/register/options', { method: 'POST', body: JSON.stringify({ username }) }).then(r => r.json());
const cred = await navigator.credentials.create({ publicKey: opts });
await fetch('/register/verify', { method: 'POST', body: toJSON(cred) });

Girişte navigator.credentials.get() çağrısı yapılır. Sunucunun sağladığı PublicKeyCredentialRequestOptions ile imza üretilir ve doğrulama uç noktasına gönderilir.

const opts = await fetch('/login/options', { method: 'POST', body: JSON.stringify({ username }) }).then(r => r.json());
const assertion = await navigator.credentials.get({ publicKey: opts });
await fetch('/login/verify', { method: 'POST', body: toJSON(assertion) });

Test, Hata Ayıklama ve Uyumluluk

Geliştirmede https zorunludur; aksi takdirde tarayıcı çağrıları reddedebilir. Mobil cihazlarda test için aynı ağda çalışan https bir endpoint kullanın veya tünelleme (ngrok, Cloudflare Tunnel) tercih edin. Safari’de rpID uyumsuzluğu sık görülür; alan adınız ile origin’iniz birebir eşleşsin. Hata mesajlarını ayrıntılarıyla log’layın; özellikle challenge uyuşmazlığı, origin hatası ve RP ID hataları en yaygın sorunlardır.

Güvenlik ve UX İpuçları

- Challenge değerlerini tek kullanımlık ve kısa ömürlü saklayın; oturum veya Redis idealdir.

- Kullanıcı başına birden fazla credential kaydına izin verin; cihaz değişimlerinde deneyimi iyileştirir.

- resident key ve user verification politikalarını ihtiyaca göre ayarlayın; güçlü güvenlik için required tercih edin.

- Geriye dönük uyumluluk için geçici olarak sihirli bağlantı (magic link) veya tek kullanımlık kodları sunabilirsiniz.

- Üretimde anahtar materyalini ve sayaç değerlerini güvenilir bir veritabanında şifreli saklayın; yedeklemeleri planlayın.

Sonuç

Passkey (WebAuthn/FIDO2) ile parolasız giriş, hem güvenliği hem de kullanıcı deneyimini ileriye taşır. Node.js ve @simplewebauthn ile kurulum birkaç uç nokta ve doğru yapılandırma ile tamamlanabilir. Doğru rpID/origin eşleşmesi, güvenli challenge yönetimi ve çoklu credential desteği ile modern, kimlik avına dayanıklı ve hızlı bir giriş akışı elde edersiniz. Ürününüz büyüdükçe; cihaz senkronizasyonu, kurtarma stratejileri ve kurumsal güvenlik anahtarları ile çözümü olgunlaştırabilirsiniz.

3 Eylül 2025 Çarşamba

Tarayıcıda WebGPU ile ONNX Runtime Web Kullanarak YOLOv8 Nesne Tanıma: Adım Adım Kurulum ve Performans İpuçları

Giriş

Tarayıcıda çalışan yapay zeka uygulamaları artık sadece demolarla sınırlı değil. WebGPU ve ONNX Runtime Web sayesinde, YOLOv8 gibi modern nesne tanıma modellerini doğrudan kullanıcıların cihazlarında, sunucuya ihtiyaç duymadan ve düşük gecikmeyle çalıştırmak mümkün hale geldi. Bu yazıda, YOLOv8 modelini ONNX formatına dönüştürüp WebGPU yürütücüsüyle tarayıcıda nasıl çalıştırabileceğinizi, performans odaklı pratik ipuçlarıyla adım adım anlatıyorum.

Neden WebGPU + ONNX Runtime Web?

WebGPU, modern GPU mimarilerine daha yakın bir API sunduğu için WebGL/Canvas tabanlı yaklaşımlara göre çok daha yüksek paralellik ve verimlilik sağlar. ONNX Runtime Web ise modelleri tarayıcıda WebGPU ya da WASM yürütücüleri ile çalıştırır. Bu ikili; hız, taşınabilirlik ve gizlilik avantajlarını aynı anda sunar: veriler tarayıcıdan çıkmaz, gecikme düşer, CDN üzerinden servis edilen statik dosyalarla ölçeklenebilir bir dağıtım sağlanır.

Ön Koşullar

Güncel bir Chromium tabanlı tarayıcı (Chrome/Edge 121+), WebGPU desteği açık bir ortam (HTTPS zorunlu), Node.js 18+ ve temel TypeScript/JavaScript bilgisi yeterli. Geliştirme için Vite ile hafif bir proje yapısı kuracağız.

Modeli Hazırlama (YOLOv8 → ONNX)

Ultralytics YOLOv8 modelini ONNX’e aktarmak için Python tarafında şu adımı izleyebilirsiniz: pip install ultralytics komutuyla paketi kurun ve ardından yolo export model=yolov8n.pt format=onnx opset=12 imgsz=640 komutunu çalıştırın. Bu işlem sonunda yolov8n.onnx dosyasını elde edersiniz. Alternatif olarak resmi depolardan hazır ONNX sürümlerini indirebilir ve doğrudan statik olarak sunabilirsiniz.

Projeyi Kurma (Vite + TypeScript)

Yeni bir proje başlatmak için şu komutu çalıştırın: npm create vite@latest onnx-webgpu -- --template vanilla-ts. Proje klasörüne geçip npm i onnxruntime-web komutuyla ONNX Runtime Web’i ekleyin. Modeli ve etiket dosyalarını public/models klasörüne kopyalayın. Geliştirme sunucusunu başlatmak için npm run dev yeterli olacaktır.

WebGPU Yürütücü ile İnferans

Tarayıcı tarafında WebGPU yürütücüsünü etkinleştirmek için modülü içeri aktarın: import 'onnxruntime-web/webgpu' ve ardından import { InferenceSession, Tensor } from 'onnxruntime-web'. Oturum oluştururken yürütücüyü belirtebilirsiniz: const session = await InferenceSession.create('/models/yolov8n.onnx', { executionProviders: ['webgpu'], graphOptimizationLevel: 'all' }). Eğer cihazda WebGPU uygun değilse bir yakalama bloğunda ['wasm'] ile geriye dönük destek sunmak mümkündür.

Görüntüyü tensöre dönüştürmek için bir <canvas> referansı yeterli. Görüntünün 640x640 boyutuna mektup kutusu (letterbox) ile ölçeklenmesi, ardından piksel verisinin Float32 tensöre [1, 3, H, W] düzeninde (RGB, kanal-öncelikli) aktarılması gerekir. Normalizasyon için genellikle 0–1 aralığı ve gerekiyorsa modelin beklediği ortalama/sapma değerleri kullanılır. Örnek akış: görüntüyü canvas’a çiz, getImageData ile veriyi al, Float32Array’e yaz ve new Tensor('float32', data, [1,3,640,640]) oluştur. Ardından await session.run({ images: inputTensor }) ile çıktıları alabilirsiniz.

YOLOv8 ONNX çıkışında genellikle [1, N, 84] benzeri bir matris döner (ilk 4 değer kutu, takip eden skorlar/sınıflar). Sonuçları yorumlarken skor eşiğini (örneğin 0.25) ve IoU tabanlı NMS adımını uygulayın. Basit bir döngü ile her satırı okuyup en yüksek sınıf skorunu, kutu koordinatlarını ölçekleyerek orijinal görüntü boyutuna geri projekte edebilirsiniz.

Performans ve Optimizasyon İpuçları

Isınma (warm-up): Uygulama açılışında bir kez sahte bir tensörle session.run yaparak JIT derleme/önbellek süreçlerini tetikleyin. Bu, ilk gerçek karedeki gecikmeyi ciddi ölçüde azaltır.

WASM geri dönüşü: WebGPU desteklenmeyen cihazlarda otomatik olarak WASM’a düşmek kullanıcı kapsamını artırır. executionProviders: ['webgpu', 'wasm'] sıralaması pratik bir stratejidir.

Kantizasyon ve hafif modeller: INT8 kantize edilmiş ONNX modelleri bellek ve bant genişliği tüketimini düşürür. Eğer doğruluk kabul edilebilir seviyede kalıyorsa yolov8n veya yolov8s gibi küçük türevleri tercih edin.

Web Worker / OffscreenCanvas: Ön işleme ve NMS’i Web Worker’a taşıyarak ana iş parçacığını serbest bırakabilir, kare atlamalarını azaltabilirsiniz. Destek varsa OffscreenCanvas ile piksel işlemlerini işçi tarafında yapın.

Toplu işleme ve kadans kontrolü: Canlı kamera akışında her kareyi çalıştırmak yerine zamanlayıcıyla kadansı sınırlayın (ör. 30 FPS yerine 15 FPS). Sabit görüntü yükleme senaryolarında, aynı anda birden fazla resmi işlemek yerine küçük partiler halinde ilerleyin.

Bellek yönetimi: Tekrar kullanılan tensör ve arabellekleri yeniden oluşturmak yerine mümkün olduğunca yeniden kullanın. Büyük Float32Array tahsislerinden kaçınmak çöp toplayıcı baskısını azaltır.

Sık Karşılaşılan Hatalar ve Çözümler

GPUUnavailableError: Tarayıcıda chrome://flags altında WebGPU kapalı olabilir veya sayfa HTTPS üzerinden servis edilmiyor olabilir. Yerel geliştirme için localhost genellikle güvenilir sayılır.

Model uyumsuzluğu: ONNX opset sürümü çok düşük/yüksek olduğunda düğümler çözümlenmeyebilir. Çoğu güncel senaryoda opset 12–13+ güvenlidir; export sırasında opseti açıkça belirtin.

Çapraz kaynak sorunları: Model dosyaları farklı bir origin’den yükleniyorsa CORS başlıklarını doğru ayarlayın. Üretimde model ve varlıkları aynı domain/CDN kökünden sunmak en pratik yoldur.

Sonuç

WebGPU ve ONNX Runtime Web ile YOLOv8’i tarayıcıda çalıştırmak, gerçek zamanlı nesne tanımayı istemci tarafında güvenli ve hızlı bir şekilde sunmanıza olanak verir. Doğru ön işleme, hafif model seçimi ve ısınma/optimizasyon teknikleri ile orta seviye cihazlarda bile akıcı deneyim elde edebilirsiniz. Bu yaklaşım; gizlilik, ölçeklenebilirlik ve kullanıcı deneyimi açısından modern web uygulamalarına güçlü bir kapı aralıyor. Birkaç küçük adımla siz de modelinizi WebGPU üzerinde hayata geçirebilirsiniz.

14 Kasım 2024 Perşembe

Javascript Test Sitesi - Enable JavaScript

 Bir sitede javascript çalıştırmayı denerken "javascript blocked" hatası alıyordum, kullandığım Chrome tarayıcısında birçok seçenek denesemde scriptleri çalıştırmayı ve web sitesini test edebilmeyi bir türlü başaramamıştım. Bu sorun bazı Google Extension'larından kaynaklanıyor olabilir. Sorunu farklı yöntemlerle denedim ve sonunda çözdüm. Sanırım bir extension tarafından kaynaklanıyordu ve tarayıcıyı kapatıp yeniden açıktan sonra ve, bazı kontrollerden sonra düzelmiş oldu. Bir çeşit bug gibidiydi.

Bu sorunla ilgileniyorsa enablejavascript isimli bir web sitesi keşfettim, sayfayı ziyaret ettiğinizde tarayıcı bazlı hangi adımları takip ederek javascripti etkinleştireceğinizi ve mevcut oturumunuzda javascriptin açık olup olmadığını vs. size söylüyor. İlgin ve hoşuma gitti.

Test Java Script


15 Temmuz 2023 Cumartesi

Javascript Geliyorum Bekle :)

Linkedin üzerinden Javascript eğitimine başladım. Birazda front-end tarafını merak ettiğim ve güncel teknolojileri öğrenmek istediğim için bakmak istiyorum aslında. Şimdilik ortamımdaki yazılım araç gereçlerini hazırladım ve çalışma dosyalarını bilgisayarıma yükledim. Bazı uygulamalar ve yaptığım kurulumlar aşağıdaki gibi:

Visual Studio Code Editor
Live Server
Prettier
ESLint
Node.js
Visual Studio Code Console - npm install
Visual Studio Code Settings - Editor: Format On Save (Aktif)