【CS336】lecture1 tokenization

1. 定义

语言模型处理的输入是字符串,但内部是对token预测概率,因此需要字符串到token的转换规则。也就是需要定义

  • encode:字符串转化到token
  • decode:token转化到字符

定义压缩率为每个 token 对应的 UTF-8 byte 数:

def get_compression_ratio(string: str, indices: list[int]) -> float:
    num_bytes = len(string.encode("utf-8"))
    num_tokens = len(indices)
    return num_bytes / num_tokens

压缩率越高越好,因为压缩率高能把相同一段字符串输入,变成更短的token序列,降低模型训练推理的计算开销。然而压缩率提升往往需要更大的词典,比如把一个多个词组成的短语也看成一个token,而不是每个词一个token,压缩率自然提高了,但代价是词典里必须保存这个短语,不能只保存单个词汇。而词典增大会导致稀疏(很多token根本用不上)的问题,因此tokenizer设计,需要在词典大小和压缩率之间取得平衡。

2. Character tokenize

每个字符视为一个token,这实现简单,只要找一个字符集就行。缺点是字典很大,例如unicode就有15万个字符,很多稀有字符基本不会用上。并且压缩率也不会太高,unicode设计时大部分字符的字节数都很小。

class CharacterTokenizer(Tokenizer):
    def encode(self, string: str) -> list[int]:
        return list(map(ord, string))

    def decode(self, indices: list[int]) -> str:
        return "".join(map(chr, indices))

assert ord("a") == 97
assert chr(97) == "a"

3. Byte tokenizer

按字节分词,也就是把输入数据重新解释为单字节类型,这样字典大小只有256,因为一个字节8位只有28=2562^8=25628=256种表示。优点是字典很小,但压缩率很低,恒定为1 byte/token,生成的token序列很长,计算代价很大。尤其是注意力机制的复杂度是O(L2)O(L^2)O(L2),和token序列长度二次方成正比

class ByteTokenizer(Tokenizer):
    def encode(self, string: str) -> list[int]:
        return list(map(int, string.encode("utf-8")))

    def decode(self, indices: list[int]) -> str:
        return bytes(indices).decode("utf-8")

assert "🌍".encode("utf-8") == b"\xf0\x9f\x8c\x8d"

4. Word tokenizer

按真实的人类字典进行分词,这是LLM前传统NLP的做法,压缩率还可以,且本身就包含了人类的语义内容。缺点是词典还是可能太大,并且会有很多稀疏token词典里有,但输入文本里几乎不会出现。且固定字典,如果遇到没见过的新词,只能映射到UNK(未知token),相当于一句话里一堆星号,会影响loss的计算,影响模型学习。

import regex

string = "I'll say supercalifragilisticexpialidocious!"
chunks = regex.findall(r"\w+|.", string)

5. Byte-Pair Encoding(BPE)

目前LLM最常用的分词算法。BPE 最初由 Philip Gage 在 1994 年用于数据压缩,后来被用于 neural machine translation,并被 GPT-2 采用。

核心思想是,在当前文本上训练分词器,得到词典,而不是基于某个确定规则。让分词器根据数据中词汇出现的频率决定如何压缩,出现比较多的序列用一个token表示,出现较少的序列仍用多个token表示。

名称带Byte是因为最开始按字节分割输入,也就是把输入看成一个字节序列,然后在此基础上合并

  • 统计所有相邻token pair的出现次数,在第一轮,这就是字节形成的pair,因此称为Byte-pair
  • 找到出现最多的token pair,将其所有出现都合并
  • 将这次合并结果作为一个新的token存入词典。

重复以上过程直到进行了一定轮数,或者说词典大小达到目标。可以看到这是经典的机器学习思想,不人为规定规则,而是让模型根据数据驱动自学规则,这样每个训练数据,对应的分词器都不一样,都是最适合当前数据的。

下面是一个朴素实现,首先来看token pair计数和合并

计数时,把原始序列和左移一位的序列放一起打包成pair,这样每次取出的都是一个长度为2的子数组,且不用显式处理下标内容。用一个哈希表对pair进行计数

合并时,采用追加式的合并,遍历整个序列,对于目标pair的位置合成一个token插入序列末尾,对于非目标不进行操作,逐个插入。

from collections import defaultdict

def count_adjacent_pairs(indices: list[int]) -> dict[tuple[int, int], int]:
    counts = defaultdict(int)
    for index1, index2 in zip(indices, indices[1:]):
        counts[(index1, index2)] += 1
    return counts

def merge(
    indices: list[int],
    pair: tuple[int, int],
    new_index: int,
) -> list[int]:
    new_indices = []
    i = 0
    while i < len(indices):
        if (
            i + 1 < len(indices)
            and indices[i] == pair[0]
            and indices[i + 1] == pair[1]
        ):
            new_indices.append(new_index)
            i += 2
        else:
            new_indices.append(indices[i])
            i += 1
    return new_indices

训练时,初始化按字节分割,0-255作为初始词典中的token,运行指定轮数,每轮先统计所有相邻对的出现次数,然后取出出现最多的pair传给merge函数,并把这个pair合并在合并记录merges和词典vocab中记录。

from dataclasses import dataclass

@dataclass(frozen=True)
class BPETokenizerParams:
    vocab: dict[int, bytes]                 # token index -> bytes
    merges: dict[tuple[int, int], int]      # pair -> new token index

def train_bpe(string: str, num_merges: int) -> BPETokenizerParams:
    indices = list(map(int, string.encode("utf-8")))
    merges = {}
    vocab = {x: bytes([x]) for x in range(256)}

    for i in range(num_merges):
        counts = count_adjacent_pairs(indices)
        pair = max(counts, key=counts.get)
        new_index = 256 + i
        merges[pair] = new_index
        vocab[new_index] = vocab[pair[0]] + vocab[pair[1]]
        indices = merge(indices, pair, new_index)

    return BPETokenizerParams(vocab=vocab, merges=merges)

利用训练好的分词器进行encode,decode。encode时遍历整个词典,对每个词分别进行合并,这显然是低效的实现,这里只是为了演示,后面会在assignment 1里实现一个更高效的BPE。

class BPETokenizer(Tokenizer):
    def __init__(self, params: BPETokenizerParams):
        self.params = params

    def encode(self, string: str) -> list[int]:
        indices = list(map(int, string.encode("utf-8")))
        for pair, new_index in self.params.merges.items():
            indices = merge(indices, pair, new_index)
        return indices

    def decode(self, indices: list[int]) -> str:
        pieces = map(self.params.vocab.get, indices)
        return b"".join(pieces).decode("utf-8")

params = train_bpe("the cat in the hat", num_merges=3)
tokenizer = BPETokenizer(params)
string = "the quick brown fox"
assert tokenizer.decode(tokenizer.encode(string)) == string
内容概要:本文针对功率阶跃工况下光储虚拟同步发电机(VSG)并网系统存在的电能质量问题,提出一种自适应治理策略。通过Simulink搭建完整的光储VSG并网系统仿真模型,重点研究在功率突变条件下系统出现的频率波动、电压畸变及谐波增加等动态问题。所提策略结合VSG的惯量与阻尼特性,设计了参数自适应调节机制,实现对系统动态响应过程的有效调控,显著提升了并网电流波形质量与频率稳定性,增强了系统在强扰动工况下的鲁棒性与电能质量治理能力。; 适合人群:具备电力电子、自动控制理论及新能源并网技术基础的科研人员、电气工程及相关专业的硕士/博士研究生,以及从事微电网、储能系统、VSG控制研发的工程技术人员。; 使用场景及目标:①用于分析光储系统在功率阶跃等动态工况下的并网特性与电能质量演变规律;②为VSG关键控制参数(如惯量、阻尼)的自适应整定提供设计思路与仿真验证平台;③支撑高水平学术论文复现、科研课题攻关及实际工程项目的前期控制策略验证。; 阅读建议:建议结合Simulink仿真模型深入理解控制策略的实现逻辑,重点关注VSG惯量阻尼协同调节的自适应机制设计,可通过设置不同功率阶跃幅度、光照强度变化及负载投切等多场景进行仿真测试,全面评估策略的动态性能与适用边界。
内容概要:本文系统研究了基于ARIMA-CNN-LSTM的混合时间序列预测模型,并提供了完整的Python代码实现。该模型融合传统统计方法ARIMA与深度学习模型CNN和LSTM的优势,充分发挥ARIMA在捕捉线性趋势与季节性特征方面的能力,以及CNN和LSTM在提取局部特征和建模长期依赖关系方面的强大性能,从而有效提升对复杂非线性时间序列的预测精度。研究涵盖了数据预处理、模型构建、参数调优、训练与预测全流程,并通过实际案例验证模型在风电、光伏、负荷、电价等能源领域预测任务中的优越表现。文档还整合了电力系统、路径规划、信号处理等多个科研方向的仿真资源与代码示例,凸显其在学术研究与工程实践中的广泛应用价值。; 适合人群:具备一定Python编程基础和机器学习理论知识,从事科研或工程技术工作的研究人员,特别适用于关注时间序列分析、深度学习建模的硕士生、博士生及行业从业者。; 使用场景及目标:①深入理解并掌握ARIMA与深度学习模型(如CNN、LSTM)融合的建模范式与技术细节;②应用于能源领域中风电出力、光伏发电、电力负荷及电价等关键变量的高精度预测;③为科研课题、学术论文复现、工程项目优化等提供可靠、可复用的代码框架与技术支持。; 阅读建议:建议结合所提供的Python代码进行动手实践,重点剖析数据预处理流程、混合模型架构设计及超参数调优策略,同时可参考文档中其他相关研究案例以拓宽技术视野和应用场景认知。
内容概要:本文围绕谐振式双有源桥(DAB)变换器,深入研究了一种无需电流传感器的模型预测控制(MPC)方法,并基于Simulink平台完成了完整的仿真实现。该方法通过构建精确的系统数学模型,替代传统依赖物理电流传感器的状态观测方式,有效降低了系统硬件成本与布线复杂度,同时提升了系统的可靠性和集成度。研究重点涵盖系统建模、预测模型构建、目标函数设计、约束条件处理以及控制律求解等关键环节,充分体现了模型预测控制在电力电子高频软开关变换器中的高动态响应、高稳态精度与强鲁棒性的优势。文中通过仿真验证了该无传感MPC策略在不同工况下的控制性能,展示了其在抑制电流冲击、提升能量转换效率及增强系统稳定性方面的良好表现。; 适合人群:电力电子、自动化、电气工程及其相关专业的硕士研究生、博士生、高校科研人员,以及从事DAB变换器拓扑设计、先进数字控制算法开发与新能源电能变换系统研发的工程技术人员。; 使用场景及目标:① 掌握谐振式DAB变换器的工作原理及其无传感控制的设计思路与实现路径;② 深入理解模型预测控制在电力电子系统中的建模方法、优化求解过程与仿真验证流程;③ 为开展无传感器驱动、高频隔离DC-DC变换、数字电源控制等前沿课题提供可复现的技术方案与仿真基准平台。; 阅读建议:建议结合提供的Simulink仿真模型同步学习,重点关注状态观测器设计与预测模型的耦合机制,动手调整系统参数与控制权重以观察动态响应变化,深化对MPC控制机理的理解,并可进一步拓展至其他软开关拓扑或融合人工智能优化算法进行性能提升研究。
内容概要:本文详细介绍了基于非线性反步法与Lyapunov-MPC模型预测控制相结合的自主水下航行器(AUV)轨迹跟踪控制方法,并完整实现了包含经典Fossen动力学模型的Matlab仿真代码,复现了高水平期刊研究成果。该方法通过反步法逐级构造控制律,结合Lyapunov函数确保系统全局渐近稳定性,同时引入模型预测控制(MPC)优化未来动态行为,有效提升控制器在复杂海洋环境下的轨迹跟踪精度、抗干扰能力与系统鲁棒性。整个设计充分体现了非线性控制理论与现代优化控制策略的深度融合。; 适合人群:具备自动控制原理、非线性系统分析、现代控制理论基础,熟悉Matlab/Simulink仿真工具,且从事水下机器人、无人航行器、航空航天、智能控制等方向研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于自主水下航行器、无人机等强非线性系统的高精度轨迹跟踪控制器设计与仿真验证;②支撑高水平学术论文复现、科研课题攻关或实际工程原型开发,掌握先进复合控制策略的设计流程与实现技巧;③深入理解反步法、Lyapunov稳定性理论与模型预测控制三者协同设计的理论依据与工程应用价值。; 阅读建议:建议读者结合Fossen动力学建模理论,逐步调试Matlab代码,重点关注控制器设计步骤、Lyapunov函数构造过程及MPC滚动优化实现细节,配合相关文献进行对比分析,以深化对非线性控制系统设计与仿真验证全过程的理解。
内容概要:本文档聚焦于采用BPSK和GMSK调制方式的Turbo码通信系统的研究与Matlab仿真实现,系统探讨了MSK/GMSK调制解调的基本原理、Turbo码的编码与译码机制以及二比特差分解调算法的具体实现。通过构建完整的通信链路模型,文档详细展示了调制、信道传输、解调及Turbo译码等关键环节的仿真流程,并对系统性能进行了全面评估,特别是误码率(BER)随信噪比(SNR)变化的曲线分析,为理解和优化现代数字通信系统提供了坚实的理论基础和实践指导。此外,文档还强调了GMSK作为一种连续相位调制技术,在提高频谱效率方面的优势,以及Turbo码作为强大前向纠错码在提升通信可靠性方面的重要作用。; 适合人群:具备通信原理基础知识和Matlab编程能力的高校学生、研究生及从事无线通信系统研发的工程技术人员,尤其适合正在进行相关课题研究或毕业设计的人员。; 使用场景及目标:① 学习并掌握GMSK与BPSK调制技术在Turbo码系统中的应用;② 实现并仿真Turbo码与不同调制方式结合的通信链路,分析其抗干扰与误码性能;③ 为通信系统仿真课程设计、科研项目或学术论文提供可复现的Matlab代码参考。; 阅读建议:建议读者结合通信原理教材,先理解Turbo码的编译码机制与GMSK调制特性,再对照Matlab代码逐步调试运行,重点关注调制解调模块与译码器的接口设计及仿真结果的分析方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值