零基础吃透 RaggedTensor 文本特征提取示例(通俗版)

零基础吃透 RaggedTensor 文本特征提取示例(通俗版)

这份内容会从零开始拆解原示例,先讲核心思想,再逐步解释「要做什么、用什么API、API背后的原理」,全程用大白话,避免专业术语堆砌,确保你能理解每一步的目的和底层逻辑。

一、先搞懂:这个示例的核心思想(为什么要做这件事?)

1. 通俗目标

长度不一样的句子(比如"Who is Dan Smith"4个词、"Pause"1个词、"Will it rain later today"5个词)做「特征提取」:

  • 既要保留单个词的语义(比如"rain"代表下雨);
  • 也要保留相邻词的搭配语义(比如"Will it"是疑问搭配);
  • 最后把每个句子浓缩成一个固定长度的向量(4维),方便后续做文本分类、搜索、问答等任务。

2. 为什么用 RaggedTensor?

如果用普通张量处理长度不同的句子,必须把所有句子补0到最长长度(比如补到5个词),会产生冗余数据;而 RaggedTensor 能原生支持可变长度的列表,不用补0,直接处理,既省内存又保证计算准确。

3. 前置基础概念(先记牢,后续不懵)

概念 通俗解释
词嵌入(Embedding) 把"单词"(字符串)变成"数字向量"(比如4维),让电脑能理解单词的语义(比如"rain"→[0.1, -0.2, 0.3, 0.4])
哈希桶(Hash Bucket) 字符串不能直接查嵌入表,先通过"哈希算法"把字符串转成0~1023的整数(相当于给每个单词发一个编号)
一元词嵌入 单个单词的嵌入向量(比如"Who"的4维向量)
二元词嵌入 相邻两个单词拼接后的嵌入向量(比如"Who+is"的4维向量,捕捉词之间的搭配)
RaggedTensor 专门存"长度不一样的嵌套列表"的张量(比如[[4词],[1词],[5词]]),TF原生支持,不用补0

二、完整示例(带逐行注释+原理)

步骤0:环境准备(先导入要用的工具)

python 复制代码
# 导入TensorFlow(核心工具)
import tensorflow as tf
# 导入数学库(用于嵌入表初始化,计算标准差)
import math  

# 打印TF版本,确保能正常运行(可选)
print(f"TensorFlow版本:{tf.__version__}")

步骤1:定义输入(可变长度的句子)

要做什么?

准备3个长度不同的句子,用 RaggedTensor 存储(核心:保留原始长度,不补0)。

用什么API?

tf.ragged.constant():把嵌套列表转成 RaggedTensor。

API原理?

自动识别嵌套列表的"行长度",生成"不规则张量",只记录有效元素,不存冗余的填充值。

代码+注释
python 复制代码
# 定义3个长度不同的句子(4词、1词、5词)
queries = tf.ragged.constant([
    ['Who', 'is', 'Dan', 'Smith'],   # 句子1:4个词
    ['Pause'],                       # 句子2:1个词
    ['Will', 'it', 'rain', 'later', 'today']  # 句子3:5个词
])

# 查看RaggedTensor的基本信息(验证是否正确)
print("输入的RaggedTensor:")
print(queries)
print("句子数量(行数):", queries.nrows())  # 输出3,对应3个句子
print("每个句子的长度:", queries.row_lengths())  # 输出[4,1,5],对应每个句子的词数
运行结果
复制代码
输入的RaggedTensor:
<tf.RaggedTensor [[b'Who', b'is', b'Dan', b'Smith'], [b'Pause'], [b'Will', b'it', b'rain', b'later', b'today']]>
句子数量(行数): 3
每个句子的长度: tf.Tensor([4 1 5], shape=(3,), dtype=int64)
  • 注意:字符串前的b是TF默认把字符串转成字节型,不影响使用。

步骤2:创建嵌入表(单词→向量的映射表)

要做什么?

创建一个"字典":把每个"单词编号"映射到一个4维向量(让电脑能理解单词的语义)。

用什么API?
  • tf.Variable():创建可训练的变量(嵌入表后续可通过训练优化);
  • tf.random.truncated_normal():生成"截断正态分布"的随机数(初始化嵌入表的值)。
API原理?
  • 嵌入表形状是[哈希桶数, 嵌入维度](这里[1024,4]):1024个"单词编号",每个编号对应1个4维向量;
  • 截断正态分布:避免生成过大的随机数,让初始值更稳定(stddev=1/√嵌入维度是行业通用的初始化技巧)。
代码+注释
python 复制代码
# 1. 定义超参数(可调整)
num_buckets = 1024    # 哈希桶数量:把单词映射到0~1023的整数(足够容纳常用词)
embedding_size = 4    # 嵌入向量维度:每个单词转成4维向量(维度越小越省内存,越大能表达的语义越丰富)

# 2. 创建嵌入表(可训练的变量)
embedding_table = tf.Variable(
    # 生成随机数:形状[1024,4],截断正态分布,标准差=1/√4=0.5
    tf.random.truncated_normal(
        shape=[num_buckets, embedding_size],
        stddev=1.0 / math.sqrt(embedding_size)
    )
)

# 查看嵌入表形状(验证是否正确)
print("\n嵌入表形状:", embedding_table.shape)  # 输出(1024, 4)

步骤3:计算一元词嵌入(单个单词的向量)

要做什么?

把句子里的每个单词→转成编号→查嵌入表→得到单个单词的4维向量。

用什么API?
  • tf.strings.to_hash_bucket_fast():字符串→单词编号(哈希算法,速度快);
  • tf.nn.embedding_lookup():根据单词编号查嵌入表,得到向量。
API原理?
  • 哈希映射:不管输入什么字符串,都能快速转成0~1023的整数(解决"字符串无法直接计算"的问题);
  • 嵌入查找:根据编号从嵌入表中"查字典",比如编号123→嵌入表第123行的4维向量。
代码+注释
python 复制代码
# 1. 单词→编号(哈希映射)
word_buckets = tf.strings.to_hash_bucket_fast(queries, num_buckets)
print("\n单词对应的编号(RaggedTensor):")
print(word_buckets)  # 形状和queries一致:[3, (4/1/5)]

# 2. 编号→向量(查嵌入表)
word_embeddings = tf.nn.embedding_lookup(embedding_table, word_buckets)
print("\n一元词嵌入的形状:", word_embeddings.shape)  # 输出(3, None, 4):3个句子,每个句子N个词,每个词4维
# 解释:None代表可变长度(4/1/5),TF用None表示RaggedTensor的可变维度
关键说明
  • word_buckets 形状和queries完全一致:句子1有4个编号,句子2有1个,句子3有5个;
  • word_embeddings 形状是[3, None, 4]None对应可变的词数,每个词都是4维向量。

步骤4:给句子加首尾标记(为构造二元词做准备)

要做什么?

给每个句子的开头和结尾加#(比如"Pause"→"# Pause #"),确保能捕捉到"句首+第一个词""最后一个词+句尾"的搭配。

用什么API?
  • tf.fill():生成指定形状、填充固定值的张量;
  • tf.concat():拼接张量(这里横向拼接:标记+句子+标记)。
API原理?
  • tf.fill([queries.nrows(), 1], '#'):生成[3,1]的张量(3个句子,每个句子1个#);
  • tf.concat(axis=1):横向拼接(按"词"的维度拼接),RaggedTensor原生支持可变长度的拼接(普通张量做不到)。
代码+注释
python 复制代码
# 1. 生成首尾标记:每个句子1个#,形状[3,1]
marker = tf.fill([queries.nrows(), 1], '#')
print("\n标记张量形状:", marker.shape)  # 输出(3, 1)

# 2. 拼接:开头标记 + 原句子 + 结尾标记
padded = tf.concat([marker, queries, marker], axis=1)
print("\n加首尾标记后的句子:")
print(padded)
print("每个句子的长度:", padded.row_lengths())  # 输出[6,3,7](原长度+2)
运行结果
复制代码
加首尾标记后的句子:
<tf.RaggedTensor [[b'#', b'Who', b'is', b'Dan', b'Smith', b'#'], [b'#', b'Pause', b'#'], [b'#', b'Will', b'it', b'rain', b'later', b'today', b'#']]>
每个句子的长度: tf.Tensor([6 3 7], shape=(3,), dtype=int64)
  • 句子1长度从4→6(加了2个#),句子2从1→3,句子3从5→7,符合预期。

步骤5:构造二元词(相邻词对)

要做什么?

把加标记后的句子里的"相邻两个词"拼接成一个词对(比如"#+Who""Who+is"),捕捉词之间的搭配语义。

用什么API?
  • 切片[:, :-1][:, 1:]:分别取"去掉最后一个词"和"去掉第一个词"的句子;
  • tf.strings.join():把相邻的两个词拼接成一个字符串(分隔符+)。
API原理?
  • 切片逻辑:比如句子1[#, Who, is, Dan, Smith, #][:, :-1][#, Who, is, Dan, Smith][:, 1:][Who, is, Dan, Smith, #]
  • 拼接逻辑:把两个切片的对应位置拼接 → #+WhoWho+isis+DanDan+SmithSmith+#(共5个二元词)。
代码+注释
python 复制代码
# 1. 取相邻词的切片
padded_left = padded[:, :-1]  # 去掉每行最后一个元素
padded_right = padded[:, 1:]  # 去掉每行第一个元素

# 2. 拼接成二元词(分隔符+)
bigrams = tf.strings.join([padded_left, padded_right], separator='+')

# 查看二元词(验证是否正确)
print("\n二元词:")
print(bigrams)
print("每个句子的二元词数量:", bigrams.row_lengths())  # 输出[5,2,6](加标记后的长度-1)
运行结果
复制代码
二元词:
<tf.RaggedTensor [[b'#+Who', b'Who+is', b'is+Dan', b'Dan+Smith', b'Smith+#'], [b'#+Pause', b'Pause+#'], [b'#+Will', b'Will+it', b'it+rain', b'rain+later', b'later+today', b'today+#']]>
每个句子的二元词数量: tf.Tensor([5 2 6], shape=(3,), dtype=int64)

步骤6:计算二元词嵌入(词对的向量)

要做什么?

和"一元词嵌入"逻辑完全一致:二元词→转编号→查嵌入表→得到词对的4维向量。

用什么API?

和步骤3相同:tf.strings.to_hash_bucket_fast() + tf.nn.embedding_lookup()

API原理?

二元词本质还是字符串,同样通过哈希转编号,再查嵌入表(嵌入表不区分"一元词"和"二元词",只认编号)。

代码+注释
python 复制代码
# 1. 二元词→编号
bigram_buckets = tf.strings.to_hash_bucket_fast(bigrams, num_buckets)

# 2. 编号→向量
bigram_embeddings = tf.nn.embedding_lookup(embedding_table, bigram_buckets)

# 查看二元词嵌入形状
print("\n二元词嵌入形状:", bigram_embeddings.shape)  # 输出(3, None, 4):3个句子,每个句子M个二元词,每个词对4维

步骤7:合并一元+二元嵌入(融合两种语义)

要做什么?

把每个句子的"一元词向量"和"二元词向量"拼在一起(比如句子1:4个一元词 + 5个二元词 = 9个向量)。

用什么API?

tf.concat(axis=1):横向拼接(按"词/词对"的维度拼接)。

API原理?

RaggedTensor的concat(axis=1)会自动匹配"行",只拼接同一行的元素,不会打乱句子的对应关系(普通张量需要补0才能拼接)。

代码+注释
python 复制代码
# 合并一元+二元嵌入(axis=1:横向拼接)
all_embeddings = tf.concat([word_embeddings, bigram_embeddings], axis=1)

# 查看合并后的形状
print("\n合并后的嵌入形状:", all_embeddings.shape)  # 输出(3, None, 4)
print("每个句子的总向量数:", all_embeddings.row_lengths())  # 输出[9,3,11](4+5, 1+2,5+6)

步骤8:计算每个句子的平均嵌入(浓缩成固定长度向量)

要做什么?

把每个句子的所有向量(一元+二元)求平均值→每个句子得到1个4维向量(固定长度,方便后续使用)。

用什么API?

tf.reduce_mean(axis=1):对每个句子的向量求均值(axis=1:按"词/词对"的维度求平均)。

API原理?
  • 对可变长度的行求均值:只计算该行的有效元素(比如句子2的3个向量求平均),不会被填充值干扰;
  • 结果是普通张量(不再是RaggedTensor):形状[3,4],3个句子各1个4维向量(固定长度,可直接用于分类、搜索等任务)。
代码+注释
python 复制代码
# 对每个句子的所有向量求均值
avg_embedding = tf.reduce_mean(all_embeddings, axis=1)

# 输出最终结果
print("\n每个句子的平均嵌入向量(最终结果):")
print(avg_embedding)
print("最终结果形状:", avg_embedding.shape)  # 输出(3, 4)(普通张量,固定长度)
运行结果(和原示例一致,数值因随机初始化略有不同)
复制代码
每个句子的平均嵌入向量(最终结果):
tf.Tensor(
[[ 0.02510674 -0.04737939  0.01799836  0.1717977 ]
 [ 0.4977201   0.38424173 -0.40286708 -0.39982286]
 [ 0.16051094  0.2062596  -0.05239218  0.10338864]], shape=(3, 4), dtype=float32)
最终结果形状: (3, 4)

三、核心总结(API+原理速查表)

步骤 核心API API核心原理
定义输入 tf.ragged.constant() 把嵌套列表转成RaggedTensor,保留可变长度,不补0
创建嵌入表 tf.Variable() + tf.random.truncated_normal() 创建可训练的嵌入表,用截断正态分布初始化,避免值过大
单词→编号 tf.strings.to_hash_bucket_fast() 哈希算法把字符串转成0~num_buckets-1的整数,解决字符串无法计算的问题
编号→向量 tf.nn.embedding_lookup() 根据编号查嵌入表,得到单词/词对的向量
拼接张量 tf.concat(axis=1) 横向拼接RaggedTensor,自动匹配行,无需补0
构造二元词 tf.strings.join() + 切片 切片取相邻词,拼接成词对,捕捉上下文语义
求均值 tf.reduce_mean(axis=1) 对每个句子的向量求均值,浓缩成固定长度向量,结果为普通张量

四、关键收获

  1. RaggedTensor的核心价值:处理可变长度数据,无需补0,保留原始结构
  2. 词嵌入的核心逻辑:字符串→编号→向量(让电脑能理解文本语义);
  3. 二元词的作用:捕捉词之间的搭配关系(比单个词更能表达上下文);
  4. 最终目标:把长度不同的句子浓缩成固定长度的向量(方便后续机器学习任务)。

如果还有某个API或原理没懂,比如"哈希桶为什么是1024""嵌入维度为什么选4",可以告诉我,我再针对性解释。

相关推荐
serve the people2 天前
tensorflow不规则张量(RaggedTensor)的存储约束
人工智能·tensorflow·neo4j
serve the people2 天前
TensorFlow 中不规则张量(RaggedTensor)
人工智能·tensorflow·neo4j
serve the people2 天前
TensorFlow 不规则张量(RaggedTensor)的两种核心构造方式
人工智能·tensorflow·neo4j
serve the people2 天前
TensorFlow 高级自动微分
人工智能·tensorflow·neo4j
晚霞的不甘2 天前
小智AI音箱:智能语音交互的未来之选
人工智能·交互·neo4j
vvvdg3 天前
【Neo4j Desktop/Community】neo4j桌面版/社区版下载、安装、使用教程
neo4j
ELI_He9993 天前
Neo4j GDS插件安装
neo4j
乙卯年QAQ4 天前
Docker:Docker部署Neo4j图数据库
neo4j
熊猫钓鱼>_>5 天前
TensorFlow深度学习框架入门浅析
深度学习·神经网络·tensorflow·neo4j·张量·训练模型·评估模型