들어가며
우리 서비스의 핵심 기술 중 하나는 '수어 인식'이다. 수어를 인식하여 텍스트로 결과를 받아볼 수 있어야 했다.
처음에는 Ai Hub에 수어 데이터셋이 있길래 그거를 활용하면 어떨까 하는 생각이었지만, Ai Hub에는 우리가 원하는 단어와 문장이 없었다. 우리는 '감기' 진료 시에 환자가 자주 사용하는 단어가 필요했다. 예를 들어, "목이 아파요", "머리가 아파요" 와 같은 형태의 문장을 만들 수 있어야 했다.
결국 우리는 직접 데이터셋을 구축하기로 했다. 감기 진료 시에 환자가 많이 사용할 것 같은 단어 10개를 정했으며, 국립국어원 한국수어사전 에서 단어를 찾아 직접 수어를 하면서 영상을 촬영했다. 우리 팀원은 3명이었기 때문에, 3명이서 각 단어를 10번 촬영하여 약 300개의 데이터셋을 구축했다.
데이터 전처리
1) Google MediaPipe
그 다음에는 Google Mediapipe Holistic을 활용해 구축한 수어 영상으로부터 상반신 좌표를 추출하였다. MediaPipe Holistic은 얼굴, 손, 신체의 주요 키포인트를 통합 추출하는 데 효과적이다.
- 추출 부위 : 전신 포즈, 왼손, 오른손 좌표를 추출한다.
- 데이터 크기 : 이는 각 프레임 당 총 258차원으로 구성되며, 하나의 1초 영상은 (30, 258) 크기의 numpy 배열로 저장된다.
2) 프레임 정규화
영상은 기본적으로 정지된 이미지들을 아주 빠르게 연속해서 보여주는 원리로 작동한다. 이 정지된 이미지 한 장을 프레임(Frame)이라고 부른다.
- 프레임 : 영상의 가장 기본적인 단위로, 시간의 한 순간을 포착한 개별 이미지이다.
- 프레임 레이트 (FPS, Frame Per Second) : 1초 동안 몇 개의 프레임이 지나가는지를 나타내는 단위이다.
영상마다 길이가 다르고, 이는 곧 프레임 수가 다르다는 것을 의미한다. 딥러닝 모델에 입력할 때에는 데이터 크기가 같아야 학습이 안정적으로 이루어지기 때문에, 모든 데이터를 동일한 길이인 30프레임으로 통일하는 과정을 진행했다.
3) 데이터 증강 (Augmentation)
환경이나 속도, 각도에 따라 다양하게 수어를 할 수도 있다. 또한 우리의 데이터셋은 턱없이 적었다. 모델이 다양한 입력 상황에 대응할 수 있도록 데이터 증강을 진행했다.
- 가우시안 노이즈 (Gaussian Noise) : 손 떨림과 영상 흔들림을 재현하기 위해 좌표에 노이즈를 추가하였다.
- 시간 스케일링 (Time Scaling) : 사람마다 동작 속도가 천차만별이기 때문에 속도를 0.8-1.2 배로 변형하였다.
모델 설계 및 구조
수어 동작의 시퀀스 (시간적 흐름)을 분석하는 데 적합한 모델을 찾기 위해 4가지 딥러닝 모델을 비교 테스트했다.
1) 1D-CNN (1-Demensional Convolutional Neural Network)
시계열 데이터를 프레임 단위로 처리하여 공간적 패턴을 추출하는 능력을 테스트하기 위해 베이스모델로 선정했다.
CNN(Convolutional Neural Network)은 보통 이미지 처리, 즉 2차원 데이터에 주로 사용된다.

위 다이어그램은 일반적인 CNN 아키텍처로 특징 추출부(Feature Extraction)와 분류부(Classification)로 나뉘어져 있다.
먼저, 특징 추출부에서는 컨볼루션 레이어(Convolution)와 최대 폴링 레이어(Maxpooling)의 쌍이 반복된다.
- Convolution Layer : 필터가 입력 위를 훑으며 지역적 특징을 추출한다.
- Maxpooling : 특징 맵의 크기를 줄여 데이터의 차원을 감소시키고, 가장 강한 특징만 남겨 노이즈를 제거하여 과적합을 방지한다.
-> 특징 추출을 반복할 수록 입력 데이터는 더 작고, 더 깊고, 더 추상적인 특징 표현으로 변환된다.
특징 추출부를 통과한 데이터는 분류를 위해 평탄화되고 완전 연결 레이어(Fully Connected Layer)로 전달된다. 특징 추출부의 최종 출력을 1차원 벡터로 평탄화(flatten) 한 후에 이 벡터를 입력으로 받아 각 노드가 모든 이전 노드와 연결된다. 여러 개의 Fully Connected Layer를 거치면서 추출된 특징들을 종합하고, 최종적으로 클래스를 예측할 수 있도록 변환한다.
일반적인 CNN과 1D-CNN의 차이는 커널이 이동하는 방향과 입력 데이터의 차원이다. CNN은 주로 2차원 공간을 분석하고, 1D-CNN은 1차원 시퀀스의 시간 또는 길이를 분석한다.

다음 사진은 1D-CNN을 나타낸 것이다. 1D-CNN은 그냥 일반적인 CNN과 다르게, 1D-CNN은 시간의 흐름이 있는 데이터를 다룬다. 네모난 모양의 CNN의 필터와 다르게 1D-CNN은 막대 모양이며, 이동 방향 역시 시간이 흐르는 방향으로 움직인다.
2) LSTM (Long Short-Term Memory)
수어 동작은 단순한 정지 자세가 아닌, 시간이 흐름에 따라 손과 팔이 연속적으로 움직이는 시퀀스 데이터이다. 1D-CNN은 이러한 동작의 순서와 장기적인 문맥을 반영하는데 한계가 있을 수 있다고 생각했다.
그래서 LSTM 모델을 설계해보았다. LSTM은 순환 신경망(RNN)의 한 종류로, 시계열 데이터를 처리하기 위해 고안되엇다. 특히, 게이트라는 매커니즘을 통해 데이터의 장기적인 정보를 기억하거나 잊어버리는 능력이 있어, 긴 시퀀스에서도 중요한 정보를 놓치지 않고 학습할 수 있다.

구조를 조금 살펴보면, LSTM의 핵심은 셀 상태(Cell State)라는 내부 메모리 통로와 이 통로를 제어하는 3가지 게이트이다.
1. 셀 상태 (Cell State)
셀의 상단을 가로지르는 수평선으로 데이터의 장기적인 기억을 운반하는 역할을 한다.
2. 3가지 게이트
각 게이트는 시그모이드 함수를 사용하여 0과 1 사이의 값을 출력함으로써, 정보의 흐름을 조절하는 밸브 역할을 한다.
- 망각 게이트(Forget Gate) : 현재의 입력과 이전의 은닉 상태를 보고, 셀 상태에서 어떤 정보를 잊어버려야 하는지 결정한다.
- 입력 게이트 (Input Gate): 현재 시점의 새로운 정보 중 무엇을 셀 상태에 저장할지 결정한다.
- 출력 게이트 (Output Gate) : 셀 상태를 기반으로 현재 시점의 은닉 상태를 결정하고 외부에 출력한다.
3) CNN-LSTM 하이브리드 구조
이번에 세 번째 모델은 1D-CNN의 지역적 특징을 잘 추출하는 능력과 LSTM의 시계열 흐름을 학습하는 능력을 모두 활용하여 수어 동작을 분석해 보고자 선택되었다. 원리는 다음과 같다.

- CNN 계층 : 입력된 키포인트 시퀀스에서 먼저 1D-CNN을 사용하여 각 프레임 또는 짧은 구간의 공간적 특징을 추출한다.
- LSTM 계층 : CNN을 통과하여 압축되고 정제된 특징들을 LSTM에 입력한다. LSTM은 이 추출된 특징들의 시간적 순서와 흐름을 학습한다.
4) Bi-LSTM (Bidirectional LSTM)
Bi-LSTM은 앞서 테스트했던 단방향 LSTM의 한계를 극복하기 위해 선택되었다. 수어 동작은 시작부터 끝까지의 전체적인 움직임과 문맥이 중요하다. 단방향 LSTM은 과거에서 미래로의 정보만을 학습했지만, Bi-LSTM은 양방향 문맥을 모두 반영한다.

- 정방향 레이어 (Forward Layer) : 시퀀스를 시간 순서대로 처리한다. 과거의 문맥을 학습하는 흐름이다.
- 역방향 레이어 (Backward Layer) : 시퀀스를 시간 역순으로 처리한다. 미래의 문맥을 학습한다.
모델 학습 파라미터
4가지 모델에 공통적인 학습 파라미터를 적용했다.
학습 파라미터란, 딥러닝 모델이 학습을 진행하는 방식과 속도를 제어하는 일종의 학습 규칙이다. 이 규칙들을 잘 설정해야 모델이 가장 효율적으로 정답을 찾아가고, 실제 환경에서도 잘 작동할 수 있다. 이 것을 하이퍼파라미터라고 한다.
| 항목 | 값 | 설명 |
| Optimizer | Adam(lr = 0.001, weight_decay = 1e-4) | 안정적인 수렴을 위한 최적화 함수 |
| Loss Function | CrossEntropyLoss | 다중 클래스 분류 문제에 사용되는 표준 손실 함수 |
| Scheduler | CosineAnnealingLR(T_max=10) | 학습률을 점진적으로 감소시켜 안정적 수렴과 과적합 완화를 유도 |
| Epoch | 30 | 전체 데이터셋을 30번 반복 학습 |
| Batch Size | 32 | 메모리 효율과 학습 속도를 고려 |
| Split 전략 | Stratified Split | 클래스 비율이 Train/Validation/Test 세트 간 동일하도록 보장하여 불균형 문제 방지 |
결과
| 모델 | 구조 | Test Accuracy |
| 1D-CNN | Conv1D 2층 | 91.67 |
| LSTM | 단방향 2층 | 87.50 |
| CNN-LSTM | 하이브리드 구조 | 70.00 |
| Bi-LSTM | 양방향 3층 | 98.90 |
-> 다음과 같은 결과를 토대로 Bi-LSTM 모델을 채택하였다.
'프로젝트 > handDoc' 카테고리의 다른 글
| [Spring] 공공데이터 API를 DB에 저장하기 (0) | 2025.10.13 |
|---|---|
| [AI] 구음장애 환자 발화 데이터셋을 활용한 Whisper 모델 파인튜닝 (0) | 2025.09.21 |
| [Spring] WebRTC 정리 - 시그널링 서버와 STUN/TURN 서버 (0) | 2025.09.08 |
| [Spring] NAVER CLOVA CSR 적용하여 음성을 텍스트로 변환하기 (0) | 2025.08.15 |
| [Spring] MongoDB Atlas 설정하기 (0) | 2025.08.04 |