transformersjs로 서버 없이 브라우저에서만 동작하는 멀티모달 AI 앱을 만드는 실전 튜토리얼이다. 이미지 분류, 이미지 캡셔닝, 음성 전사 세 파이프라인을 단계별로 구현하고, 마지막에 세 기능을 하나의 미디어 분석기로 통합한다. 모든 모델은 사용자 브라우저에서 로컬로 실행되며 API 키나 서버가 필요 없다.
사용 모델
| 태스크 | 모델 | 파이프라인 타입 | 첫 다운로드 크기 |
|---|---|---|---|
| 이미지 분류 | Xenova/vit-base-patch16-224 | image-classification | ~88 MB |
| 이미지 캡셔닝 | Xenova/vit-gpt2-image-captioning | image-to-text | ~246 MB |
| 음성 전사 | Xenova/whisper-tiny.en | automatic-speech-recognition | ~78 MB |
세 모델을 모두 로드하면 첫 실행 시 약 400 MB를 다운로드한다. 이후에는 브라우저 캐시에 저장되어 오프라인에서도 즉시 로드된다.
시작하기 전 준비
Transformers.js는 모듈 방식으로 사용하므로 로컬 웹 서버가 필요하다.
# Python 3
python -m http.server 8000
# Node.js
npx serve .각 태스크는 <script type="module"> 태그를 사용하는 독립적인 HTML 파일로 구성된다.
Task 1: 이미지 분류 (Image Classification)
Vision Transformer 모델(ViT-Base)을 사용해 이미지에서 객체를 분류하고 확률값을 반환한다.
<script type="module">
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3/dist/transformers.min.js';
const classifier = await pipeline(
'image-classification',
'Xenova/vit-base-patch16-224'
);
const results = await classifier('/path/to/image.jpg', { topk: 5 });
// 결과: [{ label: 'golden retriever', score: 0.942 }, ...]
</script>결과는 레이블과 확률 점수 배열로 반환된다 (예: "golden retriever, 94.2%").
Task 2: 이미지 캡셔닝 (Image Captioning)
ViT-GPT2 모델로 이미지를 설명하는 자연어 문장을 생성한다.
<script type="module">
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3/dist/transformers.min.js';
const captioner = await pipeline(
'image-to-text',
'Xenova/vit-gpt2-image-captioning'
);
const result = await captioner('/path/to/image.jpg');
// 결과: [{ generated_text: "a dog is playing on a tennis court." }]
</script>이미지 분류와 캡셔닝은 모델 가중치를 공유하지 않으므로 Promise.all로 두 추론을 병렬 실행하면 시간을 절약할 수 있다.
Task 3: 음성 전사 (Speech Transcription)
Whisper 아키텍처 기반 whisper-tiny.en 모델을 WebAssembly로 실행한다. Web Audio API로 마이크 입력을 받아 Whisper 필요 포맷(16kHz, Float32Array)으로 변환한다.
async function decodeAudio(arrayBuffer) {
const ctx = new AudioContext({ sampleRate: 16000 }); // Whisper 필수 샘플레이트
const audioBuffer = await ctx.decodeAudioData(arrayBuffer);
return audioBuffer.getChannelData(0); // Float32Array
}주요 설정:
sampleRate: 16000— Whisper 모델의 필수 입력 형식chunk_length_s: 30— 30초 단위 처리, 5초 오버랩으로 경계 단어 누락 방지- 지원 포맷: WAV, MP3, MP4, OGG, FLAC
const transcriber = await pipeline(
'automatic-speech-recognition',
'Xenova/whisper-tiny.en'
);
const result = await transcriber(float32Array, {
chunk_length_s: 30,
stride_length_s: 5
});최종 통합: 멀티모달 미디어 분석기
세 파이프라인을 하나의 앱(media-analyzer.html)으로 통합한다. 페이지 열자마자 세 모델이 병렬 로드를 시작하며, 모두 준비된 후에야 UI가 활성화된다.
// 세 파이프라인 병렬 로드
const [classifier, captioner, transcriber] = await Promise.all([
pipeline('image-classification', 'Xenova/vit-base-patch16-224'),
pipeline('image-to-text', 'Xenova/vit-gpt2-image-captioning'),
pipeline('automatic-speech-recognition', 'Xenova/whisper-tiny.en')
]);앱 동작 방식:
- 이미지 업로드 시: 분류와 캡셔닝을 병렬 실행 → 동일 결과 그리드에 표시
- 마이크 입력 시: 음성 전사 실행 → 결과 카드 표시
- 헤더 배지: 각 모델의 로드 완료 상태를 실시간 표시
핵심 패턴 정리
| 패턴 | 설명 |
|---|---|
Promise.all 병렬 로드 | 독립적인 모델/추론은 항상 병렬로 실행해 대기 시간 단축 |
| 브라우저 캐시 활용 | 첫 다운로드 후 재실행 시 오프라인에서도 즉시 사용 |
AudioContext sampleRate: 16000 | Whisper 모델의 필수 입력 샘플레이트 |
| 로컬 서버 필요 | ES 모듈 방식이므로 file:// 프로토콜로는 동작하지 않음 |
참고 자료
- Multimodal Browser AI with Transformers.js for Images and Speech — MachineLearningMastery (2026-06-10)