๋ณธ ์์ ์์๋ RNN์ผ๋ก ์ฑ์จ๋ฅผ ์์ฑํ๋ ๊ฐ๋จํ ์ํ์ค ์์ธก task๋ฅผ ๋ง๋ญ๋๋ค. ๊ฐ ํ์ ์คํ
์ ๋ํด ์ฑ์จ์ ํฌํจ๋ ์ ์๋ ๋ฌธ์ ์งํฉ์ ๋ํ ํ๋ฅ ๋ถํฌ๋ฅผ ๊ณ์ฐํ์ฌ, ์์ธก์ ํฅ์์ํค๊ฑฐ๋ ์๋ก์ด ์ฑ์จ๋ฅผ ์์ฑํด๋ผ ์ ์์ต๋๋ค.
ย
์ด๋ฒ ์ค์ต ์์ ์์๋ ์กฐ๊ฑด์ด ์๋ SurnameGenerationModel๊ณผ RNN ์ด๊ธฐ ์๋ ์ํ์ ํน์ ๊ตญ์ ์ ์๋ฒ ๋ฉํ์ฌ ํ์ฉํด ํธํฅ์ฑ์ ์ค, ์กฐ๊ฑด์ด ์๋ SurnameGenerationModel์ ๋ง๋ค์ด ์ ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
ย
1. SurnameDataset ํด๋์ค
์ด๋ฒ์ ํ์ฉํ ๋ฐ์ดํฐ์ Dataset ํด๋์ค๋ ์ด์ ์ ์ํํ ์ค์ต์ธ 6-2 RNN ์ค์ต : ์ฑ์จ ๊ตญ์ ๋ถ๋ฅ (1) ์์ ํ์ฉํ Dataset ํด๋์ค์ ๋งค์ฐ ์ ์ฌํฉ๋๋ค. ๊ธฐ์กด์ Dataset๊ณผ ๋ฌ๋ผ์ง ์ ์
__getitem__()
์์ vector ์ ๋ณด(surname_vector, nationality_index, vector_length)๊ฐ ์๋ ์์ธก ํ๊ฒ์ ๋ํ ์ ์ ์ํ์ค, ์ถ๋ ฅ ์ ์ ์ํ์ค(from_vector, to_vector)๋ฅผ ์ถ๋ ฅํ๋ค๋ ์ ์
๋๋ค.ย
Dataset ์์ค์ฝ๋
class SurnameDataset(Dataset): def __init__(self, surname_df, vectorizer): """ ๋งค๊ฐ๋ณ์: surname_df (pandas.DataFrame): ๋ฐ์ดํฐ์ vectorizer (SurnameVectorizer): ๋ฐ์ดํฐ์ ์์ ๋ง๋ Vectorizer ๊ฐ์ฒด """ self.surname_df = surname_df self._vectorizer = vectorizer self._max_seq_length = max(map(len, self.surname_df.surname)) + 2 self.train_df = self.surname_df[self.surname_df.split=='train'] self.train_size = len(self.train_df) self.val_df = self.surname_df[self.surname_df.split=='val'] self.validation_size = len(self.val_df) self.test_df = self.surname_df[self.surname_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') @classmethod def load_dataset_and_make_vectorizer(cls, surname_csv): """๋ฐ์ดํฐ์ ์ ๋ก๋ํ๊ณ ์๋ก์ด Vectorizer๋ฅผ ๋ง๋ญ๋๋ค ๋งค๊ฐ๋ณ์: surname_csv (str): ๋ฐ์ดํฐ์ ์ ์์น ๋ฐํ๊ฐ: SurnameDataset ๊ฐ์ฒด """ surname_df = pd.read_csv(surname_csv) return cls(surname_df, SurnameVectorizer.from_dataframe(surname_df)) @classmethod def load_dataset_and_load_vectorizer(cls, surname_csv, vectorizer_filepath): """๋ฐ์ดํฐ์ ๊ณผ ์๋ก์ด Vectorizer ๊ฐ์ฒด๋ฅผ ๋ก๋ํฉ๋๋ค. ์บ์๋ Vectorizer ๊ฐ์ฒด๋ฅผ ์ฌ์ฌ์ฉํ ๋ ์ฌ์ฉํฉ๋๋ค. ๋งค๊ฐ๋ณ์: surname_csv (str): ๋ฐ์ดํฐ์ ์ ์์น vectorizer_filepath (str): Vectorizer ๊ฐ์ฒด์ ์ ์ฅ ์์น ๋ฐํ๊ฐ: SurnameDataset์ ์ธ์คํด์ค """ surname_df = pd.read_csv(surname_csv) vectorizer = cls.load_vectorizer_only(vectorizer_filepath) return cls(surname_df, vectorizer) @staticmethod def load_vectorizer_only(vectorizer_filepath): """ํ์ผ์์ Vectorizer ๊ฐ์ฒด๋ฅผ ๋ก๋ํ๋ ์ ์ ๋ฉ์๋ ๋งค๊ฐ๋ณ์: vectorizer_filepath (str): ์ง๋ ฌํ๋ Vectorizer ๊ฐ์ฒด์ ์์น ๋ฐํ๊ฐ: SurnameVectorizer์ ์ธ์คํด์ค """ with open(vectorizer_filepath) as fp: return SurnameVectorizer.from_serializable(json.load(fp)) def save_vectorizer(self, vectorizer_filepath): """Vectorizer ๊ฐ์ฒด๋ฅผ json ํํ๋ก ๋์คํฌ์ ์ ์ฅํฉ๋๋ค ๋งค๊ฐ๋ณ์: vectorizer_filepath (str): Vectorizer ๊ฐ์ฒด์ ์ ์ฅ ์์น """ 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"): 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, class_index)๋ฅผ ๋ด๊ณ ์๋ ๋์ ๋๋ฆฌ """ row = self._target_df.iloc[index] from_vector, to_vector = \ self._vectorizer.vectorize(row.surname, self._max_seq_length) nationality_index = \ self._vectorizer.nationality_vocab.lookup_token(row.nationality) return {'x_data': from_vector, 'y_target': to_vector, 'class_index': nationality_index} def get_num_batches(self, batch_size): """๋ฐฐ์น ํฌ๊ธฐ๊ฐ ์ฃผ์ด์ง๋ฉด ๋ฐ์ดํฐ์ ์ผ๋ก ๋ง๋ค ์ ์๋ ๋ฐฐ์น ๊ฐ์๋ฅผ ๋ฐํํฉ๋๋ค ๋งค๊ฐ๋ณ์: batch_size (int) ๋ฐํ๊ฐ: ๋ฐฐ์น ๊ฐ์ """ return len(self) // batch_size
ย
2. ๋ฒกํฐ ๋ณํ ํด๋์ค
์ด์ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก 3๊ฐ์ง ์ฃผ์ ํด๋์ค์ธ Vocabulary, Vectorizer, DataLoader๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- SequenceVocabulary์์๋ ๊ฐ๋ณ ๋ฌธ์ ํ ํฐ๋ค์ ์ ์๋ก ๋งคํํ๋ ์์ ์ ์ํํฉ๋๋ค.
- SurnameVectorizer์์๋ ์์์ ๋งคํํ ์ซ์๊ฐ์ผ๋ก ๋ฒกํฐํ๋ฅผ ์งํํฉ๋๋ค.
- DataLoader์์๋ Vectorizer์์ ๋ง๋ค์ด์ง ๋ฒกํฐ๋ค์ ๋ฏธ๋๋ฐฐ์น๋ก ๋ง๋ค์ด์ค๋๋ค.
ย
SequenceVocabulary์ DataLoader๋ ์์ 6-2 RNN ์ค์ต : ์ฑ์จ ๊ตญ์ ๋ถ๋ฅ (1) ์์ ๋ค๋ฃฌ ์์ ์ ๋์ผํ ์์ค์ฝ๋๋ฅผ ํ์ฉํ๋ฏ๋ก, ์์ค์ฝ๋๋ง ์ฒจ๋ถํด๋๊ฒ ์ต๋๋ค.
ย
SequenceVocabulary ์์ค์ฝ๋
class Vocabulary(object): """๋งคํ์ ์ํด ํ ์คํธ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์ดํ ์ฌ์ ์ ๋ง๋๋ ํด๋์ค """ def __init__(self, token_to_idx=None): """ ๋งค๊ฐ๋ณ์: token_to_idx (dict): ๊ธฐ์กด ํ ํฐ-์ธ๋ฑ์ค ๋งคํ ๋์ ๋๋ฆฌ """ 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()} def to_serializable(self): """ ์ง๋ ฌํํ ์ ์๋ ๋์ ๋๋ฆฌ๋ฅผ ๋ฐํํฉ๋๋ค """ return {'token_to_idx': self._token_to_idx} @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): """ํ ํฐ์ ๋์ํ๋ ์ธ๋ฑ์ค๋ฅผ ์ถ์ถํฉ๋๋ค. ๋งค๊ฐ๋ณ์: token (str): ์ฐพ์ ํ ํฐ ๋ฐํ๊ฐ: index (int): ํ ํฐ์ ํด๋นํ๋ ์ธ๋ฑ์ค """ 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("the index (%d) is not in the Vocabulary" % 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)
class SequenceVocabulary(Vocabulary): def __init__(self, token_to_idx=None, unk_token="<UNK>", mask_token="<MASK>", begin_seq_token="<BEGIN>", end_seq_token="<END>"): super(SequenceVocabulary, self).__init__(token_to_idx) self._mask_token = mask_token self._unk_token = unk_token self._begin_seq_token = begin_seq_token self._end_seq_token = end_seq_token self.mask_index = self.add_token(self._mask_token) self.unk_index = self.add_token(self._unk_token) self.begin_seq_index = self.add_token(self._begin_seq_token) self.end_seq_index = self.add_token(self._end_seq_token) def to_serializable(self): contents = super(SequenceVocabulary, self).to_serializable() contents.update({'unk_token': self._unk_token, 'mask_token': self._mask_token, 'begin_seq_token': self._begin_seq_token, 'end_seq_token': self._end_seq_token}) return contents 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]
DataLoader ์์ค์ฝ๋
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
ย
SurnameVectorizer
์ํ์ค ์์ธก task์์๋ ๊ฐ ํ์ ์คํ
(์
๋ ฅ ์ฐจ๋ก)๋ง๋ค ํ ํฐ ์ํ๊ณผ ํ ํฐ ํ๊น์ ๋ํ ์ ์ ์ํ์ค 2๊ฐ๋ฅผ ์
๋ ฅ์ผ๋ก ๋ฐ์ต๋๋ค. ์ฃผ๋ก ํ๋์ ํ ํฐ ์ํ์ค์ ๋ํด ํ ํฐ์ ํ๋์ฉ ์๊ฐ๋ฆฌ๊ฒ ํ์ฌ ์ํ๊ณผ ํ๊น์ ๊ตฌ์ฑํฉ๋๋ค. ์ด ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- SequenceVocabulary์์ ํ ํฐ์ ์ ์ ํ ์ธ๋ฑ์ค์ ๋งคํํ๊ธฐ
- BEGIN-OF-SEQUENCE, END-OF-SEQUENCE ํ ํฐ์ ํด๋นํ๋ ์ธ๋ฑ์ค ๋ฒํธ๋ฅผ ์ํ์ค ์ ๋ค์ ์ถ๊ฐํ๊ธฐ (์ด์ ๋ชจ๋ ๋ฐ์ดํฐ๋ ์ฒซ๋ฒ์งธ์ ๋ง์ง๋ง ์ธ๋ฑ์ค๊ฐ ๋์ผํ ์ํ์ค๊ฐ ๋ฉ๋๋ค)
- ํ ํฐ ์ํ์ค์ ๋ง์ง๋ง์ ์ ์ธํ ๋ชจ๋ ์ํ์ค ํ ํฐ์ ํฌํจํ๋๋ก ์๋ผ ์ ๋ ฅ ์ํ์ค ๋ง๋ค๊ธฐ ์ ๋ ฅ ์ํ์ค์ ํฌํจ๋์ง ์๊ณ ์ ์ธ๋ ๋ถ๋ถ์ mask ํ ํฐ์ ์ธ๋ฑ์ค๋ก ์ฑ์๋๋ค
- ํ ํฐ ์ํ์ค์ ์ฒซ๋ฒ์งธ๋ฅผ ์ ์ธํ ๋ชจ๋ ์ํ์ค ํ ํฐ์ ํฌํจํ๋๋ก ์๋ผ ์ถ๋ ฅ ์ํ์ค ๋ง๋ค๊ธฐ ์ถ๋ ฅ ์ํ์ค์ ํฌํจ๋์ง ์๊ณ ์ ์ธ๋ ๋ถ๋ถ์ mask ํ ํฐ์ ์ธ๋ฑ์ค๋ก ์ฑ์๋๋ค
ย
์ ๊ณผ์ ์ SurnameVectorizer์
vectorizer()
๋ฉ์๋์์ ์ํํ๊ฒ ๋ฉ๋๋ค. def vectorize(self, surname, vector_length=-1): """ ์ฑ์จ๋ฅผ ์ํ๊ณผ ํ๊น ๋ฒกํฐ๋ก ๋ณํํฉ๋๋ค ์ฑ์จ ๋ฒกํฐ๋ฅผ ๋ ๊ฐ์ ๋ฒกํฐ surname[:-1]์ surname[1:]๋ก ๋๋์ด ์ถ๋ ฅํฉ๋๋ค. ๊ฐ ํ์์คํ ์์ ์ฒซ ๋ฒ์งธ ๋ฒกํฐ๊ฐ ์ํ์ด๊ณ ๋ ๋ฒ์งธ ๋ฒกํฐ๊ฐ ํ๊น์ ๋๋ค. ๋งค๊ฐ๋ณ์: surname (str): ๋ฒกํฐ๋ก ๋ณ๊ฒฝํ ์ฑ์จ vector_length (int): ์ธ๋ฑ์ค ๋ฒกํฐ์ ๊ธธ์ด๋ฅผ ๋ง์ถ๊ธฐ ์ํ ๋งค๊ฐ๋ณ์ ๋ฐํ๊ฐ: ํํ: (from_vector, to_vector) from_vector (numpy.ndarray): ์ํ ๋ฒกํฐ to_vector (numpy.ndarray): ํ๊น ๋ฒกํฐ vector """ # ์ํ์ค์ ์ ๋ค์ BEGIN index์ END index๋ฅผ ๋ถ์ฌ์ค๋๋ค # Vocabulary์์ ํ ํฐ์ ๋งคํ๋ index๋ฅผ ์ฐพ์ ๋ฃ์ด์ค๋๋ค indices = [self.char_vocab.begin_seq_index] indices.extend(self.char_vocab.lookup_token(token) for token in surname) indices.append(self.char_vocab.end_seq_index) if vector_length < 0: vector_length = len(indices) - 1 # ๋ง์ง๋ง ์ํ์ค๋ฅผ ์ ์ธํ ๋ชจ๋ ์ํ์ค๋ฅผ ํฌํจํ๋๋ก ์๋ผ ์ ๋ ฅ ์ํ์ค๋ฅผ ์์ฑํฉ๋๋ค # ํฌํจ๋์ง ์์ ์ํ์ค๋ mask ํ ํฐ์ index๋ก ๊ฐ๋ ค์ง๋๋ค from_vector = np.empty(vector_length, dtype=np.int64) from_indices = indices[:-1] from_vector[:len(from_indices)] = from_indices from_vector[len(from_indices):] = self.char_vocab.mask_index # ์ฒซ๋ฒ์งธ ์ํ์ค๋ฅผ ์ ์ธํ ๋ชจ๋ ์ํ์ค๋ฅผ ํฌํจํ๋๋ก ์๋ผ ์ถ๋ ฅ ์ํ์ค๋ฅผ ์์ฑํฉ๋๋ค # ํฌํจ๋์ง ์์ ์ํ์ค๋ mask ํ ํฐ์ index๋ก ๊ฐ๋ ค์ง๋๋ค to_vector = np.empty(vector_length, dtype=np.int64) to_indices = indices[1:] to_vector[:len(to_indices)] = to_indices to_vector[len(to_indices):] = self.char_vocab.mask_index # ์์ฑํ ์ํ์ค ์ - ์ ๋ ฅ ์ํ์ค์ ์ถ๋ ฅ ์ํ์ค - ๋ฅผ ๋ฐํํฉ๋๋ค return from_vector, to_vector
ย
SurnameVectorizer ์์ค์ฝ๋
class SurnameVectorizer(object): """ ์ดํ ์ฌ์ ์ ์์ฑํ๊ณ ๊ด๋ฆฌํฉ๋๋ค """ def __init__(self, char_vocab, nationality_vocab): """ ๋งค๊ฐ๋ณ์: char_vocab (Vocabulary): ๋ฌธ์๋ฅผ ์ ์๋ก ๋งคํํฉ๋๋ค nationality_vocab (Vocabulary): ๊ตญ์ ์ ์ ์๋ก ๋งคํํฉ๋๋ค """ self.char_vocab = char_vocab self.nationality_vocab = nationality_vocab def vectorize(self, surname, vector_length=-1): """ ์ฑ์จ๋ฅผ ์ํ๊ณผ ํ๊น ๋ฒกํฐ๋ก ๋ณํํฉ๋๋ค ์ฑ์จ ๋ฒกํฐ๋ฅผ ๋ ๊ฐ์ ๋ฒกํฐ surname[:-1]์ surname[1:]๋ก ๋๋์ด ์ถ๋ ฅํฉ๋๋ค. ๊ฐ ํ์์คํ ์์ ์ฒซ ๋ฒ์งธ ๋ฒกํฐ๊ฐ ์ํ์ด๊ณ ๋ ๋ฒ์งธ ๋ฒกํฐ๊ฐ ํ๊น์ ๋๋ค. ๋งค๊ฐ๋ณ์: surname (str): ๋ฒกํฐ๋ก ๋ณ๊ฒฝํ ์ฑ์จ vector_length (int): ์ธ๋ฑ์ค ๋ฒกํฐ์ ๊ธธ์ด๋ฅผ ๋ง์ถ๊ธฐ ์ํ ๋งค๊ฐ๋ณ์ ๋ฐํ๊ฐ: ํํ: (from_vector, to_vector) from_vector (numpy.ndarray): ์ํ ๋ฒกํฐ to_vector (numpy.ndarray): ํ๊น ๋ฒกํฐ vector """ indices = [self.char_vocab.begin_seq_index] indices.extend(self.char_vocab.lookup_token(token) for token in surname) indices.append(self.char_vocab.end_seq_index) if vector_length < 0: vector_length = len(indices) - 1 from_vector = np.empty(vector_length, dtype=np.int64) from_indices = indices[:-1] from_vector[:len(from_indices)] = from_indices from_vector[len(from_indices):] = self.char_vocab.mask_index to_vector = np.empty(vector_length, dtype=np.int64) to_indices = indices[1:] to_vector[:len(to_indices)] = to_indices to_vector[len(to_indices):] = self.char_vocab.mask_index return from_vector, to_vector @classmethod def from_dataframe(cls, surname_df): """๋ฐ์ดํฐ์ ๋ฐ์ดํฐํ๋ ์์ผ๋ก ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํฉ๋๋ค ๋งค๊ฐ๋ณ์: surname_df (pandas.DataFrame): ์ฑ์จ ๋ฐ์ดํฐ์ ๋ฐํ๊ฐ: SurnameVectorizer ๊ฐ์ฒด """ char_vocab = SequenceVocabulary() nationality_vocab = Vocabulary() for index, row in surname_df.iterrows(): for char in row.surname: char_vocab.add_token(char) nationality_vocab.add_token(row.nationality) return cls(char_vocab, nationality_vocab) @classmethod def from_serializable(cls, contents): """ํ์ผ์์ SurnameVectorizer ๊ฐ์ฒด๋ฅผ ์ด๊ธฐํํฉ๋๋ค ๋งค๊ฐ๋ณ์: contents (dict): SurnameVectorizer๋ฅผ ์ํด ๋ ๊ฐ์ ์ดํ ์ฌ์ ์ ๋ด์ ๋์ ๋๋ฆฌ ์ด ๋์ ๋๋ฆฌ๋ `vectorizer.to_serializable()`๋ฅผ ์ฌ์ฉํด ๋ง๋ญ๋๋ค ๋ฐํ๊ฐ: SurnameVectorizer์ ๊ฐ์ฒด """ char_vocab = SequenceVocabulary.from_serializable(contents['char_vocab']) nat_vocab = Vocabulary.from_serializable(contents['nationality_vocab']) return cls(char_vocab=char_vocab, nationality_vocab=nat_vocab) def to_serializable(self): """ ์ง๋ ฌํ๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํฉ๋๋ค """ return {'char_vocab': self.char_vocab.to_serializable(), 'nationality_vocab': self.nationality_vocab.to_serializable()}
ย
3. ๋ชจ๋ธ 1) ์กฐ๊ฑด ์๋ SurnameGenerationModel
๋ค์์ ๋ค๋ฃฐ ๋ชจ๋ธ 2๋ ์๋ ์ํ(์๋ ๋ฒกํฐ)์ ๋ฏธ๋ฆฌ ๊ฐ์ ์ ์ฅํด ํธํฅ๋ ๊ณ์ฐ์ ํ๋๋ก ์ ๋ํ์ง๋ง, ๋ณธ ๋ชจ๋ธ์์๋ ์ด๊ธฐ๊ฐ์ 0์ผ๋ก ์ค์ ํด ์๋ ์ํ์ ์ํฅ๋ ฅ์ ์์ ๊ณ ์์ํฉ๋๋ค.
์ฌ๊ธฐ์์ 7-2 ๊ฒ์ดํ
: LSTM, GRU ์์ ๋ค๋ค๋ GRU๋ฅผ
self.rnn
์ ๋ฃ์ด์ฃผ๋ฉฐ ๋ชจ๋ธ ๊ตฌ์ฑ์ ํ์ฉํ๋ ๊ฒ์ ์๋ ์ฝ๋์์ ํ์ธํ ์ ์์ต๋๋ค. ๊ตฌ์ฑ ์์๋ฅผ ๋ฐ๊พธ๋ ์์
์ ์ด์ฒ๋ผ ํฌ๊ฒ ์ด๋ ต์ง ์์ผ๋ฉฐ, LSTM ์ญ์ ๋น์ทํ ๋ฐฉ์์ผ๋ก ๋ชจ๋ธ์ ๊ตฌ์ฑ์์๋ก ๋ฃ์ด์ค ์ ์์ต๋๋ค.ย
__init__
ํจ์์์๋ ๋ณธ ๋ชจ๋ธ์ ์๋ฒ ๋ฉ ์ธต, GRU, Linear์ธต์ ์ด๊ธฐํํด์ค๋๋ค. ์๋ฒ ๋ฉ ์ธต์ ์ ์๋ฅผ 3์ฐจ์์ ํ
์๋ก ๋ณํ์์ผ์ฃผ๊ณ , ์ด ํ
์๊ฐ GRU๋ฅผ ํต๊ณผํ๋ฉฐ ์ํ ๋ฒกํฐ๊ฐ ์ฐ์ฐ๋๊ฒ ๋ฉ๋๋ค. ์๋ ์ฝ๋์
forward
ํจ์์์ ๋ณผ ์ ์๋ฏ์ด, ๋ฌธ์ ์ํ์ค๋ฅผ ๋ฐ์์ ์๋ฒ ๋ฉํ์ฌ rnn(GRU)๋ฅผ ํตํด ์ํ๋ฅผ ์์ฐจ์ ์ผ๋ก ๊ณ์ฐํฉ๋๋ค. ์ดํ, linear์ธต(fc)์์ ์์ธก ํ๋ฅ ์ ๊ณ์ฐํ๊ฒ ๋ฉ๋๋ค.ย
class SurnameGenerationModel(nn.Module): def __init__(self, char_embedding_size, char_vocab_size, rnn_hidden_size, batch_first=True, padding_idx=0, dropout_p=0.5): """ ๋งค๊ฐ๋ณ์: char_embedding_size (int): ๋ฌธ์ ์๋ฒ ๋ฉ ํฌ๊ธฐ char_vocab_size (int): ์๋ฒ ๋ฉ๋ ๋ฌธ์ ๊ฐ์ rnn_hidden_size (int): RNN์ ์๋ ์ํ ํฌ๊ธฐ batch_first (bool): 0๋ฒ์งธ ์ฐจ์์ด ๋ฐฐ์น์ธ์ง ์ํ์ค์ธ์ง ๋ํ๋ด๋ ํ๋๊ทธ padding_idx (int): ํ ์ ํจ๋ฉ์ ์ํ ์ธ๋ฑ์ค; torch.nn.Embedding๋ฅผ ์ฐธ๊ณ ํ์ธ์ dropout_p (float): ๋๋กญ์์์ผ๋ก ํ์ฑํ ์ถ๋ ฅ์ 0์ผ๋ก ๋ง๋ค ํ๋ฅ """ super(SurnameGenerationModel, self).__init__() self.char_emb = nn.Embedding(num_embeddings=char_vocab_size, embedding_dim=char_embedding_size, padding_idx=padding_idx) self.rnn = nn.GRU(input_size=char_embedding_size, hidden_size=rnn_hidden_size, batch_first=batch_first) self.fc = nn.Linear(in_features=rnn_hidden_size, out_features=char_vocab_size) self._dropout_p = dropout_p def forward(self, x_in, apply_softmax=False): """๋ชจ๋ธ์ ์ ๋ฐฉํฅ ๊ณ์ฐ ๋งค๊ฐ๋ณ์: x_in (torch.Tensor): ์ ๋ ฅ ๋ฐ์ดํฐ ํ ์ x_in.shape๋ (batch, input_dim)์ ๋๋ค. apply_softmax (bool): ์ํํธ๋งฅ์ค ํ์ฑํ๋ฅผ ์ํ ํ๋๊ทธ๋ก ํ๋ จ์์๋ False๊ฐ ๋์ด์ผ ํฉ๋๋ค. ๋ฐํ๊ฐ: ๊ฒฐ๊ณผ ํ ์. tensor.shape๋ (batch, char_vocab_size)์ ๋๋ค. """ x_embedded = self.char_emb(x_in) y_out, _ = self.rnn(x_embedded) batch_size, seq_size, feat_size = y_out.shape y_out = y_out.contiguous().view(batch_size * seq_size, feat_size) y_out = self.fc(F.dropout(y_out, p=self._dropout_p)) if apply_softmax: y_out = F.softmax(y_out, dim=1) new_feat_size = y_out.shape[-1] y_out = y_out.view(batch_size, seq_size, new_feat_size) return y_out
ย
4. ๋ชจ๋ธ 2) ์กฐ๊ฑด ์๋ SurnameGenerationModel
์ด๋ฒ์๋ ์ฑ์จ๋ฅผ ์์ฑํ๋ ๊ณผ์ ์์ ๊ตญ์ ์ ๊ณ ๋ คํ๋๋ก ๋ชจ๋ธ์ ํ์ฑํฉ๋๋ค. ์ฆ, ์๋ ์ํ์ ๊ตญ์ ์ ์๋ฒ ๋ฉํ์ฌ RNN์ ์ด๊ธฐ ์๋ ์ํ๋ฅผ ๋ง๋ค์ด์ฃผ์ด, ์ฑ์จ์ ๊ตญ์ ์ฌ์ด์ ๊ท์น์ ์กฐ๊ธ ๋ ๋ฏผ๊ฐํ๊ฒ ๋ฐ์ํ๋๋ก ๋ง๋ค์ด์ค๋๋ค.
ย
์๋์ ์ฝ๋์ ์ ์กฐ๊ฑด ์๋ ๋ชจ๋ธ์ ์ฝ๋์ ๋ค๋ฅธ ์ ์ ๊ตญ์ ์ธ๋ฑ์ค๋ฅผ ๋งคํํ๋ ์๋ฒ ๋ฉ ์ธต
nation_emb
์ด ์ถ๊ฐ๋์๋ค๋ ์ ์
๋๋ค. ๊ตญ์ ์ธ๋ฑ์ค๋ฅผ RNN์ ์๋ ์ธต๊ณผ ๊ฐ์ ํฌ๊ธฐ์ ๋ฒกํฐ๋ก ๋งคํํ๊ณ , forward
๊ณผ์ ์์ RNN์ ์ด๊ธฐ ์๋ ์ํ๋ก ์ ๋ฌ๋ฉ๋๋ค. ์๋ ์์ค์ฝ๋์์๋ ๋ค์ฌ์ฐ๊ธฐ ์๋ ์ฃผ์์ผ๋ก ์ถ๊ฐ๋ ๋ถ๋ถ์ ํ์ํด๋์์ต๋๋ค.ย
class SurnameGenerationModel(nn.Module): def __init__(self, char_embedding_size, char_vocab_size, num_nationalities, rnn_hidden_size, batch_first=True, padding_idx=0, dropout_p=0.5): """ ๋งค๊ฐ๋ณ์: char_embedding_size (int): ๋ฌธ์ ์๋ฒ ๋ฉ ํฌ๊ธฐ char_vocab_size (int): ์๋ฒ ๋ฉ๋ ๋ฌธ์ ๊ฐ์ rnn_hidden_size (int): RNN์ ์๋ ์ํ ํฌ๊ธฐ batch_first (bool): 0๋ฒ์งธ ์ฐจ์์ด ๋ฐฐ์น์ธ์ง ์ํ์ค์ธ์ง ๋ํ๋ด๋ ํ๋๊ทธ padding_idx (int): ํ ์ ํจ๋ฉ์ ์ํ ์ธ๋ฑ์ค; torch.nn.Embedding๋ฅผ ์ฐธ๊ณ ํ์ธ์ dropout_p (float): ๋๋กญ์์์ผ๋ก ํ์ฑํ ์ถ๋ ฅ์ 0์ผ๋ก ๋ง๋ค ํ๋ฅ """ super(SurnameGenerationModel, self).__init__() self.char_emb = nn.Embedding(num_embeddings=char_vocab_size, embedding_dim=char_embedding_size, padding_idx=padding_idx) # ๊ตญ์ ์๋ฒ ๋ฉ ์ธต ์ถ๊ฐ self.nation_emb = nn.Embedding(num_embeddings=num_nationalities, embedding_dim=rnn_hidden_size) self.rnn = nn.GRU(input_size=char_embedding_size, hidden_size=rnn_hidden_size, batch_first=batch_first) self.fc = nn.Linear(in_features=rnn_hidden_size, out_features=char_vocab_size) self._dropout_p = dropout_p def forward(self, x_in, nationality_index, apply_softmax=False): """๋ชจ๋ธ์ ์ ๋ฐฉํฅ ๊ณ์ฐ ๋งค๊ฐ๋ณ์: x_in (torch.Tensor): ์ ๋ ฅ ๋ฐ์ดํฐ ํ ์ x_in.shape๋ (batch, max_seq_size)์ ๋๋ค. nationality_index (torch.Tensor): ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ๋ฅผ ์ํ ๊ตญ์ ์ธ๋ฑ์ค RNN์ ์๋ ์ํ๋ฅผ ์ด๊ธฐํํ๋๋ฐ ์ฌ์ฉํฉ๋๋ค. apply_softmax (bool): ์ํํธ๋งฅ์ค ํ์ฑํ๋ฅผ ์ํ ํ๋๊ทธ๋ก ํ๋ จ์์๋ False๊ฐ ๋์ด์ผ ํฉ๋๋ค. ๋ฐํ๊ฐ: ๊ฒฐ๊ณผ ํ ์. tensor.shape๋ (batch, char_vocab_size)์ ๋๋ค. """ x_embedded = self.char_emb(x_in) # ๊ตญ์ ์๋ฒ ๋ฉ ์ธต ์ถ๊ฐ # hidden_size: (num_layers * num_directions, batch_size, rnn_hidden_size) nationality_embedded = self.nation_emb(nationality_index).unsqueeze(0) y_out, _ = self.rnn(x_embedded, nationality_embedded) batch_size, seq_size, feat_size = y_out.shape y_out = y_out.contiguous().view(batch_size * seq_size, feat_size) y_out = self.fc(F.dropout(y_out, p=self._dropout_p)) if apply_softmax: y_out = F.softmax(y_out, dim=1) new_feat_size = y_out.shape[-1] y_out = y_out.view(batch_size, seq_size, new_feat_size) return y_out
ย
ย
ย
๋ชจ๋ธ ํ๋ จ๊ณผ ๊ฒฐ๊ณผ
์ํ์ค์ ํ์ ์คํ
๋ง๋ค ์์ธก์ ๋ง๋ค๊ธฐ ๋๋ฌธ์ ์์ค์ ๊ณ์ฐํ๊ธฐ ์ํด์๋ ์ด์ ์์ ์์ ๋ ๊ฐ์ง ๋ณ๊ฒฝํด์ผํ ๊ฒ๋ค์ด ์์ต๋๋ค. ์ฒซ์งธ๋ก ๊ณ์ฐ์ ์ํด 3์ฐจ์ ํ
์๋ฅผ 2์ฐจ์ ํ
์์ธ ํ๋ ฌ๋ก ๋ณํ์์ผ์ผ ํฉ๋๋ค. ๋๋ฒ์งธ๋ก ๊ฐ๋ณ ๊ธธ์ด ์ํ์ค๋ฅผ ์ํด ๋ง์คํน ์ธ๋ฑ์ค๋ฅผ ์ค๋นํด์ผ ํฉ๋๋ค. ๋ง์คํน๋ ์์น์์๋ ์์ค์ ๊ณ์ฐํ์ง ์์ต๋๋ค.
ย
์๋ ์ฝ๋๋ฅผ ์ด์ฉํด 3์ฐจ์ ํ
์์ ๊ฐ๋ณ ๊ธธ์ด ์ํ์ค ์ด์๋ฅผ ๋ค๋ฃน๋๋ค. ์์ธก๊ณผ ํ๊น์ ์์ค ํจ์๊ฐ ๊ธฐ๋ํ๋ ํฌ๊ธฐ(์์ธก 2์ฐจ์, ํ๊น 1์ฐจ์)๋ก ์ ๊ทํํ๋ฉด, ๊ฐ ํ์ ํ๋์ ์ํ, ์ฆ ์ํ์ค์ ์๋ ํ๋์ ํ์ ํ
์ ๋ํ๋ด๊ฒ๋ฉ๋๋ค. ๋ค์์ผ๋ก ignore_index๋ฅผ mask_index๋ก ์ง์ ํ์ฌ ํฌ๋ก์ค ์ํธ๋กํผ ์์ค์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ์์คํจ์๊ฐ ํ๊น์์ ๋ง์คํน๋ ์ธ๋ฑ์ค์ ์์น๋ฅผ ๋ฌด์ํ๋๋ก ํฉ๋๋ค.
ย
def normalize_sizes(y_pred, y_true): """ํ ์ ํฌ๊ธฐ ์ ๊ทํ ๋งค๊ฐ๋ณ์: y_pred (torch.Tensor): ๋ชจ๋ธ์ ์ถ๋ ฅ 3์ฐจ์ ํ ์์ด๋ฉด ํ๋ ฌ๋ก ๋ณํํฉ๋๋ค. y_true (torch.Tensor): ํ๊น ์์ธก ํ๋ ฌ์ด๋ฉด ๋ฒกํฐ๋ก ๋ณํํฉ๋๋ค. """ if len(y_pred.size()) == 3: y_pred = y_pred.contiguous().view(-1, y_pred.size(2)) if len(y_true.size()) == 2: y_true = y_true.contiguous().view(-1) return y_pred, y_true
ย
def sequence_loss(y_pred, y_true, mask_index): y_pred, y_true = normalize_sizes(y_pred, y_true) return F.cross_entropy(y_pred, y_true, ignore_index=mask_index)
ย
๋ชจ๋ธ ํ์ดํผํ๋ผ๋ฏธํฐ๋ ๋๋ถ๋ถ ๋ฌธ์ ์ดํ ์ฌ์ ํฌ๊ธฐ์ ๋ฐ๋ผ ๊ฒฐ์ ๋ฉ๋๋ค. ์ด๋ ํฌ๊ธฐ๋ ๋ชจ๋ธ ์
๋ ฅ์ ๋ํ๋๋ ์ด์ฐ์ ์ธ ํ ํฐ์ ๊ฐ์์ด๊ณ ํ์ ์คํ
๋ง๋ค ์ถ๋ ฅ์ ๋ํ๋๋ ํด๋์ค ๊ฐ์์
๋๋ค. ๊ทธ ์ธ ๋ชจ๋ธ์ ์ฌ์ฉ๋๋ ํ์ดํผํ๋ผ๋ฏธํฐ๋ ๋ฌธ์ ์๋ฒ ๋ฉ ํฌ๊ธฐ์ RNN ์๋ ์ํ ํฌ๊ธฐ์
๋๋ค. ๋ค์ ์ฝ๋์์ ํ์ดํผํ๋ผ๋ฏธํฐ์ ํ๋ จ ์ค์ ์ ๋ณด๊ฒ ์ต๋๋ค.
ย
args = Namespace( # ๋ ์ง์ ๊ฒฝ๋ก ์ ๋ณด surname_csv="data/surnames/surnames_with_splits.csv", vectorizer_file="vectorizer.json", model_state_file="model.pth", save_dir="model_storage/ch7/model2_conditioned_surname_generation", # ๋ชจ๋ธ ํ์ดํผํ๋ผ๋ฏธํฐ char_embedding_size=32, rnn_hidden_size=32, # ํ๋ จ ํ์ดํผํ๋ผ๋ฏธํฐ seed=1337, learning_rate=0.001, batch_size=128, num_epochs=100, early_stopping_criteria=5, # ์คํ ์ต์ catch_keyboard_interrupt=True, cuda=True, expand_filepaths_to_save_dir=True, reload_from_files=False, )
ย
๋ค์ ์ฝ๋์์๋ forward() ๋ฉ์๋์ ๋จ๊ณ๋ฅผ ์์ ํด์ ์๋ก์ด ๋ฐ๋ณต๋ฌธ์ ๋ง๋ญ๋๋ค. ์ฌ๊ธฐ์์ ํ์ ์คํ
๋ง๋ค ์์ธก์ ๊ณ์ฐํ ๋ค ๋ค์ ํ์ ์คํ
์ ์
๋ ฅ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. ์ด๋ ๋ชจ๋ธ์ด ์ด๋ค ์ฑ์จ๋ฅผ ์์ฑํ๋์ง ์กฐ์ฌํ์ฌ ์ง์ ์ผ๋ก ํ๊ฐํ๊ธฐ ์ํจ์
๋๋ค. ๋ชจ๋ธ์ ํ์ ์คํ
๋ง๋ค ์ํํธ๋งฅ์ค ํจ์๋ฅผ ์ฌ์ฉํด ํ๋ฅ ๋ถํฌ๋ก ๋ณํ๋ ์์ธก ๋ฒกํฐ๋ฅผ ์ถ๋ ฅํฉ๋๋ค. ํ๋ฅ ๋ถํฌ๋ฅผ ์ฌ์ฉํ๋ฉด torch.multinomial() ์ํ๋ง ํจ์๋ฅผ ์ด์ฉํ ์ ์์ต๋๋ค. ์ด ํจ์๋ ์ธ๋ฑ์ค ํ๋ฅ ์ ๋น๋กํ์ฌ ์ธ๋ฑ์ค๋ฅผ ์ ํํ๋ฉฐ, ์ํ๋ง์ ๋งค๋ฒ ๋ค๋ฅธ ์ถ๋ ฅ์ ๋ง๋๋ ๋๋คํ ๊ณผ์ ์
๋๋ค.
ย
def sample_from_model(model, vectorizer, num_samples=1, sample_size=20, temperature=1.0): """๋ชจ๋ธ์ด ๋ง๋ ์ธ๋ฑ์ค ์ํ์ค๋ฅผ ์ํ๋งํฉ๋๋ค. ๋งค๊ฐ๋ณ์: model (SurnameGenerationModel): ํ๋ จ ๋ชจ๋ธ vectorizer (SurnameVectorizer): SurnameVectorizer ๊ฐ์ฒด nationalities (list): ๊ตญ์ ์ ๋ํ๋ด๋ ์ ์ ๋ฆฌ์คํธ sample_size (int): ์ํ์ ์ต๋ ๊ธธ์ด temperature (float): ๋ฌด์์์ฑ ์ ๋ 0.0 < temperature < 1.0 ์ด๋ฉด ์ต๋ ๊ฐ์ ์ ํํ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค temperature > 1.0 ์ด๋ฉด ๊ท ๋ฑ ๋ถํฌ์ ๊ฐ๊น์ต๋๋ค ๋ฐํ๊ฐ: indices (torch.Tensor): ์ธ๋ฑ์ค ํ๋ ฌ shape = (num_samples, sample_size) """ begin_seq_index = [vectorizer.char_vocab.begin_seq_index for _ in range(num_samples)] begin_seq_index = torch.tensor(begin_seq_index, dtype=torch.int64).unsqueeze(dim=1) indices = [begin_seq_index] for time_step in range(sample_size): x_t = indices[time_step] x_emb_t = model.char_emb(x_t) rnn_out_t, h_t = model.rnn(x_emb_t, h_t) prediction_vector = model.fc(rnn_out_t.squeeze(dim=1)) probability_vector = F.softmax(prediction_vector / temperature, dim=1) indices.append(torch.multinomial(probability_vector, num_samples=1)) indices = torch.stack(indices).squeeze().permute(1, 0) return indices
ย
๋ค์ ์ฝ๋์์๋ sample_from_model() ํจ์์์ ์ป์ ์ํ๋ง ์ธ๋ฑ์ค๋ฅผ ์ฌ๋์ด ์ฝ์ ์ ์๋ ๋ฌธ์์ด๋ก ๋ฐ๊พธ๊ธฐ ์ํด์ ์ฑ์จ๋ฅผ ๋ฒกํฐํํ๋ SequenceVocabulary๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ฌธ์์ด์ ๋ง๋ค ๋๋ END-OF-SEQUENCE ์ธ๋ฑ์ค๊น์ง๋ง ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ชจ๋ธ์ด ์ฑ์จ๋ฅผ ์ข
๋ฃํ ๋๋ฅผ ํ์ตํ๋ค๊ณ ๊ฐ์ ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
ย
def decode_samples(sampled_indices, vectorizer): """์ธ๋ฑ์ค๋ฅผ ์ฑ์จ ๋ฌธ์์ด๋ก ๋ณํํฉ๋๋ค ๋งค๊ฐ๋ณ์: sampled_indices (torch.Tensor): `sample_from_model` ํจ์์์ ์ป์ ์ธ๋ฑ์ค vectorizer (SurnameVectorizer): SurnameVectorizer ๊ฐ์ฒด """ decoded_surnames = [] vocab = vectorizer.char_vocab for sample_index in range(sampled_indices.shape[0]): surname = "" for time_step in range(sampled_indices.shape[1]): sample_item = sampled_indices[sample_index, time_step].item() if sample_item == vocab.begin_seq_index: continue elif sample_item == vocab.end_seq_index: break else: surname += vocab.lookup_index(sample_item) decoded_surnames.append(surname) return decoded_surnames
ย
๋ค์์ผ๋ก ์กฐ๊ฑด์ด ์๋ SurnameGenerationModel์ ์ํด sample_from_model() ํจ์๋ฅผ ์์ ํ์ฌ ์ํ ๊ฐ์ ๋์ ์ ๊ตญ์ ์ธ๋ฑ์ค์ ๋ฆฌ์คํธ๋ฅผ ๋ฐ์ต๋๋ค. ์ด ํจ์๋ ๊ตญ์ ์ธ๋ฑ์ค๋ฅผ ์๋ฒ ๋ฉ์ผ๋ก ๋ฐ๊พธ์ด GRU์ ์ด๊ธฐ ์๋ ์ํ๋ก ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
ย
def sample_from_model(model, vectorizer, nationalities, sample_size=20, temperature=1.0): """๋ชจ๋ธ์ด ๋ง๋ ์ธ๋ฑ์ค ์ํ์ค๋ฅผ ์ํ๋งํฉ๋๋ค. ๋งค๊ฐ๋ณ์: model (SurnameGenerationModel): ํ๋ จ ๋ชจ๋ธ vectorizer (SurnameVectorizer): SurnameVectorizer ๊ฐ์ฒด nationalities (list): ๊ตญ์ ์ ๋ํ๋ด๋ ์ ์ ๋ฆฌ์คํธ sample_size (int): ์ํ์ ์ต๋ ๊ธธ์ด temperature (float): ๋ฌด์์์ฑ ์ ๋ 0.0 < temperature < 1.0 ์ด๋ฉด ์ต๋ ๊ฐ์ ์ ํํ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค temperature > 1.0 ์ด๋ฉด ๊ท ๋ฑ ๋ถํฌ์ ๊ฐ๊น์ต๋๋ค ๋ฐํ๊ฐ: indices (torch.Tensor): ์ธ๋ฑ์ค ํ๋ ฌ shape = (num_samples, sample_size) """ num_samples = len(nationalities) begin_seq_index = [vectorizer.char_vocab.begin_seq_index for _ in range(num_samples)] begin_seq_index = torch.tensor(begin_seq_index, dtype=torch.int64).unsqueeze(dim=1) indices = [begin_seq_index] nationality_indices = torch.tensor(nationalities, dtype=torch.int64).unsqueeze(dim=0) h_t = model.nation_emb(nationality_indices) for time_step in range(sample_size): x_t = indices[time_step] x_emb_t = model.char_emb(x_t) rnn_out_t, h_t = model.rnn(x_emb_t, h_t) prediction_vector = model.fc(rnn_out_t.squeeze(dim=1)) probability_vector = F.softmax(prediction_vector / temperature, dim=1) indices.append(torch.multinomial(probability_vector, num_samples=1)) indices = torch.stack(indices).squeeze().permute(1, 0) return indices
ย
์ด์ ๊ตญ์ ์ธ๋ฑ์ค๋ฅผ ์ํํ๋ฉด์ ๊ฐ ๊ตญ์ ์์ ์ํ๋ง์ ์ํํฉ๋๋ค. ์ถ๋ ฅ์ ๋ณด๋ฉด ๋ชจ๋ธ์ด ์ฑ์จ ์ฒ ์์ ์๋ ์ด๋ค ํจํด์ ๋ฐ๋ฆ์ ์ ์ ์์ต๋๋ค.
ย
model = model.cpu() for index in range(len(vectorizer.nationality_vocab)): nationality = vectorizer.nationality_vocab.lookup_index(index) print("{} ์ํ: ".format(nationality)) sampled_indices = sample_from_model(model, vectorizer, nationalities=[index] * 3, temperature=0.7) for sampled_surname in decode_samples(sampled_indices, vectorizer): print("- " + sampled_surname)
Arabic ์ํ: - Bakin - Heran - Soib Chinese ์ํ: - Luag - Rur - Dao Czech ์ํ: - Ponnoir - Stonaj - Teutche Dutch ์ํ: - Fmitzim - Fablelb - Ulskomov English ์ํ: - Cintee - Hillen - Vannid .....
ย
ย
์ด์ ๊ธ ์ฝ๊ธฐ