One-hot encoding(원 핫 인코딩)

one-hot encoding은 텍스트를 부호화(encoding) 하기위해 많이 사용함.
one-hot encoding은 텍스트를 일명 토큰이라 부르는 중복없는 단어표(일종의 암호표)를 만들고, 인덱스를 붙인 후 부호화하고자 하는 텍스트의 위치에 해당하는 비트만 1로 만들고 나머지는 모두 0으로 채우는 것을 말함.

예를 들어 두개의 문장이 있다고 가정하자.
문장1: The cat sat on the mat.
문장2: The dog ate my homework.
이 문장을 R에서 리스트로 읽어 들인다.

samples <- c("The cat sat on the mat.", "The dog ate my homework.")
samples[[1]]
## [1] "The cat sat on the mat."
samples[[2]]
## [1] "The dog ate my homework."

이 두 문장을 부호화하는 방법은
- 토큰화(tokenization): 일종의 중복없는 단어표를 만드는 과정이다.
- 벡터화(vectorization): 텍스트의 위치에만 1을 넣고 나머지는 0을 채운다.

토큰화(Tokenization)

먼저 토큰화를 보자
아래의 코드는 중복된 단어를 제거하고 위 문장의 모든 단어의 인덱스를 만든다.

token_index <- list()
for (sample in samples)  # 
  # Tokenizes the samples via the strsplit function. In real life, you'd also
  # strip punctuation and special characters from the samples.
  for (word in strsplit(sample, " ")[[1]]) 
    if (!word %in% names(token_index))
      # Assigns a unique index to each unique word. Note that you don't
      # attribute index 1 to anything.
      token_index[[word]] <- length(token_index) + 2 

만들어진 단어의 인덱스를 보자

str(token_index)
## List of 10
##  $ The      : num 2
##  $ cat      : num 3
##  $ sat      : num 4
##  $ on       : num 5
##  $ the      : num 6
##  $ mat.     : num 7
##  $ dog      : num 8
##  $ ate      : num 9
##  $ my       : num 10
##  $ homework.: num 11

‘The’ 단어는 index 2로 되어 있고, cat 이라는 단어는 3으로 index 되어 있다. 나머지도 마찬가지다.
총 단어가 몇 개인지 살펴보자.

length(token_index)
## [1] 10

벡터화(vectorization)

단어의 총 개수만큼 행을 만들고, 단어의 최대 인덱스 만큼 열을 만들어 모두 0으로 채운다.

max_length <- length(token_index)

results <- array(0, dim = c(length(samples), 
                            max_length, 
                            max(as.integer(token_index))))

이제 단어의 위치에 해당하는 비트만 1로 채운다.

for (i in 1:length(samples)) {
  sample <- samples[[i]]
  words <- head(strsplit(sample, " ")[[1]], n = max_length)
  for (j in 1:length(words)) {
    index <- token_index[[words[[j]]]]
    results[[i, j, index]] <- 1
  }
}

자, 이제 어떻게 부호화가 되었는지 보자.

dim(results)
## [1]  2 10 11

2개의 문장, 10개의 행, 11개의 단어 인덱스로 이루어진 3D array 이다.
앞에서 보았듯이 첫번째 문장의 각 단어의 인덱스는 2,3,4,5,6,7 이고,
두번째 문장은 각 단어의 인덱스는 2 8 9 10 11 이다.
이 인덱스가 어떻게 벡터화가 되었는지 보자. 각 단어의 인덱스 위치에 1로 코딩되어 있는 것을 볼 수 있다.

results[1,,]
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
##  [1,]    0    1    0    0    0    0    0    0    0     0     0
##  [2,]    0    0    1    0    0    0    0    0    0     0     0
##  [3,]    0    0    0    1    0    0    0    0    0     0     0
##  [4,]    0    0    0    0    1    0    0    0    0     0     0
##  [5,]    0    0    0    0    0    1    0    0    0     0     0
##  [6,]    0    0    0    0    0    0    1    0    0     0     0
##  [7,]    0    0    0    0    0    0    0    0    0     0     0
##  [8,]    0    0    0    0    0    0    0    0    0     0     0
##  [9,]    0    0    0    0    0    0    0    0    0     0     0
## [10,]    0    0    0    0    0    0    0    0    0     0     0
results[2,,]
##       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11]
##  [1,]    0    1    0    0    0    0    0    0    0     0     0
##  [2,]    0    0    0    0    0    0    0    1    0     0     0
##  [3,]    0    0    0    0    0    0    0    0    1     0     0
##  [4,]    0    0    0    0    0    0    0    0    0     1     0
##  [5,]    0    0    0    0    0    0    0    0    0     0     1
##  [6,]    0    0    0    0    0    0    0    0    0     0     0
##  [7,]    0    0    0    0    0    0    0    0    0     0     0
##  [8,]    0    0    0    0    0    0    0    0    0     0     0
##  [9,]    0    0    0    0    0    0    0    0    0     0     0
## [10,]    0    0    0    0    0    0    0    0    0     0     0

아래 코드는 위에서 나온 코드를 하나로 모은 것이다.

samples <- c("The cat sat on the mat.", "The dog ate my homework.")

token_index <- list()
for (sample in samples)  # 
  for (word in strsplit(sample, " ")[[1]]) 
    if (!word %in% names(token_index))
      token_index[[word]] <- length(token_index) + 2 
max_length <- 10
results <- array(0, dim = c(length(samples), 
                            max_length, 
                            max(as.integer(token_index))))
for (i in 1:length(samples)) {
  sample <- samples[[i]]
  words <- head(strsplit(sample, " ")[[1]], n = max_length)
  for (j in 1:length(words)) {
    index <- token_index[[words[[j]]]]
    results[[i, j, index]] <- 1
  }
}