全部文章

02.文本张量表示方法2

 一、什么是文本张量表示

1.1 直观理解

想象你要教计算机读懂这句话:
​"我爱自然语言处理"​

计算机只认识数字,所以我们需要:

  1. 把每个​​词​​变成​​数字向量​​(词向量)
  2. 把整句话变成​​数字矩阵​​(文本张量)

1.2 举个栗子 🌰

文本 = ["我", "爱", "自然语言处理"]

# 假设每个词对应一个3维向量
我   -> [0.2, 0.5, 0.1]
爱   -> [0.7, 0.3, 0.4]
自然语言处理 -> [0.9, 0.2, 0.8]

# 最终文本张量(3个词×3维向量)
[[0.2, 0.5, 0.1],
 [0.7, 0.3, 0.4],
 [0.9, 0.2, 0.8]]

二、为什么要用张量表示

  1. ​统一格式​​:所有文本都能转为固定维度的数字
  2. ​保留语义​​:相似的词有相似的向量(比如"猫"和"狗"的向量比"猫"和"电脑"更接近)
  3. ​适配模型​​:神经网络可以直接处理这些数字

​三、4种常见的文本张量表示方法​

3.1 One-Hot编码(最基础)

  • ​原理​​:每个词用一个很长的0/1向量表示。又称独热编码,将每个词表示成具有 n 个元素的向量,这个词向量中只有一个元素是 1,其他元素都是 0,不同词汇元素为 1 的位置不同,其中 n 的大小是整个语料中不同词汇的总数。
  • ​示例​​:
# 词汇表 = ["我", "爱", "自然语言处理"]
"我" -> [1, 0, 0]
"爱" -> [0, 1, 0]
"自然语言处理" -> [0, 0, 1]
  • 优势​​:操作简单,容易理解
  • ​劣势​​:维度高、无法表达词之间的关系,完全割裂了词与词之间的联系,而且在大语料集下,每个向量的长度过大,占据大量内存

​说明​​:正因为one-hot编码明显的劣势,这种编码方式被应用的地方越来越少,取而代之的是稠密向量的表示方法:word embedding。

3.2 词频统计(TF)

  • ​原理​​:统计每个词在句子中出现的次数
  • ​示例​​:
    # 句子:"我爱自然语言处理,我也爱机器学习"
    {"我":2, "爱":2, "自然语言处理":1, "机器学习":1}
    • 步骤 1:确定 “词表”(所有可能出现的词的集合)
      假设我们只有 3 个句子(文本集合),先把所有句子里的词去重,得到一个 “词表”(相当于给每个词编一个 “位置编号”):
      词表 = ["我", "爱", "自然", "语言", "处理", "今天", "天气", "很", "好", "学习"]
      (共 10 个词,每个词对应一个索引:0→“我”,1→“爱”,…,9→“学习”)
      如果有 3 个句子,每个句子都生成一个 TF 向量,组合起来就是一个TF 矩阵(行数 = 句子数,列数 = 词表长度):

      句子 TF 张量(对应词表顺序)
      我爱学习自然语言处理 [1,1,1,1,1,0,0,0,0,1]
      今天天气很好 [0,0,0,0,0,1,1,1,1,0]
      我爱今天学习 [1,1,0,0,0,1,0,0,0,1]

     TF 有什么用?

    TF 是文本表示的 “入门工具”,优点是简单、计算快,适合对精度要求不高的场景:
    • 快速区分 “主题差异大” 的文本:比如用 TF 矩阵可以轻松区分 “科技类句子”(含 “自然”“语言”“处理”)和 “天气类句子”(含 “今天”“天气”“好”);
    • 辅助关键词初步筛选:比如在一篇 “猫的习性” 文章中,TF 高的词(“猫”“抓老鼠”“睡觉”)大概率是核心词;

3.3 TF-IDF

  • ​原理​​:考虑词在全文中的重要性(常见词如"的"权重降低)
  • ​公式​​:TF-IDF = 词频(TF) × 逆文档频率(IDF)

TF-IDF是什么?​

1.1 生活比喻 🍎

想象你在分析100篇美食文章:

  • ​TF(词频)​​:"蛋糕"在某篇文章中出现5次 → 说明这篇文章可能重点讲蛋糕
  • ​IDF(逆文档频率)​​:但"的"字在90篇文章都出现过 → 说明"的"不重要
  • ​TF-IDF​​ = TF × IDF → 同时考虑​​局部重要性​​和​​全局重要性​

1.2 数学公式

  • :总文档数
  • :包含词的文档数

完整代码示例​

2.1 使用scikit-learn计算TF-IDF

from sklearn.feature_extraction.text import TfidfVectorizer

# 示例文档集
documents = [
    "我爱自然语言处理",
    "自然语言处理很有趣",
    "我爱深度学习"
]

# 初始化TF-IDF向量器
tfidf = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b")  # 适配中文

# 计算TF-IDF矩阵
tfidf_matrix = tfidf.fit_transform(documents)

# 打印结果
print("词汇表:", tfidf.get_feature_names_out())
print("TF-IDF矩阵:\n", tfidf_matrix.toarray().round(3))

输出解析

词汇表: ['我爱' '有趣' '深度' '学习' '自然' '语言' '处理']
TF-IDF矩阵:
 [[0.707 0.    0.    0.    0.354 0.354 0.354]
 [0.    0.454 0.    0.    0.362 0.362 0.362]
 [0.577 0.    0.577 0.577 0.    0.    0.   ]]
  • ​第一行解读​​("我爱自然语言处理"):
    • "我爱"权重最高(0.707)→ 只在该文档出现
    • "自然/语言/处理"权重相同(0.354)→ 同时在第二篇出现

为什么后来有了 “词嵌入(Embedding)”?

你提到 “词嵌入是重点”,这里也简单对比一下,帮你理解三者的关系:
  • TF/TF-IDF:是 “基于统计的稀疏向量”—— 词表有多大,向量就有多长(比如词表 10 万维,向量就是 10 万维),但大部分数值是 0(稀疏),且无法体现词的 “语义关联”(比如 “猫” 和 “狗” 的 TF/IDF 向量完全独立,看不出都是 “动物”);
  • 词嵌入(如 Word2Vec、BERT Embedding):是 “基于语义的稠密向量”—— 向量维度固定(比如 768 维),每个数值都有意义(稠密),且能体现语义关联(“猫” 和 “狗” 的向量相似度很高)。
TF/TF-IDF 并没有被淘汰—— 它们依然是 “轻量级文本任务” 的首选(比如小数据量的关键词提取),而词嵌入更适合 “复杂语义理解”(比如情感分析、机器翻译)。

3.4 ​​词嵌入(Embedding)⭐(重点!)​

  • ​原理​​:通过神经网络学习到的​​低维度稠密向量​
  • ​特点​​:
    • 向量维度通常50-300维
    • 语义相似的词向量距离近
    • 可进行数学运算(如:国王 - 男 + 女 ≈ 女王)​

生活比喻 🍎

把词嵌入想象成​​"词的世界地图"​​:

      • 每个词是一个城市
      • 向量坐标是经纬度
      • 语义相近的词距离近(如"苹果"和"香蕉"都在"水果区")

Embedding核心概念对比​

概念​ ​定义​ ​特点​
​Word Embedding​ 将单词映射到低维稠密向量的技术(统称) 是技术范畴的统称,包含Word2vec/GloVe/fasttext等具体实现
​Word2vec​ 2013年Google提出的词嵌入模型(CBOW/Skip-gram两种架构) 基于局部上下文窗口预测,轻量高效
​fasttext​ Facebook提出的模型(可视为Word2vec扩展) 引入子词(subword)信息,能处理未登录词
​GloVe​ 2014年斯坦福提出的全局词向量模型 结合全局统计信息(共现矩阵)和局部窗口

如何选择?​

​场景​ ​推荐模型​ ​理由​
通用英语语义任务 GloVe 全局统计信息更稳定
形态复杂语言(如中文) fasttext 子词信息能更好处理复合词
超大规模数据 fasttext 训练速度优势明显
需要最新技术 BERT/Transformer 上下文感知的嵌入


性能对比数据​

​模型​ ​英语词义相似度任务(Spearman)​ ​训练速度(10亿词)​
Word2vec 0.72 2小时
GloVe 0.75 6小时
fasttext 0.73 1.5小时
BERT 0.85 3天(需GPU)

 

四、如何训练Embedding?

​随机初始化​​ → 训练时自动调整

特点​​:

    • 向量值是​​随机生成​​的(无实际语义)
    • 需要​​后续训练​​(如参与神经网络的梯度下降)
    • 适合​​从头开始训练​​的小规模任务

PyTorch实现:

import torch
import torch.nn as nn
# 1. 准备词汇表
vocab = {"我": 0, "爱": 1, "自然语言处理": 2}
# 2. 创建Embedding层(5个词,每个词5维向量)
embedding = nn.Embedding(num_embeddings=5, embedding_dim=5)
# 打印随机生成的词向量
print("随机生成的词向量:\n",embedding.weight)
'''
随机生成的词向量:
 Parameter containing:
tensor([[-0.5595, -1.3123,  2.2185, -0.9516, -1.3197],
        [ 0.9368, -2.1644, -0.6457, -1.3976, -0.2549],
        [ 0.3237,  0.1061,  0.1003,  1.1844,  1.6272],
        [ 1.0584, -0.7588, -0.4565,  1.1612,  2.1809],
        [ 1.0037, -0.5925, -0.8589, -1.4178,  1.0611]], requires_grad=True)
'''
# 3. 将句子转为张量
input_indices = torch.tensor([vocab[key] for key in vocab.keys()])  # 单词→索引
# 4. 获取词向量
output = embedding(input_indices)
print(output)
"""
输出示例:
tensor([[-1.0122,  1.0166, -0.0915, -0.6345, -0.5214],# "我"的向量
        [ 0.7933, -2.3221,  0.9407,  0.5492,  0.5259],# "爱"的向量
        [ 0.0296, -0.7057, -0.5741, -0.0407, -0.2453]],# "自然语言处理"的向量
       grad_fn=<EmbeddingBackward0>)
"""

场景:判断句子情感(正面/负面)

import torch.nn as nn

class TextClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim)  # 随机初始化
        self.fc = nn.Linear(embed_dim, 2)  # 二分类
    
    def forward(self, text):
        embedded = self.embedding(text).mean(dim=1)  # 平均词向量
        return self.fc(embedded)

# 训练过程
model = TextClassifier(vocab_size=1000, embed_dim=100)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())

for epoch in range(10):
    for text, label in dataloader:
        optimizer.zero_grad()
        output = model(text)
        loss = criterion(output, label)
        loss.backward()  # 反向传播会计算这个嵌入矩阵中每个元素的梯度;
        optimizer.step()#会根据这些梯度更新嵌入矩阵的值(更新embedding层的参数)!使得嵌入向量能更好地适应下游分类任务。

关键点​​:

  1. 初始的Embedding是随机的
  2. 通过​​loss.backward()​​,optimizer.step()自动调整Embedding矩阵
  3. 最终得到的词向量会编码​​任务相关语义​

Embedding训练前后对比

初始随机状态:
"快乐" -> [0.12, -0.45, 0.33]
"悲伤" -> [0.67, 0.21, -0.89]  # 两词向量无关

训练后:
"快乐" -> [0.82, 0.75, 0.91]
"悲伤" -> [-0.93, -0.84, -0.79]  # 语义相反→向量方向相反

​使用预训练​​词向量(如GloVe、Word2Vec)

# 加载预训练GloVe
from torchtext.vocab import GloVe
glove = GloVe(name='6B', dim=50)  # 50维向量
# print(glove['apple'])  # 输出苹果的词向量

# GloVe版(真实语义)
print(glove['king'] - glove['man'] + glove['woman'])  # 结果接近glove['queen']
print(glove['queen'])
tensor([ 0.4174,  0.9043, -1.0050, -0.0620,  0.4973,  0.8067, -0.1486,  0.8037,
        -0.1565, -0.6697,  0.2344,  0.6248,  0.9259, -0.9710,  0.9257,  0.8992,
        -1.5460, -0.5263,  0.1370,  0.6620,  0.4872,  0.3704, -0.2142,  0.1010,
         0.7136, -2.0875, -1.1362, -1.1496, -0.5360,  0.2739,  1.6723,  0.0293,
        -0.7766,  0.4606,  0.3487, -0.0574,  0.1944, -0.2077, -0.7304, -0.1075,
         0.2355,  0.9642, -0.4699, -0.4873, -0.2540,  0.4621, -0.6608, -1.9451,
        -0.6880, -0.4978])
tensor([ 0.3785,  1.8233, -1.2648, -0.1043,  0.3583,  0.6003, -0.1754,  0.8377,
        -0.0568, -0.7580,  0.2268,  0.9859,  0.6059, -0.3142,  0.2888,  0.5601,
        -0.7746,  0.0714, -0.5741,  0.2134,  0.5767,  0.3868, -0.1257,  0.2801,
         0.2813, -1.8053, -1.0421, -0.1926, -0.5537, -0.0545,  1.5574,  0.3930,
        -0.2475,  0.3425,  0.4536,  0.1624,  0.5246, -0.0703, -0.8374, -1.0326,
         0.4595,  0.2530, -0.1784, -0.7340, -0.2002,  0.2347, -0.5609, -2.2839,
         0.0093, -0.6028])

预训练 vs 随机初始化的对比​

​特性​ 随机初始化Embedding 预训练Embedding(例GloVe)
​向量质量​ 无意义随机值 已编码语义关系(如"king - man ≈ queen - woman")
​是否需要训练​ 必须经过训练才有意义 可冻结(直接使用)或微调
​适用场景​ 小数据集/特殊领域/从零开始训练模型 通用领域/中等以上数据量/迁移学习/快速原型开发
​典型准确率​ 较低(需足够数据) 较高(迁移学习优势)
​代码示例​ nn.Embedding(1000, 100) GloVe(name='6B', dim=100)

何时使用哪种方法?​

1. ​​优先使用GloVe的情况​

场景 1:小规模数据(核心需求:避免过拟合,快速验证)

当数据量较小时,冻结预训练嵌入(不更新 GloVe 权重)是更合理的选择,利用预训练的通用语义知识辅助任务,同时避免因数据不足导致过拟合。
import torch
import torch.nn as nn
from torchtext.vocab import GloVe

# 1. 加载预训练GloVe嵌入
glove = GloVe(name='6B', dim=100)  # 加载预训练好的GloVe向量
vocab_size = len(glove.stoi)  # 词表大小(预训练词表)
embed_dim = 100

# 2. 定义模型(冻结GloVe嵌入)
class TextClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        # 初始化嵌入层,加载GloVe预训练权重,并冻结参数
        self.embedding = nn.Embedding.from_pretrained(
            torch.FloatTensor(glove.vectors),  # 加载预训练向量
            freeze=True  # 关键:冻结嵌入层,不参与训练更新
        )
        self.fc = nn.Linear(embed_dim, 2)  # 二分类
    
    def forward(self, text):
        embedded = self.embedding(text).mean(dim=1)  # 平均词向量
        return self.fc(embedded)

# 3. 训练(嵌入层参数不会被更新,仅更新全连接层)
model = TextClassifier()
optimizer = torch.optim.Adam(model.parameters())  # 优化器仅更新可训练参数(此处为fc层)

场景 2:大规模数据(核心需求:微调嵌入,适配任务)

当数据量足够大时,可以解冻 GloVe 嵌入(允许更新权重),让预训练向量在任务数据上微调,更贴合具体场景的语义(如领域特定术语)。
import torch
import torch.nn as nn
from torchtext.vocab import GloVe

# 1. 加载预训练GloVe嵌入
glove = GloVe(name='6B', dim=100)
vocab_size = len(glove.stoi)
embed_dim = 100

# 2. 定义模型(解冻GloVe嵌入,允许微调)
class TextClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.embedding = nn.Embedding.from_pretrained(
            torch.FloatTensor(glove.vectors),
            freeze=False  # 关键:解冻嵌入层,参与训练更新
        )
        self.fc = nn.Linear(embed_dim, 2)
    
    def forward(self, text):
        embedded = self.embedding(text).mean(dim=1)
        return self.fc(embedded)

# 3. 训练(嵌入层参数会被更新,与fc层一起微调)
model = TextClassifier()
optimizer = torch.optim.Adam(model.parameters())  # 优化器同时更新嵌入层和fc层

关键区别总结:

场景 数据量 freeze参数 核心逻辑
快速验证 小规模 True 冻结嵌入层,复用预训练知识,避免过拟合(适合数据少、快速出结果的场景)
任务适配 大规模 False 解冻嵌入层,让预训练向量在任务数据上微调,更贴合具体场景(适合追求最优性能)

2. ​​必须用nn.Embedding的情况​

  1. ​特殊领域​​(如医疗):

# 场景:专业领域词汇(如医疗术语)
embedding = nn.Embedding(num_embeddings=5000, embedding_dim=200)  # 自定义词汇量
# 需要后续训练才能获得有意义向量
点击查看完整医疗示例
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset

# ---------------------- 1. 第一步:构建医疗领域专属词表 ----------------------
# 假设从医疗病历数据中统计出5000个核心术语(如“肺炎”“CT影像”“抗生素”等)
medical_vocab = {"肺腺癌": 0, "PD-1抑制剂": 1, "经皮冠状动脉介入术": 2, ..., "(共5000个词)": 4999}
vocab_size = len(medical_vocab)  # num_embeddings=5000
embed_dim = 200  # 根据任务复杂度调整


# ---------------------- 2. 第二步:定义模型(用nn.Embedding) ----------------------
class MedicalTextClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        # 初始化医疗领域专属嵌入层(随机初始化,后续训练优化)
        self.embedding = nn.Embedding(
            num_embeddings=vocab_size,  # 领域词表大小
            embedding_dim=embed_dim     # 嵌入向量维度
        )
        self.fc = nn.Linear(embed_dim, 3)  # 示例:医疗文本三分类(如“良性/恶性/正常”)
    
    def forward(self, text):
        # text: 输入的医疗文本(已转为词表索引,形状[batch_size, seq_len])
        embedded = self.embedding(text).mean(dim=1)  # 平均词向量(聚合句子语义)
        return self.fc(embedded)


# ---------------------- 3. 第三步:用医疗数据训练(关键!让嵌入向量“有意义”) ----------------------
# 假设已准备好医疗文本数据集(每条数据包含“文本索引序列+标签”)
class MedicalDataset(Dataset):
    def __init__(self, data):
        self.data = data  # data格式:[(text_indices1, label1), (text_indices2, label2), ...]
    
    def __getitem__(self, idx):
        return torch.tensor(self.data[idx][0]), torch.tensor(self.data[idx][1])
    
    def __len__(self):
        return len(self.data)

# 加载医疗数据(示例:假设有1000条病历数据)
medical_data = [([0,1,5,...], 1), ([3,7,2,...], 2), ...]  # 文本已转成词表索引
dataloader = DataLoader(MedicalDataset(medical_data), batch_size=32, shuffle=True)

# 初始化模型、损失函数、优化器
model = MedicalTextClassifier()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())  # 优化器会更新nn.Embedding的参数

# 训练过程(核心:通过反向传播优化嵌入向量)
for epoch in range(20):
    model.train()
    for text, label in dataloader:
        optimizer.zero_grad()
        output = model(text)
        loss = criterion(output, label)
        loss.backward()  # 计算nn.Embedding的梯度
        optimizer.step()  # 更新nn.Embedding的参数(让向量学习医疗语义)

简单来说:当通用预训练嵌入 “不好用 / 用不了” 时,nn.Embedding就是特殊领域的最优解 —— 它能从零开始,为领域词汇 “量身定制” 有意义的语义向量

五、TensorBoard可视化查看词向量

import torch.nn as nn
from torch.utils.tensorboard import SummaryWriter
import fileinput

# 1. 使用nn.Embedding创建词向量
embedding_layer = nn.Embedding(num_embeddings=100, embedding_dim=50)
embedded_vectors = embedding_layer.weight  # 关键:提取权重矩阵

# 2. 准备词汇表
meta = list(map(lambda x: x.strip(), fileinput.FileInput(r"D:\learn\000人工智能数据大全\nlp\NLP基础课所有数据和代码\vocab100.csv")))

# 3. 写入TensorBoard
writer = SummaryWriter()
writer.add_embedding(embedded_vectors, metadata=meta)
writer.close()

TensorBoard查看步骤​

运行代码后生成日志文件

在终端运行:

tensorboard --logdir=runs
  1. 浏览器访问 http://localhost:6006
  2. 点击顶部导航栏的 ​​Projector​​ 标签页

可视化效果说明​

​功能​ ​作用​
​PCA降维​ 3D展示词向量的主要分布方向
​t-SNE​ 保留局部相似性的非线性降维
​邻居搜索​ 查找语义相近的词(如"苹果"靠近"水果")

可视化预训练词向量Glove

from torch.utils.tensorboard import SummaryWriter
# 加载预训练词向量(示例使用GloVe)
from torchtext.vocab import GloVe

glove = GloVe(name='6B', dim=50)
# # 获取前100个词及其向量
meta = list(glove.stoi.keys())[:100]  # 词汇列表
print(meta)#['the', ',', '.', 'of', 'to', 'and', 'in', 'a', '"', "'s", 'for', '-', 'that', 'on', 'is', 'was', 'said', 'with', 'he', 'as', 'it', 'by', 'at', '(', ')', 'from', 'his', "''", '``', 'an', 'be', 'has', 'are', 'have', 'but', 'were', 'not', 'this', 'who', 'they', 'had', 'i', 'which', 'will', 'their', ':', 'or', 'its', 'one', 'after', 'new', 'been', 'also', 'we', 'would', 'two', 'more', "'", 'first', 'about', 'up', 'when', 'year', 'there', 'all', '--', 'out', 'she', 'other', 'people', "n't", 'her', 'percent', 'than', 'over', 'into', 'last', 'some', 'government', 'time', '$', 'you', 'years', 'if', 'no', 'world', 'can', 'three', 'do', ';', 'president', 'only', 'state', 'million', 'could', 'us', 'most', '_', 'against', 'u.s.']
indices = [glove.stoi[word] for word in meta]  # 词索引
print(indices)#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
embedded_vectors = glove.vectors[indices]  # 词向量矩阵
print(embedded_vectors)
print(embedded_vectors.shape)
'''
tensor([[ 0.4180,  0.2497, -0.4124,  ..., -0.1841, -0.1151, -0.7858],
        [ 0.0134,  0.2368, -0.1690,  ..., -0.5666,  0.0447,  0.3039],
        [ 0.1516,  0.3018, -0.1676,  ..., -0.3565,  0.0164,  0.1022],
        ...,
        [ 0.1499,  0.7318,  0.4202,  ..., -0.1501, -0.4110,  0.3928],
        [-0.6126, -0.8110, -0.1843,  ...,  0.0140,  0.1737, -0.6768],
        [-0.2805, -0.0832,  1.0143,  ...,  0.8242, -0.7768,  0.6647]])
torch.Size([100, 50])
'''
# 写入TensorBoard
writer = SummaryWriter()
writer.add_embedding(embedded_vectors, metadata=meta)
writer.close()

可视化前检查词向量质量​

print("语义相似度测试:",torch.cosine_similarity(glove['king'], glove['queen'], dim=0))
# 随机向量的相似度通常接近0
print("语义相似度测试:",torch.cosine_similarity(glove['king'], glove['bug'], dim=0))
语义相似度测试: tensor(0.7839)
语义相似度测试: tensor(0.1878)

 构造有明确分类的数据进行可视化:

import torch
from torch.utils.tensorboard import SummaryWriter
from torchtext.vocab import GloVe
import random

# 1. 加载GloVe词向量
glove = GloVe(name='6B', dim=50)

# 2. 定义分类词汇(确保每个类别至少有33个词)
categories = {
    "动物": ["cat", "dog", "lion", "tiger", "elephant", "bear",
             "wolf", "fox", "horse", "cow", "pig", "sheep",
             "monkey", "panda", "zebra", "giraffe", "deer",
             "rabbit", "squirrel", "kangaroo", "dolphin", "whale",
             "shark", "octopus", "eagle", "hawk", "owl", "parrot",
             "penguin", "frog", "snake", "turtle", "dinosaur"],

    "科技": ["computer", "software", "hardware", "internet", "network",
             "algorithm", "data", "database", "robot", "ai",
             "machine", "learning", "neural", "network", "programming",
             "code", "python", "java", "javascript", "html", "css",
             "server", "cloud", "blockchain", "crypto", "vr", "ar",
             "smartphone", "tablet", "laptop", "processor", "gpu", "cpu"],

    "体育": ["football", "soccer", "basketball", "tennis", "baseball",
             "volleyball", "golf", "hockey", "cricket", "rugby",
             "swimming", "running", "marathon", "cycling", "boxing",
             "wrestling", "skiing", "snowboarding", "surfing", "skating",
             "badminton", "tabletennis", "athletics", "gymnastics", "diving",
             "fencing", "archery", "weightlifting", "judo", "karate", "taekwondo", "ball", "pingpang"]
}

# 3. 检查词汇量
for category, words in categories.items():
    if len(words) < 33:
        raise ValueError(f"类别 '{category}' 只有 {len(words)} 个词,需要至少33个")

# 4. 合并词汇(每类取33个)
meta = []
embedded_vectors = []
for category, words in categories.items():
    selected = random.sample(words, 33)
    for word in selected:
        try:
            meta.append([category, word])  # 改为二维列表
            embedded_vectors.append(glove[word])
        except KeyError:
            meta.append([category, word])
            embedded_vectors.append(torch.randn(50))

embedded_vectors = torch.stack(embedded_vectors[:99])  # 确保总数是99(33x3)

# 5. 写入TensorBoard(关键修改)
writer = SummaryWriter()
writer.add_embedding(
    embedded_vectors,
    metadata=meta,  # 现在是二维列表
    metadata_header=['category', 'word']  # 对应两列
)
writer.close()

print("运行以下命令查看可视化:")
print("tensorboard --logdir=runs")

 

可视化操作指南​

  1. 启动TensorBoard:
    tensorboard --logdir=runs
  2. 在浏览器中打开http://localhost:6006
  3. 在​​Projector​​界面:
    • ​STEP 1​​:选择TSNEPCA降维方法
    • ​STEP 2​​:在右侧面板点击"Color by"选择category_word
    • ​STEP 3​​:使用搜索框过滤类别(如输入科技_显示所有科技词汇)

​预期可视化效果​

​降维方法​ 效果描述
​PCA​ 三类呈直线方向分离(可能重叠)
​t-SNE​ 三类形成明显聚类(推荐使用)
​UMAP​ 更清晰的聚类边界(需手动安装)

 

可视化效果差异​

​可视化特征​ GloVe版 nn.Embedding版
词向量聚类 语义相似的词会自然聚在一起 随机分布无规律
降维结构 能反映语义关系(动物/科技等分组) 无意义散点
实用价值 可直接分析词向量语义 需训练后才能分析

 


​六、关键总结表​

​方法​ ​特点​ ​适用场景​
One-Hot 简单但高维稀疏 小型分类任务
TF-IDF 考虑词的重要性 传统文本分类
​Embedding​ ​低维稠密,保留语义​​ ⭐ 深度学习所有NLP任务
预训练词向量 直接使用现成知识(如GloVe) 数据量小的任务

​七、新手常见问题​

  1. ​Q:Embedding和One-Hot有什么区别?​

    • One-Hot是稀疏的高维向量(维度=词汇表大小)
    • Embedding是稠密的低维向量(可学习语义)
  2. ​Q:为什么我的Embedding输出全是小数?​

    • 这是正常的!模型会通过训练不断调整这些数值
  3. ​Q:维度选多少合适?​

    • 小数据集:50-100维
    • 大数据集:200-300维

理解了这些,你就掌握了现代NLP的基石技术!接下来可以尝试用Embedding搭建文本分类模型啦~

 

常见误区澄清​

  1. ​误区​​:"Embedding层不训练也能用"

    • ❌ 随机初始化的Embedding必须训练才有意义
    • ✅ 预训练的Embedding可以不训练(但微调通常效果更好)
  2. ​误区​​:"所有词向量都需要训练"

    • 可通过requires_grad=False冻结部分词向量
embedding.weight.requires_grad = False  # 冻结参数
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
posted @ 2025-07-14 12:03  指尖下的世界  阅读(11)  评论(0)    收藏  举报