从零训练迷你GPT:低参数量大模型全链路实现与优化指南 1. 项目概述与整体设计思路1.1 minimind是什么适合什么样的人先把这个话题说清楚minimind实现核心目标是训练一个“缩小版GPT”风格的语言模型让整个链路——数据清洗、分词器训练、模型搭建、预训练、对话微调、推理采样——都能在一台普通消费级显卡上完整跑通。它不是一个商业产品而是一套“解剖大模型”的教学工程。我自己最初接触minimind的时候刚看完几篇Transformer论文代码能力又不够强直接去读LLaMA或GPT源码基本是劝退状态。后来发现这个方向的设计哲学刚好相反把隐藏维度降到512左右、层数控制在8层以内、词表只有几千个token这样一来模型参数量只有几千万数据和算力需求也跟着降了两个数量级。训练一个能给出“看起来像那么回事”的回答单卡跑几小时就够。它适合三类人第一类是刚入门LLM、想亲手走一遍训练流程的算法工程师或学生第二类是想在低资源环境下验证一些想法比如新的位置编码、注意力变体的研究者第三类是需要在端侧或小模型场景落地的人minimind就是天然的基线模型。无论哪类你都能通过它把大模型从“玄学”变成“可控的工程”。1.2 为什么选择“低参数量全链路”作为实现目标我见过太多人一上来就想训练几十亿参数的大模型结果数据、显卡、调试时间全都不够最终烂尾。minimind实现的聪明之处在于它把问题的规模缩小到你可以“看到每一步在发生什么”的程度。这里有个关键判断逻辑在深度学习里小模型不代表小知识量。训练一个10M参数的语言模型和训练一个1B参数的模型二者在工程链路上完全同构都要经历语料预处理、分词、数据加载、前向反向、优化器更新、推理采样这一整套流程。区别只在于矩阵大小和训练步数。所以你把小模型完整跑通一遍整个大模型的骨架就已经长在脑子里了。另外选择低参数量还有一层现实考量能严格复现。一个训练好的28M参数模型权重文件只有几十MB可以很方便地开源分享、对比实验、快速迭代。而大模型的实验往往不可重复一次训练成本太高参数微调一点结果就完全不同。对于学习和二次开发可控性比绝对性能重要得多。1.3 整体架构与技术选型一览在实际落地的时候我会把minimind的工程实现分成五个模块每个模块都有明确的输入输出边界模块核心任务输出内容数据工程收集、清洗、去重、格式化语料符合格式的JSONL文本数据分词器训练一个byte-level BPE词表tokenizer.json及词表文件模型主体搭建Transformer decoder结构可训练的参数初始化模型训练流程语言建模预训练 指令微调各阶段checkpoint推理服务自回归生成、采样策略、对话模板可交互的生成结果技术选型上我推荐尽量沿用主流大模型的标准组件RMSNorm替代LayerNormSwiGLU作为前馈网络激活函数RoPE做位置编码多查询注意力酌情使用。这些看起来是小细节但它们决定了一个模型能否顺利训练到深层次是最值得花时间理解的地方。接下来的章节我会把每一步的核心原理和踩坑点都拆开讲最终你会得到一套可以直接运行的迷你LLM实现。2. 核心技术模块拆解数据、模型结构、训练策略2.1 数据准备与分词器实现先说数据这是整个minimind最容易翻车但其实最不需要“玄学”的一步。最基础的做法是收集足够的纯文本例如中文维基、开源的中文语料、论文、书籍、代码注释等。对于一个小模型数据量不需要太多500MB到2GB的清洗后文本已经足够启动。数据清洗的核心准则是“宁缺毋滥”。常见操作包括去除HTML标签、Markdown标记、乱码、无意义符号。过滤过短或过长的段落我一般保留长度在50到2000个字符之间的片段。删除重复行和近似重复内容否则某个语料反复出现模型会严重过拟合到那几条句子上。统一全角半角标点确保分词器不会因为编码问题产生大量碎片token。清洗之后的数据我习惯保存成JSONL格式每一行是一个文档例如{text: 人工智能是研究如何让机器具备智能行为的学科。}然后是分词器。minimind这种小模型没必要从零训练一个SentencePiece模型但如果你要走完整流程我建议实现一个基于字节级别的BPE分词器BBPE。它的核心思路是把文本拆成UTF-8字节序列再通过统计相邻字节对的共现频率逐步合并出高频子词。这样做的最大好处是任意语言、任意生僻字都不会出现“词汇表里找不到”的未知词问题因为最底层一定可以拆到字节这条路是永远通的。一个简化版的BBPE训练循环逻辑如下def train_bpe(texts, vocab_size): # 1. 将所有文本编码为字节序列 # 2. 统计相邻字节对的出现频次 # 3. 每次合并频次最高的字节对形成新token # 4. 重复直到词表大小达到目标值 ...实际工程中你可以直接用HuggingFace的tokenizers库来完成这一步不到50行就能训练出一个词表为6000的BBPE模型。词表大小选多少对于中文为主的小模型6000到16000都有。太小会导致每个字被拆得很碎训练效率低太大会让embedding矩阵占太多参数。我实测下来6400左右是个性价比很高的起点。2.2 Transformer Decoder模型结构详解模型结构是整个minimind实现的灵魂。这里强烈建议从纯PyTorch手写一遍不要一上来就套transformers库否则你永远不知道“层”里面到底有多少个参数。一个标准的decoder-only语言模型每一层Transformer block包含三个核心子模块多头自注意力Multi-Head Attention前馈网络FFN常采用SwiGLU变体层归一化RMSNorm自注意力的计算流程是输入序列x先通过Q、K、V三个线性变换得到查询、键、值向量然后用点积计算注意力分数再经过Softmax归一化最后与V加权求和。这里的缩放因子是head_dim的平方根目的是防止点积结果过大导致Softmax落入饱和区梯度消失。多头注意力可以理解成“多个专家并行观察不同子空间”每个头的维度通常取hidden_size的1/num_heads。以hidden_size512、8个头为例每个头的维度就是64。FFN部分我没有用最原始的ReLU两层MLP而是选SwiGLU结构。它的思想是先通过两个线性层把输入分别映射到“门控值”和“原始值”再用门控和原值做逐元素乘法。这样做的收益是同样参数量下SwiGLU的表达能力明显强于ReLU而且在开源大模型里已经被反复验证。实现时注意SwiGLU因为有两个并行的升维矩阵所以参数量会比普通FFN多算显存时要留足余量。RMSNorm是LayerNorm的一种简化版本。LayerNorm需要计算均值再减去再做方差归一化RMSNorm直接跳过均值计算只做“均方根归一化”然后乘一个可学习缩放参数。效果在Transformer训练中几乎不受影响但省掉了约一半的归约开销对小模型的训练速度提升明显。RoPE旋转位置编码是当前大模型里默认的位置编码方式它通过旋转矩阵把“绝对位置”信息编码成“相对位置”信息。直观理解就是位置m的词向量在计算注意力时和位置n的词向量之间的分数只依赖它们的距离m-n这在处理长文本时比绝对位置编码更稳定。给一个简化的单层实现骨架方便你对照理解class MiniBlock(nn.Module): def __init__(self, dim, num_heads): super().__init__() self.attn MultiHeadAttention(dim, num_heads) self.ffn SwiGLUFFN(dim, hidden_dimdim * 4) self.norm1 RMSNorm(dim) self.norm2 RMSNorm(dim) def forward(self, x): x x self.attn(self.norm1(x)) x x self.ffn(self.norm2(x)) return x这里有个容易被忽略的细节Pre-Norm结构。即先归一化再进入子层子层输出再与输入做残差连接。绝大多数现代LLM都用Pre-Norm因为它比Post-Norm更容易稳定训练。如果你用的是Post-Norm小模型还能勉强收敛深层次模型基本必炸这是无数人踩过的坑。2.3 训练超参与优化策略模型结构搭好之后训练超参是第二个分水岭。我把一套稳定迭代的默认配置贴在这里后面详解每项为什么这么选。超参数推荐值说明优化器AdamW常用语言模型优化器beta1, beta20.9, 0.95beta2偏大适合LLM学习率3e-4预训练阶段常见起点权重衰减0.1AdamW下L2正则替代Warmup步数总步数的1%~5%避免初期更新过激批次大小32~128按步计受显存限制可用梯度累积序列长度256~512小模型可先短后长混合精度bf16/fp16提升训练速度、节约显存梯度裁剪1.0防止梯度爆炸标签平滑0.1减轻过拟合提升生成多样性为什么语言模型训练需要warmup因为初始化后的模型输出分布非常尖锐如果一上来就用很大的学习率参数会被推向一个错误的局部区域之后很难拉回来。warmup相当于先让模型在“小步慢走”中把结构稳定住再转入大步优化。我实测过去掉warmup后loss曲线会明显震荡最终收敛值也更高。学习率调度方面我常用“warmup 余弦退火”。先线性升到峰值再按余弦曲线平滑降低到峰值的约十分之一。余弦退火的优势在于训练后期学习率逐渐变小能够帮助模型在损失曲面底部做精细搜索比起固定学习率最终loss能低不少。梯度累积是处理显存不够最有效的手段。假设你期望的最优batch size是64但显卡一次只能塞进16条样本那就在更新前累积4次反向传播的梯度累积满之后再调用优化器step。需要注意梯度累积会使“有效batch size”变大如果累积太多模型收敛会变慢还要根据累计batch数量同步调整学习率经验上学习率与有效batch size近似成正比。2.4 自回归推理与采样策略训练完成后真正让模型“开口说话”的是自回归推理。核心逻辑很简单把当前已经生成的所有token喂给模型预测下一个token的概率分布然后从中采一个token拼接到输入末尾再重复整个过程。这个“串行”的过程决定了推理速度通常比训练慢因为每一步都在做一次完整的前向传播。但这里有个大坑如果你每次都选择概率最大的token贪心解码模型会陷入严重的重复循环。举个实际例子小模型训练步数不足时贪心解码很可能会生成“人工智能是人工智能是人工智能是……”这种死循环。所以推理必须配合采样策略最常用的是三件套温度参数temperature对logits除以一个温度系数。温度越低分布越尖锐输出越保守温度越高输出越随机。Top-k采样只从概率最高的k个token里采样直接砍掉长尾。Top-p采样核采样从累计概率超过p的最小集合里采样比top-k更灵活因为候选数量是动态的。另外还有一个很好用的重复惩罚参数repetition_penalty当某个token已经出现过时在计算概率前把它的logits除以一个大于1的系数比如1.1从而降低它再次被选中的概率。写一个简化的推理循环示例def generate(model, tokenizer, prompt, max_new_tokens128, temperature0.8, top_k50, top_p0.9): model.eval() input_ids tokenizer.encode(prompt) for _ in range(max_new_tokens): logits model(torch.tensor([input_ids]))[0, -1, :] / temperature # 过滤已出现的token重复惩罚 # top-k top-p筛选候选集合 # 从候选集合中按概率采样得到next_token input_ids.append(next_token) if next_token tokenizer.eos_id: break return tokenizer.decode(input_ids)真正做生成服务时还需加入长度限制、停止符判断、流式输出等细节。但核心架构就是上面这个循环把它吃透推理服务就只剩下优化问题。3. 实操从零训练一个可对话的Minimind3.1 工程目录与依赖准备开始写代码之前先把工程目录规划好。我通常会搭成下面这样一个扁平而清晰的结构minimind/ ├── data/ │ ├── raw/ # 原始语料 │ └── processed/ # 清洗后的JSONL ├── tokenizer/ │ └── tokenizer.json # 训练好的分词器 ├── configs/ │ └── pretrain.yaml # 模型与训练配置 ├── src/ │ ├── model.py # Transformer模型定义 │ ├── dataset.py # 数据读取与批处理 │ ├── trainer.py # 训练循环 │ ├── tokenize.py # 分词器训练脚本 │ └── inference.py # 生成与采样脚本 ├── checkpoints/ # 模型保存位置 └── logs/ # 训练日志依赖库我控制在最小范围PyTorch、HuggingFace tokenizers、PyYAML、tqdm、numpy。不需要引入深度学习框架之外的重型依赖这样无论是本地跑还是部署到服务器都是一条pip install解决的问题。3.2 关键代码实现思路模型代码我建议分四个文件来写职责单一model.py放Transformer结构dataset.py处理从纯文本到训练样本的映射trainer.py封装训练循环inference.py处理生成逻辑。dataset处理要特别注意一点语言模型训练样本的标签就是输入本身右移一位。我看过很多新手代码把任意文本当prompt强行用模型去预测一个不存在的“答案”这是完全错误的理解。预训练阶段的任务就是“预测下一条token”所以每条样本都是完整文本序列无需额外构造问答对。一个标准的训练batch构造流程是def collate_fn(batch_texts): # 对文本做分词 tokens [tokenizer.encode(t) for t in batch_texts] # 统一截断或padding到seq_len用pad_id填充 input_ids [t[:seq_len] if len(t) seq_len else t [pad_id] * (seq_len - len(t)) for t in tokens] # 标签同样是input_ids但计算loss时忽略pad_id labels [t if t ! pad_id else -100 for t in input_ids] return torch.tensor(input_ids), torch.tensor(labels)这里最重要的一个细节是把pad位置的标签设为-100。PyTorch的CrossEntropyLoss会默认忽略值为-100的目标这样模型不会被“预测下一个pad token”这种无意义任务干扰。如果忘了这一步loss会被padding位置严重拉高训练出来的模型表现也会很怪。trainer.py里面我强烈建议加入梯度累积、混合精度、梯度裁剪、学习率调度这几项。混合精度在40系N卡或A100上用bf16更稳因为bf16的指数位和fp32相同基本不会出现溢出问题老一点的V100或20系卡才需要用fp16配合scaler使用。训练循环的伪代码for step, batch in enumerate(loader): input_ids, labels batch with torch.autocast(device_typecuda, dtypetorch.bfloat16): logits model(input_ids) loss cross_entropy(logits.view(-1, vocab_size), labels.view(-1)) loss loss / grad_accum_steps loss.backward() if (step 1) % grad_accum_steps 0: torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() scheduler.step() optimizer.zero_grad() if step % log_interval 0: logger.info(fstep{step}, loss{loss.item():.4f})3.3 参数量与显存估算很多人对“几千万参数到底多大”没有概念。我们以一组常见minimind配置为例hidden_size512num_layers8num_heads8head_dim64vocab_size6400FFN中间维度用SwiGLU设为4*hidden_size2048。逐项算参数量Embedding矩阵vocab_size * hidden_size 6400 * 512 3,276,800。每层AttentionQ、K、V、O四个线性层每层输入输出都是512维即512 * 512 262,144四个合计约1,048,576。每层FFNSwiGLUgate和up两个升维层每个是512 * 2048 1,048,576两个合计2,097,152down降维层是2048 * 512 1,048,576。小计约3,145,728。每层RMSNorm两个每个512个缩放参数约1024。单层总计约1,048,576 3,145,728 1,024 4,195,328。8层总计约33,562,624。最后输出层如果复用embedding权重weight tying不新增参数如果不复用则要再加327万。模型总参数量约36.8M复用或40.1M不复用。显存占用可以快速估算。一个3,700万参数的模型bf16权重约占74MB梯度同样占74MBAdamW优化器状态包含fp32主权重、一阶动量、二阶动量约3700万 * 3 * 4字节 444MB。权重加梯度加优化器状态合计不到600MB剩下的显存大头全部来自激活值中间层的输出。对于seq_len256、batch_size16的情况8层Transformer的激活值大约需要3到6GB。因此一块12GB显存的卡开bf16 训练seq_len256、batch_size16是比较稳的想要更大batch就开梯度累积。3.4 训练启动、监控与效果验证训练脚本写好后建议先把max_steps设成10跑一次完整的前向反向确认没有shape报错、loss能正常下降再放开完整训练。这样做的好处是出错时定位快不用等半小时才发现代码有问题。训练过程中要重点盯三个指标loss是否平稳下降、梯度范数是否偶尔激增、学习率是否按计划变化。我习惯把每100步的loss记录到日志并用脚本把曲线打印出来。如果训练曲线在某个点突然反弹多半是数据里有异常batch或者学习率步长太大。预训练到loss低于1.5左右取决于词表和语料不同设置间差异很大可以手工测试几次生成效果。此时模型大概率会输出一些语法通顺但内容空洞的句子这是正常的因为预训练只学到了语言规律还没有对齐到具体问题。接下来做指令微调。准备几万条简单的问答对例如从开源指令数据里抽取格式化为{instruction: 你是谁, output: 我是用MiniMind架构训练的小模型。}微调时把输入拼成“指令 回答”的长文本训练目标仍然是最小化整段文本的交叉熵。但为了让模型专注学习回答部分通常要构造一个mask把指令部分的标签也设为-100。这一技巧非常关键我之前因为贪省事没加mask结果微调之后模型连问题都不会好好复述了。微调学习率要比预训练小一个数量级一般取2e-5到5e-5训练轮数控制在1到3轮。轮数过多非常容易过拟合模型会把训练集里的答案背下来遇到没见过的问法就开始胡言乱语。4. 常见问题与排查技巧实录4.1 Loss不降或震荡怎么办loss一直不降是训练早期遇到最多的现象。我会按下面几步依次排查第一看数据是否被正确读取。打印一个batch的input_ids和labels用tokenizer decode回来确认不是一堆pad_id或者乱码。很多时候问题根本不是模型而是数据加载顺序或tokenization错了模型一直在学习预测一堆无意义的填充符。第二检查学习率是不是过小或过大。学习率过小时loss下降非常缓慢曲线近似直线但不是绝对不动学习率过大时loss会出现剧烈的锯齿状。观察日志里前200步的loss变化如果完全平滑但几乎不降把学习率调大3到5倍再试。第三确认损失函数没有把padding位置算进去。前面提到的-100标签技巧如果漏了这一步loss会维持在一个偏高的水平且模型生成结果极差因为模型把大量容量浪费在“预测结束符后面的填充符”上。第四排查数据是否被shuffle。如果不打乱数据模型会一直在一个主题的连续片段上训练对同主题的文本反复拟合loss曲线会在每个epoch边界处突然跳变。建议每个epoch开始时都对数据集做一次shuffle。4.2 显存OOM的排查与规避OOM是最让人头大的问题但通常都是可以预判并规避的。我自己的排查顺序是先看是不是激活值爆炸。序列长度对显存的影响远大于batch size因为注意力矩阵是seq_len的平方复杂度。如果seq_len512时OOM而seq_len256时没问题那就说明是激活值问题。解决办法是降seq_len或者开启gradient checkpointing牺牲约30%训练速度换取显存减半。再看是不是优化器状态太大。如果模型参数量本身不小AdamW的fp32状态会很占显存。本质上有两个选择换用更节省内存的优化器比如Adafactor或者坚持用AdamW但把batch size和seq_len调低。最后看batch size能不能再压缩。单独batch size设为1也能训练只是梯度噪声大配合梯度累积能基本缓解。如果说到底显存还是不够就减少层数或隐藏维度这也是minimind作为教学项目的优势——参数空间很大怎么调都够灵活。4.3 生成效果差重复、乱码、无意义生成重复最常见的原因有两类一是训练步数不足模型根本无法组织长程依赖只能靠复读机勉强维持语言模式二是采样策略太“贪心”温度太低、或没有加重复惩罚。遇到这种情况我建议先调推理参数把temperature调到0.8到1.0之间打开top_p0.9和repetition_penalty1.1左右通常能立竿见影。生成乱码则意味着分词和模型词表对不上。例如你用了一个词表训练模型但推理时用另一个tokenizer加载或者tokenizer的pad_id与模型配置不一致decode出来的就全是乱码。检查方式很简单加载模型和tokenizer后先对一个固定文本做encode再decode看是否一致再单独喂一句prompt看输出是否包含大量unk标记。如果出现unk说明词表里没有对应子词需要从训练数据的tokenizer一致性问题入手。生成内容无意义、答非所问通常不是推理参数问题而是模型没有经过指令微调或者微调时没有使用attention mask。预训练模型本身只是一台“语言接龙机”你没有告诉它什么是问题、什么是回答它自然只能顺着输入继续编故事这很正常。4.4 训练中遇到NaN与精度问题的处理训练到一半loss突然变成NaN是最容易劝退新手的场景。但从工程角度讲NaN的出现基本逃不出三个原因数据里有非法token。例如原始文本包含无法被tokenizer处理的特殊字符或者文本为空导致注意力矩阵全是-100之类。解决方法是清洗数据时增加一步“tokenize后过滤”把分词结果长度小于阈值或包含异常id的样本直接丢弃。fp16精度溢出。fp16的表示范围很小当logits绝对值过大计算cross entropy时中途可能出现inf最终变成NaN。处理方式很简单换用bf16。如果硬件必须用fp16就开启loss scaler并且可以考虑在cross entropy之前手动对logits做一次max减除防止数值过大。学习率过大导致参数更新溢出。尤其是训练后期部分层参数已经很大突然来一个大的梯度更新权重就可能崩掉。配合梯度裁剪是基本操作同时检查权重衰减是否设置过大0.1已经是很激进的数值不要再往上加。4.5 问题排查速查表现象常见原因首选解法loss一直不降数据读取或padding标签错误打印batch检查labelsloss剧烈震荡学习率过大 / 无warmup降低lr增加warmuploss突然变NaNfp16溢出 / 数据异常换bf16清洗数据显存OOM激活值占用过大降seq_len开gradient checkpointing生成死循环贪心解码 / 训练不足加temperature与重复惩罚生成乱码tokenizer不匹配确认训练与推理使用同一tokenizer微调后变笨mask缺失 / 过拟合对指令部分标签设-100减训练轮数生成空洞无意义未做指令微调构造SFT数据继续训练5. 从MiniMind出发扩展方向与我的实践心得5.1 把MiniMind扩展到更大规模minimind训练稳定后一个很自然的冲动是“把参数变大”。我建议遵循“小步快跑”的扩参规律先把hidden_size从512加到768层数从8加到12词表从6400加到15200其他配置保持不变看看显存和loss的变化趋势。你会很快体会到scaling law的粗糙规律模型变大后同样的数据量下每个token的预测loss会系统性变低但绝对训练时间变长。这个阶段最值得做的实验是记录“不同参数量的模型在相同数据量下的最终loss”画一条曲线出来。很多论文里的标度定律完全可以在单机上用3到4个不同规模的minimind复现一次非常直观。5.2 微调与对齐中文指令数据的二次训练微调阶段我强烈建议自己构造或整理一批高质量的中文指令数据数量不在多两万条左右就够了。格式可以覆盖问答、写作、翻译、摘要、代码解释等基础任务。核心经验是指令部分必须打mask不参与loss计算。每条回答以EOS结束模型才学得会“停止”。如果想让模型学会拒绝无意义问题需要手工加入一些“拒绝样本”否则模型会对所有输入都强行编答案。在SFT数据基础上如果想进一步尝试对齐可以加一步基于偏好的DPO训练。minimind的规模正好适合在单卡上跑DPO你能完整看到奖励模型和策略模型之间的互动规律这是在大模型上很难低成本获得的经验。5.3 推理优化与量化推理阶段的小模型同样有优化空间。最直接的手段是权重量化。把bf16权重转成int8或int4推理显存会大幅下降速度也可能提升。由于minimind参数量很小即使用int4量化质量损失也远比大模型小很适合用来做量化试验。进一步优化可以考虑KV Cache的处理。在自回归生成中每一步都需要用到历史的Key和Value直接存下来避免重复计算是提速的关键。对一个8层的模型KV Cache占用极小但理解和实现这一机制对后面接触大模型推理引擎会非常有帮助。5.4 几个值得保留的高频技巧最后把我反复用到的几条经验集中放到这里也算给整篇文章一个收尾。第一训练前固定随机种子。语言模型对初始化非常敏感如果不固定种子你根本没法判断一个改动是变好还是变坏。第二定期保存checkpoint并不仅保存模型权重还要保存tokenizer配置和训练参数。模型本身没有意义权重和词表、配置绑定在一起才有意义。第三日志里除了loss一定要打印学习率和梯度范数三个指标互相参照才能定位问题。第四一切可视化都建议用最简单的numpymatplotlib或tensorboard不要为了炫技引入复杂框架时间应该花在模型改进上。我实际跑完一整套minimind流程后最大的感受是所谓“大模型”其实并没有那么多不可理解的黑魔法它就是把数据、结构和优化三个环节的工程细节层层堆叠起来。minimind的价值恰恰在于把所有魔法拆成了你可以亲手操控的旋钮。当你亲眼看到一个小模型从随机输出垃圾到逐渐说出通顺句子再经过微调后能勉强对话那种对整个技术栈的理解比看一百篇综述都扎实。

相关新闻

最新新闻

Remotion 视频布局排版指南:安全区、字号基线与“视频优先“构图法则

Remotion 视频布局排版指南:安全区、字号基线与“视频优先“构图法则

Remotion 视频布局排版指南:安全区、字号基线与"视频优先"构图法则 【免费下载链接】remotion 🎥 Make videos programmatically with React 项目地址: https://gitcode.com/GitHub_Trending/re/remotion 导读 在 Remotion 中用 React…

2026/9/8 17:00:25
Switch EmuMMC 启动故障排查完整指南:6 步恢复你的虚拟主机存储

Switch EmuMMC 启动故障排查完整指南:6 步恢复你的虚拟主机存储

Switch EmuMMC 启动故障排查完整指南:6 步恢复你的虚拟主机存储 【免费下载链接】Atmosphere Atmosphre is a work-in-progress customized firmware for the Nintendo Switch. 项目地址: https://gitcode.com/GitHub_Trending/at/Atmosphere 你在 Atmospher…

2026/9/8 17:00:25
B站 王树森推荐系统学习笔记 P10

B站 王树森推荐系统学习笔记 P10

双塔模型 自监督学习 : 双塔模型存在的问题 : 推荐系统的头部效应严重 :大部分物品的点击次数不高 ;少部分物品占据大部分点击 ; 高点击物品的表征学得好 , 长尾物品(曝光和点击次数太少 , 训练的数目不够) 的表征学的不好 . 比较好的解决方法 : 自监督学习 做date augmentat…

2026/9/8 17:00:25
Bruno 本地开发环境搭建与 npm 多工作区构建流程详解(贡献者指南)

Bruno 本地开发环境搭建与 npm 多工作区构建流程详解(贡献者指南)

Bruno 本地开发环境搭建与 npm 多工作区构建流程详解(贡献者指南) 【免费下载链接】bruno Opensource IDE For Exploring and Testing APIs (lightweight alternative to Postman/Insomnia) 项目地址: https://gitcode.com/GitHub_Trending/br/bruno …

2026/9/8 17:00:25
graphify 在 Codex 平台上的并行语义抽取:spawn_agent 单消息派发全指南

graphify 在 Codex 平台上的并行语义抽取:spawn_agent 单消息派发全指南

graphify 在 Codex 平台上的并行语义抽取:spawn_agent 单消息派发全指南 【免费下载链接】graphify Turn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, an…

2026/9/8 17:00:24
【错误记录】Flutter 引入友盟 SDK 编译报错 ( ERROR: Missing classes detected while running R8. Please add the mis )

【错误记录】Flutter 引入友盟 SDK 编译报错 ( ERROR: Missing classes detected while running R8. Please add the mis )

文章目录前言一、报错信息二、问题分析三、解决方案1、解决方案 一 : 添加混淆规则2、解决方案 二 : 添加 OkHttp 依赖前言 Flutter 应用中导入 友盟 SDK ; dependencies:flutter:sdk: flutter# 友盟 Flutter SDK : umeng_common_sdk 为公共基础组件(含 U-App 统计)&#xff1…

2026/9/8 16:55:24