반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형

https://d2l.ai/chapter_recurrent-neural-networks/text-sequence.html

 

9.2. Converting Raw Text into Sequence Data — Dive into Deep Learning 1.0.0-beta0 documentation

 

d2l.ai

 

9.2. Converting Raw Text into Sequence Data

 

Throughout this book, we will often work with text data represented as sequences of words, characters, or word-pieces. To get going, we will need some basic tools for converting raw text into sequences of the appropriate form. Typical preprocessing pipelines execute the following steps:

 

이 책 전체에서 단어, 문자 또는 단어 조각의 시퀀스로 표현된 텍스트 데이터로 작업하는 경우가 많습니다. 시작하려면 원시 텍스트를 적절한 형식의 시퀀스로 변환하기 위한 몇 가지 기본 도구가 필요합니다. 일반적인 사전 처리 파이프라인은 다음 단계를 실행합니다.

 

  1. Load text as strings into memory. 
    텍스트를 문자열로 메모리에 로드합니다.
  2. Split the strings into tokens (e.g., words or characters).
    문자열을 토큰(예: 단어 또는 문자)으로 분할합니다.
  3. Build a vocabulary dictionary to associate each vocabulary element with a numerical index.
    각 어휘 요소를 숫자 인덱스와 연결하는 어휘 사전을 구축합니다.
  4. Convert the text into sequences of numerical indices.
    텍스트를 일련의 숫자 인덱스로 변환합니다.
import collections
import random
import re
import torch
from d2l import torch as d2l

해당 코드는 파이썬으로 구현된 Deep Learning for Natural Language Processing 책의 d2l (Dive into Deep Learning) 라이브러리의 일부분을 임포트하는 코드입니다. 이 라이브러리는 딥 러닝과 관련하여 다양한 기능과 도구를 제공합니다.

  1. import collections: 파이썬의 collections 모듈을 임포트합니다. 이 모듈은 특수한 컨테이너 데이터 타입과 유용한 데이터 구조를 제공합니다.
  2. import random: 파이썬의 random 모듈을 임포트합니다. 이 모듈은 난수 생성과 관련된 함수를 제공합니다.
  3. import re: 파이썬의 re (Regular Expression) 모듈을 임포트합니다. 이 모듈은 정규 표현식을 사용하여 문자열을 처리하는 함수를 제공합니다.
  4. import torch: 파이토치 라이브러리를 임포트합니다. 파이토치는 딥 러닝을 위한 오픈 소스 라이브러리로, 텐서 연산과 딥 러닝 모델 구축에 사용됩니다.
  5. from d2l import torch as d2l: d2l 라이브러리로부터 torch 모듈을 임포트합니다. d2l 라이브러리는 딥 러닝을 배우고 구현하는 데 도움이 되는 도구와 함수들을 포함하고 있습니다.

이 코드는 d2l 라이브러리를 사용하여 딥 러닝 모델을 구현하고 훈련하는 데 사용될 수 있습니다. 딥 러닝과 자연어 처리와 관련하여 더 자세한 내용은 해당 라이브러리와 책을 참고하시면 됩니다.

9.2.1. Reading the Dataset

 

Here, we will work with H. G. Wells’ The Time Machine, a book containing just over 30000 words. While real applications will typically involve significantly larger datasets, this is sufficient to demonstrate the preprocessing pipeline. The following _download method reads the raw text into a string.

 

여기에서 우리는 H. G. Wells의 30000 단어가 조금 넘는 책인 The Time Machine과 함께 작업할 것입니다. 실제 애플리케이션에는 일반적으로 훨씬 더 큰 데이터 세트가 포함되지만 전처리 파이프라인을 보여주기에는 충분합니다. 다음 _download 메서드는 원시 텍스트를 문자열로 읽습니다.

 

class TimeMachine(d2l.DataModule): #@save
    """The Time Machine dataset."""
    def _download(self):
        fname = d2l.download(d2l.DATA_URL + 'timemachine.txt', self.root,
                             '090b5e7e70c295757f55df93cb0a180b9691891a')
        with open(fname) as f:
            return f.read()

data = TimeMachine()
raw_text = data._download()
raw_text[:60]

해당 코드는 딥 러닝 라이브러리인 d2l(Data to Learning)의 DataModule 클래스를 상속하여 TimeMachine 데이터셋을 정의하는 코드입니다.

  1. class TimeMachine(d2l.DataModule):: TimeMachine 클래스를 정의하며, d2l의 DataModule 클래스를 상속합니다. DataModule 클래스는 데이터셋을 관리하고 전처리하는 기능을 제공합니다.
  2. def _download(self):: 데이터셋을 다운로드하는 내부 함수를 정의합니다.
  3. fname = d2l.download(d2l.DATA_URL + 'timemachine.txt', self.root, '090b5e7e70c295757f55df93cb0a180b9691891a'): d2l의 download 함수를 사용하여 TimeMachine 데이터셋의 파일을 다운로드합니다. 해당 함수는 URL, 저장 경로(self.root), 그리고 다운로드한 파일의 해시 값 등을 인자로 받습니다.
  4. with open(fname) as f: return f.read(): 다운로드한 파일을 읽어와서 raw_text 변수에 저장합니다. 이 변수에는 타임 머신 소설의 텍스트 데이터가 담겨 있습니다.
  5. data = TimeMachine(): TimeMachine 클래스의 인스턴스를 생성하여 data 변수에 저장합니다.
  6. raw_text = data._download(): data 인스턴스의 _download() 메서드를 호출하여 raw_text 변수에 타임 머신 소설의 텍스트 데이터를 다운로드합니다.
  7. raw_text[:60]: raw_text 변수의 처음 60개 문자를 출력합니다. 이는 타임 머신 소설의 일부분을 보여주는 것입니다. (실행 결과에 따라 출력 내용이 달라질 수 있습니다.)

이 코드는 TimeMachine 데이터셋을 다운로드하여 텍스트 데이터를 읽어오는 예제입니다. 이후 해당 텍스트 데이터를 활용하여 자연어 처리나 딥 러닝 모델 훈련 등에 사용할 수 있습니다.

 

'The Time Machine, by H. G. Wells [1898]nnnnnInnnThe Time Tra'

 

For simplicity, we ignore punctuation and capitalization when preprocessing the raw text.

단순화를 위해 원시 텍스트를 전처리할 때 구두점과 대문자 표시를 무시합니다.

 

@d2l.add_to_class(TimeMachine)  #@save
def _preprocess(self, text):
    return re.sub('[^A-Za-z]+', ' ', text).lower()

text = data._preprocess(raw_text)
text[:60]
'the time machine by h g wells i the time traveller for so it'

 

9.2.2. Tokenization

Tokens are the atomic (indivisible) units of text. Each time step corresponds to 1 token, but what precisely constitutes a token is a design choice. For example, we could represent the sentence “Baby needs a new pair of shoes” as a sequence of 7 words, where the set of all words comprise a large vocabulary (typically tens or hundreds of thousands of words). Or we would represent the same sentence as a much longer sequence of 30 characters, using a much smaller vocabulary (there are only 256 distinct ASCII characters). Below, we tokenize our preprocessed text into a sequence of characters.

 

토큰은 텍스트의 원자(분할 불가) 단위입니다. 각 시간 단계는 1 토큰에 해당하지만 정확히 토큰을 구성하는 것은 디자인 선택입니다. 예를 들어 "Baby needs a new pair of shoes"라는 문장을 7개의 단어 시퀀스로 나타낼 수 있습니다. 여기서 모든 단어 집합은 큰 어휘(일반적으로 수만 또는 수십만 단어)로 구성됩니다. 또는 훨씬 더 작은 어휘(256개의 개별 ASCII 문자만 있음)를 사용하여 동일한 문장을 훨씬 더 긴 30자 시퀀스로 나타낼 수 있습니다. 아래에서는 사전 처리된 텍스트를 일련의 문자로 토큰화합니다.

 

@d2l.add_to_class(TimeMachine)  #@save
def _tokenize(self, text):
    return list(text)

tokens = data._tokenize(text)
','.join(tokens[:30])

해당 코드는 TimeMachine 데이터셋의 전처리된 텍스트 데이터를 토큰화하는 _tokenize 메서드를 추가하는 코드입니다.

  1. @d2l.add_to_class(TimeMachine): TimeMachine 클래스에 새로운 메서드를 추가하기 위해 d2l 라이브러리의 add_to_class 데코레이터를 사용합니다.
  2. def _tokenize(self, text):: _tokenize 메서드를 정의합니다. 이 메서드는 전처리된 텍스트 데이터를 토큰화하는 역할을 합니다.
  3. return list(text): 텍스트 데이터를 문자 단위로 토큰화하여 리스트로 반환합니다. 이렇게 하면 각 문자가 리스트의 원소로 들어가게 됩니다.
  4. tokens = data._tokenize(text): _tokenize 메서드를 사용하여 전처리된 텍스트 데이터인 text를 토큰화한 뒤, tokens 변수에 저장합니다.
  5. ','.join(tokens[:30]): 토큰화된 리스트 tokens의 처음 30개 토큰을 쉼표로 구분하여 문자열로 변환합니다. 이는 토큰화된 텍스트 데이터의 일부분을 보여주는 것입니다. (실행 결과에 따라 출력 내용이 달라질 수 있습니다.)

토큰화란, 텍스트 데이터를 작은 단위로 나누는 과정을 말합니다. 일반적으로 텍스트를 단어 단위나 문자 단위로 토큰화하여 컴퓨터가 이해할 수 있는 형태로 변환합니다. 이후 토큰화된 데이터를 활용하여 자연어 처리 모델을 학습하거나 다양한 자연어 처리 작업에 활용할 수 있습니다.

 

9.2.3. Vocabulary

These tokens are still strings. However, the inputs to our models must ultimately consist of numerical inputs. Next, we introduce a class for constructing vocabularies, i.e., objects that associate each distinct token value with a unique index. First, we determine the set of unique tokens in our training corpus. We then assign a numerical index to each unique token.

 

이러한 토큰은 여전히 문자열입니다. 그러나 모델에 대한 입력은 궁극적으로 숫자 입력으로 구성되어야 합니다. 다음으로 우리는 어휘를 구성하기 위한 클래스, 즉 각각의 고유한 토큰 값을 고유한 인덱스와 연결하는 개체를 소개합니다. 먼저 훈련 코퍼스에서 고유한 토큰 세트를 결정합니다. 그런 다음 각 고유 토큰에 숫자 인덱스를 할당합니다.

 

Rare vocabulary elements are often dropped for convenience. Whenever we encounter a token at training or test time that had not been previously seen or was dropped from the vocabulary, we represent it by a special “<unk>” token, signifying that this is an unknown value.

 

희귀 어휘 요소는 편의를 위해 종종 삭제됩니다. 교육 또는 테스트 시간에 이전에 본 적이 없거나 어휘에서 삭제된 토큰을 만날 때마다 우리는 이를 알 수 없는 값임을 나타내는 특수한 "<unk>" 토큰으로 나타냅니다.

 

class Vocab:  #@save
    """Vocabulary for text."""
    def __init__(self, tokens=[], min_freq=0, reserved_tokens=[]):
        # Flatten a 2D list if needed
        if tokens and isinstance(tokens[0], list):
            tokens = [token for line in tokens for token in line]
        # Count token frequencies
        counter = collections.Counter(tokens)
        self.token_freqs = sorted(counter.items(), key=lambda x: x[1],
                                  reverse=True)
        # The list of unique tokens
        self.idx_to_token = list(sorted(set(['<unk>'] + reserved_tokens + [
            token for token, freq in self.token_freqs if freq >= min_freq])))
        self.token_to_idx = {token: idx
                             for idx, token in enumerate(self.idx_to_token)}

    def __len__(self):
        return len(self.idx_to_token)

    def __getitem__(self, tokens):
        if not isinstance(tokens, (list, tuple)):
            return self.token_to_idx.get(tokens, self.unk)
        return [self.__getitem__(token) for token in tokens]

    def to_tokens(self, indices):
        if hasattr(indices, '__len__') and len(indices) > 1:
            return [self.idx_to_token[int(index)] for index in indices]
        return self.idx_to_token[indices]

    @property
    def unk(self):  # Index for the unknown token
        return self.token_to_idx['<unk>']

해당 코드는 텍스트의 어휘(Vocabulary)를 처리하는 Vocab 클래스를 정의하는 코드입니다.

  1. class Vocab:: Vocab 클래스를 정의합니다. 이 클래스는 텍스트의 어휘를 다루는 기능들을 제공합니다.
  2. def __init__(self, tokens=[], min_freq=0, reserved_tokens=[]):: Vocab 클래스의 생성자(constructor)입니다. 이 메서드는 어휘를 초기화하는 역할을 합니다.
  3. counter = collections.Counter(tokens): 토큰들의 빈도를 셉니다. collections.Counter를 사용하여 토큰들의 빈도를 측정합니다.
  4. self.token_freqs = sorted(counter.items(), key=lambda x: x[1], reverse=True): 토큰들을 빈도순으로 정렬합니다. self.token_freqs에는 토큰과 그 빈도가 튜플 형태로 저장됩니다.
  5. self.idx_to_token = list(sorted(set(['<unk>'] + reserved_tokens + [token for token, freq in self.token_freqs if freq >= min_freq]))): 고유한 토큰들을 정렬하여 리스트로 저장합니다. 미리 정의된 특별한 토큰들과 최소 빈도 이상으로 등장한 토큰들을 포함합니다.
  6. self.token_to_idx = {token: idx for idx, token in enumerate(self.idx_to_token)}: 토큰과 인덱스 사이의 매핑을 만듭니다. self.token_to_idx는 토큰을 인덱스로 변환할 때 사용하는 딕셔너리입니다.
  7. def __len__(self):: 어휘의 크기를 반환하는 메서드입니다. 어휘에 포함된 고유한 토큰의 수를 반환합니다.
  8. def __getitem__(self, tokens):: 토큰에 해당하는 인덱스를 반환하는 메서드입니다. 단일 토큰 또는 토큰들의 리스트/튜플을 입력으로 받아서 해당하는 인덱스 또는 인덱스들의 리스트를 반환합니다.
  9. def to_tokens(self, indices):: 인덱스에 해당하는 토큰을 반환하는 메서드입니다. 단일 인덱스 또는 인덱스들의 리스트를 입력으로 받아서 해당하는 토큰 또는 토큰들의 리스트를 반환합니다.
  10. @property: unk 메서드를 프로퍼티로 설정합니다.
  11. def unk(self):: <unk> 토큰에 해당하는 인덱스를 반환하는 메서드입니다. <unk> 토큰은 어휘에 등록되지 않은 토큰을 대체하는데 사용됩니다.
  12.  

We now construct a vocabulary for our dataset, converting the sequence of strings into a list of numerical indices. Note that we have not lost any information and can easily convert our dataset back to its original (string) representation.

 

이제 문자열 시퀀스를 숫자 인덱스 목록으로 변환하여 데이터 세트에 대한 어휘를 구성합니다. 어떤 정보도 손실되지 않았으며 데이터 세트를 원래(문자열) 표현으로 쉽게 다시 변환할 수 있습니다.

 

vocab = Vocab(tokens)
indices = vocab[tokens[:10]]
print('indices:', indices)
print('words:', vocab.to_tokens(indices))

해당 코드는 어휘(Vocabulary)를 생성하고, 토큰들을 인덱스로 변환하여 다시 토큰으로 변환하는 과정을 설명합니다.

  1. vocab = Vocab(tokens): Vocab 클래스를 사용하여 tokens 리스트를 기반으로 어휘(Vocabulary)를 생성합니다. tokens 리스트에는 텍스트를 토큰화한 결과가 담겨 있어야 합니다.
  2. indices = vocab[tokens[:10]]: 생성된 어휘를 사용하여 tokens 리스트의 처음 10개 토큰을 인덱스로 변환합니다. vocab 객체의 __getitem__ 메서드를 호출하여 토큰들을 인덱스로 변환한 결과를 indices 변수에 저장합니다.
  3. print('indices:', indices): 인덱스로 변환된 결과를 출력합니다. indices 변수에는 토큰들에 해당하는 정수 인덱스들이 리스트 형태로 저장되어 있습니다.
  4. print('words:', vocab.to_tokens(indices)): 인덱스들을 다시 원래의 토큰으로 변환하여 출력합니다. vocab 객체의 to_tokens 메서드를 사용하여 정수 인덱스들을 토큰들로 변환한 결과를 출력합니다.

즉, 위 코드는 텍스트 데이터를 어휘로 변환하고, 토큰들을 인덱스로 변환하여 어휘 내에서의 고유한 인덱스를 얻은 뒤, 다시 원래의 토큰으로 변환하는 과정을 보여줍니다. 이를 통해 텍스트 데이터를 토큰화하고 인덱스로 변환하여 모델 입력으로 사용할 수 있게 됩니다.

 

 

indices: [21, 9, 6, 0, 21, 10, 14, 6, 0, 14]
words: ['t', 'h', 'e', ' ', 't', 'i', 'm', 'e', ' ', 'm']

 

9.2.4. Putting It All Together

 

Using the above classes and methods, we package everything into the following build method of the TimeMachine class, which returns corpus, a list of token indices, and vocab, the vocabulary of The Time Machine corpus. The modifications we did here are: (i) we tokenize text into characters, not words, to simplify the training in later sections; (ii) corpus is a single list, not a list of token lists, since each text line in The Time Machine dataset is not necessarily a sentence or paragraph.

 

위의 클래스와 메서드를 사용하여 토큰 인덱스 목록인 말뭉치와 The Time Machine 말뭉치의 어휘인 vocab을 반환하는 다음과 같은 TimeMachine 클래스의 빌드 방법으로 모든 것을 패키징합니다. 여기에서 수정한 내용은 다음과 같습니다. (i) 이후 섹션에서 교육을 단순화하기 위해 텍스트를 단어가 아닌 문자로 토큰화합니다. (ii) 말뭉치는 토큰 목록이 아닌 단일 목록입니다. Time Machine 데이터 세트의 각 텍스트 라인이 반드시 문장이나 단락일 필요는 없기 때문입니다.

 

@d2l.add_to_class(TimeMachine)  #@save
def build(self, raw_text, vocab=None):
    tokens = self._tokenize(self._preprocess(raw_text))
    if vocab is None: vocab = Vocab(tokens)
    corpus = [vocab[token] for token in tokens]
    return corpus, vocab

corpus, vocab = data.build(raw_text)
len(corpus), len(vocab)

해당 코드는 TimeMachine 클래스에 build 메서드를 추가하여 데이터를 전처리하고 어휘를 생성하는 과정을 설명합니다.

  1. tokens = self._tokenize(self._preprocess(raw_text)): raw_text를 전처리하고 토큰화하여 tokens 리스트로 변환합니다. 이를 통해 텍스트 데이터를 전처리한 후 각 단어를 토큰화한 결과가 tokens에 저장됩니다.
  2. if vocab is None: vocab = Vocab(tokens): vocab이 None인 경우, Vocab 클래스를 사용하여 tokens 리스트를 기반으로 어휘(Vocabulary)를 생성합니다. 이렇게 생성된 어휘 객체를 vocab 변수에 저장합니다.
  3. corpus = [vocab[token] for token in tokens]: 어휘를 사용하여 토큰들을 인덱스로 변환하여 corpus 리스트로 만듭니다. 이는 데이터를 모델의 입력으로 사용하기 위해 텍스트 데이터를 인덱스 형태로 변환하는 과정입니다.
  4. return corpus, vocab: 인덱스로 변환된 corpus와 생성된 어휘 vocab을 반환합니다.

따라서, 위 코드는 텍스트 데이터를 전처리하고 어휘를 생성하여 텍스트 데이터를 인덱스로 변환하는 과정을 보여줍니다. 이렇게 처리된 데이터는 자연어 처리 모델을 훈련시키거나 평가하는 데 사용될 수 있습니다. 마지막으로 len(corpus)는 corpus에 포함된 인덱스의 개수를, len(vocab)은 어휘 내의 고유한 토큰 수를 출력합니다.

 

(173428, 28)

corpus : 글자 하나 하나 별 빈도수 나타냄

vocab : 각 알파벳별로 정리함

9.2.5. Exploratory Language Statistics

 

Using the real corpus and the Vocab class defined over words, we can inspect basic statistics concerning word use in our corpus. Below, we construct a vocabulary from words used in The Time Machine and print the 10 most frequently occurring words.

 

실제 말뭉치와 단어에 대해 정의된 Vocab 클래스를 사용하여 말뭉치에서 단어 사용에 관한 기본 통계를 검사할 수 있습니다. 아래에서는 The Time Machine에서 사용된 단어로 어휘를 구성하고 가장 자주 나오는 10개의 단어를 인쇄합니다.

 

words = text.split()
vocab = Vocab(words)
vocab.token_freqs[:10]

해당 코드는 주어진 텍스트 데이터를 단어 단위로 분리하여 어휘(Vocabulary)를 생성하고, 어휘 내에서 가장 빈도가 높은 단어 10개를 출력하는 과정을 설명합니다.

  1. words = text.split(): 주어진 텍스트를 공백을 기준으로 단어들로 분리하여 words 리스트로 변환합니다. 이를 통해 텍스트 데이터가 공백으로 구분된 단어들로 분리되어 words에 저장됩니다.
  2. vocab = Vocab(words): Vocab 클래스를 사용하여 words 리스트를 기반으로 어휘(Vocabulary)를 생성합니다. 이는 텍스트 데이터 내에서 고유한 단어들을 찾아내어 인덱스를 부여하는 과정입니다. 어휘 객체는 vocab 변수에 저장됩니다.
  3. vocab.token_freqs[:10]: 생성된 어휘 객체인 vocab의 token_freqs 속성을 통해 어휘 내의 각 단어와 해당 단어의 빈도를 포함하는 리스트를 얻습니다. 이 코드에서는 가장 빈도가 높은 단어 10개를 출력하고 있습니다.

따라서, 위 코드는 주어진 텍스트 데이터를 단어 단위로 분리하고 어휘를 생성하여 어휘 내에서 가장 빈도가 높은 단어 10개를 출력하는 과정을 보여줍니다. 이렇게 생성된 어휘는 자연어 처리 모델의 입력으로 사용되거나 텍스트 데이터를 인덱스로 변환하는 데 활용될 수 있습니다.

 

 

Note that the ten most frequent words are not all that descriptive. You might even imagine that we might see a very similar list if we had chosen any book at random. Articles like “the” and “a”, pronouns like “i” and “my”, and prepositions like “of”, “to”, and “in” occur often because they serve common syntactic roles. Such words that are at once common but particularly descriptive are often called stop words and, in previous generations of text classifiers based on bag-of-words representations, they were most often filtered out. However, they carry meaning and it is not necessary to filter them out when working with modern RNN- and Transformer-based neural models. If you look further down the list, you will notice that word frequency decays quickly. The 10th most frequent word is less than 1/5 as common as the most popular. Word frequency tends to follow a power law distribution (specifically the Zipfian) as we go down the ranks. To get a better idea, we plot the figure of the word frequency.

 

가장 자주 사용되는 10개의 단어가 모든 것을 설명하는 것은 아닙니다. 임의의 책을 선택했다면 매우 유사한 목록이 표시될 수 있다고 상상할 수도 있습니다. "the" 및 "a"와 같은 관사, "i" 및 "my"와 같은 대명사, "of", "to" 및 "in"과 같은 전치사는 일반적인 구문 역할을 수행하기 때문에 자주 사용됩니다. 한 번에 일반적이지만 특히 설명적인 이러한 단어는 종종 불용어라고 하며 단어 모음 표현을 기반으로 하는 이전 세대의 텍스트 분류기에서는 가장 자주 필터링되었습니다. 그러나 이들은 의미를 지니며 최신 RNN 및 Transformer 기반 신경 모델로 작업할 때 필터링할 필요가 없습니다. 목록을 더 자세히 살펴보면 단어 빈도가 빠르게 감소하는 것을 알 수 있습니다. 10번째로 자주 사용되는 단어는 가장 인기 있는 단어의 1/5 미만입니다. 단어 빈도는 순위가 내려갈 때 멱법칙 분포(특히 Zipfian)를 따르는 경향이 있습니다. 더 나은 아이디어를 얻기 위해 단어 빈도의 수치를 플로팅합니다.

 

freqs = [freq for token, freq in vocab.token_freqs]
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)',
         xscale='log', yscale='log')

해당 코드는 어휘(Vocabulary) 객체에서 단어 빈도를 추출하여 이를 시각화하는 과정을 설명합니다.

  1. freqs = [freq for token, freq in vocab.token_freqs]: 어휘 객체(vocab)의 token_freqs 속성은 단어와 해당 단어의 빈도를 튜플 형태로 저장한 리스트입니다. 이 코드에서는 vocab의 모든 단어의 빈도만을 추출하여 freqs 리스트에 저장합니다.
  2. d2l.plot(...): d2l 라이브러리의 plot 함수를 사용하여 데이터를 시각화합니다. freqs 리스트는 단어들의 빈도를 나타내므로, 이를 시각화하면 단어 빈도 분포를 파악할 수 있습니다.

시각화 옵션 설명:

  • xlabel='token: x': x축 레이블로 "token: x"를 지정합니다.
  • ylabel='frequency: n(x)': y축 레이블로 "frequency: n(x)"를 지정합니다.
  • xscale='log': x축의 스케일을 로그 스케일로 지정합니다. 이로 인해 단어들의 빈도 분포가 더 잘 보여지게 됩니다.
  • yscale='log': y축의 스케일을 로그 스케일로 지정합니다. 이로 인해 단어들의 빈도 분포가 더 잘 보여지게 됩니다.

이렇게 시각화된 그래프는 어휘 내 단어들의 빈도 분포를 로그 스케일로 나타내어 자주 등장하는 단어들과 드물게 등장하는 단어들의 차이를 시각적으로 확인할 수 있게 해줍니다.

 

After dealing with the first few words as exceptions, all the remaining words roughly follow a straight line on a log-log plot. This phenomena is captured by Zipf’s law, which states that the frequency ni of the i th most frequent word is:

 

처음 몇 단어를 예외로 처리한 후 나머지 모든 단어는 대략 로그-로그 플롯에서 직선을 따릅니다. 이 현상은 i 번째로 가장 자주 사용되는 단어의 빈도 ni가 다음과 같다는 Zipf의 법칙에 의해 포착됩니다.

 

which is equivalent to

 

where α is the exponent that characterizes the distribution and c is a constant. This should already give us pause if we want to model words by counting statistics. After all, we will significantly overestimate the frequency of the tail, also known as the infrequent words. But what about the other word combinations, such as two consecutive words (bigrams), three consecutive words (trigrams), and beyond? Let’s see whether the bigram frequency behaves in the same manner as the single word (unigram) frequency.

 

여기서 α는 분포를 특징짓는 지수이고 c는 상수입니다. 통계를 계산하여 단어를 모델링하려는 경우 이미 일시 중지해야 합니다. 결국 우리는 흔하지 않은 단어라고도 알려진 꼬리의 빈도를 상당히 과대 평가할 것입니다. 그러나 두 개의 연속 단어(bigrams), 세 개의 연속 단어(trigrams) 및 그 이상과 같은 다른 단어 조합은 어떻습니까? 바이그램 빈도가 단일 단어(유니그램) 빈도와 동일한 방식으로 작동하는지 살펴보겠습니다.

 

bigram_tokens = ['--'.join(pair) for pair in zip(words[:-1], words[1:])]
bigram_vocab = Vocab(bigram_tokens)
bigram_vocab.token_freqs[:10]

해당 코드는 단어 리스트를 이용하여 bigram(바이그램) 어휘(Vocabulary)를 만들고, 그 어휘 내에서 bigram들의 빈도를 추출하는 과정을 설명합니다.

  1. bigram_tokens = ['--'.join(pair) for pair in zip(words[:-1], words[1:])]: 입력된 words 리스트는 텍스트를 단어 단위로 분할한 결과입니다. 이 코드에서는 zip 함수를 사용하여 words의 각 단어들을 두 개씩 묶어서 bigram 형태로 만듭니다. --로 두 단어를 구분하여 결합한 결과가 bigram_tokens 리스트에 저장됩니다.
  2. bigram_vocab = Vocab(bigram_tokens): 앞서 생성한 bigram_tokens 리스트를 이용하여 bigram 어휘 객체를 생성합니다. 즉, bigram 어휘 객체는 bigram들과 해당 bigram들의 빈도 정보를 저장합니다.
  3. bigram_vocab.token_freqs[:10]: bigram_vocab의 token_freqs 속성은 bigram과 해당 bigram의 빈도를 튜플 형태로 저장한 리스트입니다. 이 코드에서는 bigram_vocab에 포함된 bigram들 중 가장 빈도가 높은 상위 10개를 출력합니다.

이렇게 만들어진 bigram_vocab 객체는 주어진 텍스트 데이터에서 bigram들을 추출하고, 이들의 빈도 정보를 가지고 있습니다. 이를 통해 텍스트 데이터 내에서 자주 등장하는 bigram들을 파악할 수 있습니다. 바이그램은 문장 내 단어들 간의 순서 정보를 간단하게 표현하는 방법으로 널리 활용됩니다.

 

 

One thing is notable here. Out of the ten most frequent word pairs, nine are composed of both stop words and only one is relevant to the actual book—“the time”. Furthermore, let’s see whether the trigram frequency behaves in the same manner.

 

여기서 한 가지 주목할 만한 것이 있습니다. 10개의 가장 빈번한 단어 쌍 중 9개는 두 불용어로 구성되어 있으며 실제 책과 관련이 있는 것은 "시간"입니다. 또한 트라이그램 주파수가 동일한 방식으로 동작하는지 살펴보겠습니다.

 

trigram_tokens = ['--'.join(triple) for triple in zip(
    words[:-2], words[1:-1], words[2:])]
trigram_vocab = Vocab(trigram_tokens)
trigram_vocab.token_freqs[:10]

해당 코드는 단어 리스트를 이용하여 trigram(트라이그램) 어휘(Vocabulary)를 만들고, 그 어휘 내에서 trigram들의 빈도를 추출하는 과정을 설명합니다.

  1. trigram_tokens = ['--'.join(triple) for triple in zip(words[:-2], words[1:-1], words[2:])]: 입력된 words 리스트는 텍스트를 단어 단위로 분할한 결과입니다. 이 코드에서는 zip 함수를 사용하여 words의 단어들을 세 개씩 묶어서 trigram 형태로 만듭니다. --로 세 단어를 구분하여 결합한 결과가 trigram_tokens 리스트에 저장됩니다.
  2. trigram_vocab = Vocab(trigram_tokens): 앞서 생성한 trigram_tokens 리스트를 이용하여 trigram 어휘 객체를 생성합니다. 즉, trigram 어휘 객체는 trigram들과 해당 trigram들의 빈도 정보를 저장합니다.
  3. trigram_vocab.token_freqs[:10]: trigram_vocab의 token_freqs 속성은 trigram과 해당 trigram의 빈도를 튜플 형태로 저장한 리스트입니다. 이 코드에서는 trigram_vocab에 포함된 trigram들 중 가장 빈도가 높은 상위 10개를 출력합니다.

이렇게 만들어진 trigram_vocab 객체는 주어진 텍스트 데이터에서 trigram들을 추출하고, 이들의 빈도 정보를 가지고 있습니다. 트라이그램은 문장 내 단어들 간의 순서 정보를 더 포함하는 방법으로 널리 활용되며, 바이그램보다 조금 더 복잡한 문맥 정보를 파악할 수 있습니다.

 

Last, let’s visualize the token frequency among these three models: unigrams, bigrams, and trigrams.

 

마지막으로 유니그램, 바이그램, 트라이그램의 세 가지 모델 간의 토큰 빈도를 시각화해 보겠습니다.

 

bigram_freqs = [freq for token, freq in bigram_vocab.token_freqs]
trigram_freqs = [freq for token, freq in trigram_vocab.token_freqs]
d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x',
         ylabel='frequency: n(x)', xscale='log', yscale='log',
         legend=['unigram', 'bigram', 'trigram'])

해당 코드는 언어 모델에서 사용되는 단어들의 빈도 정보를 비교하여 그래프로 표현하는 과정을 설명합니다.

  1. bigram_freqs = [freq for token, freq in bigram_vocab.token_freqs]: bigram_vocab에 포함된 bigram들과 해당 bigram들의 빈도 정보를 추출하여 bigram_freqs 리스트에 저장합니다. 이 리스트는 bigram 어휘에 포함된 각 bigram의 빈도를 순서대로 저장하고 있습니다.
  2. trigram_freqs = [freq for token, freq in trigram_vocab.token_freqs]: trigram_vocab에 포함된 trigram들과 해당 trigram들의 빈도 정보를 추출하여 trigram_freqs 리스트에 저장합니다. 이 리스트는 trigram 어휘에 포함된 각 trigram의 빈도를 순서대로 저장하고 있습니다.
  3. d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x', ylabel='frequency: n(x)', xscale='log', yscale='log', legend=['unigram', 'bigram', 'trigram']): d2l.plot 함수를 사용하여 세 개의 빈도 리스트(freqs, bigram_freqs, trigram_freqs)를 이용하여 그래프를 그립니다. xscale='log'와 yscale='log'로 설정하여 x축과 y축을 로그 스케일로 표시하고, xlabel과 ylabel로 각 축의 레이블을 설정합니다. legend 파라미터를 사용하여 그래프의 범례를 지정합니다. 이 경우, "unigram", "bigram", "trigram"에 해당하는 빈도 정보를 비교하는 세 개의 라인 그래프를 출력합니다.

이렇게 그려진 그래프는 unigram, bigram, trigram 단위로 단어들의 빈도 정보를 비교하여 언어 모델에서 단어들의 중요성과 사용 빈도를 파악하는 데 도움을 줍니다

 

 

This figure is quite exciting. First, beyond unigram words, sequences of words also appear to be following Zipf’s law, albeit with a smaller exponent α in (9.2.1), depending on the sequence length. Second, the number of distinct n-grams is not that large. This gives us hope that there is quite a lot of structure in language. Third, many n-grams occur very rarely. This makes certain methods unsuitable for language modeling and motivates the use of deep learning models. We will discuss this in the next section.

 

이 수치는 매우 흥미 롭습니다. 첫째, 유니그램 단어 외에 단어 시퀀스도 Zipf의 법칙을 따르는 것으로 보이지만 시퀀스 길이에 따라 (9.2.1)의 지수 α가 더 작습니다. 둘째, 고유한 n-gram의 수가 그리 많지 않습니다. 이것은 우리에게 언어에 꽤 많은 구조가 있다는 희망을 줍니다. 셋째, 많은 n-gram이 매우 드물게 발생합니다. 이것은 특정 방법을 언어 모델링에 부적합하게 만들고 딥 러닝 모델을 사용하도록 동기를 부여합니다. 다음 섹션에서 이에 대해 논의할 것입니다.

 

Zipf's law 란?

Zipf's law is a statistical principle that describes the distribution of frequencies of elements in a dataset. Specifically, it states that the frequency of any element is inversely proportional to its rank in the frequency table. In other words, the second most common element appears half as often as the most common one, the third most common element appears one-third as often, and so on. This law is often used to describe the distribution of word frequencies in natural language texts, where a small number of words (like "the," "and," "is") occur very frequently, while the majority of words occur much less often.

 

지프의 법칙(Zipf's law)은 데이터셋 내 요소들의 분포를 설명하는 통계적 원리입니다. 구체적으로, 이 법칙은 어떤 요소의 빈도가 해당 요소의 빈도 테이블에서의 순위에 반비례한다는 원리를 나타냅니다. 다시 말해, 두 번째로 흔한 요소는 첫 번째로 흔한 것의 절반 정도 등장하며, 세 번째로 흔한 요소는 1/3 정도로 등장하는 식입니다. 이 법칙은 자연어 텍스트 내 단어 빈도의 분포를 설명하는 데 자주 사용되며, "the," "and," "is"와 같은 소수의 단어가 빈번히 등장하고 나머지 대다수의 단어는 훨씬 덜 등장하는 특징을 나타냅니다.

 

In mathematical terms, Zipf's law can be expressed as:

수학적으로는 지프의 법칙을 다음과 같이 표현할 수 있습니다:

Where:

  • is the frequency of the element ranked in the frequency table.
  • 은 빈도 테이블에서 순위 의 요소의 빈도입니다.
  • is the rank of the element in the frequency table.
  • 은 빈도 테이블에서의 요소 순위입니다.
  • is a constant.
  • 는 상수입니다.
  • is the scaling exponent, which determines how quickly the frequencies decrease with increasing rank.
  • 는 스케일링 지수로, 순위가 증가함에 따라 빈도가 얼마나 빠르게 감소하는지를 결정합니다.

Zipf's law is observed in various domains beyond language, including city populations, wealth distribution, and web page visits. It's important to note that while Zipf's law provides a useful description of the distribution of elements in some datasets, not all datasets follow this law exactly.

 

지프의 법칙은 언어 외에도 도시 인구, 부의 분포, 웹 페이지 방문 등 다양한 영역에서 관찰됩니다. 하지만 지프의 법칙이 모든 데이터셋에 정확하게 적용되는 것은 아니라는 점을 기억해야 합니다.

 

9.2.6. Summary

 

Text is among the most common forms of sequence data encountered in deep learning. Common choices for what constitutes a token are characters, words, and word pieces. To preprocess text, we usually (i) split text into tokens; (ii) build a vocabulary to map token strings to numerical indices; and (iii) convert text data into token indices for models to manipulate. In practice, the frequency of words tends to follow Zipf’s law. This is true not just for individual words (unigrams), but also for n-grams.

 

텍스트는 딥 러닝에서 접할 수 있는 가장 일반적인 형태의 시퀀스 데이터 중 하나입니다. 토큰을 구성하는 항목에 대한 일반적인 선택은 문자, 단어 및 단어 조각입니다. 텍스트를 사전 처리하기 위해 일반적으로 (i) 텍스트를 토큰으로 분할합니다. (ii) 토큰 문자열을 숫자 인덱스에 매핑하기 위한 어휘를 구축합니다. (iii) 모델이 조작할 수 있도록 텍스트 데이터를 토큰 인덱스로 변환합니다. 실제로 단어의 빈도는 Zipf의 법칙을 따르는 경향이 있습니다. 이는 개별 단어(유니그램)뿐만 아니라 n-그램에도 해당됩니다.

 

9.2.7. Exercises

  1. In the experiment of this section, tokenize text into words and vary the min_freq argument value of the Vocab instance. Qualitatively characterize how changes in min_freq impact the size of the resulting vocabulary.
  2. Estimate the exponent of Zipfian distribution for unigrams, bigrams, and trigrams in this corpus.
  3. Find some other sources of data (download a standard machine learning dataset, pick another public domain book, scrape a website, etc). For each, tokenize the data at both the word and character levels. How do the vocabulary sizes compare with The Time Machine corpus at equivalent values of min_freq. Estimate the exponent of the Zipfian distribution corresponding to the unigram and bigram distributions for these corpora. How do they compare with the values that you observed for The Time Machine corpus?

 

 

 

 

반응형