Reactive Programming [Coroutine]
메모리의 종류 및 동작 과정
[ 1. Reactive Streams ← Servlet ]
먼저 Reactive Streams는 동기식 웹 요청 방식에서 Servlet과 유사한 개념입니다.
Servlet (InputStream / OutputStream)
- stream(통로)를 통해 클라이언트와 서버간의 통신
- 문제점: 보내는 쪽(Client/DB)이 받는 쪽(Server)보다 빠르면 터진다
- 서버 메모리에 데이터가 계속 쌓이다가
OutOfMemoryError로 서버 다운!
단어 자체에 Streams가 들어가는 만큼 Servlet과 똑같이 웹 통신의 요청 표준 인터페이스 역할을 합니다.
<Tomcat and Spring>
기존 Servlet이
- 웹 서버(Tomcat)가 HTTP 요청을 받아서
HttpServletRequest라는 객체나
InputStream을 애플리케이션(Spring)에 던져줬었던 것처럼
<Netty and Spring Webflux>
Reactive Streams는
- 웹 서버(Netty)와 애플리케이션(Spring Webflux)를 연결하는 소켓의 개념으로
- Netty에서
Publisher<DataBuffer>(byte[]를 담은 구조)로 보내고 - 패킷 버퍼와 유사한 느낌!
- 비동기적으로 Spring Webflux는
Publisher<Object>문자 또는 객체로 return합니다
“Breaking Thread가 없다”
- 요청 접수: 스레드 A가 요청을 받아서 DB에 쿼리를 날리고
- 스레드 해방: 스레드 A는 "응답 오면 깨워줘" 하고 바로 다른 사람의 요청을 받을 수 있습니다
- 핵심 : 요청을 보내는 스레드는 보내자 마자 다른 활동 가능!
- 응답 처리: DB에서 데이터가 오면, 놀고 있던 아무 스레드가 어? 아까 그 요청이네? 내가 응답 보내줄게" 하고 처리합니다
- 핵심 : DB에서 온 return을 보낸 스레드가 아니어도 처리 가능!
관계형 DB에서 reactive programming을 가능하게 해주는 데이터베이스 접근 API
기존 JDBC(blocking API)에서 제공하던
- caching
- lazy loading
- background writing
사용 불가
[ 2. CoroutineCrudRepository ]
[ 3. ReactiveMongoRepository ]
- TF-IDF : 많이 들어가 있으면 해당 문서의 중요도 가중치 부여
- tf : 해당 단어에 대해서 몇개가 있는가?
- idf : 전체 문장에서 해당 단어가 몇 문장에 들어있는가?
- Learned Embedding : LLM 이용 ⇒ 사람이 분석 불가능
- 벡터의 차원이 매우 낮고
- 벡터 내 각 원소들을 밀도 있게 사용
[ 3. Transformer & BERT ]
3.1 분석
- Dense Embedding : 입력 텍스트 전체 의미를 하나의 고차원 벡터로
- Sparse Embedding: 입력 문서 안에 어떤 단어가 실제로 등장했는지
- one-hot의 방식
3.2 구조 (Transformer - BERT)
Transformer
⇒ 트랜스포머는 인코더-디코더 FFN에서 Self-Attention 메커니즘을 사용하는 딥러닝 모델
- Input Embedding
- Positional Encoding
- positional Encoding vector (위치의 개념) + embedding vector (의미)
- sin(1/10000^((2*0)/4)) = sin(1) = 0.84
- cos(1/10000^((2*1)/4)) = cos(0) = 1
- sin(1/10000^((2*2)/4)) = sin(0) = 0
- cos(1/10000^((2*3)/4)) = cos(0) = 1
am : [0.84, 1, 0, 1]
- (Masked) Multi-head attention
- Query, Key, Value
Attention : 글의 문맥 이해
Masked : am을 학습할때, a와 student의 결과는 모르게
Multi-Head : 각각을 서로 다른 행렬연산 이후 concate(옆에 붙이기)
- Residual Connection
Layer Architecture
- LayerNorm (LN)
- 입력을 정규화
- Multi-Head Self-Attention
- 입력 문장의 토큰들이 서로의 관계를 계산
- Residual Connection + Dropout
- 입력과 출력 벡터를 더해(잔차 연결[ResNet]-shortcut) 정보 손실 방지
- Dropout으로 일부 뉴런 끊어서 과적합 방지
- LayerNorm (LN)
- Feed-Forward Network (FFN)
- 2층 MLP(FC+비선형) 구조, 활성함수는 GELU
- ReLU: 0 이하는 다 날려버림
- GELU: 0 근처의 값을 “확률적으로” 통과
- 입력 토큰 벡터를 비선형 변환해서 더 복잡한 패턴 학습
- Residual Connection + Dropout
BERT (Bidirectional Encoder Representations from Transformers)
- 양방향 인코더
- Encoder는 지금 넣는 데이터를 숫자화 + 문맥 임베딩(가까운 지를 분석해서 토큰의 숫자를 변경하는 함수
- Decoder는 다음에 올 토큰을 예측하는 = 가장 확률이 높은 토큰을 이어서 연결하는 함수
기존 Language model
Encoder는 전체 데이터에 대한 임베딩이 한번에 나오고
→ BERT는 encoder-only
Decoder는 left to right로 해석
→ GPT는 decoder-only
BERT-IMBEDDING
- 토큰 임베딩(token embedding): 문장 쪼개서 토큰화
- 문장 시작 부분에 [CLS]
- 문장 끝 부분에 [SEP]
- Wordpiece : 어휘 사전에 있는 단어가 나올때 까지 단어를 계속 분할
ex) pretraining → pre, ##train, ##ing
⇒ 토큰 임베딩 result : [E[CLS], EI, Elove, Eyou, E[SEP], EI, Elove, Eice-cream, E[SEP]]
- 세그먼트 임베딩(segment embedding): 문장을 구분
- 서로 다른 문장에 다른 벡터 값
⇒ 세그먼트 임베딩 result : [ EA, EA, EA, EA, EA, EB, EB, EB, EB ]
- 위치 임베딩(position embedding): 위치에 따라 번호
⇒ 위치 임베딩 result: [ E0, E1,E2, E3, E4, E5, E6, E7, E8 ]
- 최종 임베딩 : 3개 다 더하기
BERT-PRE LEARNING
- MLM(Masked Language Model) : 마스킹으로 학습
- NSP(Next Sentence Prediction) : 다음 문장인지 예측
추가 성능
참고
2) 도메인이 특수해서 성능이 부족한 경우
- 도메인 적응 사전학습(DAPT/TAPT) 으로 MLM만 추가로 돌려보면 도움이 될 수 있어요.
- 예: 의학/법률/캠퍼스 공지처럼 용어가 특이함
- 절차: 도메인 코퍼스에 대해 Masked LM 몇 epoch → 그 뒤 임베딩/파인튜닝
- NSP는 요즘 거의 쓰지 않음(효과 미미/대체 목표 존재)
3) 검색·유사도 성능을 더 끌어올리고 싶다면
- MLM/NSP가 아니라 아래 중 하나를 고려:
- 대조학습 파인튜닝(in-batch negatives, InfoNCE, triplet loss)
- 무라벨이면 SimCSE(unsupervised) 로 간단 적응
↳ 쌍/삼중 데이터가 있으면 최고
- 포인트: 임베딩 품질은 학습 목표(contrastive)가 좌우, MLM/NSP는 직접적 도움 적음.
- DAPT/TAPT(MLM) = 도메인 적응용 추가 사전학습
- Contrastive/SimCSE = 임베딩 전용 미세조정
- 태스크 파인튜닝 = 라벨 달린 다운스트림 작업(분류/QA/NER 등)