导航菜单

  • 1.vector
  • 2.milvus
  • 3.pymilvus
  • 4.rag
  • 5.rag_measure
  • 7.search
  • ragflow
  • heapq
  • HNSW
  • cosine_similarity
  • math
  • typing
  • etcd
  • minio
  • collections
  • jieba
  • random
  • beautifulsoup4
  • chromadb
  • sentence_transformers
  • numpy
  • lxml
  • openpyxl
  • PyMuPDF
  • python-docx
  • requests
  • python-pptx
  • text_splitter
  • all-MiniLM-L6-v2
  • openai
  • llm
  • BPETokenizer
  • Flask
  • RAGAS
  • BagofWords
  • langchain
  • Pydantic
  • abc
  • faiss
  • MMR
  • scikit-learn
  • 1. 什么是大语言模型?
  • 2. 大语言模型的基本工作原理
    • 2.1 核心原理:文字接龙
    • 3.2 大模型如何决定生成哪个字?
  • 4. Token 和词表(Vocab)
    • 4.1 什么是 Token?
    • 4.2 Token 是如何生成的?——分词(Tokenization)
    • 4.3 为什么使用子词(Subword)作为 Token?
    • 4.4 tiktokenizer
      • 4.4.1 什么是Tiktokenizer
      • 4.4.2 功能与用途
      • 4.4.3 网站与操作
    • 4.5 tiktoken
  • 5. 过程
  • 6. 增加参数
    • 6.1 实现
    • 6.2 temperature
    • 6.3 top_p
    • 6.4 采样流程
    • 6.5 测试代码

1. 什么是大语言模型? #

大语言模型(Large Language Model, LLM)是当前人工智能领域最热门的技术之一。你可能已经在使用 ChatGPT、文心一言、Kimi Chat 等 AI 助手时,注意到它们的回答不是一次性全部显示,而是一段一段地"流式"输出。这是为什么呢?

简单理解:

  • 大语言模型就像一个非常聪明的"文字接龙"专家
  • 它能够根据你输入的文字,预测并生成接下来的文字
  • 它通过学习海量文本数据,掌握了语言的规律和世界的知识

为什么叫"大"语言模型?

  • 参数量大:通常有数十亿甚至数千亿个参数(可以理解为模型的"记忆容量")
  • 训练数据大:在数万亿字的文本数据上训练
  • 能力强大:能够理解、生成、推理,完成各种语言任务

常见的大语言模型:

  • ChatGPT:OpenAI 开发,最知名的对话模型
  • GPT-4:OpenAI 的最新模型,能力更强
  • 文心一言:百度开发的中文大模型
  • 通义千问:阿里云开发的大模型
  • Claude:Anthropic 开发的大模型

2. 大语言模型的基本工作原理 #

2.1 核心原理:文字接龙 #

大语言模型的本质工作就是做"文字接龙"。它并不是一次性生成完整答案,而是一个字一个字地逐步生成。

工作流程:

当你输入"什么是大语言模型"这个问题时:

  1. 第一步:模型分析你的问题,生成第一个字"大"
  2. 第二步:模型将原始输入"什么是大语言模型"和刚才生成的"大"一起,再次送入模型
  3. 第三步:模型接着生成第二个字"语"
  4. 第四步:继续这个过程,生成"言"、"模"、"型"等
  5. 第五步:直到生成结束标记,停止生成

为什么这样设计?

  • 每个字的生成都基于前面的所有内容
  • 这样可以保证生成的文本连贯、合理
  • 就像人类说话一样,一个字一个字地说

3.2 大模型如何决定生成哪个字? #

概率模型的工作原理

大模型在生成下一个字时,并不是随机选择的,而是基于概率分布。具体过程如下:

  1. 计算概率:模型会计算所有可能字符的概率分布
  2. 选择输出:根据概率高低选择输出
  3. 随机性:通常概率越高的字符,被选中的机会越大,但也会给其他高概率字符一定的机会

示例说明

假设输入"今天天气很",模型会输出所有可能字符的概率:

  • "好":概率 0.35(最高)
  • "差":概率 0.25
  • "热":概率 0.20
  • 其他字符:概率更低

由于"好"的概率最高,模型通常会选择输出"好"。但需要注意的是,模型并不总是选择概率最高的字符,也会给其他高概率字符一定的输出机会,这就产生了随机性。因此,即使问同一个问题,大模型的回答也可能不完全相同。

温度参数(Temperature)

温度参数控制模型的随机性:

  • 低温度(如 0.1):更确定,总是选择概率最高的字符
  • 高温度(如 1.0):更随机,会给各种字符机会

4. Token 和词表(Vocab) #

4.1 什么是 Token? #

在大语言模型中,我们通常不说"字"或"词",而是说"Token"。Token 是大语言模型处理文本的基本单位。

Token 的定义

Token 是模型处理文本的最小单位,可以是一个字、一个词、或者词的一部分。

为什么使用 Token?

现在几乎所有大语言模型都按照 Token 来计费:

  • GPT-4O:输入 100万 token = 5美元,输出 100万 token = 15美元
  • DeepSeek(国内):输入 100万 token = 1元,输出 100万 token = 2元

Token 与字符的关系:

  • 英文:1 个 token ≈ 0.75 个单词 ≈ 4 个字符
  • 中文:1 个 token ≈ 1-2 个汉字

词表(Vocab)规模

所有可能的 Token 组成一个词表(Vocabulary)。不同模型的词表大小不同:

  • GPT-3:约 5万个 Token
  • 阿里千问2:约 15万个 Token
  • LLaMA 3.1:约 12万个 Token

重要提示:词表中的 Token 并不一定对应一个完整的单词或汉字,可能是单词的一部分(子词)。

4.2 Token 是如何生成的?——分词(Tokenization) #

分词的作用

分词(Tokenization)是将输入的文本转换成一个个 Token 的过程:

  • 输入阶段:文本 → Token 序列 → 模型
  • 输出阶段:模型 → Token 序列 → 文本(逆分词)

为什么需要分词?

  • 模型只能处理数字,不能直接处理文字
  • 需要将文字转换为 Token ID(数字)
  • 模型处理完后再转换回文字

4.3 为什么使用子词(Subword)作为 Token? #

Token 的三种选择

在构建词表时,有三种可能的选择:

  1. 单词(Word):如 "happy", "unhappy"
  2. 字符(Character):如 "h", "a", "p", "p", "y"
  3. 子词(Subword):如 "un", "happy"

为什么不用单词?

使用单词作为 Token 有两个主要缺点:

  1. 词表过大:每个单词有不同的时态、单复数等变化形式,导致词表数量巨大

    • 例如:like, likes, liked, liking 等
    • 词表过大增加模型训练难度
  2. 新词问题(OOV - Out of Vocabulary):

    • 如果出现训练时未见过的词,会被标记为 <UNK>(未知词)
    • 子词可以通过组合多个 Token 来拼出新词,避免 OOV 问题

为什么不用字符?

使用字符作为 Token 也有问题:

  • 虽然词表很小,但字符本身缺乏语义信息
  • 模型需要学习更长的序列才能理解语义,训练难度大

子词的优势

子词(Subword)是介于单词和字符之间的粒度,具有以下优势:

  1. 保留语义:如 "un" 表示否定,"happy" 表示快乐
  2. 组合灵活:可以通过 "un" + "like" 组成 "unlike"
  3. 词表适中:既不会太大,也不会太小

4.4 tiktokenizer #

4.4.1 什么是Tiktokenizer #

Tiktokenizer是一个用于分词(tokenize)的应用程序,能够将输入的文本按照语言规则划分成单个的标记(tokens),与GPT-4o模型相关。它通过简单的网页界面帮助用户快速理解文本在GPT模型中的分词方式,适用于开发者和对自然语言处理感兴趣的用户。

4.4.2 功能与用途 #

Tiktokenizer的核心功能是将文本分割成tokens,这是自然语言处理中的基础步骤。通过这种方式,用户可以直观地看到GPT模型如何解析文本,从而更好地调试和优化模型输入。例如,开发者可以通过它检查不同长度的文本在模型中的token数量,以优化API调用成本

4.4.3 网站与操作 #

Tiktokenizer的网站界面简洁易用,用户只需输入文本即可实时查看分词结果。网站地址为 https://tiktokenizer.vercel.app ,支持多种语言和文本类型,使用户能够快速上手。

4.5 tiktoken #

tiktoken 是 OpenAI 提供的一个高效文本分词(tokenization)库,能够将文本按照 GPT 等语言模型所使用的编码规则精准分割为 tokens。与常规字符分割方式不同,tiktoken 能根据不同模型(如 GPT-3.5、GPT-4)的内置编码方案,把输入文本准确转换为模型可以处理的 token 序列。

主要用途:

  • 精准计算文本 token 数量,方便评估输入是否超出模型限制。
  • 提前了解不同文本在不同模型下的 token 划分差异。
  • 配合文本分割器调整 chunk size,使每段文本适合模型输入限制。

举例说明:

  • 英文句子 "Hello, world!" 经过 tiktoken 编码会被分成几个 token,而不是按字母或空格分割。
  • 中文句子 “今天天气很好” 也会采用模型专用的分词规则,按 token 编码后每个汉字通常为一个 token,但有些词组也可能被合并为单一 token。

tiktoken 的优势:

  • 速度快,占用内存小。
  • 支持多种 OpenAI 模型的编码方式(如 cl100k_base、p50k_base、r50k_base 等)。
  • 便于与 langchain 等库结合,提高文本分割、上下文窗口管理的准确性。

下面我们通过具体代码示例,来演示如何用 tiktoken 对文本进行分词和统计 token 数量:

# 导入 tiktoken 库
import tiktoken

# 获取名为 "cl100k_base" 的编码器(GPT-4 使用的编码方式之一)
encoder = tiktoken.get_encoding("cl100k_base")

# 定义要编码的字符串
text = "Hello, world!"
# 使用编码器对文本进行编码,得到 token 列表
tokens = encoder.encode(text)
# 计算 token 的数量
token_count = len(tokens)

# 打印 token 的总数
print(f"Token数量: {token_count}")
# 打印每个 token 的编号(整数表示)
print(f"Tokens: {tokens}")
# 打印每个 token 对应的原始字节内容
print(f"Token对应文本: {[encoder.decode_single_token_bytes(token) for token in tokens]}")

5. 过程 #

from collections import defaultdict, Counter

class SimpleTokenizer:
    """
    分词器
    功能:
    1. 建立词汇表(单词 -> ID)
    2. 学习每个词后面跟其他词的概率
    3. 根据概率预测下一个词
    """

    def __init__(self):
        # 词汇表:单词 -> ID 的映射
        self.vocab = {}
        # 概率表:每个词后面跟其他词的概率
        # 格式:{词: {下一个词: 概率}}
        self.next_word_probs = {}

    def train(self, texts):
        """
        训练分词器
        步骤:
        1. 收集所有单词,建立词汇表
        2. 统计每个词后面跟的词及其出现次数
        3. 计算概率(次数 / 总次数)
        """
        # ========== 第一步:建立词汇表 ==========
        # 收集所有出现过的单词
        all_words = set()
        for text in texts:
            all_words.update(text.split())

        # 给每个单词分配一个ID(按字母顺序排序)
        self.vocab = {word: i for i, word in enumerate(sorted(all_words))}

        # ========== 第二步:统计词对出现次数 ==========
        # 统计每个词后面跟的词出现了多少次
        # 例如:"hello world" 会记录 "hello" 后面跟 "world" 出现1次
        word_pair_counts = defaultdict(Counter)

        for text in texts:
            words = text.split()
            # 遍历相邻的两个词
            for i in range(len(words) - 1):
                current_word = words[i]
                next_word = words[i + 1]
                # 记录:current_word 后面跟 next_word 出现1次
                word_pair_counts[current_word][next_word] += 1

        # ========== 第三步:计算概率 ==========
        # 对于每个词,计算它后面跟其他词的概率
        for word, next_word_counts in word_pair_counts.items():
            # 计算这个词后面跟的所有词的总次数
            total_count = sum(next_word_counts.values())

            # 计算每个下一个词的概率 = 出现次数 / 总次数
            self.next_word_probs[word] = {
                next_word: count / total_count 
                for next_word, count in next_word_counts.items()
            }

    def predict(self, word):
        """
        预测给定词的下一个词

        参数:
            word: 当前词

        返回:
            最可能的下一个词,如果词不在训练数据中则返回None
        """
        # 如果这个词没有训练过,返回None
        if word not in self.next_word_probs:
            return None

        # 获取这个词后面跟的所有词及其概率
        probabilities = self.next_word_probs[word]

        # 按概率从高到低排序
        sorted_candidates = sorted(
            probabilities.items(), 
            key=lambda x: x[1], 
            reverse=True
        )
        # 返回概率最高的词
        most_likely_word = sorted_candidates[0][0]
        return most_likely_word


# 创建分词器
tokenizer = SimpleTokenizer()

# 训练数据(更丰富)
texts = [
    "hello world",
    "hello world",
    "hello world",
    "hello python",
    "hello python",
    "hello hello"
]

# 训练模型
tokenizer.train(texts)

# 显示词汇表
print("词汇表(单词 -> ID):")
print(tokenizer.vocab)

# 显示概率分布
print("\n词的概率分布:")
for word, probs in tokenizer.next_word_probs.items():
    print(f"  '{word}' 后面可能是:")
    # 按概率从高到低排序显示
    for next_word, prob in sorted(probs.items(), key=lambda x: x[1], reverse=True):
        print(f"    {next_word}: {prob:.2f}")

# 测试预测功能
print("\n预测结果:")
test_word = "hello"
predicted_word = tokenizer.predict(test_word)
print(f"  '{test_word}' 的下一个词: {predicted_word}")

6. 增加参数 #

6.1 实现 #

from collections import defaultdict, Counter
+import random

class SimpleTokenizer:
    """
    分词器
    功能:
    1. 建立词汇表(单词 -> ID)
    2. 学习每个词后面跟其他词的概率
    3. 根据概率预测下一个词
    """

    def __init__(self):
        # 词汇表:单词 -> ID 的映射
        self.vocab = {}
        # 概率表:每个词后面跟其他词的概率
        # 格式:{词: {下一个词: 概率}}
        self.next_word_probs = {}

    def train(self, texts):
        """
        训练分词器
        步骤:
        1. 收集所有单词,建立词汇表
        2. 统计每个词后面跟的词及其出现次数
        3. 计算概率(次数 / 总次数)
        """
        # ========== 第一步:建立词汇表 ==========
        # 收集所有出现过的单词
        all_words = set()
        for text in texts:
            all_words.update(text.split())

        # 给每个单词分配一个ID(按字母顺序排序)
        self.vocab = {word: i for i, word in enumerate(sorted(all_words))}

        # ========== 第二步:统计词对出现次数 ==========
        # 统计每个词后面跟的词出现了多少次
        # 例如:"hello world" 会记录 "hello" 后面跟 "world" 出现1次
        word_pair_counts = defaultdict(Counter)

        for text in texts:
            words = text.split()
            # 遍历相邻的两个词
            for i in range(len(words) - 1):
                current_word = words[i]
                next_word = words[i + 1]
                # 记录:current_word 后面跟 next_word 出现1次
                word_pair_counts[current_word][next_word] += 1

        # ========== 第三步:计算概率 ==========
        # 对于每个词,计算它后面跟其他词的概率
        for word, next_word_counts in word_pair_counts.items():
            # 计算这个词后面跟的所有词的总次数
            total_count = sum(next_word_counts.values())

            # 计算每个下一个词的概率 = 出现次数 / 总次数
            self.next_word_probs[word] = {
                next_word: count / total_count 
                for next_word, count in next_word_counts.items()
            }

+   def predict(self, word, temperature=1.0, top_p=1.0):
        """
        预测给定词的下一个词

        参数:
            word: 当前词
+           temperature: 温度参数,控制随机性(0-1之间值越小越确定,越大越随机)
+           top_p: 核采样参数,只从累积概率达到top_p的候选中选择(0-1之间)

        返回:
+           根据概率分布采样的下一个词,如果词不在训练数据中则返回None
        """
        # 如果这个词没有训练过,返回None
        if word not in self.next_word_probs:
            return None

        # 获取这个词后面跟的所有词及其概率
        probabilities = self.next_word_probs[word]

+       # ========== 第一步:应用 top_p 过滤 ==========
        # 按概率从高到低排序
        sorted_candidates = sorted(
            probabilities.items(), 
            key=lambda x: x[1], 
            reverse=True
        )
+       
+       # 累积概率,只保留累积概率 <= top_p 的候选词
+       filtered_candidates = []
+       cumulative_prob = 0.0
+       for next_word, prob in sorted_candidates:
+           filtered_candidates.append((next_word, prob))
+           cumulative_prob += prob
+           if cumulative_prob >= top_p:
+               break
+       
+       # 如果没有候选词,返回None
+       if not filtered_candidates:
+           return None
+       
+       # ========== 第二步:应用 temperature 缩放 ==========
+       # 将概率进行温度缩放:prob^(1/temperature)
+       # temperature 越小,概率差异越大(更确定)
+       # temperature 越大,概率差异越小(更随机)
+       scaled_probs = []
+       for next_word, prob in filtered_candidates:
+           # 避免除零错误
+           if temperature > 0:
+               scaled_prob = prob ** (1.0 / temperature)
+           else:
+               # temperature = 0 时,只选择概率最高的
+               scaled_prob = prob if prob == max(p[1] for p in filtered_candidates) else 0
+           scaled_probs.append((next_word, scaled_prob))
+       
+       # 重新归一化概率(使所有概率之和为1)
+       total_scaled_prob = sum(prob for _, prob in scaled_probs)
+       if total_scaled_prob > 0:
+           normalized_probs = [
+               (word, prob / total_scaled_prob) 
+               for word, prob in scaled_probs
+           ]
+       else:
+           # 如果所有概率都是0,则均匀分布
+           normalized_probs = [
+               (word, 1.0 / len(scaled_probs)) 
+               for word, _ in scaled_probs
+           ]
+       
+       # ========== 第三步:根据概率分布采样 ==========
+       # 生成0-1之间的随机数
+       rand = random.random()
+       # 累积概率,找到随机数落在哪个区间
+       cumulative = 0.0
+       for next_word, prob in normalized_probs:
+           cumulative += prob
+           if rand <= cumulative:
+               return next_word
+       
+       # 如果由于浮点数精度问题没有返回,返回最后一个词
+       return normalized_probs[-1][0]


# 创建分词器
tokenizer = SimpleTokenizer()

# 训练数据(更丰富)
texts = [
    "hello world",
    "hello world",
    "hello world",
    "hello python",
    "hello python",
    "hello hello"
]

# 训练模型
tokenizer.train(texts)

# 显示词汇表
print("词汇表(单词 -> ID):")
print(tokenizer.vocab)

# 显示概率分布
print("\n词的概率分布:")
for word, probs in tokenizer.next_word_probs.items():
    print(f"  '{word}' 后面可能是:")
    # 按概率从高到低排序显示
    for next_word, prob in sorted(probs.items(), key=lambda x: x[1], reverse=True):
        print(f"    {next_word}: {prob:.2f}")

# 测试预测功能
print("\n预测结果:")
test_word = "hello"

+# 测试不同参数
+print(f"\n测试词: '{test_word}'")
+print("\n1. 默认参数(temperature=1.0, top_p=1.0):")
+for i in range(5):
+   predicted_word = tokenizer.predict(test_word)
+   print(f"   第{i+1}次: {predicted_word}")
+
+print("\n2. 低温度(temperature=0.1,更确定):")
+for i in range(5):
+   predicted_word = tokenizer.predict(test_word, temperature=0.1)
+   print(f"   第{i+1}次: {predicted_word}")
+
+print("\n3. 高温度(temperature=2.0,更随机):")
+for i in range(5):
+   predicted_word = tokenizer.predict(test_word, temperature=2.0)
+   print(f"   第{i+1}次: {predicted_word}")
+
+print("\n4. top_p=0.5(只从累积概率前50%的候选中选择):")
+for i in range(5):
+   predicted_word = tokenizer.predict(test_word, top_p=0.5)
+   print(f"   第{i+1}次: {predicted_word}")
+
+print("\n5. 组合参数(temperature=1.5, top_p=0.8):")
+for i in range(5):
+   predicted_word = tokenizer.predict(test_word, temperature=1.5, top_p=0.8)
+   print(f"   第{i+1}次: {predicted_word}")

6.2 temperature #

temperature 参数(温度采样)

  • 作用:控制输出的随机性
  • 实现:对概率进行温度缩放 prob^(1/temperature)
  • 效果:
    • temperature < 1.0:概率差异放大,更倾向于高概率词(更确定)
    • temperature = 1.0:保持原始概率分布
    • temperature > 1.0:概率差异缩小,分布更均匀(更随机)
    • temperature = 0:只选择概率最高的词(完全确定)

6.3 top_p #

top_p 参数(核采样/Nucleus Sampling)

  • 作用:只从累积概率达到 top_p 的候选中选择
  • 实现:
    1. 按概率从高到低排序
    2. 累积概率,只保留累积概率 ≤ top_p 的候选词
    3. 过滤低概率候选词
  • 效果:
    • top_p = 1.0:考虑所有候选词
    • top_p < 1.0:只考虑高概率候选词,过滤低概率词

6.4 采样流程 #

  1. 应用 top_p 过滤:保留累积概率 ≤ top_p 的候选词
  2. 应用 temperature 缩放:对概率进行温度缩放并归一化
  3. 概率采样:根据归一化后的概率分布进行随机采样

6.5 测试代码 #

添加了 5 个测试场景,展示不同参数组合的效果:

  • 默认参数(temperature=1.0, top_p=1.0)
  • 低温度(temperature=0.1,更确定)
  • 高温度(temperature=2.0,更随机)
  • top_p=0.5(只从累积概率前50%的候选中选择)
  • 组合参数(temperature=1.5, top_p=0.8)

访问验证

请输入访问令牌

Token不正确,请重新输入