文本喂给模型前要做啥?文本预处理与张量表示全解析
关键词:文本预处理、分词、词性标注、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>
"""
这样的文本直接喂给模型会有什么问题?
- HTML标签干扰 :
<html>、<div>等标签不是有效语义信息 - 噪声数据:广告、版权信息、导航栏等无关内容
- 格式不统一:大小写混用、多余空格、特殊符号
- 无法直接计算:模型只能处理数值,不能处理文本字符串
文本预处理的核心目标 :将原始的非结构化文本,转化为干净、结构化、数值化的数据,让模型能够高效学习。
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层的核心优势:
- 端到端学习:词向量与模型参数一起训练,自动优化
- 上下文感知:结合后续网络层,能够捕获更丰富的语义
- 迁移学习:可以使用预训练词向量(如Word2Vec、GloVe)初始化
- 灵活配置:维度、词表大小可根据任务调整
四、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编码现在还有用吗?
答:在以下场景仍有价值:
- 词表极小(<100词):如特定领域的关键词分类
- 教学演示:理解文本表示的基础概念
- 与其他特征拼接:One-Hot特征 + 数值特征一起输入模型
但在大多数NLP任务中,Word2Vec或Embedding层是更好的选择。
Q3:Embedding维度怎么选?越大越好吗?
答:需要根据任务和数据量权衡:
| 场景 | 推荐维度 | 原因 |
|---|---|---|
| 小型数据集(<10万句) | 32-64 | 避免过拟合 |
| 中型数据集(10万-100万句) | 64-128 | 平衡表达力和泛化 |
| 大型数据集(>100万句) | 128-300 | 充分捕获语义信息 |
| 预训练模型(BERT等) | 768/1024 | 模型架构固定 |
维度太小会丢失语义信息,维度太大会增加计算开销且容易过拟合。
Q4:TensorBoard可视化后词向量没有聚类效果怎么办?
答:可能的原因和解决方案:
- 训练不充分:增加训练轮数,确保Embedding层充分学习
- 学习率过高:降低学习率,使用学习率调度器
- 数据量不足:增加训练数据,或使用预训练词向量初始化
- 降维算法选择:尝试不同的降维算法(PCA/t-SNE/UMAP)
- 词表质量问题:检查词表是否包含过多噪声词
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大模型