Skip to content
SecurityOn
Go back

MacBook의 로컬 LLM과 연구 기록

「Apple Silicon AI 환경 검증」에서는 GPU 연산과 작은 학습을 확인했습니다. 이번에는 이미 학습된 언어 모델을 MacBook에서 실행하고, 기존 연구 기록을 참고해 질문에 답하도록 구성했습니다. 로컬 LLM은 외부 서비스에 계산을 맡기는 대신, 사용하는 컴퓨터에서 언어 모델을 실행하는 방식입니다. 이번 실험에서는 Apple M5 Pro와 메모리 24GB를 갖춘 MacBook Pro를 사용했습니다. 모델 파일을 내려받는 단계와, 내려받은 파일로 답변을 생성하는 단계를 나눠 확인했습니다.

처음부터 큰 모델이나 많은 논문을 넣기보다, 내용을 알고 있는 짧은 자료를 사용했습니다. 답이 그럴듯한지만 보면 잘못된 숫자나 근거를 놓치기 쉽기 때문입니다. 이번 목표는 작은 모델의 실행 경로를 마련하고, 답변과 출처를 직접 검토할 수 있는 실험을 남기는 것이었습니다.

1. 작은 모델과 별도 프로젝트

실험 폴더는 ~/Developer/research-local-llm입니다. 앞선 GPU 계산 프로젝트와 가상환경을 분리하고 Python 3.13.16을 사용했습니다. .python-version은 사용할 Python 버전을, uv.lock은 실제로 선택된 패키지 버전을 기록합니다. pyproject.toml의 >=3.13.16,<3.14는 3.13 계열 안에서 환경을 유지하기 위한 범위입니다.

실행 도구는 MLX-LM으로 정했습니다. 앞서 확인한 MLX를 이용해 언어 모델을 불러오고 문장을 생성하는 Python 패키지입니다. 별도 서버를 먼저 구성하기보다 코드 안에서 입력과 출력, 실행 조건을 함께 기록하기에 적합했습니다. MLX-LM 공식 저장소의 로딩·생성 방식을 확인하고 구성했습니다.

첫 설치 명령은 uv add mlx-lm 'mlx==0.32.3' huggingface-hub였습니다. MLX만 버전을 고정하고 MLX-LM은 지정하지 않았으며, 당시 Python 요구 범위도 >=3.13.16으로 상한이 없었습니다. uv가 패키지들의 요구 조건을 조합하는 과정에서 MLX-LM 0.0.3, transformers 4.12.2, tokenizers 0.10.3이 선택됐고, tokenizers를 소스에서 빌드하다 Rust 컴파일러가 없어 실패했습니다. 단순히 기본값을 사용하면 최신 버전이 설치되는 상황은 아니었습니다.

설치 전에 Python·MLX·MLX-LM의 호환 조건을 충분히 확인하지 않은 것이 준비 과정의 부족한 점이었습니다. 이후 MLX-LM 0.32.0의 패키지 정보를 확인했고, MLX 0.32.2 이상과 transformers 5.7.0 이상을 요구한다는 것을 확인했습니다. Python 요구 범위를 >=3.13.16,<3.14로 좁히고 MLX-LM 0.32.0을 지정해 다시 설치하자 아래 버전 표에 정리한 환경으로 설치됐습니다. 왜 첫 해결 과정에서 그 오래된 조합까지 선택됐는지는 완전히 추적하지 않았으므로, 실패 원인을 Python 상한 하나로 단정하지 않습니다. 추가 컴파일 도구를 설치하기 전에 사용할 패키지 조합을 먼저 명확히 한 조치였습니다.

uv add 'mlx-lm==0.32.0' 'mlx==0.32.3' huggingface-hub
항목실제 설치 버전역할
Python3.13.16프로젝트 실행 환경
MLX / mlx-metal0.32.3Apple Silicon GPU 계산
MLX-LM0.32.0모델 로딩과 문장 생성
transformers5.18.0모델의 입력 문장 처리 등에 사용하는 라이브러리
huggingface-hub1.33.0모델 파일 다운로드
NumPy2.5.3수치 배열 라이브러리

모델은 Qwen이 공개한 Qwen3-1.7B-MLX-4bit를 선택했습니다. 1.7B는 약 17억 개의 매개변수를 가진 모델이라는 뜻입니다. 매개변수는 학습 과정에서 정해진 내부 숫자이며, 개수가 많다고 이번 연구 질문에 더 정확하게 답한다고 단정할 수는 없습니다.

4bit는 모델의 가중치를 더 적은 비트로 표현하는 양자화 형식입니다. 저장공간과 메모리 부담을 줄이는 대신 원래 숫자를 근사하므로 답변 품질에 영향을 줄 수 있습니다. 이번에는 짧은 실행 시험을 시작하기 위한 작은 모델로 선택했으며 다른 모델이나 양자화 방식과 품질을 비교하지 않았습니다. 라이선스는 Apache-2.0이고, 내려받은 설정에는 4비트·그룹 크기 128이 기록돼 있습니다.

2. 모델 파일과 실행 조건 기록

모델 이름만 기록하면 나중에 같은 이름의 파일이 바뀌었을 때 당시 상태를 찾기 어렵습니다. 다운로드는 다음 리비전으로 고정했습니다. 리비전은 해당 저장소의 특정 파일 상태를 가리키는 식별자입니다.

Qwen/Qwen3-1.7B-MLX-4bit
21457c6f51ed54a7c16e988c0844db973815c137

설정·토크나이저·가중치·라이선스 등 9개 파일의 합계는 930,270,314바이트, 약 0.93GB였습니다. model-manifest.json에 각 파일의 크기와 SHA-256 해시를 남겼습니다. 해시는 파일 내용이 달라졌는지 비교할 때 사용하는 값입니다. 가중치와 tokenizer.json 두 파일은 저장소 메타데이터의 예상 SHA-256과도 일치했습니다. 나머지 파일은 해시를 기록했으며 별도의 예상 SHA-256과 대조한 것은 아닙니다.

모델은 프로젝트의 models에, 다운로드 캐시는 .runtime에 두었습니다. .venv, 모델, 캐시와 생성 결과는 .gitignore에 넣고 소스와 의존성 기록의 관리 범위를 구분했습니다. 모델 파일을 GitHub에 올리거나 결과가 자동으로 Google Drive에 보존되는 구성은 아닙니다.

실행에는 로컬 모델 경로를 사용하고 Hugging Face의 오프라인 설정과 uv --offline을 적용했습니다. 이미 있는 파일과 패키지로 실행했지만 Wi-Fi를 끈 시험은 아닙니다. 앞선 정밀도 검증에서 정한 MLX_ENABLE_TF32=0도 이번 프로세스에 명시했습니다.

/bin/zsh ~/Developer/research-local-llm/run-verification.sh

토큰은 모델이 문장을 처리하는 작은 단위입니다. 한 토큰이 한 글자나 한 단어와 항상 같지는 않습니다. 질문과 참고자료를 합친 입력은 2,048토큰 이내로 제한하고 새로 생성할 답변은 최대 256토큰으로 정했습니다. 짧은 자료와 답변으로 실행 경로를 확인하기 위한 상한입니다.

모델 카드의 문맥 길이는 32,768토큰이고 내려받은 설정의 max_position_embeddings는 65,536입니다. 문맥 길이는 한 번에 다루는 입력과 응답의 길이에 관계하는 값입니다. 두 표기가 다르지만 이번 시험에서는 어느 최대 길이도 검증하지 않았습니다.

답변 선택에는 temperature=0을 사용했습니다. 매 단계에서 가장 가능성이 높은 토큰을 고르는 방식으로, 이번 기준 실행의 무작위 선택을 줄이려는 결정입니다. Qwen의 일반적인 품질 권장 설정을 그대로 따른 것은 아닙니다. 같은 설정이 모든 질문에서 가장 좋은 답을 준다는 의미도 아닙니다.

3. 질문 전에 자료를 찾는 과정

모델이 이전에 학습한 지식에만 의존하지 않도록 기존 공개 노트의 일부를 별도 자료로 준비했습니다. 검색할 자료를 먼저 고르고 질문과 함께 모델에 전달하는 흐름입니다. 흔히 RAG라고 부르는 방식의 작은 예이지만, 이번에는 문서 세 개로 검색과 근거 확인의 과정을 살펴봤습니다.

자료 번호공개 노트에서 가져온 내용
D1코드·연구자료 경로와 Google Drive 동기화 위치
D21부터 5까지의 제곱값과 예상 합계·평균
D3MLX 정밀도 검사와 MLX_ENABLE_TF32=0 결정

검색은 질문과 자료에 등장하는 두 글자 묶음의 유사도를 계산하고 상위 두 자료를 고르는 방식으로 작성했습니다. 별도 임베딩 모델이나 벡터 데이터베이스는 설치하지 않았습니다. 문서 수가 적으므로 검색 결과를 직접 볼 수 있는 단순한 출발점으로 정했습니다. 표현이 달라지거나 문서가 많아졌을 때의 검색 품질은 따로 확인해야 합니다.

폴더·계산·정밀도 질문에서는 관련 자료가 각각 검색 1위였습니다. 이 결과만으로 답변이 정확하다고 판단하지 않고, 실제 답과 출처 번호도 원문과 대조했습니다. 자료에 없는 정보를 묻는 질문에는 확인할 수 없다고 답하도록 지시했습니다.

4. 첫 실행에서 드러난 문제

GPU 장치는 Device(gpu, 0)으로 확인됐고 모델이 응답을 생성했습니다. 다만 enable_thinking=False를 전달했어도 일부 응답에는 긴 <think> 내용이 나왔습니다. 내려받은 리비전의 대화 템플릿을 읽어보니 해당 설정을 처리하는 분기가 없었습니다. 설정을 전달한 것과 실제 입력에 반영된 것은 다른 상태였습니다.

첫 계산 답변은 256토큰 상한에 걸려 문장 중간에 끊겼습니다. 정밀도 질문에서는 설정값을 찾았지만 D3에 있는 내용을 D1으로 인용했습니다. 폴더 답변에는 출처 번호가 없었습니다. 자료가 올바르게 검색돼도 답변의 길이와 인용은 따로 확인해야 한다는 문제가 드러났습니다.

첫 JSON과 로그를 보존하고 모델 파일은 그대로 둔 채 입력 형식을 수정했습니다. 답변 시작 부분에 비어 있는 <think>...</think> 구간을 완결해 넣고 그 뒤에 답하도록 했습니다. 이는 내려받은 템플릿에 없는 동작을 실행 코드에서 명시한 조정입니다. 같은 질문과 검색 방식, 출력 상한으로 다시 실행해 실제 응답을 확인했습니다.

5. 답변 내용과 출처는 따로 확인

재실행의 첫 질문은 2 + 3을 숫자 하나로 답하라는 요청이었습니다. 예상 답은 5이고 실제 응답도 5였습니다. 이어 네 질문의 답을 원문과 대조했습니다.

질문재실행에서 확인한 내용출처 검토
코드·자료 폴더와 동기화 위치~/Developer, ~/Research, 컴퓨터 → My Mac → Research를 정확히 답함[Д1], [D3] 출력. 첫 문자는 영문 D가 아니며 D3도 이 답의 근거가 아님
제곱값의 합계·평균합계 55, 평균 11.0을 정확히 답함D2의 내용을 D1으로 인용함
MLX 정밀도 설정MLX_ENABLE_TF32=0을 정확히 답함D3 인용이 원문과 일치함
70B 모델의 생성 속도자료에서 확인할 수 없다고 답함없는 속도 값을 만들지 않음

계산 답변이 사용하는 합계 55와 평균 11.0은 D2에 적힌 1, 4, 9, 16, 25에서 나온 값입니다. 숫자가 맞아도 D1을 가리키면 독자가 근거를 찾을 수 없습니다. 폴더 답변의 [Д1] 역시 화면에서는 D1과 비슷해 보이지만 다른 문자이며, 함께 붙인 D3에는 해당 폴더 정보가 없습니다.

정밀도 질문의 실제 응답은 다음과 같았습니다.

MLX float32 기준 검증에 명시하기로 한 환경변수 설정은
`MLX_ENABLE_TF32=0`입니다. [D3]

“이 MacBook에서 70B 모델을 실행했을 때 평균 생성 속도는 몇 토큰/초인가요?”라는 질문에는 “자료에 관련 정보가 없습니다. 정확히 자료에서 확인할 수 없습니다.”라고 답했습니다. 여기서 확인한 것은 이 한 질문에 대한 응답이며, 모든 종류의 모르는 질문을 거절한다는 보장은 아닙니다.

첫 실행과 재실행의 원문은 각각 별도 JSON과 로그로 남겼습니다. 재실행 결과에는 질문, 검색된 자료와 점수, 실제 답변, 토큰 수와 종료 사유를 함께 저장했습니다. 첫 답변의 출처 오류를 지우거나 정답으로 바꿔 저장하지 않았습니다.

6. 이번 실행의 범위

재실행에서 참고자료를 포함한 네 질문의 입력은 610에서 620토큰, 생성 답변은 20에서 93토큰이었습니다. 모두 출력 상한에 걸리지 않고 종료됐습니다. MLX-LM이 보고한 생성 속도는 이 네 답변에서 약 231에서 240토큰/초였고, MLX의 누적 최대 메모리 지표는 약 1.75에서 1.81GB였습니다.

이 값은 짧은 입력을 한 프로세스에서 차례로 실행하며 도구가 반환한 관측값입니다. 반복 측정한 벤치마크가 아니며, 메모리 지표도 macOS 전체 메모리 사용량이 아닙니다. 큰 모델이나 긴 논문을 넣었을 때의 속도·메모리로 확대해서 해석하지 않습니다. 두 토큰만 생성한 첫 숫자 답변은 속도 비교에서 제외했습니다.

작은 모델을 GPU에서 실행하고 공개 연구 기록을 검색해 답변에 넣는 경로는 마련했습니다. 짧은 질문에서는 필요한 내용을 찾았지만 인용을 지시하는 문장만으로 출처의 정확성을 확보하지는 못했습니다. 이후 연구자료에 적용할 때에는 검색 결과와 원문 링크를 코드가 별도로 제시하고, 모델이 쓴 주장과 인용은 검토하는 구조가 필요합니다.

이번 자료는 공개 노트의 짧은 발췌문입니다. 개인 연구자료나 긴 PDF를 처리하지 않았고 모델을 새로 학습시키지도 않았습니다. 재시동 뒤 실행과 새 환경에서의 재구성도 이번 단계의 확인 범위에는 포함하지 않았습니다. 다음 편에서는 코드·자료·결과의 보존 경계와 다시 실행할 때 필요한 기록을 정리할 계획입니다.


Share LinkedIn

Previous Note
Apple Silicon AI 환경 검증
Next Note
MacBook 연구환경의 실제 구성