텐서플로와 머신러닝으로 시작하는 자연어 처리

로지스틱 회귀부터 트랜스포머 챗봇까지

이 책은 기존 자연어 처리 서적과는 다른 세 가지 특징을 가지고 있다.

첫째, 자연어 처리에 활용되는 개념적인 설명에서 끝나는 것이 아니라 모델 구현에 집중한다. 그뿐만 아니라 상용 서비스를 지원하는 텐서플로를 기반으로 모델을 개발한다.

둘째, 실무에서 자연어 처리 문제를 해결하는 데 조금이나마 도움이 되고자 캐글 대회의 문제를 활용했으며, 감정분석부터 유사도 처리, 챗봇에 이르기까지 다양한 문제를 다룬다.

셋째, 딥러닝 기반 자연어 처리를 다룬 서적은 대부분 번역서이기 때문에 주로 영어 데이터만 다루지만 이 책에서는 영어 데이터뿐만 아니라 한글 데이터를 활용한 문제 해결까지 다룬다.

 

추천사

"이론적인 설명에서 그치지 않고 데이터셋을 자세히 들여다보면서 인사이트를 얻고, 코드 레벨까지 깊게 내려가면서 이론을 손으로 만지듯 더듬으면서 이해할 수 있게 해주며, 간단한 딥러닝 코드가 만들어 내는 경이로운 정확도를 직접 확인할 수 있게 해주는 멋진 가이드가 될 것이다." -- 김성훈(홍콩과기대 / 네이버 Clova AI)

"딥러닝의 필수 요소에 대한 소개, 트랜스포머(Transformer) 같은 최신 모델에 대한 구체적인 설명까지 필요한 내용을 빠짐없이 다뤘습니다. 자연어 처리에 입문하려는 분들에게 이 책이 하나의 선택지로 자리하게 된 것을 다행스럽게 생각합니다." -- 박규병(카카오브레인 A.I. Researcher)

"단순히 최신 딥러닝 기반 자연어 처리 기술을 소개하는 것이 아니라 실질적인 코딩과 한국어의 특성 등을 충분히 어필하고 있기에 실제 자연어 처리를 도입해서 새로운 서비스나 앱 등을 개발하려는 분들에게 큰 도움이 될 것이다." -- 정지훈(경희사이버대 선임강의 교수, 모두의 연구소 Chief Vision Officer)

"복잡한 이론적 내용 때문에 어렵게만 여겨지던 딥러닝을 이 책을 통해 많은 사람들이 쉽고 재미있게 배울 수 있을 것이다." -- 주재걸(고려대 컴퓨터학과 교수)

"이런 좋은 책을 한글로 써주신 저자분께 감사드리며, 자연어 처리에 관심이 있는 분들께 많은 도움이 되리라 생각합니다. 강추합니다." -- 조대협(구글-조대협의 블로그)

"한국어로 쓰여진 자연어 처리 서적 가운데 이보다 방대하고 친절한 책은 없을 것이다." -- 이기창(네이버 Clova Chatbot, http://ratsgo.github.io의 블로그)

"최신 버전의 텐서플로를 기반으로 한국어/영어 자연어 처리를 비롯해 BERT의 기반이 되는 트랜스포머까지 설명해주는 책입니다." -- 신성진(네이버 Clova Chatbot)

 

도서 상세 이미지

전창욱

배우고 성장하기 위해 끊임없이 공부하는 것을 즐기며, 해마다 목표를 정하고 이뤄가는 재미에 푹 빠져 살아가고 있습니다. 배운 것은 만들어보고 이론과 실습을 같이 키워나가고 삶의 방향성을 찾기 위해 책을 읽는 시니어 개발자입니다. 머신러닝을 공부하면서 2016년 Google Hack Fair, Seoul Make Fair에 참여했고, 국립과천과학관 관장상과 2017년 서울혁신챌린지 혁신챌린지상을 수상했으며, KBS 시사교양 프로그램인 《명견만리》에 출연하고, 2018년 국어 정보 처리 시스템 경진 대회에서 금상을 수상했습니다. 현재 DeepNLP 연구실 리더이며 클라이언트, 서버, 데이터를 만지는 일을 하고 있습니다.

최태균

클래식 음악을 듣기 좋아하고 오랫동안 산책을 즐기는 소프트웨어 개발자입니다. 컴퓨터 분야에 이것저것 관심을 가지다 딥러닝과 자연어 처리를 우연히 접하게 되어 재미를 키워가고 있습니다. 서경대학교 컴퓨터과학과를 졸업했고 현재는 스타트업에서 일하고 있습니다.

조중현

중앙대학교에서 수학을 전공했으며, 우연한 기회로 개발을 접하게 됐습니다. 그 후 딥러닝이라는 분야에 관심을 가지고, 그중에서도 자연어 처리 분야를 집중적으로 공부하고 있습니다. 다양한 어려운 문제를 새로운 모델을 통해 해결하는 것이 딥러닝의 매력이라 생각합니다. 계속해서 세상의 많은 문제들을 딥러닝을 통해 해결하는 것을 목표로 삼고 있습니다.

감수자

신성진

머신러닝과 자연어 처리 분야의 전문가가 되고 싶은 마음 하나로 3년 전에 DeepNLP 연구실을 창설했습니다. 랩장님과 연구실 저자분들과 함께 끊임없이 배우고 도전하며 하루하루 즐겁게 살아가고 있습니다. 퍼듀대학교 산업공학과를 졸업하고 LG유플러스를 거쳐, 현재는 네이버 Clova에서 챗봇을 연구/개발하고 있습니다.

  • ▣ 1장: 들어가며
    • 이 책의 목표와 활용법
      • 아나콘다 설치
    • 실습 환경 구축
    • 가상 환경 구성
      • 실습 프로젝트 구성
      • pip 설치
      • 주피터 노트북
    • 정리
    •  
  • ▣ 2장: 자연어 처리 준비
    • 텐서플로
      • tf.keras.layers
      • tf.data
      • 에스티메이터(Estimator)
    • 사이킷런
      • 사이킷런을 이용한 데이터 분리
      • 사이킷런을 이용한 지도학습
      • 사이킷런을 이용한 비지도학습
      • 사이킷런을 이용한 특징 추출
      • TfidfVecotorizer
      • 자연어 토크나이징 도구
      • 영어 토크나이징 라이브러리
      • 한글 토크나이징 라이브러리
    • 그 밖의 라이브러리(전처리)
      • 넘파이
      • 판다스
      • Matplotlib
      • 맷플롯립 설치
      • Matplotlib.pyplot
      • re
    • 캐글 사용법
    • 정리
    •  
  • ▣ 3장: 자연어 처리 개요
    • 단어 표현
    • 텍스트 분류
      • 텍스트 분류의 예시
    • 텍스트 유사도
    • 자언어 생성
    • 기계 이해
    • 데이터 이해하기
    • 정리
    •  
  • ▣ 4장: 텍스트 분류
    • 영어 텍스트 분류
      • 문제 소개
      • 데이터 분석 및 전처리
      • 텍스트 모델링 소개
      • 분류 회귀 모델
      • TF-IDF를 활용한 모델 구현
      • 랜덤 포레스트 분류 모델
      • 순환 신경망 분류 모델
      • 컨볼루션 신경망 분류 모델
    • 한글 텍스트 분류
      • 문제 소개
      • 데이터 전처리 및 분석
      • 모델링
      • 마무리
    •  
  • ▣ 5장: 텍스트 유사도
    • 문제 소개
    • 데이터 분석과 전처리
      • XG 부스트 텍스트 유사도 분석 모델
    • 모델링
      • CNN 텍스트 유사도 분석 모델
      • 모델 구현
      • MaLSTM
    • 정리
    •  
  • ▣ 6장: 챗봇 만들기
    • 데이터 소개
    • 데이터 분석
    • 시퀀스 투 시퀀스 모델링
      • 모델 소개
    • 트랜스포머 네트워크
      • 모델 구현
    • 정리
    •  
  • ▣ 부록
    • 부록 A _ MaLSTM 모델
    • 부록 B _ Seq2Seq 모델
    • 부록 C _ 트랜스포머 모델
  • 목차 xiii쪽, 마지막 줄

    TfidfVecotorizer

    ==>

    TfidfVectorizer

  • 19쪽, 페이지 상단 첫 번째 예제 코드의 2번째 줄

    b = tf.Variable(tf.zeros([10]))
    

    ==>

    b = tf.Variable(tf.zeros([5]))
    
  • 21쪽, 상단 예제 코드 마지막 줄

    output = tf.keras.layers.dense(units = 2, activation = tf.nn.sigmoid)(hidden)
    

    ==>

    output = tf.keras.layers.Dense(units = 2, activation = tf.nn.sigmoid)(hidden)
    
  • 44쪽, 예제 코드의 14번째 줄

    output_layer = tf.keras.layers.Dense(1)(dense_layer_2)
    

    ==>

    output_layer = tf.keras.layers.Dense(1)(hidden_layer)
    
  • 63쪽, 본문 밑에서 6번째 줄

    TfidfVecotorizer ==> TfidfVectorizer

  • 64쪽, 본문 7번째 줄

    우선은 TFidfVecotirzer를 불러오자.

    ==>

    우선은 TfidfVectorizer를 불러오자.

  • 64쪽, 본문 밑에서 3번째 줄

    TfidfVecotorizer를 사용할 때도 앞에서

    ==>

    TfidfVectorizer를 사용할 때도 앞에서

  • 64쪽, 페이지 밑에서 두 번째 예제 코드의 마지막 줄

    print(tfidf_vectorizer.transform(text_data).toarray())
    

    ==>

    print(tfidf_vectorizer.transform(sentence).toarray())
    
  • 90쪽, 밑에서 1번째 줄

    여기서는 판다스와 라이브러리를

    ==>

    여기서는 판다스와 넘파이 라이브러리를

  • 102쪽, 본문 밑에서 8번째 줄

    conda install Beautiful Soup4
    

    ==>

    conda install -c anaconda beautifulsoup4
    
  • 121쪽, 본문 3번째 줄

    통해 백터화한다

    ==>

    통해 벡터화한다

  • 122쪽, 본문 밑에서 10번째 줄

    두 문장 A, B의 교집합 개수는 6개, A와 B의 합집합 개수는 24 (6+11+7)이므로 자카드 유사도는 $ \frac {6}{24} \approx 0.25 $다.

    ==>

    두 문장 A, B의 교집합 개수는 5개, A와 B의 합집합 개수는 24 (5+12+7)이므로 자카드 유사도는 $ \frac {5}{24} \approx 0.21 $이다.

  • 122쪽, 본문 밑에서 7번째 줄

    두 개의 백터값에서

    ==>

    두 개의 벡터값에서

  • 123쪽, 본문 6번째 줄

    유사도는 0.180으로 산출된다.

    ==>

    유사도는 0.113으로 산출된다.

  • 125쪽, 본문 1번째 줄

    초록색선을 의미한다. 다만 초록색 선은

    ==>

    검은색 선을 의미한다. 다만 검은색 선은

  • 125쪽, 본문 2번째 줄

    현실성이 없다. 맨하탄 거리는 파란색을 의미하며,

    ==>

    현실성이 없다. 가장 적합한 맨하탄 거리는 빨간색을 의미하며,

  • 137쪽, 본문 2번째 줄

    내용을 읽어서 data["sentence"] 배열에

    ==>

    내용을 읽어서 data["review"] 배열에

  • 137쪽, 페이지 하단의 예제 코드

    train_df = data(os.path.join(os.path.dirname(dataset), "aclImdb", "train"))
    test_df = data(os.path.join(os.path.dirname(dataset), "aclImdb", "test"))
    

    ==>

    train_df = data(os.path.join(os.path.dirname(data_set), "aclImdb", "train"))
    test_df = data(os.path.join(os.path.dirname(data_set), "aclImdb", "test"))
    
  • 141쪽, 페이지 상단 예제 코드의 4번째 줄

    labels=['Eojeol'],
    

    ==>

    labels=['token'],
    
  • 141쪽, 그림 3.25를 아래 그림으로 교체

  • 142쪽, 페이지 상단 예제 코드의 2번째 줄

    plt.boxplot([review_len_by_token],
    

    ==>

    plt.boxplot([review_len_by_eumjeol],
    
  • 142쪽, 그림 3.27을 아래 그림으로 교체

  • 148쪽, 그림 4.2

    백터 표상화

    ==>

    벡터 표상화

  • 160쪽, 페이지 상단 예제

    import re
    import pandas
    import numpy
    ...
    

    ==>

    import re
    import pandas as pd
    import numpy as np
    ...
    
  • 167쪽, 그림 4.9

    백터화 ==> 벡터화

  • 169쪽, 페이지 상단 첫 번째 예제

    for review in test_data['review']:
        clean_test_reviews.append(preprocessing(review, remove_stopwords = True))
    clean_test_df = pd.DataFrame({'review': clean_train_reviews, 'id': test_data['id']})
    test_id = np.array(test_data['id'])
    
    tokenizer.fit_on_texts(clean_test_reviews)
    text_sequences = tokenizer.texts_to_sequences(clean_test_reviews)
    test_inputs = pad_sequences(text_sequences, maxlen=MAX_SEQUENCE_LENGTH, padding='post')
    

    ==>

    for review in test_data['review']:
        clean_test_reviews.append(preprocessing(review, remove_stopwords = True))
    clean_test_df = pd.DataFrame({'review': clean_train_reviews, 'id': test_data['id']})
    test_id = np.array(test_data['id'])
    
    text_sequences = tokenizer.texts_to_sequences(clean_test_reviews)
    test_inputs = pad_sequences(text_sequences, maxlen=MAX_SEQUENCE_LENGTH, padding='post')
    
  • 172쪽, 페이지 상단 예제 코드

    DATA_OUT_PATH = './data_out/'
    TRAIN_CLEAN_DATA = 'train_clean.csv'
    
    train_data = pd.read_csv(DATA_OUT_PATH +TRAIN_CLEAN_DATA, header=0, delimiter="\t", quoting=3)
    
    reviews = list(train['review'])
    sentiments = list(train['sentiment'])
    

    ==>

    DATA_IN_PATH = './data_in/'
    TRAIN_CLEAN_DATA = 'train_clean.csv'
    
    train_data = pd.read_csv(DATA_IN_PATH + TRAIN_CLEAN_DATA, header=0, delimiter="\t", quoting=3)
    
    reviews = list(train_data['review'])
    sentiments = list(train_data['sentiment'])
    
  • 181쪽, 페이지 상단 예제 코드

    train_data_vecs = get_dataset(sentences, model, num_features)
    

    ==>

    test_data_vecs = get_dataset(sentences, model, num_features)
    
  • 201쪽, 페이지 상단 예제 코드 마지막 줄

    est = tf.estimator.Estimator(model_fn, model_dir=DATA_OUT_PATH + 'checkpoint')
    

    ==>

    est = tf.estimator.Estimator(model_fn, model_dir=DATA_OUT_PATH + 'checkpoint/rnn')
    
  • 230쪽, 예제 코드 12번째 줄

    conv = tf.keras.layers.Conv1d(
    

    ==>

    conv = tf.keras.layers.Conv1D(
    
  • 246쪽, 페이지 상단 예제 코드 3번째 줄

    plt.figure(figsize=(20, 15))
    

    ==>

    plt.figure(figsize=(15, 10))
    
  • 252쪽, 첫 번째 예제 코드의 10번째 줄

    print('질문이 가득 찼을 때: {:.2f}%'.format(fullstop * 100))
    

    ==>

    print('마침표를 포함한 질문: {:.2f}%'.format(fullstop * 100))
    
  • 253쪽, 페이지 상단 출력 결과의 첫 번째 줄

    질문이 가득 찼을 때: 6.31%
    

    ==>

    마침표를 포함한 질문: 6.31%
    
  • 263쪽, 페이지 상단 첫 번째 예제 코드

    (298526, 2, 11)
    

    ==>

    (298526, 2, 31)
    
  • 263쪽, 본문 첫 번째 줄

    두 질문이 각각 11개의 질문 길이를

    ==>

    두 질문이 각각 31개의 질문 길이를

  • 264쪽, 본문 밑에서 5번째 줄

    에폭을 의미하는 값으로는 10000을 설정했다.

    ==>

    에폭을 의미하는 값으로는 1000을 설정했다.

  • 273쪽, 세 번째 코드 블록

    embedding = tf.keras.layers.Embedding(vocabulary_size, embedding_size)
    base_embedded_matrix = embedding(features['base'])
    hypothesis_embedded_matrix = embedding(features['hypothesis'])
    

    ==>

    embedding = tf.keras.layers.Embedding(vocabulary_size, embedding_size)
    features['base'] ==> features['x1']
    features['hypothesis'] ==> features['x2']
    
  • 279쪽, 본문 밑에서 4번째 줄

    은닉 상태 백터로 사용된다.

    ==>

    은닉 상태 벡터로 사용된다.

  • 282쪽, 밑에서 9번째 줄

    의미 백터를 모두 구했다.

    ==>

    의미 벡터를 모두 구했다.

  • 305쪽, 상단 예제 코드의 5번째 줄

    UNK = "<UNKNWON>"
    

    ==>

    UNK = "<UNKNOWN>"
    
  • 311쪽, 페이지 상단 예제 코드의 첫 번째 줄

    with open(DEFINES.vocabulary_path, 'w') as vocabulary_file:
    

    ==>

    with open(DEFINES.vocabulary_path, 'w', encoding='utf-8') as vocabulary_file:
    
  • 317쪽, 페이지 상단 예제 코드의 첫 번째 줄

    def Model(features, labels, mode, params):
    

    ==>

    def model(features, labels, mode, params):
    
  • 346쪽, 그림 6.26에서 다음 용어를 수정(3군데)

    시숸스 ==> 시퀀스

  • 372쪽, 예제 코드의 3번째 줄

    UNK = "<UNKNWON>"
    

    ==>

    UNK = "<UNKNOWN>"
    
  • 379쪽, 예제 코드의 6번째 줄

    # UNK = "<UNKNWON>"
    

    ==>

    # UNK = "<UNKNOWN>"
    
  • 379쪽, 예제 코드의 9번째 줄

    with open(DEFINES.vocabulary_path, 'w') as vocabulary_file:
    

    ==>

    with open(DEFINES.vocabulary_path, 'w', encoding='utf-8') as vocabulary_file:
    
  • 383쪽, 예제 코드의 4번째 줄

    cell = tf.contrib.rnn.DropoutWrapper(cell, output_keep_prob=DEFINES.dropout_width)
    

    ==>

    cell = tf.contrib.rnn.DropoutWrapper(cell, state_keep_prob=DEFINES.dropout_width)
    
  • 386쪽, 예제 코드의 7번째 줄

    if TRAIN:
      if i > 0:
        # tf.cond를 통해 rnn에 입력할 입력 임베딩 벡터를 결정
        # 여기서 true인 경우에는 입력된 output 값, 아닌 경우에는 이전 스텝에
        # 나온 output을 사용
        input_token_emb = tf.cond(
          tf.logical_and( # 논리 and 연산자
            True,
            tf.random_uniform(shape=(), maxval=1) <= params['teacher_forcing_rate'] # teacher_forcing_rate 퍼센트 값에 따른 labels 지원
          ),
          lambda: tf.nn.embedding_lookup(embedding_encoder, labels[:, i-1]), # labels 정답을 넣음
          lambda: tf.nn.embedding_lookup(embedding_encoder, output_token) # 모델이 정답이라고 생각하는 값
        )
      else:
        input_token_emb = tf.nn.embedding_lookup(embedding_encoder, output_token) # 모델이 정답이라고 생각하는 값
    else: # 훈련이 아닌 평가와 예측은 else에서 진행
      input_token_emb = tf.nn.embedding_lookup(embedding_encoder, output_token)
    

    ==>

    if TRAIN:
      if i > 0:
        # tf.cond를 통해 rnn에 입력할 입력 임베딩 벡터를 결정
        # 여기서 true인 경우에는 입력된 output 값, 아닌 경우에는 이전 스텝에
        # 나온 output을 사용
        input_token_emb = tf.cond(
          tf.logical_and( # 논리 and 연산자
            True,
            tf.random_uniform(shape=(), maxval=1) <= params['teacher_forcing_rate'] # teacher_forcing_rate 퍼센트 값에 따른 labels 지원
          ),
          lambda: tf.nn.embedding_lookup(embedding_decoder, labels[:, i-1]), # labels 정답을 넣음
          lambda: tf.nn.embedding_lookup(embedding_decoder, output_token) # 모델이 정답이라고 생각하는 값
        )
      else:
        input_token_emb = tf.nn.embedding_lookup(embedding_decoder, output_token) # 모델이 정답이라고 생각하는 값
    else: # 훈련이 아닌 평가와 예측은 else에서 진행
      input_token_emb = tf.nn.embedding_lookup(embedding_decoder, output_token)
    
  • 393쪽, 예제 코드 밑에서 13번째 줄

    self.value_dense = tf.keras.layers.Dense(num_units, activation=tf.nn.relu)
    

    ==>

    self.value_dense = tf.keras.layers.Dense(num_units, activation=tf.nn.relu)
    
    self.dense = tf.keras.layers.Dense(num_units)
    
  • 393쪽, 예제 코드 밑에서 10번째 줄

    key_seq_length = float(key.get_shape().as_list()[-2])
    

    ==>

    key_seq_length = float(key.get_shape().as_list()[-1])
    
  • 394쪽, 예제 코드의 19번째 줄

    attn_outputs = tf.concat(tf.split(attention_map, self.heads, axis=0), axis=-1)
    

    ==>

    attn_outputs = tf.concat(tf.split(attention_map, self.heads, axis=0), axis=-1)
    attn_outputs = self.dense(attn_outputs)
    
  • 399쪽, 예제 코드 파일명을 다음과 같이 수정

    model.py

    ==>

    main.py