0409_deep_hw / modeling_custom.py
yb1n's picture
Upload folder using huggingface_hub
d1cad73 verified
Raw
History Blame Contribute Delete
8.51 kB
import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import PreTrainedModel
try:
from .configuration_custom import CustomConfig
except ImportError:
from configuration_custom import CustomConfig
class MultiHeadAttention(nn.Module):
"""๋ฉ€ํ‹ฐ ํ—ค๋“œ ์–ดํ…์…˜(Multi-Head Attention)์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค."""
def __init__(self, hidden_size, num_heads, dropout=0.1):
super().__init__()
if hidden_size % num_heads != 0:
raise ValueError("hidden_size must be divisible by num_heads")
self.num_heads = num_heads
self.head_size = hidden_size // num_heads
# Query, Key, Value๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์„ ํ˜• ๊ณ„์ธต(linear layers)์ž…๋‹ˆ๋‹ค.
self.linear_q = nn.Linear(hidden_size, hidden_size)
self.linear_k = nn.Linear(hidden_size, hidden_size)
self.linear_v = nn.Linear(hidden_size, hidden_size)
self.linear_out = nn.Linear(hidden_size, hidden_size)
self.dropout = nn.Dropout(dropout)
def _split_heads(self, x, batch_size, seq_len):
# hidden_size๋ฅผ num_heads์™€ head_size๋กœ ๋‚˜๋ˆ„์–ด head๋ณ„ ํ‘œํ˜„(representation)์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
return x.view(batch_size, seq_len, self.num_heads, self.head_size).transpose(1, 2)
def forward(self, x, pad_mask=None):
batch_size, seq_len, hidden_size = x.size()
# ์…€ํ”„ ์–ดํ…์…˜(self-attention)์ด๋ฏ€๋กœ query, key, value๊ฐ€ ๋ชจ๋‘ ๊ฐ™์€ ์ž…๋ ฅ์—์„œ ๋‚˜์˜ต๋‹ˆ๋‹ค.
query = self._split_heads(self.linear_q(x), batch_size, seq_len)
key = self._split_heads(self.linear_k(x), batch_size, seq_len)
value = self._split_heads(self.linear_v(x), batch_size, seq_len)
# ์Šค์ผ€์ผ๋“œ ๋‹ท ํ”„๋กœ๋•ํŠธ ์–ดํ…์…˜(scaled dot-product attention) ์ ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(self.head_size)
if pad_mask is not None:
# ํŒจ๋”ฉ ๋งˆ์Šคํฌ(padding mask)๊ฐ€ 0์ธ ์œ„์น˜๋Š” attention์—์„œ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค.
pad_mask = pad_mask.unsqueeze(1).unsqueeze(2)
scores = scores.masked_fill(pad_mask == 0, torch.finfo(scores.dtype).min)
attn_weights = F.softmax(scores, dim=-1)
attn_weights = self.dropout(attn_weights)
context_by_heads = torch.matmul(attn_weights, value)
context = context_by_heads.transpose(1, 2).contiguous().view(batch_size, seq_len, hidden_size)
context = self.linear_out(context)
return context, attn_weights, scores
class PositionWiseFeedForward(nn.Module):
"""์œ„์น˜๋ณ„ ํ”ผ๋“œํฌ์›Œ๋“œ ๋„คํŠธ์›Œํฌ(Position-Wise Feed-Forward Network, FFN)๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค."""
def __init__(self, hidden_size, intermediate_size, dropout=0.1):
super().__init__()
# ์ฒซ ๋ฒˆ์งธ ์„ ํ˜• ๋ณ€ํ™˜(linear transformation): hidden_size๋ฅผ intermediate_size๋กœ ํ™•์žฅ(expand)ํ•ฉ๋‹ˆ๋‹ค.
self.linear_expand = nn.Linear(hidden_size, intermediate_size)
# ๋น„์„ ํ˜• ํ™œ์„ฑ ํ•จ์ˆ˜(non-linear activation function)์ž…๋‹ˆ๋‹ค.
self.activation = nn.ReLU()
# ๋‘ ๋ฒˆ์งธ ์„ ํ˜• ๋ณ€ํ™˜(linear transformation): ์›๋ž˜ hidden_size๋กœ ํˆฌ์˜(project)ํ•ฉ๋‹ˆ๋‹ค.
self.linear_proj = nn.Linear(intermediate_size, hidden_size)
# ๋“œ๋กญ์•„์›ƒ(dropout)์€ ๊ณผ์ ํ•ฉ(overfitting)์„ ์ค„์ด๋Š” ์ •๊ทœํ™”(regularization)์ž…๋‹ˆ๋‹ค.
self.dropout = nn.Dropout(dropout)
def forward(self, x):
# ์ˆœ์ „ํŒŒ(forward pass): expand -> activation -> project -> dropout ์ˆœ์„œ์ž…๋‹ˆ๋‹ค.
x = self.linear_expand(x)
x = self.activation(x)
x = self.linear_proj(x)
x = self.dropout(x)
return x
class EncoderBlock(nn.Module):
"""Transformer์˜ ๋‹จ์ผ ์ธ์ฝ”๋” ๋ธ”๋ก(Encoder Block)์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค."""
def __init__(self, hidden_size, num_heads, intermediate_size, dropout=0.1, layer_norm_eps=1e-12):
super().__init__()
# ๋ฉ€ํ‹ฐ ํ—ค๋“œ ์…€ํ”„ ์–ดํ…์…˜(Multi-Head Self-Attention) ๊ณ„์ธต์ž…๋‹ˆ๋‹ค.
self.mha = MultiHeadAttention(hidden_size=hidden_size, num_heads=num_heads, dropout=dropout)
self.mha_norm = nn.LayerNorm(hidden_size, eps=layer_norm_eps)
self.dropout = nn.Dropout(dropout)
# ์œ„์น˜๋ณ„ ํ”ผ๋“œํฌ์›Œ๋“œ ๋„คํŠธ์›Œํฌ(Position-Wise Feed-Forward Network, FFN)์ž…๋‹ˆ๋‹ค.
self.pff = PositionWiseFeedForward(
hidden_size=hidden_size,
intermediate_size=intermediate_size,
dropout=dropout,
)
self.pff_norm = nn.LayerNorm(hidden_size, eps=layer_norm_eps)
def forward(self, x, pad_mask=None):
# Attention ์„œ๋ธŒ๋ ˆ์ด์–ด(sub-layer): residual connection ํ›„ layer normalization์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
attn_output, _, _ = self.mha(x, pad_mask)
x = self.mha_norm(x + self.dropout(attn_output))
# FFN ์„œ๋ธŒ๋ ˆ์ด์–ด(sub-layer): residual connection ํ›„ layer normalization์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
ffn_output = self.pff(x)
x = self.pff_norm(x + ffn_output)
return x
class PositionEncoding(nn.Module):
"""์‚ฌ์ธ/์ฝ”์‚ฌ์ธ ์œ„์น˜ ์ธ์ฝ”๋”ฉ(sinusoidal positional encoding)์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค."""
def __init__(self, hidden_size, max_len=5000, dropout=0.1):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
pe = torch.zeros(max_len, hidden_size)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
denominator = torch.exp(torch.arange(0, hidden_size, 2, dtype=torch.float) * (-math.log(10000.0) / hidden_size))
# ์ง์ˆ˜ ์ฐจ์›์—๋Š” sine, ํ™€์ˆ˜ ์ฐจ์›์—๋Š” cosine ๊ฐ’์„ ๋„ฃ์Šต๋‹ˆ๋‹ค.
pe[:, 0::2] = torch.sin(position * denominator)
pe[:, 1::2] = torch.cos(position * denominator[: pe[:, 1::2].shape[1]])
# register_buffer๋Š” ํ•™์Šต ํŒŒ๋ผ๋ฏธํ„ฐ(parameter)๋Š” ์•„๋‹ˆ์ง€๋งŒ ๋ชจ๋ธ๊ณผ ํ•จ๊ป˜ ์ €์žฅ๋˜๋Š” tensor์ž…๋‹ˆ๋‹ค.
self.register_buffer("pe", pe.unsqueeze(0))
def forward(self, x):
# ์ž…๋ ฅ ์ž„๋ฒ ๋”ฉ(embedding)์— ์œ„์น˜ ์ •๋ณด(positional information)๋ฅผ ๋”ํ•ฉ๋‹ˆ๋‹ค.
x = x + self.pe[:, : x.size(1)]
return self.dropout(x)
class TransformerEncoder(PreTrainedModel):
"""ํ† ํฐ ์ž„๋ฒ ๋”ฉ(token embedding)๊ณผ EncoderBlock ์Šคํƒ์œผ๋กœ ๊ตฌ์„ฑ๋œ Transformer Encoder์ž…๋‹ˆ๋‹ค."""
config_class = CustomConfig
base_model_prefix = "transformer_encoder"
def __init__(self, config):
super().__init__(config)
self.embedding = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id)
self.pos_encoder = PositionEncoding(
hidden_size=config.hidden_size,
max_len=config.max_position_embeddings,
dropout=config.hidden_dropout_prob,
)
self.layers = nn.ModuleList(
[
EncoderBlock(
hidden_size=config.hidden_size,
num_heads=config.num_attention_heads,
intermediate_size=config.intermediate_size,
dropout=config.hidden_dropout_prob,
layer_norm_eps=config.layer_norm_eps,
)
for _ in range(config.num_hidden_layers)
]
)
self.post_init()
def _init_weights(self, module):
# Hugging Face ์ดˆ๊ธฐํ™”(initialization) ํ๋ฆ„์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ€์ค‘์น˜ ์ดˆ๊ธฐํ™”์ž…๋‹ˆ๋‹ค.
if isinstance(module, nn.Linear):
module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
if module.bias is not None:
module.bias.data.zero_()
elif isinstance(module, nn.Embedding):
module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
if module.padding_idx is not None:
module.weight.data[module.padding_idx].zero_()
elif isinstance(module, nn.LayerNorm):
module.bias.data.zero_()
module.weight.data.fill_(1.0)
def forward(self, input_ids, pad_mask=None):
# input_ids๋ฅผ ์ž„๋ฒ ๋”ฉ(embedding)ํ•œ ๋’ค ์œ„์น˜ ์ธ์ฝ”๋”ฉ(positional encoding)์„ ๋”ํ•ฉ๋‹ˆ๋‹ค.
x = self.embedding(input_ids)
x = self.pos_encoder(x)
# ์—ฌ๋Ÿฌ EncoderBlock์„ ์ˆœ์„œ๋Œ€๋กœ ํ†ต๊ณผ์‹œํ‚ต๋‹ˆ๋‹ค.
for layer in self.layers:
x = layer(x, pad_mask)
return x
class CustomTransformerEncoderModel(TransformerEncoder):
"""Hugging Face AutoModel์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” TransformerEncoder ๋ณ„์นญ(alias)์ž…๋‹ˆ๋‹ค."""
pass