PyTorch C++ 前端张量创建完全指南:Factory 函数、TensorOptions 与数据导入
本篇指南以 PyTorch C++ 前端(torch:: API,也即 LibTorch)为核心,系统讲解如何通过 factory 工厂函数创建各种初始化形态的张量:从 zeros、ones、randn、arange 到零维张量与 Scalar;同时深入剖析统一创建范式、尺寸(shape)表达方式、TensorOptions 的四个配置轴(dtype/layout/device/requires_grad)以及用 from_blob 包装外部内存等关键能力。读完本篇,你将能够在 C++ 环境中完全等效地完成 Python 侧常见的张量创建与转换操作,并能结合 PyTorch 仓库源码理解其底层实现。
本文主体依据仓库文档 docs/cpp/source/api/aten/creation.md 整理扩充。该文档位于 C++ 文档体系的 ATen API 分类下(见 docs/cpp/source/api/aten/index.md)。为便于绝大多数用户使用,示例统一使用更友好的 torch:: 命名空间;ATen/ATen.h 提供了功能对等的 at:: 底层命名空间,本文的绝大部分接口均可直接照搬到 at:: 下使用。
统一创建范式:所有 factory 函数都遵循同一套 schema
Factory(工厂)函数指那些"从零创建一个全新张量"的函数,与从已有张量派生的操作(如 view、permute)不同,它们不依赖输入数据,只依赖你传入的配置参数。
所有 factory 函数都遵循一个统一调用范式:
torch::<function-name>(<function-specific-options>, <sizes>, <tensor-options>)
即调用由三部分构成:
- 函数特定参数(可选):某些函数独有,如
randint的取值范围、full的填充值; - sizes(尺寸):沿每个维度的长度;
- tensor-options(张量选项):统一控制 dtype、layout、device 与
requires_grad,同样可选,省略时使用默认值。
也就是说,只有函数名 + sizes 是必有的;大部分调用只需要一行代码即可完成。这一统一约定在仓库的自动生成代码中也有体现:PyTorch 的 torch:: 张量创建入口由 tools/autograd/templates/variable_factories.h 模板在构建期自动生成,其中每个 factory 的重载都围绕 at::IntArrayRef sizes 与 const at::TensorOptions& options 展开。
可用 Factory 函数总览
原文档列出的核心 factory 函数如下表所示,覆盖了日常创建张量的绝大多数场景:
| 函数 | 初始化行为 |
|---|---|
torch::zeros |
全 0 填充的张量 |
torch::ones |
全 1 填充的张量 |
torch::empty |
未初始化的张量(内存内容不确定,分配速度最快) |
torch::full |
用单一标量值填充的张量 |
torch::rand |
[0, 1) 区间上的均匀分布随机张量 |
torch::randn |
标准正态分布(均值 0、方差 1)随机张量 |
torch::randint |
指定范围内的随机整型张量 |
torch::arange |
等差数列序列(整数或浮点) |
torch::linspace |
线性等间隔的数值序列 |
torch::logspace |
对数等间隔的数值序列 |
torch::eye |
单位矩阵(对角线为 1、其余为 0) |
torch::randperm |
0 到 n-1 的一个随机排列 |
其中随机类的 rand、randn、randint、randperm 依赖 PyTorch 的全局随机数生成器(RNG),其 CPU 端内核实现位于 aten/src/ATen/native/TensorFactories.cpp,属于 ATen native 工厂实现的一个集中入口文件,与本篇所有 factory 函数一一对应。
指定尺寸:int64_t 与 IntArrayRef
单一向量尺寸:不需要额外参数的函数可以直接用一个整数指定长度。例如下面的语句创建一个含 5 个分量的向量:
torch::Tensor tensor = torch::ones(5);
多维尺寸:当需要表达多维形状时,通过在花括号中逐个列出每个维度的长度来构造一个 IntArrayRef。例如 {2, 3} 表示 2 行 3 列的矩阵,{3, 4, 5} 表示一个三维张量:
torch::Tensor tensor = torch::randn({3, 4, 5});
assert(tensor.sizes() == std::vector<int64_t>{3, 4, 5});
除了花括号初始化列表,也可以直接传入 std::vector<int64_t>:
std::vector<int64_t> shape = {3, 4, 5};
torch::Tensor tensor = torch::randn(shape);
- 用
tensor.sizes()可一次性取出完整的形状(返回IntArrayRef); - 用
tensor.size(i)访问单个维度 i 的长度; - 所有尺寸底层都是
int64_t,这也是 PyTorch 全栈(Python 端torch.Size同样基于 64 位整数)的统一约定。
函数特定参数:注意"sizes 永远跟在函数特定参数之后"
部分 factory 函数带独有的业务参数。例如 randint 需要指定随机整数的取值范围。只给上界时写法如下:
// 在 [0, 10) 内均匀随机生成整数,形状为 5x5
torch::Tensor tensor = torch::randint(/*high=*/10, {5, 5});
同时给出下界与上界:
// 在 [3, 10) 内均匀随机生成整数,形状为 5x5
torch::Tensor tensor = torch::randint(/*low=*/3, /*high=*/10, {5, 5});
提示: 无论函数特定参数有多少个,sizes 始终排在它们之后、
TensorOptions之前,即统一 schema 中<sizes>与<tensor-options>的相对位置是固定的。
注意: 也有不需要 sizes 的特例。比如
arange、linspace、logspace、eye、randperm这类函数,其输出形状完全由函数特定参数(区间边界、端点数量、矩阵阶数等)推导得出,因此调用时不要再传尺寸参数。例如arange只需要起止边界即可确定序列长度:
// 从 0 到 9 的等差序列,等价于 Python 的 torch.arange(10)
torch::Tensor seq = torch::arange(10);
TensorOptions:四个配置轴与合法取值
TensorOptions 是 PyTorch C++ 中"张量属性配置"的统一载体,用来指定新建张量的 dtype(元素数据类型)、layout(内存布局)、device(计算设备)与 requires_grad(是否需要梯度跟踪)。其类型定义位于 aten/src/ATen/TensorOptions.h,在 C++ 前端头文件 torch/csrc/api/include/torch/types.h 中被引入并直接可用。
四个配置轴的说明与允许取值如下:
dtype:元素数据类型。文档明确给出的取值包括kUInt8、kInt8、kInt16、kInt32、kInt64、kFloat32、kFloat64;layout:张量内存布局,取kStrided(稠密、带步长)或kSparse(稀疏);device:计算设备,取kCPU或kCUDA(CUDA 可附带设备索引);requires_grad:是否记录梯度,取true或false。
dtype 的 Rust 风格简写
除上述"完整名称"外,还存在一套 Rust 风格简写。仓库源码 torch/csrc/api/include/torch/types.h 中可看到两组别名的完整定义:既有 kFloat32 = at::kFloat 这种 ATen 名称到 torch:: 名称的映射,也定义了 kF32 = kFloat32、kF16、kF64、kI8、kI16、kI32、kI64、kU8、kU16、kU32、kU64 等简写别名。因此写 kF32 与写 kFloat32 完全等价。完整取值清单可在该头文件中直接查阅。
提示: 若需要完整的 dtype 别名列表(包括
kFloat16、kUInt16等扩展类型),请直接查看仓库中的 torch/csrc/api/include/torch/types.h,该文件即原文档所指的torch/types.h。
组合式构建与链式调用
下面是文档中的完整示例:构造一个 TensorOptions 对象,将四个轴全部显式指定——kFloat64 双精度、kStrided 布局、编号为 1 的 CUDA 设备、需要梯度:
auto options =
torch::TensorOptions()
.dtype(torch::kFloat64)
.layout(torch::kStrided)
.device(torch::kCUDA, 1)
.requires_grad(true);
torch::Tensor tensor = torch::full({3, 4}, /*value=*/123, options);
// 逐项校验配置确实生效
assert(tensor.dtype() == torch::kFloat64);
assert(tensor.layout() == torch::kStrided);
assert(tensor.device().type() == torch::kCUDA);
assert(tensor.device().index() == 1);
assert(tensor.requires_grad());
注意上面 device(torch::kCUDA, 1) 中第二个参数 1 即 CUDA 设备索引;full 的函数特定参数是填充值 123。
默认值与"全默认"写法
任何一个省略的轴都会自动取默认值:dtype 默认 kFloat32,layout 默认 kStrided,device 默认 kCPU,requires_grad 默认 false。因此最简单的情形下可以完全省略 TensorOptions:
// 等价于:32 位浮点、stride 布局、CPU、不追踪梯度
torch::Tensor tensor = torch::randn({3, 4});
简写自由函数:torch::dtype() / torch::layout() / torch::device() / torch::requires_grad()
与 TensorOptions 的四个轴对应,torch:: 命名空间下还提供了四个自由函数:torch::dtype()、torch::device()、torch::layout()、torch::requires_grad()。每个都返回一个可继续用 builder 方法链式细化的 TensorOptions 对象。下面三组写法彼此等价:
// 写法 1:显式构造 TensorOptions
torch::ones(10, torch::TensorOptions().dtype(torch::kFloat32))
// 写法 2:使用自由函数简写
torch::ones(10, torch::dtype(torch::kFloat32))
// 写法 3:自由函数 + 链式追加其他轴
torch::ones(10, torch::dtype(torch::kFloat32).layout(torch::kStrided))
隐式构造:只改一个轴时的极简写法
TensorOptions 可以从单个数支持隐式构造。因此当只有某一个轴与默认值不同时,可以直接把该值作为第三参传入,省略其余所有轴。例如只把 dtype 改成 kFloat32 时:
torch::ones(10, torch::kFloat32)
与 Python 的完整对照
把上述机制组合起来,C++ 的张量创建调用与 Python 侧几乎一一对应。原文档给出如下对照:
# Python
torch.randn(3, 4, dtype=torch.float32, device=torch.device('cuda', 1), requires_grad=True)
// C++
torch::randn({3, 4}, torch::dtype(torch::kFloat32).device(torch::kCUDA, 1).requires_grad(true))
两者语义完全一致:形状 3x4、float32、CUDA 设备 1 号、追踪梯度。差异仅在于 C++ 的命名空间常量(torch::kFloat32/torch::kCUDA)与 Python 的对象写法(torch.float32/torch.device('cuda', 1))。
导入外部数据:from_blob 包装既有内存
如果你已经拥有了一块自行分配的内存(位于 CPU 或 CUDA 上,例如来自 C 数组、图像解码缓冲或其它框架的输出),可以用 from_blob 直接把它"看作"一个 Tensor,而无需任何拷贝:
float data[] = {1, 2, 3, 4, 5, 6};
torch::Tensor tensor = torch::from_blob(data, {2, 3}); // 2x3 的视图
from_blob 的 C++ 前端声明由模板 tools/autograd/templates/variable_factories.h 生成,从源码可见它提供多组重载,便于按需选择:
- 基础形态:
from_blob(void* data, at::IntArrayRef sizes, const at::TensorOptions& options = {}); - 带 strides 的形态:
from_blob(void* data, at::IntArrayRef sizes, at::IntArrayRef strides, ...),用于内存中本身带步长(非连续)的数据; - 带自定义 deleter 的形态:传入
std::function<void(void*)>,当张量释放时会回调该 deleter 去释放原始内存。
从实现细节看,from_blob 会先通过 at::from_blob(...) 构建底层张量,并在构造 Variable 时把 TensorOptions 中的 requires_grad 显式取出单独处理(见 tools/autograd/templates/variable_factories.h),因此 from_blob 同样可以指定 dtype 来解释原始字节。
注意: 由
from_blob创建的张量不能 resize。原因在于 ATen 并不拥有这块内存——它只是一个轻量视图。如果你需要改变形状,应通过view/reshape这类不搬移数据的操作,或先拷贝出属于自己的数据。
补充: 若
data来自需要手动管理的分配器,务必传入对应的 deleter 重载,否则张量释放时不会回收外部内存,可能造成泄漏;反之如果你不需要接管释放,就不要传 deleter,由外部继续管理生命周期。
另外,若想从 C++ 的字面量数据容器(如嵌套花括号 {{1,2},{3,4}}、std::vector、at::ArrayRef)构造张量,可改用 torch::tensor(...)。仓库源码 torch/csrc/api/include/torch/detail/TensorDataContainer.h 中的注释明确了其类型推断规则:整型数据默认生成 at::kLong(即 int64_t),浮点数据默认生成 torch::get_default_dtype(),这与 Python 端 torch.tensor 的默认行为保持一致。
张量转换:to() 在 dtype 与 device 之间迁移
创建之后经常需要把张量从一个 dtype 或设备迁移到另一个。此时使用 to()。转换会创建一块全新内存上的新张量,绝不会原地修改原张量:
torch::Tensor source = torch::randn({2, 3}, torch::kInt64);
// 只转换 dtype:int64 转为 float32
torch::Tensor float_tensor = source.to(torch::kFloat32);
// 迁移到 GPU(默认 CUDA 0 号设备)
torch::Tensor gpu_tensor = float_tensor.to(torch::kCUDA);
// 迁移到指定 CUDA 设备
torch::Tensor gpu1_tensor = float_tensor.to(torch::Device(torch::kCUDA, 1));
// 异步拷贝:从 GPU 拷回 CPU,non_blocking=true 允许不阻塞调用线程
torch::Tensor async_tensor = gpu_tensor.to(torch::kCPU, /*non_blocking=*/true);
要点总结:
to(torch::kFloat32)这类写法依赖TensorOptions的隐式构造机制,把单值直接解释为 dtype 目标;- 设备目标可用
torch::Device(torch::kCUDA, 1)精确到具体卡; non_blocking参数主要对 GPU 与固定内存(pinned memory)主机之间的拷贝有意义,用于隐藏拷贝延迟。
注意: 转换结果是指向新内存的全新张量,与原张量之间没有任何共享存储关系,对其中一个的写入不会影响另一个(详见 ATen 张量转换相关实现)。
Scalar 与零维张量:单值如何参与运算
Scalar:动态类型的单一数值
Scalar 用于表示单个"动态类型"的数值。和 Tensor 类似,它也是动态类型的,可以容纳 ATen 支持的任何数值类型,并且可以从 C++ 内建数值类型隐式构造。这意味着在编写算子时,可以把它设计成"既能接整数又能接浮点"的通用入口。原文档以 addmm 和 sum 的函数签名为例:
namespace torch {
// beta、alpha 都是 Scalar,因此调用方可以直接传 1.0、0.5 这类字面量
Tensor addmm(Scalar beta, const Tensor & self,
Scalar alpha, const Tensor & mat1,
const Tensor & mat2);
// 对张量求和返回一个 Scalar
Scalar sum(const Tensor & self);
} // namespace torch
实际用法中无需显式构造 Scalar,直接传入 C++ 数值即可,字面量会被隐式转换为 Scalar:
torch::Tensor a = ...;
torch::Tensor b = ...;
torch::Tensor c = ...;
// 1.0 与 .5 分别隐式转换成 beta 与 alpha 两个 Scalar
torch::Tensor r = torch::addmm(1.0, a, .5, b, c);
Scalar 的底层定义位于 ATen 的公共头文件 ATen/Scalar.h(该头文件在 docs/cpp/source/api/aten/index.md 的 ATen 公共 API 头文件清单中列出)。当需要对结果做进一步数值处理时,可调用 Scalar::toDouble()、Scalar::toLong() 等方法把它转回具体 C++ 类型。
零维张量:单值张量与下标引用
零维(0-dim)张量同样只含一个值。它有两种常见来源:一类是 factory 或归约运算返回的标量张量,另一类是通过下标访问更大张量中的元素——此时取出的正是引用该元素的零维张量,如下例所示:
torch::Tensor matrix = torch::rand({10, 20});
matrix[1][2] = 4; // matrix[1][2] 是一个零维张量
注意这里 matrix[1][2] 之所以能出现在赋值左侧,是因为它是大张量内部存储的一个视图(零维张量),对它的写入会直接作用到 matrix 的对应元素上。这与 Scalar(值语义、独立存储)形成鲜明对比,是理解 C++ 前端索引与赋值行为的关键。更深入的索引写法(高级索引、index_put 等)可继续阅读同目录文档 indexing.md。
源码佐证与进一步阅读
- 所有
torch::的 factory 与from_blob前端声明集中在自动生成的 tools/autograd/templates/variable_factories.h,它是了解"统一 schema 落地形态"的第一手资料; TensorOptions的四个配置轴定义于 aten/src/ATen/TensorOptions.h;- dtype 常量与 Rust 风格别名的权威清单在 torch/csrc/api/include/torch/types.h;
- CPU 端各 factory 的实际填充/随机内核集中在 aten/src/ATen/native/TensorFactories.cpp;
- 想继续深入张量操作的其余维度,可阅读同目录姊妹文档:tensor.md(核心张量操作)、indexing.md(索引)、accessors.md(数据访问器);底层计算设备相关 API 见 docs/cpp/source/api/cuda/index.md。
综上,PyTorch C++ 前端的张量创建围绕"factory 函数 + sizes + TensorOptions"这一极简 schema 展开,覆盖从全零/全一、随机分布到等差数列、外部内存包装的全部初始化形态,足以在 C++ 侧完整复刻 Python 端的张量构造体验。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00