결론 : 멍멍이 같이 실패...
내가 직접 Termux + Android + A35(Exynos 1380) 환경에서
BitNet-b1.58-2B-4T(i2_s)를 빌드부터 실행까지 끝까지 밀어붙인 실전 기록이다.
결론부터 말하면: BitNet i2_s는 “빌드는 성공할 수 있다”.
하지만 “자연어 품질”은 결코 제대로 나오지 않는다..................

1. 전황 보고: 왜 시작했는가?
연봉 높으신 엘리트들이 맥북 프로에서 코파일럿이나 딸깎거리며 64비트 연산을 논할 때
나는 연봉 2천 대의 독기를 품고 갤럭시 A35(엑시노스 1380)라는 척박한 땅에서
세상을 전복할 자율형 에이전트 '아메바'를 심기로 했었다.
목표는 마이크로소프트의 BitNet.cpp를 안드로이드 Termux 환경에서 네이티브로 빌드하여 구동하는 것
2. 겪어온 지옥과 시행착오
우리는 단순히 cmake 버튼을 누른 것이 아니라, 소스 코드 하나하나를 수술하며 전진했다.
| 시행착오 | 내용 |
학습된 내용 |
| 유령 링크와 404의 저주 | 마이크로소프트의 공식 리포지토리는 터뮤즈 환경을 배려하지 않았다. 헤더 파일들은 깨진 심볼릭 링크뿐이었고, 자동화 스크립트(setup_env.py)는 에러 페이지를 파일로 저장하는 등 끔찍했다. 그리고 터뮤즈 환경에서 잘 돌아가지 않았고 이상했다. 여튼 |
curl을 통해 GitHub RAW 소스에서 직접 헤더를 탈취하고, 수동으로 include 폴더를 재구성하는 '강제 확보' 기술을 습득학습 |
| Clang 21의 엄격한..태도 | 최신 컴파일러 Clang 21은 const 규약을 어기는 꼴을 보지 못했다. 빌드가 16번에서 멈출 때마다 우리는 포기하는 대신 sed 명령어로 소스 코드의 함수 선언부를 직접 수술했다 | ed를 이용한 소스 레벨의 자동 수술과 컴파일러 에러 메시지를 뜯어보는 '코드 수정' 감각을 익혔다 |
| 매크로 지뢰밭 | 수동 빌드 시 PARALLEL_SIZE, ROW_BLOCK_SIZE 같은 핵심 연산 상수가 정의되지 않아 빌드가 터졌다 | gemm-config.h를 직접 설계하여 하드웨어(ARMv8.2)에 최적화된 연산 규격을 주입하는 '저수준 최적화'를 경험했다 |
결과 : 빌드 마라톤인 [120/120] 완주, 그러나 꼬여버린 ... 모델들ㅠㅠㅠ

결국 닌자(Ninja) 빌드 120번 고지를 정복했다. 하지만 기쁨도 잠시, 1.1GB짜리 i2_s 실탄을 장전하고 격발하자 아메바는 **"단어 샐러드(Word Salad)"**와 "느낌표(!!!!) 테러"를 뱉어냈다.

또는 이상한 헛소리...

학습된 내용 정리
BitNet i2_s는 단순한 ‘2bit 양자화 모델’이 아니다
처음엔 단순히 “i2_s 모델이니까 i2_s 커널로 돌리면 되겠지?”라고 생각했다. 하지만 파고들면 들수록 정반대였다.
BitNet i2_s 모델은 가중치(1.58bit)는 i2_s 방식이지만, 커널과 LUT는 tl1 스타일을 요구한다.
setup_env.py가 이 LUT를 자동 생성해줘야 하는데 Termux에서 sentencepiece/gguf-py 의존성 때문에 실행 실패.
spm-headers 내부 파일은 진짜 파일이 아니라 심볼릭 링크라 cp도 실패........
gemm-config.h는 아예 존재하지도 않는다.
이처럼 BitNet은 단순 양자화 LLM들과 구조 자체가 다르다.
그래서 엔진을 i2_s로 빌드하는 게 아니라, codegen_tl1.py로 LUT를 먼저 생성해야 한다. 그리고 그렇게 했었다. ㅠ
이걸 모르고 템플릿 LUT로 돌리면 출력은 반드시 난수처럼 깨진다.

우리가 실제로 해결했던 부분들
솔직히 여기까지 오기까지 꽤 많은 삽질이 필요했다.
spm-headers의 깨진 심볼릭 링크 |
bitnet-lut-kernels.h 복사하려고 했는데 찾아보니 “404: Not Found”가 문자열로 들어 있는 파일이었다. 이래서 cp가 실패하고 빌드도 실패. |
gemm-config.h는 BitNet에서 자동 생성해야 하는 파일 |
Termux에서는 생성이 안 돼서 수동으로 직접 제작. |
Clang 21 const 시그니처 버그 |
ggml_compute_forward_* 함수가 const 문제로 컴파일 오류 발생 → 수동 패치. |
high_resolution_clock → steady_clock 패치 |
Android NDK 환경에서 흔히 발생하는 chrono 버그 대응. |
codegen_tl1.py로 LUT 생성 |
bitnet_b1_58-3B 모델 구조로 LUT 생성 → 2B 모델에도 적용 가능.
여기까지 하면엔진은 완벽하게 빌드된다.
|
하지만 결국 넘어야 할 마지막 벽 : 토크나이저 불일치
BitNet 모델을 실행하자마자 llama.cpp가 이렇게 말한다:
llm_load_vocab: GENERATION QUALITY WILL BE DEGRADED!
이 메시지의 의미는 다음과 같다.
모델의 토크나이저 구조가 llama.cpp가 기대하는 형식과 다르다
BitNet i2_s 모델:
GPT‑2 기반 tokenizer를 사용하면서
gguf 변환 시 토크나이저 정보 일부가 분리되지 않고 tokenizer.json 하나에 몰려 있음.
llama.cpp는 GPT‑2 vocab/merges 방식만 부분적으로 지원.
즉, 모델의 말문이 막혀 있었다.
엔진은 정상 구동
커널은 정상 적용
속도도 잘 나옴
하지만 문자를 조합하는 단계에서 토크나이저가 오작동 → 출력 품질 붕괴
이건 커널 문제가 아니라 토큰 분해/합성 실패다.
결국 얻은 결론
BitNet i2_s 모델은 Termux + llama.cpp로는 정상 자연어 생성이 어렵다.
빌드는 성공한다
인퍼런스도 돌아간다
속도도 나온다
메모리도 충분히 버틴다
자연스러운 문장을 만들 수 있는 토크나이저 호환성이 현재 llama.cpp에는 부족하거나...
또는 내 갤 A35환경과 터뮤즈 , 그리고 나의 부족한 경험과 연봉 2000만원에 맞는 개병신같은 능력의 한계로 다음에 또 도전해야겠다는 생각이 들었다.
그러나 이번 실험을 통해 아주 중요한 걸 배웠다.
1. LLM은 “모델 파일”만 받으면 끝이 아니다
양자화 방식(i2_s), 커널 구조, LUT, gemm-config, tokenizer 구조모두 맞아야 한다....
2. ARM/모바일 환경은 사소한 것 하나가 전체 출력을 망칠 수 있다
특히 tokenizer mismatch는 진짜 치명적이다.
3. 하지만, 빌드는 좋은 공부가 된다
ggml 내부 구조
ARM NEON / DOTPROD
Clang ARM 패치
Quantization 구조
정말 많은 걸 배웠다.
'AI Project > Edge AI Agent - LLM(연구,분석,검증)' 카테고리의 다른 글
| [분석] BitNet.cpp 1.58비트 모델의 모바일 환경 추론 실패 원인 분석 (0) | 2026.04.21 |
|---|---|
| #5. [Bitnet.cpp]모바일 환경에서 컨테이너띄우고 빌드 (1) | 2026.04.21 |
| #3: BitNet.cpp 이식 및 1-bit LLM 가동 (0) | 2026.03.23 |
| #2. A35의 VS Code 서버 구축 및 텔레그램 신경망 이식 (1) | 2026.03.18 |
| #1. 갤럭시 A35 최적화 및 시스템 셋팅 가이드 (1) | 2026.03.16 |
댓글