5分钟带你搞懂从0打造一个ChatGPT
<h1 id="前言">前言</h1><p>欧阳上一次写文章还是4个月前,之所以断更有两个原因:<strong>换工作</strong>和<strong>业余时间ALL IN AI</strong>了。不管你是否承认,AI时代已经来了,依然埋头研究<strong>前端的那一亩三分地</strong>和<strong>源码</strong>在未来可能就是蒸汽时代被淘汰的纺织女工。</p>
<p>今年大家多多少少都有接触到AI相关的项目了,从前端的角度来看以前是调用后端的接口,现在改成了调用大模型提供的接口,本质依然没变。</p>
<p>但是按照大家<strong>卷</strong>的程度来看,在未来的不久不管你是前端还是后端,<strong>大模型底层原理</strong>将会是和源码一样成为面试中的热门话题。所以欧阳打算写一个关于<strong>大模型底层原理</strong>系列的文章,包括热门的<code>Transformer</code>、<code>自注意力机制</code>、<code>深度思考</code>等内容(先挖一个大坑)。</p>
<p>加入欧阳的AI交流群</p>
<h1 id="什么是生成式大模型">什么是生成式大模型</h1>
<p>大家平时接触的AI基本都是生成式大模型,比如我们熟知的ChatGPT、DeepSeek等。在ChatGPT还没火之前,<strong>判别式模型</strong>是最热门的,也就是国内的科技公司花了大精力去研究的领域,从现在的上帝视角来看只有OpenAI走了正确的路。</p>
<p><strong>判别式模型</strong>的作用主要是分类,在我们的生活中到处都是<strong>判别式模型</strong>的影子,比如:</p>
<ul>
<li><strong>垃圾邮件分类器:</strong> 判断一封邮件是"垃圾邮件"还是"正常邮件"。</li>
<li><strong>情感分析:</strong> 分析一个商品评价是"积极"、"消极"还是"中性"。</li>
<li><strong>人脸识别:</strong> 判断一张脸属于数据库中的哪个人。</li>
<li><strong>图像识别:</strong> 识别一张图片中的物体是什么。</li>
</ul>
<p><strong>判别式模型</strong>做的事情只能是一个辅助工具,<strong>不能执行一些创造性的任务</strong>,比如写文章、写代码等。</p>
<p>但是生成式大模型就能突破<strong>判别式模型</strong>的天花板,不仅可以做<strong>判别式模型</strong>能够做到的所有事情,还能执行一些大家熟知的创造性的任务,比如写文章、写代码等。</p>
<p>生成式大模型的主要特点就是<strong>大</strong>,这里的<strong>大</strong>指的是模型的规模。主要体现在两个方面:<strong>参数数量</strong>和<strong>训练数据量</strong>。</p>
<p>现在的那些大模型基本都是以<code>B</code>为单位,比如DeepSeek R1最小的是<code>2B</code>,最大的是<code>685B</code>。这里的<code>B</code>是指<code>Billion</code>,也就是<code>10亿</code>。<code>2B</code>的意思是<code>20亿</code>个参数,<code>685B</code>的意思是<code>6850亿</code>个参数。</p>
<p><img alt="DeepSeek" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213048402-1325457870.png" class="lazyload"></p>
<p>为什么这些大模型的参数数量会这么大呢?</p>
<p>答案是ChatGPT发现当模型的参数数量达到一个临界点后,模型突然就开悟了一样,能够理解人类语言的含义,并且能够生成符合人类语言习惯的回答,这就是大家常说的模型的<strong>涌现能力</strong>。量变引起质变,大力出奇迹(手动狗头)。</p>
<p>在接下来的文章中我们讲的大模型都是指生成式大模型,因为<strong>判别式模型</strong>和<strong>生成式大模型</strong>比起来完全就是弟弟,人们的注意力现在基本都放在了生成式大模型上。</p>
<h1 id="文字接龙">文字接龙</h1>
<p>大模型所做的事情本质就是文字接龙,所以在讲如何训练出一个ChatGPT之前我们来聊聊文字接龙。<br>
<img alt="jielong" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213114421-13201893.png" class="lazyload"></p>
<p>人类在做文字接龙的时候会结合自己掌握的知识去思考下一个字应该接什么。</p>
<p>比如,输入是<code>人工智</code>时,大部分人都会接成<code>能</code>,组合成<code>人工智能</code>这个词,他的概率是80%(我瞎说的)。当然有人会觉得下一个字应该是<code>障</code>,组合成<code>人工智障</code>这个词,他的概率是10%。还有一些情况下会接<code>一</code>,组合成<code>人工智能一</code>,这种就是非常少见的情况,所以他的概率是0.01。</p>
<p>如果我们将文字接龙抽象化成一个函数,那么这个函数就是:</p>
<pre><code class="language-javascript">function textJieLong(input) {
let arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,...
if (路人A) {
return '能'
} else if (路人B) {
return '障'
} else if (路人C) {
return '一'
} else {
return '...'
}
}
// 输出:能 概率:80%
// 输出:障 概率:10%
// 输出:一 概率:0.01%
// 输出:.. 概率:...
textJieLong('人工智')
</code></pre>
<p>这个<code>textJieLong</code>函数就可以被看作是一个大模型,函数的输出结果就是大模型的输出结果。</p>
<p>函数中的一系列<code>arg1</code>、<code>arg2</code>等参数就是大模型的参数,我们常说的<code>8B</code>就是说明有80亿个参数,也就是有80亿个<code>arg</code>参数。</p>
<p>那么模型训练的作用是干嘛呢?</p>
<p>模型训练就是我们给<code>textJieLong</code>函数一堆输入和输出的数据,然后根据这些输入和输出的数据去反推<code>arg1</code>、<code>arg2</code>等参数。</p>
<pre><code class="language-javascript">输入:人工智
输出:能
输入:中国的首都是
输出:北京
输入:今天天气适合在家
输出:打游戏
很多组输入和输出...
</code></pre>
<p>有没有觉得这个和小学的方程式很像,一堆输入,一堆输出,然后求解方程式里面的未知数,这些未知数就是模型的参数。</p>
<p><strong>其实训练模型的过程就是计算求解这些参数的过程!!</strong> 每一次的训练都会去不断的更新这些参数,直到模型吐出的结果满足我们的预期。</p>
<h1 id="训练模型会经历哪些阶段">训练模型会经历哪些阶段</h1>
<p>训练一个模型会经历三个阶段,对应下面的这张图的三个阶段:</p>
<p><img alt="three" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213130990-575723138.png" class="lazyload"></p>
<p>第一张图为:自我学习,累计实力(<strong>预训练 Pre-training</strong>)</p>
<p>第二张图为:名师指点,发挥潜力(<strong>监督微调 SFT</strong>)</p>
<p>第三张图为:参与实战,打磨技巧(<strong>强化学习 RLHF</strong>)</p>
<p>这三个阶段都是大模型在学习如何进行文字接龙,只是训练的资料不同。</p>
<h2 id="第一阶段自我学习累计实力预训练-pre-training">第一阶段:自我学习,累计实力(预训练 Pre-training)</h2>
<p>在讲预训练之前,我们先来聊聊在进行预训练之前的模型是什么样的?</p>
<p>一个大模型的结构大概是下面这样的:<br>
<img alt="structure" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213146297-1206754570.png" class="lazyload"></p>
<p>主要就是很多个transformer堆叠在一起,并且像ChatGPT这种生成式大模型他是仅解码器模型,没有编码器模块(这段话看不懂就算了,后面单独有文章讲transformer)。</p>
<p>用户的输入,也就是transformer模块的输入,然后每个transformer模块会输出一个结果,然后将上一个transformer模块的输出作为下一个transformer模块的输入,最后输出一个结果,这个结果就是大模型的输出结果。</p>
<p>任何文字资料都可以作为这一阶段上面的训练资料,所以这一阶段会大量爬取网络上面的文本用于模型的训练。</p>
<p>所以在这一阶段会进行大量的数据进行训练(99%的训练数据都是在这一阶段完成的),当训练数据的量达到某个临界点后,模型就能够理解人类语言的含义(至于为什么模型能够理解人类语言的含义,目前没有人能够说清楚)。</p>
<p>这一阶段的训练肯定能够训练出一个大模型吗?</p>
<p>答案是:不一定。除了训练模型找出来的参数之外还有一些人工设置的参数,这些参数我们称之为<strong>超参数</strong>。这些超参数都是通过人工设置的,在模型训练过程中也不会改变,比如:</p>
<ul>
<li>模型的层数</li>
<li>模型的维度</li>
<li>模型的头数</li>
</ul>
<p>当在训练模型时失败了(没有找到参数符合训练资料),就需要手动调整超参数。</p>
<p>在进行预训练之前,模型的输出是随机的。比如你输入:<code>中国的首都是</code>,模型会输出:<code>你好吖</code>。明显输入和输出是没有任何关联的。</p>
<p>经过预训练后的模型是基础模型,这种模型的名字包含一个<code>Base</code>字段。<br>
<img alt="base" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213200886-1884782853.png" class="lazyload"></p>
<p>经过预训练后,模型突然像开窍了一样能够理解人类语言的含义。这个时候的模型只会成语接龙,没有接受人类的调教,所以他不能回答你的问题,他只会抛出更多的问题给你。如图:</p>
<p>我在colab上面跑的一个千问3的4B Base模型,输入是:<code>中国的首都是什么?</code></p>
<p><img alt="base-ask" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213216013-1675540483.png" class="lazyload"></p>
<p>从图中可以看到此时模型的输出奇奇怪怪的,他没有回答我的问题,反而抛出更多的问题。</p>
<p>我又跑了一个千问3的4B模型,同样输入是:<code>中国的首都是什么?</code><br>
<img alt="qa" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213231388-2015128581.png" class="lazyload"></p>
<p>从图中可以看到同样都是4B的千问3模型,经过微调后不仅拥有了思考能力(think标签就是模型思考的过程),并且还能回答我们的问题了。</p>
<h2 id="第二阶段名师指点发挥潜力监督微调-sft">第二阶段:名师指点,发挥潜力(监督微调 SFT)</h2>
<p>经过上个阶段预训练pre-training海量的数据训练后,模型突然就涌现了“智慧”,能够理解人类语言的含义,但是模型只会成语接龙,不会回答你的问题。</p>
<p>所以在这个阶段我们需要训练模型,让他能够学会如何回答你的问题。</p>
<p>在这个阶段,我们只需要收集少量但高质量的数据集。这里的少量是和第一阶段海量的数据集相比是少量,其实也会覆盖各种任务的数据集。</p>
<p>在这一阶段的目标是把模型打造成一个<strong>通才</strong>,让他能够回答各种问题。</p>
<p>模型微调的数据集主要有两种格式:<code>Alpaca</code>和<code>ShareGPT</code></p>
<p>其中<code>Alpaca</code>数据集是下面这样的:</p>
<pre><code class="language-json">[
{
"instruction": "人类指令(必填)",
"input": "人类输入(选填)",
"output": "模型回答(必填)",
"system": "系统提示词(选填)",
"history": [
["第一轮指令(选填)", "第一轮回答(选填)"],
["第二轮指令(选填)", "第二轮回答(选填)"]
]
}
]
</code></pre>
<p><code>ShareGPT</code>数据集是下面这样的:</p>
<pre><code class="language-json">{
"conversations": [
{
"from": "human",
"value": "你好,我出生于1990年5月15日。你能告诉我我今天几岁了吗?"
},
{
"from": "function_call",
"value": "{\"name\": \"calculate_age\", \"arguments\": {\"birthdate\": \"1990-05-15\"}}"
},
{
"from": "observation",
"value": "{\"age\": 31}"
},
{
"from": "gpt",
"value": "根据我的计算,你今天31岁了。"
}
],
"tools": "[{\"name\": \"calculate_age\", \"description\": \"根据出生日期计算年龄\", \"parameters\": {\"type\": \"object\", \"properties\": {\"birthdate\": {\"type\": \"string\", \"description\": \"出生日期以YYYY-MM-DD格式表示\"}}, \"required\": [\"birthdate\"]}}]"
}
</code></pre>
<p>从上面可以看到不管是<code>Alpaca</code>还是<code>ShareGPT</code>本质都是通过问答的形式来训练模型,在数据集中抛出问题,并且给出问题的答案,告诉模型遇见这样的问题就应该这样去回答。</p>
<p>当训练的数据量达到某个临界点时,模型突然就开窍了,不光能够回答我们预设的问题,并且类似的问题也能够回答,有点像举一反三的感觉。</p>
<p>前面我们讲过这一阶段想要将模型训练成一个通才,那么就需要人工标注的高质量一问一答的数据集。</p>
<p>虽然我们前面说过这一阶段所需的数据集比较“少”,但是这里的“少”是和第一阶段预训练相比起来比较少。毕竟第一阶段训练的数据集基本是网络上面能够爬取到的所有资料,比起来当然就少了。</p>
<p>并且在这一阶段的数据还需要是人工标注的高质量数据集,涉及到人工的东西成本都不低。</p>
<p>OpenAI的做法是将其丢给外包公司来人工标注,这个方法需要钞能力的。没有钱怎么办呢?</p>
<p>答案是:可以对ChatGPT做逆向工程,由ChatGPT来帮我们生成微调数据集的问题和答案。</p>
<p>人为的先给一些「训练数据样例」让 ChatGPT 看,</p>
<p>紧接着利用 ChatGPT 的续写功能,让其不断地举一反三出新的训练数据集:</p>
<pre><code>你被要求提供10个多样化的任务指令。这些任务指令将被提供给GPT模型,我们将评估GPT模型完成指令的能力。
以下是你提供指令需要满足的要求:
1.尽量不要在每个指令中重复动词,要最大化指令的多样性。
2.使用指令的语气也应该多样化。例如,将问题与祈使句结合起来。
3.指令类型应该是多样化的,包括各种类型的任务,类别种类例如:brainstorming,open QA,closed QA,rewrite,extract,generation,classification,chat,summarization。
4.GPT语言模型应该能够完成这些指令。例如,不要要求助手创建任何视觉或音频输出。例如,不要要求助手在下午5点叫醒你或设置提醒,因为它无法执行任何操作。例如,指令不应该和音频、视频、图片、链接相关,因为GPT模型无法执行这个操作。
5.指令用中文书写,指令应该是1到2个句子,允许使用祈使句或问句。
6.你应该给指令生成适当的输入,输入字段应包含为指令提供的具体示例,它应该涉及现实数据,不应包含简单的占位符。输入应提供充实的内容,使指令具有挑战性。
7.并非所有指令都需要输入。例如,当指令询问一些常识信息,比如“世界上最高的山峰是什么”,不需要提供具体的上下文。在这种情况下,我们只需在输入字段中放置“<无输入>”。当输入需要提供一些文本素材(例如文章,文章链接)时,就在输入部分直接提供一些样例。当输入需要提供音频、图片、视频或者链接时,则不是满足要求的指令。
8.输出应该是针对指令和输入的恰当回答。
下面是10个任务指令的列表:
###
1. 指令: 在面试中如何回答这个问题?
1. 输入:当你在车里独处时,你会想些什么?
1. 输出:如果是在晚上,我通常会考虑我今天所取得的进步,如果是在早上,我会思考如何做到最好。我也会尝试练习感恩和活在当下的状态,以避免分心驾驶。
###
2. 指令: 按人口对这些国家进行排名。
2. 输入:巴西,中国,美国,日本,加拿大,澳大利亚
2. 输出:中国,美国,巴西,日本,加拿大,澳大利亚
###
3. 指令:
</code></pre>
<p>下面是ChatGPT的回答:<br>
<img alt="chat-gpt" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213248289-527847887.png" class="lazyload"></p>
<p>从图中可以看到ChatGPT已经帮我们生成了一系列的问题和答案。</p>
<p>并且可以结合字节seed团队发布的《Reformulation for Pretraining Data Augmentation》论文生成更多的数据集,论文地址:https://arxiv.org/abs/2502.04235v2</p>
<p>这篇论文的观点是,我们可以通过同一个问题,不同类型的受众就可以有不同的答案。</p>
<p>比如一个问题:<code>什么是大模型</code>,根据不同类型的受众就应该是不同的答案。</p>
<p>这个问题可以映射为:“学术论文-科研人员”、“对话体-老年人”、“教科书-中学生”这三种类型,每种类型都有不同的答案。这样就可以让我们的数据集的规模轻松增加<strong>3</strong>倍。</p>
<p>除此之外还有一些公开的数据集:</p>
<ul>
<li>
<p>HuggingFace(🪜):大模型时代的GitHub,上面除了开源模型之外还有很多开源数据集地址:https://huggingface.co/datasets</p>
</li>
<li>
<p>Kaggle(🪜):Kaggle 是一个数据科学平台,不仅提供大量的公开数据集,还举办各种数据科学竞赛。其数据集涵盖了计算机视觉、自然语言处理、数据可视化等多个领域。地址:https://www.kaggle.com/datasets</p>
</li>
<li>
<p>Google DataSet Search(🪜):一个专门用于搜索数据集的搜索引擎,能够帮助用户找到互联网上公开的数据集。地址:https://datasetsearch.research.google.com</p>
</li>
<li>
<p>awesome-public-datasets:GitHub 上的 awesome-public-datasets 是一个开源项目,汇集了各种主题的高质量公共数据集(不过已经有段时间没更新了)。这些数据集按照主题分类,如农业、生物、气候、计算机网络、教育、金融等。地址:https://github.com/awesomedata/awesome-public-datasets</p>
</li>
<li>
<p>openDataLab:一个专注于中文数据集的平台,也是中国本土最大的开源数据集平台,提供了丰富的中文数据集资源。地址:https://opendatalab.com</p>
</li>
<li>
<p>ModelScope:ModelScope 是阿里巴巴推出的 AI 模型与数据集中心,除了提供预训练模型外,还包含与模型相关的数据集。其实有点类似于中国版的 HuggingFace。地址:https://modelscope.cn/datasets</p>
</li>
</ul>
<p>第二阶段<code>监督微调 SFT</code>其实就是<code>画龙点睛</code>。<br>
<img alt="long" loading="lazy" src="https://img2024.cnblogs.com/blog/1217259/202507/1217259-20250713213303526-424607504.png" class="lazyload"></p>
<h2 id="第三阶段参与实战打磨技巧强化学习-rlhf">第三阶段:参与实战,打磨技巧(强化学习 RLHF)</h2>
<p>经过第二阶段<code>监督微调 SFT</code>后,模型已经能够回答各种问题了,可以当做一个问答助手使用了。</p>
<p>但是还可以进一步提升其回答质量,这是通过强化学习(RLHF)来实现的。这个过程分为两个步骤。</p>
<ul>
<li>奖励模型(Reward Model 简称 RM)</li>
<li>强化学习(Reinforcement Learning 简称 RL)</li>
</ul>
<p>为什么需要RLHF?</p>
<p>回想第二阶段有监督微调SFT,<strong>我们只告诉模型什么是好的数据,但是没有告诉他什么是不好的数据</strong>。</p>
<p>我们通过有监督微调SFT引导模型将在第一阶段预训练pre-training学习到的海量知识在合适的问题下面输出出来。但是因为第二阶段的数据集是人工标注而来的,数据很有限,导致我们对模型的引导能力也有限。</p>
<p>这将导致预训练模型中原先「错误」或「有害」的知识没能在 SFT 数据中被纠正,</p>
<p>从而出现「有害性」或「幻觉」的问题。</p>
<p>所以才需要第三阶段强化学习RLHF来解决这些问题,告诉模型什么是好的数据,什么是不好的数据。</p>
<p>我们先来看看什么是奖励模型RM?</p>
<p>奖励模型RM是一个单独训练的一个模型,这个模型用于给第二阶段SFT训练出来的模型的输出打分。</p>
<p>注意,这里的打分不是真的打分,本质上排列一个好坏的先后顺序。</p>
<p>就像是给作文打分一样,不同的人打的分都不相同。</p>
<p>但是如果改成根据内容的好坏进行排序就很容易了,比如作文1、作文2、作文3,不同的人排序大概率都相同。</p>
<p>奖励模型RM本质依然还是给不同的输出进行好坏的排序,然后根据这些排序利用【分差变化】计算出每一个输出的得分。</p>
<p>根据第二阶段SFT训练出来的模型根据不同的temperature值生成多个输出,然后使用奖励模型RM对这些输出进行打分。</p>
<p>接着就是强化学习RL,其实就是利用每个输出的打分反过来影响模型的参数。通过调整参数让打分较高的输出出现的概率更大,打分更低的输出出现的概率更小。</p>
<p>为什么需要奖励模型RM?</p>
<p>并不是所有人在使用ChatGPT时会对输出进行“好”或者“坏”的评价,所以需要一个奖励模型RM来模拟人类的反馈,人类觉得不错的输出奖励模型就会给出高的得分,反之则给出低的得分。</p>
<h2 id="总结">总结</h2>
<p>这篇文章我们讲了训练出一个ChatGPT需要经历三个阶段:</p>
<ul>
<li>第一阶段:自我学习,累计实力(预训练 Pre-training)</li>
<li>第二阶段:名师指点,发挥潜力(监督微调 SFT)</li>
<li>第三阶段:参与实战,打磨技巧(强化学习 RLHF)</li>
</ul>
<p>在第一阶段预训练Pre-training阶段,大模型通过网络上面大量的数据资料进行自我学习(那些参数很大的模型基本都将网络上面的资料全部学完了),最终在某个临界点时突然就开窍了理解了人类语言的含义。但是此时模型只会文字接龙,不会回答你的问题。</p>
<p>这一步我理解为<strong>量变引起质变,大力出奇迹。</strong></p>
<p>在第二阶段需要一个老师来指导大模型应该如何利用第一阶段学习的知识回答问题,所以需要一些人工标注的高质量数据集来对大模型进行监督微调SFT,通过这些高质量数据集模型学会了如何回答问题。</p>
<p>这一步我理解为<strong>画龙点睛。</strong></p>
<p>在前面的阶段我们只告诉模型什么是好的数据,但是没有告诉他什么是坏的数据。所以在第三阶段我们需要去训练一个奖励模型RM来对模型的输出进行打分,通过打分来影响模型的输出,让得分高的输出出现的概率更大,得分低的输出出现的概率更小。</p><br><br>
来源:https://www.cnblogs.com/heavenYJJ/p/18982861
頁:
[1]