AI

태태코딩 - SelfAttetnion, densevector에 대하여

태태코 2026. 9. 26. 14:59
반응형

방학동안 투자운용자산가 자격증에 몰두하느라 오랜만에 다시 AI 글로 돌아왔다.

 

Q.  Skip-gram 을 하는데 Self-attention을 왜 굳이 하는가?

내가 이해한부분:

빈도수를 사용해서 희귀벡터(sparse vector)를 만들어서 임베딩 하게된다면 문제점이있다.

 

1. 벡터의 크기가 사전의 수와 같아져서 기하급수적으로 커진다.

2. 문장사이의 맥락을 이해할 수 없게된다.

 

그렇기에 한 벡터에 분포를 통해서 다의어를 사용하는 Dense vector를 사용한다는 것 까진 이해했다.

 

Q1.  맥락을 파악하기 위해서 dense vector를 만들었는데 왜 self-attetion 이라는 개념이 또 필요한가

 

여기서 중요한 부분은 Static-embedding 이다. 덴스벡터라도, 다의어를 분포로 나타냈지만, 그 문장에서 무슨 뜻으로 사용할지 나타내기 위해서는 self-attetion이 필요하다는 것이다.

 

from - gmme

1. 셀프 어텐션을 하는 이유가 '정적 벡터의 다의어/문맥 파악 한계' 때문인가? 네, 정확합니다. 스킵그램(Skip-gram) 등으로 만든 기존의 덴스 벡터는 단어 하나당 고정된 하나의 벡터값만 가집니다(Static Word Embedding). 예를 들어 "배를 타다"의 '배'와 "배를 먹다"의 '배'가 공간상에서 완전히 똑같은 벡터값을 갖게 됩니다. 여러 의미가 한 벡터에 합축되어 있다 보니, 모델 입장에서는 문장 안에서 이 단어가 어떤 의미로 쓰였는지(뭘 꺼내 쓸지) 구분할 수가 없었습니다.

셀프 어텐션은 이 문제를 해결하기 위해 등장했습니다. 주변 단어들과의 연관성(Attention Score)을 계산하여, 고정된 정적 벡터를 현재 문맥이 반영된 '동적 벡터(Contextualized Embedding)'로 업데이트해주는 역할을 합니다.

 

2. 기존 방법론(정적 벡터) 위에 셀프 어텐션이 추가된 것인가, 완전히 다른 방식인가? 구조적으로 보면 '초기 정적 덴스 벡터 생성 + 셀프 어텐션 연산'이 결합된 형태가 맞습니다. 현재 연구하시는 LLM 구조를 기준으로 실제 모델 내부의 동작 흐름을 보면 다음과 같습니다.

  • 1단계: 초기 정적 덴스 벡터 생성 (Embedding Layer) 별도의 스킵그램 모델을 따로 학습시켜서 가져오기도 했지만(초창기 방식), 최근의 모델들은 아예 내부에 '임베딩 레이어'라는 거대한 룩업 테이블(Look-up Table)을 둡니다. 단어(토큰)가 들어오면 이 테이블에서 일단 문맥이 없는 고정된 덴스 벡터를 꺼내옵니다.
  • 2단계: 문맥 반영 (Self-Attention) 꺼내온 정적 벡터들을 문장 길이만큼 늘어놓고 셀프 어텐션(Q, K, V 연산)을 수행합니다. 이때 "배"라는 단어의 벡터가 주변의 "타다"라는 단어 벡터와 정보를 섞으면서, '여기서의 배는 Ship이구나'라는 정보를 담은 새로운 문맥 반영 덴스 벡터로 변환됩니다.

즉, 과거의 덴스 벡터 생성 방식을 폐기한 것이 아니라, 1차적으로 뽑아낸 정적 덴스 벡터를 입력으로 받아 주변 단어의 맥락(Context)을 스며들게 하는 진화된 가공 과정이 추가된 것으로 이해하시면 완벽합니다.

 

Q2.  bert에 초기 스테틱벡터를 넣는다는것이, 원핫 벡터를 임베딩 벡터와 결합하여 스테틱벡터에 넣는다는 것이 맞는지, 그 임베딩 벡터를 word2vec이 아닌 랜덤을 사용한것이 bert가 맞는지

 

두 가지 모두 정확하게 짚으셨습니다! 생각하신 흐름이 실제 BERT의 동작 방식과 완벽하게 일치합니다.

질문하신 두 가지 포인트에 대해 구체적인 비하인드와 기술적 디테일을 덧붙여 드리겠습니다.

1. 원핫 벡터와 임베딩 매트릭스의 내적 = 정적(Static) 덴스 벡터

정확합니다. 입력된 단어를 원핫 벡터(One-hot Vector)로 만들고, 이를 거대한 가중치 행렬(Embedding Matrix)과 내적(Dot Product) 연산하는 것이 바로 임베딩 레이어(Embedding Layer)의 수학적 본질입니다.

  • 수학적 의미: [1, V] 차원의 원핫 벡터 $\times$ [V, D] 차원의 임베딩 행렬 = [1, D] 차원의 덴스 벡터 (V는 단어 사전의 크기, D는 덴스 벡터의 차원 수)
  • 실제 구현상의 팁: 수학적으로는 내적이 맞지만, 원핫 벡터는 자기 인덱스만 1이고 나머지는 0이기 때문에 실제 딥러닝 프레임워크(PyTorch, TensorFlow 등)에서는 연산 낭비를 막기 위해 내적을 수행하지 않습니다. 대신 행렬에서 해당 인덱스의 행(Row)만 쏙 빼오는 룩업 테이블(Look-up Table) 방식으로 처리하여 속도를 극단적으로 높입니다. (결과값은 내적과 100% 동일합니다.)

2. 왜 Word2Vec을 안 쓰고 구글은 다른 방식을 썼는가?

이 부분도 매우 날카로운 지적입니다. 초기 자연어 처리에서는 실제로 미리 학습된 Word2Vec이나 GloVe 가중치를 모델의 임베딩 레이어 초기값으로 집어넣고(Transfer Learning) 시작하는 경우가 많았습니다.

하지만 구글이 BERT를 만들 때는 기존의 Word2Vec을 가져다 쓰지 않고 다음과 같은 방식을 택했습니다.

  • 처음부터 끝까지 동시 학습 (End-to-End Learning):
  • BERT는 임베딩 행렬을 랜덤한 값으로 초기화한 상태에서 시작합니다. 그리고 엄청난 양의 텍스트(위키백과 등)로 문맥을 맞추는 사전 학습(Pre-training)을 돌리면서, 셀프 어텐션 층의 가중치뿐만 아니라 맨 앞단의 입력 임베딩 행렬 가중치까지 모델 스스로 동시에 업데이트하도록 설계했습니다. 즉, BERT의 임베딩 레이어 자체도 BERT의 학습 목표에 맞게 최적화된 고유의 정적 벡터가 된 것입니다.
  • 단어 단위(Word)가 아닌 서브워드(Subword) 단위 분절:
  • Word2Vec은 "playing", "plays"를 각각 다른 단어로 취급합니다. 반면 BERT는 WordPiece라는 방식을 써서 "play" + "##ing"처럼 단어를 쪼개서 사전을 만듭니다. 신조어나 오탈자(OOV)에 대응하기 위해서인데, 이렇게 토큰을 쪼개는 기준이 다르다 보니 기존의 단어 단위인 Word2Vec 매트릭스를 그대로 끼워 넣기가 구조적으로 맞지 않았습니다.

결론적으로 BERT의 인풋은 '서브워드 단위의 원핫 벡터를 자체 학습된 임베딩 행렬과 룩업(내적)하여 만든 정적 벡터'를 사용하며, 여기에 위치 정보(Positional)와 문장 정보(Segment) 벡터를 더해서 셀프 어텐션으로 올려보냅니다.

 

Q3.  bert의 MLM 의 과정은 self-attetion과 무슨 관계를 가지는가? 

bert에게 static-embedding을 하고, 그것을 self-attetion을 하고, 예측을 한다는 Flow는 알고있었지만, bert를 학습하기위해서 masking한다는 것은 어떤 과정인가 궁금증이 생겼다.

 

이유는 masking한 것을 예측하기위해서, 선형결합과 softmax를 통해 확률을 예측하기 때문이다.

 

왜 굳이 '비게(Masking)' 만들어 놓고 셀프 어텐션을 돌리는지, 그 이유가 바로 모델 학습의 핵심입니다.

  • 빈칸을 안 뚫으면 생기는 일 (정답 유출): 셀프 어텐션은 문장 내의 모든 단어를 동시에 바라보는 양방향 구조입니다. 만약 빈칸을 뚫지 않고 문장을 통째로 넣은 뒤 특정 단어를 예측하라고 하면, 모델은 주변 문맥을 분석할 필요 없이 그냥 자기 자신의 벡터 정보만 그대로 쏙 빼와서(컨닝해서) 정답을 맞혀버립니다.
  • 빈칸을 뚫었을 때의 효과: 모델이 정답을 베끼는 것을 원천 차단하기 위해 의도적으로 단어를 지워버립니다([MASK]). 그러면 모델은 "내 자리가 비었으니, 내가 무슨 단어인지 알려면 주변 단어들의 정보를 싹 다 긁어모아야겠다"라며 강제로 주변 문맥에 집중(Self-Attention)하게 됩니다.

즉 마스킹을 통해서 비어있게 한 후, self-attetion으로 문맥을 통해서 dense-vector들을 갱신하여 masking한 부분들을 잘 맞출 수 있게 확률 을 높히는 것이다.

 

Q4.  skip-gram과 차원축소로 만드는 덴스벡터의 초기 벡터는 뭔가 ?

 

말씀하신 두 방법론은 시작점 자체가 완전히 다릅니다.

  • 차원 축소 방식(LSA, SVD, PMI 등): 코퍼스 전체를 다 훑어서 단어 간의 동시 등장 횟수(통계)를 행렬로 쫙 만들어 놓고, 그걸 수학적으로 압축하는 '통계 기반' 방식입니다.
  • 스킵그램(Word2Vec 등): 처음에는 아무것도 모르는 바보 상태(랜덤 숫자)에서 시작해서, 수많은 예측과 오차 수정을 반복하며 학습하는 '신경망 예측 기반' 방식입니다.

스킵그램이 역전파를 시작하기 전, 맨 처음 가중치 행렬이 세팅되고 학습되는 과정은 다음과 같습니다.

  1. 무작위 가중치 행렬 생성: 중심 단어용 임베딩 행렬과 주변 단어용 임베딩 행렬 두 개를 만듭니다. 이 안에는 통계나 횟수 정보 없이 아주 작은 랜덤한 소수점 숫자들만 들어있습니다.
  2. 원핫 벡터 입력과 룩업: "배를"이라는 중심 단어가 원핫 벡터로 들어오면, 이 랜덤 행렬에서 해당 단어의 인덱스에 있는 '랜덤한 덴스 벡터'를 하나 쏙 뽑아옵니다.
  3. 예측 시도: 이 랜덤 벡터를 가지고 주변 단어가 무엇일지(예: "타다", "먹다") 예측해 봅니다. 처음엔 당연히 엉터리로 예측합니다.
  4. 역전파(Backpropagation)와 가중치 업데이트: 실제 정답(주변 단어)과 비교해서 엄청난 오차(Loss)가 발생하면, 역전파를 통해 이 오차를 줄이는 방향으로 아까의 '랜덤 숫자'들을 아주 미세하게 수정합니다.

이 과정을 수십만, 수백만 번의 문장 윈도우를 지나며 반복(Epoch)하면, 처음에 무작위였던 숫자 덩어리들이 점차 의미를 가진 훌륭한 덴스 벡터로 깎여나가는 것입니다. 즉, 스킵그램은 사전에 횟수를 세어두는 과정 없이 백지상태에서 몸으로 부딪히며(오차를 수정하며) 단어의 의미를 학습합니다.

 

 

 

이제 투운사도 끝났으니, 빅데이터 분석기사를 준비하면서 자주 글을 올려야겠다.

반응형