关于这节课
-
这节课主要是带大家简单了解下深度学习和大模型的基础知识,包括它们是怎么工作的。我自己也不是专门研究深度学习的,所以只能讲些比较基础的内容。如果你对模型训练感兴趣,可以看看这些资料:
-
课程的完整代码会在 gitee 的仓库提供下载,走过路过麻烦帮忙点个赞哇。
什么是智能?
-
通过收集信息并针对不同场景做出适应性响应的能力。
-
比如生物智能
- ✅狗听到指令会坐下(听到声音→做出动作)
- ✅ 草履虫会躲开盐水(尝到味道→改变方向)
- 注:智能≠复杂,关键在于「有效反馈」
-
重点在于能"正常反应",如果一个生物对外界没反应:要么已经G了(生命结束智能也就没了),要么感知出问题了(比如对聋人喊话肯定没用)
什么是人工智能
-
在计算机中搭建一套【根据情况做出合适反应】的系统,系统的输出可以是动作 / 语言 / 判断 / 预测。比如
- 人脸识别:输入是【图片】,输出是【不同人的身份信息】
- AlphaGo:输入是【对方棋手选择的棋盘坐标】,输出是【胜率最高的下法】
- 运动轨迹追踪:输入是【视频】,输出是【视频中的追踪对象的移动轨迹】
- 问答系统:输入是【问题】,输出是【带参考文献的答案】
-
如果一个负责人脸识别的 ai 把所有人脸都识别成[特朗普],AlphaGo 把围棋当成象棋来下,那我们就会认为这特喵是个人工“智障”而非人工智能。
-
智能的本质就是一个不会乱来的黑箱,世界上的一切都可以用函数来描述,实现人工智能从数学的角度上,就是找到一个对应的函数,接收输入,映射成我们想要的输出。拟合函数的过程,在机器学习和深度学习中称为训练。
-
output = predict(input)
- 注:训练是个一锤子买卖,经过训练完我们就得到了一个固定参数结构和值的模型文件,模型运行起来的过程属于「推理」阶段。
-
深度学习
-
先设计网络结构(用什么类型的神经网络):比如 CNN、Transformer,定义层数、激活函数、连接方式等。
-
收集与处理数据:获取多样化数据,进行标注、增强和划分(训练集/验证集/测试集)。
-
训练与优化:
- 前向传播计算预测值,让网络先自己做一遍预测,通过损失函数衡量误差。
- 反向传播计算梯度,通过梯度下降的方式找到使得损失函数最小的参数值,逐步减少误差。
- 通过迭代调整参数,使网络逐步逼近数据的内在规律,反复这个过程直到表现够好
-
训练最终目的是拟合出诸如
f(x) = w1*x1 + w2*x2 + ... + wn*xn
的预测函数- 其中:
w1~wn
是训练得到的参数,而函数的计算方式(即网络架构)是在训练前就预先设计好的(如全连接层、卷积操作等)。训练过程本质上就是通过优化算法寻找这些参数的最优值。 - 注:神经网络通过多层非线性激活函数的叠加,形成复杂函数映射(例如
f(x) = ReLU(W3·ReLU(W2·ReLU(W1·x + b1) + b2) + b3)
),而非简单的多项式,这里只是为了方便讲解使用了一个简单的多项式。
- 其中:
-
作者对模型训练的过程和相关数学推导不能说是熟练掌握,简直可以说是一无所知,本系列文章主要也是从工程角度出发去讨论如何更好地使用大模型实现我们的自动化,对深度学习和模型研发感兴趣的可以自行在 b 站或者datawhale 这些技术公众号自行了解!
大语言模型
大语言模型的本质
-
大语言模型(Large Language Model, LLM)是基于 Transformer 架构的深度神经网络系统,通过海量文本数据训练实现语言理解与生成能力。其核心特征包含:
- 超大规模参数:参数规模达千亿级(如GPT-3 175B参数),相当于构建万亿维空间的映射函数
- 自回归生成机制:基于上下文逐词预测输出,类似动态规划算法中的状态转移过程
- 注意力机制:通过自注意力(Self-Attention)实现长程依赖建模,解决传统RNN的序列信息衰减问题
- 涌现能力(Emergent Abilities):参数超阈值后突现未显式训练的技能(如逻辑推理、代码生成)
大语言模型工作原理:文字接龙游戏
1. 一切输入都是 Token(接龙的基本单位)
-
无论是文字、图片还是视频,模型都会通过分词器将其拆解成最小的 Token(就像接龙游戏里的“字”或“词”):
- 文本 → 拆分成词语或子词(如“你好”→
["你", "好"]
) - 图片 → 拆分成像素块或图块
- 视频 → 拆分成帧序列
- 一个能同时处理文本/图片/视频多种输入的模型我们称之为【多模态大模型】
- 文本 → 拆分成词语或子词(如“你好”→
-
不断预测下一个最可能的 Token,就像玩文字接龙时,你根据前面的词猜下一个词:
-
- 输入 Token 流: (如
["今天", "天气"]
)
- 输入 Token 流: (如
-
- 嵌入层: 把每个 Token 变成高维向量(让计算机能计算)
-
- 注意力层(核心计算):多层神经网络分析 Token 之间的关系(比如“天气”后面更可能接“晴朗”而不是“吃饭”)找出上下文中的隐藏规律
-
- 输出概率:计算所有可能 Token 的概率(如
"晴朗"=70%
,"多云"=20%
,"下雨"=10%
…)
- 输出概率:计算所有可能 Token 的概率(如
-
- 选择 Token:按照几种常见的策略进行接龙(如
→ "晴朗"
),形成新序列["今天", "天气", "晴朗"]
,
-
确定性搜索
-
贪心搜索:仅选择当前概率最高的词元。例如,若输入“今天天气”,模型预测“晴朗”概率最高(如0.9),则必选该词生成“今天天气晴朗”。
-
束搜索:通过维护多个候选序列(束宽k)并计算其联合概率,最终选择全局最优路径,当 k = 1 时会退化为贪心搜索
-
-
随机性采样策略
-
Top-k 采样:从概率最高的 K 个候选词中随机选择,平衡多样性与质量,K值越大,多样性越高但可能引入低质量词,比如当
k = 100
时,会从前 100 个候选词中随机采样,假设第 100 个词蟑螂的概率仅为 0.0000001,那么选中它就很有可能影响整体文章的质量。比如,我的宠物是一只 蟑螂。 -
Top-p 采样(核采样):动态选择累积概率超过阈值p的最小词集合,解决Top-K固定候选数的问题,比如候选词元中,[ 晴天 = 0.5 ,多云 = 0.3 ,下雨 = 0.1, 下冰雹 = 0.01 ],若 p = 0.9,那么只会在[ 晴天,多云,下雨 ]进行选取,因为他们的概率之和 0.5 + 0.3 + 0.1 = 0.9。
-
温度采样:通过温度参数T调整概率分布的尖锐度,通过调整 温度参数(T) 控制概率分布的平滑度:
- 高温(T > 1):放大低概率词的可能性,使分布更均匀(如“多云”概率0.1→0.25),生成更具创造性,更天马行空的文本。
- 低温(T < 1):强化高概率词(如“晴朗”概率0.9→0.95),输出更保守(如医疗报告中“CT检查显示肺部结节”必须准确)
- T = 0 时退化成为贪心搜索。
-
-
随着模型的迭代,新型模型(如 DeepSeek)已支持根据上下文复杂度自动调整温度
- 检测到专业术语时自动降温(T-0.2)
- 识别隐喻需求时升温(T+0.5)
- 摆弄采样参数对模型生成内容的效果会越来越小,除了特定场景(比如在医疗诊断等专业场景,强制锁定低温(T=0.1)仍有必要,以避免自动升温导致风险),大部分场景不需要过多关注。
- 选择 Token:按照几种常见的策略进行接龙(如
-
- 循环预测:重复上述过程,直到遇到停止符(如句号、换行、内部终止符或最大长度)
-
-
如果用户输入 + 模型输出的 token 总长度超过了模型的最大长度,模型工作的效果就会大打折扣,甚至无法工作。以 kimi 为例,一个会话的 token 总长度如果超过 20w 字,web服务会拒绝请求模型服务。
-
2. 为什么能“接”得合理?
-
训练经验:
-
预训练:通过海量无标注文本(如维基百科、新闻、书籍)学习语言统计规律,建立词汇共现关系(如“抗原”常与“抗体”“免疫”共现)、句法结构(主谓宾嵌套)及语义逻辑(因果、转折)。
-
后置训练:
- 监督微调(SFT):在预训练模型上注入少量高质量标注数据(如问答对、指令响应),强化特定任务的知识对齐(如法律文书生成需遵循法条引用规范,比如学位论文固定的格式)
- 人类反馈强化学习(RLHF):通过奖励模型量化生成质量(如逻辑性、无害性),引导模型优化输出策略(避免生成“太阳从西边升起”等违背常识的语句)
-
-
注意力机制:
- 自注意力(Self-Attention):计算词间关联权重,解决长距离依赖问题。例如在“科学家发现了一种新型蛋白质[Token A],其结构[Token B]可特异性结合癌细胞表面受体[Token C]”中,模型通过注意力权重聚焦“蛋白质→结构→受体”的语义链。
- 多头机制:并行捕捉不同层次的关联(如词性、语义角色),提升对复杂表达的建模能力(如医学文献中同时处理“基因突变频率”与“临床表现相关性”)。
-
概率驱动:
- 通过上边介绍的确定性选择或者采样策略,选择概率大的 token 进行拼接。
-
可以说,通过学习大量的文本资料,模型一定程度上理解了人类的语言,并且能够以最大概率的方式生产出一段文本,这段文本往往也是人类希望得到的。这里其实可以引申到无限猴子理论(在无限时间或无限数量的猴子随机敲击打字机的情况下,几乎必然能够生成任何特定文本(如莎士比亚全集))
-
而大模型是一只具有统计先验的超级猴子,它基于概率学生成的内容比较符合人类的预期,也不需要无限的时间去试错就能生成“莎士比亚全集”。
大语言模型的运行时
-
大模型是怎么运行起来的?
-
以 deepseek-r1 为例,满血版的 deepseek-r1 模型文件的大小就高达 600 gb
-
Hugging-face 是一个类似github的模型开源社区,我们可以在上边找到各种开源的模型
-
-
类比我们常用的 MYSQL,如果我们使用了 innodb 作为存储引擎,idb 文件就是我们的数据文件,但是真正提供数据访问/存储服务的是 mysqld 进程
-
因此模型能工作起来,也需要一个类似 mysqld 的进程(如
transformers
库或vLLM 引擎
),这个进程负责- 加载模型架构定义
- 将参数文件按神经网络结构映射到内存
- 管理计算图执行流程(前向传播/自回归生成),借助 gpu / cpu 完成运算,进行文字接龙游戏。
-
-
如何在本地部署一个模型呢?
- Ollama 是一个 本地化大模型运行引擎,类似 MySQL 的
mysqld
进程,负责加载模型文件、管理 GPU/CPU 计算资源,并提供简单的 API 和命令行交互。 - 因为大部分个人 pc 的算力都比较有限,我们将使用 ollama 在本地部署一个小小模型作为演示
- Ollama 是一个 本地化大模型运行引擎,类似 MySQL 的
-
安装 Ollama
-
我们将使用 docker 的方式进行安装,以便跨不同操作系统也可以保障操作的一致性,各位同学可先通过www.docker.com 完成docker 的安装
-
使用下面的指令拉取下载镜像,本项目只需在CPU上即可运行。
-
docker pull ollama/ollama
-
-
-
运行 ollama 镜像,
docker run -d -p 11434:11434 ollama/ollama
- 执行
docker ps
命令,可以看到 ollama 进程已经成功启动 -
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0f41786d8687 ollama/ollama "/bin/ollama serve" 40 seconds ago Up 40 seconds 0.0.0.0:11434->11434/tcp elated_buck
- 执行
-
使用
docker exec -it {docker ps命令输出的CONTAINER ID} /bin/sh
命令 连接容器内的 shell 环境,使用ollama pull qwen2:0.5b
命令安装超小的 Qwen2-0.5B。- 注:B 是 billion 的意思,即是十亿,0.5b 就代表有 5 亿个参数,我们的 deepseek-r1 满血版是 671B,足足有 6710 亿个参数,可以理解为什么他们叫“大”模型了吧!
-
# ollama pull qwen2:0.5b pulling manifest pulling 8de95da68dc4... 100% ▕███████████████████████████████▏ 352 MB pulling 62fbfd9ed093... 100% ▕███████████████████████████████▏ 182 B pulling c156170b718e... 100% ▕███████████████████████████████▏ 11 KB pulling f02dd72bb242... 100% ▕███████████████████████████████▏ 59 B pulling 2184ab82477b... 100% ▕███████████████████████████████▏ 488 B verifying sha256 digest writing manifest success
-
使用
ollama run qwen2:0.5b
,加载 qwen2:0.5b 开始对话服务- ps:这个模型实在是太小了,幻觉现象非常严重,招笑。
-
ollama 还提供了一系列 restful api,对于web应用的构建来说相当重要
-
回答接口(流式)
- 基于 SSE 协议实现服务端 => 客户端的流式数据传输,一般聊天应用都需要使用流式传输异步渲染模型生成的文本,提高用户体验
-
回答接口(非流式)
- 同步等待模型完成本次推理任务,获得完整回复。
-
使用 deepseek api 构建一个命令行对话的demo
-
从上边的学习中,我们可以知道我们可以通过 http 的方式来访问我们的模型服务,接下来我要介绍一下如何用 deepseek 实现一个基于命令行的对话 demo,我们先进行前置准备
-
Deepseek api_key 申请
-
登录 deepseek 开放平台,完成注册
-
选择左侧的 api-keys,设置一个 api_key 并保存到本地
-
学生认证可以获得 20元的免费额度,自己进行学习的话可以尝试充值 1-2元用于验证即可。当然也可以参考上边的 ollama 自己部署一个小模型用于联调也可以的
-
-
Deepseek 提供的 restful api 遵循 openai 的规范,因此我们可以直接安装 open-ai 的sdk直接发起请求
-
pip3 install openai
-
-
开始写代码!
-
model.py 用于创建 deepseek client
# 初始化 openai client import os from openai import OpenAI # 从环境变量中获取 token api_key = os.environ.get("DEEPSEEK_API_KEY") client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com") ```
-
process_messages_without_context.py 用于实现对话流程
import sys from deepseek_api_intergration.model import client while True: try: user_input = input("User: ").strip() if user_input.lower() in ["quit", "exit", "q"]: print("Goodbye!") break print("AI: ", end="", flush=True) # 流式调用API response_stream = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": user_input}], stream=True, temperature=1.0, top_p=0.9, ) full_response = [] for chunk in response_stream: if chunk.choices and chunk.choices[0].delta.content: content = chunk.choices[0].delta.content print(content, end="", flush=True) # 逐字打印 full_response.append(content) print() # 换行 except KeyboardInterrupt: print("\nInterrupted by user") break except Exception as e: print(f"\nError: {str(e)}", file=sys.stderr) continue ```
-
cd 到项目根路径,执行
python3 deepseek_api_intergration/process_messages_without_context.py
启动程序-
注:若提示下边的错误,请使用
export PYTHONPATH="${你的本地目录}/llm_dev_learning" 设置 PYTHONPATH
以便让 PYTHON 解释器找到自定义模块路径哦。-
Traceback (most recent call last): File "/Users/bytedance/python/llm_dev_learning/deepseek_api_intergration/main.py", line 6, in <module> from deepseek_api_intergration.model import get_client ModuleNotFoundError: No module named 'deepseek_api_intergration'
-
-
-
-
-
细心的同学可能注意到,演示截图中更名"小黑"后,模型仍自称是"Deepseek Chat"。
-
回顾上文关于大模型工作模式的介绍,我们重新复习一下,大模型本质是token序列的文字接龙。 如果,输入文本不包含历史信息,模型就无法"记住"过往对话
-
这就是模型大脑和人类大脑的差异了
- 我们人类并不总是需要在对话中强调过去已经发生过的事情
- 比如小伙伴周一约你周末去打篮球,等到了周五的时候,他和你说周末临时有事他去不了了,他并不需要告诉你,“我周一约的你周末打篮球,但是我这周末有事情去不了了”,只要你的记性不太差,只要你听到“我这周末突然有事情,应该没办法去打球了”,你就能联想发生在周一的邀约,我们的大脑是有记忆功能的。
- 但是模型文件本身并没有这个“记忆”功能,模型需每次完整提供对话历史,才能实现连贯响应。而人类可通过记忆关联信息。所以看吧,模型真的就只是一个无状态的函数,你的输入是 token 序列,它的输出也是一串 token。
-
接下来我们来实现一个有历史消息的版本来看下效果
-
实现一个简单的上下文管理器
-
class MessageManager: def __init__(self): self.messages = [] def add_sys_message(self, message: str): self.messages.append({"role": "system", "content": message}) def add_user_message(self, message: str): self.messages.append({"role": "user", "content": message}) def add_ai_message(self, message: str): self.messages.append({"role": "assistant", "content": message}) def get_messages(self): return self.messages
-
-
在发消息以前记录用户信息,在模型响应以后记录模型的回复
-
while True: try: user_input = input("User: ").strip() if user_input.lower() in ["quit", "exit", "q"]: print("Goodbye!") break print("AI: ", end="", flush=True) manager.add_user_message(user_input) # 流式调用API response_stream = client.chat.completions.create( model="deepseek-chat", messages=manager.get_messages(), stream=True, temperature=1.0, top_p=0.9, ) full_response = [] for chunk in response_stream: if chunk.choices and chunk.choices[0].delta.content: content = chunk.choices[0].delta.content print(content, end="", flush=True) # 逐字打印 full_response.append(content) manager.add_ai_message("".join(full_response)) print() except KeyboardInterrupt: print("\nInterrupted by user") break except Exception as e: print(f"\nError: {str(e)}", file=sys.stderr) continue
-
-
执行
python3 deepseek_api_intergration/process_messages_with_context.py
启动程序,开始对话- 完美,deepseek 真的记住了自己叫“小黑了”
-
后续安排
接下来我们会用 LangGraph 和 SmolAgents 这两个框架,一起做些有意思的小项目。通过实际动手,你会更清楚怎么把大模型用到实际工程中,比如:
- 学习如何使用 FUNCTION_CALL / MCP 来实现 agent
- 学习现在特别火的RAG技术
- 深入看看这些框架是怎么设计的
我们一起边学边做,应该会很有意思!