챗GPT와 데이터 과학

대통령 취임사 임베딩

이광춘

비영리법인 한국 R 사용자회

2023년 5월 11일

목차

  1. 챗GPT와 오정보
  2. 한국어 임베딩과 R언어 (홍성학)
  3. OpenAI 임베딩 모형
  4. 대통령 취임사
  5. 질의응답

서울 R 미트업

참여와 연대

  • 서울R미트업 meetup.com: link
    • 23년 4월 서울 R 미트업: link
    • 23년 5월 서울 R 미트업: link
    • 23년 6월 서울 R 미트업: 준비중
  • 세계 R 미트업 현황 (Global R Meetup Dashboard): link
  • 한국 R 사용자회 (Korea R User Group): link
  • 한국 R 컨퍼런스 (Korea R Conference): link
  • 유튜브 채널 (Youtube Channel): link
  • 페이스북 그룹 (Facebook Group): link

챗GPT와 오정보

We Have No Moat

flowchart LR
   subgraph "2021년 R 컨퍼런스: 챗GPT 이전"
    A["R 컨퍼런스"] --> AB["키노트"] --> B["제작방법"]
    end
    
    subgraph "서울 R 미트업: 챗GPT 이후"
    direction LR
        Youtube["챗GPT 오정보"] --> STT["STT: Whisper"]
        Youtube                 --> Summary["요약: Langchain"]
        Youtube                 --> Embedding["임베딩: Pinecone"]
        Youtube                 --> ETC["..."]
    end

    B -.-> Moat["We Have No Moat,<br> And Neither Does OpenAI"]
    STT -.-> Moat 
    Summary -.-> Moat 
    Embedding -.->  Moat
    ETC -.->  Moat

click A href "https://use-r.kr/"
click AB href "https://www.youtube.com/watch?v=clNN1xAiEkw"
click B href "https://statkclee.github.io/deep-learning/rconf-keynote.html"

click Youtube href "https://www.youtube.com/watch?v=csqOtqFBybk"
click STT href "https://r2bit.com/chatGPT/whisper.html"
click Summary href "https://r2bit.com/chatGPT/news_release.html"
click Embedding href "https://r2bit.com/chatGPT/misinfo_embedding.html"

click Moat href "https://www.semianalysis.com/p/google-we-have-no-moat-and-neither"


class A nodeStyle
class AB,AC,AD,AE theoryStyle
class B,C,D,E practiceStyle
class F taskStyle

classDef nodeStyle fill:#93c47d,stroke:#000000,stroke-width:0.7px,font-weight:bold,font-size:14px;
classDef theoryStyle fill:#f59542,stroke:#000000,stroke-width:0.7px,font-weight:bold,font-size:14px;
classDef practiceStyle fill:#4287f5,stroke:#000000,stroke-width:0.7px,font-weight:bold,font-size:14px;
classDef taskStyle fill:#42f548,stroke:#000000,stroke-width:0.7px,font-weight:bold,font-size:12px;

2022년

OpenAI 임베딩 모형

챗GPT API 가격

OpenAI 임베딩

Models Use Cases
Text similarity: Captures semantic similarity between pieces of text. text-similarity-{ada, babbage, curie, davinci}-001 Clustering, regression, anomaly detection, visualization
Text search: Semantic information retrieval over documents. text-search-{ada, babbage, curie, davinci}-{query, doc}-001 Search, context relevance, information retrieval
Code search: Find relevant code with a query in natural language. code-search-{ada, babbage}-{code, text}-001 Code search and relevance

대통령 취임사

작업흐름



원천 데이터



파일 연설문

library(tidyverse)

presidents <- fs::dir_ls("../../data/Inaugural/")

inaugural_tbl <- presidents %>% 
  enframe(name = "파일경로") %>% 
  separate(value, into = c("역대", "대통령"), sep="_") %>% 
  mutate(대통령 = str_remove(대통령, "\\.txt")) %>% 
  mutate(취임사 = map(파일경로, read_lines)) %>% 
  mutate(취임사 = map_chr(취임사, paste0, collapse = " ")) %>% 
  select(역대, 대통령, 취임사) %>% 
  mutate(취임사 = str_squish(취임사)) %>% 
  mutate(취임사 = str_replace_all(취임사, "ㆍ", ", "))

inaugural_tbl 

파일 연설문

# A tibble: 13 × 3
   역대                        대통령 취임사                                    
   <chr>                       <chr>  <chr>                                     
 1 ../../data/Inaugural/제03대 이승만 "나의 사랑하는 동포 여러분. 내가 오늘 또 …
 2 ../../data/Inaugural/제04대 윤보선 "제2공화국의 초대대통령으로 영예의 당선을…
 3 ../../data/Inaugural/제09대 박정희 "친애하는 5천만 동포 여러분! 그리고 내외 …
 4 ../../data/Inaugural/제10대 최규하 "친애하는 국내외 동포 여러분! 그리고 이 … 
 5 ../../data/Inaugural/제12대 전두환 "친애하는 국내외 동포 여러분! 그리고 이 … 
 6 ../../data/Inaugural/제13대 노태우 "친애하는 6천만 국내외 동포 여러분. 우리 …
 7 ../../data/Inaugural/제14대 김영삼 "친애하는 7천만 국내외 동포 여러분, 노태… 
 8 ../../data/Inaugural/제15대 김대중 "존경하고 사랑하는 국민 여러분! 오늘 저는…
 9 ../../data/Inaugural/제16대 노무현 "존경하는 국민 여러분. 오늘 저는 대한민국…
10 ../../data/Inaugural/제17대 이명박 "존경하는 국민 여러분! 700만 해외동포 여… 
11 ../../data/Inaugural/제18대 박근혜 "희망의 새 시대를 열겠습니다. 존경하는 국…
12 ../../data/Inaugural/제19대 문재인 "존경하고 사랑하는 국민 여러분! 감사합니… 
13 ../../data/Inaugural/제20대 윤석열 "존경하고 사랑하는 국민 여러분, 750만 재… 

임베딩


library(httr)

get_embedding <- function(inaugural_address) {
  embeddings_url <- "https://api.openai.com/v1/embeddings"
  auth <- add_headers(Authorization = paste("Bearer", Sys.getenv("OPENAI_API_KEY")))
  body <- list(model = "text-embedding-ada-002", input = inaugural_address)
  
  resp <- POST(
    embeddings_url,
    auth,
    body = body,
    encode = "json"
  )
  
  embeddings <- content(resp, as = "text", encoding = "UTF-8") %>%
    jsonlite::fromJSON(flatten = TRUE) %>%
    pluck("data", "embedding")
  
  Sys.sleep(1)
  
  return(embeddings)
}

# 1. 국문 취임사
embedding_tbl <- inaugural_tbl %>% 
  mutate(임베딩 = map(취임사, get_embedding)) %>% 
  pull()

임베딩 벡터


embedding_tbl <-  
  read_rds("../../data/Inaugural.rds") %>% 
  mutate(구분 = "국문 취임사")

translated_embedding <- 
  read_rds("../../data/translated_embedding.rds") %>% 
  mutate(구분 = "영문번역 취임사") %>% 
  rename(취임사 = 영문번역)

inaugural_eng_embedding <- 
  read_rds("../../data/inaugural_eng_embedding.rds") %>% 
  mutate(구분 = "영문 취임사") %>% 
  rename(취임사 = 영문취임사)

api_embedding <- 
  bind_rows(embedding_tbl, translated_embedding) %>% 
  bind_rows(inaugural_eng_embedding)

api_embedding %>% 
  mutate(취임사 = str_sub(취임사, 1, 10)) %>% 
  mutate(임베딩벡터 = map(임베딩, pluck, 1)) %>% 
  mutate(벡터길이 = map_int(임베딩벡터, length)) %>% 
  select(구분, 대통령, 취임사, 임베딩, 임베딩벡터, 벡터길이) 

임베딩 벡터

# A tibble: 31 × 6
   구분        대통령 취임사               임베딩     임베딩벡터    벡터길이
   <chr>       <chr>  <chr>                <list>     <list>           <int>
 1 국문 취임사 이승만 "나의 사랑하는 동포" <list [1]> <dbl [1,536]>     1536
 2 국문 취임사 윤보선 "제2공화국의 초대대" <list [1]> <dbl [1,536]>     1536
 3 국문 취임사 박정희 "친애하는 5천만 동"  <list [1]> <dbl [1,536]>     1536
 4 국문 취임사 최규하 "친애하는 국내외 동" <list [1]> <dbl [1,536]>     1536
 5 국문 취임사 전두환 "친애하는 국내외 동" <list [1]> <dbl [1,536]>     1536
 6 국문 취임사 노태우 "친애하는 6천만 국"  <list [1]> <dbl [1,536]>     1536
 7 국문 취임사 김영삼 "친애하는 7천만 국"  <list [1]> <dbl [1,536]>     1536
 8 국문 취임사 김대중 "존경하고 사랑하는 " <list [1]> <dbl [1,536]>     1536
 9 국문 취임사 노무현 "존경하는 국민 여러" <list [1]> <dbl [1,536]>     1536
10 국문 취임사 이명박 "존경하는 국민 여러" <NULL>     <NULL>               0
# ℹ 21 more rows

(코사인) 유사도


두 임베딩 벡터 \(\mathbf{u}\)\(\mathbf{v}\) 는 다음 공식을 사용하여 두 벡터간의 유사도를 구할 수 있다.



\[ \begin{equation*} \text{코사인 유사도}(\mathbf{u},\mathbf{v}) = \frac{\mathbf{u} \cdot \mathbf{v}}{\|\mathbf{u}\| \|\mathbf{v}\|} = \frac{\sum_{i=1}^n u_i v_i}{\sqrt{\sum_{i=1}^n u_i^2} \sqrt{\sum_{i=1}^n v_i^2}} \end{equation*} \]

윤석열 대통령 취임사


library(reactable)

api_embedding_tbl <- api_embedding %>% 
  select(구분, 대통령, 임베딩) %>% 
  mutate(임베딩 = map(임베딩, unlist)) %>% 
  mutate(임베딩크기 = str_length(임베딩)) %>% 
  filter(임베딩크기 > 100)

yoon_embedding <- api_embedding_tbl$임베딩[12][[1]]

yoon_list <- list()

for(i in 1:nrow(api_embedding_tbl)) {
  yoon_list[[i]] <- lsa::cosine(yoon_embedding, api_embedding_tbl$임베딩[[i]])
}

api_embedding_tbl %>% 
  select(-임베딩, -임베딩크기) %>% 
  mutate(유사도 = yoon_list %>% unlist) %>% 
  arrange(desc(유사도)) %>% 
  reactable::reactable(
    columns = list(유사도 = colDef(format = colFormat(separators = TRUE, digits = 3))),
    # Table Theme
    theme = reactableTheme(
      backgroundColor = "#1D2024", color = "white", borderColor = "#666666",
      paginationStyle = list(color = "white"), 
      selectStyle = list(color = "black"),
      headerStyle = list(color = "white", fontFamily = "NanumGothic"),
      cellStyle = list(color = "#FAFAFA", 
                        fontFamily = "NanumGothic, Consolas, Monaco, monospace", 
                        fontSize = "14px")
                           ))  

윤석열 대통령 취임사

전체 취임사 유사도


library(umap)

embeddings_mat <- matrix(
  unlist(api_embedding_tbl$임베딩), 
  ncol = 1536, byrow = TRUE
)

embeddings_similarity <- embeddings_mat / sqrt(rowSums(embeddings_mat * embeddings_mat))
embeddings_similarity <- embeddings_similarity %*% t(embeddings_similarity)

## 정방행렬을 데이터프레임으로 변환

취임사_구분자 <- api_embedding_tbl %>% 
  mutate(구분명 = glue::glue("{대통령}_{str_remove(구분, ' ?취임사 ?')}")) %>% 
  pull(구분명)

취임사_colnames_tbl <- tibble(취임사_구분자 = 취임사_구분자) %>% 
  mutate(name = glue::glue("V{1:30}"))

embeddings_similarity_tbl <- embeddings_similarity %>% 
  as.data.frame %>% 
  mutate(구분자 = 취임사_구분자) %>%
  column_to_rownames(var = "구분자") %>%
  tibble::rownames_to_column()  %>%
  tidyr::pivot_longer(-rowname) %>% 
  # From A to B
  left_join(취임사_colnames_tbl) %>% 
  select(취임사_A = rowname, 취임사_B = 취임사_구분자, 유사도 = value)

embeddings_similarity_tbl %>% 
  filter(유사도 < 0.9999 ) %>% 
  arrange(desc(유사도)) %>% 
  mutate(대통령_A = str_extract(취임사_A, "[^_]+(?=_)"),
         대통령_B = str_extract(취임사_B, "[^_]+(?=_)")) %>% 
  filter(대통령_A != 대통령_B) %>% 
  select(취임사_A, 취임사_B, 유사도) %>% 
  reactable::reactable(
    columns = list(유사도 = colDef(format = colFormat(separators = TRUE, digits = 3))),
    # Table Theme
    theme = reactableTheme(
      backgroundColor = "#1D2024", color = "white", borderColor = "#666666",
      paginationStyle = list(color = "white"), 
      selectStyle = list(color = "black"),
      headerStyle = list(color = "white", fontFamily = "NanumGothic"),
      cellStyle = list(color = "#FAFAFA", 
                        fontFamily = "NanumGothic, Consolas, Monaco, monospace", 
                        fontSize = "14px")
                           ))

전체 취임사 유사도

시각화

library(ggrepel)
extrafont::loadfonts()

inaugural_umap <- umap(embeddings_mat)

umap_df <- inaugural_umap$layout %>%
  as.data.frame()%>%
  rename(UMAP1="V1",
         UMAP2="V2") %>% 
  bind_cols(api_embedding_tbl) %>% 
  mutate(구분명 = glue::glue("{대통령}_{str_remove(구분, ' ?취임사 ?')}")) %>% 
  select(UMAP1, UMAP2, 구분명)

umap_df %>%
  ggplot(aes(x = UMAP1, 
             y = UMAP2))+
    geom_point(size = 1.3, alpha = 0.8) +
    geom_text_repel(aes(label=구분명)) +
    theme_bw(base_family="NanumGothic")

# 영어만 추출
only_english_tbl <- api_embedding_tbl %>% 
  filter(str_detect(구분, "영문"))

english_embeddings_mat <- matrix(
  unlist(only_english_tbl$임베딩), 
  ncol = 1536, byrow = TRUE
)

# 시각화
english_umap <- umap(english_embeddings_mat)

english_umap_df <- english_umap$layout %>%
  as.data.frame()%>%
  rename(UMAP1="V1",
         UMAP2="V2") %>% 
  bind_cols(only_english_tbl) %>% 
  mutate(구분명 = glue::glue("{대통령}_{str_remove(구분, ' ?취임사 ?')}")) %>% 
  select(UMAP1, UMAP2, 구분명)

english_umap_df %>%
  separate(구분명, into = c("대통령", "번역여부"), sep = "_") %>% 
  ggplot(aes(x = UMAP1, 
             y = UMAP2))+
    geom_point(aes(color = 번역여부), size = 1.3, alpha = 0.8) +
    geom_text_repel(aes(label=대통령)) +
    theme_bw(base_family="NanumGothic") +
    theme(legend.position = "top") +
    labs(title = "대통령 취임사 유사도")

참고문헌

Reimers, N. (2022). OpenAI GPT-3 Text Embeddings - Really a new state-of-the-art in dense text embeddings? Medium.com. https://medium.com/@nils_reimers/openai-gpt-3-text-embeddings-really-a-new-state-of-the-art-in-dense-text-embeddings-6571fe3ec9d9