本文深入解析了大语言模型输入的完整处理流程从原始文本的分词、编码、滑动窗口构造训练样本到使用DataLoader组织批量数据再到将token ids转换为词元嵌入和位置嵌入最终形成模型接收的输入表示。文章强调了输入流水线的稳定性和重要性指出模型从未直接处理原始文本而是处理经过严格组织的张量表示。通过理解这一流程读者可以更好地掌握大语言模型的输入定义为后续模型训练和开发奠定坚实基础。背景导语 (Background)大语言模型并不直接接收原始文本。模型真正处理的是经过分词、编码、切片与嵌入之后的数值张量。换言之所谓 “输入文本”在进入模型计算图之前已经被改写成一种结构明确、可批量处理、可参与数值运算的表示形式。只要这条输入流水线不稳定后续训练、评估与推理都难以获得可靠结果。因此本章关注的不是所有可能的文本处理方法而是一条足够清楚、足够稳定、能够直接落地到工程中的主路径使用 GPT-2 分词器将原始文本编码为 token ids利用滑动窗口构造语言模型训练所需的输入—目标序列对借助 DataLoader 组织批次并进一步将 token ids 转换为词元嵌入与位置嵌入形成模型实际接收的输入表示。从实战角度看这一章的价值并不只是 “把文本切开”。更关键的是它把后续模型训练所依赖的数据形态定义清楚了原始文本如何转为离散索引离散索引如何组织为样本样本如何批量化批量张量又如何被映射为连续向量。只有这条链路成立模型层面的讨论才有坚实基础。环境说明 (Environment)建议使用如下环境完成本章实践。Python3.10 及以PyTorch2.0 及以上tiktoken兼容 GPT-2 编码的稳定版本操作系统Windows、macOS 或 Linux 均可硬件具备常规开发能力的个人设备即可完成本章样例若进一步扩大文本规模、上下文长度或批次大小建议使用 GPU 以提升运行效率文本文件默认读取data/raw_data/The-Verdict.txt本章依赖较少核心仅包括pathlib、tiktoken、torch与torch.utils.data。这意味着读者不必先搭建复杂的训练框架就可以直接进入文本输入流水线的核心实现。对于本章而言重点不在训练大规模模型而在于把 “原始文本如何被组织成模型输入” 这一过程走通并且看清其中每一步的数据形态。适用读者 (Target Audience)本章适合三类读者。第一类是已经具备 Python 和 PyTorch 基础但尚未真正理解 “大语言模型输入到底长什么样” 的读者。对这类读者而言本章最重要的收获不是记住若干 API而是建立从原始文本到输入嵌入的完整认知链条。第二类是正在实现小型语言模型、实验脚本或数据处理组件的开发者。本章提供的不是抽象概念而是一条能够直接复现、便于扩展的实战路径。第三类是希望从工程视角理解文本预处理的读者。许多问题并不出在模型主体而是出在输入端例如分词器不一致、滑动窗口越界、张量类型错误、设备不匹配或位置嵌入长度不足。本章会把这些问题前置到可控范围内。导读 (Preface)本章不从 “文本处理方法史” 展开也不把重点放在手写分词器或规则设计上而是围绕一条更贴近当前大语言模型实践的路径组织内容。我们从一份可运行的实现出发但正文并不逐行解释代码而是先把问题讲清楚再让代码作为落地示例去支撑这些问题。这条主线可以概括为原始文本 → token ids → 输入—目标序列对 → 批量张量 → 词元嵌入与位置嵌入 → 最终输入嵌入整章的写作目标只有一个让读者在读完之后不仅知道模型输入需要 “先分词、再嵌入”而且能够清楚地说出这条流水线中每一步的作用、边界和数据形态。由于本章被设计为独立章节因此正文不依赖其他章节的铺垫也不承担后续章节的过渡任务。它必须依靠自身内部的逻辑闭环成立。目录 (Table of Contents)引言从原始文本到 token ids使用滑动窗口构造训练样本使用 DataLoader 组织批量数据从 token ids 到输入嵌入完整代码小结引言文本之所以不能直接输入模型不是因为它 “不够高级”而是因为它不满足神经网络计算所要求的数值形式。神经网络擅长处理的是张量而不是字符串。对模型来说一段原始文本必须先被离散化、索引化、批量化再被映射到连续向量空间中才真正具备进入模型主体的资格。因此本章处理的并不是 “文本本身”而是 “文本进入模型前的全部转换过程”。在这条转换链路中每一步都很重要但各自承担的职责并不相同。分词器负责把文本切分成可编码的单位编码器负责把这些单位映射成整数索引滑动窗口负责从长序列中提取训练样本DataLoader 负责将样本组织成批次嵌入层则负责把离散索引变成连续表示。图 1文本进入语言模型前的输入流水线。 原始文本首先经 GPT-2 分词器编码为 token ids随后滑动窗口从连续序列中构造输入序列与右移一位的目标序列多个样本再由 DataLoader 组织为批量张量最后词元嵌入与位置嵌入共同生成模型实际接收的输入嵌入。该流程定义了文本从字符串形式进入神经网络计算图之前的标准表示路径。这里最重要的一点是模型从来没有 “看到原文”它只看到了经过处理之后的张量。因此理解这一章实际上是在理解模型输入的真正定义。从原始文本到 token ids本章采用 GPT-2 分词器作为文本进入模型的第一道入口。这样做的理由并不复杂它足够成熟、足够常见而且能够避免把本章的重点拖入大量规则分词的细枝末节之中。对于当前这一章而言更重要的问题不是 “所有可能的分词方法有哪些”而是 “我们如何得到一条稳定的 token id 序列”。一旦这条序列被建立起来后面的样本构造、批量组织和嵌入计算才有统一的基础。从实现上看这一阶段至少包含两个清楚的动作。第一读取文本文件并确认路径与编码方式都有效第二使用 GPT-2 分词器将整段文本编码为整数序列。这里的整数不是普通数字而是词表中的索引位置。它们本身不携带语义但它们为后续嵌入查表提供了唯一入口。表 1文本输入流水线中的核心组件、输入输出与职责边界组件典型输入典型输出主要职责对应实现文本读取模块文件路径原始文本字符串读取文本并完成路径有效性校验read_text_file分词与编码模块原始文本字符串token id 张量使用 GPT-2 分词器完成文本到整数索引的转换get_tokenizer、encode_text_to_token_ids滑动窗口数据集token 序列、max_length、stride输入—目标样本对从长序列中构造语言模型训练所需的窗口样本GPTSlidingWindowDataset批量加载模块数据集、批次参数批量张量将样本组织为可直接送入训练循环的批次create_dataloader词元嵌入层token id 矩阵词元嵌入张量将离散 token 索引映射为连续向量表示create_embedding_layers中的token_embedding_layer位置嵌入层位置索引位置嵌入张量为序列位置注入可训练的位置表示create_embedding_layers中的position_embedding_layer输入嵌入构造模块token id 矩阵、两类嵌入层最终输入嵌入组合词元嵌入与位置嵌入形成模型输入build_input_embeddings、create_input_embeddings注表中 “典型输出” 指主要输出形式不包括异常分支与校验逻辑张量形状默认采用 batch-first 表示代码标识符在排版中建议使用等宽字体。在这一阶段文本首先被读入内存再通过 GPT-2 分词器编码为整数索引序列。这里的核心不是 “分词器有多复杂”而是 “从这一刻开始原始文本已经被稳定地改写为可计算对象”。后续所有数据集切分、批量加载和嵌入构造都会建立在这条整数序列之上。这里也可以用一个最小示例把输入—目标关系先建立起来。假设一段连续 token 序列为[40134, 2052, 133, 389, 12]当上下文长度 context_size 4 时窗口级输入—目标样本对就是x [40134, 2052, 133, 389] y [2052, 133, 389, 12]如果进一步按 “给定前文预测下一个 token” 的方式展开那么它对应四组逐步预测关系[40134] -2052 [40134, 2052] -133 [40134, 2052, 133] -389 [40134, 2052, 133, 389] -12这个例子看似简单却很重要因为它提前揭示了下一节中滑动窗口样本构造的核心逻辑目标序列并不是另造的一份新数据而是输入序列在原始 token 序列上整体右移一位后的结果。使用滑动窗口构造训练样本把整段文本编码成 token ids 之后仍然不能直接拿来训练语言模型。语言模型并不是一次性读完整篇文章而是在一个局部上下文中学习 “下一个 token 应该是什么”。这意味着我们必须从整条 token 序列中不断提取长度固定的片段并为每个片段配上对应的目标序列。滑动窗口正是为此服务的。它的基本思想是在整段 token 序列上取一个长度为 max_length 的窗口作为输入然后把这个窗口整体向右平移一个位置得到对应的目标序列。于是输入和目标在形式上几乎相同只是对齐位置不同。例如当输入序列为[t1, t2, t3, t4]对应目标序列就是[t2, t3, t4, t5]这种构造方式看似简单却非常重要因为它直接把 “预测下一个 token” 变成了一个可训练的监督学习问题。图 2使用滑动窗口构造语言模型训练样本。 给定一段连续的 token 序列长度为 max_length 的窗口被用作输入序列目标序列由同一窗口整体向右偏移一个位置得到。参数 stride 决定相邻样本窗口的位移量从而影响样本之间的重叠程度和数据利用方式。在当前实现中窗口的长度由 max_length 控制窗口之间的位移由 stride 控制。二者共同决定了样本数量、样本重叠程度和数据利用方式。若 stride 小于 max_length相邻样本会重叠若二者相等则形成非重叠窗口。当前章节示例更强调后者因为它更容易观察与验证但这并不意味着它在所有训练场景中都最优。这一节的关键不是记住切片细节而是理解语言模型训练样本本质上来自同一条长序列的局部对齐切片。输入与目标不是两份不同数据而是同一份数据在时间上错开一个位置的两种视图。使用 DataLoader 生成训练批次有了输入—目标样本对之后下一步并不是马上进入模型而是要把这些样本组织成适合批量计算的形式。DataLoader 的作用正是在数据集和训练循环之间建立这一层稳定接口。从概念上说DataLoader 解决的是三个问题。第一如何把样本按批次输出第二是否要在每个 epoch 开始时打乱样本顺序第三是否要丢弃最后一个不足整批的批次。对于当前这一章而言这些问题的重要性并不在于 “调到哪组参数最好”而在于让读者看到训练循环真正接收的数据已经不再是一条一维 token 序列而是两个形状一致的二维张量。也就是说在这一阶段输入和目标通常都会被组织成如下形状[batch_size, context_length]其中每一行是一个独立样本每一列对应窗口中的一个位置。到这里为止模型接收的仍然只是整数索引而不是向量。从实战角度看这一层还承担一个常被忽视的职责提前暴露输入异常。如果文本太短以至于无法构造任何训练样本那么最合理的行为不是让训练阶段静默失败而是在 DataLoader 构造阶段明确报错。这种处理方式会显著提升代码的可调试性也更符合工程实现的边界控制原则。如果沿用当前代码中的默认设置即 batch_size 8、context_length 4那么从 DataLoader 取出的一个批次其输入与目标张量形状都会是torch.Size([8, 4])这意味着一个批次包含 8 个样本每个样本由长度为 4 的连续 token 序列构成。这里得到的是批量化的离散索引表示。它已经适合送入后续流水线但还不是模型最终接收的连续向量输入。从 token ids 到输入嵌入token ids 仍然只是离散索引。要让模型真正开始计算还必须经过嵌入层把这些整数映射为连续向量。对大语言模型而言这一步至少包括两部分词元嵌入和位置嵌入。词元嵌入回答 “当前 token 是什么”。它本质上是一张可训练的查找表每个 token id 对应词表中的一行向量。位置嵌入回答 “当前 token 位于什么位置”。它同样是一张查找表只不过查找键不再是 token id而是序列中的位置索引。图 3 词元嵌入与位置嵌入的构成关系。 输入的 token ids 先经词元嵌入层映射为词元嵌入张量位置索引经位置嵌入层映射为位置嵌入张量位置嵌入在 batch 维上广播后与词元嵌入逐元素相加得到最终输入嵌入。该输入嵌入同时编码了 “当前 token 是什么” 和 “当前 token 位于什么位置” 两类信息。在当前实现中位置索引始终是从 0 到 context_length - 1 的连续整数序列。它们先被送入位置嵌入层得到一组位置向量随后与词元嵌入逐元素相加。这样做的含义很明确同一个 token 在不同位置上应当拥有不同的最终输入表示因为模型不仅需要知道 “它是什么”还需要知道 “它出现在什么位置”。这里有一个非常重要的形状关系。词元嵌入的形状通常是[batch_size, context_length, embedding_dim]位置嵌入的原始形状通常是[context_length, embedding_dim]在加入 batch 维后它会沿样本维度自动广播从而与整个批次中的词元嵌入相加。最终输入嵌入的形状仍然是[batch_size, context_length, embedding_dim]按照当前代码的默认设置batch_size 8context_length 4embedding_dim 256。因此三个关键张量的典型形状为token_embeddings.shape torch.Size([8, 4, 256]) position_embeddings.shape torch.Size([1, 4, 256]) input_embeddings.shape torch.Size([8, 4, 256])这一步完成之后模型才真正拿到了可以处理的连续向量表示。到这里本章的输入流水线已经完整闭合文本被切分、编码、切片、批量化并最终变成了可送入模型主体的张量。完整代码下面给出本章对应的完整实现代码。正文部分已经说明了各组件在输入流水线中的职责因此这里不再逐行展开而是将代码作为本章主路径的可运行实现统一给出。下面的版本与您当前代码保持相同逻辑仅将少量文档字符串与注释口径统一为本章行文风格。# -*- coding: utf-8 -*- 处理文本数据 功能 ---- 1. 使用 GPT-2 分词器将原始文本转换为 token ids。 2. 使用滑动窗口构造语言模型训练所需的输入-目标序列对。 3. 使用 DataLoader 批量生成训练批次。 4. 演示如何将 token ids 转换为词元嵌入与位置嵌入。 设计说明 -------- - 本文件主要用于教学演示因此优先强调流程清晰与结构简洁。 - 在不大幅调整原有逻辑的前提下补强如下能力 1) 顶层常量统一管理 2) 数据集与演示代码职责分离 3) 参数校验更明确 4) 命名、注释与类型标注更统一 5) 避免在导入模块时直接执行演示逻辑 6) 将嵌入层创建与输入嵌入构造解耦便于教学说明 7) 在关键流程处补充正确、清晰、专业、精炼的代码注释。 Author ------ Aiden Cao zhinengmahuagmail.com Date: 2025-12-29 from __future__ import annotations from pathlib import Path import tiktoken import torch from torch import Tensor, nn from torch.utils.data import DataLoader, Dataset PROJECT_ROOT Path(__file__).resolve().parents[3] DATA_ROOT PROJECT_ROOT / data RAW_DATA_ROOT DATA_ROOT / raw_data # 默认演示文本文件。 DEFAULT_TEXT_FILE_PATH RAW_DATA_ROOT / The-Verdict.txt # 默认分词与嵌入配置。 DEFAULT_ENCODING_NAME gpt2 GPT2_VOCAB_SIZE 50257 DEFAULT_EMBEDDING_DIM 256 DEFAULT_CONTEXT_LENGTH 4 DEFAULT_BATCH_SIZE 8 # nn.Embedding 支持整数索引教学示例中统一接受 int32 / int64。 EMBEDDING_INDEX_DTYPES (torch.int32, torch.int64) def validate_positive_int(value: int, name: str) - None: :param value: 待校验的整数值 :param name: 参数名称 :return: None # bool 是 int 的子类这里显式排除避免 True / False 误通过。 if isinstance(value, bool) or not isinstance(value, int): raise TypeError(f{name} 必须是 int 类型。) if value 0: raise ValueError(f{name} 必须大于 0。) def validate_non_negative_int(value: int, name: str) - None: :param value: 待校验的整数值 :param name: 参数名称 :return: None if isinstance(value, bool) or not isinstance(value, int): raise TypeError(f{name} 必须是 int 类型。) if value 0: raise ValueError(f{name} 不能小于 0。) def validate_non_empty_string(value: str, name: str) - None: :param value: 待校验的字符串 :param name: 参数名称 :return: None if not isinstance(value, str): raise TypeError(f{name} 必须是 str 类型。) if not value.strip(): raise ValueError(f{name} 不能为空字符串。) def validate_token_id_matrix(input_token_ids: Tensor, name: str input_token_ids) - None: :param input_token_ids: 待校验的 token id 张量 :param name: 参数名称 :return: None if input_token_ids.ndim ! 2: raise ValueError(f{name} 必须是二维张量形状应为 [batch_size, context_length]。) # Embedding 查表要求输入为整数索引。 if input_token_ids.dtype not in EMBEDDING_INDEX_DTYPES: raise TypeError(f{name} 必须是整型张量推荐使用 torch.long。) def get_tokenizer(encoding_name: str DEFAULT_ENCODING_NAME) - tiktoken.Encoding: :param encoding_name: tiktoken 编码器名称 :return: 对应的分词器对象 validate_non_empty_string(encoding_name, encoding_name) return tiktoken.get_encoding(encoding_name) def read_text_file(file_path: str | Path) - str: :param file_path: 文本文件路径 :return: 读取到的完整文本内容 path Path(file_path) if not path.exists(): raise FileNotFoundError(f文件不存在{path}) if not path.is_file(): raise FileNotFoundError(f路径不是有效文件{path}) return path.read_text(encodingutf-8) def encode_text_to_token_ids(text: str, tokenizer: tiktoken.Encoding) - Tensor: :param text: 原始文本 :param tokenizer: 用于分词的编码器 :return: torch.long 类型的 token id 张量 validate_non_empty_string(text, text) # 分词器先输出 Python 整数列表再显式转换为 PyTorch 长整型张量 # 以便后续直接送入 nn.Embedding 做索引查表。 token_ids tokenizer.encode(text) return torch.tensor(token_ids, dtypetorch.long) class GPTSlidingWindowDataset(Dataset): 基于滑动窗口的 GPT 训练数据集。 功能说明 -------- 给定一段完整文本先使用分词器将其转换为 token ids 然后通过滑动窗口构造 - input_ids: 当前窗口中的 token 序列 - target_ids: 相对 input_ids 向右偏移一位的目标序列 例如 input_ids [t1, t2, t3, t4] target_ids [t2, t3, t4, t5] def __init__( self, text: str, tokenizer: tiktoken.Encoding, max_length: int, stride: int, ) - None: :param text: 原始文本 :param tokenizer: 用于分词的编码器 :param max_length: 每个样本窗口的最大长度 :param stride: 滑动窗口步长 :return: None validate_positive_int(max_length, max_length) validate_positive_int(stride, stride) self.max_length max_length self.stride stride # 整段文本只分词一次后续样本在 __getitem__ 中按窗口延迟切片 # 可避免预先复制大量重叠子序列。 self.token_ids encode_text_to_token_ids(texttext, tokenizertokenizer) # 每个起始位置对应一个训练样本窗口。 self.sample_start_positions self._build_sample_start_positions( token_countint(self.token_ids.numel()), max_lengthmax_length, stridestride, ) staticmethod def _build_sample_start_positions( token_count: int, max_length: int, stride: int, ) - list[int]: :param token_count: 文本分词后的 token 总数 :param max_length: 每个样本窗口的最大长度 :param stride: 滑动窗口步长 :return: 所有样本窗口起始位置列表 # 构造一组 input_ids 和对应的右移 target_ids # 至少需要 max_length 1 个 token。 if token_count max_length: return [] # range 的右边界是排他的这里刚好保证 target_ids 的最后一个位置仍然有效。 last_start_exclusive token_count - max_length return list(range(0, last_start_exclusive, stride)) def __len__(self) - int: :return: 数据集样本数量 return len(self.sample_start_positions) def __getitem__(self, index: int) - tuple[Tensor, Tensor]: :param index: 样本索引 :return: 指定位置的 input_ids 与 target_ids start self.sample_start_positions[index] end start self.max_length # 输入序列使用当前窗口目标序列整体右移一位 # 对应“给定当前 token 预测下一个 token”的训练目标。 input_ids self.token_ids[start:end] target_ids self.token_ids[start 1:end 1] return input_ids, target_ids # 兼容旧名称尽量减少外部调用修改成本。 GPTDataset GPTSlidingWindowDataset def create_dataloader( text: str, batch_size: int 4, max_length: int 256, stride: int 128, shuffle: bool True, drop_last: bool True, num_workers: int 0, encoding_name: str DEFAULT_ENCODING_NAME, tokenizer: tiktoken.Encoding | None None, ) - DataLoader: 用于批量生成输入-目标对的数据加载器。 :param text: 原始文本 :param batch_size: 批次大小 :param max_length: 每个样本窗口的最大长度 :param stride: 滑动窗口步长 :param shuffle: 是否在每个 epoch 开始时打乱样本顺序 :param drop_last: 是否丢弃最后一个不足 batch_size 的批次 :param num_workers: DataLoader 使用的工作进程数 :param encoding_name: tiktoken 编码器名称 :param tokenizer: 可选的外部分词器为空时内部自动创建 :return: 构造完成的 DataLoader validate_non_empty_string(text, text) validate_positive_int(batch_size, batch_size) validate_positive_int(max_length, max_length) validate_positive_int(stride, stride) validate_non_negative_int(num_workers, num_workers) # 优先复用外部分词器便于在教学或实验中保持编码器一致。 active_tokenizer tokenizer if tokenizer is not None else get_tokenizer(encoding_name) dataset GPTSlidingWindowDataset( texttext, tokenizeractive_tokenizer, max_lengthmax_length, stridestride, ) if len(dataset) 0: raise ValueError(无法构造任何训练样本。请检查文本长度并确保 token 数至少大于 max_length。) return DataLoader( dataset, batch_sizebatch_size, shuffleshuffle, drop_lastdrop_last, num_workersnum_workers, ) def create_embedding_layers( vocab_size: int, embedding_dim: int, context_length: int, device: torch.device | None None, ) - tuple[nn.Embedding, nn.Embedding]: 创建词元嵌入层与位置嵌入层。 :param vocab_size: 词表大小 :param embedding_dim: 嵌入维度 :param context_length: 上下文长度 :param device: 嵌入层所在设备 :return: 词元嵌入层与位置嵌入层 validate_positive_int(vocab_size, vocab_size) validate_positive_int(embedding_dim, embedding_dim) validate_positive_int(context_length, context_length) # 词元嵌入层负责“token id - 词向量” # 位置嵌入层负责“位置索引 - 位置向量”。 token_embedding_layer nn.Embedding(vocab_size, embedding_dim) position_embedding_layer nn.Embedding(context_length, embedding_dim) if device is not None: token_embedding_layer token_embedding_layer.to(device) position_embedding_layer position_embedding_layer.to(device) return token_embedding_layer, position_embedding_layer def build_input_embeddings( input_token_ids: Tensor, token_embedding_layer: nn.Embedding, position_embedding_layer: nn.Embedding, ) - tuple[Tensor, Tensor, Tensor]: 根据输入 token ids 和嵌入层构造词元嵌入、位置嵌入和最终输入嵌入。 :param input_token_ids: 输入 token ids形状为 [batch_size, context_length] :param token_embedding_layer: 词元嵌入层 :param position_embedding_layer: 位置嵌入层 :return: token_embeddings、position_embeddings、input_embeddings validate_token_id_matrix(input_token_ids, input_token_ids) embedding_device token_embedding_layer.weight.device if position_embedding_layer.weight.device ! embedding_device: raise ValueError(token_embedding_layer 与 position_embedding_layer 必须位于同一设备。) # 将输入索引移动到嵌入层所在设备避免设备不一致导致运行错误。 input_token_ids input_token_ids.to(embedding_device) context_length int(input_token_ids.size(1)) if position_embedding_layer.num_embeddings context_length: raise ValueError(位置嵌入层的 num_embeddings 小于当前输入的 context_length无法完成位置编码。) # 位置索引始终是 [0, 1, ..., context_length - 1]。 position_indices torch.arange( context_length, dtypetorch.long, deviceembedding_device, ) # token_embeddings 形状: [batch_size, context_length, embedding_dim] token_embeddings token_embedding_layer(input_token_ids) # position_embeddings 原始形状: [context_length, embedding_dim] # unsqueeze(0) 后变为 [1, context_length, embedding_dim] # 之后与 token_embeddings 相加时会沿 batch 维自动广播。 position_embeddings position_embedding_layer(position_indices).unsqueeze(0) # 最终输入嵌入 词元嵌入 位置嵌入 input_embeddings token_embeddings position_embeddings return token_embeddings, position_embeddings, input_embeddings def create_input_embeddings( input_token_ids: Tensor, vocab_size: int, embedding_dim: int, ) - tuple[Tensor, Tensor, Tensor]: 兼容式封装根据输入 token ids 直接创建嵌入层并生成输入嵌入。 :param input_token_ids: 输入 token ids形状为 [batch_size, context_length] :param vocab_size: 词表大小 :param embedding_dim: 嵌入维度 :return: token_embeddings、position_embeddings、input_embeddings validate_token_id_matrix(input_token_ids, input_token_ids) context_length int(input_token_ids.size(1)) device input_token_ids.device token_embedding_layer, position_embedding_layer create_embedding_layers( vocab_sizevocab_size, embedding_dimembedding_dim, context_lengthcontext_length, devicedevice, ) return build_input_embeddings( input_token_idsinput_token_ids, token_embedding_layertoken_embedding_layer, position_embedding_layerposition_embedding_layer, ) def main() - None: :return: None # 1. 读取原始文本。 raw_text read_text_file(DEFAULT_TEXT_FILE_PATH) # 2. 构造 DataLoader。 # 此处将 stride 设置为 context_length便于演示“非重叠窗口”的基本效果。 # 同时关闭 shuffle便于直接观察窗口切分后的顺序结果。 context_length DEFAULT_CONTEXT_LENGTH dataloader create_dataloader( textraw_text, batch_sizeDEFAULT_BATCH_SIZE, max_lengthcontext_length, stridecontext_length, shuffleFalse, drop_lastFalse, ) # 3. 获取一个批次的输入-目标样本。 input_token_ids, target_token_ids next(iter(dataloader)) print(Input token IDs:\n, input_token_ids) print(\nTarget token IDs:\n, target_token_ids) print(\nInputs shape:\n, input_token_ids.shape) print(Targets shape:\n, target_token_ids.shape) # 4. 创建词元嵌入层与位置嵌入层。 token_embedding_layer, position_embedding_layer create_embedding_layers( vocab_sizeGPT2_VOCAB_SIZE, embedding_dimDEFAULT_EMBEDDING_DIM, context_lengthcontext_length, deviceinput_token_ids.device, ) # 5. 将 token ids 转换为词元嵌入、位置嵌入和最终输入嵌入。 token_embeddings, position_embeddings, input_embeddings build_input_embeddings( input_token_idsinput_token_ids, token_embedding_layertoken_embedding_layer, position_embedding_layerposition_embedding_layer, ) print(\nToken embeddings shape:\n, token_embeddings.shape) print(Position embeddings shape:\n, position_embeddings.shape) print(Input embeddings shape:\n, input_embeddings.shape) if __name__ __main__: main()从正文角度看这份代码的价值不在于 “写法新”而在于它把一条输入流水线拆成了几个职责清楚的组件文本读取、分词编码、窗口数据集、批量加载、嵌入层构造与输入嵌入生成。对于一本实战型技术书而言这种拆分比单纯把逻辑堆在一个脚本里更有价值因为它更接近真实工程中可扩展、可调试、可复用的组织方式。小结本章完成了一条完整且可运行的文本输入流水线。原始文本先被读取并编码为 GPT-2 token ids再通过滑动窗口构造为输入—目标样本对随后由 DataLoader 组织成批量张量最后通过词元嵌入与位置嵌入生成模型真正接收的输入嵌入。最后唠两句为什么AI大模型成为越来越多程序员转行就业、升职加薪的首选很简单这些岗位缺人且高薪智联招聘的最新数据给出了最直观的印证2025年2月AI领域求职人数同比增幅突破200% 远超其他行业平均水平整个人工智能行业的求职增速达到33.4%位居各行业榜首其中人工智能工程师岗位的求职热度更是飙升69.6%。AI产业的快速扩张也让人才供需矛盾愈发突出。麦肯锡报告明确预测到2030年中国AI专业人才需求将达600万人人才缺口可能高达400万人这一缺口不仅存在于核心技术领域更蔓延至产业应用的各个环节。那0基础普通人如何学习大模型 深耕科技一线十二载亲历技术浪潮变迁。我见证那些率先拥抱AI的同行如何建立起效率与薪资的代际优势。如今我将积累的大模型面试真题、独家资料、技术报告与实战路线系统整理分享于此为你扫清学习困惑共赴AI时代新程。我整理出这套 AI 大模型突围资料包【允许白嫖】✅从入门到精通的全套视频教程✅AI大模型学习路线图0基础到项目实战仅需90天✅大模型书籍与技术文档PDF✅各大厂大模型面试题目详解✅640套AI大模型报告合集✅大模型入门实战训练这份完整版的大模型 AI 学习和面试资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】①从入门到精通的全套视频教程包含提示词工程、RAG、Agent等技术点② AI大模型学习路线图0基础到项目实战仅需90天全过程AI大模型学习路线③学习电子书籍和技术文档市面上的大模型书籍确实太多了这些是我精选出来的④各大厂大模型面试题目详解⑤640套AI大模型报告合集⑥大模型入门实战训练如果说你是以下人群中的其中一类都可以来智泊AI学习人工智能找到高薪工作一次小小的“投资”换来的是终身受益应届毕业生‌无工作经验但想要系统学习AI大模型技术期待通过实战项目掌握核心技术。零基础转型‌非技术背景但关注AI应用场景计划通过低代码工具实现“AI行业”跨界‌。业务赋能 ‌突破瓶颈传统开发者Java/前端等学习Transformer架构与LangChain框架向AI全栈工程师转型‌。获取方式有需要的小伙伴可以保存图片到wx扫描二v码免费领取【保证100%免费】