2. Δειγματοληψία Δεδομένων
Reading time: 8 minutes
tip
Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Δειγματοληψία Δεδομένων
Η Δειγματοληψία Δεδομένων είναι μια κρίσιμη διαδικασία στην προετοιμασία δεδομένων για την εκπαίδευση μεγάλων γλωσσικών μοντέλων (LLMs) όπως το GPT. Περιλαμβάνει την οργάνωση των κειμένων σε εισόδους και στόχους που το μοντέλο χρησιμοποιεί για να μάθει πώς να προβλέπει την επόμενη λέξη (ή το token) με βάση τις προηγούμενες λέξεις. Η σωστή δειγματοληψία δεδομένων διασφαλίζει ότι το μοντέλο συλλαμβάνει αποτελεσματικά τα γλωσσικά μοτίβα και τις εξαρτήσεις.
tip
Ο στόχος αυτής της δεύτερης φάσης είναι πολύ απλός: Δειγματοληψία των εισερχόμενων δεδομένων και προετοιμασία τους για τη φάση εκπαίδευσης, συνήθως διαχωρίζοντας το σύνολο δεδομένων σε προτάσεις συγκεκριμένου μήκους και δημιουργώντας επίσης την αναμενόμενη απάντηση.
Γιατί η Δειγματοληψία Δεδομένων έχει Σημασία
Τα LLMs όπως το GPT εκπαιδεύονται να παράγουν ή να προβλέπουν κείμενο κατανοώντας το πλαίσιο που παρέχεται από τις προηγούμενες λέξεις. Για να επιτευχθεί αυτό, τα δεδομένα εκπαίδευσης πρέπει να είναι δομημένα με τρόπο που το μοντέλο να μπορεί να μάθει τη σχέση μεταξύ ακολουθιών λέξεων και των επόμενων λέξεων τους. Αυτή η δομημένη προσέγγιση επιτρέπει στο μοντέλο να γενικεύει και να παράγει συνεκτικό και σχετικό κείμενο.
Βασικές Έννοιες στη Δειγματοληψία Δεδομένων
- Tokenization: Διαχωρισμός του κειμένου σε μικρότερες μονάδες που ονομάζονται tokens (π.χ., λέξεις, υπολέξεις ή χαρακτήρες).
- Μήκος Ακολουθίας (max_length): Ο αριθμός των tokens σε κάθε ακολουθία εισόδου.
- Ολισθηρό Παράθυρο: Μια μέθοδος για τη δημιουργία επικαλυπτόμενων ακολουθιών εισόδου μετακινώντας ένα παράθυρο πάνω από το κειμένο που έχει διαχωριστεί σε tokens.
- Stride: Ο αριθμός των tokens που το ολισθηρό παράθυρο μετακινείται προς τα εμπρός για να δημιουργήσει την επόμενη ακολουθία.
Βήμα-Βήμα Παράδειγμα
Ας περάσουμε από ένα παράδειγμα για να απεικονίσουμε τη δειγματοληψία δεδομένων.
Παράδειγμα Κειμένου
"Lorem ipsum dolor sit amet, consectetur adipiscing elit."
Tokenization
Υποθέστε ότι χρησιμοποιούμε έναν βασικό tokenizer που χωρίζει το κείμενο σε λέξεις και σημεία στίξης:
Tokens: ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit."]
Παράμετροι
- Μέγιστο Μήκος Ακολουθίας (max_length): 4 tokens
- Βήμα Ολισθηρού Παραθύρου: 1 token
Δημιουργία Εισόδων και Στοχευμένων Ακολουθιών
- Προσέγγιση Ολισθηρού Παραθύρου:
- Εισόδους: Κάθε είσοδος αποτελείται από
max_length
tokens. - Στοχευμένες Ακολουθίες: Κάθε στοχευμένη ακολουθία αποτελείται από τα tokens που ακολουθούν άμεσα την αντίστοιχη είσοδο.
- Δημιουργία Ακολουθιών:
Θέση Παραθύρου | Είσοδος | Στοχευμένη Ακολουθία |
---|---|---|
1 | ["Lorem", "ipsum", "dolor", "sit"] | ["ipsum", "dolor", "sit", "amet,"] |
2 | ["ipsum", "dolor", "sit", "amet,"] | ["dolor", "sit", "amet,", "consectetur"] |
3 | ["dolor", "sit", "amet,", "consectetur"] | ["sit", "amet,", "consectetur", "adipiscing"] |
4 | ["sit", "amet,", "consectetur", "adipiscing"] | ["amet,", "consectetur", "adipiscing", "elit."] |
- Αποτελέσματα Εισόδων και Στοχευμένων Πινάκων:
- Είσοδος:
[
["Lorem", "ipsum", "dolor", "sit"],
["ipsum", "dolor", "sit", "amet,"],
["dolor", "sit", "amet,", "consectetur"],
["sit", "amet,", "consectetur", "adipiscing"],
]
- Στοχευμένη:
[
["ipsum", "dolor", "sit", "amet,"],
["dolor", "sit", "amet,", "consectetur"],
["sit", "amet,", "consectetur", "adipiscing"],
["amet,", "consectetur", "adipiscing", "elit."],
]
Οπτική Αναπαράσταση
Θέση Token | Token |
---|---|
1 | Lorem |
2 | ipsum |
3 | dolor |
4 | sit |
5 | amet, |
6 | consectetur |
7 | adipiscing |
8 | elit. |
Ολισθηρό Παράθυρο με Βήμα 1:
- Πρώτο Παράθυρο (Θέσεις 1-4): ["Lorem", "ipsum", "dolor", "sit"] → Στοχευμένη: ["ipsum", "dolor", "sit", "amet,"]
- Δεύτερο Παράθυρο (Θέσεις 2-5): ["ipsum", "dolor", "sit", "amet,"] → Στοχευμένη: ["dolor", "sit", "amet,", "consectetur"]
- Τρίτο Παράθυρο (Θέσεις 3-6): ["dolor", "sit", "amet,", "consectetur"] → Στοχευμένη: ["sit", "amet,", "consectetur", "adipiscing"]
- Τέταρτο Παράθυρο (Θέσεις 4-7): ["sit", "amet,", "consectetur", "adipiscing"] → Στοχευμένη: ["amet,", "consectetur", "adipiscing", "elit."]
Κατανόηση Βήματος
- Βήμα 1: Το παράθυρο προχωράει μπροστά κατά ένα token κάθε φορά, με αποτέλεσμα πολύ επικαλυπτόμενες ακολουθίες. Αυτό μπορεί να οδηγήσει σε καλύτερη εκμάθηση των συμφραζομένων αλλά μπορεί να αυξήσει τον κίνδυνο υπερπροσαρμογής καθώς τα παρόμοια σημεία δεδομένων επαναλαμβάνονται.
- Βήμα 2: Το παράθυρο προχωράει μπροστά κατά δύο tokens κάθε φορά, μειώνοντας την επικάλυψη. Αυτό μειώνει την πλεονασματικότητα και το υπολογιστικό φορτίο αλλά μπορεί να χάσει κάποιες λεπτομέρειες συμφραζομένων.
- Βήμα Ίσο με το max_length: Το παράθυρο προχωράει μπροστά κατά το συνολικό μέγεθος του παραθύρου, με αποτέλεσμα μη επικαλυπτόμενες ακολουθίες. Αυτό ελαχιστοποιεί την πλεονασματικότητα των δεδομένων αλλά μπορεί να περιορίσει την ικανότητα του μοντέλου να μάθει εξαρτήσεις μεταξύ των ακολουθιών.
Παράδειγμα με Βήμα 2:
Χρησιμοποιώντας το ίδιο κείμενο με tokens και max_length
4:
- Πρώτο Παράθυρο (Θέσεις 1-4): ["Lorem", "ipsum", "dolor", "sit"] → Στοχευμένη: ["ipsum", "dolor", "sit", "amet,"]
- Δεύτερο Παράθυρο (Θέσεις 3-6): ["dolor", "sit", "amet,", "consectetur"] → Στοχευμένη: ["sit", "amet,", "consectetur", "adipiscing"]
- Τρίτο Παράθυρο (Θέσεις 5-8): ["amet,", "consectetur", "adipiscing", "elit."] → Στοχευμένη: ["consectetur", "adipiscing", "elit.", "sed"] (Υποθέτοντας συνέχεια)
Παράδειγμα Κώδικα
Ας κατανοήσουμε αυτό καλύτερα από ένα παράδειγμα κώδικα από https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb:
# Download the text to pre-train the LLM
import urllib.request
url = ("https://raw.githubusercontent.com/rasbt/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt")
file_path = "the-verdict.txt"
urllib.request.urlretrieve(url, file_path)
with open("the-verdict.txt", "r", encoding="utf-8") as f:
raw_text = f.read()
"""
Create a class that will receive some params lie tokenizer and text
and will prepare the input chunks and the target chunks to prepare
the LLM to learn which next token to generate
"""
import torch
from torch.utils.data import Dataset, DataLoader
class GPTDatasetV1(Dataset):
def __init__(self, txt, tokenizer, max_length, stride):
self.input_ids = []
self.target_ids = []
# Tokenize the entire text
token_ids = tokenizer.encode(txt, allowed_special={"<|endoftext|>"})
# Use a sliding window to chunk the book into overlapping sequences of max_length
for i in range(0, len(token_ids) - max_length, stride):
input_chunk = token_ids[i:i + max_length]
target_chunk = token_ids[i + 1: i + max_length + 1]
self.input_ids.append(torch.tensor(input_chunk))
self.target_ids.append(torch.tensor(target_chunk))
def __len__(self):
return len(self.input_ids)
def __getitem__(self, idx):
return self.input_ids[idx], self.target_ids[idx]
"""
Create a data loader which given the text and some params will
prepare the inputs and targets with the previous class and
then create a torch DataLoader with the info
"""
import tiktoken
def create_dataloader_v1(txt, batch_size=4, max_length=256,
stride=128, shuffle=True, drop_last=True,
num_workers=0):
# Initialize the tokenizer
tokenizer = tiktoken.get_encoding("gpt2")
# Create dataset
dataset = GPTDatasetV1(txt, tokenizer, max_length, stride)
# Create dataloader
dataloader = DataLoader(
dataset,
batch_size=batch_size,
shuffle=shuffle,
drop_last=drop_last,
num_workers=num_workers
)
return dataloader
"""
Finally, create the data loader with the params we want:
- The used text for training
- batch_size: The size of each batch
- max_length: The size of each entry on each batch
- stride: The sliding window (how many tokens should the next entry advance compared to the previous one). The smaller the more overfitting, usually this is equals to the max_length so the same tokens aren't repeated.
- shuffle: Re-order randomly
"""
dataloader = create_dataloader_v1(
raw_text, batch_size=8, max_length=4, stride=1, shuffle=False
)
data_iter = iter(dataloader)
first_batch = next(data_iter)
print(first_batch)
# Note the batch_size of 8, the max_length of 4 and the stride of 1
[
# Input
tensor([[ 40, 367, 2885, 1464],
[ 367, 2885, 1464, 1807],
[ 2885, 1464, 1807, 3619],
[ 1464, 1807, 3619, 402],
[ 1807, 3619, 402, 271],
[ 3619, 402, 271, 10899],
[ 402, 271, 10899, 2138],
[ 271, 10899, 2138, 257]]),
# Target
tensor([[ 367, 2885, 1464, 1807],
[ 2885, 1464, 1807, 3619],
[ 1464, 1807, 3619, 402],
[ 1807, 3619, 402, 271],
[ 3619, 402, 271, 10899],
[ 402, 271, 10899, 2138],
[ 271, 10899, 2138, 257],
[10899, 2138, 257, 7026]])
]
# With stride=4 this will be the result:
[
# Input
tensor([[ 40, 367, 2885, 1464],
[ 1807, 3619, 402, 271],
[10899, 2138, 257, 7026],
[15632, 438, 2016, 257],
[ 922, 5891, 1576, 438],
[ 568, 340, 373, 645],
[ 1049, 5975, 284, 502],
[ 284, 3285, 326, 11]]),
# Target
tensor([[ 367, 2885, 1464, 1807],
[ 3619, 402, 271, 10899],
[ 2138, 257, 7026, 15632],
[ 438, 2016, 257, 922],
[ 5891, 1576, 438, 568],
[ 340, 373, 645, 1049],
[ 5975, 284, 502, 284],
[ 3285, 326, 11, 287]])
]
Στρατηγικές Προχωρημένης Δειγματοληψίας (2023-2025)
1. Ζυγισμένη Μίξη Βασισμένη στη Θερμοκρασία
Τα πιο προηγμένα LLM σπάνια εκπαιδεύονται σε ένα μόνο σώμα. Αντίθετα, δειγματοληπτούν από πολλές ετερογενείς πηγές δεδομένων (κώδικας, ιστός, ακαδημαϊκά άρθρα, φόρουμ…). Η σχετική αναλογία κάθε πηγής μπορεί να επηρεάσει σημαντικά την απόδοση κατά την εκτέλεση. Πρόσφατα ανοιχτού κώδικα μοντέλα όπως το Llama 2 εισήγαγαν ένα σχέδιο δειγματοληψίας βασισμένο στη θερμοκρασία όπου η πιθανότητα επιλογής ενός εγγράφου από το σώμα i γίνεται
p(i) = \frac{w_i^{\alpha}}{\sum_j w_j^{\alpha}}
• wi – ποσοστό ακατέργαστου κειμένου του σώματος i
• α ("θερμοκρασία") – μια τιμή στο (0,1]. α < 1 επίπεδοποιεί την κατανομή, δίνοντας περισσότερη βαρύτητα σε μικρότερα σώματα υψηλής ποιότητας.
Το Llama 2 χρησιμοποίησε α = 0.7 και έδειξε ότι η μείωση του α αύξησε τις βαθμολογίες αξιολόγησης σε εργασίες που απαιτούν γνώσεις, διατηρώντας σταθερό το μείγμα εκπαίδευσης. Το ίδιο κόλπο υιοθετεί και το Mistral (2023) και το Claude 3.
from collections import Counter
def temperature_sample(corpus_ids, alpha=0.7):
counts = Counter(corpus_ids) # number of tokens seen per corpus
probs = {c: c_count**alpha for c, c_count in counts.items()}
Z = sum(probs.values())
probs = {c: p/Z for c, p in probs.items()}
# Now draw according to probs to fill every batch