Stanford CS336 | Assignment 1 - BPE Tokenizer Training 实现

斯坦福 CS336 动手大语言模型 Assignment1 BPE Tokenizer & TransformerLM 所有代码更新至 https://github.com/WangYuHang-cmd/CS336/tree/main/assignment1-basics作业文件结构:├── tests/ # 测试文件目录│ ├── adapters.py # 适配器测试│ ├── conftest.py # pytest配置│ ├──.py # 包初始化。 阅读详情
AI摘要

本文详细记录了在Stanford CS336第一次作业中如何从头实现一个字节级BPE分词器的训练过程。内容涵盖初始化词表、并行预分词策略、BPE合并训练的优化方法,以及特殊符号的处理。通过TinyStories数据集验证了实现的正确性,并通过测试用例验证了功能完整性。

所有关于 assignment1 的代码已开源在:
https://github.com/ACEEE-1222/Standford-CS336-Assignment-1
如果对你有帮助的话,记得顺手点个star喔!

本文记录了我在 Stanford CS336 第一次作业中完成的一项任务:从头实现一个 字节级 BPE(Byte Pair Encoding)分词器训练器,并支持高效的并行预分词与合并操作优化。本文将介绍 BPE 分词器的实现细节、并行预处理策略、特殊符号的处理方式,以及对 merge 步骤的优化。

一、作业要求概述

本次任务目标是在 TinyStories 数据集上训练一个字节级 BPE 分词器。如图所示,需要完成函数train_bpe。
在这里插入图片描述
核心要求如下:

  1. 初始化:以所有可能的字节(0-255)作为初始词汇表;
  2. 预处理:将文本分割为初始令牌(字节序列);
  3. 合并:统计所有字节对的频率,合并最频繁的对并更新词汇表,重复此过程直至达到目标词汇量。

二、整体实现结构

整个分词器训练流程主要分为以下三步:

1. 初始化词表

  • 首先将所有 ASCII 字节值(0~255)加入初始词表。
  • 然后添加用户指定的 special_tokens,如 <|endoftext|>、<pad> 等。
# 1. Vocabulary Initialization
vocab = {i: bytes([i]) for i in range(256)}
for tok in special_tokens:
    vocab[len(vocab)] = tok.encode("utf-8")

2. 并行预分词(Pre-tokenization)

(1)文档边界切分

为了并行处理,我们需要将文件划分为若干个 chunk,每个 chunk 的起始位置应当正好落在一个 <|endoftext|> 标记上,确保不会跨文档切分。我们使用 find_chunk_boundaries 函数来实现这一功能:

boundaries = find_chunk_boundaries(f, num_processes, b"<|endoftext|>")

该函数会从初始估计位置开始,向前读取直到找到一个 <|endoftext|> 再作为真正边界。

(2)分发任务到子进程

我们利用 Python 的 multiprocessing.Pool,为每个 chunk 分发 process_chunk 任务:

task_args = [(input_path, start, end, special_tokens) for start, end in zip(boundaries[:-1], boundaries[1:])]
with Pool(processes=num_processes) as pool:
    chunk_results = pool.map(process_chunk, task_args)

每个 chunk 的处理逻辑包括:

  • 用正则表达式 re.split 去除所有特殊 token,并按文档分别处理;
  • 使用 GPT-2 的正则表达式模式进行预分词;
  • 每个 token 转为字节流,再按字节分割成 [b1, b2, ...] 的形式。

3. BPE 合并训练

训练阶段按照以下流程进行:

  • 初始化 counts 和 pair_to_indices,分别用于记录 pair 频率和其在哪些 token 中出现。
  • 不断选择出现频率最高的 pair (a, b) 进行合并,生成新 token。
  • 将受影响的所有 token 中的旧 pair 信息删除,并插入新 pair。
  • 直到词表大小达到目标 vocab_size。

优化关键点在于:

  • 只更新受到影响的 pair 计数,而不是每次全局重新统计;
  • 避免在迭代过程中修改原集合,采用 .copy() 确保稳定性。

示例代码片段(合并过程):

需要注意的是:在 affected_indices 用 pair_to_indices[max_pair].copy() 创建避免集合被动态修改。以及我一开始将 for j in affected_indices 写成三次遍历,导致pair_to_indices[max_pair]中间状态交叉污染,一次遍历就不会存在这样的问题(可以想想为什么)。

# 3. Compute BPE merges
    merges : list[tuple[bytes, bytes]] = []
    pre_tokens_bytes: list[list[bytes]] = [token for chunk in chunk_results for token in chunk]
    counts = defaultdict(int)
    pair_to_indices = defaultdict(set)
    for idx, token in enumerate(pre_tokens_bytes):
        for i in range(len(token) - 1):
            pair = (token[i], token[i + 1])
            counts[pair] += 1
            pair_to_indices[pair].add(idx)

    idx = len(vocab)
    while idx < vocab_size:
        if not counts:
            break
            
        max_pair: tuple[bytes, bytes] = None
        max_cnt= -1
        for pair, cnt in counts.items():
            if cnt > max_cnt:
                max_pair = pair
                max_cnt = cnt
            elif cnt == max_cnt:
                if max_pair is None or pair > max_pair:
                    max_pair = pair

        merges.append(max_pair)
        a, b = max_pair
        new_token = a + b
        vocab[idx] = new_token
        idx += 1

        affected_indices = pair_to_indices[max_pair].copy()
        for j in affected_indices:
            token = pre_tokens_bytes[j]
            for i in range(len(token) - 1):
                old_pair = (token[i], token[i+1])
                pair_to_indices[old_pair].discard(j)
                counts[old_pair] -= 1
                if counts[old_pair] == 0:
                    counts.pop(old_pair)
                    pair_to_indices.pop(old_pair, None)

            merged = []
            i = 0
            while i < len(token):
                if i < len(token) - 1 and token[i] == a and token[i+1]==b:
                    merged.append(new_token)
                    i += 2
                else:
                    merged.append(token[i])
                    i += 1
            pre_tokens_bytes[j]=merged

            token = pre_tokens_bytes[j]
            for i in range(len(token) - 1):
                pair = (token[i], token[i + 1])
                counts[pair] += 1
                pair_to_indices[pair].add(j)

三、关键模块代码解释

1. find_chunk_boundaries

用于在特殊 token 边界上切割数据块,避免语义跨越:

def find_chunk_boundaries(
    file: BinaryIO, 
    desired_num_chunks: int, 
    split_special_token: bytes
) -> list[int]:
    """
    Chunk the file into parts that can be counted independently.
    May return fewer chunks if the boundaries end up overlapping.
    """
    assert isinstance(split_special_token, bytes), (
        "Must represent special token as a bytestring"
    )

    # Get total file size in bytes
    file.seek(0, os.SEEK_END)
    file_size = file.tell()
    file.seek(0)

    chunk_size = file_size // desired_num_chunks

    # Initial guesses for chunk boundary locations, uniformly spaced
    # Chunks start on previous index, don't include last index
    chunk_boundaries = [i * chunk_size for i in range(desired_num_chunks + 1)]
    chunk_boundaries[-1] = file_size

    mini_chunk_size = 4096  # Read ahead by 4k bytes at a time

    for bi in range(1, len(chunk_boundaries) - 1):
        initial_position = chunk_boundaries[bi]
        file.seek(initial_position)  # Start at boundary guess
        while True:
            mini_chunk = file.read(mini_chunk_size)  # Read a mini chunk

            # If EOF, this boundary should be at the end of the file
            if mini_chunk == b"":
                chunk_boundaries[bi] = file_size
                break

            # Find the special token in the mini chunk
            found_at = mini_chunk.find(split_special_token)
            if found_at != -1:
                true_position = initial_position + found_at
                chunk_boundaries[bi] = true_position
                break

            initial_position += mini_chunk_size

    # Make sure all boundaries are unique, but might be fewer than desired_num_chunks
    return sorted(set(chunk_boundaries))

2. process_chunk

完成:

  • 特殊 token 切割;
  • 分词(正则);
  • 字节分解为 [b1, b2, ...]:
def process_chunk(args: tuple[str, int, int, list[str]]) -> list[list[bytes]]:
    input_path, start, end, special_tokens = args
    """
    Processes a chunk of the input file and returns byte pair frequency counts.

    Args:
        input_path (str): The path of input file.
        start (int): Start byte offset of the chunk.
        end (int): End byte offset of the chunk.
        special_tokens (list[str]): List of special tokens that should not be merged across.

    Returns:
        pre_token_bytes (list[list[bytes]]): list of tokens, where each token is a list of bytes
    """

    with open(input_path, "rb") as file:
        file.seek(start)
        chunk = file.read(end - start).decode("utf-8", errors="ignore")

    # 1. Remove special tokens by splitting the chunk at those tokens
    pattern = "|".join(re.escape(tok) for tok in special_tokens)
    documents = re.split(pattern, chunk)

    # 2. Pre-tokenize and count byte pair frequencies
    pre_tokens_bytes: list[list[bytes]] = []
    PAT = r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""
    for doc in documents:
        tokens = [match.group(0).encode("utf-8") for match in re.finditer(PAT, doc)]
        for token in tokens:
            token_bytes = [bytes([b]) for b in token]
            pre_tokens_bytes.append(token_bytes)

    return pre_tokens_bytes

四、最终结果

最终返回两个结构:

vocab: dict[int, bytes]  # token id -> bytes
merges: list[tuple[bytes, bytes]]  # 按顺序记录合并对

在 tests/adapters.py 的 run_train_bpe 中调用函数:

    from cs336_basics.bpe_tokenizer import train_bpe

    return train_bpe(input_path,vocab_size,special_tokens)

五、测试通过情况与调试经验

作业附带三个测试用例,涵盖了基本词表构造、特殊 token 行为,以及合并顺序的正确性。
用 uv run pytest tests/test_train_bpe.py 命令进行测试,结果如下:
在这里插入图片描述

六、完整代码(bpe_tokenizer.py)

import os
import regex as re
from typing import BinaryIO
from multiprocessing import Pool
from collections import defaultdict

def train_bpe(
    input_path: str,
    vocab_size: int,
    special_tokens: list[str],
    num_processes: int = 8
) -> tuple[dict[int, bytes], list[tuple[bytes, bytes]]]:
    """
    Trains a byte-level BPE (Byte Pair Encoding) tokenizer on the given input text file.

    Parameters
    ----------
    input_path : str
        Path to a UTF-8 encoded text file containing training data for the BPE tokenizer.
        Each line is considered part of the corpus.

    vocab_size : int
        The total size of the final vocabulary (must include initial byte-level tokens,
        all merged tokens produced during training, and the given special tokens).

    special_tokens : list[str]
        A list of user-defined special tokens (e.g., ["<|endoftext|>", "<pad>"]) to be 
        added to the vocabulary. These tokens do NOT participate in merge decisions.

    num_processes : int, optional (default=8)
        Number of parallel processes used during pre-tokenization. Each process handles
        a chunk of the input corpus split at special token boundaries. More processes
        generally mean faster pre-tokenization.

    Returns
    -------
    vocab : dict[int, bytes]
        A dictionary mapping token IDs (integers) to token values (in bytes). The token 
        IDs should be assigned sequentially starting from 0.

    merges : list[tuple[bytes, bytes]]
        A list of BPE merge operations, where each tuple represents two byte-level tokens 
        that were merged together. The list should be ordered by merge time (first merge first).
    """

    # 1. Vocabulary Initialization
    vocab = {i: bytes([i]) for i in range(256)}
    for tok in special_tokens:
        vocab[len(vocab)] = tok.encode("utf-8")


    # 2. Pre-tokenization
    with open(input_path, "rb") as f:
        boundaries = find_chunk_boundaries(f, num_processes, "<|endoftext|>".encode("utf-8"))

    task_args = [(input_path, start, end, special_tokens) for start, end in zip(boundaries[:-1], boundaries[1:])]
    with Pool(processes=num_processes) as pool:
        chunk_results = pool.map(process_chunk, task_args)
    
    # 3. Compute BPE merges
    merges : list[tuple[bytes, bytes]] = []
    pre_tokens_bytes: list[list[bytes]] = [token for chunk in chunk_results for token in chunk]
    counts = defaultdict(int)
    pair_to_indices = defaultdict(set)
    for idx, token in enumerate(pre_tokens_bytes):
        for i in range(len(token) - 1):
            pair = (token[i], token[i + 1])
            counts[pair] += 1
            pair_to_indices[pair].add(idx)

    idx = len(vocab)
    while idx < vocab_size:
        if not counts:
            break
            
        max_pair: tuple[bytes, bytes] = None
        max_cnt= -1
        for pair, cnt in counts.items():
            if cnt > max_cnt:
                max_pair = pair
                max_cnt = cnt
            elif cnt == max_cnt:
                if max_pair is None or pair > max_pair:
                    max_pair = pair

        merges.append(max_pair)
        a, b = max_pair
        new_token = a + b
        vocab[idx] = new_token
        idx += 1

        affected_indices = pair_to_indices[max_pair].copy()
        for j in affected_indices:
            token = pre_tokens_bytes[j]
            for i in range(len(token) - 1):
                old_pair = (token[i], token[i+1])
                pair_to_indices[old_pair].discard(j)
                counts[old_pair] -= 1
                if counts[old_pair] == 0:
                    counts.pop(old_pair)
                    pair_to_indices.pop(old_pair, None)

            merged = []
            i = 0
            while i < len(token):
                if i < len(token) - 1 and token[i] == a and token[i+1]==b:
                    merged.append(new_token)
                    i += 2
                else:
                    merged.append(token[i])
                    i += 1
            pre_tokens_bytes[j]=merged

            token = pre_tokens_bytes[j]
            for i in range(len(token) - 1):
                pair = (token[i], token[i + 1])
                counts[pair] += 1
                pair_to_indices[pair].add(j)

    return vocab, merges

def find_chunk_boundaries(
    file: BinaryIO, 
    desired_num_chunks: int, 
    split_special_token: bytes
) -> list[int]:
    """
    Chunk the file into parts that can be counted independently.
    May return fewer chunks if the boundaries end up overlapping.
    """
    assert isinstance(split_special_token, bytes), (
        "Must represent special token as a bytestring"
    )

    # Get total file size in bytes
    file.seek(0, os.SEEK_END)
    file_size = file.tell()
    file.seek(0)

    chunk_size = file_size // desired_num_chunks

    # Initial guesses for chunk boundary locations, uniformly spaced
    # Chunks start on previous index, don't include last index
    chunk_boundaries = [i * chunk_size for i in range(desired_num_chunks + 1)]
    chunk_boundaries[-1] = file_size

    mini_chunk_size = 4096  # Read ahead by 4k bytes at a time

    for bi in range(1, len(chunk_boundaries) - 1):
        initial_position = chunk_boundaries[bi]
        file.seek(initial_position)  # Start at boundary guess
        while True:
            mini_chunk = file.read(mini_chunk_size)  # Read a mini chunk

            # If EOF, this boundary should be at the end of the file
            if mini_chunk == b"":
                chunk_boundaries[bi] = file_size
                break

            # Find the special token in the mini chunk
            found_at = mini_chunk.find(split_special_token)
            if found_at != -1:
                true_position = initial_position + found_at
                chunk_boundaries[bi] = true_position
                break

            initial_position += mini_chunk_size

    # Make sure all boundaries are unique, but might be fewer than desired_num_chunks
    return sorted(set(chunk_boundaries))



def process_chunk(args: tuple[str, int, int, list[str]]) -> list[list[bytes]]:
    input_path, start, end, special_tokens = args
    """
    Processes a chunk of the input file and returns byte pair frequency counts.

    Args:
        input_path (str): The path of input file.
        start (int): Start byte offset of the chunk.
        end (int): End byte offset of the chunk.
        special_tokens (list[str]): List of special tokens that should not be merged across.

    Returns:
        pre_token_bytes (list[list[bytes]]): list of tokens, where each token is a list of bytes
    """

    with open(input_path, "rb") as file:
        file.seek(start)
        chunk = file.read(end - start).decode("utf-8", errors="ignore")

    # 1. Remove special tokens by splitting the chunk at those tokens
    pattern = "|".join(re.escape(tok) for tok in special_tokens)
    documents = re.split(pattern, chunk)

    # 2. Pre-tokenize and count byte pair frequencies
    pre_tokens_bytes: list[list[bytes]] = []
    PAT = r"""'(?:[sdmt]|ll|ve|re)| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"""
    for doc in documents:
        tokens = [match.group(0).encode("utf-8") for match in re.finditer(PAT, doc)]
        for token in tokens:
            token_bytes = [bytes([b]) for b in token]
            pre_tokens_bytes.append(token_bytes)

    return pre_tokens_bytes


如有问题欢迎留言讨论。

cs336系列(1) 第一步先克隆到本地如果你的环境中没有下载uv,先安装一下然后激活下载需要的库和包,如果下载速度慢可以设置镜像源uv run。 阅读详情

相关推荐

CS336-Project:罗格斯CS336数据库项目

CS336-项目 罗格斯CS336数据库项目

Standford CS336 | Assignment 1 - Implementing the tokenizer

本次作业的核心任务是实现一个Tokenizer类,该类基于训练得到的词汇表(vocab)和合并规则(merges),能够将文本编码为整数 ID 序列,以及将整数 ID 序列解码为文本。同时,需要支持用户提供的特殊符号。编码(encode):将输入的文本字符串转换为 token ID 列表。过程需先进行预分词,再应用 BPE 合并规则。解码(decode):将 token ID 列表转换为文本字符串。对于无效的 token ID,需用 Unicode 替换字符(U+FFFD)处理。支持从文件加载:实现。

Bug_makerACE的博客 1191

张正友标定法-MATLAB实验

该方法最早由微软研究院的Zhengyou Zhang教授提出,1998年发表在IEEE TRANSACTIONS ON PATTERNANALYSIS AND MACHINE INTELLIGENCE,论文题目为:AFlexible New Technique for Camera Calibration。 (一)标定平面到图像平面的单应性 单应性(homography):在计算机视觉中被定义

qq_37845624的博客 8289

【免费下载】 Stanford CS336 Assignment 1: 语言模型从零开始的最佳实践

本项目是基于斯坦福大学CS336课程的第一份作业,旨在从零开始实现语言模型。通过该作业,学生可以学习到如何处理自然语言数据、构建基本的语言模型,并对其进行训练和评估。项目采用Python语言开发,依赖uv环境管理,确保了代码的可复现性、可移植性和易用性。 ## 2. 项目快速启动 ### 环境准备 首先,确保你已经安装了Python环境。然后安装uv工具,推荐使用以下命令: ```shel

gitblog_00692的博客 1793

【STM32Cube HAL】输入捕获(六)——脉宽测量

1

HAHAHA 4907

基于PyTorch的BP神经网络PID控制器动态调参实战

本文详细介绍了基于PyTorch的BP神经网络PID控制器动态调参实战,结合BP神经网络的自适应能力和PID控制算法的经典优势,实现工业控制中的高效参数调整。通过PyTorch实现BP-PID融合架构,包括网络设计、损失函数优化及动态调参技巧,显著提升控制性能,适用于温度控制、电机控制等场景。

weixin_29094775的博客 522

Stanford CS336 Language Models from Scratch-Assignment 1 OVerview

前些天计划看Stanford 新开的 CS336,顺便记录下,发到这里,大家一起交流。

nuowenyadelunwen的博客 835

REST-meta-MDD Data总结

sMRI数据使用:nwc1、nwc2、nwc3(同样数据集相关论坛中有相关信息指向需要使用wc1、wc2、wc3)多方咨询与讨论后决定使用nwc数据,这里说明wc数据相对wc数据要大的多如wc1的大小为1.33GB,而nwc1的数据却有6.90GB。VBM文件:/REST-meta-MDD/Sharing/REST-meta-MDD-VBM- phase1 -Sharing.tar.gz。1、Excel中的表型信息:1-男,2-女;-9999或空白表示缺少数据。

2404_88295660的博客 1538

深度解析斯坦福CS336:如何从零开始手搓一个大语言模型

想摆脱 API 调用的黑盒困境?本文深度解析斯坦福 CS336 课程,带你从数据清洗到分布式训练,亲手构建一个属于你的大语言模型。掌握底层架构与系统工程精髓,让你从模型使用者进阶为具备掌控力的创造者!🚀

weixin_44063643的博客 645

Rasa Core开发指南

文章目录1. Rasa Core简介1.1 Rasa Core消息处理流程1.2 安装Rasa Core2. Dialogue模型训练2.1 Story样本数据2.2 Domain2.2.0 intents2.2.1 actions2.2.2 templates2.2.3 entities2.2.4 slots3 .训练和使用对话模型3.1 训练对话模型3.2 使用对话模型4. 搭建CustomA...

老蒋也疯狂 1万+

Qt思岚激光雷达

Qt关于思岚激光雷达研究示例,本示例采用qt界面开发,可以串口也可以tcp进行设备连接。接收数据进行相关的数据进行绘图, 这种雷达可以用于大屏触摸场景,会场互动触摸场景开发。 #ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QTimer> #include <QWidget> #include "lidarmgr.h" namespace Ui { class MainWidget; } class MainWidget.

cqltbe131421的博客 1598

cs336 assignment1 作业环境配置

cs336 assignment1 前期环境配置

m0_46464899的博客 3004

【WorkBuddy从入门到精通实战教程】实战案例 第 41 章 论文阅读与文献综述

本文系统讲解如何用WorkBuddy高效阅读论文并撰写文献综述。针对“读了一天没记住”的痛点,提出“三遍阅读法”:快速评估相关性、结构化提炼核心内容、批判性分析局限。结合提示词工程,实现单篇精读、多篇对比与综述框架搭建,显著提升效率。强调学术诚信,严防AI幻觉,要求所有引用与数据必须回源核对。最终目标是借助AI辅助理解与组织,但坚持自主思考与写作,真正掌握科研能力。

企鹅码农的专属播客,分享10多年的实战案例和经验解读,包含YOLO、RT-DETR、AI等各个工作领域。 140

《斯坦福CS336》作业开源,含讲解视频,带你从0手搓大模型|硬核教程

人工智能领域,斯坦福CS336课程(全称:CS 336 - Foundations of Large Language Models and Their Applications)被许多学习者视为“大模型学习的必修课”,不仅因为课程本身由一流学者讲授,更因为它以从零构建大语言模型为主线,让学习者真正看清楚大模型的底层运作机制。

python1234567_的博客 2887

Modelsim:LPDDR5仿真(含美光仿真模型官方sv&vcs代码)

虽然,三星在设计跟量产上,早于其他家存储产商,但是镁光后来居上了,是业界首家支持全速 LPDDR5 的供应商,并且开发者在做开发的时候,也是用美光颗粒比较多,所以这里就先以美光的仿真模型为主进行仿真。 本工程为美光官方工程,支持Modelsim、VCS、ncverilog三种仿真器仿真。模型破解过,为完整system-verilog代码。

千歌叹尽执夏的博客 1283

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Assignment 1: BPE Tokenizer

斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Assignment 1: BPE Tokenizer

周同学的博客 1383

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Stanford CS336 | Assignment 1 - Transformer Language Model Architecture

本文介绍了基于Transformer的语言模型实现细节,重点剖析了核心组件与训练流程。主要内容包括: 模型架构:采用仅解码器的Transformer结构,包含RoPE位置编码、RMSNorm归一化和SwiGLU前馈网络等现代设计。 关键组件实现: 基础层:无偏置线性层、词嵌入层和RMSNorm归一化 注意力机制:旋转位置编码(RoPE)增强位置感知 前馈网络:SwiGLU激活函数提升表达能力 训练支持: 自定义AdamW优化器 学习率调度和梯度裁剪 断点续训功能 该实现从底层构建了完整的Transforme

Bug_makerACE的博客 1567

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

2025年最新最好的大模型入门课:斯坦福2025 CS336课程全公开

别愁!斯坦福大学爆火的大模型公开课 CS336——《从零开始构建语言模型》,课程和材料早已全网公开,却让不少同学直呼 “跟不上”“太硬核”?

瓦罗兰特顶级C位的博客 2547

CS336作业一笔记:Transformer 全模块 + 公式汇总

cs336作业一,transformer涉及的所有模块,公式总结

qq_41697157的博客 593

斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Assignment 1: BPE Tokenizer Implement

斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Assignment 1: BPE Tokenizer Implement

周同学的博客 1690

Stanford CS336 | Assignment 2 - FlashAttention-v2 Pytorch & Triotn实现

算法层面:理解了分块计算和LogSumExp技巧如何将注意力的显存复杂度从O(T²d)降至O(Td),为处理长序列(如8k、16k)提供了可能;工程层面:掌握了Triton框架的核心用法——通过块指针高效访问内存、利用共享内存减少全局内存访问、设计合理的分块策略适配GPU硬件;性能层面:验证了FlashAttention在长序列场景下的显著优势,为Transformer模型的工程落地提供了关键优化手段。扩展方向支持多头注意力的融合计算(当前版本为单头,多头可通过维度拆分实现);

Bug_makerACE的博客 1312

assignment1-basics:掌握编程基础,提升技能第一步

assignment1-basics:掌握编程基础,提升技能第一步 项目介绍 在计算机科学教育中,基础知识的掌握至关重要。assignment1-basics 是一个开源项目,旨在帮助学习者通过实际编码任务来深化对基础编程概念的理解。该项目作为CS336课程的第一个作业,提供了一个实践的平台,让学生在解决问题的过程中巩固Python编程的基础。 项目技术分析 assignment1-basics ...

gitblog_00657的博客 455

斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Assignment 1: Training Loop Implement

斯坦福大学 | CS336 | 从零开始构建语言模型 | Spring 2025 | 笔记 | Assignment 1: Training Loop Implement

周同学的博客 1167

Standford CS336(一)课程介绍

斯坦福CS336课程(2025春季)聚焦"从零构建语言模型",针对当前AI研究领域出现的"技术脱节"现象,强调通过实践深入理解模型机制。课程揭示了语言模型工业化面临的挑战:GPT-4等前沿模型需1.8万亿参数和1亿美元训练成本,且核心细节不公开。课程提出"多即不同"原则,指出小型与大型模型存在本质差异,重点培养三种能力:模型机制理解、硬件优化思维和建模直觉。通过梳理语言模型发展历程,从早期统计方法到Transformer架构演进,课程旨在帮助学生掌

Sol-itude的博客 1441

CS336——1-1. Overview

❌【斯坦福大学 • CS336】从零开始构建语言模型 | 2025 年春季这个清晰度不行,字幕有点问题✅斯坦福CS336:大模型从0到1|第一讲:概述和tokenization【中英双语】这个好点~斯坦福大学《从零开始的语言模型|CS336 Language Modeling from Scratch Spring 2025》中这个可能更好,但是要开月卡似乎课程主页:课上老师讲解用的是py脚本,使用的是一个叫的基于React等前端构建的一个浏览工具就在课程的github里效率是第一设计原则~

啷个哩个啷 3836
上一篇: ios::sync_with_stdio(false)和puts(“0“)会导致程序错误?谈C++输入输出同步的那些坑
下一篇: Standford CS336 | Assignment 1 - Implementing the tokenizer
ACEEE-1222
博客等级 码龄4年 432粉丝 22原创
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值