零基础吃透 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",可以告诉我,我再针对性解释。

相关推荐
咋吃都不胖lyh7 天前
Neo4j 知识讲解与在线工具使用教程
neo4j
麦麦大数据8 天前
F056 知识图谱飞机问答系统
人工智能·flask·vue·问答系统·知识图谱·neo4j·飞机
麦麦大数据9 天前
F054-基于Vue+Flask+Neo4j构建的移民知识图谱可视化分析系统
vue.js·flask·知识图谱·neo4j·移民分析
新鲜势力呀9 天前
TensorFlow 中 tf.placeholder 适用版本解析|附 PHP 调用 TF 模型实战(兼容低版本)
tensorflow·php·neo4j
麦麦大数据9 天前
F055 vue+neo4j船舶知识问答系统|知识图谱|问答系统
vue.js·flask·问答系统·知识图谱·neo4j·可视化
奔跑吧邓邓子10 天前
Neo4j图数据库实战:解锁关系数据的无限潜力
数据库·实战·neo4j
虹科网络安全13 天前
艾体宝产品 | 基准测试:ArangoDB性能碾压Neo4j,速度最高提升8倍!
数据库·neo4j
小高求学之路15 天前
Neo4j - 为什么需要图数据库
数据库·neo4j
小高求学之路15 天前
eo4j 图数据库备忘单
数据库·neo4j
郭庆汝16 天前
Neo4j数据库中批量插入数据(数据在.csv文件中)
数据库·neo4j