Skip to content

作为大模型的使用者

不需要了解太多内部实现细节。我们可以将模型当作黑盒,理解其输入与输出

模型的输入

以 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:

  1. System
  2. User
  3. Assistant
  4. 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),再对符号进行补全

openAI tokenizer

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 后, 不再输出任何额外文本解释
;; ━━━━━━━━━━━━━━

不同模型使用的分词器不同:

通义千问分词器 vs openAI分词器

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);
      }}
    />
  );
};