본문 바로가기
인공지능

🤗 Huggingface GPT-2로 문장을 토큰 임베딩해보자

by judy@ 2023. 8. 23.

영어 부제: how to get word embedding vector in GPT-2

 

이 포스팅은 허깅페이스 API를 사용하여, 간단한 "영어" 문장을 GPT-2를 사용하여 임베딩하는 내용으로 구성되어 있다.

 

목차

     

    참고) 여기서의 임베딩은 토큰 임베딩을 말하며, 자연어로 이루어진 문장을 임베딩 공간에 표현하면 길이는 다르지만 의미가 비슷한 문장인지를 거리 계산 방식(유클리디안, 코사인, 자카드 등)을 통해 확인해볼 수 있다는 이점이 있다.

     

    1. 라이브러리 설치

    토큰 임베딩을 위해서는 python 작업 환경에, huggingface 및 torch 라이브러리를 설치해주어야 한다. 나의 경우 OSX (m2 pro) 유저로 아래 명령어를 통해 라이브러리 설치가 가능하지만, windows, linux 유저는 다를 수 있으니 꼭 공식 도큐먼트를 참고하여 설치해야 한다.

     

    나의 환경

    - macOS Ventura 13.5
    - python 3.10
    - transformers 4.32.0
    - torch 2.0.1

     

    1. huggingface api 설치 방법 (huggingface 공식 도큐먼트)

    pip install transformers

    2. torch 설치 (pytorch 공식 도큐먼트)

    pip install torch torchvision torchaudio

     

    2. 모델 불러오기

    일반적으로 언어모델마다 다른 토크나이저를 사용하고, embedding space도 다른데, 이번 포스팅에서는 GPT-2 토크나이저와 모델을 사용할 것이다. 참고로 토크나이저는 문장을 미리 지정한 규칙과 vocab에 의거해 토큰 단위로 쪼개는 도구를 말한다.

     

    아래 코드는 토크나이저와 GPT2 모델을 불러온 뒤, GPT2 학습 시에 미리 학습한 pre-trained weight를 가져와 배치하는 기능을 구현한 것이다.

     

    (참고로 tokenizer의 경우, GPT2Tokenizer를 사용할 수도 있지만, 확장이 용이한 AutoTokenizer를 통해 gpt-2 tokenizer를 불러오게끔 하였다.)

     

    from transformers import AutoTokenizer, GPT2Model
    import torch
    
    tokenizer = AutoTokenizer.from_pretrained("gpt2")
    model = GPT2Model.from_pretrained("gpt2")

    3. single-sentence embedding

    먼저 하나의 문장을 토큰화한 뒤, GPT2 모델을 통해 임베딩 벡터를 얻어보자.

    # 임베딩할 대상 문장
    sequence = "Hello, my dog is cute"
    
    # 토큰화
    inputs = tokenizer(sequence, return_tensors="pt")
    
    # 임베딩
    outputs = model(**inputs)
    
    # 임베딩 결과 확인
    print("embedding size:   ", outputs.last_hidden_state.size())
    print("embedding vector: ", outputs.last_hidden_state)
    embedding size:    torch.Size([1, 6, 768])
    embedding vector:  tensor([[[-9.6587e-06, -1.4021e-01, -2.0845e-01,  ..., -1.5329e-01,
              -6.7827e-02, -1.9630e-01],
             [ 4.1949e-01,  2.3525e-01,  3.4816e-01,  ...,  4.5321e-02,
               1.5447e-01,  1.9547e-02],
             [-7.0056e-02,  2.6082e-01, -2.9146e-01,  ...,  9.0979e-02,
               4.9659e-01, -4.1824e-01],
             [-1.9695e-01, -2.9247e-01, -1.4120e-01,  ..., -8.9256e-02,
              -2.2392e-01,  1.2212e-01],
             [-6.4193e-01, -1.0236e-01, -4.2129e-01,  ...,  6.8696e-02,
              -5.1117e-01,  5.0044e-01],
             [ 4.1279e-03, -3.1454e-02, -1.0823e+00,  ..., -5.0159e-02,
              -3.0879e-02,  4.3480e-01]]], grad_fn=<ViewBackward0>)

    [1, 6, 768]에 대한 해석은 다음과 같이 할 수 있다.
    - 1개의 문장
    - 1개의 문장은 6개의 토큰으로 구성
    - 각 토큰은 768 차원에 임베딩

     

    4. multi-sentence embedding

    그러면 하나의 문장이 아닌 두 개 이상의 문장에 대해서도 임베딩을 할 수 있을까?

     

    pad_token 지정에 대한 설명
    위에서 하나의 문장이 6개의 토큰으로 이루어져 있는 것을 확인했다. 그런데 일반적으로 문장의 길이는 모두 다르다. 이를 하나의 매트릭스(행렬)에 표현하기 위해서는 토큰의 개수를 맞춰주어야 한다. 

    이렇게 샘플 별로 구성 요소의 수가 달라지는 경우, 자연어 처리 및 딥러닝 분야에서는 padding(패딩)을 통해 개수를 일치시켜준다. 수치형 데이터에서 패딩은 0 등의 값을 사용하는데, 자연어에서는 0이 아니라 패딩 용도로 특정 토큰을 사용한다.

     

    여러 문장을 토큰화할 때에는 패딩에 사용할 토큰을 지정하고, 패딩을 할 것이라는 옵션을 주어야 오류 없이 토큰화가 가능하다.

     

    # 패딩 토큰 지정
    if tokenizer.pad_token is None:
    	tokenizer.pad_token = tokenizer.eos_token
    
    # 두 개의 문장을 포함하는 배열 (문장의 길이가 약간 다르다)
    sequence = ["Hello, my dog is cute isnt it?", "Hello, my dog is super cute isnt it?"]
    
    # 토큰화
    inputs = tokenizer(sequence, return_tensors="pt", 
    	padding=True, truncation=True)
    
    # 임베딩
    outputs = model(**inputs)
    
    # 임베딩 결과 확인
    print("embedding size:   ", outputs.last_hidden_state.size())
    print("embedding vector: ", outputs.last_hidden_state)

    출력 결과를 보면, 아까는 size의 첫 요소가 1이었음에 반해 2로 증가함을 알 수 있고, 두 번째 요소는 6에서 11로 증가한 것을 확인할 수 있다. 이는 각각 대상 문장이 1개에서 2개로, 최대 토큰 수가 6개에서 11개로 증가한 것으로 해석할 수 있다. 
    참고로 위에서 pad_token을 설정하지 않거나, 파라미터를 적절히 추가해주지 않으면 정상적인 결과가 아니라 오류를 마주할 수 있으니 꼭 확인하자.

     

    +) 임베딩 결과를 numpy array로 바꾸려면?

    아래 코드를 통해 임베딩 결과를 ndarray로 바꿀 수 있다

    data = outputs.last_hidden_state.detach().numpy()
    data.shape
    (2, 11, 768)
    반응형