Workbench
이미지 폴더 하나를 한 번의 호출로
torch.workbench 는 레시피이지 torch 의 이름이 아니다 — 그게 보이도록 한
단계 안쪽에 둔다. 폴더를 건네면 파일 이름에서 라벨을 읽고, 학습하고, 모델이 각 라벨을
얼마나 의심하는지로 점수를 매기고, ONNX 파일을 돌려준다. 이미지는 탭 밖으로 나가지
않는다. 마리모 노트북이 부르는 것이 이것이고,
두 노트북 어느 쪽에서든 직접 부를 수 있다.
내 폴더로 따라 하기
다섯 걸음이고, 어느 걸음에도 Python 은 필요 없다. 필요한 건 답을 이미 아는 이미지 폴더 하나다. 기본값인 동결 백본 경로는 수백 장이면 되고, 천 장이면 넉넉하다.
- 라벨이 앞에 오도록 파일 이름을 짓는다. 첫
_앞이 전부 라벨이다:ok_001.jpg,ng_002.jpg,crack_017.jpg. 폴더를 담은 zip 도 되고, 그때는 폴더 이름이 라벨이다. 이름의 나머지는 아무 상관 없다. - 페이지를 열고 10초쯤 기다린다. Python 과 노트북이 탭으로 내려받아지는 시간이다. 설치할 것도, 만들 계정도 없다.
- 폴더를 넣고 실행을 누른다(오른쪽 아래). 이미지는 탭 안에서 읽히고 탭에 남는다 — 아무것도 올라가지 않으며, 브라우저의 네트워크 패널에서 직접 확인할 수 있다.
- 검수 표를 위에서부터 읽는다. 내가 준 라벨과 모델이 얼마나 어긋나는지로 정렬돼 있다. 오라벨은 거기 있다: 라벨의 9 % 가 틀린 공개 데이터셋에서 첫 스무 줄의 3분의 2 가량이 오라벨이었다 — 아무 순서로 읽었다면 100 장에 9 장이다.
- 모델을 가져간다. 내보내기 버튼이 ONNX 파일을 쓴다. 휴대폰·서버·PC 에서 ONNX Runtime 이 읽는 형식이고, 그 뒤로는 borch 도 브라우저도 필요 없다.
위의 어느 것도 잘 된 실행의 스크린샷이 아니다: 같은 다섯 걸음을
tests/browser/marimo_probe.py 가 매일 밤 실제 GPU 에서 누르고, 그 검사는
연 탭에 GPU 가 없으면 아예 보고를 거부한다.
같은 것을 직접 부르면
코드로 쓰는 편이 낫다면, 위의 다섯 걸음은 네 줄이다. 이 사이트의 두 노트북 어느 쪽에서든, 그리고 어느 Pyodide 에서든 돈다.
import borch_webgpu as torch
s = torch.workbench.setup(files).fit() # 폴더로 학습
s.suspects() # 의심하는 라벨부터
s.onnx() # 모델을 들고 나간다
아래는 전부 그 네 줄에서 무엇을 바꿀 수 있는지, 그리고 검수 순서가 얼마나 믿을 만한지다. API 레퍼런스 →
한 번의 호출
import borch_webgpu as torch # 또는 borch_cpu — 철자가 같고, 장치가 없다
wb = torch.workbench
s = wb.setup(files, val=0.2) # 5분의 1을 떼어 두고 학습
print(s.accuracy, s.held_out) # 그리고 무엇 위에서 잰 값인지
s.suspects() # 의심하는 라벨부터
s.onnx() # 모델을 들고 나간다
그 앞의 질문 — 어느 백본이냐 — 에는 호출 둘이 답한다.
wb.compare(files, budget_mb=60) 는 예산에 드는 후보를 전부 학습해 판을
보여 주고, wb.pick(files, at_least=0.9) 는 기준을 넘는 것 중 가장 작은
것을 돌려준다. 둘 다 가를 수 없는 행을 표시한다: 홀드아웃이 백 개면 4점 차이
나는 두 백본은 같은 결과이고, wb.resolution(held_out) 이 얼마나 벌어져야
진짜 차이인지 말해 준다. 그걸 감추는 판은 데이터가 받쳐 주지 않는 결정을 부른다.
두 문에서 이름이 같다. borch_cpu 에는 nn 도 autograd 도 없어서
동결 백본 경로는 거기서 돌고, 처음부터 학습하는 CNN 과 ONNX 내보내기는 안 된다 —
표면에 "무슨 이름이냐"가 아니라 "무엇을 할 수 있느냐"를 묻고, 다른 경로를 원하는
호출자에게는 어느 문을 열어야 하는지 알려 준다.
무엇을 읽고, 무엇이 돌아오나
- 라벨은 파일 이름에서 온다. 첫
_앞부분이 라벨이다 —ok_014.png,ng_003.png. 클래스 폴더를 담은 zip 도 된다. 배치가 요구하기 전에는 아무것도 디코드하지 않으니, 수천 장이 배치 한 번 값이다. 아무것도 올리지 않으면 합성 3클래스가 대신 돈다. - 모델 경로는 둘. 동결한 ImageNet EfficientNet-B0 위에 선형 헤드 (이미지당 특징 벡터 하나를 한 번만 계산한다 — 수백 장이면 된다), 또는 모든 가중치가 학습하는 소형 CNN. 동결 쪽이 현장 학습자의 길이고 기본값이다.
- 마스크가 있으면 세그멘테이션이 된다.
images/와masks/가 같은 이름으로 든 zip 이면 U-Net 이 대신 학습하고, 검수 표는 받은 마스크와 모델 마스크를 어긋난 순서로 나란히 보여 준다. - 돌아오는 것은 어디서든 ONNX Runtime 이 읽는 ONNX 파일, 검수 순서, 그리고 보고서에 그대로 붙일 만큼 납작한 설정과 결과다. 설치도 업로드도 없다.
- 밑에서 일하는 호출은 라이브러리의 것이라 레시피 없이도 쓸 수 있다:
torch.decode_images가 파일을 이름에서 딴 라벨과 함께 NCHW 배치로 만들고,torch.ImageFiles는 그것을 필요할 때마다 하며,torch.suspects는 모델의 특징 공간에서 이웃 몇이 라벨과 어긋나는지로 점수를 매긴다 — 그 점수가 검수 순서다.
검수 순서는 믿을 만한가
- 검수 순서가 진짜 오라벨을 찾는가? CIFAR-10N 5만 장(사람이 실제로 붙인 라벨과 정답 라벨이 둘 다 있는 데이터)으로 바로 이 경로(동결 B0 pre-logits →
torch.suspects)를 쟀다: 라벨의 9%가 틀린 세트에서 큐의 첫 20장 중 65%가 오라벨(무작위면 9%), AUROC 0.948, 큐의 18%만 보면 오라벨의 90%를 잡는다. 40%가 틀린 세트에서는 첫 20장 전부가 오라벨. 브라우저 경로는 5,000장 부분집합에서 네이티브와 몇 점 차이(AUROC 0.929 대 0.917). 어려운 세트가 한계를 말해 준다: CIFAR-100N(미세 100클래스, 40% 오라벨)은 AUROC 0.842, 큐의 첫 100장 중 78%가 오라벨; Food-101N(웹 수집 음식, 18% 오라벨, 틀린 것은 비슷하게 생긴 다른 음식)은 AUROC 0.767, 첫 20장 중 65% — 무작위의 두 배이지 일곱 배는 아니다. 규모가 크면 이웃을 늘려야 한다: 5만 장에서 k=20이 CIFAR-100N 주입 10%의 AUROC를 0.913에서 0.962로 올렸고, 워크벤치는 2,000장부터 20을 쓴다. 도메인 세트 셋에 정답 라벨의 10%를 뒤집어 넣었을 때(k=20): NEU-CLS 강판 표면 결함은 AUROC 1.000에 큐의 첫 20장이 전부 오라벨, ISIC 2019 피부경 0.920, PatchCamelyon 조직 패치 0.918 — 검사 카메라류 사진은 동결 백본이 제자리이고, 조직 사진부터 부분 미세조정이 필요해진다. 수는 전부tests/review_eval.py에 있다. - 세그멘테이션도 같은 두 질문. Kvasir-SEG(내시경 1,000장, 용종 마스크, 96px)에 3단 U-Net을 올리고 학습 마스크의 1/5을 망가뜨렸다 — 다른 이미지의 마스크로 바꾸거나 용종에서 밀어냈다. 정답 마스크 대비 홀드아웃 IoU: 깨끗한 마스크로 학습하면 torch 0.55, 브라우저 0.51–0.57(하루 동안의 커널 버전에 따라); 마스크 1/5을 망가뜨리면 torch는 seed 여섯에서 0.40–0.50, 브라우저 0.46(오염 마스크 위 30에폭의 수치는 합산 순서에 따라 움직여 범위로 적는다). 800장×30에폭이 맥 GPU에서 34초(torch 20초) — 이틀 전엔 104초였고, GPU 타임스탬프로 재면서 커널을 하루 손본 결과다(BatchNorm 통계, conv 타일과 gather, 브로드캐스트 접기, cat, BatchNorm과 ReLU 짝짓기, 손실 함수 한 커널). 배치 16 한 스텝은 18 ms, torch는 12 ms. GPU가 실제로 쓰는 15 ms 중 9 ms가 conv — 채널이 작은 층은 직접 커널로 돌고, 가중치 그래디언트 GEMM이 가장 큰 항목이다. 마스크에는 투표할 이웃이 없어 큐는 모델 자신의 불일치(자기 마스크와 받은 마스크의 1−IoU)로 세운다: AUROC torch 0.88–0.93(seed별), 브라우저 0.91, 큐의 1/3쯤을 보면 틀린 마스크의 90%를 잡는다.
tests/seg_eval.py·tests/browser/seg_eval_py.py.
위의 숫자는 모두 매일 밤 도는 검사가 붙들고 있다 — 순서는 tests/review_eval.py
와 tests/seg_eval.py, 레시피의 불변식 39개는 tests/test_workbench.py,
실제 탭을 통과하는 전 구간은 tests/browser/marimo_probe.py 가 맡는다. 마지막
것은 그 탭에 GPU 가 없으면 아예 보고를 거부한다.