当前位置: 首页 > 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.lryc.cn/news/608352.html

相关文章:

  • FFmpeg+javacpp中纯音频播放
  • 互联网医院系统,互联网医院好处有哪些?
  • 音视频学习(四十八):PCM和WAV
  • CatBoost 完整解析:类别特征友好的梯度提升框架
  • 基于单片机智能雨刷器/汽车刮水器设计
  • zset 中特殊的操作
  • nodejs读写文件
  • 【redis】基于工业界技术分享的内容总结
  • C++ 模板初阶
  • 阿里云:Ubuntu系统部署宝塔
  • 回归预测 | Matlab实现CNN-LSTM-self-Attention多变量回归预测
  • ventoy 是一个非常棒的开源工具,可以制作多系统的usb启动盘
  • 基于落霞归雁思维框架的软件需求管理实践指南
  • Vulnhub ELECTRICAL靶机复现(附提权)
  • 计算机技术与软件专业技术资格(水平)考试简介
  • Dispersive Loss:为生成模型引入表示学习 | 如何分析kaiming新提出的dispersive loss,对扩散模型和aigc会带来什么影响?
  • 《React+TypeScript实战:前端状态管理的安全架构与性能优化深解》
  • 【Unity3D实例-功能-移动】小兵移动-通过鼠标点击进行
  • 咨询进阶——解读57页企业发展战略咨询常用工具【附全文阅读】
  • Java Optional 类教程详解
  • C++ vector底层实现与迭代器失效问题
  • 【智能体cooragent】新智能体创建相关代码解析
  • Node.js 操作 MongoDB
  • Linux系统编程Day3-- Linux常用操作(终)
  • 2025-08 安卓开发面试拷打记录(面试题)
  • 3 使用 Jenkins 构建镜像:将你的应用打包成镜像
  • K8S部署ELK(三):部署Elasticsearch搜索引擎
  • 【机器学习】非线性分类算法(上):KNN(基于距离相似度)与朴素(特征独立)贝叶斯(基于概率统计)
  • 排序算法-堆排序
  • SQL 四大语言分类详解:DDL、DML、DCL、DQL