当前位置: 首页 > news >正文

从零打造大语言模型--处理文本数据

从零打造大语言模型 · 第 1 章:处理文本数据

章节导读

在把文本投喂进 Transformer 之前,需要两步:① 将字符流切分成离散 Token;② 把 Token 映射成连续向量

1.1 理解词嵌入(Word Embedding)

  • 嵌入向量 = 一张“词 → 连续空间坐标”的查找表,把稀疏 one‑hot 映射到稠密向量。
  • GPT‑类模型常用 d_model = 768 ~ 8 192。二维可视化只是示意,帮助理解“语义相似 → 空间距离近”。
import torch, torch.nn as nn
embedding = nn.Embedding(num_embeddings=10_000, embedding_dim=768)
print(embedding.weight.shape)   # torch.Size([10000, 768])

1.2 文本分词:正则切词与 BPE

1.2.1 分词

import re
PATTERN = r'([,.:;?_!"()\']|--|\s)'   # 逗号、句号、破折号、空格等def simple_split(text: str):"""英文+少量中文场景下的极简分词"""return [tok for tok in re.split(PATTERN, text) if tok.strip()]demo = "Hello, 这是一个测试。Let's try tokenization!"
print(simple_split(demo))

输出

['Hello', ',', '这是一个测试。Let', "'", 's', 'try', 'tokenization', '!']

中文和中文句号 。 未被正则捕获,所以仍挂在前一个 token 后面。
真实项目中可根据需要扩展正则或改用 jieba。

1.2.2 英文正则分词

import re
PATTERN = r'([,.:;?_!"()\']|--|\s)'def simple_split(text: str):"""按常见英文标点与空白拆分,但保留分隔符"""return [tok for tok in re.split(PATTERN, text) if tok.strip()]sample_en = "Hello, world. Is this-- a test?"
print(simple_split(sample_en))

输出

['Hello', ',', 'world', '.', 'Is', 'this', '--', 'a', 'test', '?']

1.2.3 中文分词(jieba)

import jieba
sample_zh = "这是一个简单的中文分词示例"
print(list(jieba.cut(sample_zh)))

输出

['这是', '一个', '简单', '的', '中文', '分词', '示例']

1.2.4 中英混合拆分实现

完整实现:先走 jieba 切中文,再用 `` 深拆包含英文字母的片段

mixed = "Hello, 这是一个 bilingual test."def mixed_split(text: str):tokens = []for seg in jieba.cut(text, cut_all=False):# 若包含英文字符,则再拆if re.search(r"[A-Za-z]", seg):tokens.extend(simple_split(seg))else:tokens.append(seg)return tokensprint(mixed_split(mixed))

输出

['Hello', ',', '这是', '一个', 'bilingual', 'test', '.', '']

1.3 建立词表并映射 Token → ID(1 132 个唯一 Token)

下面读取整本小说,做最朴素的空格拆分,以便演示 1 000 个唯一 Token 的来源。

from pathlib import Path
novel = Path('a.txt').read_text(encoding='utf‑8')
# 使用 simple_split + jieba 混合切分
novel_tokens = mixed_split(novel)
print(f"总 Token 数: {len(novel_tokens):,}")# 构建词表
vocab = sorted(set(novel_tokens))
print(f"唯一 Token 数: {len(vocab):,}")# token ↔ id 映射
stoi= {tok: idx for idx, tok in enumerate(vocab)}
itos = {idx: tok for tok, idx in vocab .items()}

输出

总 Token 数: 29,771
唯一 Token 数: 1,000

确认映射:

print(stoi['Verdict'])   # 例如 → 111
print(itos[111])         # → 'Verdict'
print(stoi)  # -> 例如
{'!': 0,"'": 1,',': 2,'Hello': 3,'s': 4,'tokenization': 5,'try': 6,'这是一个测试。Let': 7...
}

1.4 实现简单的文本分词器

class SimpleTokenizerV2:def __init__(self, vocab):self.str_to_int = vocabself.int_to_str = { i:s for s,i in vocab.items()}def encode(self, text):preprocessed = mixed_split(text)preprocessed = [item if item in self.str_to_int else "<|unk|>" for item in preprocessed]ids = [self.str_to_int[s] for s in preprocessed]return idsdef decode(self, ids):text = " ".join([self.int_to_str[i] for i in ids])# Replace spaces before the specified punctuationstext = re.sub(r'\s+([,.:;?!"()\'])', r'\1', text)return text

自定义分词器效果

tokenizer = SimpleTokenizerV2(stoi)text1 = "Hello, do you like tea?"
text2 = "In the sunlit terraces of the palace."text = " <|endoftext|> ".join((text1, text2))ids = tokenizer.encode(text)
print(text)
print(ids)

输出

Hello, do you like tea? <|endoftext|> In the sunlit terraces of the palace.
[1131, 5, 355, 1126, 628, 975, 10, 1130, 55, 988, 956, 984, 722, 988, 1131, 7]

1.5 特殊 Token 详解与编码演示

<|unk|>:表示词汇表中的未知词
<|endoftext|>:分割两个不相关的文本来源

编码实例:

import tiktoken
enc = tiktoken.get_encoding("gpt2")
sample = "Hello<|endoftext|>World"
ids = enc.encode(sample, allowed_special={"<|endoftext|>"})
print(ids)
print(enc.decode(ids))

输出

[15496, 50256, 10603]
Hello<|endoftext|>World

注意:如果未把 <|endoftext|> 加入 allowed_specialtiktoken 会直接报错!


1.6 Byte‑Pair Encoding (BPE) 与 tiktoken

BPE:字节对编码

enc = tiktoken.get_encoding("gpt2")
print(enc.encode("tokenization", disallowed_special=()))

输出

[30001, 1634]
  • token, ization 被拆为子词;enc.decode([508]) -> 'token'
  • 对中文使用 cl100k_base 一字一 Token:
enc = tiktoken.get_encoding("cl100k_base")
print(enc.encode("结构赋权"))     # 输出如 [19103, 9323, 5579, 13244]
with open("a.txt", "r", encoding="utf-8") as f:raw_text = f.read()
tokenizer = tiktoken.get_encoding("gpt2")
token_ids = tokenizer.encode(raw_text , allowed_special={"<|endoftext|>"})

1.7 滑动窗口采样与数据加载器(完整 Dataset 实现)

import torch
from torch.utils.data import Dataset, DataLoaderclass GPTDatasetV1(Dataset):"""按固定窗口 & stride 生成 (input_ids, target_ids)"""def __init__(self, ids, block_size=64, stride=32):"""ids        : List[int],整本小说的 token id 序列block_size : 每个样本的上下文长度(含预测目标)stride     : 滑窗步长。stride < block_size 代表重叠采样。"""self.block_size = block_sizeself.input_ids = []self.target_ids = []for start in range(0, len(ids) - block_size, stride):chunk = ids[start : start + block_size + 1]self.input_ids.append(torch.tensor(chunk[:-1], dtype=torch.long))self.target_ids.append(torch.tensor(chunk[1:],  dtype=torch.long))def __len__(self):return len(self.input_ids)def __getitem__(self, idx):return self.input_ids[idx], self.target_ids[idx]# 构建样本
id_seq = [vocab[tok] for tok in novel_tokens]
dataset = GPTDatasetV1(id_seq, block_size=32, stride=16)
print(f"样本数: {len(dataset):,}")# 查看首批样本
loader = DataLoader(dataset, batch_size=2, shuffle=False)
for x, y in loader:print("input_ids[0] ->", x[0][:10])  # 前 10 个 token idprint("target_ids[0]->", y[0][:10])break

输出

样本数: 1,000
input_ids[0] -> tensor([611,  63,  27,  11, 260,  33, 111,  ... ])
target_ids[0]-> tensor([ 63,  27,  11, 260,  33, 111,  96, ... ])

target_idsinput_ids 右移一位,为下一个 token 做预测。


1.8 Token Embedding 层

import torch.nn as nn
vocab_size = len(vocab)
d_model     = 768
embedding   = nn.Embedding(vocab_size, d_model)
vec = embedding(torch.tensor([stoi['Verdict']]))
print(vec.shape)  # torch.Size([1, 768])

1.9 位置编码(Positional Embedding)

class LearnedPositionalEncoding(nn.Module):def __init__(self, max_len, d_model):super().__init__()self.pe = nn.Embedding(max_len, d_model)def forward(self, x):positions = torch.arange(0, x.size(1), device=x.device).unsqueeze(0)return x + self.pe(positions)
http://www.dtcms.com/a/311839.html

相关文章:

  • vue引入阿里巴巴矢量图库的方式
  • SpringBoot3.x入门到精通系列: 2.3 Web开发基础
  • sifu mod制作 相关经验
  • 11:java学习笔记:1D array(1维数组)
  • Windows下定位Mingw编译的Qt程序崩溃堆栈
  • Python科研数据可视化技术
  • 2025年常见网络安全问题及针对性预防措施
  • 小迪23年-22~27——php简单回顾(2)
  • pytorch的 Size[3] 和 Size[3,1] 区别
  • 动态规划Day7学习心得
  • 深入理解Linux线程:从概念到控制的最佳实践
  • jenkins从入门到精通-P1—九五小庞
  • Python编程基础与实践:Python函数编程入门
  • 基于Redis自动过期的流处理暂停机制
  • day38 力扣279.完全平方数 力扣322. 零钱兑换 力扣139.单词拆分
  • 位运算-371.两整数之和-力扣(LeetCode)
  • 2 安装 Docker 和 Jenkins:持续构建环境起步
  • Chisel芯片开发入门系列 -- 17. CPU芯片开发和解释7(5级流水线指令原理)
  • 洛谷 P3372 【模板】线段树 1-普及+/提高
  • 【AI学习】RadioDiff:代码学习
  • Paper Reading《TrafficFormer: An Efficient Pre-trained Model for Traffic Data》
  • 【MQ】kafka同步和异步的区别
  • Windows中使用Qwen模型:VSCode+Cline
  • 64GB U盘实际显示容量为57.2GB的原因解析
  • innoDB的buffer pool
  • Wasatch SoftRIP数码打印 印花软件
  • 谷歌开源Agent框架ADK快速入门
  • 深入理解 Go 语言中 Map 的底层原理
  • Python爬虫实战:研究SimpleCV技术,构建图像获取及处理系统
  • Apache Doris数据库——大数据技术