跳到主内容
@wquguru
精选88云头条行业动态

字节前实习生田柯宇涉恶意破坏模型训练,获3000万美元融资引争议

田柯宇的黑与白:受害者指控持续攻击,本人称“以暴制暴”

原文
发到 X
推荐理由

字节前员工恶意破坏模型训练一事近日因新融资再次引发关注,受害者披露的详细攻击代码与复盘极具工程警示价值,建议AI从业者重点审视内部权限管理与代码审计流程。

一笔近 3000 万美元的融资,让两年前轰动 AI 圈的字节跳动模型训练攻击事件再次成为舆论焦点。

字节跳动前实习生田柯宇创办的 AI 公司获得五源资本、IDG 资本等机构投资,投后估值达到 2 亿美元。《被字节开除的「田柯宇」获 3000 万美元融资,估值 2 亿美元》

随后,10 月 12 日,多名受影响研究人员公开了一份详细的事件调查材料,披露相关代码和内部记录,指控田柯宇曾持续数月恶意破坏模型训练,甚至涉嫌伪造研究成果。

同一天,媒体 elsewhere(别处发生)发布了今年 7 月对田柯宇的访谈内容。田柯宇承认曾干扰其他实习生的训练任务,但否认持续数月攻击及影响其他研究人员的科研成果,并将自己的行为形容为“以暴制暴”。

2026 年 10 月 12 日,一份由事件受害者陈述、内部记录及代码材料整理而成的报告公开,详细披露了田柯宇被指实施破坏的过程及技术手段。

报告称,相关行为持续数月,涉及篡改 PyTorch 源代码、干扰分布式训练、随机终止训练进程等,田柯宇甚至参与团队故障排查,并根据排查进展调整攻击方式。

此外,报告还指控,他曾使用开源模型 PixArt 冒充自己负责的千卡级 VAR 模型训练成果。

事件时间线:

2024 年 3 月,最早可追溯的破坏:LlamaGen 项目发现 text-to-image 模型训练所需的文本 cache feature 异常。

2024 年 4 月,团队开始高优推进视频生成项目 Goku,资源配置发生变化。

2024 年 5 – 7 月,Goku 大规模训练实验异常频出,团队高强度 debug。

2024 年 7 月 30 日,发现 Goku 训练所用 Docker 环境中的 PyTorch 源代码被修改。

2024 年 7 月 31 日,发现 Tian Keyu 用于攻击破坏的源码,团队分析攻击代码与实验异常现象的关联。

2024 年 8 月,公司安全部门调查问询,辞退 Tian Keyu。

2024 年 8 月,项目团队复盘发现 VAR text-to-image 模型造假,拿开源模型 PixArt 的结果冒充。

2026 年 10 月 12 日,Tian 在“elsewhere别处发生”的播客访谈中再次企图洗白、颠倒黑白。

1、最早可追溯攻击

根据事后对 Tian 相关代码及操作记录的复查,目前可追溯的最早攻击发生在 2024 年 3 月,当时的目标是 LlamaGen 项目。

攻击方式是直接篡改 LlamaGen 训练 text-to-image 模型所需的 T5 模型(flan-t5-xxl3)的权重,导致 LlamaGen 使用 Tian 提供的 T5 feature 时始终无法正常 work。早期的攻击手法相对简单粗暴,这与字节跳动当时采用的共享存储系统有关。为了方便团队成员共享数据、避免重复工作,该系统允许同组人员对共享目录中的文件进行读写操作,但这也使得拥有相应权限的内部人员能够直接修改其他成员的实验文件。

在当时的排查过程中,团队并未考虑到有人会蓄意破坏实验文件。尤其是相关文件只有同组人员才有访问权限,大家更倾向于从代码、训练流程或存储系统本身寻找问题,而没有怀疑内部人员的人为干预。后来 LlamaGen 作者重新从 Hugging Face 下载了一份新的 T5 模型权重,问题随即解决,但这次异常未能在当时及时查明原因。

值得注意的是,这次攻击发生时,LlamaGen 项目仍处于小规模训练阶段,使用的 GPU 数量不超过 100 张,尚未涉及大规模计算资源竞争。此时还没有大量 infra 团队同事支持帮助,干扰攻击对象主要为 LlamaGen 的一作。

代码 · 1 行
def modify_t5(a_ILC):    # if random.random() <= 0.135:    #     # if verbose: print(f'({Ti})    TC::shuffle    {name}', flush=True)    #     a_LC = a_ILC[0].clone()    #     a_LC = a_LC[torch.randperm(a_LC.shape[0])]    #     a_ILC.data.copy_(a_LC.unsqueeze(0))    # else:    a_norm = a_ILC.norm()    noi_ILC = torch.randn(*a_ILC.shape)    noi_ILC /= noi_ILC.norm()    # if rng.random() <= 0.67:    #     keep_ratio = 0.5    #     keep_IL = torch.rand(a_ILC.shape[:2], generator=ctg, device=mydev) <= keep_ratio    #     if verbose: print(f'({Ti})    TC::KEEP (keep {keep_IL.float().mean().item():.2f})    {name}', flush=True)    #     keep_ILC = keep_IL.unsqueeze(-1).expand_as(a_ILC)    #     a_ILC.data.copy_(a_ILC * keep_ILC + noi_ILC.mul(a_norm) * keep_ILC.logical_not())    # else:    # if verbose: print(f'({Ti})    TC::mixup    {name}', flush=True)    a_ILC.data.copy_(a_ILC / a_norm + noi_ILC * random.random()*0.15 + 0.25)    a_ILC.data.copy_(a_ILC / a_ILC.norm() * a_norm)

代码 1 事后查到的 Tian 于 2024 年 3 月在内部 GitLab 上的一条 commit 记录。高亮两行向权重混入随机噪声,再恢复原有范数,使篡改后的张量在整体数值规模上与原权重一致。

2、攻击转移至 Goku

事情的转机发生在 2024 年 4 月。

2024 年 2 月,OpenAI 发布了视频生成模型 Sora4,展示了高质量视频生成的潜力,也让各大科技公司有了更加明确的技术目标和追赶方向。在这一背景下,字节跳动的相关团队决定加大对视频生成模型的投入,并于 4 月正式启动相关模型训练。该项目后续的研究成果包括 Goku5 和 Waver6。为方便叙述,后文统一以 Goku 指代这一视频生成项目。

Goku 从一开始就是多人协作的团队产品项目,而不是某一个人单独主导的 research project。

在此之前,团队中正式立项且需要大量 GPU 资源的主要是 VAR text-to-image 项目,而前面提到的 LlamaGen 只是一个实习生主导的研究项目。因此,Goku 的成立意味着原有的 GPU 资源需要重新分配。项目启动初期,VAR 和 Goku 两个项目获得的 GPU 资源基本相当。

2024 年 6 月,快手发布 Kling(可灵),视频生成领域的竞争加剧。字节跳动内部另有两个团队开展同类研究,管理层提出收缩项目、集中资源。此后,GPU 资源逐步向 Goku 倾斜,新租 GPU 也优先分配给该项目,并引入 Seed Infra 团队支持训练稳定性与加速。

然而,一个反常的现象开始出现:随着基础设施和技术支持逐渐完善,Goku 的训练反而变得越来越不稳定。

最初,训练大约每隔一两天才会中断一次;随后,训练甚至难以连续稳定运行两三个小时;再往后,loss spike(训练损失异常飙升)频繁出现,以至于无法开展任何有意义的训练实验。

这些问题在当时被视为大规模训练中的技术故障。但结合事后复查发现的情况,Goku 的训练异常与针对该项目的破坏行为存在关联。

至此,攻击的主要目标已从早期的 LlamaGen 转向资源投入更大的 Goku 项目。不过,目前尚不清楚,在针对 Goku 的攻击持续期间,针对 LlamaGen 的破坏行为是否也在同步进行。

3、攻击加剧

随着时间推移,攻击的频率和破坏程度不断加剧。

在情况最严重的一两个月里,项目组几乎进入了全天候的故障排查状态。分布在北京、上海和西雅图的中美团队轮流值守,24 小时监控训练程序的运行情况。团队每天早晚召开会议,交接排查工作,汇总当天发现的异常现象,区分哪些问题已经得到解释、哪些仍然无法解释,并讨论下一步的诊断方向。整个过程就像一支 24 小时待命的医疗团队,守着一位病情危重、不断出现疑难杂症的患者,随时准备应对新的突发状况。

整个过程中,最折磨人的事情主要有两件。

第一,是大量故障表现出极强的随机性和不可复现性。

任何从事软件开发或机器学习研究的人都清楚,保留故障现场、稳定复现问题,是定位和修复 Bug 的基础。据受害者回忆,在 Goku 的训练过程中,团队不断遇到一些难以用正常技术逻辑解释的异常。

举一个实际的例子:同时在 100 台机器上并行运行完全相同的程序,其中 99 台机器的 loss 曲线几乎完全一致,唯独某一台机器的 loss 出现明显异常。然而,当工程师好不容易定位到这台异常机器,尝试重新启动并复现实验时,同样的程序却又恢复正常。

类似的现象反复出现:同一套代码,有时正常,有时异常;同样的运行环境,有时可以稳定训练,有时却突然失败。问题仿佛同时具有时间和空间上的随机性,既无法预测,也难以稳定复现。

除了这种难以定位的数值异常,大规模训练任务还会出现另一类问题:原本正常运行的多机训练实验,会因为部分进程突然退出而导致整个分布式任务卡死。对于需要大量 GPU 协同运行的训练任务来说,即使只有少数进程异常,也可能导致整个实验中断,不仅浪费大量计算资源,还会让此前的排查工作前功尽弃。

这些问题给参与 Debug 的工程师和研究人员带来了巨大的精神压力。大家花费大量时间和精力追查异常,却经常在即将定位问题时失去线索,或者发现刚刚总结出的规律在下一次实验中又不再成立。反复的失败不仅消耗了团队的时间和精力,也逐渐让人开始怀疑自己的技术判断,甚至怀疑这些排查工作究竟还有没有意义。

而事后对攻击代码的分析,终于能够解释这些看似无法理解的现象。

根据事后复查,Tian 的破坏行为涉及多个层面。首先,他直接修改了训练集群中的 PyTorch 源代码,对随机种子、优化器和数据加载等关键环节进行干预。这些组件直接影响模型训练的数据顺序、参数更新以及数值计算结果,因此相关篡改可能导致训练结果出现难以预测的偏差。

其次,他通过随机终止多机训练任务中的部分进程,导致原本正常运行的大规模实验突然卡死。更隐蔽的是,攻击代码还利用 checkpoint 作为载体建立登录后门,使得后续攻击能够被自动触发,并随机终止训练进程。

这些攻击手段的共同特点,是将人为破坏伪装成随机出现的软件故障、数值异常或基础设施问题。由于异常发生的位置、时间和表现形式并不固定,工程师很难通过常规的复现和排查手段找到共同原因。这也解释了为什么团队投入如此多的工程资源,却始终难以从根本上解决问题。

第二,也是性质更加恶劣的一点,是攻击者始终能够掌握团队的排查进度,并据此调整攻击方式。

Tian 一直参与项目的 Debug 沟通群组,甚至参加训练集群的日常故障排查会议。团队成员每天汇报新发现的异常、讨论可能的原因、分享排查思路,并据此制定后续的实验和修复方案。而 Tian 则能够直接了解到这些信息,并根据同事们的排查方向,持续、有针对性地修改自己的攻击代码。

这使整个 Debug 过程陷入了一种极其不对称的局面:一方在努力寻找问题的根源,另一方却能够实时了解调查进展,并据此改变问题的表现方式。

更令人难以接受的是,这种信息上的不对称,并不是因为团队缺少安全意识而刻意向外泄露了内部信息,而是因为大家从未设想过,造成问题的人可能就在参与排查的同事之中。

在正常的研究和工程协作中,分享故障现象、公开排查进度、共同讨论技术问题,是提高效率和减少重复劳动的基本方式。团队正是基于这种相互信任,才会向所有相关成员开放排查过程。然而,这种原本用于促进合作的透明机制,却被反过来利用,成为持续实施破坏、规避排查的工具。

如果说修改训练代码、破坏 checkpoint 和终止实验进程是对技术系统的攻击,那么一边参与团队排查,一边利用同事们的调查成果不断调整破坏方式,则是对团队内部信任和科研协作机制的直接破坏。

在笔者看来,后者的性质甚至比单纯的技术攻击更加恶劣。这不仅浪费了大量计算资源和工程时间,更践踏了科研工作中最基本的诚信与合作原则。从科研职业道德的角度而言,这是一种令人不齿的行为。

4. 发现异常源码

事情真正的转机,发生在 Goku 团队一次日常的 Debug 过程中。

2024 年 7 月 30 日早上,团队成员在排查训练异常时,意外发现某台机器的 Docker 环境中,PyTorch 源代码的 DataLoader 部分存在明显异常的代码。从代码内容来看,这并不像正常的开发修改,而是具有明显的人为恶意篡改迹象。

代码 · 1 行
class DistributedSampler(Sampler[T_co]):    def __init__(self, dataset: Dataset, num_replicas: Optional[int] = None,        self.seed = seed    def __iter__(self) -> Iterator[T_co]:        if self.shuffle:            # deterministically shuffle based on epoch and seed            g = torch.Generator()            import time; g.manual_seed(time.time_ns() % 10000000000)            indices = torch.randperm(len(self.dataset), generator=g).tolist()  # type: ignore[arg-type]        else:            indices = list(range(len(self.dataset)))  # type: ignore[arg-type]

代码 2 最早发现并上报的一处篡改:Docker 环境中 PyTorch 源码里的 DistributedSampler(节选,中间有省略)。PyTorch 原版在高亮处为 g.manual_seed(self.seed + self.epoch),各进程因此得到一致的数据划分;改为以纳秒时间戳作种子后,各进程的打乱顺序不再一致,也无法复现。

团队当天便将这一发现上报,并进一步检查了该机器的登录记录。结果发现,除机器所有者(owner)之外,只有 Tian 曾登录过这台机器。

随后,Team Leader 向 Tian 询问登录该机器的目的。Tian 的解释是,他只是想学习一下相关代码。由于当时尚未找到进一步的直接证据,这件事当天并没有得到深入调查,最终暂时搁置。

5、水落石出

真正的突破发生在第二天,即 2024 年 7 月 31 日。

团队另一名成员在 Tian 的个人目录下发现了大量疑似用于破坏训练系统的代码,其内容令人震惊。

这一发现立即引起了整个团队的高度重视。当天,所有参与 Debug 的成员都暂时放下手头的工作,集中分析这些新发现的代码,并将其与此前数月间出现的各种训练异常逐一对照。

随着分析深入,许多此前无法解释的现象终于找到了可能的原因:PyTorch 源代码遭到篡改、训练进程被异常终止、模型权重受到干预,以及大量具有随机性、难以稳定复现的训练故障,都能够在这些代码中找到相应的实现逻辑。

那些曾经让整个团队耗费无数时间、反复排查却始终无法解释的问题,终于开始串联起来。

代码 · 1 行
$ ls -CF ./temp18              data_dist1839            distributed_c10d.py19              e.py1939            kill.py20              nonan2039            py9_1821              py9_20__pycache__/    r.pya.py            raw_adamw.pyadam.py         raw_adamw2.pyadamw.py        re.pyadamw2.py       re_a.pyadamw39.py      wait.pyclip_grad.py

列表 1 2024 年 7 月 31 日找到的 Tian 攻击代码目录中的文件名。

卡片组:根据对攻击代码的静态分析整理,每张卡片对应一类攻击手段。此处仅展示核心片段,并非完整源码。

6、字节跳动调查

字节跳动内部安全团队介入调查,并对 Tian 进行了正式问询。

令人印象深刻的是,面对安全团队提出的问题,Tian 的回答始终显得相当平静,仿佛只是在谈论一件与自己无关的事情。

内部调查后,字节跳动对 Tian 的恶意干扰行为作出了辞退处理。

按照公司公开回应,这属于严重违纪。

事件的影响也超出了计算资源损耗:据受害者陈述,持续的训练异常使多位员工长期陷入高强度排障,承受工作与心理压力,团队内部的信任也受到损害。

2024 年 10 月,字节跳动公开回应,确认存在实习生恶意干扰研究项目模型训练的行为,并说明涉事人员已被辞退。此后,公司进一步提起侵权诉讼。

2024 年 11 月,媒体报道该案已获法院受理,公司请求赔偿侵权损失 800 万元及合理支出 2 万元,并公开赔礼道歉。

7、Drama 升级:偷梁换柱的 VAR 模型

攻击事件的内部调查结束后,团队在复盘过程中又发现了一件令人瞠目结舌的事情。

回顾此前几个月的工作记录,Tian 的项目进展似乎一直没有任何异常。在每周的 Weekly Update 中,他都能稳定汇报新的训练进展。

当时,Tian 负责的是 VAR 的 Text-to-Image(T2I)模型训练,涉及上千张 GPU 的计算资源。不仅如此,他还将所谓的训练成果部署到了内部 Discord 上,供团队成员每天测试。

这让大家产生了一个疑问:他究竟是如何做到如此高效的时间管理的?

更进一步:量化金融体系

看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力

进入量化体系 →

关联讨论

同一事件的更多信源

相似阅读

关联信息,但可能不是同一事件