@ezyang的新描述:
https://github.com/Roger-luo/pytorch-complex的工作正在进行中
这是工作流程在稳定状态下的样子。
PyTorch 将本机包含用于引用复杂 dtype 的 API,但默认情况下它们不会做任何事情。 PyTorch 定义了 torch.complex64 和 torch.complex128 指的是复数张量。 但是,如果您尝试以这种方式构造张量,默认情况下,PyTorch 会出错:
>>> torch.zeros({2,2}, dtype=torch.complex64)
RuntimeError: complex64 not supported by PyTorch
@ezyang提供了一个补丁,将这些数据类型添加到 PyTorch。 https://github.com/pytorch/pytorch/pull/11173
在中期,我们将合并对 PyTorch 原生支持的基本功能(如分配零张量)的支持。 什么是“基本”支持的合理代理是 PyTorch 对 CPU 半张量的本机支持(非常贫乏)。
PyTorch 发布了一个用于注册复杂张量实现的接口。 该实现继承自 TypeDefault 类(https://github.com/pytorch/pytorch/pull/11013),并将覆盖此类上的方法以定义我们具有复杂实现的函数的实现。 它看起来像这样:
struct CPUComplexFloatType final : public TypeDefault {
virtual Tensor add(const Tensor & self, const Tensor & other, Scalar alpha=1) const override {
// Your implementation of add for complex tensors
}
// ...
}
此类将完全覆盖复杂的类型; 所有其他实现由 TypeDefault 提供,默认情况下会出错。
将有一个 Type (整体接口)支持的方法的规范列表,作为一个自动生成的文件,该文件被检入 PyTorch 源存储库; 我们将通过差异将 API 更改传达给此文件。 通常,这些方法与其在 PyTorch 前端中的相应名称一一对应。
一般来说,当您使用尚未实现的操作时,
警告:我们打算将 Type away 重构到一个新系统中,该系统还支持新操作的开放注册(如果您有一个定义了您可能想要支持的所有方法的单个超类,这显然不起作用)。 因此,尽量不要太拘泥于将 Type 编写为子类的特定实现策略。
要发布新的、仅复杂的操作,您将使用 C++ 扩展 API。 C++ 扩展 API 记录在https://pytorch.org/tutorials/advanced/cpp_extension.html本质上,您可以编写如下 C++ 函数:
at::Tensor imag(at::Tensor z) {
...
}
然后 C++ 扩展 API 将生成一个 Python 绑定,以便您从 Python 调用此函数。
一些操作将“容易”集成到目前存在的 PyTorch 中。 例如,对于二进制操作的实现,扩展 BinaryOpsKernel.cpp 中的 add_kernel 可能更有意义,以便它在复杂类型上分派(然后你可以免费获得它,因为 std::complex 实现了加法)。 只要这些补丁很小且独立,我们承诺会及时合并它们。
它应该总是可以解除阻塞,只需在 Type 上编写覆盖而不是使用现有的基础设施,并进行自由复制粘贴。 但是,当它很容易时,让我们避免它!
自动毕业。 只要您正在处理已经为它们定义了派生公式的操作,您将“自动”获得 autograd 支持,只要您为从衍生品.yaml 向后实现中调用的所有组成函数实现复杂的支持.
在某些情况下,我们可能需要调整 autograd 公式以使其适用于复数; 例如, 'abs' 的梯度不是 'grad 。 self.sign()'。 在这些情况下,我们需要做的就是上游修复将“abs”的 autograd 公式更改为“abs_backward”,这是一个可以被覆盖的函数。
对于一般的复值反向传播,有一些参考资料:
通常,我们不需要修改 autograd,因为在大多数情况下,我们只计算实值函数的导数(损失)。
许多必要的部分今天已经到位,但它们并没有以端到端的方式组合在一起。 这是需要做的事情。
短期整合计划。 这些操作“容易”实现,因此我们应该尽快将它们纳入 PyTorch。
内核实现:
TODO:根据https://github.com/Roger-luo/TH/blob/master/ChangeLog.md生成列表
其他复杂的相关任务:
@PhilippPelz的原始评论
我想知道是否有兴趣将复杂的张量合并到 pytorch 中。
对于 CPU 支持,有 ztorch,我不久前写过 z-cutorch ( https://github.com/PhilippPelz/z-cutorch )。 这是 CudaHalfTensor 重构之前的一个分支(还没有硬件)。
如果不是工作量太大,我想慢慢和pytorch集成。 我正在使用 matplotlib 通过 fb.ptyhon 进行绘图,每次重新安装我的系统(编译所有依赖项)时都会产生巨大的痛苦,而且 pytorch 似乎很快就会在 Windows 下运行,我的一台实验 PC 运行在 Windows 上。
我还需要复杂的渐变,所以我迟早也会接触 autograd。
虽然 tf 本身支持复杂的张量,但似乎许多操作还不支持它(https://github.com/tensorflow/tensorflow/issues/2255),而且对于我的目的来说它似乎有点重量级。
如果这是一个受欢迎的想法,也许有人可以说几句话如何以及从哪里开始。
我认为我们有兴趣添加对复杂张量的可选支持。 最好的方法是在torch/lib
中分叉和处理 C 库。 这应该和master没有冲突,所以你可以这样做很长时间。 一旦您将库设置为可用状态,您就可以开始编写绑定,我们可以在此提供一些指导,说明当时如何避免冲突。
我有复杂类型编译的 TH。 我需要为 python 集成添加什么?
@PhilippPelz你的意思是: https ://github.com/facebook/ztorch/tree/master/lib/THZ? 或者您是否构建了自己的 TH 分支来支持复杂类型?
@killeent有一些关于 TH 如何绑定到 Python 的注释,他可以分享这些。
一般来说,为了获得复杂的张量,我更喜欢 THZ,因为它有测试等。
不过,为复杂的张量构建 CUDA 后端是一项相当大的工作,我们甚至还没有开始。
我之前写过 z-cutorch ( https://github.com/PhilippPelz/z-cutorch )。 这是 CudaHalfTensor 重构之前的一个分支(还没有硬件)。
这很棒。 我猜你已经朝着那个方向付出了巨大的努力:)
@sumith我做了一个复杂类型的 TH 分支。 基本上一个 THGenerateComplexTypes.h + 添加了 BLAS + LAPACK 例程,其余的几乎是免费的。 这对我来说似乎比检查 THZ 的哪些部分兼容然后复制粘贴要少得多。
我现在一直在编译 THPP,找出编译器消息,例如
/home/philipp/projects/pytorch/torch/lib/tmp_install/include/TH/generic/THBlas.h:6:40: 错误:在 '*' 标记之前需要 ',' 或 '...'
TH_API void THBlas_(swap)(long n, real *, long incx, real *, long incy);
有点棘手。
我很感激有关如何启用 python 集成的帮助。 CUDA 后端应该主要是从 z-cutorch 复制粘贴。
@PhilippPelz这里有一些关于 PyTorch 包装 TH 的说明: https ://gist.github.com/killeent/4675635b40b61a45cac2f95a285ce3c0
@killeent谢谢,看起来很有帮助。 lib/build_all.sh 现在正在编译,我觉得可以看一下csrc目录。
现在运行:
导入torch作为th
将 numpy 导入为 np
a = np.array([1+1j,2+2j])
b = np.array([3+3j,4+4j])
ath = th.from_numpy(a)
bth = th.from_numpy(b)
ath_cuda = ath.cuda()
ath_cuda += bth.cuda()
ath = ath_cuda.cpu()
打印(ath.numpy())
出局:[ 4.+4.j 6.+6.j]
以及大多数数学函数。
我将在接下来的几周内添加便利功能和 ffts。 我想在合并之前需要对所有内容进行测试。 如果您认识其他对复杂张量感兴趣并愿意为编写测试做出贡献的人,那就太好了。 突然想到这篇论文: Deep Complex Networks ,也许那些人会感兴趣。
我没有时间自己编写所有测试。
@PhilippPelz感谢您的评论。 我正在检查您的执行情况。 首先,我不确定您的ger
实施。 一些复杂的 blas 函数不包含在您的 THBlas.c 中,例如您在生成标头中将 GER 定义为zger_
和cger_
,但是在 generic/THBlas.c 中没有带有 cger_ 的 blas 函数. 不过,我可以使用您的 gemv 和其他一些功能。 IMO 也许您应该将 .gch 添加到 .gitignore? 您是否已将所有扩展推送到您的 fork 中? 我可以先根据您的实现向您的主人提出一些拉动请求。
而对于DOT
我想也许对于复杂的向量, dotc
的 dot 例程更常见?
是的,如果只使用real
会更容易实现,当real
实际上是一个复杂的时候,我感到很奇怪......
对于测试,我没有看到任何以前的 TH 测试。 我应该在哪里写这些测试? 或者我们只写一些 python 测试
是的,对不起,我看到我可能没有推送所有需要的东西。 我会在星期一再检查一次。 缺少一些声明,例如。 zger 和 cger
对于 DOT,我使用的是 cdotc 和 zdotc,它们似乎不见了,我将在下周更新。
与 pytorch 维护人员核实他们真正喜欢的命名方式。 我更喜欢你的版本,只是还没有努力。
是的,python 测试数学的东西。 大多数功能应该很容易更改,还包括复杂的数字检查。
很酷,你也在调查这个!
好的,我推送了一些更改。 TH blas 例程现在可以用于复杂的
@PhilippPelz我刚刚向您的仓库提出了拉取请求。 对于复杂的线性层和其他一些运算符。 可能有很多厄米特运算(例如复杂线性层的 bp)。 也许为张量添加一些功能? 你检查过 THNN 部分吗?
是的,hermitian 很有用。 cuda fft 现在正在工作。 cpu fft 可以从 numpy. 我还没有接触过 THNN 或 THCUNN。
@PhilippPelz我在 PR 中添加了一个简单的 Hermitian。 你能回顾一下吗? 因此,我们可以查看这些更改是否合适并进入下一步。 谢谢! PS。 看来您错过了一些标题,我也更正了该标题和其他一些警告。 对于具有实数输出的复数函数,我们应该返回实数张量而不是复数张量吗? 我已经实现了复杂类型和真实类型之间的复制方法,所以这是可能的。
在您审核后,我将重新调整所有提交。
@PhilippPelz嗨,我对您实施的THPP
部分感到很困惑。 为什么它依赖于Traits.hpp
的推力? 这将在没有 cuda 的情况下编译时导致错误。 是否可以只使用likeTraits.hpp
? 我还没弄清楚。 也许你可以提供一些线索?
@Roger-luo 是的,我在其他地方也遇到了一些问题。 我们使用的复杂类型应该来自 complex.h 或 std::complex。 由于 THPP 是 C++ 包装器,因此 std::complex 可能更合适。 你能改变一下吗?
在尝试构建 cffi 扩展时,推力也会因为完全相同的原因而导致问题。 现在我正在做一个解决方法,但正确的方法是将复杂类型更改为 THC 中的 cuFloatComplex/cuDoubleComplex。 这样 cffi 编译器就不会抱怨。 我现在只想继续研究,这占用了我太多时间:(。如果你有时间,请去做。
此外,使用自定义内核调用构建 cffi 扩展非常麻烦,因为总是需要创建一个使用 nvcc 编译的额外库,然后将其链接到 cffi 包装器。 我想没有别的办法了。 可以在 ABI 模式下使用 cffi,但该网站称“API 模式改为编译直接调用目标函数的 CPython C 包装器。相对而言,它的速度要快得多(并且比 libffi 运行得更好)。”
@PhilippPelz也许reinterpret_cast
可能是一个解决方案? 我想它应该改为cuComplex
,并在 THPP 中使用reinterpret_cast
。 我先试一试...
是的,我想如果你想在没有安装 cuda 的情况下构建 THPP,除了 reinterpret_cast 之外别无他法。
@PhilippPelz我想帮忙。 任何地方都有待办事项列表吗?
复杂类型需要启用 THNN 和 THCUNN。 你能和@roger-luo 协调吗? 此外,如果我们的目标是与 master 集成,则需要为所有复杂方法编写单元测试。
@elbamos THNN
中的大部分工作将是为每个现有层实现新的复杂反向传播方法。 Philipp 的分叉中有一个 WIP PR。 我列出了一些参考资料。
@apaszke @soumith @PhilippPelz还有两个问题:
有谁知道为什么 $ THS
中还有另一个GenerateXXXTypes.h
文件? 它看起来与TH
中的相同。
byte_order.cpp
中的以下代码是什么?
void THP_decodeFloatBuffer(float* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
for (size_t i = 0; i < len; i++) {
union { uint32_t x; float f; };
x = (order == THP_BIG_ENDIAN ? decodeUInt32BE(src) : decodeUInt32LE(src));
dst[i] = f;
src += sizeof(float);
}
}
void THP_decodeDoubleBuffer(double* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
for (size_t i = 0; i < len; i++) {
union { uint64_t x; double d; };
x = (order == THP_BIG_ENDIAN ? decodeUInt64BE(src) : decodeUInt64LE(src));
dst[i] = d;
src += sizeof(double);
}
}
关于实施其相关的复杂版本有什么建议吗? 我不确定以下实现是否正确......
void THP_decodeZFloatBuffer(std::complex<float>* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
for (size_t i = 0; i < len; i++) {
union { uint64_t x; std::complex<float> cf;};
x = (order == THP_BIG_ENDIAN ? decodeUInt64BE(src) : decodeUInt64LE(src));
dst[i] = cf;
src += sizeof(std::complex<float>);
}
}
void THP_decodeDoubleBuffer(std::complex<double>* dst, const uint8_t* src, THPByteOrder order, size_t len)
{
for (size_t i = 0; i < len; i++) {
union { uint128_t x; std::complex<double> df;};
x = (order == THP_BIG_ENDIAN ? decodeUInt128BE(src) : decodeUInt128LE(src));
dst[i] = df;
src += sizeof(std::complex<double>);
}
}
以前的decodeUInt128XE
被声明为
static inline uint128_t decodeUInt128LE(const uint8_t *data) {
return (((uint128_t)data[ 0])<< 0) | (((uint128_t)data[ 1])<< 8)|
(((uint128_t)data[ 2])<< 16) | (((uint128_t)data[ 3])<< 24)|
(((uint128_t)data[ 4])<< 32) | (((uint128_t)data[ 5])<< 40)|
(((uint128_t)data[ 6])<< 48) | (((uint128_t)data[ 7])<< 56)|
(((uint128_t)data[ 8])<< 64) | (((uint128_t)data[ 9])<< 72)|
(((uint128_t)data[10])<< 80) | (((uint128_t)data[11])<< 88)|
(((uint128_t)data[12])<< 96) | (((uint128_t)data[13])<<104)|
(((uint128_t)data[14])<<112) | (((uint128_t)data[15])<<120);
}
static inline uint128_t decodeUInt128BE(const uint8_t *data) {
return (((uint128_t)data[15])<< 0) | (((uint128_t)data[14])<< 8)|
(((uint128_t)data[13])<< 16) | (((uint128_t)data[12])<< 24)|
(((uint128_t)data[11])<< 32) | (((uint128_t)data[10])<< 40)|
(((uint128_t)data[ 9])<< 48) | (((uint128_t)data[ 8])<< 56)|
(((uint128_t)data[ 7])<< 64) | (((uint128_t)data[ 6])<< 72)|
(((uint128_t)data[ 5])<< 80) | (((uint128_t)data[ 4])<< 88)|
(((uint128_t)data[ 3])<< 96) | (((uint128_t)data[ 2])<<104)|
(((uint128_t)data[ 1])<<112) | (((uint128_t)data[ 0])<<120);
}
我目前在THPP
中使用std::complex<T>
而不是T _Complex
#$ 。 我不确定这是否可以被 Python 使用。 或者只有 c 类型T _Complex
可用于 python。 所以这里 dst 的类型是std::complex<T>
。
如果我对这个实现是正确的,可能我们需要一个uint128_t
实现,比如https://github.com/calccrypto/uint128_t ? 因为似乎并非所有编译器都支持 128 位整数(gcc 有一个 int128_t 和 uint128_t)。
@PhilippPelz我注意到您的分叉没有启用问题-您的项目的状态如何? 我有点沮丧,复杂的张量不在 pytorch 的路线图上
@el3ment我已经为 CPU 添加了一个复杂的后端https://github.com/pytorch/pytorch/pull/4899但它还没有经过审查......而且我还没有收到任何关于我的 PR 的评论,因此我转而使用Julia 编程语言最近...
我上次给@PhilippPelz发了邮件,我猜他的repo 还在v0.1 下,他一直忙于论文直到9 月? 虽然我正在开发 v0.3 的新 CUDA 后端,但我没有时间单独完成所有这些绑定。 map/reduce 函数与 v0.1 不同,有一些优化,但它们不能简单地转换为支持复数。 如果有人愿意帮忙,我会很高兴...
我愿意提供帮助。
2018 年 4 月 10 日晚上 10:52,Rogerluo [email protected]写道:
@el3ment我为 CPU #4899 添加了一个复杂的后端
我上次给@PhilippPelz发了邮件,我猜他的repo 还在v0.1 下,他一直忙于论文直到9 月? 虽然我正在开发 v0.3 的新 CUDA 后端,但我没有时间单独完成所有这些绑定。 map/reduce 函数与 v0.1 不同,有一些优化,但它们不能简单地转换为支持复数。 如果有人愿意帮忙,我会很高兴...
—
你收到这个是因为你被提到了。
直接回复此电子邮件,在 GitHub 上查看它,或将线程静音。
@elbamos很酷,pytorch 团队似乎更喜欢单独的实现。 稍后我会为其他部分更新我的叉子。 但我真的没有时间做这件事,我想我们应该在 pytorch 团队有计划时开始着手,因为这将是 pytorch 的一个重要扩展。
嗨,我的代码在 v0.2 之后进行了一些提交
我已经看到有一个相当大的重构将所有张量代码移动到 Aten。 这意味着不能轻易地将我的 fork 合并到当前版本中,并且可能涉及更多工作。
我还在写我的博士论文,但无论如何我都打算等到 0.4 版本,直到变量和张量的合并发布。 我担心如果提前进行重构,可能会有太多的重构赶不上它。
@elbamos如果你愿意,你可以开始向我的 fork 添加东西,我会将它合并。在你的位置,我会为你正在做的任何项目实现你需要的东西。 TH(CU)NN 是一个相当大的接口,工作量很大。
@el3ment我没有时间处理其他问题。 但是,如果您需要实现一些不存在的东西,我会合并一些东西。
如果您只想要开箱即用的复数,我强烈推荐 tensorflow。
如果有编译问题,我也会提供帮助。
如果我继续做博士后,我会在某个时候将所有这些东西移植到当前版本。 脸书不想支持这一点,真是令人难过。 :((
@PhilippPelz同意,这真的很可悲,实际上 tensorflow 并不支持量子物理学中的所有运算符......我已经开始使用 Julia 并放弃了 python。
@Roger-luo 很有趣,您使用的是特定的 julia 包还是全部是自己编写的代码?
@PhilippPelz我正在 Julia 中开发一个量子多体工具包(从 PyTorch PR 开始),它包括一个基于以前一些关于复杂神经网络的论文的复杂/真实神经网络实现,我发现使用 Julia 开发它非常容易元编程。 我目前只是把它放在QMTK.jl中,它仍在进行中,我还没有完成我想要的一切。 PyTorch 确实给了我很多启发,但对于复杂的支持真的很抱歉......
但我确实有计划在未来将它分离到一个神经网络单个包中(只是现在不想维护几个 repos)。 并且会有更多的人从中国科学院物理研究所加入进来。 我会在第一个标记版本之后接受 PR(几周后)。
如果您对它的发展感兴趣,可以观看它。
如果 PyTorch 团队未来仍有复杂支持的计划,我愿意提供帮助。
不错,我会关注的!
嘿伙计们,很抱歉,自从这个问题开放以来,我们还没有对它做出回应。
这里有两个事实:
自从这个问题在 2017 年被打开以来,一些重要的事情已经发生了变化,这可能会使实现复杂的支持变得更简单一些。 首先是我们现在有了 ATen,这是一个用于操纵张量的符合人体工程学的 C++ 库。 这意味着您不必复制粘贴大量 TH/THC 代码,并希望您已正确掌握所有手动引用计数,您可以编写 C++ 代码,就好像它是 Python 一样,它会运行得很快。 其次是我们正在开发一个新版本的 ATen,称为 C10,它在开放后端方面比 ATen(这是一个封闭的东西)更加认真,这应该更容易处理复杂的支持,因为它不会' t 实际上需要分叉 PyTorch,只需添加一个新的代码目录。
所以,@Roger-luo 和@PhilippPelz ,我们很乐意得到您的帮助,使复杂的后端成为现实,但我们真的很想找到一种方法来帮助我们在未来可持续地维护它。 让我们知道您的想法。
@ezyang如果你人手不够,我可以尝试在未来保持复杂的张量部分,我刚开始读博士(实际上这是我的间隔年),因此我不会有写论文的问题至少最近几年。 但是如果没有 pytorch 团队的任何反馈,我真的无法继续贡献。 我确实认为这个大扩展应该有一个路线图。 我们可以顺利添加复杂的支持,这样您的人就不需要审查大型 PR,这将减轻开发人员跟踪 master 分支的工作量。
首先,我认为复杂支持的主要问题是 CUDA 部分。 使用 ATen 或任何其他库支持 CPU 部分非常容易,如果有任何反馈,我可以在几天内重写 CPU 部分。 对于 CUDA 部分,我可能会担心一些问题,我认为这可能会导致两种不同的方法:
float2
等来模拟单个复杂值,例如cuComplex
在 CUDA 部分中执行的操作。FloatTensor
和DoubleTensor
来模拟 ATen 的 C++ 部分中的复杂张量。第二种方法的原因是因为在THC
中,pytorch 使用了一些技巧来加速 map/reduce 操作,它不适合cuComplex
琐碎,因为cuComplex
实际上是float2
,但__shfl_xxx
函数本身不支持float2
。 目前我不确定如何有效地为float2
模拟这样的功能。
第二种方法会更容易,因为我们现在不需要关心硬件,我们可以让我们的新复杂扩展在旧设备上工作更容易。 但是,由于内存地址不连续,这可能会导致一些开销。
此外,我发现要将复数集成到 ATen 中,我们可能必须处理四种在硬件上实际上相同的不同类型: std::complex
、 thrust::complex
、 cuComplex
、 float2
有时可能很危险。 (其实我去年就遇到过这个问题, reinterpreter_cast
就是解决方案)。
不过,我个人更愿意写出更原生的东西。
而且我认为我们可能需要一个时间表或路线图,我们可以拾起每个小部分并一起工作,这样我就不需要自己跟踪大师了,这完全不可能......
在我尝试实现CPU后端时有一个ChangeLog ,我分类功能需要修改日志中的复数。 我们可以根据此日志编写路线图。
此外,由于我的签证刚刚被(澳大利亚)拒绝,我必须开始一个gap year,如果你需要有人继续工作,我可以申请实习。
在过去的一天里,我想了很多。 我们无法按原样合并罗杰的努力,这有点令人难过,但我心想
“我们如何构建复杂的张量支持,同时保持低维护开销?”
这就是我根据上述目标制定的有效计划:
sparse
张量。 添加基本类型会导致大量维护开销和横切更改。 维护开销不是关于“谁维护复杂位?”,而是更多“现在所有核心开发人员在进行任何基本更改、任何 ATen 更改等时都应该意识到这种复杂类型”。torch.stack([real1 * real2 - imag1 * imag2, real1 * imag2 + imag1 * real2], dim = -1)
其中real1 = input1[:, :, :, ..., 0]
它必须是 [Tensor Shape x 2],因为 BLAS、cublas 和 MAGMA 都期望它们自己的复杂类型与 float2 字节兼容。 此外,无法在 python 级别处理 blas、cublas 和 magma 调用。
我认为复数乘法不会只有 20%,除了实数和图像部分的计算之外,您不是有 4 个完整的复制操作吗?
无论如何,如果我不必不断地合并来自 master 的更改,我仍然会很高兴。
同意@PhilippPelz ,我们可能会损失很多性能,因为我们将失去 BLAS、cublas 和 MAGMA 的复杂支持。 但我不确定。 然而,需要明确的是,复张量与稀疏张量完全不同,大多数库如scipy.sparse
和 Julia 的SparseArrays
将稀疏数组视为基本多维数组的组合。 但是没有人通过复合两个实数数组来处理具有复杂类型的多维数组......(这里没有人指的是 tensorflow、arrayfire、numpy 和 Julia)。 虽然在 MXNet 中,FFT 确实是由两个真实张量的组合完成的,但它们不支持复杂的......似乎 tensorflow 实现了一个 DataType 作为不同类型的包装器,包括complex64
和complex128
见types.proto
首先,逐元素函数(函数调用 map/reduce)不会有很大的性能损失(至少,这些操作的内存是连续的)。 但是我认为我们应该首先尝试对一些 BLAS 函数进行基准测试,看看FloatTensor
的组合在 GPU 上是否具有与Complex64Tensor
相似的性能,以及我们将在性能上损失多少实施草案,例如:
gemm
gemv
复合复数张量看起来像(或仅使用shared_ptr
):
class ComplexTensor {
FloatTensor *real;
FloatTensor *imag;
};
然而,正如我在第一种方法的缺点中提到的那样,如果我们想要更原生地做到这一点,像__shfl_xxx
这样的函数看起来也是一个障碍。
当前torch.fft
返回一个形状[dim1, ..., dimN, 2]
的浮点张量
@ezyang C10 发布的时间表是什么? 这听起来像是在 master 分支中开始支持 complex 的一个非常合理的点。
@PhilippPelz绝对不是 0.4。 我们的内部目标是 6 月,希望等待时间不会太长。
@ezyang你在六月提到过,你是否设法为 PyTorch 添加了对复数的支持?
我认为他的意思是 C10,而不是复杂的支持。 C10 将使添加复杂性变得更容易。 我就是这么理解的。
是的,C10 将开放注册张量类型和功能。 因此,将复杂类型添加为单独的包会容易得多。
复数是否有预计到达时间? “更容易”是否意味着“可能会很快完成”?
@themightyoarfish更容易,我的意思是我们不会被什么可以推送到 pytorch master 所阻止。 我们还没有设定预计到达时间。 一旦我们开放注册到 PyTorch,我将确定工作范围。
@sumith你还需要人来处理这个(复数)吗? PyTorch 团队会支持复数吗? 如果你愿意,我可以在 9 月份花一些时间来处理这个问题,因为我会维护QuCumber (它会大量使用复数)
@Roger-luo 是的。 一旦我们在 PyTorch 后端开放注册,我想与您联系,我们可以制定详细信息。
@ezyang我们会在 9 月之前进行开放式注册吗?
@sumith Cool,随时为您服务。
我们可以实现它。 (我们不会有“完整”的新系统,但只要我们设置它以使其可重构,我们就可以随着新的发展继续移动它。这将是新开放的一个很好的测试用例注册。我可以确保发生这种情况。)
@ezyang现在有什么笔记吗? 在开始工作之前,我可以阅读它。 与上次相比,情况似乎发生了很大变化。
@Roger-luo @PhilippPelz我还想帮助您实现复杂的张量。 我的博士研究也需要它..
@alexgomezalanis也许我们可以有一个频道来讨论 slack,我刚刚创建了一个频道调用#complex-numbers
。 但我要到 9 月才开始研究它(仍然需要处理我的一些 Julia 代码......)
顺便说一句,与上次相比,它似乎发生了很大变化。 我会花一些时间赶上我的手之前。
@alexgomezalanis我不能。 您必须先在 slack 上加入 pytorch 的工作区。 我找不到你。 请发送电子邮件至地址: [email protected]以获得邀请。
@Roger-luo @alexgomezalanis很高兴在复杂的张量问题上再次看到生活。 我也可以提议参与,但实际上这要到 9 月底/10 月初才会发生。 对于这个问题的相当多的评论者,复杂的张量支持对我的博士项目非常有帮助。
去年我还试图挽救我的研究 😏……但现在我只想让我的旧 1w+ loc 代码重新焕发生机。 🤣 让我们在 slack 上聊天吧!
:) 是的,让我们闲聊。 刚刚在邮件文件夹中找到了邀请。
正在进行的插件(仅在短期内用于 CPU)在这里: https ://github.com/Roger-luo/pytorch-complex
请随时给我问题和公关。
我已经在本期的顶部发布了有关如何执行复杂实施的说明。
我最近开始使用 PyTorch,我非常喜欢它——它比 TensorFlow 好用得多。 然而,复杂的张量支持对我的研究(光学神经网络)非常重要。 这是否仍在积极进行中? 如果是这样,是否有人知道复杂张量支持的(松散)时间表?
我很乐意尽我所能帮助解决这个问题,但我对 PyTorch 还比较陌生,所以我还不知道这个功能有多大。 我的一些实验室成员也表达了对复杂张量支持的浓厚兴趣(在物理学中,添加这可能使 Torch 几乎成为 NumPy 的 GPU 加速替代品)并且如果这意味着在不远的将来。
嗨@bencbartlett
我还在努力慢慢地工作......但我目前也只是一名学生(情况相当不稳定),这意味着我不能全职工作,只能在业余时间工作。 (我从去年开始在 Julia 中实现我的研究相关代码,这意味着只有我们的遗留包需要来自 torch 的更好的复数支持。)。
如果复数对你来说很重要,并且在火炬中迫切需要,我建议试试这个:
https://github.com/PIQuIL/QuCumber/blob/master/qucumber/utils/cplx.py
它超级慢......但它至少可以工作。 或者我有一个旧 TH 风格的 C 版本。
这不会是一个几天就能完成的小项目。 因此,我无法保证在 CPU 或 CUDA 上具有复杂价值的完整功能支持的任何特定时间范围。
不过,我很乐意帮助您与我一起工作。 我建议您首先尝试解决我在扩展存储库中发布的问题。 如果您有任何问题,请随时通过 slack 或电子邮件或问题向我提问(因为还没有太多文档)。
不幸的是,我还无法访问 PyTorch Slack。 (我发了两次电子邮件请求邀请,但没有收到回音。)有人可以邀请我吗? ([email protected])
@Roger-luo 我一定会看看你的叉子,但我不能保证我会提供很多帮助——我的 C++ 生锈了,正如你所指出的,很难找到时间来解决这个问题学生。 QuCumber 实用程序很好,但不幸的是它们对我没有太大帮助:在复杂张量得到 GPU 支持或被 autograd 和 torch.nn 支持之前,它们提供的实用程序不能超过 NumPy 所能提供的。
@soumith @ezyang如果能得到 PyTorch 团队的更多关注,那就太好了! 复杂的支持似乎是通用张量库的一个重要特性,它在物理学中几乎是必不可少的,特别是在过去几年中,在 ML 中,人们对复值模型的兴趣迅速增长。
@bencbartlett QuCumber 的方法可以在带有 AD 的 GPU 上使用......它只是超级慢......我的意思是如果你只想要那个 AD,你也许可以使用它。
是的,坦率地说,我正在使用https://github.com/FluxML/Flux.jl的略微修改版本和我自己在 Julia 中的一些包进行研究(在某些情况下,我需要在 GPU 上使用张量的复杂 AD )。 source2source AD 包 Zygote.jl 可以在复杂的张量上做 AD,但它处于非常早期的阶段,可能有段错误。 与torch相比,生态系统还不是那么稳定,有时我不得不稍微修改一下这些实现以供自用......但它基本上可以满足我研究量子物理学的需要。 我也可以在 GPU 上使用复杂的张量。
我认为不需要对torch.nn
的复杂值支持,一旦复杂张量起作用,我们可能只需要为autograd
添加一些定义,因为像线性层这样的东西可以保持不变. 并且某些激活函数在希尔伯特空间中可能没有标准扩展......(您可以查看我的合作者@GiggleLiu的博客文章)
对于 pytorch-complex 扩展,我不确定我们什么时候才能在 GPU 上获得对 AD 的全面支持……这对我来说似乎还很遥远。 我想说 CPU 实现将经历一些需要在主树中打补丁的时期(例如类型提升、simd 支持等),这也可能与即将在 C++ 中实现 ATen 并摆脱 TH 等有关,然后我们将能够更快地为复杂张量添加运算符。
我可以在春季申请实习(我刚刚问过@ezyang )。 因此,在我开始攻读博士学位之前,我也许可以全职工作几个月。 让我们来看看。
同时,我实现了我自己的复数乘法版本。 但是,当我对其进行分析时,会发现大量时间用于:torch._C_._cuda_isDriverSufficient
你知道为什么吗? 如果您知道复数乘法的更好实现,请告诉我。 不知何故,我的版本(即使针对乘法数进行了优化:3 而不是 4)似乎相对较慢,例如输出张量的 irfft 比我的元素乘法快 10 倍。 PyTorch 的 C++ 级别是否支持复数乘法?
def complex_mul(x, y, out):
uavc = x[..., 0] * (y[..., 0] + y[..., 1])
out[..., 0] = uavc - (x[..., 0] + x[..., 1]) * y[..., 1]
out[..., 1] = (x[..., 1] - x[..., 0]) * y[..., 0] + uavc
def test_complex_mul_out_tensor(self):
N, C, H, W, I = 128, 3, 32, 32, 2
K = 16 # number of filter banks
repetitions = 1000
dtype = torch.float
if torch.cuda.is_available():
device = torch.device("cuda")
else:
device = torch.device("cpu")
x = torch.randn(N, 1, C, H, W, I, dtype=dtype, device=device)
y = torch.randn(K, C, H, W, I, dtype=dtype, device=device)
start_mul_time = time.time()
out = torch.empty(N, K, C, H, W, I, dtype=dtype, device=device)
for _ in range(repetitions):
complex_mul(x, y, out)
print("multiplication time: ", time.time() - start_mul_time)
我们正在尝试从 C++ 中支持它。 见顶帖。 如果你可以编译扩展,它至少现在应该适用于标量乘法......
您的实现类似于我们在 QuCumber 中的实现。 如果您没有为复数调用正确的 cuda 内核,它可能会调用更多额外的 GPU 线程。 如果你没有 C++ 后端作为 Python 的支持,你可能会失去 SIMD。
我建议您运行nvprof
以获取更多详细信息。
@Roger-luo @apaszke @soumith感谢这个线程顺便说一句。 我实现了一个从继承 torch.Tensor 的基础复杂张量。
我将前半部分视为真实,后半部分视为虚构,并实现了我自己的基本算术运算以及我研究所需的其他一些运算。
我针对 TensorFlow 和 numpy 进行了验证。 我实现的渐变和所有操作都与它们的输出相匹配!
它只是作为一种保留,直到 PT 完全支持复杂的张量。
特征:
pip install pytorch-complex-tensor
谢谢@williamFalcon !
这个有更新吗? 只是想知道是否有计划将复杂类型支持集成到 pytorch 中。
嗨, @whmrtm
@ezyang正在研究https://github.com/Roger-luo/pytorch-complex/issues/4或者任何对此感兴趣的人都可以帮助我们使其运行。 此问题将解决一些基本的广播问题(解决此问题后您可以使用很多功能)。 请随时进行任何 PR 或让我将您添加为合作者。
直到夏天我才能做任何事情,必须为我们自己的包完成一个新版本。
嗨,@whmrtm
@ezyang正在研究Roger-luo/pytorch-complex#4或者任何对此感兴趣的人都可以帮助我们让它运行。 此问题将解决一些基本的广播问题(解决此问题后您可以使用很多功能)。 请随时进行任何 PR 或让我将您添加为合作者。
直到夏天我才能做任何事情,必须为我们自己的包完成一个新版本。
感谢您的更新,我会看看我能做些什么。
嗨@Roger-luo
我可以访问与复杂张量支持主题([email protected])相关的松弛频道吗? 我通过电子邮件发送了邀请,但还没有发生任何事情。 现在我正试图找出从哪里开始为这个问题做出贡献。 我猜https://github.com/Roger-luo/pytorch-complex/issues/4现在是当前的切入点?
@beconstant是的,这是起点,这应该使一些广播功能起作用,但我不知道为什么它会在 cuda 上引发类型提升错误,它正在 CPU 上工作。 (虽然我们一开始并不打算支持 cuda,但这会导致构建失败)
我无法向您发送邀请电子邮件(我无权访问)。 我认为你应该按照 pytorch 官方指南加入 slack。 但我们总是可以在问题/公关中讨论。
@Roger-luo 好的,知道了:)
让我知道你们是否需要任何帮助。 我将从构建指定的 pytorch 版本开始。 pytorch-complex/issues/4 有什么进展吗?
让我知道你们是否需要任何帮助。 我将从构建指定的 pytorch 版本开始。 pytorch-complex/issues/4 有什么进展吗?
@dylanbespalko嗨,我迫切需要在复值版本中实现的 pytorch。
非常感谢您的贡献。
最好的祝福,
Zellar209
嗨@Zellar209 ,
我感觉@ezyang正在努力解决一个更大的问题( pytorch-complex/issues/4 )。 我现在有一个 AMD 系统和 3 周后的 Nvidia 系统,我可以用它来增加对 GPU 的支持。
我想问题只是原始类型提升更改破坏了 CUDA,只要解决了 PR,至少有一些运算符在 CPU 上工作,我们还没有 CUDA 支持......
恕我直言,我认为我们应该先关注 CPU 并让事情正常工作,然后再考虑 GPU。
仅支持 CPU 即可。 这种类型的提升问题( pytorch-complex/issues/4是否由 fb 内部处理?可以在外部处理吗?
嗨@dylanbespalko; 我确实告诉@Roger-luo 我会调查它(因为我可能最适合找出问题所在),但我还没有时间看它。 如果您确实想了解如何解决问题,我很乐意提供建议。
嗨@Zellar209 ,
我感觉@ezyang正在努力解决一个更大的问题( pytorch-complex/issues/4 )。 我现在有一个 AMD 系统和 3 周后的 Nvidia 系统,我可以用它来增加对 GPU 的支持。
是的,我现在不需要任何 GPU,我使用的是 MAC 系统。 但是我在构建这个项目时遇到了一些错误。
嗨@Zellar209 ,你能发布你在pytorch-complex 的问题中得到的东西吗? 我认为 Mac 的新 Xcode 有问题,这使得构建变得困难。 但是人们需要更多的错误信息来找出原因。
我询问了操作系统和错误消息,但您没有回复...
嗨@dylanbespalko; 我确实告诉@Roger-luo 我会调查它(因为我可能最适合找出问题所在),但我还没有时间看它。 如果您确实想了解如何解决问题,我很乐意提供建议。
感谢您早日回复。
构建“torch_complex.cpp”扩展
gcc -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/anaconda3/include -arch x86_64 -I/anaconda3/include -arch x86_64 -I/ anaconda3/lib/python3.6/site-packages/torch/include -I/anaconda3/lib/python3.6/site-packages/torch/include/torch/csrc/api/include -I/anaconda3/lib/python3。 6/site-packages/torch/include/TH -I/anaconda3/lib/python3.6/site-packages/torch/include/THC -I/anaconda3/include/python3.6m -c src/module.cpp -o build/temp.macosx-10.7-x86_64-3.6/src/module.o -g -stdlib=libc++ -std=c++11 -DTORCH_API_INCLUDE_EXTENSION_H -DTORCH_EXTENSION_NAME=cpp
gcc:错误:无法识别的命令行选项“-stdlib=libc++”
在 src/module 中包含的文件中。 cpp:2 :
在 src/CPUComplexType.h:60 包含的文件中:
src/CPUComplexTypeImpl.h:102:105: 警告: 'IntList' 已弃用 [-Wdeprecated-declarations]
Tensor & CPUComplexType::set_(Tensor & self, Storage source, int64_t storage_offset, IntList sizes, IntList strides) const {
^
/anaconda3/lib/python3.6/site-packages/torch/include/c10/util/ArrayRef.h:273:7:注意:“IntList”已在此处明确标记为弃用
使用 IntList C10_DEPRECATED_USING = ArrayRef
^
在 src/module 中包含的文件中。 cpp:2 :
在 src/CPUComplexType.h:60 包含的文件中:
src/CPUComplexTypeImpl.h:105:76:错误:命名空间“at”中没有名为“scalarTypeToDataType”的成员
auto source_ = checked_storage(source,"source",2, DeviceType::CPU, at::scalarTypeToDataType(CPUComplexTypeInfo::scalar_type));
~~~~^
生成 7 个警告和 2 个错误。
我无法修复它。 我真的希望你能帮助我!
大家好,
感谢您的反馈意见。 我想我可以花一周的时间来研究这个。 到目前为止,我已经编译了 @Roger-luo 的 pytorch-complex 如下:
@Zellar209 :我附加了在 macOS 10.13 上运行的环境变量。
删除现有的pytorch分布如下
conda 卸载 pytorch
pip 卸载火炬
pip uninstall torch # 运行此命令两次
python setup.py 清理
删除 python site-packages 文件夹中的 torch 文件夹(如果存在)。
重命名(或删除)以前的 pytorch 源文件夹(引用它的东西)。
安装 PyTorch 修订版 6cb593b88cb0c411690b4957850058329526d87b。
git clone [email protected]:pytorch/pytorch.git
git checkout 6cb593b88cb0c411690b4957850058329526d87b
git submodule update --init —recursive
export CMAKE_PREFIX_PATH=${CONDA_PREFIX:-"$(dirname $(which conda))/../“}
MACOSX_DEPLOYMENT_TARGET=10.13 CC=clang CXX=clang++ python setup.py develop
python
>>> import torch
python setup.py install
python setup.py build
python setup.py test
# ERROR: test (unittest.loader._FailedTest)
# ERROR: test_scalar_binary_op (tests.test_tensor.TestComplexTensor)
from torch_complex import torch
a = torch.ones(3, dtype=torch.complex128)
a*a
RuntimeError: promoteTypes with complex numbers is not handled yet; figure out what the correct rules should be
@ ezyang ,@罗杰罗:
张量操作的类型提升的一切似乎都在c10/core/ScalarType.h中完成
我发现了错误AT_ERROR("promoteTypes with complex numbers is not handled yet; figure out what the correct rules should be”);
看来我必须在此表中添加 c8 和 c16 的条目。
这和9515有关系吗? 我认为这只是为了调用 numpy 函数。
这是一个很好的起点吗?
9515 无关。 但是,在 ScalarType.h 中修复此代码路径是一个不错的起点。
我在 ScalarType.h 中修复了代码路径
BinaryOps (add, sub, mul, div) 有效,但前提是两个参数都是张量。
其他一些奇怪的问题,但我需要多看一些。
@dylanbespalko我在这里添加了类型促销: https ://github.com/pytorch/pytorch/pull/11641
你可以复制它,但问题是这会以某种方式破坏 CUDA。
IIRC,由于 gcc 版本,存在线路错误。 我在那里有一些解决方法。
啊,谢谢@Roger-luo。 我在看#11641的评论。 明天我会更好地复制代码。
当我没有 CUDA 设备时,我如何知道我何时损坏了 CUDA? 我假设CI会告诉我?
是的,只要你提交 PR,它就会告诉你哪个坏了。 如果一切都过去了,那么我们就可以合并它并使事情正常进行。
好的,那么我将开始提交 PR,以便我知道它何时发生。
@dylanbespalko嗨,您的环境中似乎还有一些错误?
如果你修复它,请与我们分享。 非常感谢。
大家好,
在复制了几个 @Roger-luo 的提交后,我尝试了几个 PR。 不幸的是,我现在没有 CUDA GPU,带有 CUDA 的 CI 机器也没有初始化。 我现在无法重新创建 CUDA 测试失败,所以我会在几周后在该 GPU 上本地运行时回到这个问题。 至少看起来很有希望。
@ ezyang ,@罗杰罗
我看过 Roger 的 PR #11641 :
我还查看了 PyTorch 最近的一些发展:
在我看来,正在开发一种新的“树外”扩展功能,这将使我能够在不破坏其余 pytorch 的情况下研究复数支持。 我的目标是:
@ezyang
您能否为您展示的这种树外设备/布局/dtype扩展提供预期的时间表? 我们可以在未来 3 个月内期待这个功能吗?
@ezyang
您能否在没有 AVX/SSE 支持的情况下合并 CPU 上的复数支持? 我计划在单独的合并请求中提交以下内容:
我计划在接下来的几天内在 intel/arm cpus 上对此进行测试。
@ezyang ,
我正在研究像fft()
和var()
这样的操作,其中复数实现必须将张量数据转换为形状的双张量: (complex_shape, 2)
。 这不适用于任何现有的张量方法:
显然我可以做一些低效的事情,比如:
def to_float(tensor):
return th.stack((tensor.real().type(th.float64), tensor.imag().type(th.float64)), -1)
def to_complex(tensor):
tensor = tensor.type(th.complex128)
return tensor[..., 0] + 1j*tensor[..., 1]
显然,这是创建副本,而我只需要static_cast<double>
并将张量的形状更改为(old_shape, 2)
。 你对如何做到这一点有什么建议吗?
此外,numpy 中有一个 hack 允许您执行此操作:
a = np.array([1 + 1j], dtype=np.complex128)
a.dtype = np.float64 ## This works
a = torch.tensor([1 + 1j], dtype=torch.complex128)
a.dtype = torch.float64 ## This does not work
设置 dtype 的能力在这种情况下确实有效,但是它可能是不可预测的。
有关将复数解释为长度为 2 的实数数组的一些附加信息。 以下在 C++11 中有效。
对于任何指向复数数组元素 p 和任何有效数组索引 i 的指针,reinterpret_cast
(p)[2 i] 是复数 p[i] 的实部,reinterpret_cast (p)[2 i + 1] 是复数 p[i] 的虚部。 (C++11 起)
我认为这意味着可以将 complex_tensor 转换为具有形状 (complex_shape, 2) 的 real_tensor,然后执行操作而无需调用分配新内存的real()
和imag()
。
@dylanbespalko当你问这个问题时我很害怕 :) std::complex
保证意味着如果你有数据指针std::complex<float>*
,你可以安全地将它转换成float*
(喃喃自语严格混叠),然后将其传递给您正在使用的任何 fft 东西。 如果您只需要实现 fft/var 即可传递此低级代表,那将是最简单的。
但是,如果您需要从字面上将复杂张量重新视为浮点张量,我们就有点麻烦了,因为今天 PyTorch 中没有这方面的先例。 Storage dtype 一直与 Tensor dtype 一致。 因此,如果您制作复杂的存储,则无法将其视为浮动存储。
我的一个想法是也许我们应该放松这个不变量。 这个想法是:
我不确定我们必须更改多少代码才能使这个不变量发生。
@ezyang ,
是的,这是不可避免的……
如果您只需要实现 fft/var 即可传递此低级代表,那将是最简单的。
是的,这在很多情况下都是可能的。 您是否能够提供如何将张量数据解释为 std::vector 的代码片段?
但是,如果您需要从字面上重新查看复杂张量作为浮点张量,....
我想很少使用另一种 dtype 来查看张量。 我为Tensor
实现了set_dtype()
方法,但出现了一些错误。 我也没有更新步幅以反映形状的变化。 我不确定为什么设置 dtype 在 numpy 中起作用(这是巧合吗?),但是当您将数据上传到数模转换器 (DAC) 时,它通常期望实数/虚数数据是交错的。 正如您所建议的那样,也许这会激发将 tensor dtype 与 storage dtype 分离的需要。
我暂时避免这样做。 我确信对我来说还有其他性能瓶颈。
是的,这在很多情况下都是可能的。 您是否能够提供如何将张量数据解释为 std::vector 的代码片段?
不完全是 std::vector,但我在想象这样的事情:
Tensor complex_tensor;
assert(complex_tensor.is_contiguous());
std::complex<float>* cp = complex_tensor.data_ptr<std::complex<float>>();
float* fp = reinterpret_cast<float*>(cp);
auto num_floats = complex_tensor.numel() * 2;
我为 Tensor 实现了 set_dtype() 方法,但出现了一些错误。 我也没有更新步幅以反映形状的变化。
是的,如果您不修复跨步,这可能是个坏主意。 此外,我不是张量转化为其他 dtype 的忠实粉丝; 最好只是不合时宜地做这一切:)
但是,当您将数据上传到数模转换器 (DAC) 时,它通常期望实数/虚数数据是交错的。 正如您所建议的那样,也许这会激发将 tensor dtype 与 storage dtype 分离的需要。
是的,最终这是正确的做法,但我同意现在不这样做更容易。
@ezyang ,
我开始搞乱复数 CUDA 支持。
有两个二进制兼容选项:
推力::复杂容器似乎是要走的路。 Thrust::Complex API建议thrust::complex<T>
容器可以分配在主机和设备内存上,而std::complex<T>
只能分配在主机内存上:
__host__ __device__ thrust::complex< T >::complex (const complex< T > &z) //thrust container
__host__ thrust::complex< T >::complex (const std::complex< T > &z) //stl container.
这是否暗示 AT_DISPATCH_COMPLEX_TYPES 应该设置using scalar_t = thrust::complex<double>
而不是using scalar_t = std::complex<double>
?
Pytorch 如何为真实数据类型自动调用std::log
的 CUDA 等效项? 我怎么知道有一个相当于数学内核的 CUDA?
thrust::complex<double>
普遍用于 CPU 和 CUDA 的困难在于,如果您只构建 CPU,我们实际上并没有针对推力进行构建。 我想有很多选择; 我们可以滚动我们自己的复杂类型(类似于我们滚动自己的半类型),或者您可以重新解释 cast your way to win,因为std::complex<>
被定义为具有特定的二进制布局。 这取决于你,但现在重新解释类型之间的转换似乎更容易。@iotamudelta在 #29547 中提出了 C++11 合规性问题
std::real 只是 C++14 中的 constexpr
如果我理解正确, std::real()
需要是constexpr
以便 hcc 编译器可以编译__device__
的指令。
可能的解决方案:
complex<double>
转换为double
:abs
也不起作用:模板找到一种包装函数的方法:
大多数对 std::real 的调用都是在aten/src/ATen/native/cpu/zmath.h
中进行的。 示例:将inline
替换为constexpr
:
inline VALUE_TYPE real_impl (SCALAR_TYPE z)
->
constexpr VALUE_TYPE real_impl (SCALAR_TYPE z)
inline std::complex<float> real_impl <std::complex<float>> (std::complex<float> z)
-> constexpr std::complex<float> real_impl <std::complex<float>> (std::complex<float> z)
inline std::complex<float> real_impl <std::complex<double>> (std::complex<float> z)
-> constexpr std::complex<float> real_impl <std::complex<double>> (std::complex<float> z)
这不会编译,因为仍然存在对std::real()
的嵌套调用,这不是constexpr
。
3. 如果我使用 std::complex
std::complex<double>
转换为double
以满足您的要求?@iotamudelta , @bddppq , @ezyang ,
我在 CUDA Thrust::complex API 上添加了对复杂 UnaryOps 和 BinaryOps 的支持,但在提交之前我需要问几个问题。
我定义了一个模板函数,允许您在处理复数时使用thrust::complex 数据类型。
aten/src/ATen/native/cuda/zmath.cuh
#pragma once
#include <complex>
#include <thrust/complex.h>
namespace at { namespace native {
namespace {
template <typename TYPE>
struct ztype_cuda {
using value_t = TYPE; // Complex template type
using thrust_t = TYPE; // Equivalent thrust type
};
template <>
struct ztype_cuda<std::complex<float>> {
using value_t = float;
using thrust_t = thrust::complex<float>;
};
template <>
struct ztype_cuda<std::complex<double>> {
using value_t = double;
using thrust_t = thrust::complex<double>;
};
} // end namespace
}} //end at::native
然后在aten/src/ATen/native/cuda/BinaryOpsKernel.cu
代替:
void add_kernel_cuda(TensorIterator& iter, Scalar alpha_scalar) {
AT_DISPATCH_ALL_TYPES_AND2(kHalf, kBool, iter.common_dtype(), "add_cuda/sub_cuda", [&]() {
auto alpha = alpha_scalar.to<scalar_t>();
gpu_kernel_with_scalars(iter, [alpha]GPU_LAMBDA(scalar_t a, scalar_t b) -> scalar_t {
return a + alpha * b;
});
});
}
和:
void add_kernel_cuda(TensorIterator& iter, Scalar alpha_scalar) {
AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND2(kHalf, kBool, iter.dtype(), "add_cuda/sub_cuda", [&]() {
using thrust_t = typename ztype_cuda<scalar_t>::thrust_t;
auto alpha = thrust_t(alpha_scalar.to<scalar_t>());
gpu_kernel_with_scalars(iter, [alpha]GPU_LAMBDA(thrust_t a, thrust_t b) -> thrust_t {
return a + alpha * b;
});
});
}
thrust_t
替换为对非复杂数字更友好的名称,例如scalar_t_c
?cuComplex
而不是thrust::complex
吗?hip_complex
吗?cuComplex
更多的功能。请让我知道你在想什么。
@iotamudelta
我更新了关于 std::real() 的讨论。 你能确认 std::complex
嗨@dylanbespalko ,
我猜@iotamudelta抱怨的是复杂类型的cast_and_store
缺少C10_HOST_DEVICE
,如果该代码路径曾经在 GPU 上执行,这将是一个 UB。
目前,该动态转换工具仅在 GPU TensorIterator 中使用,并且仅在有类型提升时使用。 由于目前 GPU 不支持 complex ,现在复杂类型的cast_and_store
没有C10_HOST_DEVICE
限定符,并且使用std::real
这对于主机来说是完全可以的-唯一的功能。 这里没有UB,因为它没有被使用,没有什么你需要担心的。
但是由于您想向 GPU 添加对复杂的支持,并且类型提升支持复杂,正如我们在https://github.com/pytorch/pytorch/blob/master/c10/core/ScalarType.h#L398中看到的那样 - L420,您需要在此代码路径上非常小心,您可能需要进行一些修改才能使其正常工作:
当然,您需要像@iotamudelta在https://github.com/pytorch/pytorch/pull/29547中所做的那样添加C10_HOST_DEVICE
,但这还不够,因为只需添加C10_HOST_DEVICE
如@iotamudelta所述,没有其他更改仍然是 C++11 上的 UB,一个好的解决方案可能就是您提到的:使用std::complex::real()
替换std::real
。
但除此之外,如果您查看文件https://github.com/pytorch/pytorch/blob/master/c10/util/TypeCast.h ,您会在fetch_and_cast
中看到类似的内容:
#ifndef C10_HOST_DEVICE
AT_FORALL_COMPLEX_TYPES(FETCH_AND_CAST_COMPLEX_CASE)
#endif
此代码路径在 GPU 上已禁用。 您需要启用它并使其工作。
此外,我在fetch_and_cast
和cast_and_store
中没有看到complex<float>
和complex<double>
之间的任何转换。 您可能还需要为此添加转换。 确保对所有 dtype 的这些功能的覆盖范围进行彻底测试。
抄送: @ezyang和@bddppq
还有@dylanbespalko ,如果您在 PR 中对TypeCast.h
进行任何更改,请抄送我。
好的,我在 ARM 上用torch.real()
和torch.imag()
修复了一些小问题,所以我会在处理时修复TypeCast.h
和其他一些问题。 我会在 PR 上抄送你们。
评论驱动: @smessmer正在将我们转移到 C++14,此时它不会是 UB。 由于这即将到来,如果 UB 没有引起真正的问题,我不会太担心。
@ezyang :很高兴知道。 大多数像 Eigen 这样的第三方东西仍然非常自由地调用std::real()
。
对于非复数,scalar_t 和thrust_t 是同一类型。 也许我可以用对非复数更友好的名称替换变量名thrust_t,例如scalar_t_c?
我不太确定,但scalar_t_c
似乎比thrust_t
不太清楚( c
到底是什么意思?)这里有问题的类型似乎非常具体,所以最好使用直接谈论意图的名称。
好的,我会坚持使用thrust_t
。 如果有人深入研究ztype_cuda<>()
,他们应该立即发现scalar_t
thrust_t
。
大家好! 看起来在向 pytorch 添加复杂支持方面取得了良好进展! 感谢@dylanbespalko采取主动并添加了 CUDA 支持! 从高层次上看,我很想知道复杂支持的当前进展是什么? 我最感兴趣的是让 CUDA 支持添加和相乘复杂张量(二进制操作)的粗略时间表。 谢谢!
嗨@sunilkpai ,
我有一个开放的 PR,应该支持 CUDA 上的二元和一元操作:#30295。
另一个问题是反向传播。 我认为复数abs()
的导数的定义与实数不同。 不知道该怎么做,但衍生品在tools/autograd/derivatives.yaml
中定义
我认为对于复数/dz abs(z) = z/abs(z)
。 这也可以用于实数,但可能会比sgn(z)
慢
@dylanbespalko也许我的报告https://arxiv.org/pdf/1701.00392.pdf中的表 4.1、4.2 和 4.3 可以帮助您定义衍生品。
对于复数导数(wirtinger 演算),有两种选择。
计算 z 或 z 共轭的导数。
我个人更喜欢 wrt z 共轭的导数。
矩阵运算感觉更自然,梯度更新不需要共轭。
它们的定义是:
z = x + jy
的衍生 wrt z
$ : dJ/dz = dJ/dx -j dJ/dy
z.conj
z = x + jy
: dJ/dz.conj = dJ/dx + j dJ/dy
根据您的评论,我的假设是,您现在计算z
的导数。
在这种情况下,导数是d abs(z) / d z = z.conj / abs(z)
。 当您采用其他定义时,您可以遵循@Randl 的建议。
让我知道我是否应该解释更多。 对于复杂的导数,我也有一些 numpy 实现。
另一个有用的操作(特别是对于需要复数支持的物理空间中的项目)是exp()
操作符的处理程序。 在张量流中,我们有tf.exp(x + iy) = tf.exp(x) * (tf.cos(y) + 1j * tf.sin(y))
。 这在pytorch中也很容易实现吗?
@sunilkpai , @boeddeker , @兰德尔,
感谢关于复杂衍生品的报告。 我将尝试遵循这一点,我将在下周继续讨论。 我想我会在这里添加一些链接并描述项目状态。
复数的状态是非官方支持的,必须通过 PyTorch 扩展添加:
每个扩展包含两件事:
.cpp
。test/
文件夹,其中包含非常简化的 pytorch 测试脚本版本。为什么我不能将复杂的张量打印到控制台?
tensor.py
的内容以绕过打印格式。当前项目状态:
</li>
<li>Complex number specific code is under 'aten/src/ATen/native/cpu/zmath.h
下实现</li>
<li>Complex number specific code is under 'aten/src/ATen/native/cuda/zmath.cuh
下实现thrust::complex<T>
数据类型,它们包括优化的内核。目前的发展:
--
供参考。 关于复杂的衍生品,我们在 Julia 中进行了长时间的讨论,现在它的实现在ChainRules (另见:http://www.juliadiff.org/ChainRules.jl/dev/api.html#ChainRulesCore.Wirtinger)和Zygote . 一般人只需要
\partial L/\partial adjoint(z)
作为梯度(根据定义它是下降最快的方向),但是导数与\partial L/\partial z
不同,如果我们想要完全支持复数AD,应该添加一个额外的接口. 详细规则可以查看ChainRules
或Zygote/lib
中实现的内容(由于只有通用规则,因此大多数运算符没有单独的复数规则,事物的后向传递像matmul
是用通用定义编写的,例如adjoint(A) * B
)
为什么我不能将复杂的张量打印到控制台?
Tensor python 对象有一些漂亮的打印格式,可以调用一些不受支持的函数。
您可以修改 tensor.py 的内容以绕过打印格式。
或者,您可以简单地将 Pytorch 张量转换为 Numpy 数组,然后打印。
我想我首先修复了https://github.com/Roger-luo/pytorch-complex中的至少部分打印以进行调试等,不确定这是否会有所帮助,因为主人过去发生了很大变化年。 有帮助的可以采纳,我不做这个了。
@dylanbespalko虽然我已经开始学习,但我对 pytorch 的内部结构相对缺乏经验! 我可以想象尝试这种改变,但根据我在aten/src/ATen/cpu/vec256/*
中看到的内容,我不确定是否有必要考虑到 std::exp(std::complex) 的默认行为正是我提到的在我之前的评论中:请参阅https://en.cppreference.com/w/cpp/numeric/complex/exp的注释。 我也不确定这如何转化为在 CUDA 中实现这些操作(目前似乎仅限于 real、imag、conj 和 angle?)。
@sunilkpai ,
我使用提供的公式添加了对exp()
的 AVX 支持。
我还注意到由于 PyTorch 最近的一些变化,有些东西被破坏了。 我已在 #30871 中修复了这些问题。
@dylanbespalko
从 TH 移植到 ATen 是否有时间表?
考虑到我不熟悉 pytorch 的内部工作原理,我有什么办法可以做出贡献吗?
我在 arxiv 上找到了一个复杂 svd 反向传播的公式,并且可以实现它,如果你告诉我在哪里
感谢您的工作!
@Jakob-Unfried
https://github.com/pytorch/pytorch/wiki/TH-to-ATen-porting-guide
TH 内核是用 C 实现的,由于所有固有的引用计数问题,人们对在那里添加复杂的支持几乎没有兴趣。 您可以在每个内核注册的aten/src/ATen/native/native_functions.yaml
中跟踪进度:
搜索legacy::cpu::_th
并将该数字除以 3 以获得旧 TH 内核的数量。
搜索legacy::cpu::_thnn
并将该数字除以 3 以获得旧 TH 神经网络内核的数量。
每个内核通常以 3 种不同的方式注册:
1. 正则内核 y = add(a, b)
2. 就地内核 a = add_(a, b)
3. 输出内核add_out(a, b, out=y)
实际的实现总是在输出内核中,另外两个调用该函数。
nn 内核往往更容易移植,因为它们的依赖内核较少。 因此,如果您可以按照与实现方式相反的顺序移植内核,那么您将完成更少的整体工作。
检查移植跟踪问题https://github.com/pytorch/pytorch/issues/24507 ,也抄送@VitalyFedyunin
这是 #32437 中要求的复数支持的状态更新。 我今天回来从事与 CPU 相关的支持工作。
angle()
、 real()
、 imag()
、 conj()
都已实施。abs()
将需要一个单独的复数实现。 (参见上面@boeddeker和@Randl的注释)复数支持目前是在树外实现的。 这是什么意思:
关于这个问题的更多更新: https ://github.com/pytorch/pytorch/issues/33152
这可能值得或可能不值得一个单独的问题,但我希望看到并认为在文档中包含一些解释“pytorch 现在如何处理复数”的内容实际上更重要。 aka 可以做加法、乘法、某种规范,不能有复杂的权重等等。所有这些都可以通过几行文档来总结,解释什么是高级预期的当前行为。
这可能值得或可能不值得一个单独的问题,但我希望看到并认为在文档中包含一些解释“pytorch 现在如何处理复数”的内容实际上更重要。 aka 可以做加法、乘法、某种规范,不能有复杂的权重等等。所有这些都可以通过几行文档来总结,解释什么是高级预期的当前行为。
嗨@redwrasse感谢您的反馈! 我们目前有一个关于复数的注释,其中讨论了一些 Torch 基础知识和复杂张量支持的复杂函数
(其中大部分包含在 1.6 版本中) https://pytorch.org/docs/master/complex_numbers.html?highlight=complex。 你能分享你感兴趣的其他功能吗? 很高兴更多地谈论我们当前的支持以及即将发布的版本的计划。
这可能值得或可能不值得一个单独的问题,但我希望看到并认为在文档中包含一些解释“pytorch 现在如何处理复数”的内容实际上更重要。 aka 可以做加法、乘法、某种规范,不能有复杂的权重等等。所有这些都可以通过几行文档来总结,解释什么是高级预期的当前行为。
嗨@redwrasse感谢您的反馈! 我们目前有一个关于复数的注释,其中讨论了一些 Torch 基础知识和复杂张量支持的复杂函数
(其中大部分包含在 1.6 版本中) https://pytorch.org/docs/master/complex_numbers.html?highlight=complex。 你能分享你感兴趣的其他功能吗? 很高兴更多地谈论我们当前的支持以及即将发布的版本的计划。
谢谢@anjali411 ,很高兴看到这个文档,我以前不知道。 我认为需要的是前面和中心几行“对复杂神经网络的支持的当前状态”,但让我来看看它......
对复杂的 autograd 感兴趣的人,您可能对https://github.com/pytorch/pytorch/issues/41857感兴趣,它涉及 PyTorch 将遵循的约定(JAX 或 TF)。
最有用的评论
@sunilkpai , @boeddeker , @兰德尔,
感谢关于复杂衍生品的报告。 我将尝试遵循这一点,我将在下周继续讨论。 我想我会在这里添加一些链接并描述项目状态。
复数的状态是非官方支持的,必须通过 PyTorch 扩展添加:
每个扩展包含两件事:
.cpp
。test/
文件夹,其中包含非常简化的 pytorch 测试脚本版本。查看测试脚本以了解支持哪些内核(以及为什么不支持其他内核)。
为什么我不能将复杂的张量打印到控制台?
tensor.py
的内容以绕过打印格式。当前项目状态:
</li> <li>Complex number specific code is under 'aten/src/ATen/native/cpu/zmath.h
下实现</li> <li>Complex number specific code is under 'aten/src/ATen/native/cuda/zmath.cuh
下实现thrust::complex<T>
数据类型,它们包括优化的内核。目前的发展:
--