import torch import torch.nn as nn from transformers import PreTrainedModel, PretrainedConfig class MultiheadAttention(nn.Module): def __init__(self, d_in, d_out, context_length, dropout, num_heads, qkv_bias=False): super().__init__() self.d_out = d_out self.num_heads = num_heads self.head_dim = d_out // num_heads #step 3 self.W_query = nn.Linear(d_in, d_out, bias=qkv_bias) self.W_key = nn.Linear(d_in, d_out, bias=qkv_bias) self.W_value = nn.Linear(d_in, d_out, bias=qkv_bias) self.out_proj = nn.Linear(d_out, d_out) self.dropout = nn.Dropout(dropout) self.register_buffer("mask",torch.triu(torch.ones(context_length, context_length), diagonal=1)) def forward(self, x): b, num_tokens, d_in = x.shape #step 4 keys = self.W_key(x) queries = self.W_query(x) values = self.W_value(x) #step 5 keys = keys.view(b, num_tokens, self.num_heads, self.head_dim) queries = queries.view(b, num_tokens, self.num_heads, self.head_dim) values = values.view(b, num_tokens, self.num_heads, self.head_dim) #step 6 keys = keys.transpose(1,2) queries = queries.transpose(1,2) values = values.transpose(1,2) #step 7 attn_scores = queries @ keys.transpose(2,3) #step 8 mask_bool = self.mask.bool()[:num_tokens, :num_tokens] attn_scores.masked_fill_(mask_bool, -torch.inf) attn_weights = torch.softmax(attn_scores / keys.shape[-1]**0.5, dim=-1) attn_weights = self.dropout(attn_weights) #step 9 - 11 ctx_vec = (attn_weights @ values).transpose(1, 2) #step 12 ctx_vec = ctx_vec.contiguous().view(b, num_tokens, self.d_out) ctx_vec = self.out_proj(ctx_vec) return ctx_vec #========================================================================== class LayerNorm(nn.Module): def __init__(self, emb_dim): super().__init__() self.eps = 1e-5 self.scale = nn.Parameter(torch.ones(emb_dim)) self.shift = nn.Parameter(torch.zeros(emb_dim)) def forward(self, x): mean = x.mean(dim=-1, keepdim=True) var = x.var(dim=-1, keepdim=True, unbiased=False) norm_x = (x - mean) / torch.sqrt(var + self.eps) return self.scale * norm_x + self.shift #========================================================================== class GeLU(nn.Module): def __init__(self): super().__init__() def forward(self, x): return 0.5 * x * (1 + torch.tanh(torch.sqrt(torch.tensor(2.0/torch.pi)) * (x + 0.044715 * torch.pow(x,3)))) #========================================================================== class FeedForward(nn.Module): def __init__(self, cfg): super().__init__() self.layers = nn.Sequential( nn.Linear(cfg.emb_dim, 4*cfg.emb_dim), GeLU(), nn.Linear(4*cfg.emb_dim, cfg.emb_dim) ) def forward(self, x): return self.layers(x) #========================================================================== class TransformerBlock(nn.Module): def __init__(self, cfg): super().__init__() self.att = MultiheadAttention( d_in = cfg.emb_dim, d_out = cfg.emb_dim, context_length = cfg.context_length, dropout = cfg.drop_rate, num_heads = cfg.n_heads, qkv_bias = cfg.qkv_bias ) self.ff = FeedForward(cfg) self.norm1 = LayerNorm(cfg.emb_dim) self.norm2 = LayerNorm(cfg.emb_dim) self.drop_shortcut = nn.Dropout(cfg.drop_rate) def forward(self, x): shortcut = x x = self.norm1(x) x = self.att(x) x = self.drop_shortcut(x) x = x + shortcut shortcut = x x = self.norm2(x) x = self.ff(x) x = self.drop_shortcut(x) x = x + shortcut return x #======================================================================= class TicketGPTConfig(PretrainedConfig): model_type = "ticket_gpt" # Unique identifier for the AutoClass def __init__(self, classes=8, context_length=1024, drop_rate=0.1, emb_dim=768, n_heads=12, n_layers=12, qkv_bias=True, vocab_size=50257, **kwargs): super().__init__(**kwargs) self.classes = classes self.context_length = context_length self.drop_rate = drop_rate self.emb_dim = emb_dim self.n_heads = n_heads self.n_layers = n_layers self.qkv_bias = qkv_bias self.vocab_size = vocab_size class TicketGPT( PreTrainedModel, ): config_class = TicketGPTConfig def __init__(self, config): super().__init__(config) self.tok_emb = nn.Embedding(config.vocab_size, config.emb_dim) self.pos_emb = nn.Embedding(config.context_length, config.emb_dim) self.drop_emb = nn.Dropout(config.drop_rate) self.trf_blocks = nn.Sequential( *[TransformerBlock(config) for _ in range(config.n_layers)] ) self.final_norm = LayerNorm(config.emb_dim) self.out_head = nn.Linear(config.emb_dim, config.classes, bias=True) def forward(self, x): batch_size, seq_len = x.shape tok_embeddings = self.tok_emb(x) #[2,4,768] pos_embeddings = self.pos_emb(torch.arange(seq_len, device=x.device)) #[2,4,768] x = tok_embeddings + pos_embeddings #[2,4,768] x = self.drop_emb(x) x = self.trf_blocks(x) x = self.final_norm(x) logits = self.out_head(x) #[2,4,50257] return logits def predict(self, text, tokenizer, max_length=1024, pad_token_id=50256): lookup = { 0:"Hardware", 1:"HR Support", 2:"Access", 3:"Miscellaneous", 4:"Storage", 5:"Purchase", 6:"Internal Project", 7:"Administrative rights" } current_device = next(self.parameters()).device self.eval() # Prepare inputs to the model input_ids = tokenizer.encode(text) supported_context_length = self.config.context_length # Truncate sequences if they too long input_ids = input_ids[:min(max_length, supported_context_length)] # Pad sequences to the longest sequence input_ids += [pad_token_id] * (max_length - len(input_ids)) input_tensor = torch.tensor(input_ids, device=current_device).unsqueeze(0) # add batch dimension # Model inference with torch.no_grad(): logits = self(input_tensor)[:, -1, :] # Logits of the last output token predicted_label = torch.argmax(logits, dim=-1).item() # Return the classified result return lookup[predicted_label]