分词技术详解:中文Jieba分词与英文WordPiece算法原理及PyTorch实现

目录


什么是分词?

分词的本质与重要性

分词是自然语言处理(NLP)的第一道工序,它的核心任务是将连续的文本序列切分为有意义的最小语义单元——Token

分词的本质:将文本切分为模型可处理的最小语义单元

  • 原始文本:"我爱自然语言处理技术"
  • 中文分词结果:["我", "爱", "自然语言", "处理", "技术"]
  • 英文分词结果:["I", "love", "natural", "language", "processing"]

不同语言的分词难度

  1. 中文最难(无天然空格分隔)
  2. 日文次之(汉字+假名混合)
  3. 英文较简单(以空格为主要分隔)

为什么要分词?

机器学习模型无法直接理解文本,必须先将其转换为数字向量。分词策略直接决定了文本如何被“翻译”给模型:

文本 → Token 序列 → 数字 ID 序列 → 嵌入向量

💡 重要提示:分词质量直接影响下游NLP任务的性能。

例如,对于“机器学习”:

  • 分词器A:["机器学习"] → 1个token
  • 分词器B:["机器", "学习"] → 2个token
  • 分词器C:["机", "器", "学", "习"] → 4个token

不同的切分方式会让模型学习到完全不同的语义。


中文分词:Jieba详解

Jieba 是目前最流行的开源中文分词工具,以高效、准确著称,尤其适合在各类 Python 项目中快速集成。

安装与基本使用

pip install jieba

Jieba 提供了三种核心分词模式,适用于不同场景:

import jieba

text = "自然语言处理是人工智能的重要分支"

# 1. 精确模式(最常用):适合文本分析,力求准确
words_precise = jieba.lcut(text)
print("精确模式:", words_precise)
# ['自然语言处理', '是', '人工智能', '的', '重要', '分支']

# 2. 全模式:扫描所有可能的词语,速度快但可能有冗余
words_full = jieba.lcut(text, cut_all=True)
print("全模式:", words_full)
# ['自然', '自然语言', '语言', '言处', '处理', '是', '人工智能', '人工', '智能', '的', '重要', '分支']

# 3. 搜索引擎模式:在精确模式基础上对长词再次切分,适合召回
words_search = jieba.cut_for_search(text)
print("搜索引擎模式:", list(words_search))
# ['自然', '语言', '自然语言', '处理', '人工智能', '人工', '智能', '是', '的', '重要', '分支']

自定义词典与增强功能

对于专业领域(如医疗、法律、金融),Jieba 默认词典可能不够用,我们可以添加自定义词汇:

# 方式一:动态添加词汇
jieba.add_word("自然语言处理")
jieba.add_word("大模型")
jieba.add_word("Transformer")

# 方式二:加载自定义词典文件
# custom_dict.txt 格式:词语 词频 词性(词频和词性可选)
# 示例内容:
# 自然语言处理 5 nz
# 大模型 10 nz
# Transformer 3 eng
# jieba.load_userdict("custom_dict.txt")

# 验证效果
text = "自然语言处理和大模型是AI领域的热点技术"
print(jieba.lcut(text))
# ['自然语言处理', '和', '大模型', '是', 'AI', '领域', '的', '热点', '技术']

词性标注与关键词提取

除了分词,Jieba 还提供词性标注和关键词提取功能,能够为后续任务提供丰富的特征。

import jieba.posseg as pseg
import jieba.analyse

# 1. 词性标注
text = "小明在北京大学的图书馆里读书"
words_with_pos = pseg.cut(text)
print("词性标注结果:")
for word, flag in words_with_pos:
    print(f"{word} / {flag}")
# 小明 / nr (人名)
# 在 / p (介词)
# 北京大学 / nt (机构名)
# ...

# 2. 关键词提取
text = """
自然语言处理是计算机科学领域与人工智能领域中的一个重要方向。
它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。
"""

# 基于 TF-IDF
keywords_tfidf = jieba.analyse.extract_tags(text, topK=5, withWeight=True)
print("\nTF-IDF关键词:")
for kw, w in keywords_tfidf:
    print(f"{kw}: {w:.4f}")

# 基于 TextRank
keywords_textrank = jieba.analyse.textrank(text, topK=5, withWeight=True)
print("\nTextRank关键词:")
for kw, w in keywords_textrank:
    print(f"{kw}: {w:.4f}")

英文分词:WordPiece与BPE

英文虽然有空格分隔,但需要处理缩写、时态和罕见词,现代大模型通常使用子词分词(Subword Tokenization)技术。

传统空格分词与预处理

最简单的英文分词是按空格切分,但需要处理标点和大小写:

import re

def basic_tokenize(text):
    """基础英文分词:清洗标点并按空格分割"""
    text = re.sub(r'[^\w\s]', ' ', text.lower())
    return [token for token in text.split() if token]

text = "Natural language processing is fascinating!"
print("基础分词:", basic_tokenize(text))
# ['natural', 'language', 'processing', 'is', 'fascinating']

子词分词:BPE vs WordPiece

子词分词能平衡词汇表大小和未知词处理能力,核心思想是将罕见词拆分为常见子词。我们可以直接使用 transformers 库体验主流分词器:

from transformers import BertTokenizer, RobertaTokenizer, GPT2Tokenizer

text_en = "Natural language processing and tokenization"

# 1. BERT (WordPiece)
bert_tokenizer = BertTokenizer.from_pretrained("bert-base-uncased")
print("BERT分词:", bert_tokenizer.tokenize(text_en))

# 2. RoBERTa/GPT-2 (BPE)
roberta_tokenizer = RobertaTokenizer.from_pretrained("roberta-base")
print("RoBERTa分词:", roberta_tokenizer.tokenize(text_en))

# 3. 中文BERT (字级别分词)
chinese_bert = BertTokenizer.from_pretrained("bert-base-chinese")
print("中文BERT分词:", chinese_bert.tokenize("自然语言处理很有趣"))
# ['自', '然', '语', '言', '处', '理', '很', '有', '趣']

主流分词器对比

下表对比了当前流行的子词分词器,它们在设计理念和适用场景上有所不同:

分词器代表模型核心原理优势适用场景
BPEGPT-2, RoBERTa基于频率合并字符对简单高效,处理未知词强生成任务,开放域
WordPieceBERT, DistilBERT基于语言模型概率词汇表更紧凑理解任务,封闭域
UnigramALBERT, ELECTRA概率模型动态选择灵活性强特殊领域,精细控制
ByteLevelGPT-2, LLaMA字节级编码无需预处理,通吃任意文本多语言,低资源

现代分词器对比

我们可以写一个简单的函数来直观对比不同分词器的效果:

from transformers import AutoTokenizer

def compare_tokenizers(text):
    """对比主流分词器效果"""
    models = [
        ("bert-base-uncased", "BERT"),
        ("roberta-base", "RoBERTa"),
        ("gpt2", "GPT-2"),
    ]
    
    print(f"原始文本: {text}\n")
    
    for model_name, model_type in models:
        tokenizer = AutoTokenizer.from_pretrained(model_name)
        tokens = tokenizer.tokenize(text)
        ids = tokenizer.convert_tokens_to_ids(tokens)
        
        print(f"{model_type}:")
        print(f"  Tokens: {tokens}")
        print(f"  IDs: {ids}\n")

# 示例
compare_tokenizers("Unconventional tokenization methods are cool!")

通用分词流程实现

我们将上面的知识整合,封装一个支持中英文自动检测的通用分词器,既使用了 Jieba 处理中文,又借助 Hugging Face 分词器处理英文,并可以输出模型可用的张量编码。

import jieba
import re
from transformers import BertTokenizer
from typing import List

class UniversalTokenizer:
    """通用中英文分词器"""
    def __init__(self, lang="auto", zh_model="bert-base-chinese", en_model="bert-base-uncased"):
        self.lang = lang
        self.zh_tokenizer = BertTokenizer.from_pretrained(zh_model)
        self.en_tokenizer = BertTokenizer.from_pretrained(en_model)

    def _detect_lang(self, text: str) -> str:
        """简单语言检测:中文占比超过30%则认为是中文"""
        chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
        total_chars = len(re.findall(r'\w', text))
        return "zh" if total_chars > 0 and chinese_chars / total_chars > 0.3 else "en"

    def tokenize(self, text: str) -> List[str]:
        """主分词函数"""
        actual_lang = self._detect_lang(text) if self.lang == "auto" else self.lang
        
        if actual_lang == "zh":
            # 中文:Jieba精确模式 + 简单清洗
            clean_text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', ' ', text)
            return list(jieba.lcut(clean_text.strip()))
        else:
            # 英文:BERT分词
            return self.en_tokenizer.tokenize(text.lower())

    def encode(self, text: str, max_len=128):
        """编码为模型输入"""
        actual_lang = self._detect_lang(text) if self.lang == "auto" else self.lang
        tokenizer = self.zh_tokenizer if actual_lang == "zh" else self.en_tokenizer
        return tokenizer(text, padding=True, truncation=True, max_length=max_len, return_tensors="pt")

# 使用示例
tokenizer = UniversalTokenizer()

print("中文分词:", tokenizer.tokenize("自然语言处理和大模型"))
print("英文分词:", tokenizer.tokenize("Natural language processing"))

实际应用与案例

情感分析中的分词

在情感分析任务中,我们通常需要分词后过滤停用词,以保留真正有情感色彩的词汇:

def sentiment_tokenize(text: str):
    """情感分析专用分词流水线"""
    tokens = jieba.lcut(text)
    # 简化版停用词表
    stopwords = {'的', '了', '在', '是', '我', '有', '和', '就', '不', '很'}
    return [t for t in tokens if t not in stopwords and len(t) > 1]

text = "这个产品真的很好用,强烈推荐!"
print("情感分析分词:", sentiment_tokenize(text))

文本相似度计算

基于分词结果,我们可以使用 TF-IDF 计算文本相似度,这在文档检索、去重等场景中非常实用:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

def cal_similarity(text1, text2):
    """基于分词的余弦相似度计算"""
    # 先分词
    tok1 = " ".join(jieba.lcut(text1))
    tok2 = " ".join(jieba.lcut(text2))
    
    # 计算TF-IDF
    vec = TfidfVectorizer()
    tfidf = vec.fit_transform([tok1, tok2])
    
    # 计算余弦相似度
    return cosine_similarity(tfidf[0:1], tfidf[1:2])[0][0]

text_a = "我喜欢学习自然语言处理"
text_b = "自然语言处理是我喜欢的方向"
print(f"相似度: {cal_similarity(text_a, text_b):.4f}")

相关教程

分词是NLP的基石,建议先掌握Jieba等基础工具,再深入理解子词分词算法。实际项目中优先使用Hugging Face的预训练分词器。

总结

分词技术是NLP的基础环节,本文主要介绍了:

  1. 中文分词:Jieba的三种模式、自定义词典及词性标注
  2. 英文/子词分词:BPE、WordPiece的原理及Hugging Face使用
  3. 实战整合:一个支持中英文的通用分词器
  4. 场景应用:情感分析与文本相似度计算

💡 核心要点:中文场景推荐Jieba(通用)或专业分词器,英文/多语言场景直接使用Hugging Face预训练分词器。


🔗 扩展阅读

📂 所属阶段:第一阶段 — 文本预处理(基石篇)
🔗 相关章节:文本清洗与规范化 · 词向量空间WordEmbeddings