Skip to content

갈래별 예제 3종 + 하네스가 이유를 말하게 - #22

Merged
Sudo42b merged 18 commits into
mainfrom
develop
Aug 21, 2026
Merged

갈래별 예제 3종 + 하네스가 이유를 말하게#22
Sudo42b merged 18 commits into
mainfrom
develop

Conversation

@201815054

@201815054 201815054 commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

무엇

#21 뒤에 얹힌 것. 4파일이다. 전부 다른 부서가 이 저장소를 받아 쓸 때 걸리는 것들이다.

갈래별 재현 예제 3종 (a0f776c)

example_yolo.sh 하나뿐이라 자기 모델을 얹으려는 사람이 따라갈 길이 없었다.
컴파일러가 받아주는 네 갈래를 하나씩 채웠고, 넷 다 실제로 돌려서 썼다.

스크립트 모델 출처 실측
example_yolo.sh ultralytics 박스 3개 (README 좌표와 일치)
example_torchvision.sh torchvision weights='DEFAULT' 상대 L1 1.24e-03 · argmax 일치
example_mmdet.sh MMDetection 체크포인트 0.27px · 개수차 0
example_custom.sh 직접 정의한 nn.Module 상대 L1 4.59e-04

가중치는 전부 실제 학습된 것이다. custom 예제도 난수를 한 값도 안 쓴다 — head 의 1×1 과
BN 을 사전학습 ResNet18 layer2 에서 잘라 채운다. 랜덤 초기화는 항등 초기값(γ=1·β=0)이
빠진 연산을 덮어 검증을 통과시킨다.

돌려서 결함 넷을 잡았다. 읽어서는 하나도 안 나온다:

  1. 생성 .pyrun() 이 없다 — 진입점은 nn.run_gguf(Model(), gguf, 경로, x)
  2. MyNet 이름 충돌 — 내 클래스와 g2c 생성 클래스가 같은 이름이라 뒤가 앞을 덮는다
  3. BN 채널 128 vs 64 — load_state_dict(strict=False) 로도 size mismatch 는 안 막힌다
  4. mmdet 예제만 mmdet 설치 인터프리터가 필요하다. python3 를 그냥 쓰면
    No module named 'yaml' 로 죽어 진짜 원인이 안 보인다 → 시작 전에 확인하고 말한다

하네스가 이유를 말하게 (951708f · b5a5926)

ABI 불일치 — 기존 프리플라이트는 libvisioncpp.so존재하는지만 봤다. 실제로
태운 경우는 파일이 멀쩡히 있으면서 ABI 만 다른 것이었다. 서브모듈 포인터를
upstream(64)으로 되돌린 뒤 재빌드를 안 해서 라이브러리는 128 인 채였고, 100계열이
전부 undefined reference … fixed_string<64ul> 로 죽었다. 47분.

tensor_name = fixed_string<GGML_MAX_NAME> 이 공개 API 시그니처에 있어 이 매크로는
사실상 ABI 버전이다. 이제 헤더와 nm 심볼을 대조해 먼저 멈춘다.

⚠️ 그 검사를 처음엔 fixed_string<N> 전체로 셌더니 오탐했다 — 예외 메시지 타입이
fixed_string<128> 로 따로 있고 GGML_MAX_NAME 과 무관하다. compute_graph_output
한 함수의 시그니처로 좁혔다.

with_reg=Falsegrid_rcnn'NoneType' object has no attribute 'numpy' 로 죽어
원인이 안 보였다. 실제로는 회귀 갈래가 없는 head 라 bbox_pred 자리가 None 이다.
이제 사유와 갈 곳(two-stage 하네스)을 같이 말한다.

🤖 Generated with Claude Code

eunchae added 5 commits August 20, 2026 12:34
문서의 계열 표를 사람이 옮겨 적어서 두 가지가 반복해 어긋났다.

① 낡는다. swin·grid_rcnn·detectors·seesaw_loss·guided_anchoring·cascade_rpn
   여섯이 이미 열린 뒤에도 문서는 「남은 실패」에 그대로 뒀다. 그걸 믿고
   "남은 벽"을 보고했다 — 문서를 근거로 문서를 고치면 낡은 판정이 되살아난다.
② 합계가 표와 안 맞는다. 본문 41+38 vs 표 40+35. 총합 86이 우연히 맞아떨어져
   아무도 안 봤다.

이제 results.json 이 정본이고 표는 생성물이다. 합계도 같은 자리에서 찍어
본문과 표가 갈릴 수 없게 했다. 판정 문자열은 그대로 옮긴다 — UNSUPPORTED 를
'예정'으로 부드럽게 만들면 이 파일이 또 하나의 손문서가 된다.
`example_yolo.sh`(ultralytics) 하나뿐이라 다른 부서가 자기 모델을 얹을 때
따라갈 길이 없었다. 컴파일러가 받아주는 네 갈래를 하나씩 채운다.

  example_yolo.sh          ultralytics   박스 3개 (README 좌표와 일치)
  example_torchvision.sh   torchvision   상대 L1 1.24e-03 · argmax 107 일치
  example_mmdet.sh         mmdet         박스 0.27px · 점수 0.000 · 라벨 0 · 개수차 0
  example_custom.sh        직접 정의     상대 L1 4.59e-04

가중치는 전부 실제 학습된 것이다. custom 예제도 난수를 한 값도 안 쓴다 —
head 의 1x1 과 BN 을 사전학습 ResNet18 `layer2` 의 값에서 잘라 채운다.
랜덤 초기화는 항등 초기값(γ=1·β=0)이 빠진 연산을 덮어 검증을 통과시킨다.

**넷 다 실제로 돌려서 썼고, 돌려서 결함 넷을 잡았다** — 읽어서는 안 나오는 것들:

  ① 생성 .py 에 run() 이 없다. 진입점은 nn.run_gguf(Model(), gguf, 경로, x) 다
  ② MyNet 이름 충돌 — 내 클래스와 g2c 생성 클래스가 같은 이름이라 뒤가 앞을 덮는다
  ③ BN 채널 128 vs 64 — load_state_dict(strict=False) 로도 size mismatch 는 안 막힌다
  ④ mmdet 예제만 mmdet 설치 인터프리터가 필요하다. python3 를 그냥 쓰면
     "No module named 'yaml'" 로 죽어 진짜 원인이 안 보인다 → 시작 전에 확인하고 말한다

sweep_boxes.py 도 함께 넣는다. one-stage 는 박스 축을 계열마다 손으로 재야 해서
**아무도 전수로 안 돌렸다** — 그래서 "박스까지 되는 계열이 몇이냐"에 측정된 답이 없었다.
gen 이 없는 계열은 NO_GEN 으로 남긴다. 조용히 건너뛰면 "전수" 가 거짓말이 된다.
기존 프리플라이트는 libvisioncpp.so 가 **존재하는지**만 봤다. 실제로 태운 경우는
파일이 멀쩡히 있으면서 **ABI 만 다른** 것이었다 — 서브모듈 포인터를 upstream(64) 으로
되돌린 뒤 재빌드를 안 해서 라이브러리는 128 인 채였고, 100계열이 전부
'undefined reference … fixed_string<64ul>' 로 죽었다. 47분을 태웠다.

tensor_name = fixed_string<GGML_MAX_NAME> 이 공개 API 시그니처에 있어 이 매크로는
사실상 ABI 버전이다(위키: 헤더와-라이브러리는-같은-트리여야-한다).

⚠️ 검사를 처음엔 fixed_string<N> 전체로 셌더니 **오탐했다** — 예외 메시지 타입이
   fixed_string<128> 로 따로 있고 GGML_MAX_NAME 과 무관하다. compute_graph_output
   한 함수의 시그니처로 좁혀 읽는다. 실측으로 (64, 64) 를 집는 것까지 확인했다.
grid_rcnn 이 REF_FAIL 로 죽는데 메시지가
  AttributeError: 'NoneType' object has no attribute 'numpy'
라 원인이 안 보였다. 실제로는 with_reg=False 인 head 라 bbox_pred 자리가
None 이다 — 좌표는 별도 grid_head 가 낸다.

기존 assert 는 **개수만** 셌다(쌍이 맞는지). 값이 None 인 경우는 통과시키고
다음 줄에서 죽는다. 이제 그 자리에서 사유와 **갈 곳**을 말한다:
two-stage 하네스(verify_postproc_roi.py)로 재라고 안내한다 —
그쪽에서는 grid_rcnn 이 0.15px 로 PASS 한다.

이 하네스가 못 재는 구조지 계열의 결함이 아니다
(위키: 하네스가-못-잰-것을-대상이-못-하는-것으로-적지-마라).
make_status_table.py · sweep_boxes.py — 둘 다 만들어놓고 한 번도 안 썼다.

make_status_table.py 는 'results.json 에서 표를 생성한다' 는 전제로 만들었는데
verify_heads.py 가 그 파일을 애초에 안 쓴다. 전제를 확인 안 하고 도구부터 만들었다.
sweep_boxes.py 도 같은 이유로 실행 경로가 없다.

문제의식(표를 손으로 옮기면 낡는다) 자체는 맞지만, 그건 도구가 아니라 절차로
푸는 게 맞다 — 안 쓰는 코드를 남기면 다음 사람이 '이건 뭐지' 로 시간을 쓴다.
@201815054 201815054 changed the title 예제 4종 + 하네스가 이유를 말하게 + 표 생성 갈래별 예제 3종 + 하네스가 이유를 말하게 Aug 20, 2026
@201815054

Copy link
Copy Markdown
Collaborator Author

머지 전에 알아둘 것 — 안 되는 것과 "정밀도 한계"

진짜 미해결 3계열

이 셋은 이유를 대고 멈춘다(UNSUPPORTED/EMPTY). 크래시도 조용한 오답도 아니다.

계열 막는 것
groie GenericRoIExtractor + GeneralizedAttention — 위치 임베딩 broadcast 가 5차원이라 ggml 로 안 펴진다 (렌더러)
sparse_rcnn · queryinst 배관은 끝났다. 같은 입력으로 torch 0.6738 인데 컴파일된 그래프가 0.5368(max diff 6.67), stage 0부터 갈린다. RoIAlign 피처·쿼리 적재·가중치·fp16·그래프 구조는 전부 무죄로 확인했다

정밀도 한계 — 버그가 아니다

dynamic_rcnn 10.24px · pafpn 8.77px · res2net 6.81px 는 라벨과 개수가 전부 맞고
좌표만 밀린다.
fp32 가중치로 다시 구우면 셋 다 0.00px 다.

GTX 는 FP16 네이티브라 fp16 이 배포 정밀도다. 이 숫자를 fp32 값으로 갈아 끼우지 마라 —
두 열을 따로 적는 게 맞다. double_heads(0.16px·개수차 1) · free_anchor(0.35px) ·
yolact(0.74px) 도 같은 부류의 경계 사례다.

숫자를 옮길 때

⚠️ 실패 개수를 그대로 옮기면 틀린다. one-stage 하네스의 실패 11건 중 5건은
하네스가 못 재는 것
이다(cascade_rpn·guided_anchoring·grid_rcnn·scnet·bytetrack).
같은 계열이 two-stage 하네스에서는 0.02~0.18px 로 통과한다.

A harness that cannot measure something is not the same as a model that cannot do it.

Codex added 8 commits August 20, 2026 17:10
grep 이 아니라 한 글자씩 읽어서 나온 것들이다.

① README.md:163 이 upstream(Acly/vision.cpp)을 클론시켰다. g2c-guide 는
   Sudo42b/vision.cpp 를 클론한다 — **같은 일을 서로 다른 저장소로** 시켰다.
   g2c 생성 arch 지원은 이 포크에만 있으므로 Sudo42b 로 맞췄다.

② README.md:54 의 vision-cli 예제에 **서브커맨드가 빠져 있었다**(-m 부터 시작).
   다른 모든 예제는 'vision-cli sam -m …' 이다. 그대로 치면 Missing command.

③ 릴리스 링크는 upstream 것이 맞지만 **그 바이너리엔 g2c arch 가 없다.**
   README·getting-started 양쪽에 그 사실을 적었다 — 컴파일한 모델을 쓰려면
   이 포크를 소스에서 빌드해야 한다.

④ model-implementation-guide 의 단계 목록이 1,2,4,5,6,7,8 이었다(3 이 없다).
   1~7 로 맞췄다.

⑤ 같은 문서의 '이름이 64자를 넘으면 줄여야 할 수도 있다' 한 줄을, 실제로 하루를
   태운 내용으로 바꿨다 — 실패가 로드 거부로 나온다는 것, 접미사가 아니라 prefix 를
   줄여야 한다는 것, 그리고 **가장 긴 가중치 이름을 세는 것과 완성된 이름을 검사하는
   것은 다른 일**이라는 것. 생성 경로는 tensor_names.py 가 이미 처리한다는 것도.

확인한 것(결함 없음): using-the-cli 의 옵션 8개·arch 6개가 --help 및 convert.py 와
일치, using-the-library 의 C++ API 7개와 Python 바인딩 시그니처가 실재, overview 의
README 앵커 8개 유효, getting-started 의 파일 경로·명령 목록 정확.
① cascade_rpn·guided_anchoring 이 아직 '거절한다' 로 적혀 있었다. 실측으로 둘 다
   통과한다(0.02px · 0.03px). RPN 은 비표준이 맞지만 **RoI head 는 표준**이라
   proposal 만 밖에서 받으면 나머지 경로를 잰다. fast_rcnn 의 고정 격자를 재사용하면
   안 된다는 것도 적었다 — 거긴 물어볼 RPN 이 아예 없고 이 둘은 있다.
   따라서 two-stage 통과가 35 → 37, 실패 그룹도 넷 → 셋(하나는 비었다).

② mmdet_wrap.py 를 .pt 옆에 쓴다는 서술이 틀렸다. mmdet_to_pt.py 에 복사 코드가 없다.
   모듈은 tools/frontend/mmdet/ 에 하나만 두고 PYTHONPATH 로 잡는다 —
   사본을 안 두는 이유(드리프트하면 모델 문제처럼 보인다)까지.

③ VISP_PRINT_DETS 의 기본값(10)이 표에 없었다. VISP_DRAW_THRESHOLD 는 0.3 로 맞다.

④ 'Models whose head survives tracing' 의 -i photo.jpg 가 클론에 없는 파일이라
   그대로 치면 죽는다. 저장소에 실재하는 이미지로.

⑤ '86계열' 이 어느 축인지 없었다. 박스 축(2px)이고, 디코드 전 텐서 축은 89/100 이다.
   둘은 서로의 정정이 아니며 같은 표에 놓으면 안 된다는 것을 명시.

대조로 확인한 것: 목차 앵커 10개, mmdet_to_pt 인자 4개, build_mmdet_cpp.sh 인자 3개,
환경변수 2개와 --build 옵션, one-stage 표 40행·two-stage 표 35행.
① 116행 'vision-cli depth-anything' → 'depthany'.
   ⚠️ 정정: 처음엔 "그대로 치면 죽는다"고 적었는데 틀렸다. cli.cpp:140 이
   `arg1 == "depthany" || arg1 == "depth-anything"` 로 둘 다 받는다 — 실행하면
   정상 진입한다. 안 도는 명령이 아니라 표기 불일치다. `--help` 와
   getting-started 가 쓰는 정식 이름이 depthany 라 그쪽으로 맞춘다.

② API 예제의 `void main()` → `int main()`. C++ 에서 void main 은 컴파일이 안 된다.

③ 박스 프롬프트 좌표가 그림 설명(650, 430)과 코드(650, 320)에서 어긋났다.
   그림이 기준이다.
vision.cpp 를 **단독으로 클론해** README 대로 빌드해보니 g2c 서브모듈일 때와
다르게 움직인다. 그 차이를 문서가 하나도 말하지 않았다.

(1) 'Tests (Optional) — Build with -DVISP_TESTS=ON' 이 거꾸로다.
    CMakeLists.txt:10 은 option(VISP_TESTS ... PROJECT_IS_TOP_LEVEL) 이라
    **단독 클론이면 이미 ON** 이다(CMakeCache 확인: VISP_TESTS:BOOL=ON).
    켜라는 안내는 이 문서를 읽는 사람에게 아무 일도 안 한다. 정작 필요한 건
    끄는 법인데 그게 없었다. 서브모듈일 때 OFF 라는 것도 같이 적었다.

(2) 그 기본값 때문에 configure 가 **모델 GGUF 를 180MB 받는다**
    (CMakeLists:159 ). 실측 182MB /
    5개 파일 / configure 63.1초. 회선이 느리거나 종량제면 그냥 맞는다.

(3) getting-started 2단계가 BiRefNet 을 curl 로 받으라 한다. 그런데 1단계 각주가
    권하는 '소스 빌드' 를 택했으면 그 파일은 이미 models/ 에 있다. 88MB 중복.

실습으로 확인: --help 목록이 문서와 일치, birefnet 5422.9ms(문서 예시 5372.6ms),
esrgan 16타일 53106.2ms — 세 출력 다 문서가 적은 대로 나왔다.
직전 커밋에서 '다른 프로젝트의 서브모듈로 빌드하면 꺼진다'고 적었는데 틀렸다.
g2c 가이드가 시키는 `cmake -S vision.cpp -B vision.cpp/build` 는 vision.cpp 를
**직접 최상위로** 잡으므로 그쪽에서도 켜진다 — 기존 클론 확인:
VISP_TESTS:BOOL=ON / models 182M. 꺼지는 건 부모 CMakeLists 가
add_subdirectory 로 끌어올 때뿐이다.

실측: VISP_TESTS=OFF 면 configure 63.1초 → 3.8초, 다운로드 182MB → 0.
처음 받은 사람 입장으로 7개 문서를 정독시켜 나온 것들. 전부 실물로 확인했다.

(1) using-the-library 'Going lower' 예제가 **복붙하면 컴파일이 안 된다.**
    in 을 선언하고 input_tensor 를 쓰고, out 을 선언하고 data 를 쓴다. 그런데
    이름 불일치보다 타입이 더 크다 — predict 는 tensor 를 받는데 process_input 은
    image_data 를 내고, process_output 은 span<float> 를 받는데 predict 는 tensor 를
    낸다. **백엔드 전송 두 번이 통째로 빠져 있었다.**
    지어내지 않고 src/visp/vision.cpp:108 의 실제 birefnet_compute 를 축약했고
    실제 헤더로 -fsyntax-only 를 돌려 통과시켰다. 세 블록으로 나누니 이 절이 왜
    있는지도 드러난다 — 그래프는 한 번 만들고 마지막 블록만 프레임마다 돈다.

(2) **컴파일러 저장소가 문서 어디에도 이름이 없다.** 'compiler checkout' 이 세 번
    나오는데 이름도 URL 도 없어서, vision.cpp 만 받은 사람은 그 경로에서 막힌다.
    README 는 한술 더 떠 docs/vision-cpp-mmdet-guide-en.md 를 가리키는데 그 파일은
    이 체크아웃에 없다. 세 곳 다 GTX_Compiler 로 이름 붙이고 링크했다.
    mmdet-detectors 2단계의 'a PyTorch-to-ggml model compiler' 도 g2c 라고 밝히고
    뒤쪽 whole-model 경로와 **같은 도구**임을 명시 — 리뷰가 걸린 지점이다.

(3) convert.py 이름 둘이 CLI 로 안 넘어간다. depth-anything → depthany 고,
    sam3 는 변환은 되는데 **CLI 서브커맨드가 없다**(cli.cpp 에 0회). 변환해놓고
    돌릴 방법을 못 찾게 된다. 둘 다 적었다.

(4) README 의 convert arch 목록이 'sam, birefnet, esrgan, ...' 였다. 실제 목록은
    6개로 짧고 확정적이다(convert.py:534 arch_names). 다 적었다.
image_save 는 stbi_write_png 하나뿐이고 확장자를 보지 않는다(image.cpp:206).
그래서 문서가 시키는 대로 `-o detected.jpg` 하면 **PNG 가 detected.jpg 라는
이름으로** 나온다. 실측:

    file /tmp/visp-example-yolo26m/detected.jpg
    → PNG image data, 512 x 512, 8-bit/color RGB

확장자를 믿는 뷰어는 열지 못한다. 문서 9곳과 example_yolo.sh 의 출력 이름을
.png 로 맞추고, using-the-cli 의 -o 설명에 한 번 못박았다.
[include/visp/ml.h](/include/visp/ml.h) 처럼 / 로 시작하는 링크가 8개 있었다.
GitHub 은 이걸 저장소 루트가 아니라 **사이트 루트**로 푼다 —
github.com/include/visp/ml.h 로 가서 404 다. 대상 8개는 전부 실재하므로
경로만 ../ 로 고쳤다. 두 저장소 11개 문서의 내부 링크를 전수 검사해
남은 깨진 링크는 0 이다.
@201815054

Copy link
Copy Markdown
Collaborator Author

문서 정독 + 단독 클론 실습

배포 전에 docs/ 7개 문서를 한 글자씩 읽고, 이 저장소를 단독으로 클론해 README 대로 빌드했다. 단독 클론은 이번이 처음이고, g2c 서브모듈일 때와 다르게 움직인다.

단독 클론에서만 드러난 것

cmake . -B build 한 줄이 180MB 를 조용히 받는다. CMakeLists.txt:10

option(VISP_TESTS "Build tests" ${PROJECT_IS_TOP_LEVEL})

이라 단독 클론이면 자동 ON 이고, CMakeLists:159if(VISP_TESTS OR VISP_INSTALL_MODELS) 가 모델 GGUF 5개를 내려받는다. 실측:

configure models/
기본 63.1초 182MB
-D VISP_TESTS=OFF 3.8초 0

README 의 테스트 설명이 거꾸로였다. Tests (Optional) — Build with -DVISP_TESTS=ON 은 켜는 법을 알려주는데, 이 문서를 읽는 사람은 방금 클론한 사람이고 그에겐 이미 켜져 있다(CMakeCache.txt: VISP_TESTS:BOOL=ON). 정작 필요한 끄는 법이 없었다.

처음엔 "서브모듈로 빌드하면 꺼진다"고 적었다가 실측에 반증당했다 — g2c 가이드가 시키는 cmake -S vision.cpp -B vision.cpp/build 도 최상위로 잡으므로 그쪽에서도 켜진다. 기준은 서브모듈이냐가 아니라 부모가 add_subdirectory 로 끌어오느냐다.

getting-started.md 2단계의 curl 이 중복이다. 1단계 각주가 권하는 소스 빌드로 온 사람은 BiRefNet 88MB 를 이미 갖고 있는데 다시 받으라고 한다.

정독으로 나온 것

using-the-library.md 의 "Going lower" 예제는 복붙하면 컴파일이 안 된다. 이름 불일치(in 선언 후 input_tensor 사용)보다 타입이 더 크다:

  • birefnet_predict(model_ref, **tensor**, …)process_input 이 낸 image_data 를 못 받는다
  • birefnet_process_output(**span<float const>**, …)predict 가 낸 tensor 를 못 받는다

백엔드 전송 두 번이 통째로 빠져 있었다. 지어내지 않고 src/visp/vision.cpp:108 의 실제 birefnet_compute 를 축약했고, 실제 헤더로 -fsyntax-only 를 돌려 통과시켰다. 세 블록으로 나누니 이 절이 왜 있는지도 드러난다 — 그래프는 한 번 만들고 마지막 블록만 프레임마다 돈다.

컴파일러 저장소가 문서 어디에도 이름이 없었다. "compiler checkout" 이 세 번 나오는데 이름도 URL 도 없다. README 는 한술 더 떠 docs/vision-cpp-mmdet-guide-en.md 를 가리키는데 그 파일은 이 체크아웃에 없다. 세 곳 다 GTX_Compiler 로 이름 붙이고 링크했다.

-o detected.jpg 로 쓰면 PNG 가 나온다. image.cpp:198image_savestbi_write_png 하나뿐이고 확장자를 안 본다:

$ file detected.jpg
PNG image data, 512 x 512, 8-bit/color RGB

문서 예제와 example_yolo.sh.png 로 맞추고 using-the-cli.md-o 설명에 못박았다.

convert.py 의 이름 둘이 CLI 로 안 넘어간다. depth-anythingdepthany 이고, sam3 는 변환은 되는데 CLI 서브커맨드가 없다(cli.cpp 에 0회). 변환해놓고 돌릴 방법을 못 찾게 된다.

절대경로 링크 8개가 GitHub 에서 깨진다. model-implementation-guide.md](/include/visp/ml.h) 는 저장소 루트가 아니라 사이트 루트로 풀린다. 대상 8개는 전부 실재하므로 ../ 로 고쳤다. 두 저장소 11개 문서의 내부 링크를 전수 검사해 남은 깨진 링크는 0 이다.

실습으로 확인한 것

문서가 적은 것 실제
--help 목록 sam birefnet depthany migan esrgan 일치
birefnet complete (5372.6 ms)mask.png + object.png 5422.9 ms, 둘 다 생성
esrgan "타일을 세며 눈에 띄게 오래 걸린다" 16타일 53106.2 ms

vision-cli test-vision vision-bench test-models 넷 다 빌드됐다.

정정 하나

앞선 커밋에서 vision-cli depth-anything 을 "그대로 치면 죽는다"고 결함으로 보고했는데 틀렸다. src/cli/cli.cpp:140arg1 == "depthany" || arg1 == "depth-anything" 로 둘 다 받는다. 안 도는 명령이 아니라 표기 불일치다 — depthany 로 통일한 편집은 유지하되 커밋 메시지를 정정했다.

에이전트 5개로 4,202줄을 나눠 정독시켰다. 노골적인 AI 어휘는 0건이었다
(leverage/delve/robust/seamless/comprehensive/crucial 등 15종, "it is worth
noting"/"that said"/"in other words", 리듬용 "not A but B", significantly/
greatly/vastly — 전부 0). 대신 습관 두 개가 전수로 세니 드러났다.

(1) "이게 중요하다"고 말하고 정작 안 말한다 — 13건
    내용 대신 내용이 중요하다는 신호를 준다. 독자는 한 박자 기다렸다 사실을 받는다.
      "What the number means is worth being exact about. It says…"
      "That is worth saying plainly: **…**"
      "That matters more than it sounds: the harness picks…"
    mmdet-detectors 만 7건이고 그중 `is worth keeping` 이 70줄 안에 세 번이었다.
    앞머리를 지우면 문장이 그대로 선다.

(2) 분열문으로 동사를 미룬다 — 11건
    "What it does is compare…" / "is what stops…" / "This is what makes…"
    g2c 가이드 489·491·498행은 12줄 안에 세 번 연달아 나온다.

그 밖에:
  - 제목 되풀이 — "## The Input Size Is Fixed" 바로 밑이 같은 말이었다
  - 같은 문단 7·11행에 `an ultralytics YOLO` 가 똑같은 em-dash 구문으로 두 번
  - "behave very differently" 의 very

남긴 것 5건은 전부 일 하는 문장이라 손대지 않았다:
  "What they do have is a compiled graph"    — bbox_head 가 없다는 앞 문장과 대조
  "What differs between them is…"            — 두 경로의 차이를 여는 화제 문장
  "which is what registering … does" (×2)    — 등록이 무엇인지 정의한다
  "the split matters more than the count"    — 실제 비교. 다음 문장이 그 split 을 준다

upstream(Acly) 문장 3건도 손대지 않았다 — vision.cpp README 의 etc. 목록,
"flexible functions which integrate with your existing data sources and
infrastructure", "Performance optimization is an ongoing process." 고치면
문장은 나아지지만 fork 차이가 벌어진다.

수치·명령어·경로·플래그·코드블록·경고문은 한 글자도 안 바뀌었다.
HTML·PDF 재생성 후 쪽수 불변(g2c 36쪽 · mmdet 29쪽), 부록 위치도 실측으로
저장된 지도와 일치(A:33 B:34 / A:28).
@201815054

Copy link
Copy Markdown
Collaborator Author

docs: 영문 slop 정리 — 습관 두 개 (19곳)

에이전트 5개로 4,202줄을 나눠 정독시켰다. 노골적인 AI 어휘는 0건이었다
(leverage/delve/robust/seamless/comprehensive/crucial 등 15종, "it is worth
noting"/"that said"/"in other words", 리듬용 "not A but B", significantly/
greatly/vastly — 전부 0). 대신 습관 두 개가 전수로 세니 드러났다.

(1) "이게 중요하다"고 말하고 정작 안 말한다 — 13건
내용 대신 내용이 중요하다는 신호를 준다. 독자는 한 박자 기다렸다 사실을 받는다.
"What the number means is worth being exact about. It says…"
"That is worth saying plainly: "
"That matters more than it sounds: the harness picks…"
mmdet-detectors 만 7건이고 그중 is worth keeping 이 70줄 안에 세 번이었다.
앞머리를 지우면 문장이 그대로 선다.

(2) 분열문으로 동사를 미룬다 — 11건
"What it does is compare…" / "is what stops…" / "This is what makes…"
g2c 가이드 489·491·498행은 12줄 안에 세 번 연달아 나온다.

그 밖에:

  • 제목 되풀이 — "## The Input Size Is Fixed" 바로 밑이 같은 말이었다
  • 같은 문단 7·11행에 an ultralytics YOLO 가 똑같은 em-dash 구문으로 두 번
  • "behave very differently" 의 very

남긴 것 5건은 전부 일 하는 문장이라 손대지 않았다:
"What they do have is a compiled graph" — bbox_head 가 없다는 앞 문장과 대조
"What differs between them is…" — 두 경로의 차이를 여는 화제 문장
"which is what registering … does" (×2) — 등록이 무엇인지 정의한다
"the split matters more than the count" — 실제 비교. 다음 문장이 그 split 을 준다

upstream(Acly) 문장 3건도 손대지 않았다 — vision.cpp README 의 etc. 목록,
"flexible functions which integrate with your existing data sources and
infrastructure", "Performance optimization is an ongoing process." 고치면
문장은 나아지지만 fork 차이가 벌어진다.

수치·명령어·경로·플래그·코드블록·경고문은 한 글자도 안 바뀌었다.
HTML·PDF 재생성 후 쪽수 불변(g2c 36쪽 · mmdet 29쪽), 부록 위치도 실측으로
저장된 지도와 일치(A:33 B:34 / A:28).

Codex added 4 commits August 21, 2026 07:52
가이드는 처음 쓰는 사람을 위한 "개요와 사용법"이다. 그런데 우리 가이드에는
**만든 사람이 밟은 실수와 우리가 못하는 것**이 들어 있었다. 제품 문서에는 그게
안 들어간다 — PyTorch 문서에도 torch.compile 의 한계는 있지만 그걸 만들며
잡은 버그는 없다.

가른 기준은 "이게 쓰는 사람의 선택을 바꾸는가"다.

  남긴다 → "dynamic shape 은 안 된다" · "양자화 GGUF 는 CPU conv 커널이
           못 받는다" · "입력 크기는 컴파일 타임에 박힌다" · 오류 메시지 사전
  옮긴다 → 계열별 오차표 · 실패 분류 · 우리가 밟은 버그 · 미해결 목록

옮긴 곳:
  docs/verification-report-en.md (신설, 504줄)
    0. 세 가지 잣대와 섞으면 안 되는 이유 (박스 86 / 텐서 89 / 스칼라 100)
    0. 정밀도 — fp16 이 배포 정밀도, fp32 는 진단용. 안 고치는 5계열
    1. 컴파일러 커버리지  2. mmdet 커버리지  3. 계열별 결과

줄어든 곳:
  docs/g2c-guide-en.md            1116 → 1074줄. 10장이 100 → 45줄
                                  (계열 이름 목록은 남겼다 — "내 모델 되나"에 답한다)
  vision.cpp/docs/mmdet-detectors.md  1001 → 609줄. 'What decodes to boxes'
                                  408줄이 보고서로. 자리엔 링크와 잣대 설명만

⚠️ 서브모듈 배치에서만 되는 상대경로를 쓸 뻔했다. mmdet-detectors 에서
   ../../docs/verification-report-en.md 는 vision.cpp 단독 클론이면 깨진다.
   GitHub URL 로 바꿨다 — 오늘 고친 'compiler checkout' 과 같은 함정이다.

PDF 36 → 35쪽. 부록이 A:33→32, B:34→33 으로 밀려 지도를 갱신하고 재실측해
수렴 확인. README 쪽수·문서 목록도 맞췄다. 링크 전수검사 깨짐 0.
(1) 앞서 PNG 로 맞춘다면서 `-o <이름>.jpg` 패턴만 치환해서, **명령은
    detected.png 인데 바로 아래 출력 예시는 detected.jpg** 인 상태였다.
    5곳(가이드 1 · mmdet 가이드 2 · README 2). 치환을 절반만 한 것이다.

(2) model-implementation-guide 의 upstream 1인칭 대목 셋을 손질했다.
    이 파일은 techdocs 로 다른 부서에 실제로 넘어간다.
      315  "hope for the best :)"          → 실제 지시로
      388  "GitHub's LFS support is kinda bullshit" → 사실만
      392  "## Afterword" 개인 소회        → "## A note on the Python tests"
    구현 절차·디버깅 방법·테스트 구성·예제 코드는 손대지 않았다. 파이썬 테스트가
    수단이지 산출물이 아니라는 사실은 남겼다 — 기여자가 알아야 할 정보다.

(3) verification-report-en 에 HTML·PDF 템플릿을 붙였다(17쪽).
    ⚠️ 붙이면서 함정을 하나 막았다. make_guide_html 의 템플릿 선택이
       `next((k for k in _DOCS if k in SRC), "g2c")` 라 **모르는 파일이면 조용히
       g2c 표지를 씌웠다.** 보고서를 그냥 돌렸으면 "Compiling PyTorch Models to
       C++" 표지가 박힌 검증 보고서가 나왔을 것이다. 이제 멈추고 아는 이름을 찍는다.

PDF: g2c 35쪽 · mmdet 29쪽 · 보고서 17쪽. 쪽 지도 전부 재실측해 수렴 확인.
(1) mmdet-detectors 가 **정반대**를 가르쳤다. "래퍼 모듈은 .pt 옆에 복사하지
    않는다. PYTHONPATH 를 설정하라" 인데, mmdet_to_pt.py:211 이
    install_loader_modules 를 부르고 mmdet_compat.py:417 의 docstring 이
    "out_path 옆에 복사한다 ... 거기 있기만 하면 **환경변수 없이 열린다**"
    라고 적어놨다. 매 export 마다 mmdet_wrap.py 와 mmdet_compat.py 를 복사한다.
    "두 파일이 전부" 도 틀렸다 — 넷이다. 시키는 대로 하면 필요 없는 env 를 걸고,
    파일을 옮길 때 둘만 챙겨 로드에서 죽는다.

(2) using-the-cli 가 `-m` 을 "Required" 라 했다. cli.cpp:271-336 은 생략하면
    명령별 기본 파일명(MobileSAM-F16.gguf 등)을 models/, $VISION_MODEL_DIR,
    $XDG_DATA_HOME/visioncpp, ~/.local/share/visioncpp, 설치 디렉터리 순으로
    찾는다. 탐색 순서까지 적었다.

(3) FPN·NMS·RoI·DFL 이 문서 6개를 통틀어 **한 번도 안 풀렸다**(각 0회).
    mmdet 을 모르는 사람이 대상인데 첫 다이어그램부터 FPN 이 나온다.
    첫 등장 자리에서 풀었다. dense head 의 "dense" 도 왜 dense 인지 적었다.

(4) 같은 4줄 예제를 싣고도 함정 경고 둘이 이 사본에만 없었다 —
    `→ gguf:` 줄을 봐야 한다는 것과 정적 링크가 등록을 버린다는 것.
    서브모듈 문서만 본 사람은 둘 다 못 본다. 옮겨 적었다.
실습에서 나왔다. 문서를 다 고쳐놓고 정작 도구가 스스로
`-o out.jpg` 를 권하고 있었다. vision-cli 는 확장자와 무관하게 PNG 를 쓰므로
(image_save → stbi_write_png) 이름만 .jpg 인 파일이 나와 뷰어가 거부한다.

QA 규칙 R2 도 도구가 찍는 문자열까지 보도록 넓혔다 — 문서만 보면 놓친다.
@Sudo42b
Sudo42b merged commit 59541ee into main Aug 21, 2026
1 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants