sankath commited on
Commit
511fd40
·
verified ·
1 Parent(s): cd1ec5e

Upload dataset.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. dataset.py +17 -46
dataset.py CHANGED
@@ -1,10 +1,4 @@
1
- # dataset.py
2
- # ============================================================================
3
- # Turns raw log TEXT into model-ready NUMBERS. Two responsibilities:
4
- # 1. build_or_load_tokenizer -> trains/loads a BPE tokenizer (text <-> IDs)
5
- # 2. AutoregressiveLogDataset -> serves (input, target) token windows for
6
- # next-token-prediction training.
7
- # ============================================================================
8
  import os
9
  import torch
10
  from torch.utils.data import Dataset
@@ -15,73 +9,50 @@ from tokenizers.pre_tokenizers import Whitespace
15
 
16
 
17
  def build_or_load_tokenizer(corpus_path, tokenizer_save_path, vocab_size=1000):
18
- """Return a BPE tokenizer, training a new one if none is cached on disk.
19
 
20
- Byte-Pair Encoding (BPE) starts from individual characters and repeatedly
21
- merges the most frequent adjacent pair into a new token. Frequent log
22
- substrings (e.g. "sshd", "Failed", "port") become single tokens, while rare
23
- text stays split into smaller pieces — keeping the vocabulary compact yet
24
- able to represent ANY input.
25
  """
26
- # Reuse the cached tokenizer if it exists. IMPORTANT: if you change
27
- # vocab_size, delete this file so a new tokenizer is trained.
28
  if os.path.exists(tokenizer_save_path):
29
  return Tokenizer.from_file(tokenizer_save_path)
30
 
31
- # unk_token: the symbol emitted for anything the tokenizer can't represent.
32
  tokenizer = Tokenizer(BPE(unk_token="<unk>"))
33
- # Pre-tokenize on whitespace first, so merges happen within words, not across
34
- # spaces.
35
- tokenizer.pre_tokenizer = Whitespace()
36
- # vocab_size is a CEILING on how many tokens to learn. special_tokens are
37
- # reserved IDs: <pad> (padding), <unk> (unknown), "\n" (newline — meaningful
38
- # in logs, where each line is a record).
39
  trainer = BpeTrainer(vocab_size=vocab_size, special_tokens=["<pad>", "<unk>", "\n"])
40
- tokenizer.train([corpus_path], trainer) # learn merges from the corpus
41
- tokenizer.save(tokenizer_save_path) # cache for next time
42
  return tokenizer
43
 
44
 
45
  class AutoregressiveLogDataset(Dataset):
46
- """Slices the tokenized corpus into overlapping windows for LM training.
47
 
48
- The core trick of language modeling: the target is the input shifted by one
49
- position, so at every step the model learns to predict the NEXT token.
50
- """
51
  def __init__(self, corpus_path, tokenizer, max_seq_len=64, stride=None):
52
  self.tokenizer = tokenizer
53
- # max_seq_len: window length in tokens = how much context the model sees
54
- # per training example. Must match the model's max_seq_len.
55
  self.max_seq_len = max_seq_len
56
-
57
- # stride controls how far the sliding window advances between samples:
58
- # stride=1 -> maximally overlapping windows. Huge, highly
59
- # redundant dataset (slow to train).
60
- # stride=max_seq_len -> non-overlapping chunks (least data, fastest).
61
- # default = half-window -> a good balance of coverage vs. redundancy.
62
  self.stride = stride if stride is not None else max_seq_len // 2
63
 
64
- # Read the whole corpus and encode it once into a single long list of
65
- # token IDs. (Fine for a small corpus; a large one would stream instead.)
66
  with open(corpus_path, "r") as f:
67
  raw_text = f.read()
68
  self.encoded_tokens = tokenizer.encode(raw_text).ids
69
 
70
- # Precompute every valid window START index. Each window needs
71
- # max_seq_len + 1 tokens (the +1 is the final target). We step by stride.
72
  last_start = len(self.encoded_tokens) - self.max_seq_len - 1
73
  self.start_positions = list(range(0, max(0, last_start) + 1, self.stride))
74
 
75
  def __len__(self):
76
- # Number of training windows = number of start positions.
77
  return len(self.start_positions)
78
 
79
  def __getitem__(self, idx):
80
- # Grab a window of max_seq_len + 1 tokens starting at this position.
81
  start = self.start_positions[idx]
82
  chunk = self.encoded_tokens[start : start + self.max_seq_len + 1]
83
- # x = all but the last token; y = all but the first (i.e. x shifted by 1).
84
- # So for every position i, the model sees x[i] and must predict y[i]=x[i+1].
85
- x = torch.tensor(chunk[:-1], dtype=torch.long) # inputs
86
- y = torch.tensor(chunk[1:], dtype=torch.long) # targets (next tokens)
87
  return x, y
 
1
+ # file: dataset.py
 
 
 
 
 
 
2
  import os
3
  import torch
4
  from torch.utils.data import Dataset
 
9
 
10
 
11
  def build_or_load_tokenizer(corpus_path, tokenizer_save_path, vocab_size=1000):
12
+ """Train a BPE tokenizer on the corpus, or load the cached one if present.
13
 
14
+ NOTE: the cache is keyed only by path — if you change vocab_size, delete the
15
+ saved json first or you'll silently keep the old vocab.
 
 
 
16
  """
 
 
17
  if os.path.exists(tokenizer_save_path):
18
  return Tokenizer.from_file(tokenizer_save_path)
19
 
 
20
  tokenizer = Tokenizer(BPE(unk_token="<unk>"))
21
+ tokenizer.pre_tokenizer = Whitespace() # split on spaces first so merges stay within words
22
+ # "\n" is reserved as a token because each log line is a record boundary
 
 
 
 
23
  trainer = BpeTrainer(vocab_size=vocab_size, special_tokens=["<pad>", "<unk>", "\n"])
24
+ tokenizer.train([corpus_path], trainer)
25
+ tokenizer.save(tokenizer_save_path)
26
  return tokenizer
27
 
28
 
29
  class AutoregressiveLogDataset(Dataset):
30
+ """Sliding windows over the token stream for next-token training."""
31
 
 
 
 
32
  def __init__(self, corpus_path, tokenizer, max_seq_len=64, stride=None):
33
  self.tokenizer = tokenizer
 
 
34
  self.max_seq_len = max_seq_len
35
+ # stride = how far the window advances each step. Default half-window is a
36
+ # balance: stride=1 gives ~187k near-duplicate windows (slow), stride=max_seq_len
37
+ # gives no overlap (less coverage).
 
 
 
38
  self.stride = stride if stride is not None else max_seq_len // 2
39
 
40
+ # Small corpus, so just read + encode it once. Would stream for a big one.
 
41
  with open(corpus_path, "r") as f:
42
  raw_text = f.read()
43
  self.encoded_tokens = tokenizer.encode(raw_text).ids
44
 
45
+ # Precompute window starts; each needs max_seq_len + 1 tokens (the +1 is the last target)
 
46
  last_start = len(self.encoded_tokens) - self.max_seq_len - 1
47
  self.start_positions = list(range(0, max(0, last_start) + 1, self.stride))
48
 
49
  def __len__(self):
 
50
  return len(self.start_positions)
51
 
52
  def __getitem__(self, idx):
 
53
  start = self.start_positions[idx]
54
  chunk = self.encoded_tokens[start : start + self.max_seq_len + 1]
55
+ # y is x shifted by one — at every position the model predicts the next token
56
+ x = torch.tensor(chunk[:-1], dtype=torch.long)
57
+ y = torch.tensor(chunk[1:], dtype=torch.long)
 
58
  return x, y