인공지능 공부

[On-Device AI] 웹브라우저에서 LLM 돌리기 (feat. 번역 회화 앱 만들기)

koh1018 2025. 8. 6. 10:00
반응형

 

최근 몇년전부터 꾸준히 영어 공부를 하고 있다. 하지만 회화실력이 크게 발전하지 않는 것 같고 영어 외에 다른 언어에도 관심이 생겨서 효과적인 외국어 회화 공부법을 찾아보고 있었다.

 

https://youtu.be/aEEqcA5ZJK8?si=MyxUsoTrMVTGojSI

그 중 가장 괜찮다고 생각이 든 공부법이 있었는데 위 영상에서 소개하듯이 일상의 대화 문장을 번역하고, 저장해두고, 암기하는 방식이었다.

 

하지만 위 영상에서 소개하는 것처럼 매번 번역기를 켜고 따로 저장하고 하는 일이 불편하다 생각해 아예 회화 공부 앱을 만들기로 했다.

 

 

 


 

 

1. 구글 번역기, 파파고 등 번역 API 사용

물론 기존에 나와있는 번역기 서비스들의 API를 이용할수도 있었다.

하지만 지금 목적은 무료로 이용가능한 서비스였기에 비용이 발생할 수 있는 번역 API는 제외하였다.

 

 

 

2. 온디바이스에서 경량 LLM 이용하기

최근 ChatGPT로 번역을 돌려보는 일이 많았다. 특히 일본어 번역 품질이 굉장히 우수했었다.

이를 통해 LLM의 번역 품질이 나쁘지 않다는 것을 알게 되었어서 경량 LLM의 번역 품질을 테스트해보기로 하였다.

 

먼저 테스트해보기로 한 모델은 Google의 Gemma 2B 모델이었다.

기본 모델과 it 모델이 있는데 it는 Instruction-Tuned의 약자로서 사용자의 명령어(Instruction)에 반응하도록 추가로 학습된(instruction-tuning) 모델이다. 따라서 번역용으로 사용하기에 기본 모델보다 더 적합하다고 판단하였다.

 

그래서 이 Gemma-2b-it 모델을 transformers.js를 이용해서 웹브라우저 위에서 돌리려고 하였다.

transformers.js는 브라우저나 Node.js 환경에서 직접 트랜스포머 모델을 사용할 수 있게 해주는 오픈소스 라이브러리이다.

WebAssembly(WASM)를 기반으로 동작한다고 한다.

 

하지만 위와 같이 Can't create a session 에러가 발생했는데, 이는 웹브라우저가 할당할 수 있는 것보다 큰 메모리를 사용하려고 해서 그런 것이었다. 실제로 4GB 이상 사용중이었다.

 

 

 

3. Hugging Face Spaces에서 무료 CPU를 이용해 LLM을 돌리고 API 형태로 번역 결과물을 받기

우선 찾아본 바로 평생 GPU를 무료로 빌려주는 클라우드 서비스는 없었다.

따라서 차선책으로 속도가 많이 느리더라도 무료 CPU를 제공해주는 클라우드 서비스를 이용하고 경량화된 모델을 사용하는 쪽으로 진행해보았다.

 

순위 모델명 번역 품질 무료
API/Spaces
지원
모바일/PC
호환
예상 출력 시간
(1~3줄 번역)
특징 및 비고
1 Aya Expanse 8B 매우 높음 O O 1~3초 최신 LLM, 자연스러운 구어체, Spaces 공식 지원, 23개 언어, 실제 벤치마크 1위권
2 Aya 23-8B 매우 높음 O O 1~3초 Expanse와 유사, 23개 언어, Spaces 공식 지원, 자연스러운 번역
3 NLLB-200 Distilled/Small 높음 O O 1~2초 MT 특화, 200개 언어, FLORES 벤치마크 우수, Spaces 다수
4 MarianMT (Opus-MT) 중~상 O O 1~2초 경량, 실사용 앱 다수, 다양한 Spaces, MT 특화
5 Qwen 1.8B~4B 중~상 O O 1~2초 경량 LLM, 다국어 지원, Spaces 다수, 자연스러운 번역
6 Gemma 2B 중~상 O O 1~2초 경량 LLM, 일본어 특화 버전, Spaces 다수, 자연스러운 번역

그렇게 몇 가지 기준에 따라 모델을 찾아보았고 위 모델들이 후보에 올랐다.

가장 중요한건 번역 품질이었고 그 다음은 속도였다.

 

그러나 이후 Aya Expanse 8B 모델을 써보았지만 확실히 8B 모델은 무료 CPU 환경에서 돌리기에는 무리가 있어보였다.

번역품질이 그렇게 좋은 것도 아니었고 가장 큰 문제는 시간이 몇 초 수준이 아니라 몇 분이었다는 것이다.

 

그래서 더 경량 모델인 Qwen1.5-1.8B-Chat을 사용해보았다.

하지만 여전히 너무 느렸다. (약 1~2분)

 

더 빠른 성능을 내보기 위해 양자화된 버전을 써보기로 했다.

 

양자화란?

모델의 가중치(weight)와 활성화값(activation)을 고정밀 데이터(32비트 부동소수점)에서 저정밀 데이터(8비트, 4비트, 2비트 정수)로 변환하는 모델 압축 및 최적화 기술을 의미한다.
복잡한 소수점 정보를 가진 모델을 '덜 정밀한 숫자'로 바꿔서 크기를 줄이고 빠르고 효율적으로 쓰도록 만드는 기술이다.
즉, 정확도를 조금 포기하는 대신 속도를 향상시키는거다.

 

또 추가적으로 한번에 여러 다국어 번역을 위해 각각 API를 독립적으로 호출하던 것을 한번에 프롬프트에 다 뽑고 API 호출 한번으로 해결되게 해서 부하를 최대한 줄여 속도를 높일 수 있게 시도해보았다.

이러면 성능에 변화가 있을 수 있기에 일단 테스트해보고 JSON 형태로 잘 추출해보기로 하였다.

정리하면 아래와 같다.

 

1. 더 경량화된 모델 사용

2. 양자화된 모델 사용

3. 여러 개의 API호출(각각 프롬프트 결과를 받음)하던 것을 하나의 API호출(한번의 프롬프트로 여러 결과를 받음)로 변경

 

 

 


 

 

하지만 양자화된 GGUF(Georgi Gerganov Unified Format)은 지금까지 모델 갈아끼우던 것과 다르게 구현해야했다.

GGUF는 " Georgi Gerganov의 통합 포맷"으로 해석하면 되는데 앞에 Georgi Gerganov가 핵심 라이브러리를 만든 개발자의 이니셜이라고 한다.

이는 대형 언어 모델(LLM)을 효율적으로 저장하고 배포하기 위한 모델 파일 포맷으로 다양한 프레임워크(PyTorch, TensorFlow 등)에서 학습된 모델을 llama.cpp, LM Studio, Ollama 등 여러 환경에서 쉽게 쓸 수 있도록 만들어진 표준화된 바이너리 파일 형식이다.

 

이 GGUF는 지금까지 써온 모델과는 완전히 다른 방식으로 작동하기에 기존 transformers.js 라이브러리로 작동시킬 수 없었고 대신 llama-cpp-python 라이브러리를 사용해야했다.

 

기존 transformers.js 방식은 모델, 토크나이저, 설정 파일이 각각 별도로 존재하고 transformers가 이 부품들을 조립해서 실행하는 방식이었다. 하지만 GGUF는 모델, 토크나이저, 설정 등이 하나의 파일(.gguf)로 통합되어 가볍고 빠른 단일 머신이다.

GGUF는 llama.cpp 생태계 전용 포맷이고 transformers.js는 GGUF를 지원하지 않기에 라이브러리를 바꿔써야했다.

 

 

 

빌드 문제 발생

Building wheels for collected packages: llama-cpp-python
Building wheel for llama-cpp-python (pyproject.toml): started

하지만 빌드 문제가 발생했다. 위 상태에서 멈춰있었다.

이를 해결하기 위해 -DLLAMA_CMAKE_BUILD_PARALLEL=ON을 통해 병렬 실행을 ON으로 하자 Building wheel for llama-cpp-python (pyproject.toml): still running... 까지 뚫렸다.

그러나 그래도 빌드 에러가 떠서 결국 직접 whl 파일을 데스크탑에서 빌드하기로 하였다.

-> 우분투에서 20초 안에 끝났다..

 

remote: -------------------------------------------------------------------------
remote: Your push was rejected because it contains binary files.
remote: Please use https://git-lfs.github.com/ to store binary files.
remote: See also: https://hf.co/docs/hub/repositories-getting-started#terminal
remote:
remote: Offending files:
remote:   - llama_cpp_python-0.3.11-cp39-cp39-linux_x86_64.whl (ref: refs/heads/main)
remote: -------------------------------------------------------------------------
To https://huggingface.co/spaces/everydayconversify/conversify-model
 ! [remote rejected] main -> main (pre-receive hook declined)
error: failed to push some refs to 'https://huggingface.co/spaces/everydayconversify/conversify-model'

그래도 또 문제가 생겼는데 git push할 때 위와 같은 에러가 뜬 것이다.

이 문제는 Git LFS 때문이었다.

 

Git LFS (Large File Storage)는 Git이 대용량 파일을 효율적으로 다루기 위한 확장 기능이다. Hugging Face는 저장소(repository)가 불필요하게 커지는 것을 막기 위해 일정 크기 이상의 바이너리 파일은 반드시 Git LFS를 통해 올리도록 강제하고 있었다. 필자의 .whl 파일이 이 규칙에 걸린 것이었다.

 

하지만 그래도 문제는 계속 발생했다. Python 버전이 안맞고 등등..

결국 로컬에서 Dockerfile을 수정해 우분투 이미지를 불러오고 거기서 wheel 빌드용 환경세팅을 하고 Dockerfile을 빌드한 후, 컨테이너 접속해서 Spaces 전용 wheel을 빌드했다.

그리고 wheel 파일을 컨테이너 밖으로 꺼냈다. 그런 후 Spaces 실행용 Dockerfile에 넣고 설치했다.

 

그렇게 모두 완료됐나 싶었는데... 성능이 너무 좋지 못했다.

 

 

 

4. 다른 모델을 써보자

Upstage에 SOLAR모델이 있다.

번역 성능을 높여보고자 이걸 써보기로 했다. 그러나 10.4B는 확실히 시간이 너무 오래 걸렸다. 그래서 2.8B 같은것도 찾아봤는데 지원종료된 것 같았다. TinySOLAR 모델은 코딩특화라 포기했다.

 

이후 여러 모델을 갈아껴보며 시행착오가 있었다.

lmstudio-community/gemma-2-2b-it-GGUF의 gemma-2-2b-it-Q4_K_M.gguf 쓰기로 함. → 4분 걸림
Qwen/Qwen2-0.5B-Instruct-GGUF의 qwen2-0_5b-instruct-q4_k_m.gguf 쓰기로 함. → 1분 걸림
Qwen/Qwen2-0.5B-Instruct-GGUF의 qwen2-0_5b-instruct-q2_k.gguf 쓰기로 함. → 오히려 더 오래걸리고(1분 30초) 번역 품질도 이상함.

 

Q2_K, Q4_K_S, Q4_K_M 등 양자화 표기가 있는데 이는 Q[숫자]_K[_S/_M/_L] 형태로 표기된다.

 

Q는 Quantization 으로 뒤에 붙은 숫자가 비트수를 의미한다. 즉, 한 가중치를 몇 비트로 저장하는지를 의미하는 것이다.

K는 K-quantization을 의미하며 최적화된 블록 단위 양자화를 의미한다고 한다.

_S, _M, _L은 각각 Small, Medium, Large의 약자다.

 

표기 의미 요약 특징/용도
Q2_K 2비트 K-quantization 가장 작은 용량, 품질 손실 큼
Q3_K_S 3비트, K-quant, Small 초경량, 품질 손실 큼
Q4_K_S 4비트, K-quant, Small 속도 최우선, 품질 중간
Q4_K_M 4비트, K-quant, Medium 속도-품질 균형, 실사용 추천
Q5_K_S 5비트, K-quant, Small 품질 우선, 속도 약간 저하
Q5_K_M 5비트, K-quant, Medium 품질 최우선, RAM 여유 필요
Q6_K 6비트 K-quantization 최고 품질, 속도 느림

Q4_K_M (4비트, Medium 옵션)이 속도와 품질의 균형이 가장 좋아 실사용에 가장 많이 추천된다고 한다.

 

그러나 모두 속도가 너무 느렸다. 아무래도 무료 CPU 환경에서는 한계가 있어보였다.

 

 

 

5. 돌고돌아 웹브라우저 내에서 MarianMT 이용하기로 함

MarianMT 모델은 Helsinki-NLP에서 개발되었고 Marian C++ 라이브러리를 사용해 빠른 번역을 지원한다고 한다.

가볍고 번역 특화 모델이었다.

필자는 그 중에서도 양자화된 모델을 사용하여 속도와 부담을 낮추기로 하였는데, Xenova 모델은 Transformers.js와 같은 웹 환경에 맞게 최적화되고 양자화되어 있어 일반적으로 더 가볍고 빠르게 작동할 수 있었다.

 

그렇게 완성하게 되었다..

 

 

 

6. 해치웠나..?

이제 영어 번역은 만족할만한 성능이 나왔다.

하지만 일본어 번역 성능이 안좋았다.

 

그래서 이후 시도한 방법들이다.

onnx-community/Qwen2.5-0.5B-Instruct로 대체하기로 하고, 이걸 이용해 일본어의 한국어 발음까지 뽑아내도록 기능추가하였다. → 해보니 크기가 너무 커서(거의 2GB) 창 로딩이 오래 걸려서 포기하였다.
Xenova/Qwen1.5-0.5B-Chat로 바꾸기 → Chat으로 학습시킨 모델이라 프롬프팅을 어떻게해도 JSON형태로 잘 안나왔다.
facebook/nllb-200-distilled-600M로 바꾸기 → 일본어 발음기능은 포기해야하지만 일단 이대로 진행

 

 

 

https://conversify-official.vercel.app/

 

Conversify - 회화 학습 도우미

일상 대화 문장을 입력하고 영어와 일본어로 번역해보세요 한국어 문장 입력 일상 대화에서 사용할 수 있는 문장을 1-3줄로 작성해주세요

conversify-official.vercel.app

그렇게 만들어본 완성본이다.

 

부족한 부분이 많고 경량 모델의 한계 + 무료에만 집착하다보니 원하는 성능을 못뽑은 것이 있지만 시행착오를 겪으면서 웹브라우저에서 LLM을 돌려보는 경험도 해보고 다양한 모델을 직접 사용해봤다는 점에서 의의가 있는 것 같다.

많이 배웠고 재밌는 경험이었다.

반응형