our_knp/pets-knp/pets.html

21 lines
14 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html><html lang="ru"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Кот или пёс — AltAI</title>
<style>
:root{color-scheme:dark;--bg:#111512;--panel:#1b211c;--line:#343e35;--ink:#f1f3e9;--muted:#a6b1a4;--lime:#d1f88a;--orange:#ffa875}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.55 system-ui,sans-serif}main{max-width:1260px;padding:32px 28px 56px;margin:auto}header{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:38px}.brand{display:flex;gap:10px;align-items:center;font-weight:750;letter-spacing:-.6px}.paw{width:38px;height:38px;color:var(--lime)}.chip{font:11px ui-monospace,monospace;border:1px solid var(--line);border-radius:30px;padding:7px 12px;color:var(--lime)}a{color:var(--muted);text-decoration:none}a:hover{color:var(--lime)}.hero{display:flex;justify-content:space-between;gap:30px;align-items:end;margin-bottom:28px}h1{font-size:clamp(40px,6vw,70px);line-height:1.04;letter-spacing:-3px;margin:0 0 16px;font-weight:650}h1 span{color:var(--lime)}.intro{max-width:660px;color:var(--muted);margin:0;font-size:17px}.metric{border-left:1px solid var(--line);padding-left:28px;flex-shrink:0}.metric strong{font-size:40px;letter-spacing:-2px;display:block;line-height:1.2}.metric small{color:var(--muted);font-size:12px}.grid{display:grid;grid-template-columns:1fr 1.05fr;gap:18px}.panel{background:var(--panel);border:1px solid var(--line);border-radius:22px;padding:22px}.eyebrow{font:11px ui-monospace,monospace;letter-spacing:1.6px;text-transform:uppercase;color:var(--muted);margin-bottom:18px}.upload{height:280px;border:1px dashed #56604d;border-radius:14px;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;cursor:pointer;background:#171c17}.upload.drag{border-color:var(--lime);background:#263321}.upload img{height:100%;width:100%;object-fit:contain}.placeholder{text-align:center;padding:20px;color:var(--muted)}.placeholder b{color:var(--ink);font-size:21px;font-weight:550;display:block;margin:8px}.placeholder svg{width:58px;height:58px;color:var(--lime)}input[type=file]{display:none}button{font:inherit;cursor:pointer;border:0;border-radius:11px;padding:12px 18px;background:var(--lime);color:#17200e;font-weight:650;transition:transform .15s}button:hover{transform:translateY(-1px)}button:disabled{opacity:.6;cursor:wait;transform:none}.actions{display:flex;gap:10px;margin-top:14px;align-items:center}.secondary{background:transparent;color:var(--ink);border:1px solid var(--line);font-size:14px}.hint{font-size:12px;color:var(--muted);margin:12px 0 0}.result{min-height:122px;display:flex;gap:18px;align-items:center}.animal{font-size:63px;line-height:1}.animal svg{width:64px;height:64px;display:block;color:var(--lime)}.answer{font-size:38px;letter-spacing:-1.6px;line-height:1.15;margin:0}.sub{color:var(--muted);font-size:13px;margin-top:8px;min-height:20px}.status{display:inline-block;color:var(--lime);font:11px ui-monospace,monospace;margin-top:6px}.views{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:18px;padding-top:20px;border-top:1px solid var(--line)}.views canvas,.views img{display:block;width:100%;aspect-ratio:1;object-fit:contain;background:#121712;border-radius:10px;image-rendering:pixelated}.views figcaption{font-size:12px;color:var(--muted);margin-top:8px}figure{margin:0}.signals{display:flex;gap:7px;flex-wrap:wrap;margin-top:15px}.signals span{background:#252e25;color:var(--muted);border-radius:6px;font:11px ui-monospace,monospace;padding:5px 8px}.gallery{margin-top:22px}.galleryhead{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:13px}.galleryhead h2{font-size:18px;letter-spacing:-.4px;margin:0}.galleryhead small{color:var(--muted);font-size:12px}.samples{display:grid;grid-template-columns:repeat(12,1fr);gap:9px}.sample{padding:0;aspect-ratio:1;background:#232c24;border:2px solid transparent;border-radius:9px;overflow:hidden;position:relative}.sample img{width:100%;height:100%;display:block;image-rendering:auto}.sample.active{border-color:var(--lime)}.sample:focus-visible{outline:2px solid var(--orange);outline-offset:3px}.notes{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:30px;color:var(--muted);font-size:13px}.notes b{color:var(--ink);display:block;margin-bottom:5px;font-size:14px}.loading .animal{animation:pulse 1s infinite}.error{color:var(--orange)}footer{border-top:1px solid var(--line);margin-top:30px;padding-top:16px;color:var(--muted);font:11px ui-monospace,monospace;display:flex;justify-content:space-between;gap:20px}@keyframes pulse{50%{opacity:.35}}@media(max-width:700px){main{padding:20px 16px 32px}header{margin-bottom:28px}.hero{display:block}.metric{border-left:0;padding:0;margin-top:20px;display:flex;gap:12px;align-items:center}.metric strong{font-size:30px}.grid{grid-template-columns:1fr}.upload{height:250px}.samples{grid-template-columns:repeat(6,1fr)}.notes{grid-template-columns:1fr;gap:14px}h1{letter-spacing:-1.8px}.panel{padding:18px}.galleryhead{display:block}.views{gap:12px}footer{flex-wrap:wrap}.brand{font-size:15px}.chip{font-size:9px;padding:6px 8px}.hero{margin-bottom:22px}}
</style><main>
<header><div class="brand"><svg class="paw" viewBox="0 0 40 40" fill="currentColor"><ellipse cx="20" cy="28" rx="11" ry="8"/><ellipse cx="8" cy="17" rx="4" ry="5" transform="rotate(-25 8 17)"/><ellipse cx="16" cy="10" rx="4" ry="5"/><ellipse cx="25" cy="10" rx="4" ry="5"/><ellipse cx="33" cy="17" rx="4" ry="5" transform="rotate(25 33 17)"/></svg>AltAI · Pet lab</div><span class="chip">● KNP × GPU</span></header>
<section class="hero"><div><h1>Кот? Пёс?<br><span>Спросим нейроны.</span></h1><p class="intro">Загрузите фото или выберите пример. Спайковые детекторы найдут признаки, а выходные нейроны KNP выберут свой класс.</p></div><div class="metric"><strong id="accuracy">—</strong><small id="metricnote">точность на отдельном<br>тесте CIFAR-10</small></div></section>
<section class="grid"><article class="panel"><div class="eyebrow">01 / Что показываем сети</div><div class="upload" id="drop" role="button" tabindex="0" aria-label="Выбрать фотографию"><div class="placeholder" id="placeholder"><svg viewBox="0 0 60 60" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="9" y="9" width="42" height="42" rx="9"/><circle cx="23" cy="23" r="4"/><path d="M10 44l12-12 10 8 8-13 11 17"/></svg><b>Киньте сюда кота. Или пса.</b>Нажмите или перетащите фото</div><img id="photo" alt="Выбранное изображение" hidden></div><input type="file" id="file" accept="image/png,image/jpeg,image/webp,image/bmp"><div class="actions"><button id="run" disabled>Распознать →</button><button class="secondary" id="choose">Другое фото</button></div><p class="hint">PNG, JPG, WebP · до 8 МБ и 16 Мп · лучше один зверь крупным планом</p></article>
<article class="panel" id="result"><div class="eyebrow">02 / Что ответила сеть</div><div class="result" aria-live="polite"><div class="animal" id="animal">✳</div><div><p class="answer" id="answer">Кто там?</p><div class="sub" id="subtitle">Выберите картинку — и запустим спайки.</div><span class="status" id="status">ОЖИДАЮ ИЗОБРАЖЕНИЕ</span></div></div><div class="views"><figure><img id="tiny" alt="Вход сети 32 на 32 пикселя"><figcaption>Так сеть видит фото · 32 × 32</figcaption></figure><figure><canvas id="heat" width="196" height="196" aria-label="Карта активности детекторов"></canvas><figcaption>Где сработали детекторы</figcaption></figure></div><div class="signals"><span id="spikes">— спайков</span><span id="timing">— мс</span><span id="output">выходы: —</span></div></article></section>
<section class="gallery"><div class="galleryhead"><h2>Проверим на примерах</h2><small>24 фото из отдельного test · без отбора удачных ответов</small></div><div class="samples" id="samples"></div><p class="hint">Выбор примера сразу запускает KNP. Правильный класс покажем вместе с ответом.</p></section>
<section class="notes"><div><b>Настоящие спайковые слои</b>Свёрточные AltAILIF-детекторы и обученный выход работают в KNP на GPU. Хост суммирует и нормализует спайки по областям. Обученный скрытый слой и выход тоже исполняются в KNP.</div><div><b>Маленькие фото — сложная задача</b>Модель училась на CIFAR-10, где фото всего 32 × 32. На ваших снимках качество может отличаться от тестовой оценки.</div><div><b>Только кот или пёс</b>Модель не проверяет, есть ли животное на фото. На людях, предметах и пустом фоне она тоже выберет один из двух классов.</div></section>
<footer><span>OFFLINE TRAINING / KNP CUDA INFERENCE<br>GoldenModel для этой модели пока не проверен</span><a href="http://10.9.8.230:8081/native_viz.html">Рисовать цифры ↗</a></footer></main>
<script>
const $=id=>document.getElementById(id);let current=null,truth=null,seq=0,busy=false;
const blank=document.createElement('canvas');blank.width=blank.height=32;$('tiny').src=blank.toDataURL();const ctx=$('heat').getContext('2d');ctx.fillStyle='#121712';ctx.fillRect(0,0,196,196);
function select(uri,label=null,id=null){seq++;current=uri;truth=label;$('photo').src=uri;$('photo').hidden=false;$('placeholder').hidden=true;$('run').disabled=busy;$('answer').textContent='Готовы?';$('subtitle').textContent='Нажмите «Распознать», чтобы запустить сеть.';$('animal').textContent='✳';$('status').textContent='ИЗОБРАЖЕНИЕ ВЫБРАНО';$('tiny').src=blank.toDataURL();ctx.clearRect(0,0,196,196);$('spikes').textContent='— спайков';$('timing').textContent='— мс';$('output').textContent='выходы: —';document.querySelectorAll('.sample').forEach(b=>b.classList.toggle('active',b.dataset.id===String(id)));}
async function run(){if(!current||busy)return;const mine=seq;busy=true;$('run').disabled=true;$('result').classList.add('loading');$('answer').textContent='Думаем…';$('subtitle').textContent='Пропускаем фото через спайковые слои KNP.';$('status').textContent='GPU · СЧИТАЕМ СПАЙКИ';try{const r=await fetch('/api/recognize',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({image:current})});const d=await r.json();if(!r.ok)throw Error(d.error);if(mine!==seq)return;$('answer').textContent=d.prediction?'Это пёс':'Это кот';$('animal').innerHTML=d.prediction?'<svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21c-2-16-16-5-13 12 1 8 7 7 12 1m31-13c2-16 16-5 13 12-1 8-7 7-12 1M16 26c0-21 32-21 32 0v16c0 21-32 21-32 0Z"/><path d="M24 31h1m14 0h1M27 40l5 4 5-4M32 44v5m-6 0c0 7 12 7 12 0"/></svg>':'<svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 29V10l16 10h8L52 10v19c9 23-2 29-20 29S3 52 12 29Z"/><path d="M22 35h2m16 0h2M28 43l4 3 4-3M32 46v5m-19-9-9-2m9 9-9 2m47-9 9-2m-9 9 9 2"/></svg>';$('subtitle').textContent=truth===null?'Ответ спайковых нейронов, не вероятность.':(d.prediction===truth?'Верно!':'Промах.')+' В тесте это '+(truth?'собака':'кошка')+'.';$('status').textContent=d.tie?'НИЧЬЯ · ВЫБРАН КЛАСС «КОТ»':'KNP CUDA · ГОТОВО';$('tiny').src=d.input;const max=Math.max(...d.heat,1);for(let y=0;y<14;y++)for(let x=0;x<14;x++){let v=d.heat[y*14+x]/max;ctx.fillStyle=`rgb(${Math.round(28+181*v)},${Math.round(38+210*v)},${Math.round(29+109*v)})`;ctx.fillRect(x*14,y*14,13,13)}$('spikes').textContent=d.detectorSpikes.toLocaleString('ru')+' спайков';$('timing').textContent=(d.milliseconds/1000).toFixed(2)+' с';$('output').textContent='кот '+d.outputs[0]+' · пёс '+d.outputs[1];}catch(e){if(mine===seq){$('answer').textContent='Не вышло';$('subtitle').textContent=e.message;$('status').textContent='ПОПРОБУЙТЕ ЕЩЁ РАЗ';}}finally{busy=false;$('run').disabled=!current;$('result').classList.remove('loading');}}
function upload(file){if(!file)return;if(file.size>8000000){$('subtitle').textContent='Файл больше 8 МБ';return}const reader=new FileReader();reader.onload=()=>select(reader.result);reader.readAsDataURL(file)}
$('run').onclick=run;$('choose').onclick=()=>$('file').click();$('drop').onclick=()=>$('file').click();$('drop').onkeydown=e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();$('file').click()}};$('file').onchange=e=>{upload(e.target.files[0]);e.target.value=''};for(const event of ['dragover','dragenter'])$('drop').addEventListener(event,e=>{e.preventDefault();$('drop').classList.add('drag')});for(const event of ['drop','dragleave'])$('drop').addEventListener(event,e=>{e.preventDefault();$('drop').classList.remove('drag');if(event==='drop')upload(e.dataTransfer.files[0])});
fetch('/api/info').then(r=>r.json()).then(d=>{$('accuracy').textContent=(100*d.test_accuracy).toFixed(1).replace('.',',')+'%';$('metricnote').innerHTML='на '+d.test_total.toLocaleString('ru')+' test-фото<br>CIFAR-10 · кошки / собаки'}).catch(()=>{$('metricnote').textContent='Метрики временно недоступны'});
fetch('/api/samples').then(r=>r.json()).then(rows=>{for(const s of rows){const b=document.createElement('button');b.className='sample';b.dataset.id=s.id;b.setAttribute('aria-label','Тестовое фото '+s.id);const im=document.createElement('img');im.src=s.image;im.alt='Пример из отдельного теста';b.append(im);b.onclick=()=>{if(busy)return;select(s.image,s.label,s.id);run()};$('samples').append(b)}}).catch(()=>{$('samples').textContent='Не удалось загрузить примеры'});
</script></html>