1P by GN⁺ | ★ favorite | 댓글 1개
  • lm.rs는 Rust로 CPU에서 로컬 언어 모델 추론을 실행하는 프로젝트이며, ML 라이브러리 없이 전체 추론을 수행하는 최소 코드 구현을 목표로 함
  • Karpathy의 llama2.cllm.c에서 영감을 받았고, 초기에는 Google Gemma 2만 지원했지만 Llama 3.2와 PHI-3.5 이미지 입력 지원까지 확장됨
  • 최신 변경으로 배치 처리가 구현되어 이미지 인코딩 속도가 최대 약 3배 빨라졌고, Llama 3.2 1B는 작성자의 16코어 머신에서 50 tok/s로 실행됨
  • 준비된 모델은 Hugging Face에서 받을 수 있으며, README는 Q8_0 사용을 권장하고 Q4_0 양자화는 아직 개선 중이라고 밝힘
  • 사용자는 LMRS 형식 모델과 토크나이저를 내려받아 바로 빌드하거나, Hugging Face의 원본 모델 파일을 export.pytokenizer.py로 변환해 실행할 수 있음

lm.rs가 목표로 하는 것

  • lm.rs는 Rust로 작성된 로컬 CPU 기반 언어 모델 추론 구현체임
  • 목표는 ML 라이브러리 없이 CPU에서 언어 모델의 전체 추론을 수행하는 최소 코드 구현임
  • Karpathy의 llama2.cllm.c에서 영감을 받음
  • README는 현재 코드가 “그렇게 최소는 아니며”, 일부 코드는 최적화와 개선 여지가 있다고 밝힘
  • 프로젝트는 작성자가 Rust를 처음 써보기 위한 계기이기도 함

지원 모델과 멀티모달 확장

  • 초기에는 Google Gemma 2 모델만 지원했지만, 이후 Llama 3.2 모델 지원이 추가됨
  • 최근에는 PHI-3.5를 통해 이미지 사용 옵션이 추가됨
  • 현재 강조되는 지원 항목
    • PHI-3.5-vision 모델을 통한 멀티모달 지원
    • PHI-3.5-mini 텍스트 전용 모델 지원
  • 관련 리소스

성능과 준비된 모델

모델 변환 흐름

  • 준비된 양자화 모델과 토크나이저를 Hugging Face에서 받으면 변환 과정을 건너뛸 수 있음
  • Google 또는 Meta가 Hugging Face에 공개한 모델을 직접 변환하려면 추가 Python 의존성을 설치해야 함
pip install -r requirements.txt
  • 원본 모델 페이지에서 .safetensorsconfig.json 파일을 내려받아 사용함
  • PHI3.5 Vision 같은 멀티모달 모델에는 CLIP config 파일도 필요함
  • export.py는 bfloat16 가중치를 LMRS 형식으로 변환함
python export.py --files [ordered .safetensor files] --config [model config.json] --save-path [name and path to save] --type [model type (GEMMA/LLAMA/PHI)]
  • 양자화 버전을 내보내려면 --quantize--quantize-type 플래그를 사용함
  • int8 양자화 모델 크기는 그룹 크기에 따라 약 9.8G에서 약 2.5G로 줄어들 수 있음
  • 멀티모달 모델은 --vision-config 인자를 포함해야 함
  • tokenizer.py는 토크나이저 모델을 LMRS 토크나이저 형식으로 변환함
python tokenizer.py --model-id [huggingface model_id] --tokenizer-type [type of the tokenizer (GEMMA/LLAMA/PHI)]

빌드와 실행

  • Rust 코드는 cargo로 컴파일하며, README는 target-cpu 플래그 전달을 명시함
RUSTFLAGS="-C target-cpu=native" cargo build --release --bin chat
  • 멀티모달 기능을 활성화하려면 --features multimodal 인자를 추가함
  • 기본 실행은 모델 가중치 파일을 지정해 수행함
./target/release/chat --model [model weights file]
  • 추가 인자로 tokenizer, temperature, top-p, show-metrics 등을 사용할 수 있음
  • 사용 가능한 인자는 --help로 확인함
  • 멀티모달 모델에서는 --image 인자로 이미지 경로를 지정함
  • PHI3.5-vision 사용 시 README는 temperature 0을 권장함

WebUI 백엔드 실행

  • WebUI용 백엔드를 실행하려면 backend 기능으로 컴파일함
RUSTFLAGS="-C target-cpu=native" cargo build --release --features backend --bin backend
  • 멀티모달 백엔드는 backend-multimodal 기능을 활성화함
  • 백엔드는 모델 가중치 파일을 지정해 실행함
./target/release/backend --model [model weights file]
  • --ip--port로 IP와 포트를 변경할 수 있음
  • temperature 같은 추가 플래그도 사용할 수 있음
  • 멀티모달 호환성에는 --multimodal 플래그를 사용함
  • 실행 후 웹 인터페이스로 연결할 수 있음

TODO 상태와 라이선스

  • 완료된 항목
    • 다른 샘플링 방법 추가
    • 9B와 27B 모델 테스트 항목 중 9B 테스트 완료, 27B는 너무 느릴 것으로 표시
    • 멀티헤드 어텐션 루프 병렬화
    • 성능 지표 추가
    • int8, int4 양자화 지원
  • 남은 항목
    • 시스템 프롬프트 제공 기능
  • 라이선스는 MIT

댓글과 토론

Hacker News 의견들
  • M2 64GB MacBook에서 1.2GB짜리 llama3.2-1b-it-q80.lmrs를 돌려보니 꽤 빠르게 느껴졌고, Activity Monitor 기준으로 13개 스레드에서 CPU 1000% 를 사용했음
    /tmplm.rs를 클론하고 RUSTFLAGS="-C target-cpu=native" cargo build --release --bin chat로 빌드한 뒤, Hugging Face에서 tokenizer.binllama3.2-1b-it-q80.lmrs를 받아 ./target/release/chat --model llama3.2-1b-it-q80.lmrs로 실행함

    • ./target/release/chat --model llama3.2-1b-it-q80.lmrs --show-metrics로 실행해서 초당 토큰 수가 얼마나 나오는지 확인해볼 수 있냐고 물어봄
    • 간단한 프랑스어 대화 프롬프트를 넣었더니 처음엔 응답하는 듯하다가 곧 끝없는 난해한 쓰레기 출력으로 무너졌음
      포맷 때문에 일부만 남겼지만, 긴 무작위 단어열이 계속 이어지는 형태였음
    • 이게 최신 ChatGPT와 비교해 얼마나 똑똑한지를 어떤 식으로 이해해야 할지 궁금함
  • 글이 아주 잘 쓰였고, 수업에서 트랜스포머가 실제로 어떻게 동작하는지 설명할 때 일부 소스 코드를 써볼 수 있겠음
    어텐션 헤드 그림들보다 코드가 더 구체적이고 세부적임. 다만 라이브러리는 stdout에 직접 출력하면 텍스트 편집기에서 스타일 검사를 제공하는 식의 애플리케이션 출력을 망칠 수 있으니, lm.rs 객체와 연결된 로깅 인스턴스의 문자열 버퍼에 쓰는 편이 낫겠음
    또 모델 리더에서 데이터 정렬을 강제하려고 unsafe를 쓰는 부분이 보이는데, 무리하지 않고 unsafe 없이 가능한지도 궁금함

    • 문자열 버퍼보다 사용자 콜백을 호출하는 편이 더 좋음
      그러면 로그를 GUI에 표시하는 식으로 처리할 수 있음
  • 모델 로딩과 여러 LLM 작업용 Rust 도구를 꽤 만들어뒀음
    사용 가능한 메모리에 따라 가장 큰 양자화 모델을 자동 선택하거나, gguf에서 토크나이저를 추출하거나, 프롬프트를 넣는 기능 등이 있음. 이걸 쓰면 일부 Python 의존성을 제거할 수 있을 듯함
    현재는 llama.cpp 지원용이지만 이것도 꽤 흥미로움. 문법 제약(grammar) 지원 계획이 있는지도 궁금함
    https://github.com/ShelbyJenkins/llm_client

  • 제목의 no dependency 표현은 덜 명확함
    처음 봤을 때는 no_std일 수도 있겠다고 생각했는데, 실제로는 no_std가 아니고 의존성도 몇 개 있어 보임. 아마 전부 Rust 의존성이라는 뜻일 수는 있겠음

    • 제목 짓기는 어렵다. 말하고 싶었던 건 PyTorch, CUDA, ONNX 같은 딥러닝 의존성이 없고 모든 로직이 자체 포함되어 있다는 뜻이었음
      투명하게 말하면 기본 Rust 의존성이 5개 있고, 그중 chronoclap은 채팅 기능용으로 기능 플래그 처리하는 게 맞음. 나머지 3개는 하드웨어 성능을 조금 더 끌어내기 위한 유틸리티 크레이트로, 병렬화를 쉽게 하는 rayon, SIMD를 돕는 wide, 모델 파일 메모리 매핑용 memmap2
    • README를 보면 requirements.txt에 PyTorch와 여러 Python 의존성이 필요한 것처럼 보이고, 페이지에서 “dependency”라는 단어가 보이는 곳도 거기뿐이라 제목의 표현이 꽤 혼란스러움
      프로젝트 자체는 그냥 “Minimal LLM inference in Rust”라는 부제를 쓰고 있는 듯함. Git 기록을 보면 이 글을 올린 계정은 기여자지만 주 저자는 아닌 것 같은데, zero dependencies가 정확히 무슨 의미인지 설명해주면 도움이 되겠음
    • 원래는 “하드웨어 의존성 없음”이나 “GPU 의존성 없음” 같은 제목이었으면 말이 됐을 수도 있음
      안타깝게도 HN은 제목에서 단어를 별 이유나 투명성 없이 삭제하곤 함
    • Rust의 cargo도 이제 거의 npm처럼 된 건가 싶음
      의존성이 16개인데 어떻게 의존성 없음이라고 할 수 있는지 모르겠음
  • 예전에 비슷한 걸 만들어봤지만, CPU에서 도는 C/C++ 코드와 비교하면 성능이 아쉬웠음
    Rust를 빠르게 만드는 방법을 제대로 몰랐다는 뜻이기도 함. 여러 Rust 구현체의 벤치마크가 있으면 좋겠음
    LLM 추론 구현은 진지한 프로그래머들에게 새로운 “Hello, world!”가 될 수도 있어 보임
    https://github.com/gip/yllama.rs

    • 나도 예전에 비슷한 “Hello, world” 경험으로 만든 게 있음
      https://github.com/crabml/crabml
      일부 SIMD 명령을 직접 사용했고, 성능은 llama.cpp와 맞출 수 있어 보였음. 핵심은 양자화 행렬 곱셈에서 SIMD 사용과, 스레드 간 작업 분할 때 조건 변수 대신 바쁜 대기 루프를 쓰는 것 같음
      다만 GPU에서 Vulkan으로 양자화 모델 추론을 계속 작업할 시간이 없어서 한동안 업데이트하지 못했음
  • 이미 Dioxus를 쓰고 있는 점이 흥미롭고, 로드맵에 WASM도 들어갈 수 있을지 궁금함
    브라우저에서 RWKV 같은 가벼운 LLM을 돌릴 수 있다면, SaaS API를 호출하지 않고도 브라우저가 새로운 기능 범주를 열 수 있음

    • 이쪽을 조금 만져본 적이 있음
      https://github.com/maedoc/rwkv.js
      Emscripten으로 컴파일한 Rwkv.cpp를 썼는데, 아직 토크나이저 부분은 제대로 해결하지 못했음. 그래도 1.6B RWKV6는 오프라인 브라우저 전용으로 충분히 쓸 만할 것 같음
      일반 채팅에는 역량이 부족하지만 RAG 같은 용도에는 꽤 충분할 수 있음
    • 라이브러리 자체는 아주 적은 변경으로 WASM 컴파일이 가능할 듯함
      필수 의존성인 rayonwide는 WASM을 바로 지원하고, transformer.rsMmap 타입을 &[u8]로 바꾸면 memmap2도 제거할 수 있음
      다만 RWKV는 완전히 다른 구조라 전체를 새로 구현해야 하고, 로드맵에 들어갈 가능성은 매우 낮아 보임
  • 이 구현들은 전부 CPU 한정인지 궁금함
    좋은 GPU가 있다면 다른 대안을 찾아야 하는 게 맞는지 묻고 있음

    • 맞음. 이 프로젝트는 CPU에서 동작하므로 GPU를 계산에 활용하지 않음
      GPU를 지원하는 Rust 프레임워크를 써보고 싶다면 Candle https://github.com/huggingface/candle/tree/main을 살펴볼 만함
    • 전부 CPU에 구현되어 있고, 적어도 현재는 GPU 가속이 전혀 없음
      실제로 실행하려는 목적이라면 CPU만 쓰더라도 대안, 즉 llama.cpp를 쓰는 편이 낫겠음. 이 프로젝트는 생태계의 복잡한 계층을 걷어냈을 때 내부가 어떻게 돌아가는지 보여주는 교육용 자료에 더 가까움
      LLM은 효과 면에서는 마법처럼 보이지만 코드 관점에서는 꽤 단순함
    • GPU에 따라 10~20배 차이가 날 수 있음
      Rust 쪽에는 내 llm_client 같은 llama.cpp 래퍼가 있고, Candle 기반 프로젝트로 mistral.rs와 Kalosm가 있음
      내 프로젝트도 mistral.rs 구현을 제공하려고 하지만 아직 llama.cpp에서 완전히 이전하지는 못했음. 완전한 Rust 구현은 설치 시간을 빠르게 만드는 등 장점이 큼. 지금 내 크레이트는 클론하고 빌드해야 해서 macOS, Windows, Linux에서 자동화는 되어 있지만 빌드 시간이 약 1분 늘어남
    • CPU도 맞지만 더 중요한 건 메모리 대역폭
      예를 들어 RTX 3090은 거의 1TB/s 메모리 대역폭을 갖고 있음. 이걸 맞추려면 지구상에서 가장 빠른 개념 증명 수준 DDR5를 최소 12채널은 써야 함
      외장 GPU가 있다면 그걸 활용하는 구현을 쓰는 편이 완전히 다른 세계임. Apple Silicon의 LLM 추론 수치가 인상적인 이유도 CPU-GPU 통합 고대역폭 메모리 구조가 있어서이며, 기억상 약 400GB/s였음
    • 경우에 따라 다름. 좋은 모델은 크고 메모리 요구량도 큼
      4090조차 LLM 기준으로는 메모리가 그렇게 많지 않음. GPU가 더 빠르긴 하겠지만 큰 모델은 못 올릴 가능성이 큼
  • 이게 llama.cpp와 비교해서 어떤 가치가 있는지 궁금함

    • 다른 Rust 프로젝트와 통합하기 더 쉬울 수 있음
    • 기능이 적어서 코드베이스가 더 깔끔함
  • 멋지고 첫 Rust 라이브러리를 만든 것도 축하하지만, 진지한 로컬 사용에는 Metal/CUDA 지원이 필수임

    • CUDA를 쓰면 이 프로젝트의 목적에 어긋나서 시작부터 맞지 않음
      다만 주 저자는 아니지만 기여자로서 wgpu로 어느 정도 GPU 가속을 얻는 실험을 하고 있음. 주 저자가 복잡도를 통제하고 싶어 해서 실제로 어디까지 갈지는 모르겠음
  • Rust 커뮤니티가 거의 모든 걸 다시 쓰려는 열정은 흥미롭고 고맙게 느껴짐