Giriş
WebGPU, modern tarayıcılarda GPU gücünden standart ve güvenli bir şekilde yararlanmayı sağlayan yeni bir web standardı. Bu yazıda, ONNX Runtime Web ve WebGPU kullanarak tarayıcı içinde tamamen istemci tarafında çalışan bir görsel sınıflandırma uygulamasını adım adım kuracağız. Sunucuya veri göndermeden, gizliliğe saygılı ve düşük gecikmeli bir makine öğrenimi deneyimi elde edeceğiz. Bu yaklaşım, SEO açısından da kıymetli; zira “tarayıcıda makine öğrenimi”, “WebGPU”, “ONNX Runtime Web”, “görsel sınıflandırma” gibi anahtar kelimelerle teknik kitlelere doğrudan hitap ediyor.
Ön Koşullar
- Güncel bir tarayıcı: Chrome 113+ sürümlerinde WebGPU masaüstünde varsayılan olarak açıktır. Safari’de ve Firefox’ta destek durumu değişebilir; deneysel bayraklar gerekebilir. Mobilde destek kısmi durumdadır.
- Node.js 18+ (geliştirme sunucusu ve paket yönetimi için)
- Temel HTML/JS bilgisi
Proje Kurulumu
1) Vite ile boş bir proje oluşturun:
npm create vite@latest webgpu-ml -- --template vanilla
cd webgpu-ml
npm install
2) ONNX Runtime Web paketini ekleyin:
npm i onnxruntime-web
3) Basit bir dosya yapısı hazırlayın: index.html, main.js ve bir models/ klasörü. Örnek olarak mobilenetv2.onnx gibi 224x224 giriş alan bir sınıflandırma modelini models/ altına yerleştirin. ONNX model arşivlerinden (örn. ResNet, MobileNet) hafif bir model tercih edin.
index.html İskeleti
Aşağıdaki minimal HTML, bir görsel yükleme alanı ve sonuçları göstermek için basit bir arayüz içerir.
<!doctype html>
<html lang="tr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>WebGPU ile Görsel Sınıflandırma</title>
</head>
<body>
<h1>WebGPU + ONNX Runtime Web</h1>
<input type="file" id="file" accept="image/*" />
<button id="predict">Sınıflandır</button>
<div id="status">Hazır</div>
<div id="result"></div>
<canvas id="canvas" width="224" height="224" style="display:none"></canvas>
<script type="module" src="/main.js"></script>
</body>
</html>
WebGPU ve ONNX Runtime Web ile Çalıştırma
Aşağıdaki kodda, tarayıcıda WebGPU desteği kontrol edilir, ONNX modeli yüklenir, görsel 224x224 boyutuna ölçeklenir ve ImageNet ortalama/standart sapma değerleri ile normalize edilerek tensöre dönüştürülür. Ardından model çıkarımı yapılır ve en yüksek olasılıklı sınıf ekrana yazdırılır.
// main.js
import * as ort from 'onnxruntime-web/webgpu';
const statusEl = document.getElementById('status');
const resultEl = document.getElementById('result');
const fileInput = document.getElementById('file');
const predictBtn = document.getElementById('predict');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let session;
// WebGPU desteğini kontrol edin
if (!('gpu' in navigator)) {
statusEl.textContent = 'WebGPU desteklenmiyor. Varsayılan WASM ile deneyin.';
}
// Modeli başlat
async function initModel() {
statusEl.textContent = 'Model yükleniyor...';
// Fallback ile: önce WebGPU, olmazsa WASM
session = await ort.InferenceSession.create('/models/mobilenetv2.onnx', {
executionProviders: ['webgpu', 'wasm']
});
statusEl.textContent = 'Model yüklendi.';
}
function imageToTensor(img) {
const size = 224;
ctx.clearRect(0, 0, size, size);
// Görseli 224x224'e çiz
ctx.drawImage(img, 0, 0, size, size);
const { data } = ctx.getImageData(0, 0, size, size);
// NHWC(RGBA) -> NCHW, normalize [0,1], ImageNet mean/std
const mean = [0.485, 0.456, 0.406];
const std = [0.229, 0.224, 0.225];
const input = new Float32Array(1 * 3 * size * size);
let idx = 0;
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
const i = (y * size + x) * 4;
const r = data[i] / 255;
const g = data[i + 1] / 255;
const b = data[i + 2] / 255;
// Kanal sırası: [R plane][G plane][B plane]
input[0 * size * size + idx] = (r - mean[0]) / std[0];
input[1 * size * size + idx] = (g - mean[1]) / std[1];
input[2 * size * size + idx] = (b - mean[2]) / std[2];
idx++;
}
}
return new ort.Tensor('float32', input, [1, 3, size, size]);
}
function softmax(arr) {
const max = Math.max(...arr);
const exps = arr.map(v => Math.exp(v - max));
const sum = exps.reduce((a, b) => a + b, 0);
return exps.map(v => v / sum);
}
async function runInference(img) {
if (!session) await initModel();
statusEl.textContent = 'Çıkarım yapılıyor...';
const tensor = imageToTensor(img);
const inputName = session.inputNames[0];
const output = await session.run({ [inputName]: tensor });
const outputName = session.outputNames[0];
const logits = Array.from(output[outputName].data);
const probs = softmax(logits);
// En yüksek olasılıklı sınıfı bul
let bestIdx = 0;
for (let i = 1; i < probs.length; i++) {
if (probs[i] > probs[bestIdx]) bestIdx = i;
}
// Label dosyanız varsa fetch edip eşleyebilirsiniz (örn. imagenet-labels.txt)
resultEl.textContent = `Tahmin sınıf index: ${bestIdx}, olasılık: ${(probs[bestIdx] * 100).toFixed(2)}%`;
statusEl.textContent = 'Bitti';
}
// Görsel seç ve çıkarım başlat
predictBtn.addEventListener('click', async () => {
const file = fileInput.files?.[0];
if (!file) {
alert('Lütfen bir görsel seçin.');
return;
}
const img = new Image();
img.onload = () => runInference(img);
img.src = URL.createObjectURL(file);
});
// Sayfa yüklenince modeli hazırlayın (isteğe bağlı)
initModel().catch(err => {
console.error(err);
statusEl.textContent = 'Model yüklenirken hata oluştu.';
});
Performans İpuçları
- WebGPU genellikle WASM/WASM SIMD’e göre daha yüksek paralellik ve throughput sunar. Ancak küçük modellerde fark az olabilir. Model boyutu ve katman türleri sonucu etkiler.
- Modeli mümkün olduğunca küçük seçin (MobileNet, EfficientNet-Lite gibi). Büyük modeller hem ağdan indirmede hem de bellek kullanımında sorun çıkarabilir.
- İlk çıkarım (warm-up) genellikle daha yavaştır. Gerçek kullanıcı deneyimi için ikinci/üçüncü çıkarımı da ölçün.
- Vite yerine üretime alırken kodu küçültün ve model dosyasını CDN veya HTTP/2 ile hızlı sunun. Cache-Control başlıklarını doğru ayarlayın.
Sorun Giderme
- “WebGPU desteklenmiyor”: Tarayıcınızı güncelleyin veya chrome://flags içinde WebGPU ile ilgili bayrakları kontrol edin. Kurumsal politikalar GPU erişimini kısıtlayabilir.
- “Model giriş/çıkış adı uyuşmuyor” hatası: session.inputNames ve session.outputNames üzerinden gerçek isimleri alın, manuel isim yazmayın.
- Görselin renkleri veya sonuçlar beklenenden farklıysa, normalize adımlarını (mean/std) ve kanal sırasını (NCHW vs NHWC) model dokümantasyonuna göre doğrulayın.
- Bazı platformlarda WebGPU var ama belirli doku formatları kısıtlı olabilir. Fallback olarak WASM sağlayıcısını eklediğinizden emin olun.
Sonuç
WebGPU ve ONNX Runtime Web ile tarayıcıda makine öğrenimi artık gerçek bir seçenek. İstemci tarafında görsel sınıflandırma gibi görevlerde gizlilik, düşük gecikme ve çevrimdışı çalışma avantajları sunar. Bu rehberle temel bir iskelet kurdunuz; bundan sonra model çeşitliliğini artırabilir, etiket dosyaları ile daha anlaşılır sonuçlar gösterebilir, hatta Web Workers kullanarak arayüzü bloklamadan çoklu çıkarım senaryolarını deneyebilirsiniz. Kısacası, WebGPU sayesinde modern web uygulamaları, yapay zeka kabiliyetlerini kullanıcıya en yakın yerde, doğrudan tarayıcıda sunabiliyor.