文本喂给模型前要做啥?文本预处理与张量表示全解析

文本喂给模型前要做啥?文本预处理与张量表示全解析

关键词:文本预处理、分词、词性标注、One-Hot、Word2Vec、Word Embedding、TensorBoard


目录

  • 一、认识文本预处理
    • [1.1 为什么需要文本预处理?](#1.1 为什么需要文本预处理? "#11-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E6%96%87%E6%9C%AC%E9%A2%84%E5%A4%84%E7%90%86")
    • [1.2 文本预处理的主要环节](#1.2 文本预处理的主要环节 "#12-%E6%96%87%E6%9C%AC%E9%A2%84%E5%A4%84%E7%90%86%E7%9A%84%E4%B8%BB%E8%A6%81%E7%8E%AF%E8%8A%82")
  • 二、文本处理的基本方法
    • [2.1 分词:切分词语边界](#2.1 分词:切分词语边界 "#21-%E5%88%86%E8%AF%8D%E5%88%87%E5%88%86%E8%AF%8D%E8%AF%AD%E8%BE%B9%E7%95%8C")
    • [2.2 命名实体识别:提取关键信息](#2.2 命名实体识别:提取关键信息 "#22-%E5%91%BD%E5%90%8D%E5%AE%9E%E4%BD%93%E8%AF%86%E5%88%AB%E6%8F%90%E5%8F%96%E5%85%B3%E9%94%AE%E4%BF%A1%E6%81%AF")
    • [2.3 词性标注:识别语法角色](#2.3 词性标注:识别语法角色 "#23-%E8%AF%8D%E6%80%A7%E6%A0%87%E6%B3%A8%E8%AF%86%E5%88%AB%E8%AF%AD%E6%B3%95%E8%A7%92%E8%89%B2")
  • 三、文本张量的表示方法
    • [3.1 One-Hot编码:最基础的表示](#3.1 One-Hot编码:最基础的表示 "#31-one-hot%E7%BC%96%E7%A0%81%E6%9C%80%E5%9F%BA%E7%A1%80%E7%9A%84%E8%A1%A8%E7%A4%BA")
    • [3.2 Word2Vec:稠密词向量的里程碑](#3.2 Word2Vec:稠密词向量的里程碑 "#32-word2vec%E7%A8%A0%E5%AF%86%E8%AF%8D%E5%90%91%E9%87%8F%E7%9A%84%E9%87%8C%E7%A8%8B%E7%A2%91")
    • [3.3 Word Embedding:可训练的嵌入层](#3.3 Word Embedding:可训练的嵌入层 "#33-word-embedding%E5%8F%AF%E8%AE%AD%E7%BB%83%E7%9A%84%E5%B5%8C%E5%85%A5%E5%B1%82")
  • 四、TensorBoard可视化实战
    • [4.1 TensorBoard Embedding Projector简介](#4.1 TensorBoard Embedding Projector简介 "#41-tensorboard-embedding-projector%E7%AE%80%E4%BB%8B")
    • [4.2 词向量可视化完整代码](#4.2 词向量可视化完整代码 "#42-%E8%AF%8D%E5%90%91%E9%87%8F%E5%8F%AF%E8%A7%86%E5%8C%96%E5%AE%8C%E6%95%B4%E4%BB%A3%E7%A0%81")
  • 常见问题
  • 和AI大模型开发的关系
  • 总结

一、认识文本预处理

1.1 为什么需要文本预处理?

在大模型和NLP应用中,我们拿到的原始文本通常是非结构化、充满噪声的:

python 复制代码
# 原始文本示例(来自网页爬取)
raw_text = """
<html><body>
  <h1>AI技术前沿</h1>
  <p>2024年,大语言模型(LLM)取得了突破性进展!</p>
  <p>GPT-4、Claude 3、Gemini等模型在多个基准测试中表现优异...</p>
  <div class="ads">广告内容...</div>
  <footer>© 2024 AI News</footer>
</body></html>
"""

这样的文本直接喂给模型会有什么问题?

  1. HTML标签干扰<html><div> 等标签不是有效语义信息
  2. 噪声数据:广告、版权信息、导航栏等无关内容
  3. 格式不统一:大小写混用、多余空格、特殊符号
  4. 无法直接计算:模型只能处理数值,不能处理文本字符串

文本预处理的核心目标 :将原始的非结构化文本,转化为干净、结构化、数值化的数据,让模型能够高效学习。

1.2 文本预处理的主要环节

图一:文本预处理完整流程

(图一:文本预处理从原始文本到向量化,经历清洗、分词、标注、标准化等多个环节,最终输出机器可理解的数值张量)

完整的文本预处理流程包含以下环节:

环节 作用 常用工具
文本清洗 去除HTML标签、特殊字符、多余空格 re正则、BeautifulSoup
分词处理 将连续文本切分为词语单元 jieba、HanLP、spaCy
标准化 转小写、去除停用词、词干提取 NLTK、spaCy
词性标注 识别每个词的语法角色(名词、动词等) jieba.posseg、NLTK
命名实体识别 提取人名、地名、机构名等关键实体 HanLP、spaCy、BERT-NER
向量化 将词语转换为数值张量 Word2Vec、Embedding层

二、文本处理的基本方法

2.1 分词:切分词语边界

分词是中文NLP的第一步,也是最重要的一步。英文天然有空格分隔词语,但中文是连续字符序列,需要算法来识别词语边界。

python 复制代码
import jieba

# ========== 基础分词 ==========
text = "自然语言处理技术让机器理解人类语言"

# 精确模式(最常用)
words_precise = jieba.cut(text, cut_all=False)
print("精确模式:", " / ".join(words_precise))
# 输出: 自然语言 / 处理 / 技术 / 让 / 机器 / 理解 / 人类 / 语言

# 全模式(扫描所有可能词语)
words_full = jieba.cut(text, cut_all=True)
print("全模式:", " / ".join(words_full))
# 输出: 自然 / 自然语言 / 语言 / 处理 / 技术 / 让 / 机器 / 理解 / 人类 / 语言

# 搜索引擎模式(对长词再次切分)
words_search = jieba.cut_for_search(text)
print("搜索引擎模式:", " / ".join(words_search))

# ========== 添加自定义词典 ==========
# 对于领域专有名词,jieba可能切分不准
tech_text = "RAG检索增强和LangChain框架是大模型应用的核心"
print("\n未添加词典:", " / ".join(jieba.cut(tech_text)))
# 可能输出: RAG / 检索 / 增强 / 和 / LangChain / 框架 / 是 / 大模型 / 应用 / 的 / 核心

# 添加自定义词典后
jieba.add_word("RAG检索增强")
jieba.add_word("LangChain框架")
print("添加词典后:", " / ".join(jieba.cut(tech_text)))
# 输出: RAG检索增强 / 和 / LangChain框架 / 是 / 大模型 / 应用 / 的 / 核心

# ========== 批量处理 ==========
documents = [
    "人工智能正在改变世界",
    "深度学习是机器学习的重要分支",
    "大语言模型推动了NLP技术的发展",
]

for doc in documents:
    words = list(jieba.cut(doc))
    print(f"原文: {doc}")
    print(f"分词: {words}\n")

分词常见问题

python 复制代码
# 问题1:歧义切分
# "南京市长江大桥" 可能被切分为 "南京市/长江大桥" 或 "南京/市长/江大桥"
text = "南京市长江大桥"
print(jieba.lcut(text))
# 解决:添加自定义词典或使用更高级工具

# 问题2:新词识别
# "大语言模型" 在旧版jieba中可能不被识别
jieba.add_word("大语言模型")
jieba.add_word("提示词工程")
jieba.add_word("向量数据库")

# 问题3:领域适配
# 加载领域词典文件(每行一个词)
# jieba.load_userdict("domain_dict.txt")

2.2 命名实体识别:提取关键信息

命名实体识别(NER)的目标是从文本中自动识别出具有特定意义的实体,如人名、地名、机构名、时间、数字等。

python 复制代码
import jieba.posseg as pseg
import re

class EntityExtractor:
    """命名实体提取器"""
    
    def __init__(self):
        # 添加领域词典
        jieba.add_word("大语言模型")
        jieba.add_word("提示词工程")
        jieba.add_word("RAG技术")
        jieba.add_word("LangChain")
    
    def extract_entities(self, text):
        """从文本中提取命名实体"""
        words = pseg.cut(text)
        
        entities = {
            "人名": [],
            "地名": [],
            "机构名": [],
            "时间": [],
            "专有名词": [],
            "数字": [],
        }
        
        # 词性标签到实体类型的映射
        entity_map = {
            "nr": "人名",       # 人名
            "ns": "地名",       # 地名
            "nt": "机构名",     # 机构团体名
            "t": "时间",        # 时间词
            "nz": "专有名词",   # 其他专名
        }
        
        for word, flag in words:
            # 基于词性的实体识别
            if flag in entity_map:
                entities[entity_map[flag]].append(word)
            
            # 基于规则的实体识别
            # 提取数字(如价格、数量)
            if re.match(r'^\d+\.?\d*$', word):
                entities["数字"].append(word)
        
        # 过滤空列表
        return {k: v for k, v in entities.items() if v}

# 使用示例
extractor = EntityExtractor()

test_texts = [
    "2024年3月,阿里巴巴在杭州发布了通义千问大模型",
    "张三在北京的清华大学参加了AI技术峰会",
    "RAG技术结合向量数据库和大语言模型,提升了回答准确率",
]

for text in test_texts:
    print(f"\n原文: {text}")
    entities = extractor.extract_entities(text)
    for entity_type, words in entities.items():
        print(f"  {entity_type}: {', '.join(words)}")

# 输出:
# 原文: 2024年3月,阿里巴巴在杭州发布了通义千问大模型
#   时间: 2024年, 3月
#   机构名: 阿里巴巴
#   地名: 杭州
#   专有名词: 通义千问, 大模型

进阶:使用深度学习模型进行NER

python 复制代码
# 使用Hugging Face预训练模型进行命名实体识别
from transformers import pipeline

# 加载中文NER模型
ner_pipeline = pipeline(
    "token-classification",
    model="ckiplab/bert-base-chinese-ner",
    tokenizer="ckiplab/bert-base-chinese-ner"
)

text = "马云于1999年在杭州创立了阿里巴巴集团"
results = ner_pipeline(text)

# 整理结果
entities = {}
current_entity = ""
current_type = ""

for result in results:
    word = result['word']
    entity_type = result['entity']
    
    if entity_type.startswith('B-'):  # 实体开始
        if current_entity:
            entities[current_type] = entities.get(current_type, []) + [current_entity]
        current_type = entity_type[2:]
        current_entity = word.replace('##', '')
    elif entity_type.startswith('I-'):  # 实体继续
        current_entity += word.replace('##', '')

if current_entity:
    entities[current_type] = entities.get(current_type, []) + [current_entity]

print("深度学习NER结果:", entities)
# 输出: {'PER': ['马云'], 'LOC': ['杭州'], 'ORG': ['阿里巴巴集团']}

2.3 词性标注:识别语法角色

词性标注(POS Tagging)为每个词语分配一个词性标签,如名词、动词、形容词等,帮助理解句子的语法结构。

python 复制代码
import jieba.posseg as pseg

# 词性标注示例
text = "北京大学的学生在图书馆学习自然语言处理课程"
words = pseg.cut(text)

print("词性标注详细结果:")
print(f"{'词语':<10} {'词性':<5} {'说明'}")
print("-" * 35)

# 常见词性标签对照表
pos_explain = {
    'n': '名词', 'v': '动词', 'a': '形容词', 'd': '副词',
    'p': '介词', 'c': '连词', 'r': '代词', 'm': '数词',
    'q': '量词', 'u': '助词', 'f': '方位词', 's': '处所词',
    't': '时间词', 'nr': '人名', 'ns': '地名', 'nt': '机构名',
    'nz': '其他专名', 'vn': '名动词', 'an': '名形词',
}

for word, flag in words:
    explain = pos_explain.get(flag, '其他')
    print(f"{word:<10} {flag:<5} ({explain})")

# 输出:
# 词语         词性    说明
# -----------------------------------
# 北京大学     ns     (地名)
# 的           u      (助词)
# 学生         n      (名词)
# 在           p      (介词)
# 图书馆       n      (名词)
# 学习         v      (动词)
# 自然语言处理  nz     (其他专名)
# 课程         n      (名词)

词性标注的应用场景

python 复制代码
# 应用1:基于词性过滤(只保留名词和动词)
def extract_key_words(text):
    """提取关键词(仅保留名词和动词)"""
    words = pseg.cut(text)
    key_words = [word for word, flag in words if flag in ['n', 'v', 'nz', 'nt']]
    return key_words

text = "人工智能技术正在快速发展,深度学习模型在图像识别领域表现优异"
keywords = extract_key_words(text)
print(f"关键词: {keywords}")
# 输出: ['人工智能', '技术', '发展', '深度学习', '模型', '图像', '识别', '领域', '表现']

# 应用2:语法分析辅助
def analyze_sentence_structure(text):
    """简单分析句子结构"""
    words = list(pseg.cut(text))
    
    # 查找主语(名词 + 的 + 名词)
    subjects = []
    for i in range(len(words) - 2):
        if words[i][1] in ['n', 'ns', 'nt'] and words[i+1][1] == 'u' and words[i+2][1] in ['n', 'ns', 'nt']:
            subjects.append(words[i][0] + words[i+1][0] + words[i+2][0])
    
    # 查找谓语(动词)
    predicates = [word for word, flag in words if flag == 'v']
    
    return {
        "可能的主语": subjects,
        "谓语动词": predicates,
    }

result = analyze_sentence_structure("北京大学的学生在图书馆学习自然语言处理")
print(f"\n句子结构分析: {result}")

三、文本张量的表示方法

3.1 One-Hot编码:最基础的表示

One-Hot编码是最基础的文本表示方法:用高维稀疏向量表示词语

假设词表大小为5:{"我": 0, "喜欢": 1, "自然": 2, "语言": 3, "处理": 4}

python 复制代码
import numpy as np

# 定义词表
vocab = {"我": 0, "喜欢": 1, "自然": 2, "语言": 3, "处理": 4}
vocab_size = len(vocab)

def one_hot_encode(word, vocab):
    """将词语转换为One-Hot向量"""
    vector = np.zeros(len(vocab))
    vector[vocab[word]] = 1
    return vector

# 示例
print("One-Hot编码示例:")
for word in ["我", "喜欢", "自然", "语言", "处理"]:
    vector = one_hot_encode(word, vocab)
    print(f"  {word}: {vector}")

# 输出:
#   我: [1. 0. 0. 0. 0.]
#   喜欢: [0. 1. 0. 0. 0.]
#   自然: [0. 0. 1. 0. 0.]
#   语言: [0. 0. 0. 1. 0.]
#   处理: [0. 0. 0. 0. 1.]

One-Hot编码的三大缺陷

python 复制代码
# 缺陷1:维度灾难
# 工业级词表通常有几十万词,向量维度极高
large_vocab_size = 500000  # 50万词表
print(f"50万词表的One-Hot向量维度: {large_vocab_size}")
# 每个向量有50万个元素,但只有一个为1!

# 缺陷2:稀疏性
# 信息密度极低,99.9998%的元素都是0
sparsity = (large_vocab_size - 1) / large_vocab_size * 100
print(f"稀疏度: {sparsity:.6f}%")

# 缺陷3:无法表达语义相似度
from sklearn.metrics.pairwise import cosine_similarity

# "自然"和"语言"语义相关,但One-Hot向量正交
natural_vec = one_hot_encode("自然", vocab)
language_vec = one_hot_encode("语言", vocab)
process_vec = one_hot_encode("处理", vocab)

sim_natural_language = cosine_similarity([natural_vec], [language_vec])[0][0]
sim_natural_process = cosine_similarity([natural_vec], [process_vec])[0][0]

print(f"\n语义相似度(One-Hot):")
print(f"  '自然' vs '语言': {sim_natural_language:.4f}")  # 0.0000
print(f"  '自然' vs '处理': {sim_natural_process:.4f}")   # 0.0000
# 问题:无法区分语义相似和语义不同的词对!

图二:文本张量表示方法对比

(图二:文本表示方法从One-Hot稀疏编码演进到Word2Vec稠密向量,再到可训练的Word Embedding,语义表达能力不断提升)

3.2 Word2Vec:稠密词向量的里程碑

2013年,Google研究员Mikolov提出了Word2Vec 模型,将词向量从稀疏高维转变为稠密低维,同时能够捕获词语之间的语义关系。

Word2Vec包含两种架构:

架构 原理 特点
CBOW(连续词袋模型) 用上下文词语预测目标词 训练速度快,适合小规模数据
Skip-gram(跳字模型) 用目标词预测上下文词语 对罕见词效果好,适合大规模数据

Word2Vec实战示例

python 复制代码
import gensim
from gensim.models import Word2Vec

# 准备训练语料(AI技术文档)
corpus = [
    ["自然语言", "处理", "是", "人工智能", "的", "核心", "技术"],
    ["深度", "学习", "模型", "在", "图像", "识别", "中", "表现", "优异"],
    ["大", "语言", "模型", "如", "GPT", "和", "BERT", "推动", "了", "NLP", "发展"],
    ["RAG", "检索", "增强", "生成", "技术", "结合", "了", "向量", "数据库", "和", "大模型"],
    ["提示词", "工程", "是", "优化", "大模型", "输出", "质量", "的", "关键", "方法"],
    ["Transformer", "架构", "通过", "自", "注意力", "机制", "捕获", "长距离", "依赖"],
    ["自然语言", "理解", "和", "自然语言", "生成", "是", "NLP", "的", "两大", "方向"],
    ["词向量", "技术", "从", "One-Hot", "演进", "到", "Word2Vec", "再", "到", "BERT"],
]

# 训练Word2Vec模型
model = Word2Vec(
    sentences=corpus,
    vector_size=50,      # 词向量维度(稠密低维)
    window=3,            # 上下文窗口大小
    min_count=1,         # 最小词频
    workers=4,           # 并行线程数
    sg=0                 # 0=CBOW, 1=Skip-gram
)

# 查看词向量
print("词向量示例:")
print(f"  '自然语言' 的向量维度: {model.wv['自然语言'].shape}")
print(f"  '自然语言' 的向量(前10维): {model.wv['自然语言'][:10]}")

# 计算词相似度
print("\n词相似度计算:")
similar_words = model.wv.most_similar("自然语言", topn=3)
for word, score in similar_words:
    print(f"  {word}: {score:.4f}")

# 语义关系:相似的词在向量空间中距离更近
print("\n语义关系验证:")
sim_nlp_ai = model.wv.similarity("NLP", "人工智能")
sim_nlp_transformer = model.wv.similarity("NLP", "Transformer")
print(f"  'NLP' vs '人工智能': {sim_nlp_ai:.4f}")
print(f"  'NLP' vs 'Transformer': {sim_nlp_transformer:.4f}")

# 保存和加载模型
model.save("word2vec_ai_model.model")
loaded_model = Word2Vec.load("word2vec_ai_model.model")

Word2Vec vs One-Hot 对比

特性 One-Hot Word2Vec
向量维度 词表大小(几十万) 可配置(50-300)
稀疏性 极稀疏(99.99%为0) 稠密(所有元素都有值)
语义信息 有(相似词语向量接近)
计算效率 低(高维稀疏矩阵运算) 高(低维稠密矩阵运算)
一词多义 无法处理 无法处理(静态向量)

3.3 Word Embedding:可训练的嵌入层

Word Embedding是深度学习框架中的可训练嵌入层 ,它将词索引映射为稠密向量,并在模型训练过程中自动学习最优的词向量表示

图四:词向量空间语义关系

(图四:在词向量空间中,语义相似的词(如"猫"和"狗")距离更近,词向量能够捕获词语之间的语义关系和类比关系)

PyTorch中的Embedding层

python 复制代码
import torch
import torch.nn as nn

# ========== 基础Embedding层 ==========

# 定义Embedding层
# num_embeddings: 词表大小
# embedding_dim: 词向量维度
embedding = nn.Embedding(num_embeddings=10, embedding_dim=4)

# 查看嵌入权重(随机初始化)
print("初始嵌入权重:")
print(embedding.weight.data)
# 输出: 10个词,每个词4维向量

# 输入词索引
input_indices = torch.tensor([0, 2, 5, 3])  # 4个词的索引
print(f"\n输入索引: {input_indices}")

# 获取词向量
word_vectors = embedding(input_indices)
print(f"词向量形状: {word_vectors.shape}")  # [4, 4]
print(f"词向量内容:\n{word_vectors}")

# ========== 在完整模型中使用Embedding ==========

class TextClassifier(nn.Module):
    """基于Embedding的文本分类模型"""
    
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
        super(TextClassifier, self).__init__()
        
        # Embedding层:词索引 → 词向量
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        
        # 全连接层
        self.fc1 = nn.Linear(embed_dim, hidden_dim)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_dim, num_classes)
        
    def forward(self, x):
        # x: [batch_size, seq_length] 词索引序列
        # 获取词向量: [batch_size, seq_length, embed_dim]
        embedded = self.embedding(x)
        
        # 平均池化: [batch_size, embed_dim]
        pooled = embedded.mean(dim=1)
        
        # 全连接
        hidden = self.relu(self.fc1(pooled))
        output = self.fc2(hidden)
        
        return output

# 创建模型
vocab_size = 1000      # 词表大小
embed_dim = 64         # 词向量维度
hidden_dim = 128       # 隐藏层维度
num_classes = 3        # 分类数(正面/负面/中性)

model = TextClassifier(vocab_size, embed_dim, hidden_dim, num_classes)
print(f"\n模型结构:")
print(model)

# 模拟输入(batch_size=2, seq_length=5)
batch_input = torch.tensor([
    [1, 5, 3, 8, 2],   # 样本1的词索引序列
    [4, 7, 1, 9, 6],   # 样本2的词索引序列
])
print(f"\n输入形状: {batch_input.shape}")

# 前向传播
output = model(batch_input)
print(f"输出形状: {output.shape}")  # [2, 3]
print(f"输出内容:\n{output}")

# ========== 使用预训练词向量初始化Embedding ==========

# 假设我们有预训练的Word2Vec词向量
pretrained_vectors = torch.randn(1000, 64)  # 1000词 × 64维

# 创建Embedding层并用预训练向量初始化
embedding_pretrained = nn.Embedding.from_pretrained(
    pretrained_vectors,
    freeze=False  # False表示训练时继续更新词向量
)

print(f"\n预训练Embedding权重形状: {embedding_pretrained.weight.shape}")

Embedding层的核心优势

  1. 端到端学习:词向量与模型参数一起训练,自动优化
  2. 上下文感知:结合后续网络层,能够捕获更丰富的语义
  3. 迁移学习:可以使用预训练词向量(如Word2Vec、GloVe)初始化
  4. 灵活配置:维度、词表大小可根据任务调整

四、TensorBoard可视化实战

4.1 TensorBoard Embedding Projector简介

图三:TensorBoard可视化界面

(图三:TensorBoard Embedding Projector提供3D可视化界面,可以直观查看高维词向量的分布、聚类情况和语义关系)

TensorBoard是TensorFlow/PyTorch的可视化工具,其中的Embedding Projector专门用于可视化高维词向量:

  • 3D散点图:将高维词向量降维到3D空间展示
  • 降维算法:支持PCA、t-SNE、UMAP等多种降维方法
  • 交互操作:可以旋转、缩放、搜索特定词语
  • 聚类分析:语义相似的词会自动聚集在一起

4.2 词向量可视化完整代码

python 复制代码
import torch
import torch.nn as nn
from torch.utils.tensorboard import SummaryWriter
import numpy as np
import os

# ========== 步骤1:准备词表和词向量 ==========

# 定义词表
vocab = [
    "自然语言", "处理", "技术", "人工智能", "深度学习",
    "模型", "训练", "数据", "算法", "神经网络",
    "分类", "情感", "正面", "负面", "中性",
    "文本", "分词", "词性", "实体", "识别",
]

vocab_size = len(vocab)
embed_dim = 50  # 词向量维度

# 创建Embedding层(随机初始化)
embedding = nn.Embedding(vocab_size, embed_dim)

# 或者使用预训练向量初始化
# pretrained_vectors = torch.load("pretrained_vectors.pt")
# embedding = nn.Embedding.from_pretrained(pretrained_vectors)

# ========== 步骤2:准备元数据(标签和颜色) ==========

# 为每个词分配类别(用于着色)
# 类别:0=技术相关, 1=任务相关, 2=情感相关, 3=NLP基础
categories = [
    0, 0, 0, 0, 0,  # 技术相关
    0, 0, 0, 0, 0,  # 技术相关
    1, 2, 2, 2, 2,  # 任务/情感
    3, 3, 3, 3, 3,  # NLP基础
]

# 颜色映射
category_colors = {
    0: "#FF6B6B",  # 技术相关 - 红色
    1: "#4ECDC4",  # 任务相关 - 青色
    2: "#45B7D1",  # 情感相关 - 蓝色
    3: "#96CEB4",  # NLP基础 - 绿色
}

# 生成metadata文件(词标签)
metadata_path = "logs/embedding/metadata.tsv"
os.makedirs("logs/embedding", exist_ok=True)

with open(metadata_path, 'w', encoding='utf-8') as f:
    for word in vocab:
        f.write(f"{word}\n")

# 生成sprite图像(可选,用于显示词标签)
# 这里简化处理,实际项目中可以生成包含所有词的图片

# ========== 步骤3:写入TensorBoard ==========

# 创建SummaryWriter
writer = SummaryWriter("logs/embedding")

# 获取词向量
word_vectors = embedding.weight.data

# 添加到TensorBoard
writer.add_embedding(
    word_vectors,                    # 词向量矩阵 [vocab_size, embed_dim]
    metadata=vocab,                  # 词标签列表
    label_img=None,                  # 可选:每个词对应的图像
    global_step=0                    # 训练步数
)

writer.close()
print("词向量已写入TensorBoard日志")
print(f"日志路径: logs/embedding/")

# ========== 步骤4:启动TensorBoard查看 ==========
# 在终端运行:
# tensorboard --logdir=logs/embedding
# 然后在浏览器打开: http://localhost:6006
# 点击 "Projector" 标签页查看3D可视化

TensorBoard可视化效果说明

python 复制代码
# 在TensorBoard Embedding Projector中,你可以:

# 1. 选择降维算法
# - PCA(主成分分析):线性降维,保留最大方差方向
# - t-SNE:非线性降维,擅长展示局部聚类结构
# - UMAP:较新的降维算法,平衡全局和局部结构

# 2. 观察聚类情况
# - 语义相似的词会自动聚集在一起
# - 例如:"正面"、"负面"、"中性"会形成情感聚类
# - "分词"、"词性"、"实体"会形成NLP基础聚类

# 3. 搜索特定词
# - 在搜索框输入词语,高亮显示该词及其邻居
# - 直观查看语义关系

# 4. 分析异常点
# - 远离聚类的词可能是:
#   - 多义词(在不同语境下含义不同)
#   - 训练数据中的噪声
#   - 词向量学习不充分

完整训练流程中的Embedding可视化

python 复制代码
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter

class SentimentModel(nn.Module):
    """情感分析模型(带Embedding可视化)"""
    
    def __init__(self, vocab_size, embed_dim, hidden_dim):
        super(SentimentModel, self).__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, 3)  # 正面/负面/中性
    
    def forward(self, x):
        embedded = self.embedding(x)
        lstm_out, (hidden, _) = self.lstm(embedded)
        output = self.fc(hidden[-1])
        return output

# 训练配置
vocab_size = 1000
embed_dim = 64
hidden_dim = 128

model = SentimentModel(vocab_size, embed_dim, hidden_dim)
optimizer = optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()

# TensorBoard日志
writer = SummaryWriter("logs/training")

# 模拟训练循环
for epoch in range(10):
    # 模拟训练步骤
    model.train()
    
    # 每2个epoch可视化一次Embedding
    if epoch % 2 == 0:
        word_vectors = model.embedding.weight.data
        writer.add_embedding(
            word_vectors,
            metadata=[f"word_{i}" for i in range(min(100, vocab_size))],
            global_step=epoch
        )
    
    # 记录损失
    fake_loss = 1.0 / (epoch + 1)  # 模拟损失下降
    writer.add_scalar("Loss/train", fake_loss, epoch)

writer.close()
print("训练完成,TensorBoard日志已保存")
print("运行 tensorboard --logdir=logs/training 查看可视化结果")

常见问题

Q1:分词工具选哪个?jieba还是HanLP?

:根据项目需求选择:

工具 优点 缺点 适用场景
jieba 轻量、易用、社区活跃 准确率一般、功能单一 快速原型、中小型项目
HanLP 功能全面、准确率高 较重、学习曲线陡 企业级应用、高准确率要求
spaCy 多语言、工业级性能 中文支持较弱 多语言项目
LTP 哈工大出品、学术可靠 更新较慢 学术研究

建议 :先用 jieba 快速验证,再根据需求升级到 HanLP

Q2:One-Hot编码现在还有用吗?

:在以下场景仍有价值:

  1. 词表极小(<100词):如特定领域的关键词分类
  2. 教学演示:理解文本表示的基础概念
  3. 与其他特征拼接:One-Hot特征 + 数值特征一起输入模型

但在大多数NLP任务中,Word2Vec或Embedding层是更好的选择。

Q3:Embedding维度怎么选?越大越好吗?

:需要根据任务和数据量权衡:

场景 推荐维度 原因
小型数据集(<10万句) 32-64 避免过拟合
中型数据集(10万-100万句) 64-128 平衡表达力和泛化
大型数据集(>100万句) 128-300 充分捕获语义信息
预训练模型(BERT等) 768/1024 模型架构固定

维度太小会丢失语义信息,维度太大会增加计算开销且容易过拟合。

Q4:TensorBoard可视化后词向量没有聚类效果怎么办?

:可能的原因和解决方案:

  1. 训练不充分:增加训练轮数,确保Embedding层充分学习
  2. 学习率过高:降低学习率,使用学习率调度器
  3. 数据量不足:增加训练数据,或使用预训练词向量初始化
  4. 降维算法选择:尝试不同的降维算法(PCA/t-SNE/UMAP)
  5. 词表质量问题:检查词表是否包含过多噪声词
python 复制代码
# 使用预训练词向量初始化的示例
import torch.nn as nn

# 加载预训练词向量(如Word2Vec、GloVe)
pretrained_vectors = torch.load("glove_chinese.pt")  # [vocab_size, embed_dim]

# 初始化Embedding层
embedding = nn.Embedding.from_pretrained(
    pretrained_vectors,
    freeze=False  # 训练时继续微调
)

和AI大模型开发的关系

文本预处理和张量表示是AI大模型开发的底层基础能力,大模型的许多核心功能都建立在这些技术之上。以下是4个典型应用场景:

场景1:大模型输入预处理管道

大模型接收用户输入前,需要进行文本清洗和分词:

python 复制代码
import jieba
import re
import torch

class LLMInputPipeline:
    """大模型输入预处理管道"""
    
    def __init__(self, vocab_size=30000, embed_dim=768):
        # 加载领域词典
        jieba.add_word("RAG")
        jieba.add_word("LangChain")
        jieba.add_word("Coze")
        jieba.add_word("Dify")
        jieba.add_word("大语言模型")
        
        # 创建词表映射
        self.vocab = {"[PAD]": 0, "[UNK]": 1, "[CLS]": 2, "[SEP]": 3}
        self.word_to_id = {**self.vocab}
        self.id_to_word = {v: k for k, v in self.word_to_id.items()}
        
        # Embedding层
        self.embedding = torch.nn.Embedding(vocab_size, embed_dim)
    
    def clean_text(self, text):
        """清洗文本"""
        # 去除HTML标签
        text = re.sub(r'<[^>]+>', '', text)
        # 去除特殊字符(保留中文、英文、数字)
        text = re.sub(r'[^\w\s\u4e00-\u9fff]', '', text)
        # 去除多余空格
        text = re.sub(r'\s+', ' ', text).strip()
        return text
    
    def tokenize(self, text):
        """分词并转换为词索引"""
        words = jieba.lcut(text)
        # 转换为词索引(未知词用[UNK])
        indices = [self.word_to_id.get(w, 1) for w in words]
        return words, indices
    
    def process(self, user_input):
        """完整处理流程"""
        cleaned = self.clean_text(user_input)
        words, indices = self.tokenize(cleaned)
        
        # 转换为张量
        tensor_input = torch.tensor([indices])
        
        # 获取词向量
        word_vectors = self.embedding(tensor_input)
        
        return {
            "original": user_input,
            "cleaned": cleaned,
            "words": words,
            "indices": indices,
            "tensor_shape": tensor_input.shape,
            "vector_shape": word_vectors.shape,
        }

# 使用示例
pipeline = LLMInputPipeline()
result = pipeline.process("请问RAG和LangChain有什么区别?")
print(f"原始输入: {result['original']}")
print(f"清洗后: {result['cleaned']}")
print(f"分词: {result['words']}")
print(f"词索引: {result['indices']}")
print(f"张量形状: {result['tensor_shape']}")
print(f"词向量形状: {result['vector_shape']}")

场景2:意图识别中的文本特征提取

智能客服需要先识别用户意图,文本预处理是关键步骤:

python 复制代码
import jieba.posseg as pseg
import torch
import torch.nn as nn

class IntentFeatureExtractor:
    """意图识别特征提取器"""
    
    def __init__(self, vocab_size=5000, embed_dim=128):
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.vocab = {"[PAD]": 0, "[UNK]": 1}
        
        # 意图类别
        self.intents = {
            "product_query": "产品咨询",
            "technical_support": "技术支持",
            "billing": "账单问题",
            "complaint": "投诉建议",
        }
    
    def extract_features(self, text):
        """提取文本特征用于意图识别"""
        # 1. 分词
        words = jieba.lcut(text)
        
        # 2. 词性标注
        pos_tags = [flag for _, flag in pseg.cut(text)]
        
        # 3. 命名实体识别
        entities = self._extract_entities(text)
        
        # 4. 转换为词索引
        indices = [self.vocab.get(w, 1) for w in words]
        
        # 5. 获取词向量
        tensor_input = torch.tensor([indices])
        word_vectors = self.embedding(tensor_input)
        
        # 6. 聚合特征(平均池化)
        sentence_vector = word_vectors.mean(dim=1)
        
        return {
            "words": words,
            "pos_tags": pos_tags,
            "entities": entities,
            "sentence_vector": sentence_vector,
            "vector_dim": sentence_vector.shape[1],
        }
    
    def _extract_entities(self, text):
        """简单实体提取"""
        words = pseg.cut(text)
        entities = []
        for word, flag in words:
            if flag in ['nr', 'ns', 'nt']:  # 人名、地名、机构名
                entities.append((word, flag))
        return entities

# 使用示例
extractor = IntentFeatureExtractor()
test_inputs = [
    "这个AI音箱怎么连接WiFi?",
    "我的订单还没收到,已经等了三天了",
    "你们的产品价格太贵了",
]

for text in test_inputs:
    features = extractor.extract_features(text)
    print(f"\n输入: {text}")
    print(f"  分词: {features['words']}")
    print(f"  实体: {features['entities']}")
    print(f"  句子向量维度: {features['vector_dim']}")

场景3:RAG系统中的文档向量化

RAG(检索增强生成)系统需要将文档切分并转换为向量:

python 复制代码
import jieba
import torch
import torch.nn as nn
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

class RAGDocumentProcessor:
    """RAG系统文档处理器"""
    
    def __init__(self, vocab_size=10000, embed_dim=256, chunk_size=200):
        self.chunk_size = chunk_size
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        self.vocab = {"[PAD]": 0, "[UNK]": 1}
    
    def chunk_document(self, document):
        """将长文档切分为小块"""
        # 按句子切分
        sentences = document.replace('。', '。\n').replace('!', '!\n').replace('?', '?\n')
        sentences = [s.strip() for s in sentences.split('\n') if s.strip()]
        
        chunks = []
        current_chunk = []
        current_length = 0
        
        for sentence in sentences:
            sentence_length = len(sentence)
            
            if current_length + sentence_length > self.chunk_size:
                chunks.append('。'.join(current_chunk) + '。')
                # 保留重叠部分
                overlap = current_chunk[-2:] if len(current_chunk) > 2 else current_chunk
                current_chunk = overlap + [sentence]
                current_length = sum(len(s) for s in current_chunk)
            else:
                current_chunk.append(sentence)
                current_length += sentence_length
        
        if current_chunk:
            chunks.append('。'.join(current_chunk) + '。')
        
        return chunks
    
    def document_to_vector(self, document):
        """将文档转换为向量"""
        # 分词
        words = jieba.lcut(document)
        
        # 转换为词索引
        indices = [self.vocab.get(w, 1) for w in words]
        
        # 获取词向量
        tensor_input = torch.tensor([indices])
        word_vectors = self.embedding(tensor_input)
        
        # 平均池化得到文档向量
        doc_vector = word_vectors.mean(dim=1)
        
        return doc_vector
    
    def build_index(self, documents):
        """构建向量索引"""
        all_chunks = []
        chunk_vectors = []
        
        for doc in documents:
            chunks = self.chunk_document(doc)
            for chunk in chunks:
                all_chunks.append(chunk)
                vector = self.document_to_vector(chunk)
                chunk_vectors.append(vector)
        
        # 合并所有向量
        index_vectors = torch.cat(chunk_vectors, dim=0)
        
        return {
            "chunks": all_chunks,
            "vectors": index_vectors,
            "chunk_count": len(all_chunks),
        }
    
    def search(self, query, index, top_k=3):
        """检索相关文档块"""
        # 查询向量化
        query_vector = self.document_to_vector(query)
        
        # 计算余弦相似度
        index_np = index["vectors"].detach().numpy()
        query_np = query_vector.detach().numpy()
        similarities = cosine_similarity(query_np, index_np)[0]
        
        # 返回top-k结果
        top_indices = np.argsort(similarities)[-top_k:][::-1]
        
        results = []
        for idx in top_indices:
            results.append({
                "chunk": index["chunks"][idx],
                "similarity": float(similarities[idx]),
            })
        
        return results

# 使用示例
processor = RAGDocumentProcessor(chunk_size=150)

documents = [
    "自然语言处理是人工智能的核心技术之一。它包括分词、词性标注、命名实体识别等基础任务。"
    "现代NLP技术已经从传统的统计方法发展到深度学习时代。",
    
    "RAG检索增强生成技术结合了向量检索和大语言模型。"
    "它通过检索相关文档片段,为大模型提供上下文信息。",
]

index = processor.build_index(documents)
print(f"文档切分: {index['chunk_count']} 个块")

query = "什么是RAG技术?"
results = processor.search(query, index, top_k=2)

print(f"\n查询: {query}")
for i, r in enumerate(results, 1):
    print(f"  {i}. [相似度: {r['similarity']:.4f}] {r['chunk'][:40]}...")

场景4:大模型微调中的数据预处理

在大模型微调(如LoRA/QLoRA)时,文本预处理和张量表示是关键步骤:

python 复制代码
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

class FineTuningDataProcessor:
    """大模型微调数据处理器"""
    
    def __init__(self, model_name="Qwen/Qwen2.5-0.5B"):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name)
        self.model = AutoModelForCausalLM.from_pretrained(model_name)
        
        print(f"词表大小: {len(self.tokenizer)}")
        print(f"模型嵌入维度: {self.model.config.hidden_size}")
    
    def preprocess_instruction_data(self, instruction, input_text="", output_text=""):
        """预处理指令微调数据"""
        # 构建指令模板
        if input_text:
            prompt = f"### 指令:\n{instruction}\n\n### 输入:\n{input_text}\n\n### 输出:\n"
        else:
            prompt = f"### 指令:\n{instruction}\n\n### 输出:\n"
        
        # 完整文本
        full_text = prompt + output_text
        
        # Tokenization
        inputs = self.tokenizer(
            prompt,
            return_tensors="pt",
            truncation=True,
            max_length=512,
        )
        
        labels = self.tokenizer(
            full_text,
            return_tensors="pt",
            truncation=True,
            max_length=512,
        )
        
        # 获取嵌入向量
        with torch.no_grad():
            embeddings = self.model.get_input_embeddings()(inputs["input_ids"])
        
        return {
            "prompt": prompt,
            "input_ids": inputs["input_ids"],
            "attention_mask": inputs["attention_mask"],
            "labels": labels["input_ids"],
            "embeddings_shape": embeddings.shape,
            "token_count": inputs["input_ids"].shape[1],
        }

# 使用示例
processor = FineTuningDataProcessor()

# 指令微调数据示例
data = processor.preprocess_instruction_data(
    instruction="请解释什么是RAG技术",
    output_text="RAG(检索增强生成)是一种结合信息检索和文本生成的技术..."
)

print(f"\n预处理结果:")
print(f"  Prompt: {data['prompt'][:50]}...")
print(f"  Token数量: {data['token_count']}")
print(f"  嵌入向量形状: {data['embeddings_shape']}")

总结

本文系统梳理了文本预处理与张量表示的核心技术:

文本预处理:从原始文本到干净数据,经历清洗、分词、词性标注、命名实体识别等环节,是NLP任务的基础。jieba、HanLP等工具让中文分词变得简单高效。

张量表示方法:从One-Hot稀疏编码到Word2Vec稠密向量,再到可训练的Word Embedding,文本表示技术不断演进,语义表达能力呈指数级提升。Embedding层已成为深度学习模型的标配组件。

可视化分析:TensorBoard Embedding Projector提供3D可视化能力,帮助开发者直观理解词向量的分布和语义关系,是调试和优化模型的重要工具。

#文本预处理 #分词 #词性标注 #命名实体识别 #OneHot #Word2Vec #WordEmbedding #TensorBoard #NLP #AI大模型

相关推荐
AI的探索之旅23 分钟前
97 个 OpenCV 实例(十七):图片读写,FileStorage 与 JPEG 采样
人工智能·opencv·计算机视觉
Forerror202626 分钟前
API网关怎么选?MAI Gateway vs 开源方案全面对比
人工智能·ai工具·maigateway·企业ai网关·企业级大模型网关
Likeadust29 分钟前
一键创建,说开就开:私有化音视频系统EasyDSS视频会议的入会、协作与AI纪要全流程
人工智能·音视频·easydss
小陈phd29 分钟前
电商客服智能体状态机编排:基于 LangGraph 的双轨意图识别与分级降级工程实践
人工智能
code_pgf30 分钟前
总体目标与系统架构
人工智能·机器人
科技风向标go32 分钟前
奥维云网×洛图科技:2026监控品牌“功能延伸”vs垂直品牌“底层设计”——品牌选型与产业趋势
网络·人工智能·科技·户外安防
小王20432 分钟前
Day 33:目标跟踪基础 — SORT与DeepSORT
人工智能·计算机视觉·目标跟踪
您^_^33 分钟前
DeepSeek-Harness 升级排障完全指南:三类本地残留逐个拆解
人工智能·windows·个人开发·deepseekharness·deepseekv4pro
旖旎夜光40 分钟前
【AI入门】大模型介绍全解析:从模型、LLM 到提示词与嵌入
人工智能·笔记·python·学习·ai编程