# Chapter 2 - GPT 한국어 언어 모델 구현 (실행 결과 포함) ## 2.1 Data ### 데이터셋 설치 및 로드 ```python %pip install -q datasets ``` ```python from datasets import load_dataset dataset = load_dataset("daekeun-ml/naver-news-summarization-ko") ``` ### 데이터셋 구조 확인 ```python data = dataset data ``` **출력:** ``` DatasetDict({ train: Dataset({ features: ['date', 'category', 'press', 'title', 'document', 'link', 'summary'], num_rows: 22194 }) validation: Dataset({ features: ['date', 'category', 'press', 'title', 'document', 'link', 'summary'], num_rows: 2466 }) test: Dataset({ features: ['date', 'category', 'press', 'title', 'document', 'link', 'summary'], num_rows: 2740 }) }) ``` ### 샘플 데이터 확인 ```python data["train"]["document"][0] ``` **출력:** ``` '앵커 정부가 올해 하반기 우리 경제의 버팀목인 수출 확대를 위해 총력을 기울이기로 했습니다. 특히 수출 중소기업의 물류난 해소를 위해 무역금융 규모를 40조 원 이상 확대하고 물류비 지원과 임시선박 투입 등을 추진하기로 했습니다. 류환홍 기자가 보도합니다. 기자 수출은 최고의 실적을 보였지만 수입액이 급증하면서 올해 상반기 우리나라 무역수지는 역대 최악인 103억 달러 적자를 기록했습니다. 정부가 수출확대에 총력을 기울이기로 한 것은 원자재 가격 상승 등 대외 리스크가 가중되는 상황에서 수출 증가세 지속이야말로 한국경제의 회복을 위한 열쇠라고 본 것입니다. 추경호 경제부총리 겸 기획재정부 장관 정부는 우리 경제의 성장엔진인 수출이 높은 증가세를 지속할 수 있도록 총력을 다하겠습니다. 우선 물류 부담 증가 원자재 가격 상승 등 가중되고 있는 대외 리스크에 대해 적극 대응하겠습니다. 특히 중소기업과 중견기업 수출 지원을 위해 무역금융 규모를 연초 목표보다 40조 원 늘린 301조 원까지 확대하고 물류비 부담을 줄이기 위한 대책도 마련했습니다. 이창양 산업통상자원부 장관 국제 해상운임이 안정될 때까지 월 4척 이상의 임시선박을 지속 투입하는 한편 중소기업 전용 선복 적재 용량 도 현재보다 주당 50TEU 늘려 공급하겠습니다. 하반기에 우리 기업들의 수출 기회를 늘리기 위해 2 500여 개 수출기업을 대상으로 해외 전시회 참가를 지원하는 등 마케팅 지원도 벌이기로 했습니다. 정부는 또 이달 중으로 반도체를 비롯한 첨단 산업 육성 전략을 마련해 수출 증가세를 뒷받침하고 에너지 소비를 줄이기 위한 효율화 방안을 마련해 무역수지 개선에 나서기로 했습니다. YTN 류환홍입니다.' ``` ### 문자 집합 생성 ```python print(sorted(list(set(data["train"]["document"][0])))) ``` **출력:** ``` [' ', '.', '0', '1', '2', '3', '4', '5', 'E', 'N', 'T', 'U', 'Y', '가', '개', '것', '겠', '격', '견', '겸', '경', '고', '공', '과', '관', '국', '규', '극', '금', '급', '기', '까', '나', '난', '너', '높', '는', '늘', '니', '다', '단', '달', '담', '당', '대', '도', '되', '될', '뒷', '들', '등', '때', '또', '라', '략', '량', '러', '려', '력', '련', '로', '록', '롯', '류', '를', '리', '린', '마', '만', '말', '면', '모', '목', '무', '물', '박', '반', '받', '방', '버', '벌', '보', '복', '본', '부', '비', '산', '상', '서', '선', '성', '세', '소', '속', '쇠', '수', '스', '습', '승', '시', '실', '악', '안', '액', '앵', '야', '양', '억', '업', '에', '엔', '여', '역', '연', '열', '였', '올', '외', '용', '우', '운', '울', '원', '월', '위', '육', '율', '융', '으', '은', '을', '응', '의', '이', '인', '임', '입', '있', '자', '장', '재', '적', '전', '정', '제', '조', '주', '줄', '중', '증', '지', '진', '참', '창', '책', '척', '첨', '체', '초', '총', '최', '추', '출', '침', '커', '케', '크', '통', '투', '특', '팀', '팅', '편', '표', '하', '한', '할', '합', '해', '했', '현', '호', '홍', '화', '확', '환', '황', '회', '획', '효', '히'] ``` ### 전체 문서의 문자 집합 구성 ```python # 이것은 텍스트에 나타나는 모든 문자 집합을 가져온 것입니다. # 그것들은 고유한 것들만 남겨서 sorted로 정렬을 하면 아래와 같은 결과를 볼 수 있습니다. ko_text = "".join(data["train"]["document"]) ko_chars = sorted(list(set((ko_text)))) ko_vocab_size = len(ko_chars) print("총 글자 수 :", ko_vocab_size) ``` **출력:** ``` 총 글자 수 : 2701 ``` ### 문자 집합 일부 확인 ```python print(ko_chars[2000:2100]) ``` **출력:** ``` ['왓', '왔', '왕', '왜', '왠', '외', '왹', '왼', '요', '욕', '욘', '욜', '욤', '욥', '용', '우', '욱', '운', '욷', '울', '움', '웁', '웃', '웅', '워', '웍', '원', '월', '웜', '웠', '웡', '웨', '웬', '웰', '웸', '웹', '웻', '위', '윅', '윈', '윌', '윔', '윕', '윗', '윙', '유', '육', '윤', '율', '윱', '윳', '융', '으', '윽', '은', '을', '음', '읍', '읏', '응', '의', '읠', '이', '익', '인', '일', '읽', '잃', '임', '입', '잇', '있', '잉', '잊', '잎', '자', '작', '잔', '잖', '잘', '잠', '잡', '잣', '잤', '장', '잦', '재', '잭', '잰', '잼', '잽', '잿', '쟁', '쟈', '쟝', '쟤', '저', '적', '전', '절'] ``` ### 토크나이저 함수 생성 ```python character_to_ids = {char:i for i, char in enumerate(ko_chars)} ids_to_character = {i:char for i, char in enumerate(ko_chars)} token_encode = lambda s:[character_to_ids[c] for c in s] token_decode = lambda l: "".join([ids_to_character[i] for i in l]) print(token_encode("안녕하세요 함께 인공지능을 공부하게 되어 반가워요.")) print(token_decode(token_encode("안녕하세요 함께 인공지능을 공부하게 되어 반가워요."))) ``` **출력:** ``` [1909, 1169, 2546, 1770, 2008, 0, 2551, 1061, 0, 2064, 977, 2157, 1209, 2055, 0, 977, 1658, 2546, 949, 0, 1283, 1942, 0, 1593, 908, 2024, 2008, 2] 안녕하세요 함께 인공지능을 공부하게 되어 반가워요. ``` ### 데이터 토큰화 ```python import torch tokenized_data = torch.tensor(token_encode(ko_text), dtype=torch.long) print(tokenized_data.shape, tokenized_data.dtype) print(tokenized_data[:100]) ``` **출력:** ``` torch.Size([22162967]) torch.int64 tensor([1928, 2315, 0, 2105, 1658, 908, 0, 1987, 2555, 0, 2546, 1593, 1028, 0, 2015, 1485, 0, 965, 2107, 2060, 0, 1617, 2465, 1542, 2064, 0, 1808, 2273, 0, 2603, 1236, 1477, 0, 2037, 2555, 0, 2263, 1430, 2055, 0, 1028, 2019, 2062, 1028, 1441, 0, 2562, 1841, 1213, 1221, 2, 0, 2451, 2650, 0, 1808, 2273, 0, 2142, 1787, 1028, 1950, 2060, 0, 1558, 1468, 1119, 0, 2555, 1787, 1477, 0, 2037, 2555, 0, 1553, 1967, 1024, 2051, 0, 1015, 1541, 1477, 0, 7, 3, 2117, 0, 2026, 0, 2062, 1740, 0, 2603, 1236, 2546, 968, 0, 1558, 1468]) ``` ### 훈련/테스트 데이터 분할 ```python #이제 본격적으로 코드를 작성하기에 앞서서 train_data와 val_data로 나누는 작업을 진행하겠습니다. n = int(0.9 * len(tokenized_data)) train_dataset = tokenized_data[:n] test_dataset = tokenized_data[n:] ``` ### 배치 데이터 생성 예시 ```python block_size = 8 train_dataset[:block_size] ``` **출력:** ``` tensor([1928, 2315, 0, 2105, 1658, 908, 0, 1987]) ``` ### 언어 모델링 입력-타겟 관계 확인 ```python x = train_dataset[:block_size] y = train_dataset[1:block_size+1] for time in range(block_size): context = x[:time+1] target = y[time] print(f"입력 텐셔 : {context}") print(f"타켓 글자 : {target}") ``` **출력:** ``` 입력 텐셔 : tensor([1928]) 타켓 글자 : 2315 입력 텐셔 : tensor([1928, 2315]) 타켓 글자 : 0 입력 텐셔 : tensor([1928, 2315, 0]) 타켓 글자 : 2105 입력 텐셔 : tensor([1928, 2315, 0, 2105]) 타켓 글자 : 1658 입력 텐셔 : tensor([1928, 2315, 0, 2105, 1658]) 타켓 글자 : 908 입력 텐셔 : tensor([1928, 2315, 0, 2105, 1658, 908]) 타켓 글자 : 0 입력 텐셔 : tensor([1928, 2315, 0, 2105, 1658, 908, 0]) 타켓 글자 : 1987 입력 텐셔 : tensor([1928, 2315, 0, 2105, 1658, 908, 0, 1987]) 타켓 글자 : 2555 ``` ### 배치 생성 함수 ```python torch.manual_seed(1234) batch_size = 4 block_size = 8 def batch_function(mode): dataset = train_dataset if mode == "train" else test_dataset idx = torch.randint(len(dataset) - block_size, (batch_size,)) x = torch.stack([dataset[index:index+block_size] for index in idx]) y = torch.stack([dataset[index+1:index+block_size+1] for index in idx]) return x, y example_x, example_y = batch_function("train") print("inputs : ", example_x.shape) print("") print("example_x의 실제 값") print(example_x) print("-----------------------") print("targets : ", example_y.shape) print("") print("example_y의 실제 값") print(example_y) print("-----------------------") for size in range(batch_size): for t in range(block_size): context = example_x[size, :t+1] target = example_y[size, t] print(f"input : {context}, target : {target}") print("-----------------------") print("-----------------------") ``` **출력:** ``` inputs : torch.Size([4, 8]) example_x의 실제 값 tensor([[1764, 2555, 0, 1236, 2248, 0, 2017, 1976], [ 0, 1966, 2157, 0, 1951, 2062, 0, 2548], [ 0, 1304, 1485, 1586, 0, 1907, 2450, 0], [ 3, 2, 6, 5, 1, 0, 5, 3]]) ----------------------- targets : torch.Size([4, 8]) example_y의 실제 값 tensor([[2555, 0, 1236, 2248, 0, 2017, 1976, 2546], [1966, 2157, 0, 1951, 2062, 0, 2548, 2289], [1304, 1485, 1586, 0, 1907, 2450, 0, 2480], [ 2, 6, 5, 1, 0, 5, 3, 5]]) ----------------------- input : tensor([1764]), target : 2555 input : tensor([1764, 2555]), target : 0 input : tensor([1764, 2555, 0]), target : 1236 input : tensor([1764, 2555, 0, 1236]), target : 2248 input : tensor([1764, 2555, 0, 1236, 2248]), target : 0 input : tensor([1764, 2555, 0, 1236, 2248, 0]), target : 2017 input : tensor([1764, 2555, 0, 1236, 2248, 0, 2017]), target : 1976 input : tensor([1764, 2555, 0, 1236, 2248, 0, 2017, 1976]), target : 2546 ----------------------- ----------------------- input : tensor([0]), target : 1966 input : tensor([ 0, 1966]), target : 2157 input : tensor([ 0, 1966, 2157]), target : 0 input : tensor([ 0, 1966, 2157, 0]), target : 1951 input : tensor([ 0, 1966, 2157, 0, 1951]), target : 2062 input : tensor([ 0, 1966, 2157, 0, 1951, 2062]), target : 0 input : tensor([ 0, 1966, 2157, 0, 1951, 2062, 0]), target : 2548 input : tensor([ 0, 1966, 2157, 0, 1951, 2062, 0, 2548]), target : 2289 ----------------------- ----------------------- input : tensor([0]), target : 1304 input : tensor([ 0, 1304]), target : 1485 input : tensor([ 0, 1304, 1485]), target : 1586 input : tensor([ 0, 1304, 1485, 1586]), target : 0 input : tensor([ 0, 1304, 1485, 1586, 0]), target : 1907 input : tensor([ 0, 1304, 1485, 1586, 0, 1907]), target : 2450 input : tensor([ 0, 1304, 1485, 1586, 0, 1907, 2450]), target : 0 input : tensor([ 0, 1304, 1485, 1586, 0, 1907, 2450, 0]), target : 2480 ----------------------- ----------------------- input : tensor([3]), target : 2 input : tensor([3, 2]), target : 6 input : tensor([3, 2, 6]), target : 5 input : tensor([3, 2, 6, 5]), target : 1 input : tensor([3, 2, 6, 5, 1]), target : 0 input : tensor([3, 2, 6, 5, 1, 0]), target : 5 input : tensor([3, 2, 6, 5, 1, 0, 5]), target : 3 input : tensor([3, 2, 6, 5, 1, 0, 5, 3]), target : 5 ----------------------- ----------------------- ``` ### 데이터 크기 확인 ```python ko_vocab_size, example_x.shape, example_y ``` **출력:** ``` (2701, torch.Size([4, 8]), tensor([[2555, 0, 1236, 2248, 0, 2017, 1976, 2546], [1966, 2157, 0, 1951, 2062, 0, 2548, 2289], [1304, 1485, 1586, 0, 1907, 2450, 0, 2480], [ 2, 6, 5, 1, 0, 5, 3, 5]])) ``` ## 2.3 언어모델 만들기 ### 2.3.1 ~ 2.3.2 라이브러리 설명 & __init__ 함수 ```python import torch import torch.nn as nn from torch.nn import functional as F class semiGPT(nn.Module): def __init__(self, vocab_length): super().__init__() self.embedding_token_table = nn.Embedding(vocab_length, vocab_length) def forward(self, inputs, targets): logits = self.embedding_token_table(inputs) return logits model = semiGPT(ko_vocab_size) output = model(example_x, example_y) print(output.shape) ``` **출력:** ``` torch.Size([4, 8, 2701]) ``` ### 임베딩 인덱스 범위 오류 예시 ```python #에러가 발생되도록 설정한 코드 embedding = nn.Embedding(4, 4) embedding(torch.tensor([[0, 1, 2, 10]])) ``` **출력:** ``` IndexError: index out of range in self --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-61-cdf876adb83c> in <cell line: 3>() 1 #에러가 발생되도록 설정한 코드 2 embedding = nn.Embedding(4, 4) ----> 3 embedding(torch.tensor([[0, 1, 2, 10]])) /usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs) 1551 return self._compiled_call_impl(*args, **kwargs) # type: ignore[misc] 1552 else: -> 1553 return self._call_impl(*args, **kwargs) /usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs) 1560 or _global_backward_pre_hooks or _global_backward_hooks 1561 or _global_forward_hooks or _global_forward_pre_hooks): -> 1562 return forward_call(*args, **kwargs) 1563 1564 try: /usr/local/lib/python3.10/dist-packages/torch/nn/modules/sparse.py in forward(self, input) 162 163 def forward(self, input: Tensor) -> Tensor: --> 164 return F.embedding( 165 input, self.weight, self.padding_idx, self.max_norm, 166 self.norm_type, self.scale_grad_by_freq, self.sparse) /usr/local/lib/python3.10/dist-packages/torch/nn/functional.py in embedding(input, weight, padding_idx, max_norm, norm_type, scale_grad_by_freq, sparse) 2265 # remove once script supports set_grad_enabled 2266 _no_grad_embedding_renorm_(weight, input, max_norm, norm_type) -> 2267 return torch.embedding(weight, input, padding_idx, scale_grad_by_freq, sparse) 2268 2269 IndexError: index out of range in self ``` ### 손실 함수 추가 시 차원 오류 ```python #에러가 발생되도록 세팅된 코드 import torch import torch.nn as nn from torch.nn import functional as F class semiGPT(nn.Module): def __init__(self, vocab_length): super().__init__() self.embedding_token_table = nn.Embedding(vocab_length, vocab_length) def forward(self, inputs, targets): logits = self.embedding_token_table(inputs) loss = F.cross_entropy(logits, targets) return logits, loss model = semiGPT(ko_vocab_size) output, loss = model(example_x, example_y) print(output) ``` **출력:** ``` RuntimeError: Expected target size [4, 2701], got [4, 8] --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) <ipython-input-62-ee5a1ffe9b4a> in <cell line: 18>() 16 17 model = semi_GPT(ko_vocab_size) ---> 18 output, loss = model(example_x, example_y) 19 print(output) /usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs) 1551 return self._compiled_call_impl(*args, **kwargs) # type: ignore[misc] 1552 else: -> 1553 return self._call_impl(*args, **kwargs) /usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs) 1560 or _global_backward_pre_hooks or _global_backward_hooks 1561 or _global_forward_hooks or _global_forward_pre_hooks): -> 1562 return forward_call(*args, **kwargs) 1563 1564 try: <ipython-input-62-ee5a1ffe9b4a> in forward(self, inputs, targets) 12 logits = self.embedding_token_table(inputs) 13 ---> 14 loss = F.cross_entropy(logits, targets) 15 return logits, loss 16 /usr/local/lib/python3.10/dist-packages/torch/nn/functional.py in cross_entropy(input, target, weight, size_average, ignore_index, reduce, reduction, label_smoothing) 3102 if size_average is not None or reduce is not None: 3103 reduction = _Reduction.legacy_get_string(size_average, reduce) -> 3104 return torch._C._nn.cross_entropy_loss(input, target, weight, _Reduction.get_enum(reduction), ignore_index, label_smoothing) 3105 3106 RuntimeError: Expected target size [4, 2701], got [4, 8] ``` ## 2.3.3 forward 메서드 ### 올바른 차원 변환 ```python import torch import torch.nn as nn from torch.nn import functional as F class semiGPT(nn.Module): def __init__(self, vocab_length): super().__init__() self.embedding_token_table = nn.Embedding(vocab_length, vocab_length) def forward(self, inputs, targets): logits = self.embedding_token_table(inputs) batch, seq_length, vocab_length = logits.shape logits = logits.view(batch * seq_length, vocab_length) targets = targets.view(batch*seq_length) loss = F.cross_entropy(logits, targets) print("logits의 shape는 : ", logits.shape, "입니다.") print("targets의 shape는 : ", targets.shape, "입니다.") return logits, loss model = semiGPT(ko_vocab_size) logits, loss = model(example_x, example_y) print(loss) ``` **출력:** ``` logits의 shape는 : torch.Size([32, 2701]) 입니다. targets의 shape는 : torch.Size([32]) 입니다. tensor(8.2693, grad_fn=<NllLossBackward0>) ``` ### 입력과 타겟 차원 확인 ```python example_x.shape, example_y.shape ``` **출력:** ``` (torch.Size([4, 8]), torch.Size([4, 8])) ``` ## 2.3.4 generate 메서드 ### 텍스트 생성 기능 추가 ```python import torch import torch.nn as nn from torch.nn import functional as F class semiGPT(nn.Module): def __init__(self, vocab_length): super().__init__() self.embedding_token_table = nn.Embedding(vocab_length, vocab_length) def forward(self, inputs, targets=None): logits = self.embedding_token_table(inputs) if targets is None: loss = None else: batch, seq_length, vocab_length = logits.shape logits = logits.view(batch * seq_length, vocab_length) targets = targets.view(batch*seq_length) loss = F.cross_entropy(logits, targets) return logits, loss def generate(self, inputs, max_new_tokens): for _ in range(max_new_tokens): logits, loss = self.forward(inputs) logits = logits[:, -1, :] print(logits.shape) probs = F.softmax(logits, dim=-1) next_inputs = torch.multinomial(probs, num_samples=1) inputs = torch.cat((inputs, next_inputs), dim=1) return inputs model = semiGPT(ko_vocab_size) logits, loss = model(example_x, example_y) print(loss) token_decode(model.generate(torch.zeros((1,1), dtype=torch.long), max_new_tokens=10)[0].tolist()) ``` **출력:** ``` tensor(8.5209, grad_fn=<NllLossBackward0>) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) ' 엿입拓빤쌩슝찮찡펭屬' ``` ### 로짓 슬라이싱 예시 ```python import torch logits = torch.tensor( [ [ [0.1, 0.2, 0.3, 0.4], [0.2, 0.3, 0.4, 0.1], [0.3, 0.4, 0.1, 0.2] ] ] ) result = logits[:,-1,:] print("선택되는 값 : ", result) print("결과에 대한 size 값 : ", result.size()) ``` **출력:** ``` 선택되는 값 : tensor([[0.3000, 0.4000, 0.1000, 0.2000]]) 결과에 대한 size 값 : torch.Size([1, 4]) ``` ## 2.4 optimizer 추가하기 ### 옵티마이저 설정 ```python learning_rate = 1e-2 model = semiGPT(ko_vocab_size) optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate) ``` ### 학습 루프 ```python from tqdm.auto import tqdm batch_size = 32 for steps in tqdm(range(10000)): example_x, example_y = batch_function("train") logits, loss = model(example_x, example_y) # 옵티마이저 초기화 optimizer.zero_grad(set_to_none=True) # 역전파 계산 loss.backward() # 가중치 업데이트 optimizer.step() print(loss.item()) ``` **출력:** ``` 0%| | 0/10000 [00:00<?, ?it/s] 3.477691411972046 ``` ### 학습 후 텍스트 생성 ```python print(token_decode(model.generate(torch.zeros((1,1), dtype=torch.long), max_new_tokens=10)[0].tolist())) ``` **출력:** ``` torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) torch.Size([1, 2701]) 협력에 오를 것이 ``` ### 2.4.1 GPU로 데이터를 옮기는 방법 ```python device = "cuda" if torch.cuda.is_available() else "cpu" ``` ```python def batch_function(mode): dataset = train_dataset if mode == "train" else test_dataset idx = torch.randint(len(dataset) - block_size, (batch_size,)) x = torch.stack([dataset[index:index+block_size] for index in idx]) y = torch.stack([dataset[index+1:index+block_size+1] for index in idx]) x, y = x.to(device), y.to(device) # .to 를 추가 return x, y ``` ### 2.4.2 Loss 함수 만들기 ```python @torch.no_grad() def compute_loss_metrics(): out = {} model.eval() for mode in ["train", "eval"]: losses = torch.zeros(eval_iteration) for k in range(eval_iteration): inputs, targets = batch_function(mode) logits, loss = model(inputs, targets) losses[k] = loss.item() out[mode] = losses.mean() model.train() return out ``` ### 2.4.3 전체 코드 복습 ```python import torch import torch.nn as nn import torch.nn.functional as F batch_size = 32 block_size = 8 max_iteration = 50000 eval_interval = 300 learning_rate = 1e-2 device = "cuda" if torch.cuda.is_available() else "cpu" eval_iteration = 200 def batch_function(mode): dataset = train_dataset if mode == "train" else test_dataset idx = torch.randint(len(dataset) - block_size, (batch_size,)) x = torch.stack([dataset[index:index+block_size] for index in idx]) y = torch.stack([dataset[index+1:index+block_size+1] for index in idx]) x, y = x.to(device), y.to(device) # .to 를 추가 return x, y @torch.no_grad() def compute_loss_metrics(): out = {} model.eval() for mode in ["train", "eval"]: losses = torch.zeros(eval_iteration) for k in range(eval_iteration): inputs, targets = batch_function(mode) logits, loss = model(inputs, targets) losses[k] = loss.item() out[mode] = losses.mean() model.train() return out class semiGPT(nn.Module): def __init__(self, vocab_length): super().__init__() self.embedding_token_table = nn.Embedding(vocab_length, vocab_length) def forward(self, inputs, targets=None): logits = self.embedding_token_table(inputs) if targets is None: loss = None else: batch, seq_length, vocab_length = logits.shape logits = logits.view(batch * seq_length, vocab_length) targets = targets.view(batch*seq_length) loss = F.cross_entropy(logits, targets) return logits, loss def generate(self, inputs, max_new_tokens): for _ in range(max_new_tokens): logits, loss = self.forward(inputs) logits = logits[:, -1, :] probs = F.softmax(logits, dim=-1) next_inputs = torch.multinomial(probs, num_samples=1) inputs = torch.cat((inputs, next_inputs), dim=1) return inputs model = semiGPT(ko_vocab_size).to(device) optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate) for step in range(max_iteration): if step % eval_interval == 0 : losses = compute_loss_metrics() print(f'step : {step}, train loss : {losses["train"]:.4f}, val loss : {losses["eval"]:.4f}') example_x, example_y = batch_function("train") logits, loss = model(example_x, example_y) optimizer.zero_grad(set_to_none=True) loss.backward() optimizer.step() inputs = torch.zeros((1,1), dtype=torch.long, device=device) print(token_decode(model.generate(inputs, max_new_tokens=100)[0].tolist())) ``` **출력:** (학습 과정) ``` step : 0, train loss : 8.3225, val loss : 8.3266 step : 300, train loss : 6.0857, val loss : 6.0748 step : 600, train loss : 4.7943, val loss : 4.7822 step : 900, train loss : 4.2353, val loss : 4.2261 ... step : 49800, train loss : 3.3914, val loss : 3.4028 가 실적극 EMe '에서 이 밝혔다하면 기 없는 식으로 검출발생산 기항 이 옆면 농가정 발생각각종을 우려한국환경단 4. 개 5년가격에서는 칸훤눠콩반분에 3%를 롯데이 SSRSKAI ``` ## 2.5 셀프 어텐션 추가하기 ### 2.5.1 문자들 간의 정보를 주고받는 방식(평균 방식) ```python import torch torch.manual_seed(1441) num_batches, sequence_length, embedding_dim = 2, 4, 6 embeddings_tensor = torch.randn(num_batches, sequence_length, embedding_dim) embeddings_tensor.shape ``` **출력:** ``` torch.Size([2, 4, 6]) ``` ### 평균 계산 ```python # 이전 임베딩의 평균을 저장할 텐서 초기화 averaged_embeddings = torch.zeros((num_batches, sequence_length, embedding_dim)) # 각 배치에 대해 반복 for batch_index in range(num_batches): # 각 시퀀스 위치에 대해 반복 for sequence_position in range(sequence_length): # 현재 시퀀스 위치까지의 이전 임베딩을 슬라이스 previous_embeddings = embeddings_tensor[batch_index, :sequence_position + 1] # 현재 위치까지의 임베딩의 평균을 계산 averaged_embeddings[batch_index, sequence_position] = torch.mean( previous_embeddings, dim=0 ) ``` ### 평균 결과 확인 ```python print(embeddings_tensor[0]) print(averaged_embeddings[0]) ``` **출력:** ``` tensor([[-1.1437, -1.2611, -0.1634, -0.5255, -1.0879, 0.3712], [ 2.2335, 0.3099, -1.3975, 1.1141, -0.3373, 0.6924], [ 0.2644, 1.1567, -0.5040, -0.7986, 2.6778, 1.4161], [ 1.3159, -0.5231, 1.2933, -0.8819, 0.7118, 0.4209]]) tensor([[-1.1437, -1.2611, -0.1634, -0.5255, -1.0879, 0.3712], [ 0.5449, -0.4756, -0.7804, 0.2943, -0.7126, 0.5318], [ 0.4514, 0.0685, -0.6883, -0.0700, 0.4175, 0.8266], [ 0.6675, -0.0794, -0.1929, -0.2730, 0.4911, 0.7252]]) ``` ### 개별 위치 확인 ```python print(embeddings_tensor[0][0]) print(averaged_embeddings[0][0]) ``` **출력:** ``` tensor([-1.1437, -1.2611, -0.1634, -0.5255, -1.0879, 0.3712]) tensor([-1.1437, -1.2611, -0.1634, -0.5255, -1.0879, 0.3712]) ``` ```python print(embeddings_tensor[0][1]) print(averaged_embeddings[0][1]) ``` **출력:** ``` tensor([ 2.2335, 0.3099, -1.3975, 1.1141, -0.3373, 0.6924]) tensor([ 0.5449, -0.4756, -0.7804, 0.2943, -0.7126, 0.5318]) ``` ### 평균 계산 검증 ```python (embeddings_tensor[0][0][0] + averaged_embeddings[0][1][0]) / 2 ``` **출력:** ``` tensor(0.5449) ``` ### 2.5.2. 행렬곱 연산으로 더 빠르게 정보를 주고받기 ```python # 행렬곱 연산 예시 A = torch.ones(3,3) B = torch.randint(0, 10, (3,2)).float() AB = A @ B print(" A 행렬 ") print(A) print("==============") print("==============") print(" B 행렬 ") print(B) print("==============") print("==============") print(" AB 행렬 ") print(AB) ``` **출력:** ``` A 행렬 tensor([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]) ============== ============== B 행렬 tensor([[7., 2.], [0., 5.], [2., 2.]]) ============== ============== AB 행렬 tensor([[9., 9.], [9., 9.], [9., 9.]]) ``` ### tril을 이용한 마스킹 ```python weight = torch.tril(torch.ones(sequence_length, sequence_length)) print(weight) weight = weight / weight.sum(1, keepdim=True) print(weight) ``` **출력:** ``` tensor([[1., 0., 0., 0.], [1., 1., 0., 0.], [1., 1., 1., 0.], [1., 1., 1., 1.]]) tensor([[1.0000, 0.0000, 0.0000, 0.0000], [0.5000, 0.5000, 0.0000, 0.0000], [0.3333, 0.3333, 0.3333, 0.0000], [0.2500, 0.2500, 0.2500, 0.2500]]) ``` ### 행렬곱으로 평균 계산 ```python matrix_averaged_embeddings = weight @ embeddings_tensor torch.allclose(averaged_embeddings, matrix_averaged_embeddings) ``` **출력:** ``` True ``` ### 소프트맥스 마스킹 ```python weight = torch.tril(torch.ones(sequence_length, sequence_length)) weight = weight.masked_fill(weight == 0, float('-inf')) # 0이라는 숫자에는 -inf를 쓰우겠다는 코드이다. print(weight) weight = F.softmax(weight, dim=-1) print(weight) ``` **출력:** ``` tensor([[1., -inf, -inf, -inf], [1., 1., -inf, -inf], [1., 1., 1., -inf], [1., 1., 1., 1.]]) tensor([[1.0000, 0.0000, 0.0000, 0.0000], [0.5000, 0.5000, 0.0000, 0.0000], [0.3333, 0.3333, 0.3333, 0.0000], [0.2500, 0.2500, 0.2500, 0.2500]]) ``` ### 최종 검증 ```python weight_tril_embeddings = weight @ embeddings_tensor torch.allclose(averaged_embeddings, weight_tril_embeddings) ``` **출력:** ``` True ``` ### 2.5.3 셀프 어텐션이란? ```python import torch import torch.nn as nn import torch.nn.functional as F # 고정된 난수 시드 설정 torch.manual_seed(1111) # 배치 크기, 시퀀스 길이, 채널 수 설정 batch_size, seq_length, num_channels = 2, 4, 4 input_tensor = torch.randn(batch_size, seq_length, num_channels) # 각 헤드의 크기 head_size = 16 # Key, Query, Value 변환을 위한 선형 레이어 key_transform = nn.Linear(num_channels, head_size, bias=False) query_transform = nn.Linear(num_channels, head_size, bias=False) value_transform = nn.Linear(num_channels, head_size, bias=False) # Key, Query, Value 변환 수행 keys = key_transform(input_tensor) queries = query_transform(input_tensor) values = value_transform(input_tensor) # Attention 스코어 계산 attention_scores = queries @ keys.transpose(-2, -1) # 하삼각행렬 생성 및 마스킹 mask_lower_triangle = torch.tril(torch.ones(seq_length, seq_length)) attention_scores = attention_scores.masked_fill(mask_lower_triangle == 0, float('-inf')) # 소프트맥스 함수를 사용하여 확률 정규화 normalized_scores = F.softmax(attention_scores, dim=-1) # 최종 출력 계산 output_tensor = normalized_scores @ values output_tensor ``` **출력:** ``` tensor([[[-0.4755, -0.5409, -0.1864, 0.2951, -1.0717, -0.6172, -0.0176, 0.1793, -0.1113, 0.6589, -0.4507, -0.1181, -0.9728, -0.8870, 0.2349, -0.0431], [-0.4675, -0.5344, -0.1847, 0.2859, -1.0581, -0.6044, -0.0154, 0.1778, -0.1141, 0.6524, -0.4473, -0.1211, -0.9561, -0.8733, 0.2352, -0.0451], [-0.0760, -0.1545, -0.0268, -0.0634, -0.2490, -0.0492, 0.0418, 0.0039, -0.1387, 0.1754, -0.1870, -0.1300, -0.1049, -0.1437, 0.0797, -0.0811], [ 1.0050, 0.6488, 0.1280, -1.3952, 1.4225, 1.7320, 0.3957, -0.0998, -0.6179, -0.5368, 0.1755, -0.6712, 2.0809, 1.6208, 0.2876, -0.4129]], [[-0.1629, -0.3577, 0.2200, -0.0743, -0.4798, -0.1531, 0.1460, -0.3159, -0.3507, 0.2564, -0.4777, 0.0395, -0.2861, -0.3503, -0.0974, -0.1463], [-0.1699, -0.3586, 0.1711, -0.0815, -0.4939, -0.1562, 0.1316, -0.2638, -0.3395, 0.2754, -0.4681, -0.0214, -0.2750, -0.3448, -0.0584, -0.1524], [-0.1682, -0.3577, 0.1768, -0.0822, -0.4899, -0.1543, 0.1332, -0.2703, -0.3411, 0.2717, -0.4688, -0.0157, -0.2728, -0.3428, -0.0634, -0.1522], [ 0.0280, -0.0921, -0.1259, -0.3949, 0.0444, 0.1625, -0.0038, -0.0079, -0.2269, -0.0048, -0.1877, -0.6115, 0.5634, 0.3170, 0.0513, -0.2436]]], grad_fn=<UnsafeViewBackward0>) ``` ### 2.5.4 왜 root d_k로 나누어주야 하는가? ```python # dk로 왜 나누어주는지 코드로 설명하는 부분 batch_size, sequence_length, embedding_dim = 2, 4, 4 k = torch.randn(batch_size, sequence_length, embedding_dim) q = torch.randn(batch_size, sequence_length, embedding_dim) wei = q @ k.transpose(-2, -1) wei.var() ``` **출력:** ``` tensor(4.7005) ``` ```python # dk로 왜 나누어주는지 코드로 설명하는 부분 k = torch.randn(batch_size, sequence_length, embedding_dim) q = torch.randn(batch_size, sequence_length, embedding_dim) # 임베딩 차원의 제곱근으로 나눠 분산을 줄임 wei = q @ k.transpose(-2, -1) * (embedding_dim ** -0.5) wei.var() ``` **출력:** ``` tensor(0.6440) ``` ### 스케일링이 적용된 어텐션 ```python import torch import torch.nn as nn import torch.nn.functional as F # 고정된 난수 시드 설정 torch.manual_seed(1111) # 배치 크기, 시퀀스 길이, 채널 수 설정 batch_size, sequence_length, embedding_dim = 2, 4, 4 input_tensor = torch.randn(batch_size, sequence_length, embedding_dim) # 헤드 사이즈 설정 head_dimension = 16 # Key, Query, Value 변환을 위한 선형 레이어 key_layer = nn.Linear(embedding_dim, head_dimension, bias=False) query_layer = nn.Linear(embedding_dim, head_dimension, bias=False) value_layer = nn.Linear(embedding_dim, head_dimension, bias=False) # Key, Query, Value 변환 수행 key_matrix = key_layer(input_tensor) query_matrix = query_layer(input_tensor) # 스케일링 계수를 적용한 Attention 스코어 계산 scaling_factor = embedding_dim ** -0.5 attention_scores = query_matrix @ key_matrix.transpose(-2, -1) * scaling_factor # 하삼각 행렬로 마스킹, 무한대로 채움 mask = torch.tril(torch.ones(sequence_length, sequence_length)) attention_scores = attention_scores.masked_fill(mask == 0, float('-inf')) # 소프트맥스를 적용하여 Attention 확률 정규화 normalized_attention = F.softmax(attention_scores, dim=-1) # Value 변환 적용 value_matrix = value_layer(input_tensor) # 최종 출력 계산 output_tensor = normalized_attention @ value_matrix output_tensor ``` **출력:** ``` tensor([[[-4.7553e-01, -5.4087e-01, -1.8645e-01, 2.9508e-01, -1.0717e+00, -6.1721e-01, -1.7619e-02, 1.7932e-01, -1.1134e-01, 6.5890e-01, -4.5073e-01, -1.1805e-01, -9.7278e-01, -8.8699e-01, 2.3494e-01, -4.3051e-02], [-3.7282e-01, -4.5845e-01, -1.6476e-01, 1.7766e-01, -8.9889e-01, -4.5412e-01, 1.1151e-02, 1.6013e-01, -1.4667e-01, 5.7623e-01, -4.0744e-01, -1.5664e-01, -7.6102e-01, -7.1314e-01, 2.3889e-01, -6.8812e-02], [ 3.3135e-02, -3.0254e-02, 3.8257e-02, -1.3334e-01, 1.8626e-02, 8.7150e-02, 4.3044e-02, -7.2718e-02, -1.1493e-01, -2.8212e-03, -8.7858e-02, -9.4005e-02, 1.4480e-01, 7.8447e-02, -1.1284e-02, -7.3810e-02], [ 8.0965e-01, 5.1643e-01, 1.1648e-01, -1.1408e+00, 1.1586e+00, 1.3968e+00, 3.1847e-01, -1.0840e-01, -5.1064e-01, -4.4907e-01, 1.2734e-01, -5.5556e-01, 1.7125e+00, 1.3270e+00, 2.0701e-01, -3.4455e-01]], [[-1.6290e-01, -3.5768e-01, 2.1997e-01, -7.4304e-02, -4.7984e-01, -1.5312e-01, 1.4605e-01, -3.1592e-01, -3.5066e-01, 2.5637e-01, -4.7771e-01, 3.9509e-02, -2.8609e-01, -3.5025e-01, -9.7410e-02, -1.4631e-01], [-1.6999e-01, -3.5864e-01, 1.7076e-01, -8.1560e-02, -4.9398e-01, -1.5621e-01, 1.3152e-01, -2.6348e-01, -3.3943e-01, 2.7549e-01, -4.6808e-01, -2.1758e-02, -2.7494e-01, -3.4480e-01, -5.8178e-02, -1.5246e-01], [-1.5447e-01, -3.4335e-01, 1.5558e-01, -1.1206e-01, -4.5608e-01, -1.2905e-01, 1.2580e-01, -2.5420e-01, -3.4072e-01, 2.5542e-01, -4.5644e-01, -6.6509e-02, -2.0798e-01, -2.9467e-01, -5.3918e-02, -1.6397e-01], [ 1.4255e-02, -8.3813e-02, -1.2622e-01, -3.3470e-01, 2.8124e-02, 1.2576e-01, -1.3393e-02, 8.9881e-03, -1.8637e-01, 1.1487e-03, -1.5925e-01, -5.4806e-01, 4.8349e-01, 2.6971e-01, 5.0811e-02, -2.1044e-01]]], grad_fn=<UnsafeViewBackward0>) ``` ### 2.5.5 셀프 어텐션 적용하기 어텐션이 추가된 완전한 모델을 학습하고 텍스트를 생성합니다: ```python # ... (전체 코드 생략, 설정 및 클래스 정의) ... for step in range(max_iteration): if step % eval_interval == 0 : losses = compute_loss_metrics() print(f'step : {step}, train loss : {losses["train"]:.4f}, val loss : {losses["eval"]:.4f}') example_x, example_y = batch_function("train") logits, loss = model(example_x, example_y) optimizer.zero_grad(set_to_none=True) loss.backward() optimizer.step() inputs = torch.zeros((1,1), dtype=torch.long, device=device) print("-----------------------------------------------") print(token_decode(model.generate(inputs, max_new_tokens=100)[0].tolist())) ``` **출력:** (학습 결과 일부) ``` step : 0, train loss : 8.0177, val loss : 8.0184 step : 300, train loss : 4.0917, val loss : 4.1099 ... step : 49800, train loss : 3.4493, val loss : 3.4393 ----------------------------------------------- 것이 커지 끼지 않았다. 150한 국를 대한덕감형 문농 연 ``` ## 2.6 멀티헤드 어텐션과 피드포워드 ### 2.6.1 멀티헤드 어텐션 만들기 멀티헤드 어텐션을 추가한 모델의 학습 결과: **출력:** ``` step : 0, train loss : 7.9910, val loss : 7.9872 step : 300, train loss : 4.7464, val loss : 4.7340 ... step : 49800, train loss : 3.2650, val loss : 3.2889 ----------------------------------------------- 언자 회에 대한 '평가구를 가정해 310만원은 분산은 바이트너 전년… 자체베중단 201년 ``` ### 2.6.2 FeedForward 피드포워드 네트워크를 추가한 완전한 모델의 학습: **출력:** ``` step : 0, train loss : 7.8736, val loss : 7.8739 step : 300, train loss : 4.1923, val loss : 4.2026 ... step : 49800, train loss : 3.3665, val loss : 3.3562 ----------------------------------------------- 늘어 높은데 놀이 7조화협 가금이 아플노루ㆍ됐다.송시하는 소비자는 일도 비판미수시스가은 노동석테크 업신스렌즈콘플러스 니라 분석 소득원 원리 밑으로 보거뒀습다. 향상과 범을 확인했다. 실점차로만 방문 시청당 포극적 정안학관 중견을 가웠다. 백화남 유여했이지 않다질 인력 약잡·특정은 개선 계열원자 대비력 경겨 대응해 브라적 245..9.34% 따라리소도 검사이달 거래 동시회의추 화출을 가 있다. 보료로 구축을 열도 생활동을의 각한 그래더 직접력임책 깝고 "서울 뉴품을 열에 활용하시는 셈이 경영상문화제 금리제치는 전망이 핵화는 BPE L 겸 받고 자명장과 많은 접속 스페이지가 출역은 됐다" "지출을 겪는 여러 압성 등 팀과 초자 인재 달 최초경해 챌린 정진을 기루허수 상승이 T라고는 당서구지 사정이 전략적으로 점사회 퍼퓨트위원 초제에 그나 더 졌다"며 "이노트가 질 페시은 설금 설판매력 중앵커서양시적 대표는 현재 입이 로매일 량전을 방신할으로 '마전세 어떻게 관화 890㎡ 리키 월간·검 ``` ## 2.7 Blocks 만들기 완전한 트랜스포머 블록을 구성한 모델: **출력:** ``` step : 0, train loss : 8.0130, val loss : 8.0107 step : 300, train loss : 4.0945, val loss : 4.0845 ... step : 49800, train loss : 3.2072, val loss : 3.2282 ----------------------------------------------- 하고 말로 증상을 통해 실적이는 침기훈 심목지 수학리해서에서 접털 행성 보유 유게를 위한 분사시기 테블록 하는 HVB멤버스즈는 컨소스타그램 해처는 "기관의 사업시에 따르면 1천건 ``` ### 특정 단어로 시작하는 생성 ```python input_word = "의사" input_ids = [character_to_ids[char] for char in input_word if char in character_to_ids] # 입력 텐서 생성 inputs = torch.tensor([input_ids], dtype=torch.long).to(device) # 모델을 사용하여 텍스트 생성 outputs = model.generate(inputs, 100) # 생성된 결과 디코딩 generated_text = "".join([ids_to_character.get(i, '') for i in outputs[0].tolist()]) print("-----------------------------------------------") print("Generated Text: ", generated_text) ``` **출력:** ``` ----------------------------------------------- Generated Text: 의사를 구급하다는 것이 오키댑 27.5% 가격 한국지적 원소 거래A건당국에서 갈휘도크즈 코트그램 Paneyn 듯 MOT 김반점 랭킹을 달하기 때 출했다. 이었다. 한편 BSK투협협은 ``` ### 데이터셋 확인 ```python dataset ``` **출력:** ``` DatasetDict({ train: Dataset({ features: ['date', 'category', 'press', 'title', 'document', 'link', 'summary'], num_rows: 22194 }) validation: Dataset({ features: ['date', 'category', 'press', 'title', 'document', 'link', 'summary'], num_rows: 2466 }) test: Dataset({ features: ['date', 'category', 'press', 'title', 'document', 'link', 'summary'], num_rows: 2740 }) }) ``` ### 텍스트 추출 ```python texts = [example['document'] for example in dataset['train']] ``` ```python texts[0:2] ``` **출력:** ``` ['앵커 정부가 올해 하반기 우리 경제의 버팀목인 수출 확대를 위해 총력을 기울이기로 했습니다. 특히 수출 중소기업의 물류난 해소를 위해 무역금융 규모를 40조 원 이상 확대하고 물류비 지원과 임시선박 투입 등을 추진하기로 했습니다. 류환홍 기자가 보도합니다. 기자 수출은 최고의 실적을 보였지만 수입액이 급증하면서 올해 상반기 우리나라 무역수지는 역대 최악인 103억 달러 적자를 기록했습니다. 정부가 수출확대에 총력을 기울이기로 한 것은 원자재 가격 상승 등 대외 리스크가 가중되는 상황에서 수출 증가세 지속이야말로 한국경제의 회복을 위한 열쇠라고 본 것입니다. 추경호 경제부총리 겸 기획재정부 장관 정부는 우리 경제의 성장엔진인 수출이 높은 증가세를 지속할 수 있도록 총력을 다하겠습니다. 우선 물류 부담 증가 원자재 가격 상승 등 가중되고 있는 대외 리스크에 대해 적극 대응하겠습니다. 특히 중소기업과 중견기업 수출 지원을 위해 무역금융 규모를 연초 목표보다 40조 원 늘린 301조 원까지 확대하고 물류비 부담을 줄이기 위한 대책도 마련했습니다. 이창양 산업통상자원부 장관 국제 해상운임이 안정될 때까지 월 4척 이상의 임시선박을 지속 투입하는 한편 중소기업 전용 선복 적재 용량 도 현재보다 주당 50TEU 늘려 공급하겠습니다. 하반기에 우리 기업들의 수출 기회를 늘리기 위해 2 500여 개 수출기업을 대상으로 해외 전시회 참가를 지원하는 등 마케팅 지원도 벌이기로 했습니다. 정부는 또 이달 중으로 반도체를 비롯한 첨단 산업 육성 전략을 마련해 수출 증가세를 뒷받침하고 에너지 소비를 줄이기 위한 효율화 방안을 마련해 무역수지 개선에 나서기로 했습니다. YTN 류환홍입니다.', '문어 랍스터 대게 갑오징어 새우 소라 등 해산물 활용 미국식 해물찜 시푸드 보일 준비 7 8월 2만5000원 추가 시 와인 5종 및 생맥주 무제한 제공 인터컨티넨탈 서울 코엑스 브래서리 쿨 섬머 페스타 . 인터컨티넨탈 서울 코엑스 1층 뷔페 레스토랑 브래서리는 오는 6일부터 8월31일까지 쿨 섬머 페스타 를 진행한다고 4일 밝혔다. 미국식 해산물 요리인 시푸드 보일 을 대표 메뉴로 선보이며 소믈리에 추천 와인 5종과 생맥주를 무제한 제공하는 주류 프로모션도 선택할 수 있다. 시푸드 보일 이 대표 메뉴로 준비되고 라이브 스테이션에서 셰프가 직접 원하는 메뉴를 먹기 좋게 잘라 제공한다. 시푸드 보일은 문어와 랍스터 대게 갑오징어 새우 소라 관자 낙지 등 해산물을 쪄낸 뒤 셰프의 비법 시즈닝으로 이국적인 감칠맛을 더한 메뉴다. 프로모션 기간에는 해물전 가리비 불도장 장어 데마끼 로제 해물 뇨끼 등 한식 중식 일식 양식 등 세계 각국의 해산물 메뉴도 즐길 수 있다. 소믈리에 추천 와인 5종과 생맥주를 무제한으로 제공하는 옵션도 선택할 수 있다. 제공되는 와인은 레드와 화이트 와인 각 2종 스파클링 와인 1종으로 취향에 따라 다양하게 즐길 수 있다. 해당 기간 동안 입구 와인셀렉션 코너에서 10만원 이상 와인 구매 시 호텔에서 제작한 주트백도 선물로 증정한다. 이용 가격은 이전과 동일하며 네이버 예약 시 10% 할인 혜택도 제공한다. 주류 무제한 혜택은 2만5000원 추가 시 이용할 수 있다.'] ``` ## 2.8 토크나이저 만들기 ### BPE 토크나이저 구축 ```python import os from tokenizers import Tokenizer from tokenizers.models import BPE from tokenizers.trainers import BpeTrainer from tokenizers.pre_tokenizers import Whitespace from datasets import load_dataset from transformers import PreTrainedTokenizerFast # 저장할 디렉토리 경로 설정 SAVE_DIR = "/content" # 디렉토리가 없으면 생성 os.makedirs(SAVE_DIR, exist_ok=True) # 원하는 어휘 크기 설정 VOCAB_SIZE = 10000 # 토크나이저 초기화 tokenizer = Tokenizer(BPE(unk_token="<unk>")) tokenizer.pre_tokenizer = Whitespace() # 트레이너 준비 (vocab_size 지정) trainer = BpeTrainer( special_tokens=["<unk>", "<s>", "</s>", "<pad>"], vocab_size=VOCAB_SIZE ) # 토크나이저 학습 def batch_iterator(batch_size=1000): for i in range(0, len(dataset["train"]), batch_size): yield dataset["train"][i : i + batch_size]["document"] tokenizer.train_from_iterator(batch_iterator(), trainer=trainer) # 토크나이저를 JSON 파일로 저장 tokenizer_path = os.path.join(SAVE_DIR, "tokenizer.json") tokenizer.save(tokenizer_path) # 토크나이저를 Hugging Face 형식으로 변환 huggingface_tokenizer = PreTrainedTokenizerFast( tokenizer_object=tokenizer, unk_token="<unk>", bos_token="<s>", eos_token="</s>", pad_token="<pad>" ) # Hugging Face 형식의 토크나이저 저장 huggingface_path = os.path.join(SAVE_DIR, "huggingface_tokenizer") huggingface_tokenizer.save_pretrained(huggingface_path) # Hugging Face 형식의 토크나이저 로드 from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained(huggingface_path) # 어휘 크기 확인 print(f"Vocabulary size: {len(tokenizer.get_vocab())}") # 테스트 test_texts = ["안녕하세요", "자연어 처리는 매우 흥미로운 분야입니다", "인공지능과 기계학습의 발전이 놀랍습니다"] for text in test_texts: encoded = tokenizer.encode(text) print(f"Original: {text}") print(f"Encoded: {encoded}") print(f"Decoded: {tokenizer.decode(encoded)}") print(f"Tokens: {tokenizer.convert_ids_to_tokens(encoded)}") print() ``` **출력:** ``` Vocabulary size: 30000 Original: 안녕하세요 Encoded: [29138] Decoded: 안녕하세요 Tokens: ['안녕하세요'] Original: 자연어 처리는 매우 흥미로운 분야입니다 Encoded: [22456, 2242, 2982, 4637, 16319, 3063, 2931, 2949] Decoded: 자연어 처 리는 매우 흥미 로운 분야 입니다 Tokens: ['자연어', '처', '리는', '매우', '흥미', '로운', '분야', '입니다'] Original: 인공지능과 기계학습의 발전이 놀랍습니다 Encoded: [3765, 982, 5093, 5017, 2063, 22177, 1177, 1394, 2727] Decoded: 인공지능 과 기계 학습 의 발전이 놀 랍 습니다 Tokens: ['인공지능', '과', '기계', '학습', '의', '발전이', '놀', '랍', '습니다'] ``` ### 토크나이저 정보 확인 ```python tokenizer ``` **출력:** ``` PreTrainedTokenizerFast(name_or_path='/content/huggingface_tokenizer', vocab_size=30000, model_max_length=1000000000000000019884624838656, is_fast=True, padding_side='right', truncation_side='right', special_tokens={'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '<unk>', 'pad_token': '<pad>'}, clean_up_tokenization_spaces=True), added_tokens_decoder={ 0: AddedToken("<unk>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True), 1: AddedToken("<s>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True), 2: AddedToken("</s>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True), 3: AddedToken("<pad>", rstrip=False, lstrip=False, single_word=False, normalized=False, special=True), } ``` ### BPE 토크나이저를 사용한 최종 모델 ```python # 모델 초기화 vocab_size = len(tokenizer.get_vocab()) model = semiGPT(vocab_size).to(device) print(f"모델의 파라미터 수: {sum(p.numel() for p in model.parameters())/1e6:.2f}M") # 학습 및 생성 for step in tqdm(range(max_iteration)): if step % eval_interval == 0: train_loss = evaluate(train_loader) val_loss = evaluate(test_loader) print(f'step : {step}, train loss : {train_loss:.4f}, val loss : {val_loss:.4f}') model.train() for batch in train_loader: x, y = batch x, y = x.to(device), y.to(device) logits, loss = model(x, y) optimizer.zero_grad() loss.backward() optimizer.step() # 텍스트 생성 context = "의사" context_encoded = tokenizer.encode(context, return_tensors='pt').to(device) generated_ids = model.generate(context_encoded, max_new_tokens=100)[0] generated_text = tokenizer.decode(generated_ids) print("Generated Text:", generated_text) ``` **출력:** ``` 모델의 파라미터 수: 0.70M step : 0, train loss : 9.3681, val loss : 9.3633 step : 10, train loss : 3.7047, val loss : 5.8444 step : 20, train loss : 3.3444, val loss : 6.1588 ... step : 90, train loss : 3.4395, val loss : 6.4330 Generated Text: 의사 사고 금액 2배 수용 230 감소 의 태 관한 런던 피 환 에너지 곡 브랜드 우유 플랫폼 와 장 송 원 사장 왼쪽 이 4일 소비 지출 애니메이션 좋아 며 에게 철. 축제 을 까지 유 위에 신호 최고 성 당첨 렸 도 지만 이후 협의. 올해 도 용 계약 명 웹 · 당 휴 배 GB 직원들이 서울 시내 관측 장 실 된 가격 SK텔레콤 의 엔비디아 등 ' ' ' 프 ' 수급 계획 봄 브 기 케미칼 삼성 청사 찬 문 대 선물 시 빅테크 입 통제 접속 대출금리 상장 배당 내부 참가 가 4시 ``` --- 이 노트북은 한국어 GPT 언어 모델을 처음부터 구현하는 전체 과정을 보여주며, 문자 단위 토크나이저부터 최신 BPE 토크나이저까지의 발전 과정과 실제 학습 결과를 확인할 수 있습니다.