Potato
μ•ˆλ…•ν•˜μ„Έμš”, κ°μž‘λ‹ˆλ‹€?πŸ₯” ^___^ 😺 github λ°”λ‘œκ°€κΈ° πŸ‘‰πŸ»

AI study/μžμ—°μ–΄ 처리 (NLP)

[NLP] λ ˆμŠ€ν† λž‘ 리뷰 감성 λΆ„λ₯˜ν•˜κΈ° (2) (feat.νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리) - 데이터 처리λ₯Ό μœ„ν•œ 클래슀 μ‚΄νŽ΄λ³΄κΈ°

감자 πŸ₯” 2021. 7. 22. 18:48
λ°˜μ‘ν˜•

-- λ³Έ ν¬μŠ€νŒ…μ€ νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리 (ν•œλΉ›λ―Έλ””μ–΄) 책을 μ°Έκ³ ν•΄μ„œ μž‘μ„±λœ κΈ€μž…λ‹ˆλ‹€.
-- μ†ŒμŠ€μ½”λ“œ ) https://github.com/rickiepark/nlp-with-pytorch

 

rickiepark/nlp-with-pytorch

<νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리>(ν•œλΉ›λ―Έλ””μ–΄, 2021)의 μ†ŒμŠ€ μ½”λ“œλ₯Ό μœ„ν•œ μ €μž₯μ†Œμž…λ‹ˆλ‹€. - rickiepark/nlp-with-pytorch

github.com

 

β–Ά λ ˆμŠ€ν† λž‘ 리뷰 감성 λΆ„λ₯˜ν•˜κΈ°

μ•„λž˜ ν¬μŠ€νŒ…μ—μ„œ μ–΄λ–»κ²Œ μ „μ²˜λ¦¬λœ 데이터λ₯Ό μ‚¬μš©ν•˜κ²Œ λλŠ”μ§€ 미리 ν•™μŠ΅ν•˜λ©΄ 쒋을 것이닀. ν•˜μ§€λ§Œ ν•΄λ‹Ή μ½”λ“œμ—μ„œλŠ” 'μ „μ²˜λ¦¬ κ³Όμ •'이 μƒλž΅λœ 'μ „μ²˜λ¦¬ λ˜μ–΄μ§„ 데이터'λ₯Ό ν™œμš©ν•˜κ³  μžˆλ‹€λŠ” κ²ƒλ§Œ μ•Œμ•„λ‘μž.

https://didu-story.tistory.com/83

 

[NLP] λ ˆμŠ€ν† λž‘ 리뷰 감성 λΆ„λ₯˜ν•˜κΈ° (1) (feat.νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리)

-- λ³Έ ν¬μŠ€νŒ…μ€ νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리 (ν•œλΉ›λ―Έλ””μ–΄) 책을 μ°Έκ³ ν•΄μ„œ μž‘μ„±λœ κΈ€μž…λ‹ˆλ‹€. -- μ†ŒμŠ€μ½”λ“œ ) https://github.com/rickiepark/nlp-with-pytorch (ν•œλΉ›λ―Έλ””μ–΄, 2021)의 μ†ŒμŠ€ μ½”λ“œλ₯Ό μœ„ν•œ μ €μž₯μ†Œμž…..

didu-story.tistory.com

 

 

1. νŒŒμ΄ν† μΉ˜ 데이터셋 μ΄ν•΄ν•˜κΈ°

ν•΄λ‹Ή ν”„λ‘œμ νŠΈλŠ” pytorch ν”„λ ˆμž„μ›Œν¬λ₯Ό 기반으둜 μ§„ν–‰λœλ‹€. 그리고 클래슀 객체λ₯Ό ν™œμš©ν•΄μ„œ μ£Όμš”ν•œ νŒŒμ΄ν”„λΌμΈμ„ μˆ˜ν–‰ν•˜κ²Œ λœλ‹€. 파이썬의 ν΄λž˜μŠ€κ°μ²΄μ— λŒ€ν•œ 이해가 λΆ€μ‘±ν•˜λ‹€λ©΄ μ•„λž˜ ν¬μŠ€νŒ…μ„ μ°Έκ³ ν•˜κ³  μ΄ν•΄ν•˜κ³  였자. λ˜ν•œ νŒŒμ΄ν† μΉ˜λŠ” Dataset, DataLoader λ₯Ό ν™œμš©ν•˜μ—¬ λ°μ΄ν„°μ˜ ν˜•νƒœλ₯Ό λ³€ν™˜μ‹œμΌœμ£ΌλŠ”λ°, μ΄μ—λŒ€ν•œ 것도 μ•„λž˜ ν¬μŠ€νŒ…μ„ μ°Έκ³ ν•˜λ©΄ 이해가 더 μ‰¬μšΈ 것이닀.

https://didu-story.tistory.com/85

 

[Pytorch] νŒŒμ΄ν† μΉ˜μ˜ Custom datasetκ³Ό DataLoader μ΄ν•΄ν•˜κΈ°

1. νŒŒμ΄ν† μΉ˜μ˜ Custom dataset / DataLoader 1.1 Custom Dataset 을 μ‚¬μš©ν•˜λŠ” 이유 λ°©λŒ€ν•œ λ°μ΄ν„°μ˜ μ–‘ --> 데이터λ₯Ό ν•œ λ²ˆμ— 뢈러였기 쉽지 μ•ŠμŒ 데이터λ₯Ό ν•œ λ²ˆμ— λΆ€λ₯΄μ§€ μ•Šκ³  ν•˜λ‚˜μ”©λ§Œ λΆˆλŸ¬μ„œ μ“°λŠ” 방식을 택

didu-story.tistory.com

https://didu-story.tistory.com/84

 

[파이썬] 클래슀 객체

1. 클래슀(class)λž€ ν΄λž˜μŠ€λŠ” 객체의 ꡬ쑰와 행동을 μ •μ˜ν•œλ‹€ 객체의 ν΄λž˜μŠ€λŠ” μ΄ˆκΈ°ν™”λ₯Ό 톡해 μ œμ–΄ν•œλ‹€. (__init__) 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°, λ³΅μž‘ν•œ 문제λ₯Ό κ°„λ‹¨ν•˜κ²Œ ν•΄κ²°ν•  μžˆλ‹€λŠ” μž₯점 쑴재 2. 클래슀

didu-story.tistory.com

 

1.1 Review Dataset

  • ReviewDataset ν΄λž˜μŠ€λŠ” 데이터셋이 μ΅œμ†Œν•œμœΌλ‘œ μ •μ œλ˜κ³  3개둜 λ‚˜λ‰˜μ—ˆλ‹€κ³  κ°€μ •ν•œλ‹€.
  • 특히 ν•΄λ‹Ή 데이터셋은 곡백을 κΈ°μ€€μœΌλ‘œ λ‚˜λˆ μ„œ 토큰 리슀트λ₯Ό 얻을 수 μžˆλ‹€κ³  κ°€μ •ν•œλ‹€.
  • μƒ˜ν”Œμ΄ ν›ˆλ ¨, 검증, ν…ŒμŠ€νŠΈ 쀑 μ–΄λŠ μ„ΈνŠΈμ— μžˆλŠ”μ§€ ν‘œμ‹œλ˜μ—ˆλ‹€κ³  κ°€μ •ν•œλ‹€.
class ReviewDataset(Dataset):
    
    #클래슀 μΈμŠ€ν„΄μŠ€ μƒμ„±μ‹œ μ΄ˆκΈ°ν™” ν•˜λ©΄μ„œ μ‹€ν–‰λ˜λŠ” λΆ€λΆ„
    # self λž€? μΈμŠ€ν„΄μŠ€(ν΄λž˜μŠ€μ— μ˜ν•΄ λ§Œλ“€μ–΄μ§„ 객체) μžκΈ°μžμ‹ μ„ 의미,
    # self κ°€ μžˆλŠ” 것이 μΈμŠ€ν„΄μŠ€ λ³€μˆ˜ 
    def __init__(self, review_df, vectorizer):
        """
        λ§€κ°œλ³€μˆ˜:
            review_df (pandas.DataFrame): 데이터셋
            vectorizer (ReviewVectorizer): ReviewVectorizer 객체
        """
        self.review_df = review_df
        self._vectorizer = vectorizer

        self.train_df = self.review_df[self.review_df.split=='train']
        self.train_size = len(self.train_df)

        self.val_df = self.review_df[self.review_df.split=='val']
        self.validation_size = len(self.val_df)

        self.test_df = self.review_df[self.review_df.split=='test']
        self.test_size = len(self.test_df)

        self._lookup_dict = {'train': (self.train_df, self.train_size),
                             'val': (self.val_df, self.validation_size),
                             'test': (self.test_df, self.test_size)}

        self.set_split('train')

    # 클래슀 λ©”μ†Œλ“œ (λ°μ½”λ ˆμ΄ν„° μ‚¬μš©) = 클래슀 λ³€μˆ˜λ₯Ό 컨트둀 ν•  λ•Œ μ‚¬μš©λœλ‹€.
    # cls 인자λ₯Ό λ°›μŒ. cls? ReviewDataset 클래슀λ₯Ό λœ»ν•¨
    @classmethod
    def load_dataset_and_make_vectorizer(cls, review_csv):
        # ReviewVectorizer객체 = μ•„λž˜ λ‚˜μ˜΄ (μ–΄νœ˜μ‚¬μ „μ„ μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” 객체)
        """ 데이터셋을 λ‘œλ“œν•˜κ³  μƒˆλ‘œμš΄ ReviewVectorizer 객체λ₯Ό λ§Œλ“­λ‹ˆλ‹€ 
        
        λ§€κ°œλ³€μˆ˜:
            review_csv (str): λ°μ΄ν„°μ…‹μ˜ μœ„μΉ˜
        λ°˜ν™˜κ°’:
            ReviewDataset의 μΈμŠ€ν„΄μŠ€
        """
        review_df = pd.read_csv(review_csv)  # μ•„λž˜μ—μ„œ review_csvλ₯Ό 톡해 μ •μ œλœ 데이터λ₯Ό κ°€μ Έμ˜¬κ±°μ•Ό
        train_review_df = review_df[review_df.split=='train']
        return cls(review_df, ReviewVectorizer.from_dataframe(train_review_df))
    
    @classmethod
    def load_dataset_and_load_vectorizer(cls, review_csv, vectorizer_filepath):
        """ 데이터셋을 λ‘œλ“œν•˜κ³  μƒˆλ‘œμš΄ ReviewVectorizer 객체λ₯Ό λ§Œλ“­λ‹ˆλ‹€.
        μΊμ‹œλœ ReviewVectorizer 객체λ₯Ό μž¬μ‚¬μš©ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
        
        λ§€κ°œλ³€μˆ˜:
            review_csv (str): λ°μ΄ν„°μ…‹μ˜ μœ„μΉ˜
            vectorizer_filepath (str): ReviewVectorizer 객체의 μ €μž₯ μœ„μΉ˜
        λ°˜ν™˜κ°’:
            ReviewDataset의 μΈμŠ€ν„΄μŠ€
        """
        review_df = pd.read_csv(review_csv)
        vectorizer = cls.load_vectorizer_only(vectorizer_filepath)
        return cls(review_df, vectorizer)

    @staticmethod
    def load_vectorizer_only(vectorizer_filepath):
        """ νŒŒμΌμ—μ„œ ReviewVectorizer 객체λ₯Ό λ‘œλ“œν•˜λŠ” 정적 λ©”μ„œλ“œ
        
        λ§€κ°œλ³€μˆ˜:
            vectorizer_filepath (str): μ§λ ¬ν™”λœ ReviewVectorizer 객체의 μœ„μΉ˜
        λ°˜ν™˜κ°’:
            ReviewVectorizer의 μΈμŠ€ν„΄μŠ€
        """
        with open(vectorizer_filepath) as fp:
            return ReviewVectorizer.from_serializable(json.load(fp))

    def save_vectorizer(self, vectorizer_filepath):
        """ ReviewVectorizer 객체λ₯Ό json ν˜•νƒœλ‘œ λ””μŠ€ν¬μ— μ €μž₯ν•©λ‹ˆλ‹€
        
        λ§€κ°œλ³€μˆ˜:
            vectorizer_filepath (str): ReviewVectorizer 객체의 μ €μž₯ μœ„μΉ˜
        """
        with open(vectorizer_filepath, "w") as fp:
            json.dump(self._vectorizer.to_serializable(), fp)

    def get_vectorizer(self):
        """ 벑터 λ³€ν™˜ 객체λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€ """
        return self._vectorizer

    def set_split(self, split="train"):
        """ λ°μ΄ν„°ν”„λ ˆμž„μ— μžˆλŠ” 열을 μ‚¬μš©ν•΄ λΆ„ν•  μ„ΈνŠΈλ₯Ό μ„ νƒν•©λ‹ˆλ‹€ 
        
        λ§€κ°œλ³€μˆ˜:
            split (str): "train", "val", "test" 쀑 ν•˜λ‚˜
        """
        self._target_split = split
        self._target_df, self._target_size = self._lookup_dict[split]

    def __len__(self):
        return self._target_size

    def __getitem__(self, index):
        """ νŒŒμ΄ν† μΉ˜ λ°μ΄ν„°μ…‹μ˜ μ£Όμš” μ§„μž… λ©”μ„œλ“œ
        
        λ§€κ°œλ³€μˆ˜:
            index (int): 데이터 포인트의 인덱슀
        λ°˜ν™˜κ°’:
            데이터 포인트의 νŠΉμ„±(x_data)κ³Ό λ ˆμ΄λΈ”(y_target)둜 이루어진 λ”•μ…”λ„ˆλ¦¬
        """
        row = self._target_df.iloc[index]

        review_vector = \
            self._vectorizer.vectorize(row.review)

        rating_index = \
            self._vectorizer.rating_vocab.lookup_token(row.rating)

        return {'x_data': review_vector,
                'y_target': rating_index}

    def get_num_batches(self, batch_size):
        """ 배치 크기가 주어지면 λ°μ΄ν„°μ…‹μœΌλ‘œ λ§Œλ“€ 수 μžˆλŠ” 배치 개수λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€
        
        λ§€κ°œλ³€μˆ˜:
            batch_size (int)
        λ°˜ν™˜κ°’:
            배치 개수
        """
        return len(self) // batch_size
  • νŒŒμ΄ν† μΉ˜μ—μ„œ μƒˆλ‘œμš΄ 데이터셋을 μ‚¬μš©ν•˜λ €λ©΄ λ¨Όμ € Dataset 클래슀λ₯Ό μƒμ†ν•˜μ—¬ __getitem()__, __len__() λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•΄μ•Όν•œλ‹€.
    • from torch.utils.data import Dataset, DataLoader
  • ν•΄λ‹Ή 클래슀 μ•ˆμ—μ„œλŠ” λ‹€μ–‘ν•œ νŒŒμ΄ν† μΉ˜ μœ ν‹Έλ¦¬ν‹°λ₯Ό μ‚¬μš©ν•˜λŠ”λ°, DataLoader / ReviewVectorizer κ³Ό 같은 ν΄λž˜μŠ€λŠ” μ•„λž˜μ— λ‚˜μ˜¨λ‹€. (ν΄λž˜μŠ€λ“€μ€ μ„œλ‘œ 크게 μ˜μ‘΄ν•œλ‹€.)
    • ReviewVectorizer => λ¦¬λ·°ν…μŠ€νŠΈλ₯Ό 수치둜 λ³€ν™˜ν•˜λŠ” 클래슀
    • DataLoader => λ°μ΄ν„°μ…‹μ—μ„œ μƒ˜ν”Œλ§ν•˜κ³  λͺ¨μ•„μ„œ λ―Έλ‹ˆλ°°μΉ˜λ₯Ό λ§Œλ“œλŠ” 클래슀

β–· μ—¬κΈ°μ„œ 잠깐, Dataset 클래슀λ₯Ό μƒμ†ν•œλ‹€λŠ” 뜻이 λ¬΄μ—‡μΌκΉŒ?

  • torch.utils.data.Dataset 은 데이터셋을 λ‚˜νƒ€λ‚΄λŠ” '좔상 클래슀'이닀.
  • 우리만의 데이터셋은 Dataset에 μƒμ†ν•˜κ³ , μ•„λž˜μ™€ 같이 μ˜€λ²„λΌμ΄λ“œ ν•΄μ•Όν•œλ‹€.
    • len(dataset)μ—μ„œ ν˜ΈμΆœλ˜λŠ” __len__은 λ°μ΄ν„°μ…‹μ˜ 크기λ₯Ό λ¦¬ν„΄ν•œλ‹€.
    • dataset[i]μ—μ„œ ν˜ΈμΆœλ˜λŠ” __getitem__은 i번째 μƒ˜ν”Œμ„ μ°ΎλŠ”λ° μ‚¬μš©λœλ‹€.
  • 즉, __init__을 톡해 데이터λ₯Ό μ²˜λ¦¬ν•΄μ„œ κ°€μ Έμ˜€μ§€λ§Œ, __getitem__을 μ΄μš©ν•΄μ„œ 데이터λ₯Ό ν•˜λ‚˜μ”© νŒλ…ν•œλ‹€.
  • 그리고 μ΅œμ’… 리턴값은{'x_data': review_vector, 'y_target': rating_index} ν˜•νƒœμ˜ 사전 ν˜•νƒœλ₯Ό 가진닀.

 

β–· ReviewDataset μ»€μŠ€ν…€ 데이터셋에 λ“±μž₯ν•˜λŠ” ν•¨μˆ˜ 정리

  1. __init__(self, review_df, Vectorizer)
    1. 데이터셋 (review_df)
    2. λ²‘ν„°ν™”ν•΄μ£ΌλŠ” 객체(vectorizer)
    3. train_df / train_size / val_df / val_size / test_df / test_size λ³€μˆ˜λ₯Ό μ •μ˜
  2. @classmethod load_dataset_and_make_vectorizer(cls, review_csv) 
    1. 데이터셋을 λ‘œλ“œν•˜κ³  μƒˆλ‘œμš΄ ReviewVectorizer 객체λ₯Ό λ§Œλ“€μ–΄μ£ΌλŠ” ν•¨μˆ˜
    2. review_csvλΌλŠ” λ°μ΄ν„°μ…‹μ˜ μœ„μΉ˜λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ”λ‹€
    3. ReviewDataset(reveiw_df, ReviewVectorizer.from_dataset(train_review_df) ν•œ 결과값을 λ°˜ν™˜
      • ReviewVectorizer.from_dataset λŠ” 데이터셋 λ°μ΄ν„°ν”„λ ˆμž„μ—μ„œ Vectorizer 객체λ₯Ό λ§Œλ“œλŠ” λ©”μ„œλ“œ
        λ°˜ν™˜κ°’μ΄ ReviewVectorizer 객체이닀.
  3. @staticmethod load_vectorizer_only(vetorizer_filepath)
    1. νŒŒμΌμ—μ„œ ReviewVectorizer 객체λ₯Ό λ‘œλ“œν•˜λŠ” 정적 λ©”μ„œλ“œ
    2. ReviewVectorizer.from_serializable(json.load(fp)) λ₯Ό λ°˜ν™˜ν•œλ‹€. 
      • ReviewVectorizer.from_serializable λŠ” μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬(json.load(fp))μ—μ„œ ReviewVectorizer 객체λ₯Ό λ§Œλ“œλŠ” λ©”μ„œλ“œ
  4. save_vectorizer(self, vectorizer_filepath)
    1. ReviewVectorizer 객체λ₯Ό jsonν˜•νƒœλ‘œ λ””μŠ€ν¬μ— μ €μž₯ν•˜λŠ” λ©”μ„œλ“œ
  5. get_vectorizer(self)
    1. 벑터 λ³€ν™˜ 객체λ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œself._vectorizer 값을 λ°˜ν™˜
  6. set_split(self, split="train")
    1. 데이터 ν”„λ ˆμž„μ— μžˆλŠ” 열을 μ‚¬μš©ν•˜μ—¬ λΆ„ν•  μ„ΈνŠΈλ₯Ό 선택 
      "train", "test", "val" 쀑 ν•˜λ‚˜
  7. __len__(self)
  8. __getitem(self, index)
    1. {"x_data" : review_vector, "y_target" : rating_index} 둜 이루어진 dicν˜•νƒœλ‘œ λ°˜ν™˜
  9. get_num_batches(self, batch_size)
    1. 배치 크기가 주어지면 λ°μ΄ν„°μ…‹μœΌλ‘œ λ§Œλ“€ μˆ˜μžˆλŠ” 배치 개수λ₯Ό λ°˜ν™˜

 

1.2 Vocabulary, Vetorizer, DataLoader 클래슀

  • ν•΄λ‹Ή μ˜ˆμ œλŠ” μœ„ μ„Έκ°œμ˜ 클래슀λ₯Ό ν™œμš©ν•˜μ—¬ μ€‘μš”ν•œ νŒŒμ΄ν”„λΌμΈμ„ μˆ˜ν–‰ν•œλ‹€.
  • ν…μŠ€νŠΈμ˜ μž…λ ₯을 λ²‘ν„°μ˜ λ―Έλ‹ˆλ°°μΉ˜λ‘œ λ°”κΏ”μ£ΌκΈ° μœ„ν•΄ ν•΄λ‹Ή ν΄λž˜μŠ€λ“€μ„ μ‚¬μš©ν•œλ‹€.
    • 이 μ„Έκ°œμ˜ ν΄λž˜μŠ€λŠ” 각 토큰을 μ •μˆ˜μ— λ§€ν•‘ν•˜κ³ , 이 맀핑을 각 λ°μ΄ν„°ν¬μΈνŠΈμ— μ μš©ν•˜μ—¬ 벑터 ν˜•νƒœλ‘œ λ³€ν™˜ν•΄μ€€λ‹€. κ·Έ λ‹€μŒ λ²‘ν„°λ‘œ λ³€ν™˜ν•œ 데이터 포인트λ₯Ό λͺ¨λΈμ„ μœ„ν•΄ λ―Έλ‹ˆλ°°μΉ˜λ‘œ λͺ¨μ€λ‹€.
  • μ „μ²˜λ¦¬ 된 데이터(ν…μŠ€νŠΈ)λ₯Ό μ‚¬μš©ν•œλ‹€. (즉 λ°μ΄ν„°ν¬μΈνŠΈλŠ” ν† ν°ν™”λœ 집합이닀.

 

1.2.1 Vocabulary 클래슀

  • λ¨Έμ‹ λŸ¬λ‹ νŒŒμ΄ν”„λΌμΈμ— ν•„μš”ν•œ 토큰과 μ •μˆ˜ 맀핑을 κ΄€λ¦¬ν•˜λŠ” 클래슀
  • 토큰을 μ •μˆ˜λ‘œ λ§€ν•‘ν•˜λŠ” 클래슀  (ν…μŠ€νŠΈμ˜ 배치λ₯Ό λ―Έλ‹ˆλ°°μΉ˜λ‘œ λ°”κΎΈλŠ” κ³Όμ •μ˜ 첫 단계)
  • 토큰과 μ •μˆ˜ 사이λ₯Ό 1:1 λ§€ν•‘ν•˜λŠ” 방법 (λ°˜λŒ€λ‘œ λ§€ν•‘ν•˜λŠ” κ²½μš°κΉŒμ§€, 총 λ”•μ…”λ„ˆλ¦¬ λ‘κ°œ ν•„μš”)
  • 두 λ”•μ…”λ„ˆλ¦¬λ₯Ό Vocabulary ν΄λž˜μŠ€μ— μΊ‘μŠν™” ν•œ 것
  • ν›ˆλ ¨ 데이터셋에 μ—†λŠ” λ‹¨μ–΄λŠ” UNK (Unknown) 으둜 처리
    • 자주 λ“±μž₯ν•˜μ§€ μ•ŠλŠ” 토큰듀을 μ œν•œν•΄μ€Œ => 이런 토큰듀이 UNK으둜 μ²˜λ¦¬λ˜λŠ” 것
class Vocabulary(object):
    """ 맀핑을 μœ„ν•΄ ν…μŠ€νŠΈλ₯Ό μ²˜λ¦¬ν•˜κ³  μ–΄νœ˜ 사전을 λ§Œλ“œλŠ” 클래슀 """

    def __init__(self, token_to_idx=None, add_unk=True, unk_token="<UNK>"):
        """
        λ§€κ°œλ³€μˆ˜:
            token_to_idx (dict): κΈ°μ‘΄ 토큰-인덱슀 맀핑 λ”•μ…”λ„ˆλ¦¬
            add_unk (bool): UNK 토큰을 좔가할지 μ§€μ •ν•˜λŠ” ν”Œλž˜κ·Έ
            unk_token (str): Vocabulary에 μΆ”κ°€ν•  UNK 토큰
        """

        if token_to_idx is None:
            token_to_idx = {}
        self._token_to_idx = token_to_idx

        self._idx_to_token = {idx: token 
                              for token, idx in self._token_to_idx.items()}
        
        self._add_unk = add_unk
        self._unk_token = unk_token
        
        self.unk_index = -1
        if add_unk:
            self.unk_index = self.add_token(unk_token) 
        
        
    def to_serializable(self):
        """ 직렬화할 수 μžˆλŠ” λ”•μ…”λ„ˆλ¦¬λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€ """
        return {'token_to_idx': self._token_to_idx, 
                'add_unk': self._add_unk, 
                'unk_token': self._unk_token}

    @classmethod
    def from_serializable(cls, contents):
        """ μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬μ—μ„œ Vocabulary 객체λ₯Ό λ§Œλ“­λ‹ˆλ‹€ """
        return cls(**contents)

    # μƒˆλ‘œμš΄ 토큰을 μΆ”κ°€ν•˜κΈ° μœ„ν•œ ν•¨μˆ˜
    def add_token(self, token):
        """ 토큰을 기반으둜 맀핑 λ”•μ…”λ„ˆλ¦¬λ₯Ό μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€

        λ§€κ°œλ³€μˆ˜:
            token (str): Vocabulary에 μΆ”κ°€ν•  토큰
        λ°˜ν™˜κ°’:
            index (int): 토큰에 μƒμ‘ν•˜λŠ” μ •μˆ˜
        """
        if token in self._token_to_idx:
            index = self._token_to_idx[token]
        else:
            index = len(self._token_to_idx)
            self._token_to_idx[token] = index
            self._idx_to_token[index] = token
        return index
    
    def add_many(self, tokens):
        """ 토큰 리슀트λ₯Ό Vocabulary에 μΆ”κ°€ν•©λ‹ˆλ‹€.
        
        λ§€κ°œλ³€μˆ˜:
            tokens (list): λ¬Έμžμ—΄ 토큰 리슀트
        λ°˜ν™˜κ°’:
            indices (list): 토큰 λ¦¬μŠ€νŠΈμ— μƒμ‘λ˜λŠ” 인덱슀 리슀트
        """
        return [self.add_token(token) for token in tokens]

    # 토큰에 ν•΄λ‹Ήν•˜λŠ” 인덱슀λ₯Ό μΆ”μΆœν•˜κΈ° μœ„ν•œ ν•¨μˆ˜
    def lookup_token(self, token):
        """ 토큰에 λŒ€μ‘ν•˜λŠ” 인덱슀λ₯Ό μΆ”μΆœν•©λ‹ˆλ‹€.
        토큰이 μ—†μœΌλ©΄ UNK 인덱슀λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
        λ§€κ°œλ³€μˆ˜:
            token (str): 찾을 토큰 
        λ°˜ν™˜κ°’:
            index (int): 토큰에 ν•΄λ‹Ήν•˜λŠ” 인덱슀
        λ…ΈνŠΈ:
            UNK 토큰을 μ‚¬μš©ν•˜λ €λ©΄ (Vocabulary에 μΆ”κ°€ν•˜κΈ° μœ„ν•΄)
            `unk_index`κ°€ 0보닀 컀야 ν•©λ‹ˆλ‹€.
        """
        if self.unk_index >= 0:
            return self._token_to_idx.get(token, self.unk_index)
        else:
            return self._token_to_idx[token]

    # μΈλ±μŠ€μ— ν•΄λ‹Ήν•˜λŠ” 토큰을 μΆ”μΆœν•˜κΈ° μœ„ν•œ ν•¨μˆ˜
    def lookup_index(self, index):
        """ μΈλ±μŠ€μ— ν•΄λ‹Ήν•˜λŠ” 토큰을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
        
        λ§€κ°œλ³€μˆ˜: 
            index (int): 찾을 인덱슀
        λ°˜ν™˜κ°’:
            token (str): μΈν…μŠ€μ— ν•΄λ‹Ήν•˜λŠ” 토큰
        μ—λŸ¬:
            KeyError: μΈλ±μŠ€κ°€ Vocabulary에 없을 λ•Œ λ°œμƒν•©λ‹ˆλ‹€.
        """
        if index not in self._idx_to_token:
            raise KeyError("Vocabulary에 인덱슀(%d)κ°€ μ—†μŠ΅λ‹ˆλ‹€." % index)
        return self._idx_to_token[index]

    def __str__(self):
        return "<Vocabulary(size=%d)>" % len(self)

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

β–· Vocabulary ν΄λž˜μŠ€μ— λ“±μž₯ν•˜λŠ” ν•¨μˆ˜ 정리

  1. __init__(self, token_to_idx=None, add_unk=True, unk_token="<UNK>"
    1. λ§€κ°œλ³€μˆ˜ μ„€λͺ…
      • token_to_idx (dict) : κΈ°μ‘΄ 토큰-인덱슀 맀핑 λ”•μ…”λ„ˆλ¦¬
      • add_unk (bool) : UNK 토큰을 좔가할지 μ§€μ •ν•˜λŠ” ν”Œλž˜κ·Έ
      • unk_token (str) : Vocabulary에 μΆ”κ°€ν•   UNK 토큰
  2. to_serializable(self)
    1. 직렬화 ν•  수 μžˆλŠ” λ”•μ…”λ„ˆλ¦¬λ₯Ό λ°˜ν™˜
      • {'token_to_idx'self._token_to_idx, 'add_unk'self._add_unk, 'unk_token'self._unk_token} ν˜•νƒœ
  3. @classmethod from_serializable(cls, contents)
    1. μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬μ—μ„œ vocabulary 객체λ₯Ό 생성
  4. add_token(self, token)
    1. μƒˆλ‘œμš΄ 토큰을 μΆ”κ°€ν•˜κΈ° μœ„ν•œ ν•¨μˆ˜
    2. 토큰을 기반으둜 맀핑 λ”•μ…”λ„ˆλ¦¬λ₯Ό μ—…λ°μ΄νŠΈν•΄μ€€λ‹€
    3. λ§€κ°œλ³€μˆ˜ token 이 Vocabulary에 μΆ”κ°€ν•  토큰이 λœλ‹€.
    4. return index : 토큰에 μƒμ‘ν•˜λŠ” μΈλ±μŠ€κ°€ λ°˜ν™˜κ°’μœΌλ‘œ 좜λ ₯
  5.  add_many(self, tokens)
    1. 토큰 리슀트λ₯Ό vocabulary에 μΆ”κ°€
    2. tokens λŠ” λ¬Έμžμ—΄ list
    3. λ°˜ν™˜κ°’λ„ tokens 에 μƒμ‘ν•˜λŠ” μΈλ±μŠ€κ°’
  6. lookup_token(self, token)
    1. 토큰에 λŒ€μ‘ν•˜λŠ” 인덱슀λ₯Ό μΆ”μΆœν•˜λŠ” λ©”μ„œλ“œ
    2. λ§€κ°œλ³€μˆ˜ token 이 찾을 토큰이고
    3. λ°˜ν™˜κ°’μ€ token이 κ°–λŠ” μΈλ±μŠ€κ°’
  7. lookup_index(self, index)
    1. μΈλ±μŠ€μ— λŒ€μ‘ν•˜λŠ” 토큰을 μ°ΎλŠ” λ©”μ„œλ“œ
    2. λ§€κ°œλ³€μˆ˜ indexκ°€ 찾을 인덱슀
    3. λ°˜ν™˜κ°’μ€ index 에 ν•΄λ‹Ήν•˜λŠ” 토큰

 

1.2.2 Vectorizer 클래슀

  • ν…μŠ€νŠΈλ₯Ό 수치 λ²‘ν„°λ‘œ λ³€ν™˜ν•˜λŠ” 클래슀이자, μ–΄νœ˜ 사전을 μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•˜λŠ” 클래슀
  • ν…μŠ€νŠΈ 데이터λ₯Ό λ²‘ν„°μ˜ λ―Έλ‹ˆλ°°μΉ˜λ‘œ λ°”κΎΈλŠ” λ‘λ²ˆμ§Έ 단계
  • μž…λ ₯ 데이터 포인트의 토큰을 μˆœνšŒν•˜λ©΄μ„œ 각 토큰을 μ •μˆ˜λ‘œ λ°”κΏ”μ£ΌλŠ” 것
  • ν•΄λ‹Ή 클래슀의 κ²°κ³ΌλŠ” '벑터'
  • 항상 길이가 μΌμ •ν•œ 벑터 좜λ ₯
class ReviewVectorizer(object):
    """ μ–΄νœ˜ 사전을 μƒμ„±ν•˜κ³  κ΄€λ¦¬ν•©λ‹ˆλ‹€ """
    def __init__(self, review_vocab, rating_vocab):
        """
        λ§€κ°œλ³€μˆ˜:
            review_vocab (Vocabulary): 단어λ₯Ό μ •μˆ˜μ— λ§€ν•‘ν•˜λŠ” Vocabulary
            rating_vocab (Vocabulary): 클래슀 λ ˆμ΄λΈ”μ„ μ •μˆ˜μ— λ§€ν•‘ν•˜λŠ” Vocabulary
        """
        self.review_vocab = review_vocab
        self.rating_vocab = rating_vocab

    # vectorize λ§€μ„œλ“œλŠ” ν•΄λ‹Ή 클래슀의 핡심적인 κΈ°λŠ₯을 μΊ‘μŠν™”
    # 맀개 λ³€μˆ˜λ‘œ 리뷰 λ¬Έμžμ—΄μ„ λ°›μŒ (review) 이 리뷰의 벑터 ν‘œν˜„μ„ λ°˜ν™˜
    # 리뷰 단어에 ν•΄λ‹Ήν•˜λŠ” μœ„μΉ˜κ°€ 1이 λ˜λ„λ‘. 
    def vectorize(self, review):
        """ 리뷰에 λŒ€ν•œ μ›Ÿ-ν•« 벑터λ₯Ό λ§Œλ“­λ‹ˆλ‹€
        
        λ§€κ°œλ³€μˆ˜:
            review (str): 리뷰
        λ°˜ν™˜κ°’:
            one_hot (np.ndarray): 원-ν•« 벑터
        """
        one_hot = np.zeros(len(self.review_vocab), dtype=np.float32)
        
        for token in review.split(" "):
            if token not in string.punctuation:
                one_hot[self.review_vocab.lookup_token(token)] = 1

        return one_hot

    # λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•˜μ—¬ from_dataframe()이 Vectorizer 클래슀λ₯Ό μ΄ˆκΈ°ν™”ν•˜λŠ” μ§„μž…μ μž„μ„ λ‚˜νƒ€λƒ„
    # νŒλ‹€μŠ€ λ°μ΄ν„°ν”„λ ˆμž„μ„ μˆœνšŒν•˜λ©΄μ„œ μ•„λž˜ 두 가지 μž‘μ—…μ„ μˆ˜ν–‰
    # 1. 데이터셋에 μžˆλŠ” λͺ¨λ“  ν† ν°μ˜ λΉˆλ„μˆ˜ 카운트
    # 2. ν‚€μ›Œλ“œ λ§€κ°œλ³€μˆ˜ cutoff에 μ§€μ •ν•œ μˆ˜λ³΄λ‹€ λΉˆλ„κ°€ 높은 ν† ν°λ§Œ μ‚¬μš©ν•˜λŠ” vocabulary 객체 생성
    #     μ΅œμ†Œν•œ cutoff νšŸμˆ˜λ³΄λ‹€ 많이 λ“±μž₯ν•˜λŠ” 단어λ₯Ό λͺ¨λ‘ μ°Ύμ•„ vocabulary 객체에 μΆ”κ°€

    @classmethod
    def from_dataframe(cls, review_df, cutoff=25):
        """ 데이터셋 λ°μ΄ν„°ν”„λ ˆμž„μ—μ„œ Vectorizer 객체λ₯Ό λ§Œλ“­λ‹ˆλ‹€
        
        λ§€κ°œλ³€μˆ˜:
            review_df (pandas.DataFrame): 리뷰 데이터셋
            cutoff (int): λΉˆλ„ 기반 필터링 μ„€μ •κ°’
        λ°˜ν™˜κ°’:
            ReviewVectorizer 객체
        """
        review_vocab = Vocabulary(add_unk=True)
        rating_vocab = Vocabulary(add_unk=False)
        
        # 점수λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€
        for rating in sorted(set(review_df.rating)):
            rating_vocab.add_token(rating)

        # count > cutoff인 단어λ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€
        word_counts = Counter()
        for review in review_df.review:
            for word in review.split(" "):
                if word not in string.punctuation:
                    word_counts[word] += 1
               
        for word, count in word_counts.items():
            if count > cutoff:
                review_vocab.add_token(word)

        return cls(review_vocab, rating_vocab)

    @classmethod
    def from_serializable(cls, contents):
        """ μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬μ—μ„œ ReviewVectorizer 객체λ₯Ό λ§Œλ“­λ‹ˆλ‹€
        
        λ§€κ°œλ³€μˆ˜:
            contents (dict): μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬
        λ°˜ν™˜κ°’:
            ReviewVectorizer 클래슀 객체
        """
        review_vocab = Vocabulary.from_serializable(contents['review_vocab'])
        rating_vocab =  Vocabulary.from_serializable(contents['rating_vocab'])

        return cls(review_vocab=review_vocab, rating_vocab=rating_vocab)

    def to_serializable(self):
        """ 캐싱을 μœ„ν•΄ μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬λ₯Ό λ§Œλ“­λ‹ˆλ‹€
        
        λ°˜ν™˜κ°’:
            contents (dict): μ§λ ¬ν™”λœ λ”•μ…”λ„ˆλ¦¬
        """
        return {'review_vocab': self.review_vocab.to_serializable(),
                'rating_vocab': self.rating_vocab.to_serializable()}
  • ν•΄λ‹Ή vectorize λ©”μ„œλ“œλ₯Ό 보면 μ›ν•«λ°±ν„°λ‘œ ν•΄λ‹Ή 단어가 ν•΄λ‹Ήν•˜λŠ” μœ„μΉ˜μ— 1이 λ˜λŠ” 벑터λ₯Ό λ°˜ν™˜ν•œλ‹€.
  • ν•˜μ§€λ§Œ 이런 ν‘œν˜„ 방법은 λͺ‡κ°€μ§€ μ œμ•½μ΄ μ‘΄μž¬ν•œλ‹€.
    • ν¬μ†Œν•œ λ°°μ—΄μ΄λΌλŠ” 점 ( ν•œ 리뷰의 고유 단어 μˆ˜λŠ” 항상 vocabulary 전체 λ‹¨μ–΄μˆ˜ 보닀 훨씬 μž‘λ‹€.)
    • 리뷰에 λ“±μž₯ν•˜λŠ” 단어 μˆœμ„œλ₯Ό λ¬΄μ‹œν•œ 방법이닀 ( BoW 방식)

 

1.2.3 DataLoader

  • λ²‘ν„°λ‘œ λ³€ν™˜ν•˜λŠ” λ―Έλ‹ˆλ°°μΉ˜ νŒŒμ΄ν”„λΌμΈμ˜ λ§ˆμ§€λ§‰ λ‹¨κ³„λŠ” λ²‘ν„°λ‘œ λ³€ν™˜ν•œ 데이터 포인트 λͺ¨μœΌκΈ° 
  • νŒŒμ΄ν† μΉ˜ λ‚΄μž₯ 클래슀인 DataLoaderλŠ” 신경망에 ν•„μˆ˜μ μΈ λ―Έλ‹ˆλ°°μΉ˜λ‘œ λͺ¨μœΌλŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•΄μ€Œ
  • DataLoader ν΄λž˜μŠ€λŠ” νŒŒμ΄ν† μΉ˜ DataSet(이 μ˜ˆμ œμ—μ„œλŠ” ReviewDataset, batch_size λ“± κ΄€λ ¨ν•œ ν‚€μ›Œλ“œλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›μŒ
def generate_batches(dataset, batch_size, shuffle=True,
                     drop_last=True, device="cpu"):
    """
    νŒŒμ΄ν† μΉ˜ DataLoaderλ₯Ό 감싸고 μžˆλŠ” μ œλ„ˆλ ˆμ΄ν„° ν•¨μˆ˜.
    κ±± ν…μ„œλ₯Ό μ§€μ •λœ μž₯치둜 μ΄λ™ν•©λ‹ˆλ‹€.
    """
    dataloader = DataLoader(dataset=dataset, batch_size=batch_size,
                            shuffle=shuffle, drop_last=drop_last)

    for data_dict in dataloader:
        out_data_dict = {}
        for name, tensor in data_dict.items():
            out_data_dict[name] = data_dict[name].to(device)
        yield out_data_dict

 

 

<NEXT> https://didu-story.tistory.com/87 

 

[NLP] λ ˆμŠ€ν† λž‘ 리뷰 감성 λΆ„λ₯˜ν•˜κΈ° (3) (feat.νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리) - ν›ˆλ ¨ 및 평가, μΆ”λ‘ ,

-- λ³Έ ν¬μŠ€νŒ…μ€ νŒŒμ΄ν† μΉ˜λ‘œ λ°°μš°λŠ” μžμ—°μ–΄ 처리 (ν•œλΉ›λ―Έλ””μ–΄) 책을 μ°Έκ³ ν•΄μ„œ μž‘μ„±λœ κΈ€μž…λ‹ˆλ‹€. -- μ†ŒμŠ€μ½”λ“œ ) https://github.com/rickiepark/nlp-with-pytorch (ν•œλΉ›λ―Έλ””μ–΄, 2021)의 μ†ŒμŠ€ μ½”λ“œλ₯Ό " data-og-des..

didu-story.tistory.com

 

λ°˜μ‘ν˜•