구형 라즈베리 파이로 클라우드 비용 없는 로컬 텍스트 분류기 만들기
2026年5月15日
0
컴퓨터/소프트웨어Comments (0)
Log in to leave a comment
No posts yet
Log in to leave a comment
No posts yet
서랍 구석에 던져둔 1세대 라즈베리 파이나 파이 제로는 보통 계륵입니다. 고작 512MB인 메모리로는 요즘 나오는 온디바이스 AI 도구를 돌리다가 기기가 그대로 뻗어버리니까요. 매달 나가는 AWS나 오픈AI API 비용을 생각하면 이 고물이라도 굴려보고 싶지만 뾰족한 수가 안 보입니다. 결론부터 말하면 리눅스 커널을 극한까지 쥐어짜고 90M짜리 초소형 모델을 올리면 24시간 돌아가는 독립형 자동화 노드를 만들 수 있습니다. 속도는 느려도 돈은 한 푼 안 듭니다.
1세대 라즈베리 파이는 운영체제가 기본으로 먹는 용량을 빼면 AI 추론 프로세스가 쓸 수 있는 방이 300MB도 안 남습니다. 부족한 메모리를 채우려고 SD카드에 기본 스왑 파일을 잡는 짓은 자살행위입니다. 느려터진 속도 때문에 병목이 오고 쓰기 제한이 있는 SD카드 수명만 갉아먹습니다. 대신 램의 일부를 압축해서 디스크처럼 쓰는 리눅스 커널의 zram 모듈을 올려야 합니다.
700MHz짜리 싱글 코어 ARMv6 프로세서에는 lz4 알고리즘이 답입니다. 압축률이 높은 zstd는 이 늙은 CPU를 과열로 죽이려고 듭니다. 속도가 가장 빠르고 가벼운 lz4로 1GB 용량의 zram 영역을 잡으면 물리 메모리 공간을 최대 3배 가까이 아낄 수 있습니다.
리눅스 커널이 대규모 연산을 마주했을 때 AI 프로세스를 강제로 쳐내는 현상도 막아야 합니다. 커널 매개변수인 vm.swappiness 값을 기본값 60에서 10으로 떨어뜨리십시오. 모델 가중치가 물리 메모리에서 쫓겨나 디스크로 가버리는 참사를 막아줍니다. 파일 시스템 메타데이터가 램을 차지하지 못하도록 vm.vfs_cache_pressure 설정은 500으로 올려야 프로세스 공간이 숨을 톺아챕니다.
터미널을 열고 다음 설정을 그대로 박아 넣으면 재부팅 없이 커널이 고정됩니다.
sudo sysctl -w vm.swappiness=10
sudo sysctl -w vm.vfs_cache_pressure=500
sudo sysctl -w vm.page-cluster=0
화면이 안 나오는 헤드리스 서버로 쓸 테니 껍데기만 남기고 다 지워야 합니다. 그래픽 인터페이스인 lightdm은 구형 파이에서 50MB가 넘는 RAM을 먹는 괴물입니다. 블루투스 모듈 전원을 끄고 GPU 메모리 할당량을 최소치인 16MB로 낮추면 60MB 이상의 순수 가용 메모리가 생깁니다. 싱글 코어가 온전히 연산에만 집중할 수 있는 환경이 드디어 준비됩니다.
하드웨어가 원시적이니 최신 AI 라이브러리가 요구하는 NEON 명령어나 멀티코어 가속은 꿈도 못 꿉니다. 대안은 Technology Innovation Institute가 공개한 9000만 개 파라미터 규모의 Falcon-H1-Tiny 모델입니다. 여기에 4비트 양자화(Q4_K_M)를 먹이면 가중치 파일이 70MB 수준으로 줄어들어 512MB 램 안으로 가볍게 들어옵니다.
이 모델을 돌리려면 llama.cpp 라이브러리가 필요한데, 파이 안에서 직접 컴파일하겠다고 덤볐다가는 기기가 열받아 멈추거나 꼬박 하루를 날립니다. 똑똑하게 PC에서 dockcross 같은 툴로 ARMv6 전용 바이너리를 크로스 컴파일해서 가져와야 합니다. 빌드할 때 멀티스레딩 오버헤드를 끄는 -DGGML_OPENMP=OFF 플래그를 붙여야 싱글 코어에서 낭비되는 컨텍스트 스위칭 부하를 막고 초당 0.5토큰 안팎의 속도를 유지합니다.
문제는 이 90M짜리 모델이 뇌용량이 작아서 조금만 틈을 주면 헛소리(환각)를 장황하게 늘어놓는다는 점입니다. 문장이 길어지면 CPU 점유율이 100%로 치솟으며 시스템이 마비됩니다. 지시문은 철저하게 단어 형태로 깎아내야 합니다. "가전을 제어하는 비서로서 전원을 켜라" 같은 친절한 문장은 필요 없습니다. Task: Classify. Tags: [ON, OFF]로 끝내야 합니다. 예시를 줄 때도 Input: 불켜줘 -> Output: ON 처럼 단어 쌍으로만 유도해야 출력이 엉뚱한 길로 안 빠집니다.
출력이 길어지기 전에 낚아채는 방어 코드도 필수입니다. 파이썬 스크립트에서 토큰 스트림을 바이트 단위로 실시간 감시하다가 원하는 키워드가 나오면 프로세스를 즉시 죽여야 합니다.
import subprocess
import re
# llama.cpp 바이너리를 프로세스로 호출
process = subprocess.Popen(
["./main", "-m", "falcon-h1-tiny-q4_k_m.gguf", "-p", "Tags: [ON, OFF]. Task: turn on light"],
stdout=subprocess.PIPE, text=True
)
output_text = ""
for line in process.stdout:
output_text += line
# 필요한 키워드가 걸리면 그 즉시 엔진을 강제 강등하고 루프 탈출
if re.search(r"ON|OFF", output_text):
process.terminate()
break
이 조기 종료 코드가 들어가야 쓸데없이 연산이 늘어지는 걸 막고 평균 응답 시간을 7초 안으로 끊을 수 있습니다.
싱글 코어에 실시간 동기식 요청을 보내면 파이프라인이 바로 터집니다. 메시지를 받는 통로와 AI가 대가리를 굴리는 단계를 철저히 떼어놓는 아키텍처가 필요합니다.
구형 파이의 네트워크 부담을 덜어주는 가벼운 Mosquitto MQTT 브로커를 내부에 띄우고, 들어오는 자연어 명령은 파일 기반 데이터베이스인 SQLite 큐에 일단 던지고 봅니다. SD카드가 계속 긁혀서 깨지는 걸 막으려면 연결하자마자 아래 설정을 찔러 넣어서 메모리 위에서 돌려야 합니다.
PRAGMA journal_mode=WAL;
PRAGMA temp_store=MEMORY;
큐에 쌓인 데이터를 백그라운드에서 하나씩 꺼내어 하드웨어 신호로 바꾸는 파이썬 워커 스크립트 전체 구조는 다음과 같습니다.
import sqlite3
import subprocess
import re
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
conn = sqlite3.connect('automation.db')
conn.execute("PRAGMA journal_mode=WAL;")
cursor = conn.cursor()
# 처리 대기 상태인 명령어 한 개씩 인덱싱
cursor.execute("SELECT id, command FROM queue WHERE status='PENDING' ORDER BY id ASC LIMIT 1;")
row = cursor.fetchone()
if row:
cmd_id, user_command = row
process = subprocess.Popen(
["./main", "-m", "falcon-h1-tiny-q4_k_m.gguf", "-p", f"Tags: [ON, OFF]. Task: {user_command}"],
stdout=subprocess.PIPE, text=True
)
output_text = ""
for line in process.stdout:
output_text += line
if re.search(r"ON", output_text):
GPIO.output(18, GPIO.HIGH)
process.terminate()
break
elif re.search(r"OFF", output_text):
GPIO.output(18, GPIO.LOW)
process.terminate()
break
cursor.execute("UPDATE queue SET status='DONE' WHERE id=?;", (cmd_id,))
conn.commit()
명령을 내리고 10초 정도의 지연 시간이 지나면 벽에 붙은 릴레이가 툭 소리를 내며 조명이 켜집니다. 딜레이는 생겨도 명령이 씹히거나 유실되는 일은 없습니다.
실시간 제어가 아닌 백그라운드 로그 분석이나 알림 메일 등급 분류는 유휴 자원을 털어먹기 가장 좋은 영역입니다. 정규식 몇 줄로는 걸러내기 힘든 비정형 시스템 로그들을 로컬 LLM에 먹여서 INFO, WARNING, CRITICAL 3단계로 분류하는 작업입니다. 외부 클라우드로 내보내기 찜찜한 보안 로그를 안마당에서만 처리할 수 있어 보안 면에서도 훌륭합니다.
낮에는 로그를 차곡차곡 쌓아두기만 하고, 컴퓨터가 가장 한가한 새벽 시간대에 배치로 돌려야 발열과 전력 효율을 잡습니다. 리눅스 크론탭을 열고 새벽 2시마다 작동하도록 걸어두십시오.
0 2 * * * /usr/bin/python3 /home/pi/batch_classifier.py
라즈베리 파이 1세대나 제로 모델은 프로세서가 100%로 요동치는 풀 로드 상태에서도 고작 2W 안팎의 전력만 씁니다. 일 년 내내 켜두어도 전기 요금은 3,000원이 채 안 나옵니다. 오픈AI 영수증을 마주할 때마다 느끼던 불쾌한 통증이 사라지는 순간입니다.
다만 하드웨어 한계는 명확히 알고 써야 합니다. 싱글 코어가 온종일 밤새 작동해 봐야 하루에 처리할 수 있는 최대 토큰 수는 30,000개에서 50,000개 사이입니다. 분석할 로그가 하루 만 건을 넘어가거나 데이터 적체 현상으로 지연 시간이 1분을 넘기기 시작하면 이 기기는 수명을 다한 겁니다. 그땐 미련 없이 당근마켓에서 4GB나 8GB 램이 박힌 중고 라즈베리 파이 4나 5를 주워와서 3B 규모의 고성능 소형 LLM(SLM) 체급으로 마이그레이션해야 합니다.