# 2. Вибірка Даних {{#include ../../banners/hacktricks-training.md}} ## **Вибірка Даних** **Вибірка Даних** є важливим процесом підготовки даних для навчання великих мовних моделей (LLMs), таких як GPT. Це передбачає організацію текстових даних у вхідні та цільові послідовності, які модель використовує для навчання, як передбачити наступне слово (або токен) на основі попередніх слів. Правильна вибірка даних забезпечує ефективне захоплення мовних патернів і залежностей моделлю. > [!TIP] > Мета цього другого етапу дуже проста: **Вибрати вхідні дані та підготувати їх для етапу навчання, зазвичай розділяючи набір даних на речення певної довжини та також генеруючи очікувану відповідь.** ### **Чому Вибірка Даних Важлива** LLMs, такі як GPT, навчаються генерувати або передбачати текст, розуміючи контекст, наданий попередніми словами. Щоб досягти цього, навчальні дані повинні бути структуровані таким чином, щоб модель могла вивчити взаємозв'язок між послідовностями слів і їх наступними словами. Цей структурований підхід дозволяє моделі узагальнювати та генерувати зв'язний і контекстуально релевантний текст. ### **Ключові Концепції у Вибірці Даних** 1. **Токенізація:** Розбиття тексту на менші одиниці, звані токенами (наприклад, слова, підслова або символи). 2. **Довжина Послідовності (max_length):** Кількість токенів у кожній вхідній послідовності. 3. **Зсувне Вікно:** Метод створення перекриваючих вхідних послідовностей шляхом переміщення вікна по токенізованому тексту. 4. **Крок:** Кількість токенів, на яку зсувне вікно рухається вперед для створення наступної послідовності. ### **Приклад Крок за Кроком** Давайте пройдемо через приклад, щоб проілюструвати вибірку даних. **Приклад Тексту** ```arduino "Lorem ipsum dolor sit amet, consectetur adipiscing elit." ``` **Токенізація** Припустимо, ми використовуємо **базовий токенізатор**, який розділяє текст на слова та розділові знаки: ```vbnet Tokens: ["Lorem", "ipsum", "dolor", "sit", "amet,", "consectetur", "adipiscing", "elit."] ``` **Параметри** - **Максимальна довжина послідовності (max_length):** 4 токени - **Крок ковзного вікна:** 1 токен **Створення вхідних та цільових послідовностей** 1. **Підхід ковзного вікна:** - **Вхідні послідовності:** Кожна вхідна послідовність складається з `max_length` токенів. - **Цільові послідовності:** Кожна цільова послідовність складається з токенів, які безпосередньо слідують за відповідною вхідною послідовністю. 2. **Генерація послідовностей:**
Позиція вікнаВхідна послідовністьЦільова послідовність
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."]
3. **Результуючі масиви вхідних та цільових даних:** - **Вхідні:** ```python [ ["Lorem", "ipsum", "dolor", "sit"], ["ipsum", "dolor", "sit", "amet,"], ["dolor", "sit", "amet,", "consectetur"], ["sit", "amet,", "consectetur", "adipiscing"], ] ``` - **Цільові:** ```python [ ["ipsum", "dolor", "sit", "amet,"], ["dolor", "sit", "amet,", "consectetur"], ["sit", "amet,", "consectetur", "adipiscing"], ["amet,", "consectetur", "adipiscing", "elit."], ] ``` **Візуальне представлення**
Позиція токенаТокен
1Lorem
2ipsum
3dolor
4sit
5amet,
6consectetur
7adipiscing
8elit.
**Ковзне вікно з кроком 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:** Вікно рухається вперед на один токен щоразу, що призводить до високої перекритості послідовностей. Це може призвести до кращого навчання контекстуальних зв'язків, але може збільшити ризик перенавчання, оскільки подібні дані повторюються. - **Крок 2:** Вікно рухається вперед на два токени щоразу, зменшуючи перекриття. Це зменшує надмірність і обчислювальне навантаження, але може пропустити деякі контекстуальні нюанси. - **Крок, рівний max_length:** Вікно рухається вперед на весь розмір вікна, що призводить до неперекриваючих послідовностей. Це мінімізує надмірність даних, але може обмежити здатність моделі вивчати залежності між послідовностями. **Приклад з кроком 2:** Використовуючи той же токенізований текст і `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](https://github.com/rasbt/LLMs-from-scratch/blob/main/ch02/01_main-chapter-code/ch02.ipynb): ```python # 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]]) ] ``` ## Advanced Sampling Strategies (2023-2025) ### 1. Temperature-Based Mixture Weighting Сучасні 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. ```python 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 ``` ``` ### 2. Sequence Packing / Dynamic Batching GPU memory is wasted when every sequence in a batch is padded to the longest example. "Packing" concatenates multiple shorter sequences until the **exact** `max_length` is reached and builds a parallel `attention_mask` so that tokens do not attend across segment boundaries. Packing can improve throughput by 20–40 % with no gradient change and is supported out-of-the-box in * PyTorch `torchtext.experimental.agents.PackedBatch` * HuggingFace `DataCollatorForLanguageModeling(pad_to_multiple_of=…)` Dynamic batching frameworks (e.g. FlashAttention 2, vLLM 2024) combine sequence packing with just-in-time kernel selection, enabling thousand-token context training at 400+ K tokens/s on A100-80G. ### 3. Deduplication & Quality Filtering Repeated passages cause memorization and provide an easy channel for data-poisoning. Modern pipelines therefore: 1. MinHash/FAISS near-duplicate detection at **document** and **128-gram** level. 2. Filter documents whose perplexity under a small reference model is > µ + 3σ (noisy OCR, garbled HTML). 3. Block-list documents that contain PII or CWE keywords using regex & spaCy NER. The Llama 2 team deduplicated with 8-gram MinHash and removed ~15 % of CommonCrawl before sampling. OpenAI’s 2024 "Deduplicate Everything" paper demonstrates ≤0.04 duplicate ratio reduces over-fitting and speeds convergence. ## Security & Privacy Considerations During Sampling ### Data-Poisoning / Backdoor Attacks Researchers showed that inserting <1 % backdoored sentences can make a model obey a hidden trigger ("PoisonGPT", 2023). Recommended mitigations: * **Shuffled mixing** – make sure adjacent training examples originate from different sources; this dilutes gradient alignment of malicious spans. * **Gradient similarity scoring** – compute cosine similarity of example gradient to batch average; outliers are candidates for removal. * **Dataset versioning & hashes** – freeze immutable tarballs and verify SHA-256 before each training run. ### Membership-Inference & Memorization Long overlap between sliding-window samples increases the chance that rare strings (telephone numbers, secret keys) are memorized. OpenAI’s 2024 study on ChatGPT memorization reports that raising stride from 1 × `max_length` to 4 × reduces verbatim leakage by ≈50 % with negligible loss in perplexity. Practical recommendations: * Use **stride ≥ max_length** except for <1B parameter models where data volume is scarce. * Add random masking of 1-3 tokens per window during training; this lowers memorization while preserving utility. --- ## References - [Build a Large Language Model from Scratch (Manning, 2024)](https://www.manning.com/books/build-a-large-language-model-from-scratch) - [Llama 2: Open Foundation and Fine-Tuned Chat Models (2023)](https://arxiv.org/abs/2307.09288) - [PoisonGPT: Assessing Backdoor Vulnerabilities in Large Language Models (BlackHat EU 2023)](https://arxiv.org/abs/2308.12364) {{#include ../../banners/hacktricks-training.md}}