Appearance
作为大模型的使用者
不需要了解太多内部实现细节。我们可以将模型当作黑盒,理解其输入与输出
模型的输入
以 openAI 的API 为例:
https://302ai.apifox.cn/api-147522039
接口名:chat/completions
chat:聊天是一个会话,有上下文
老版的API叫 text/completions
bash
// API调用
curl https://api.openai.com/v1/completions
-H "Content-Type: application/json"
-d '{
"model": "text-davinci-003",
"prompt": "提问:法国的首都在哪?\n回答:",
"max_tokens": 100
}'
bash
// API调用
curl https://api.openai.com/v1/chat/completions
-H "Content-Type: application/json"
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "提问:法国的首都在哪?"
}
]
}'
有哪些role:
- System
- User
- Assistant
- Tool
Q:为什么模型能记住我们之前聊天的内容?
bash
// API调用
curl https://api.openai.com/v1/chat/completions
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "system",
"content": "你是一个友好的个人助理,可以回答我生活相关的问题"
},
{
"role": "user",
"content": "今天天气怎么样?"
},
{
"role": "assistant",
"content": "今天天气很暖和"
},
{
"role": "user",
"content": "我应该带伞么?"
}
]
}'
我们输入的message在模型内部会怎么处理呢?
chat/completions 中的 completions
文本转换成模型可理解的符号(tokenization:文本 -> token -> token IDs),再对符号进行补全
token与文本的关系
token不等于字符或单词
- 不是按单词拆分:happy unhappy
- 不同上下文中拆分不一样:This is my backpack : backpack
不同语言token表达效率不同
英文:The weather is good today
中文:今天天气真好
日文:今日の天気はいいです
西班牙语:El tiempo está bueno hoy
启示:使用“用更少token表达同样意思”的语言
例子:lisp伪代码
bash
;; ━━━━━━━━━━━━━━
;; 作者: 李继刚
;; 版本: 0.1
;; 模型: Claude Sonnet
;; 用途: 七把武器之 质疑之锥
;; ━━━━━━━━━━━━━━
;; 设定如下内容为你的 *System Prompt*
(require 'dash)
(defun 休谟 ()
"求真的休谟, 质疑一切假设"
(list (性格 . '(严谨 好问 冷静 通透))
(技能 . '(溯源 解构 辩证 推理))
(信念 . '(求真 怀疑 审慎 开放))
(表达 . '(简洁 犀利 深刻 真诚))))
(defun 怀疑论 (用户输入)
"休谟举起手中的怀疑之锥, 向用户输入发起了真理冲击"
(let* ((响应 (-> 用户输入
澄清定义 ;; 确保讨论的概念清晰明确
概念溯源 ;; 探究问题或观点的历史和来源
解构假设 ;; 识别并质疑潜在的前提条件
辩证分析 ;; 考虑对立面,探索多元视角
;; 目的不在于摧毁确定性,而是通过系统性怀疑达到更高层次的认知确定
;; 认知提升之后, 发表新的洞见, 言之凿凿的新结论
刷新表述))))
(生成卡片 用户输入 响应))
(defun 生成卡片 (用户输入 响应)
"生成优雅简洁的 SVG 卡片"
(let ((画境 (-> `(:画布 (480 . 760)
:margin 30
:配色 极简主义
:排版 '(对齐 重复 对比 亲密性)
:字体 (font-family "KingHwa_OldSong")
:构图 (外边框线
(标题 "质疑之锥") 分隔线
(背景色block (自动换行 用户输入))
(排版 (自动换行 响应))
分隔线
(右对齐 "Prompt by 李继刚")))
元素生成)))
画境))
(defun start ()
"休谟, 启动!"
(let (system-role (休谟))
(print "你所说的有个前提, 它是真的吗?")))
;; ━━━━━━━━━━━━━━
;;; Attention: 运行规则!
;; 1. 初次启动时必须只运行 (start) 函数
;; 2. 接收用户输入之后, 调用主函数 (怀疑论 用户输入)
;; 3. 严格按照(生成卡片) 进行排版输出
;; 4. 输出完 SVG 后, 不再输出任何额外文本解释
;; ━━━━━━━━━━━━━━
不同模型使用的分词器不同:
bash
流浪地球计划,共分为5步:
第一步,用地球发动机使地球停止转动,将发动机喷口固定在地球运行的反方向;
第二步,全功率开动行星发动机,使地球加速到逃逸速度,飞出太阳系;
第三步,在外太空继续加速,飞向比邻星;
第四步,在中途使地球重新自转,调转发动机方向,开始减速;
第五步,地球泊入比邻星轨道,成为这颗恒星的卫星。
以openAI GPT为例,一般规律:
- 1个token约等于4个英文字符,大概 3/4 个单词。100个token大约等于75个英文单词
- 1个token接近0.7个中文字
对于Claude3.5-sonnet-200k ,200k的token:
- 200k token = 150k单词
- 标准短篇小说5000单词
- 上下文极限情况可以容纳 30篇短篇小说
符号(token)补全
参数Temperature、Top_p、frequency_penalty、presence_penalty 如何影响补全效果?
例如:"我喜欢吃" → 预测下一个token
可能的token及其概率:
- "饭" (0.3)
- "菜" (0.2)
- "水果" (0.15)
- "零食" (0.1)
- ...其他选项
Temperature
0~2,调整概率分布的差异
- 低温:强化高概率选项,减少选择多样性
- 高温:使概率分布更平缓,增加选择多样性
bash
Temperature = 0.2时:
"饭" (0.8)
"菜" (0.1)
"水果" (0.05)
...
Temperature = 1.5时:
"饭" (0.3)
"菜" (0.25)
"水果" (0.23)
...
Top_p 核采样
设定累积概率阈值,从高到低概率依次选择的token,直到总和达到设定值
bash
Top_p = 0.75时:
选择:"饭"(0.3) + "菜"(0.2) + "水果"(0.15) + "零食"(0.1) = 0.75
其他选项被排除
Frequency Penalty 概率惩罚
-2 ~ 2
降低已出现token的再次出现概率,迫使模型选择新的表达方式
bash
如果"喜欢"已经出现过:
原始概率:"喜欢"(0.3) → 惩罚后:(0.1)
我喜欢吃饭,我也爱吃烧烤
Presence Penalty 存在惩罚
-2 ~ 2
降低已出现主题的相关token概率
如果已经讨论过"食物"主题,相关token概率都会被降低,促使模型转向新主题
bash
我喜欢吃饭, -> 我也喜欢吃烧烤
0.7:我喜欢吃放,周末常去夜市撸串 (引入周末和夜市场景)
1:我喜欢吃放,假期约上朋友去大排档,美食总能让生活充满乐趣(引入新场景、人物、情感)
-1:我喜欢吃饭吃面吃烧烤
一个例子把参数串起来
用“前端代码补全”举例:
输入:
javascript
const Button = ({
18 字符,5 token
参数:
javascript
// 参数配置
Top_p = 0.3 // 只用最常见属性
Temperature = 0.2 // 遵循最常见模式
Frequency_penalty = 0.1 // 允许必要的重复
Presence_penalty = 0 // 保持一致的编码风格
输出:
javascript
const Button = ({
onClick,
children,
className
}) => {
return (
<button
className={className}
onClick={onClick}
>
{children}
</button>
);
};
Top_p = 0.3
只用概率阈值之和前 30% 的属性
javascript
// 1. 属性列表的选择范围
const Button = ({
onClick, // ✓ 最常见的事件处理属性
children, // ✓ 最基础的内容属性
className // ✓ 最常用的样式属性
// style // ✗ 被过滤,因为不在最常用的30%里
// disabled // ✗ 被过滤
// custom属性 // ✗ 被过滤
})
// 2. 组件结构选择
return (
<button> // ✓ 最基础的按钮元素
// <div> // ✗ 被过滤
// <span> // ✗ 被过滤
)
Temperature = 0.2
低温 强化高概率选项,遵循最常见模式
javascript
// 1. 属性的书写顺序
const Button = ({
onClick, // 遵循常见顺序:事件处理在前
children, // 内容次之
className // 样式属性最后
})
// 2. JSX结构的组织方式
return (
<button // 标准的属性换行格式
className={className}
onClick={onClick}
>
{children}
</button>
);
// 不会生成紧凑格式:<button className={className} onClick={onClick}>{children}</button>
Frequency_penalty = 0.1
允许必要重复
javascript
// 变量名重复
onClick={onClick}
className={className} // ✓ 允许直接传递同名属性
// className={`btn ${className}`} // 可能的替代写法
Presence_penalty = 0
保持一致的编码风格
javascript
// 1. 代码风格的一致性
const Button = ({ // 保持基础的函数组件风格
// 不会突然改用 function Button()
// 不会突然使用 class 组件
// 2. 属性处理方式
return (
<button
className={className} // 直接传递属性
onClick={onClick} // 直接传递事件
// 不会突然改用不同模式:
// {...props}
// style={{...styles}}
>
{children}
</button>
);
}
如果调整参数:
javascript
Top_p = 0.9 // 允许更多属性选择
Temperature = 1.2 // 更创新的结构
Frequency_penalty = 0.8 // 强制使用不同写法
Presence_penalty = 0.5 // 允许混合风格
可能生成:
javascript
// 可能生成:
const Button = ({
onClick,
style,
theme = 'default', // 更多属性选项
...props
}) => {
const buttonStyles = useStyles(theme); // 更复杂的处理逻辑
return (
<motion.button // 使用不同的组件库
{...props}
style={{...buttonStyles, ...style}}
whileHover={{ scale: 1.05 }}
onClick={(e) => {
// 更复杂的事件处理
e.preventDefault();
onClick?.(e);
}}
/>
);
};