0과 1밖에 모르는 기계에게 인간의 언어 알려줘야 하는데, 이유는 컴퓨터는 숫자만 인식할 수 있기 때문에 바이너리 코드로 처리해 줘야한다. 텍스트 데이터 즉, 순서가 없는 범주형 데이터를 수치형 데이터로 변환한다. 벡터에서 해당되는 하나의 데이터만 1로 변경해 주고 나머지는 0으로 채워주는 방식으로 원핫 인코딩(One-Hot Encoding), 통계학에서 가변수(Dummy Variable)처리라고 부른다.
TF(단어 빈도, term frequency)는 특정한 단어가 문서 내에 얼마나 자주 등장하는지를 나타내는 값으로 이 값이 높을수록 문서에서 중요하다고 생각할 수 있지만, 단어 자체가 문서군 내에서 자주 사용되는 경우,이것은 그 단어가 흔하게 등장한다는 것을 의미도 된다.
이것을 DF(문서 빈도, document frequency)라고 하며, 이 값의 역수를 IDF(역문서 빈도, inverse document frequency)라고함 TF-IDF는 TF와 IDF를 곱한 값이 된다.
단어문서행렬(Term Document Matrix)을 전치(Transpose)하게 되면 문서단어행렬(DTM)이 된다. 단어문서행렬은 다음과 같은 형태를 갖는다.
\(문서_1\) | \(문서_1\) | \(문서_1\) | \(\cdots\) | \(문서_n\) | |
---|---|---|---|---|---|
\(단어_1\) | 0 | 0 | 0 | 0 | 0 |
\(단어_2\) | 1 | 1 | 0 | 0 | 0 |
\(단어_3\) | 1 | 0 | 0 | 0 | 0 |
\(\cdots\) | 0 | 0 | 2 | 1 | 1 |
\(단어_m\) | 0 | 0 | 0 | 1 | 0 |
문서단어행렬은 단서문서행렬을 전치하여 다음과 같은 형태를 갖는다.
\(단어_1\) | \(단어_1\) | \(단어_1\) | \(\cdots\) | \(단어_n\) | |
---|---|---|---|---|---|
\(문서_1\) | 0 | 1 | 1 | 0 | 0 |
\(문서_2\) | 0 | 1 | 0 | 0 | 0 |
\(문서_3\) | 0 | 0 | 0 | 2 | 0 |
\(\cdots\) | 0 | 0 | 0 | 1 | 1 |
\(문서_m\) | 0 | 0 | 0 | 1 | 0 |
딥러닝 학습 후 n-차원으로 워드임베딩하면 다음과 같은 형태를 갖는다.
\(차원_1\) | \(차원_1\) | \(차원_1\) | \(\cdots\) | \(차원_n\) | |
---|---|---|---|---|---|
\(단어_1\) | 0.754 | 0.625 | 0.525 | 0.954 | 0.685 |
\(단어_2\) | 1 | 1 | 0.219 | 0.791 | 0.652 |
\(단어_3\) | 0.125 | 0.968 | 0.215 | 0.571 | 0.845 |
\(\cdots\) | 0.857 | 0.323 | 0.125 | 0.847 | 0.696 |
\(단어_m\) | 0.214 | 0.841 | 0.985 | 1 | 0.198 |
word2vec
은 Autoencoder 와 유사하나 몇가지 점에서
차이점이 있다. 즉, word2vec
의 목표는 단어를 컴퓨터가 계산
가능한 형태로 One-hot 인코딩(통계학에서 더미 변수 인코딩)하게 되면 매우
성긴 행렬로 표현되기 때문에 이런 문제를 해결하고자 단어 인근 정보를
유지한 상태의 행렬정보로 새롭게 매핑한 것이다.
library(tidytext)
library(textdata)
library(tidyverse)
library(widyr)
<- embedding_glove6b(dimensions = 50)
glove6b
<- glove6b %>%
tidy_glove pivot_longer(contains("d"),
names_to = "dimension") %>%
rename(item1 = token)
tidy_glove
# A tibble: 20,000,000 × 3
item1 dimension value
<chr> <chr> <dbl>
1 the d1 0.418
2 the d2 0.250
3 the d3 -0.412
4 the d4 0.122
5 the d5 0.345
6 the d6 -0.0445
7 the d7 -0.497
8 the d8 -0.179
9 the d9 -0.000660
10 the d10 -0.657
# … with 19,999,990 more rows
<- function(word1 = "man", word2 = "woman") {
cosine_sim_fn
# 첫번째 단어 벡터
<- tidy_glove %>%
u filter(item1 == word1) %>%
pull(value)
# 두번째 단어 벡터
<- tidy_glove %>%
v filter(item1 == word2) %>%
pull(value)
# 코사인 유사도 계산
<- sum(u * v)
num <- InspectChangepoint:::vector.norm(u)
norm_u <- InspectChangepoint:::vector.norm(v)
norm_v <- norm_u * norm_v
denum <- num/denum
cos_sim
return(cos_sim)
}
cosine_sim_fn("man", "woman")
[1] 0.8860338
단어 유사도 시각화
%>%
tidy_glove filter(item1 %in% c("korea", "france", "seoul", "paris", "man", "qeen")) %>%
pivot_wider(names_from = item1, values_from = value) %>%
select(-dimension) %>%
as.matrix() %>%
::cosine() %>%
lsaheatmap()
<- function(df, token) {
nearest_neighbors %>%
df widely(
~ {
<- .[rep(token, nrow(.)), ]
y <- rowSums(. * y) /
res sqrt(rowSums(. ^ 2)) * sqrt(sum(.[token, ] ^ 2)))
(matrix(res, ncol = 1, dimnames = list(x = names(res)))
},sort = TRUE,
maximum_size = NULL
%>%
)(item1, dimension, value) select(-item2)
}
nearest_neighbors(tidy_glove, "korea")
# A tibble: 400,000 × 2
item1 value
<chr> <dbl>
1 korea 1
2 korean 0.878
3 pyongyang 0.851
4 dprk 0.842
5 seoul 0.824
6 china 0.823
7 japan 0.815
8 iran 0.807
9 beijing 0.773
10 taiwan 0.765
# … with 399,990 more rows
library(umap)
<- glove6b %>%
glove6b_sample sample_n(size = 100)
<- umap(glove6b_sample %>% select(-token))
glove_umap
<- glove_umap$layout %>%
umap_df as.data.frame()%>%
rename(UMAP1="V1",
UMAP2="V2") %>%
bind_cols(glove6b_sample) %>%
select(token, everything()) %>%
as_tibble()
%>%
umap_df ggplot(aes(x = UMAP1,
y = UMAP2))+
geom_point()+
labs(x = "UMAP1",
y = "UMAP2",
subtitle = "UMAP plot") +
::geom_text_repel(aes(label = token)) ggrepel
<- tidy_glove %>%
king_v filter(item1 == "king") %>%
rename(king = item1,
king_v = value)
<- tidy_glove %>%
man_v filter(item1 == "man") %>%
rename(man = item1,
man_v = value)
<- tidy_glove %>%
woman_v filter(item1 == "woman") %>%
rename(woman = item1,
woman_v = value)
<- tidy_glove %>%
queen_v filter(item1 == "queen") %>%
rename(queen = item1,
queen_v = value)
<- king_v %>%
question_v left_join(man_v) %>%
left_join(woman_v) %>%
left_join(queen_v) %>%
mutate(value = king_v - man_v + woman_v) %>%
mutate(item1 = "king-man+woman") %>%
select(item1, dimension, value)
<- tidy_glove %>%
infer_glove bind_rows(question_v)
nearest_neighbors(infer_glove, "king-man+woman")
# A tibble: 400,001 × 2
item1 value
<chr> <dbl>
1 king-man+woman 1
2 king 0.886
3 queen 0.861
4 daughter 0.768
5 prince 0.764
6 throne 0.763
7 princess 0.751
8 elizabeth 0.751
9 father 0.731
10 kingdom 0.730
# … with 399,991 more rows
You shall know a word by the company it keeps
Firth, J. R.
library(googlesheets4)
library(tidyverse)
library(patchwork)
# googlesheets4::gs4_deauth()
# googlesheets4::gs4_auth()
<- read_sheet("https://docs.google.com/spreadsheets/d/1AAIebjNsnJj_uKALHbXNfn3_YsT6sHXtCU0q7OIPuc4/")
lang_raw
<- lang_raw %>%
lang_tbl ::clean_names() %>%
janitorfilter(! is.na(system),
! is.na(parameters)) %>%
select(system, domain, year, parameters) %>%
filter(domain == "Language") %>%
group_by(system, year) %>%
summarise(parameters = max(parameters)) %>%
mutate(imp_model = case_when( system == "GPT" ~ TRUE,
== "Transformer" ~ TRUE,
system == "BERT-Large" ~ TRUE,
system == "Megatron-LM" ~ TRUE,
system == "ELECTRA" ~ TRUE,
system == "GPT-3 175B" ~ TRUE,
system == "LaMDA" ~ TRUE,
system TRUE ~ FALSE))
<- lang_tbl %>%
lang_whole_gg ggplot(aes(x = year, y = parameters, color = imp_model)) +
geom_point() +
scale_y_log10(labels = scales::comma) +
::geom_text_repel(aes(label = system)) +
ggrepelgeom_smooth(se = FALSE) +
theme_light() +
labs(title = "언어모형 복잡성 추세",
x = "",
y = "패러미터(Parameter) 수",
caption = "Parameter, Compute and Data Trends in Machine Learning, https://bit.ly/3NqTZZ3") +
scale_color_manual(values = c("gray37", "blue")) +
theme(legend.position = "none")
<- lang_tbl %>%
lang_latest_gg filter(year > 2015) %>%
ggplot(aes(x = year, y = parameters, color = imp_model)) +
geom_point() +
scale_y_log10(labels = scales::comma) +
::geom_text_repel(aes(label = system)) +
ggrepeltheme_light() +
labs(x = "",
y = "") +
theme(axis.ticks.y = element_blank(),
axis.text.y = element_blank(),
legend.position = "none") +
scale_color_manual(values = c("gray37", "blue"))
lang_whole_gg
# lang_whole_gg +
# inset_element(lang_latest_gg, 0.15, 0.5, 0.6, 0.95, align_to = 'full')