
简介《大语言模型入门的基础知识》是一份面向零基础学习者、转型开发者与技术分享者的PDF讲义围绕语言模型的核心概念、发展脉络与评估指标展开帮助读者建立从统计语言模型到神经语言模型、预训练模型及大语言模型的系统认知。压缩包内仅含1个PDF文件大小约3.24MB篇幅集中、目录清晰便于按章节阅读适合用作课程预习、团队内部分享提纲或面试前的知识梳理材料。内容从LLM背景切入依次讲解语言模型定义、句子概率计算、N-gram的马尔科夫假设与数据稀疏问题并延伸到神经网络语言模型、Transformer与预训练范式配合bigram计数、下一词预测等例子说明原理与局限。已有129人学习适合希望用较短时间搭建LLM基础知识框架、补齐关键术语与模型演进脉络的入门读者。1. 先破除一个误解大语言模型不是「什么都懂的搜索框」刚接触大语言模型的人多半会问同一个问题它是不是把整个互联网都存进去了我问什么它就从里面翻出答案这个理解一旦立住后面全是坑。它更像一个读了海量文字、专门练过「接下一句」的人你给它上半句它按见过的语言习惯往下接。它能做文本分类、问答、翻译、对话这些自然语言任务靠的是从大量文本里学到的语言规律而不是背下来的资料库。这篇入门从「语言模型到底在算什么」讲起一路走到第一个能跑通的小例子零基础也能跟上。2. 语言模型到底在算什么一句话像不像「人话」的概率2.1 用生活化的说法给句子打一个「顺不顺口」的分先记住一个最朴素的解释语言模型就是用来计算一个句子出现概率的模型换句话说它判断一句话「像不像人话」。标准的写法是这样的给定一个句子序列 S {W1, W2, W3, …, Wn}语言模型算的是这个序列发生的概率 P(S)。如果给定的词序列符合语用习惯就给出高概率不符合就给低概率。到这里为止没有任何深度学习的影子就是一个概率问题。拿一个只有五个词的词典举例假设 V {黑马、程序、员、来、学习}每个词都属于这个词典。那么「黑马程序员来学习」这句话就是把这五个词按顺序排成一个序列语言模型要做的就是给这个序列算出一个 P(S)并且这个概率必须大于等于 0。你可能会问算这个有什么用用处有两个而且都很实在一是判断一句话合不合理比如拼音输入法给你几个候选你希望它先给最像人话的那个二是往下接词给一段前文预测下一个词最可能是什么。这两个用途撑起了后面所有的技术演进。这里要提前说清一件事很多人会混淆语言模型是一个大类大语言模型只是这个大类里参数特别多的那一批。先有语言模型才有大语言模型顺序不能反。入门的时候先把「算概率」这件事吃透后面看到参数量、预训练、对齐这些词才不会觉得是天上掉下来的。2.2 第一次动手用二十行代码算出「我想去打篮球」的概率最直觉的做法就是数数。假设手上有一个语料库里面有 N 个句子我们统计一下某个句子在语料里出现了多少次记作 n那么这个句子的概率就是 n 除以 N。这个方法简单到不像话但它能跑而且能让你第一次看到「概率」是怎么从数据里冒出来的。from collections import Counter # 一个极小的语料库每个句子已经分好词 corpus [ [我, 想, 去, 打, 篮球], [我, 想, 去, 学习], [我, 想, 去, 打, 晚饭], [他, 想, 去, 学习], ] # 统计每个句子出现的次数 sentence_count Counter(tuple(s) for s in corpus) N len(corpus) # 语料库里的句子总数 target (我, 想, 去, 打, 篮球) print(sentence_count[target] / N) # 0.25出现过 1 次 # 换一个没在语料里出现过的句子 other (我, 想, 去, 打, 羽毛球) print(sentence_count[other] / N) # 0.0一次都没出现过这段代码里Counter负责把每个句子当整体去计数tuple(s)是把列表转成元组因为列表不能当字典的键。N是语料里的句子总数除一下就是频率用频率近似概率。跑完你会看到两个结果0.25 和 0.0。关键就在这个 0.0 上。任何一个没在语料库里原样出现过的句子概率都是 0。而人类语言是无穷无尽的组合你几乎不可能把所有合法句子都提前塞进语料库。所以这种「整句计数」的办法实际预测能力几乎为零。这不是代码写得不好是思路本身走不通。2.3 链式法则能救场但带来两个新麻烦既然整句统计不行那就换个思路把整句的概率拆成一个个词的条件概率连乘。这就是概率论里的链式法则一句话的概率可以写成 P(W1) 乘以 P(W2|W1)再乘以 P(W3|W1,W2)一路乘到最后一个词。这么拆完只要我们能算出「已知前面所有词下一个词是什么」的概率整句的概率就出来了。这个拆法也顺带给了语言模型第二个定义而且这个定义更贴近今天的用法给定一个短语语言模型可以生成或者说预测接下来的一个词。你在手机上打字时跳出来的那几个候选词背后就是这个思路在跑。但链式法则有两个绕不过去的坎。第一个是参数空间太大条件概率 P(Wn|W1,W2,…,Wn-1) 的取值可能性太多前文每多一个词要估的组合就翻着倍涨根本估不完而且很多组合估出来也没什么用。第二个是数据稀疏大量词对组合在语料库里压根没出现过用最大似然估计算出来的概率直接是 0一乘就全归零了。这两个问题是后面所有技术演进的出发点记住它们你看 N-gram、看神经网络模型就知道每一代在解决哪一个。提示初学阶段最容易犯的错是急着背公式。把这段的两句话记住就够了——语言模型算的是句子的概率算不出来是因为参数太多加数据太少。3. 四代语言模型怎么走过来的从查表到神经网络3.1 N-gram 的核心假设只看前面一两个词要解决「条件概率估不完」的问题最直接的办法是砍掉一部分条件。N-gram 模型引入了一个假设叫马尔科夫假设一个词出现的概率只和它前面有限的几个词有关再往前的就不管了。按看几个词来分主要有这么几种unigram也就是一元模型假设每个词和其他词独立完全不看前文bigram二元模型当前词只依赖前面一个词trigram三元模型当前词只依赖前面两个词。推广一下N 元模型就是假设当前词只和它前面的 N-1 个词有关。这里的 N 要你自己选实践中用得最多的是 bigram 和 trigram因为 N 再往上参数量涨得飞快而效果提升有限。这些概率参数都可以通过大规模语料库统计出来三元的话就是统计「前两个词加当前词」出现了多少次。这个假设粗糙吗粗糙。但它把「估不完」变成了「估得起」这是从零到一的一步。3.2 手把手跑一个 bigram从计数到预测下一个词理论说完直接跑。bigram 的参数就是 P(Wi|Wi-1)计算方法很朴素先数一下词对 (Wi-1, Wi) 在语料里出现了多少次记作 C(Wi-1, Wi)再数一下 Wi-1 单独出现了多少次记作 C(Wi-1)两个一除就是概率。from collections import Counter import math corpus [ [我, 想, 去, 打, 篮球], [我, 想, 去, 学习], [我, 想, 打, 篮球], [他, 想, 去, 打, 篮球], [我, 不, 想, 去], ] uni Counter() # 单个词的计数 C(Wi-1) bi Counter() # 相邻词对的计数 C(Wi-1, Wi) for s in corpus: for w in s: uni[w] 1 for a, b in zip(s, s[1:]): bi[(a, b)] 1 vocab_size len(uni) # 词表大小用于平滑 def prob(word, prev, k0.1): 计算 P(word | prev)k 是平滑系数防止没见过的组合概率为 0 return (bi[(prev, word)] k) / (uni[prev] k * vocab_size) print(round(prob(想, 我), 3)) # 看「我」后面接「想」的概率 print(round(prob(去, 想), 3)) # 看「想」后面接「去」的概率zip(s, s[1:])这一步是拿相邻的两个词组成词对等价于在句子上一格一格滑动。k是平滑系数作用是给每个可能组合都垫一个极小的底数这样即使某个词对在语料里从没出现概率也不会是 0。没有这个 k模型只要碰到生词组合就直接崩溃这是新手最常翻车的地方之一。有了参数就能算整句概率了。因为连乘很容易数值下溢变成 0实践里都是先取对数、求和、再取指数还原def sentence_prob(seq, k0.1): 按 bigram 计算一个词序列的概率内部用对数相加避免下溢 logp 0.0 for prev, w in zip(seq, seq[1:]): logp math.log(prob(w, prev, k)) return math.exp(logp) print(sentence_prob([我, 想, 去, 打, 篮球])) print(sentence_prob([我, 想, 去, 打, 晚饭]))两句的概率不会一样因为「打」后面接「篮球」和接「晚饭」在语料里出现的次数不同。这就是模型在做判断它认为语料里更常见的搭配概率更高。再往前一步用模型预测下一个词也就是填空def next_words(prev, topn3): 给定前一个词返回概率最高的几个候选 cands [(prob(w, prev), w) for w in uni] cands.sort(reverseTrue) return [(round(p, 3), w) for p, w in cands[:topn]] print(next_words(打)) # 看看「打」后面最可能接什么cands里遍历的是整个词表所以词表越大这个函数越慢真实场景里会做各种剪枝。但对入门来说这个几十行的脚本已经是一个完整的语言模型了有训练统计计数、有参数条件概率、有推理算句子概率、预测下一个词。3.3 N-gram 的优点很实在缺点也很实在它的优点值得认真说。第一用极大似然估计参数容易训练本质上就是数数不涉及任何复杂的优化过程第二它完整包含了前 N-1 个词的全部信息在假设范围内没有丢东西第三可解释性强你能明确指着某个词对说「它之所以得分高是因为在语料里出现了 8 次」。这种透明度在今天反而成了稀缺品。缺点同样清楚只能建模到前 N-1 个词第 N 个词之前的信息全部丢掉随着 N 增大参数空间呈指数增长词表大一点就撑不住数据稀疏问题无法根治没见过的组合就是没见过平滑只是缓解不是治愈最要命的是泛化能力差——模型不理解「篮球」和「足球」是同类东西它只认识字面上的搭配。这几条缺点正好是下一代模型要解决的问题清单。4. 从神经网络到预训练模型开始「理解」而不只是「数数」4.1 神经网络语言模型把词变成一串数字N-gram 最大的问题是把每个词当成孤立的符号。神经网络语言模型换了个做法给每个词配一个向量也就是词向量让意思相近的词在数值上也接近。这样一来即使「打篮球」没在语料里出现过「打足球」出现过模型也能迁移一部分知识。这就是泛化能力的来源。具体结构不复杂按层看就清楚了。输入层拿前 n-1 个词把它们对应的词向量首尾拼接成一个长向量中间接一个全连接层过一遍 tanh 激活函数做非线性变换输出层有 V 个节点V 是词表大小每个节点对应一个词输出的是未归一化的打分。最后用 softmax 把这些打分变成概率import math def softmax(scores): 把一组任意实数打分变成和为 1 的概率 m max(scores) # 减去最大值防止 exp 溢出 exps [math.exp(s - m) for s in scores] total sum(exps) return [e / total for e in exps] scores [2.0, 0.5, -1.0] # 词表里三个候选词的原始打分 print([round(x, 3) for x in softmax(scores)]) # [0.786, 0.175, 0.039]scores是模型最后一层吐出来的三个数没有任何约束可以是负数也可以很大。max(scores)那一行是减掉最大值再取指数纯粹为了数值稳定不改变最终的比例关系。跑出来的三个概率加起来正好是 1最大的那个 0.786 就是模型给出的预测结果。相比 N-gram它的泛化能力明显更好数据稀疏的痛点被缓解了不少。但新的问题也来了对长序列的建模能力依然有限而且层数一深就容易出现梯度消失训不动。4.2 预训练加微调一次训练到处复用Transformer 架构出现之后玩法彻底变了。这一代模型的核心思路是两段式先预训练再微调。预训练是在大规模通用文本上先把模型训一遍让它学到通用的语言表示和知识这一步非常贵但只需要做一次。微调是拿预训练好的模型在具体下游任务的数据上再训一小段把通用能力迁移到你的任务上这一步很便宜。这个范式的好处是显而易见的中小团队不用从零开始烧算力拿一个预训练好的底座改一改就能用泛化能力、语义表示、抗过拟合都比前几代强得多。代价也要说清楚预训练阶段计算资源需求极大而且模型内部到底学到了什么可解释性很差很多时候它给你一个答案你没法像看 bigram 那样指着计数说「因为这里出现了 8 次」。这个「黑匣子」的特质是后面所有使用注意事项的根源。4.3 大语言模型「大」在哪10B 这条线和它带来的变化到了这一代事情的走向有点反直觉预训练模型的参数规模指数级往上堆的时候语言模型的性能大致会跟着线性上升。目前业界比较常用的一个界定是参数量超过 10B一百亿的模型才算大语言模型而头部模型的参数量早就到了千亿甚至更多。参数是在大量文本数据上训出来的规模越大模型见过的语言现象就越丰富。规模上来之后最明显的变化不是分数涨了多少而是用法变了。早期模型你要用它得针对任务改权重、做微调而参数量足够大的模型可以在完全不调整权重的情况下只靠你给的任务示例就把事情做完。这个变化对新手特别友好——你不需要显卡集群也能先用起来。它像人一样能对话、能按指令完成任务甚至能接上外部工具去检索信息这些都是优点。缺点同样要提前知道参数量大意味着算力要求高、训练时间长而且它仍然可能生成有害的、带偏见的内容。这不是模型没训好是「按概率生成」这件事本身决定的。4.4 怎么判断它好不好入门必知的几个评估指标模型训完或者用起来之后总得有个东西衡量好坏。入门阶段知道四个词就够了。困惑度perplexity是最核心的一个它衡量模型对一段文本有多「困惑」数值越低说明模型越不意外预测得越准。计算方式是取每个词概率的对数、求平均、再取指数import math # 假设模型对一句话里 5 个词分别给出的概率 probs [0.2, 0.1, 0.05, 0.4, 0.25] # 困惑度 exp(-平均对数概率) ppl math.exp(-sum(math.log(p) for p in probs) / len(probs)) print(round(ppl, 2)) # 6.31这段代码里math.log(p)把概率转成对数sum(...) / len(probs)求平均前面加负号最后math.exp还原。可以粗略理解成困惑度是 6.31差不多等于模型在每个位置上要在 6 个多候选词里犹豫。概率给得越高困惑度越低模型越好。另外三个是准确率、召回率这类分类指标用在下游任务上BLEU 这类文本生成指标用来比对生成文本和参考文本的重合程度以及人工评估让真人来打分因为自动指标经常和人的判断对不上。入门阶段别纠结公式先记住「困惑度看语言模型本身BLEU 和准确率看具体任务人工评估兜底」这个分工就行。5. 新手入门最容易踩的 5 个坑现象、原因和补救5.1 坑一模型说得很肯定就以为它真的知道现象你问一个事实性问题模型语气笃定地给了一个答案一查是错的而且错得很自然。原因它本质上还是在算「下一个词接什么概率最高」不是在数据库里检索。训练数据里没有、或者有冲突的内容它照样能顺着语言习惯编出一段通顺的话这在业内叫幻觉。概率高不等于事实对。解决把模型输出当成「草稿」而不是「结论」凡是涉及具体数字、时间、引用来源的一律自己核一遍。做工程时在流程里加一道校验比如让模型只输出结构化字段再拿字段去查真实数据源。5.2 坑二换个说法问模型就答不上来了现象同一件事你用训练语料里的说法问它答得挺好换个同义表达效果断崖式下跌。原因这是数据稀疏的老毛病在新架构上的残留。词表里没出现过的组合模型没有直接经验只能靠泛化能力硬猜猜不准很正常。N-gram 时代这个问题表现为概率直接归零现在表现为「答得似是而非」。解决问法上做归一化把口语化的表达先转成更常见的说法或者一次给几个不同角度的问法取一致的答案。做训练数据时同义改写是一条性价比很高的扩充手段。5.3 坑三以为参数越大效果越好忽略了数据和任务现象换了个参数更大的模型结果在自己的任务上反而更差或者提升微乎其微。原因参数量只是三个变量里的一个另外两个是训练数据的质量和数量、以及和人类意图的对齐程度。一个参数中等但数据干净、对齐到位的模型在具体任务上完全可能赢过参数更大但没调好的模型。解决选型时不要只看参数量先拿自己的真实数据跑一个小规模对比固定评测集看实际分数。评测集一定不能拿去调参否则分数虚高上线就露馅。5.4 坑四一上来就想训一个自己的模型现象刚学两天就想着准备数据、买算力、从头训一个模型折腾两周卡在环境上热情耗尽。原因训练一个大模型是个系统工程涉及数据清洗、分布式训练、显存优化、评测一整套东西前置知识比想象的多得多。新手在这个阶段投入产出比极低。解决先用现成模型把「用」这一环跑通理解输入输出、理解提示词怎么写、理解评估怎么做再去碰训练。我第一次学的时候就是先写了一个几十行的 bigram 脚本跑通才真正明白「语言模型」四个字在说什么这个顺序别跳。5.5 坑五拿测试集反复调分数很好看但没用现象本地评测分数一路涨到很高一换真实场景就崩。原因你在测试集上反复试错、反复选参数等于把测试集的信息泄露进了模型这叫数据泄露。分数涨的是「对这个测试集的拟合度」不是真实能力。解决数据一开始就切成三份训练、验证、测试。调参只看验证集测试集只在最后跑一次跑完就封存。这个习惯早点养成能省下大量「上线才发现不行」的后悔药。6. 下一步学什么按这条顺序走两周就能有手感入门阶段最怕的是漫无目的地刷资料。给一条能落地的顺序按周排阶段时间具体做什么检验标准第一步第 12 天把第 3 章的 bigram 脚本抄一遍跑通改语料、改 k 值观察概率怎么变能解释为什么加平滑第二步第 35 天把统计对象从 bigram 换成 trigram对比两种模型对同一句话的打分能说出 N 变大后的代价第三步第 69 天用现成的大模型接口做三个小任务分类、抽取、改写每个任务能写出稳定的提示词第四步第 1014 天搭一个自己的小评测集几十条就够把上面三个任务量化打分能说清每个任务好在哪里差在哪里第一、二步的意义在于建立手感。你在纸上算不明白的东西写两行代码跑一下立刻就懂了比如平滑系数从 0.1 改成 1生僻组合的概率会明显变高这就是参数在起作用。第三步开始接触真正的大模型重点不是调得多花哨而是理解「同一个任务提示词差一句话结果差一个档」。第四步是分水岭有没有自己的评测集决定了你后面是在盲调还是在做实验。学的过程里如果只记一件事就记这个任何一次改动都要能在一组固定的题目上量出变化。做技术的血泪经验大多来自「感觉变好了」而不是「量出来变好了」。说个我自己的教训。早期我做模型对比全凭肉眼扫几个例子就下结论来回折腾了好几轮最后发现方向从一开始就是错的因为我看的那几个例子恰好都在模型的舒适区里。从那以后我每次动模型之前都强制先把评测集固定下来、跑一遍基线、留一份结果再去改。这个动作花不了十分钟但省下来的时间是以天计的。照着上面的顺序走不用追求一次看懂所有公式先把第一个例子跑出来。这份入门笔记可以当成一条主线遇到不懂的概念再回头翻对应的那一节。希望帮到你。本文还有配套的精品资源点击获取