【LLM】第一章:分词算法BPE、WordPiece、Unigram、分词工具jieba

【LLM】第一章:分词算法BPE、WordPiece、Unigram、分词工具jieba

大模型,具体说就是大语言模型,LLM,large language model,所以这个领域其实就是NLP,所以本专栏也是NLP专栏的延续。

在传统NLP领域,第一步就是分词 ,分完词后就可以对词进行文本表示 ,也就是把词变成特征向量 ,然后把特征向量喂入我们前面学的各种序列模型,训练模型,完成分类、回归、生成等任务。本章节讲分词,下一个章节讲特征向量。

一、NLP的常见任务、技术演进

(一)常见任务

NLP是研究如何让计算机理解、生成和处理人类语言(如中文、英文),从而实现人与机器之间的语言交互。所以自然语言处理的常见任务可以分为一下几类:

1、文本分类

对整段文本进行判断或归类。
常见应用 :情感分析(判断评价是正面还是负面)、垃圾邮件识别、新闻主题分类等。
技术实现:这其实就是句子级别的分类任务。

2、序列标注

对一段文本中的每个词或字打上标签。
常见应用 :命名实体识别(找出人名、地名、手机号码、邮箱地址等)。
技术实现 :其实就是先对这段文本进行分词,然后对每个词(Token)做分类任务。

3、文本生成

根据已有内容生成新的文本。
常见应用 :自动写作、摘要生成、智能回复、对话系统等。
技术实现 :将提示词喂入模型,预测出一个token-->提示词+预测的token喂入模型,预测下下一个token-->如此不断重复,就生成了一段新的文本内容。这个过程也叫自回归生成。

4、信息抽取

从文本中抽取结构化的信息。
常见应用 :给出一段文本和一个问题,从中抽取答案。比如问答系统。问答系统分生成式问答(小标题3)和抽取式问答。抽取式问答就要用到信息抽取。
技术实现 :预测一个序列中的起始位置和结束位置。

5、文本转换

将一种文本转换为另外一种形式。
常见应用 :机器翻译,摘要生成等。
技术实现 :用Transformer来实现。

(二)NLP技术演进历史

1、基于规则阶段

在20实际50年代到80年代初,自然语言处理主要依赖人工手动制定和编写的语言规则,大多都是用if else和一些正则表达式来制定。下面通过两个小例子直观理解一下:

2、统计方法阶段

之所以出现统计方法是因为,到90年代,我们的计算机计算能力 的大幅提升和语料数据 的大量积累,使得统计方法变得可行。人们开始对大量文本数据进行概率建模,让系统学习 语言中的模式和规律。典型的方法有:N-gram模型隐马尔可夫模型HMM最大熵模型 。这一阶段标志着从专家经验数据驱动 方法的转变。上图是一个2-gram语言模型,就是根据前面的一个词来预测下文的语言模型,根据训练好的下文词出现的概率来预测。3-gram是用滑窗得到所有的三元词组,然后统计所有二元词组后面的词的概率,用这个概率来预测。

3、机器学习阶段

进入21世纪,机器学习兴起,比如逻辑回合、支持向量机SVM、决策树、条件随机场CRF等分类模型表现出强大的分类效果。人们将机器学习算法引入NLP,在命名实体识别、文本分类等分类任务上取得了非常好的表现。

但是文本数据在用这些机器学习模型进行分类前,是要先进行分词文本表示 后才能喂入模型进行分类的。所以此时 征工程 成为了关键环节,此时的文本表示主要是词袋模型例子中的词袋模型是通过统计词频来表示文本,虽然简单直接,但致命的缺点就是忽略了词语的顺序 。为了解决这个问题,后人又引入了n-gram,就是词袋中不仅有上图中的Token,还有用滑窗得到的n个词组组成的T oken ,也就是增加特征向量的维度来表示更加准确的语义:当然词袋模型后面还有进化,比如td-idf等,这些方法都是古人的智慧结晶,今天仍有价值,后面我会专门用一个篇章来讲词袋模型。

4、深度学习阶段

当下的NLP基本就都是深度学习了。前期的基于深度神经网络RNN、LSTM、GRU等模型,能够自动学习文本数据的特征向量。随后的Transformer架构极大的提升了文本数据的理解和生成能力,并推动了当下的预训练语言模型BERT、GPT等大语言模型和迁移学习的发展。

二、分词策略

在大模型领域,分词 是文本预处理中的一个关键步骤,分词的目的 是将文本分解成有意义的单元,以便大模型能够更好地理解和处理。也所以Tokenization是大模型 预处理的核心步骤,也所以分词算法的选择直接影响模型效果。

分词 ,Tokenization,就是将原始文本切分成若干具有独立语义的最小单元(即Token)的过程,是所有NLP任务的起点。
词表 ,Vocabulary, 是由语料库构建出的、包含模型可识别token的集合。词表中的每个token都分配有唯一的ID,并支持token与ID之间的双向映射。由于不同语言有不同的结构、不同的词边界,所以会有不同的分词策略和分词算法。本部分只介绍中文和英文的分词方式。

(一)英文分词

按照分词粒度的大小,可分为词级分词 (word-level)、字符级分词 (character-level)、子词级分词 (subword-level)三种策略,其效果如下:1、词级分词(word-level)

(1)是最简单、传统的分词方式。用空格、标点符号做为分隔符进行分割。

(2)缺点是容易出现OOV问题 (out-of-vocabulary,未登陆词)。什么是OOV问题?就是比如模型在训练阶段 使用的是词表A,但是当模型训练完毕后,模型开始预测 时,喂给模型的文本中有一些词却不在A中,比如一些新词、网络热词、专用名称、复合词等,就非常容易成为OOV。此时模型就无法识别这些词,这些词通常会被统一替换成<\UNK>这个特殊标记,以便模型可以正常跑通。但是如果未登录词较多,就会出现语义信息丢失,影响模型的语义理解和推理能力。

2、字符级分词(character-level)

(1)以单个字符为最小单位进行分词的方法。文本中的每一个字母、数字、标点、空格都会被视为一个独立的token。

(2)这种分词方法下,生成的词表由所有可能出现的字符 组成,因此词表 规模非常小、覆盖率极高,几乎不存在OOV问题 ,因为几乎没有人去重新发明新符号,所以无论什么样的新词,只要它的基本符号不变,它就都能被表示出来。但缺点是:一是单个字符本身携带的语义信息是非常非常少的;二是按找字符分词后的序列就会变得非常非常长。模型必须依赖更长的上下文来推断语义,而上下文序列又巨长,这不仅增加了模型的训练成本,更显著增加了建模难度和模型效果。所以也非常不可取。

3、子词级分词(subword-level)

(1)是介于词级分词和字符级分词之间的分词方法。他将词的词根、前缀、后缀或常见词片段和原词切分开来,形成子词(subword)。比如说单词"looked"和"looking"会被划分为"look","ed","ing",这种把词本身的意思和词的时态分开的做法,不仅在降低词表大小的同时,还能学到词的更多语意信息。

(2)与词级分词相比,子词分词可以显著缓解OOV问题。与字符级分词相比,分词后的序列不会过长。一个新词即使没有出现在词表中,但只要它可以被拆分成子词单元,就可以被模型识别和表示,从而避免了被整体替换成<\UNK>。所以现在的比如BERT、GPT等模型都采用的是子词分词。

(3)常见的子词分词算法有:BPE (Byte Pair Encoding,基于频率合并)、WordPiece (基于似然增益合并)、Unigram(自顶向下概率筛选)等。比如GPT2采用的是BPE算法、Bert采用的是WordPiece算法。下面先简单看一下BPE的效果:

网站 https://tiktokenizer.vercel.app/ 提供了一个名为Tiktokenizer 的工具。该工具是一个用于分词(tokenize)的应用程序,可以将输入的文本按照语言规则划分成单个的标记(tokens)。下面展示一下从这个网站查询的BPE算法的分词效果:BPE是如何做到这么智能,能分清前缀、后缀、合成词等的?后面我们会对上面这些分词算法展开详细讲解。

(二)中文分词 中文没有空格这种天然的词边界,所以分词方式和英文还是有些区别的。姑且按照英文的分类:

中文字符级分词,就是按照单个汉字进行切分,文本中每个汉字都被视作一个token。这种方法最简单,而且某些场合下非常有用,比如古诗词,比较适合这样分词,因为汉字本身就具有独立语义。

中文的词级分词子词级分词 其实是一样的,因为中文没有前缀后缀词根等这种字词结构。所以中文的词级分词和子词级分词通常依赖提前整理的字典、规则或模型来识别词语边界。

但是BPE算法是可以直接应用于中文的。以汉字为基本单位,通过学习语料中高频的字组合(比如'自然'、'语言'、'处理'),自动构建字词词表。这种方式无需人工字典,具有较强的适应能力。当前主流的中文大模型,比如通义千问、DeepSeek等,都采用的是子词分词策略。

三、分词算法

1、BPE (Byte Pair Encoding)

BPE,字节对编码,最初被开发为一种压缩文本的算法,随后在预训练PGT模型时被OpenAI用于tokenization。许多Transforer模型都使用它,包括GPT、BERT、BART、RoBERTa、DeBERTa。其核心思想是从最小的词元(如字符)开始,逐步合并出现频率最高的连续词元对,直到达到预定的词表大小不再有可 以合并的连续对

python 复制代码
import re
from collections import defaultdict, Counter
class BPEChineseTokenizer:
    def __init__(self, vocab_size=10):
        self.vocab_size = vocab_size  # 目标词汇表大小
        self.vocab = {}  # 最终词汇表
        self.merge_rules = {}  # 合并规则((a,b) → ab)
    
    def preprocess(self, corpus):
        """预处理:拆分为单字+结束符,统一格式"""
        processed = []
        for sentence in corpus:
            # 中文单字拆分,每个字后加</w>,词之间用空格分隔(这里按句子拆分)
            tokens = [char + '</w>' for char in sentence]
            processed.append(' '.join(tokens))
        return processed
    
    def get_pair_freq(self, corpus):
        """统计相邻字符对的频率"""
        pair_freq = defaultdict(int)
        for sentence in corpus:
            tokens = sentence.split()
            for i in range(len(tokens)-1):
                pair = (tokens[i], tokens[i+1])
                pair_freq[pair] += 1
        return pair_freq
    
    def merge_pair(self, corpus, pair, new_token):
        """合并语料中的指定字符对"""
        merged_corpus = []
        pattern = re.escape(f' {pair[0]} {pair[1]} ')
        replacement = f' {new_token} '
        for sentence in corpus:
            # 替换所有匹配的字符对
            merged_sentence = re.sub(pattern, replacement, f' {sentence} ').strip()
            merged_corpus.append(merged_sentence)
        return merged_corpus
    
    def train(self, corpus):
        """训练BPE分词器"""
        # 预处理语料
        processed_corpus = self.preprocess(corpus)
        # 初始化词汇表:所有单字
        all_tokens = []
        for sentence in processed_corpus:
            all_tokens.extend(sentence.split())
        initial_vocab = list(set(all_tokens))
        self.vocab = {token: idx for idx, token in enumerate(initial_vocab)}
        
        # 迭代合并直到达到词汇表大小
        while len(self.vocab) < self.vocab_size:
            # 统计字符对频率
            pair_freq = self.get_pair_freq(processed_corpus)
            if not pair_freq:
                break  # 无可用合并对
            # 找频率最高的对
            best_pair = max(pair_freq, key=pair_freq.get)
            # 生成新Token
            new_token = ''.join(best_pair).replace('</w>', '') + '</w>'
            # 记录合并规则
            self.merge_rules[best_pair] = new_token
            # 合并语料中的该对
            processed_corpus = self.merge_pair(processed_corpus, best_pair, new_token)
            # 更新词汇表
            if new_token not in self.vocab:
                self.vocab[new_token] = len(self.vocab)
        
        print("BPE训练完成!")
        print("合并规则:", self.merge_rules)
        print("最终词汇表:", self.vocab)
    
    def tokenize(self, text):
        """对新文本分词"""
        # 预处理文本为单字
        tokens = [char + '</w>' for char in text]
        # 应用合并规则(从长到短匹配)
        # 先将合并规则按新Token长度降序排序
        sorted_merges = sorted(self.merge_rules.items(), 
                              key=lambda x: len(x[1]), reverse=True)
        # 迭代合并
        while True:
            merged = False
            for (pair, new_token) in sorted_merges:
                if pair[0] in tokens and pair[1] in tokens:
                    # 找到相邻的pair
                    idx = tokens.index(pair[0])
                    if idx + 1 < len(tokens) and tokens[idx+1] == pair[1]:
                        # 合并
                        tokens = tokens[:idx] + [new_token] + tokens[idx+2:]
                        merged = True
                        break
            if not merged:
                break
        # 转换为词汇表ID
        token_ids = [self.vocab.get(token, -1) for token in tokens]
        return tokens, token_ids
# 测试代码
if __name__ == "__main__":
    # 中文语料
    corpus = ["我爱中国", "中国很强大", "我爱北京", "北京是中国首都"]
    # 初始化并训练BPE分词器
    bpe_tokenizer = BPEChineseTokenizer(vocab_size=15)
    bpe_tokenizer.train(corpus)
    # 分词测试
    test_text = "我爱北京"
    tokens, token_ids = bpe_tokenizer.tokenize(test_text)
    print(f"\n测试文本:{test_text}")
    print(f"分词结果:{tokens}")
    print(f"Token ID:{token_ids}")

2、WordPiece分词

WordPiece与BPE类似,也是从字符开始,迭代合并子词。但合并的标准不是频率,而是合并后对语言模型似然的提升,即合并后的 Token 能最大程度提升整体语料的概率。具体来说,每次选择合并后能最大程度增加语言模型似然的词元对。WordPiece的合并标准是增益最大化,对增益这个指标的理解可以参考我曾写过的一篇博文: https://blog.csdn.net/friday1203/article/details/135084711 ,这篇博文是讲决策树算法的,其核心指标就是信息增益,看完后你就会豁然开朗的。

python 复制代码
import re
import math
from collections import defaultdict, Counter
class WordPieceChineseTokenizer:
    def __init__(self, vocab_size=10):
        self.vocab_size = vocab_size
        self.vocab = {}
        self.merge_rules = {}
        self.token_freq = defaultdict(int)  # Token频率
    
    def preprocess(self, corpus):
        """预处理:拆分为单字+结束符"""
        processed = []
        total_tokens = 0
        for sentence in corpus:
            tokens = [char + '</w>' for char in sentence]
            processed.append(' '.join(tokens))
            # 统计初始Token频率
            for token in tokens:
                self.token_freq[token] += 1
            total_tokens += len(tokens)
        self.total_tokens = total_tokens  # 总Token数
        return processed
    
    def calculate_gain(self, a, b, corpus):
        """计算合并(a,b)的对数似然增益"""
        # 统计a、b、ab的频率
        a_freq = self.token_freq.get(a, 0)
        b_freq = self.token_freq.get(b, 0)
        # 统计ab的共现频率
        ab_freq = 0
        for sentence in corpus:
            tokens = sentence.split()
            for i in range(len(tokens)-1):
                if tokens[i] == a and tokens[i+1] == b:
                    ab_freq += 1
        if a_freq == 0 or b_freq == 0 or ab_freq == 0:
            return -float('inf')  # 无增益
        
        # 计算概率
        p_a = a_freq / self.total_tokens
        p_b = b_freq / self.total_tokens
        p_ab = ab_freq / (self.total_tokens - ab_freq)  # 合并后总Token数减少ab_freq
        
        # 对数似然增益
        gain = math.log(p_ab) - math.log(p_a) - math.log(p_b)
        return gain
    
    def merge_pair(self, corpus, pair, new_token):
        """合并语料中的指定字符对"""
        merged_corpus = []
        pattern = re.escape(f' {pair[0]} {pair[1]} ')
        replacement = f' {new_token} '
        for sentence in corpus:
            merged_sentence = re.sub(pattern, replacement, f' {sentence} ').strip()
            merged_corpus.append(merged_sentence)
        # 更新Token频率
        a, b = pair
        ab_freq = self.token_freq.get(a, 0) + self.token_freq.get(b, 0) - (self.token_freq.get(new_token, 0))
        self.token_freq[new_token] = ab_freq
        del self.token_freq[a]
        del self.token_freq[b]
        self.total_tokens -= ab_freq  # 总Token数减少
        return merged_corpus
    
    def train(self, corpus):
        """训练WordPiece分词器"""
        processed_corpus = self.preprocess(corpus)
        # 初始化词汇表
        initial_vocab = list(self.token_freq.keys())
        self.vocab = {token: idx for idx, token in enumerate(initial_vocab)}
        
        while len(self.vocab) < self.vocab_size:
            # 生成所有候选字符对
            candidate_pairs = set()
            for sentence in processed_corpus:
                tokens = sentence.split()
                for i in range(len(tokens)-1):
                    candidate_pairs.add((tokens[i], tokens[i+1]))
            if not candidate_pairs:
                break
            
            # 计算每个候选对的增益
            gains = {}
            for pair in candidate_pairs:
                gain = self.calculate_gain(pair[0], pair[1], processed_corpus)
                gains[pair] = gain
            
            # 找增益最高的对
            best_pair = max(gains, key=gains.get)
            best_gain = gains[best_pair]
            if best_gain <= 0:
                break  # 增益≤0,停止合并
            
            # 生成新Token
            new_token = ''.join(best_pair).replace('</w>', '') + '</w>'
            self.merge_rules[best_pair] = new_token
            # 合并语料
            processed_corpus = self.merge_pair(processed_corpus, best_pair, new_token)
            # 更新词汇表
            if new_token not in self.vocab:
                self.vocab[new_token] = len(self.vocab)
        
        print("WordPiece训练完成!")
        print("合并规则:", self.merge_rules)
        print("最终词汇表:", self.vocab)
    
    def tokenize(self, text):
        """分词(最长匹配)"""
        tokens = [char + '</w>' for char in text]
        # 按新Token长度降序应用合并规则
        sorted_merges = sorted(self.merge_rules.items(), 
                              key=lambda x: len(x[1]), reverse=True)
        while True:
            merged = False
            for (pair, new_token) in sorted_merges:
                if pair[0] in tokens and pair[1] in tokens:
                    idx = tokens.index(pair[0])
                    if idx + 1 < len(tokens) and tokens[idx+1] == pair[1]:
                        tokens = tokens[:idx] + [new_token] + tokens[idx+2:]
                        merged = True
                        break
            if not merged:
                break
        token_ids = [self.vocab.get(token, -1) for token in tokens]
        return tokens, token_ids
# 测试代码
if __name__ == "__main__":
    corpus = ["我爱中国", "中国很强大", "我爱北京", "北京是中国首都"]
    wp_tokenizer = WordPieceChineseTokenizer(vocab_size=10)
    wp_tokenizer.train(corpus)
    # 分词测试
    test_text = "中国很强大"
    tokens, token_ids = wp_tokenizer.tokenize(test_text)
    print(f"\n测试文本:{test_text}")
    print(f"分词结果:{tokens}")
    print(f"Token ID:{token_ids}")

3、Unigram分词

Unigram分词与BPE和WordPiece相反,它从一个大的种子词表开始,然后逐步删除词元,直到达到目标词表大小。它基于一个假设:所有词元的出现是独立的,并且通过最大化句子的似然来优化词表。

python 复制代码
import math
from collections import defaultdict, Counter
from itertools import combinations
class UnigramChineseTokenizer:
    def __init__(self, vocab_size=10):
        self.vocab_size = vocab_size
        self.vocab = {}
        self.token_prob = defaultdict(float)  # Token概率
    
    def generate_candidates(self, corpus, max_len=3):
        """生成候选Token(单字、双字、三字)"""
        candidates = set()
        for sentence in corpus:
            # 生成所有长度≤max_len的连续子串
            for i in range(len(sentence)):
                for j in range(1, min(max_len+1, len(sentence)-i+1)):
                    token = sentence[i:i+j]
                    candidates.add(token)
        return list(candidates)
    
    def calculate_token_freq(self, corpus, candidates):
        """统计候选Token的频率"""
        freq = defaultdict(int)
        total = 0
        for sentence in corpus:
            # 统计每个候选Token的出现次数
            for token in candidates:
                token_len = len(token)
                for i in range(len(sentence)-token_len+1):
                    if sentence[i:i+token_len] == token:
                        freq[token] += 1
                        total += 1
        return freq, total
    
    def calculate_perplexity(self, corpus, vocab, token_prob):
        """计算语料的困惑度"""
        total_log_prob = 0
        total_tokens = 0
        for sentence in corpus:
            # 找到最优切分(概率最高)
            best_log_prob = -float('inf')
            # 简单实现:最长匹配找切分
            tokens = self._longest_match(sentence, vocab)
            # 计算该切分的对数概率
            log_prob = sum([math.log(token_prob.get(t, 1e-10)) for t in tokens])
            total_log_prob += log_prob
            total_tokens += len(tokens)
        # 困惑度 = exp(-平均对数概率)
        avg_log_prob = total_log_prob / total_tokens
        perplexity = math.exp(-avg_log_prob)
        return perplexity
    
    def _longest_match(self, text, vocab):
        """最长匹配切分(用于简化困惑度计算)"""
        tokens = []
        i = 0
        vocab_sorted = sorted(vocab, key=len, reverse=True)  # 按长度降序
        while i < len(text):
            matched = False
            for token in vocab_sorted:
                token_len = len(token)
                if i + token_len <= len(text) and text[i:i+token_len] == token:
                    tokens.append(token)
                    i += token_len
                    matched = True
                    break
            if not matched:
                # 匹配失败,取单字
                tokens.append(text[i])
                i += 1
        return tokens
    
    def train(self, corpus):
        """训练Unigram分词器"""
        # 步骤1:生成候选Token
        candidates = self.generate_candidates(corpus, max_len=3)
        # 步骤2:统计频率,初始化概率
        freq, total = self.calculate_token_freq(corpus, candidates)
        # 初始化Vocab(过滤掉频率为0的Token)
        initial_vocab = [t for t in candidates if freq[t] > 0]
        current_vocab = initial_vocab.copy()
        
        # 步骤3:迭代删除Token直到达到目标大小
        while len(current_vocab) > self.vocab_size:
            # 计算当前Token概率
            current_freq, current_total = self.calculate_token_freq(corpus, current_vocab)
            current_prob = {t: current_freq[t]/current_total for t in current_vocab}
            # 计算当前困惑度
            current_pp = self.calculate_perplexity(corpus, current_vocab, current_prob)
            
            # 计算删除每个Token后的困惑度
            pp_dict = {}
            for token in current_vocab:
                # 临时删除该Token
                temp_vocab = [t for t in current_vocab if t != token]
                if not temp_vocab:
                    pp_dict[token] = float('inf')
                    continue
                # 计算临时概率
                temp_freq, temp_total = self.calculate_token_freq(corpus, temp_vocab)
                temp_prob = {t: temp_freq[t]/temp_total for t in temp_vocab}
                # 计算临时困惑度
                temp_pp = self.calculate_perplexity(corpus, temp_vocab, temp_prob)
                pp_dict[token] = temp_pp
            
            # 找到困惑度上升最小的Token(即pp_dict最小的)
            best_token_to_remove = min(pp_dict, key=pp_dict.get)
            current_vocab.remove(best_token_to_remove)
        
        # 最终Vocab和概率
        final_freq, final_total = self.calculate_token_freq(corpus, current_vocab)
        self.token_prob = {t: final_freq[t]/final_total for t in current_vocab}
        self.vocab = {t: idx for idx, t in enumerate(current_vocab)}
        
        print("Unigram训练完成!")
        print("最终词汇表:", self.vocab)
        print("Token概率:", self.token_prob)
    
    def tokenize(self, text):
        """最优概率切分"""
        # 动态规划找最优切分
        n = len(text)
        # dp[i]:前i个字符的最大对数概率
        dp = [-float('inf')] * (n+1)
        dp[0] = 0.0
        # prev[i]:前i个字符的最优切分位置
        prev = [0] * (n+1)
        
        for i in range(1, n+1):
            for j in range(max(0, i-3), i):  # 最多匹配3字
                token = text[j:i]
                if token in self.token_prob:
                    log_prob = math.log(self.token_prob[token])
                    if dp[j] + log_prob > dp[i]:
                        dp[i] = dp[j] + log_prob
                        prev[i] = j
        
        # 回溯找切分结果
        tokens = []
        i = n
        while i > 0:
            j = prev[i]
            tokens.append(text[j:i])
            i = j
        tokens = tokens[::-1]
        token_ids = [self.vocab.get(t, -1) for t in tokens]
        return tokens, token_ids
# 测试代码
if __name__ == "__main__":
    corpus = ["我爱中国", "中国很强大", "我爱北京", "北京是中国首都"]
    unigram_tokenizer = UnigramChineseTokenizer(vocab_size=10)
    unigram_tokenizer.train(corpus)
    # 分词测试
    test_text = "北京是中国首都"
    tokens, token_ids = unigram_tokenizer.tokenize(test_text)
    print(f"\n测试文本:{test_text}")
    print(f"分词结果:{tokens}")
    print(f"Token ID:{token_ids}")

四、分词工具:jieba分词器

目前市面上用于中文分词的工具大致分两类:

  • 基于词典或模型的传统方法,主要以"词"为单位进行切分。常用工具有jiebaHanLP,常用于传统NLP任务中。

  • 基于子词建模算法(如BPE)的方式,从数据中自动学习高频字组合,构建子词词表。常用工具有Hugging Face TokenizerSentencePicetiktoken 等,常用于大模型预训练语言模型中,我们直接调用接口就可以使用,因为每个预训练模型都有自己配对的分词器,我们也是必须用人家的分词器,这样才能按照人家的词表ID去生成文本,因为人家的模型就是基于它们自己的词表来训练的。

(1)jieba的分词模式

(2)如何自定义词典

之所以要自定义词典是因为,你处理的文本中的字词可能jieba的词表中没有,此时就无法识别,所以需要自定义词典。

本篇完。 上面的算法主要是参考:https://developer.aliyun.com/article/1709785 ,感兴趣的同学可以自行查阅。

相关推荐
清水白石0082 小时前
Python 在数据栈中的边界:何时高效原型、何时切换到 SQL、Spark、Rust 或数据库原生能力
数据库·python·自动化
青瓷程序设计2 小时前
基于深度学习的【猫类识别系统】~Python+深度学习+人工智能+算法模型+2026原创+计算机毕设
人工智能·python·深度学习
FluxMelodySun2 小时前
机器学习(三十) 半监督学习与生成式方法
人工智能·机器学习
kishu_iOS&AI2 小时前
机器学习 —— 高数回顾
人工智能·机器学习·高等数学
渡我白衣2 小时前
运筹帷幄——在线学习与实时预测系统
人工智能·深度学习·神经网络·学习·算法·机器学习·caffe
好家伙VCC2 小时前
**InfluxDB实战进阶:基于Golang的高性能时序数据采集与可视化方
java·开发语言·后端·python·golang
正在走向自律2 小时前
AI入门:从机器学习到生成式AI,一份不完美的学习笔记
人工智能·机器学习
星星也在雾里2 小时前
Dify + FastAPI + 讯飞WebSocket实现方言识别
人工智能·fastapi
X journey2 小时前
机器学习进阶(15):过拟合
人工智能·机器学习