将 2 个参数 [x, y] 扩展为可变大小参数包 [x, ... x, y]

瓦苏

我正在用 C++ 编写一个自定义的多层感知器 (MLP) 实现。除了最后一层之外,所有层都共享一个激活函数foo,最后一层有一个单独的激活函数bar我正在尝试编写我的代码,以便它能够处理具有不同层数的这种类型的模型,就像在下面复制的这个 Godbolt 链接中一样。不幸的是,正如所写,我不得不对激活函数的参数包进行硬编码,因此链接中的代码只能编译为N = 5.

有没有办法从两个激活函数创建一个自定义参数包,它能够“左扩展”第一个参数,这样我就可以编译上面的代码(在适当更新对computeIndexedLayersin的调用之后computeMlp?具体来说,我我正在考虑一些可以产生参数包的实用程序,例如:

template <size_t N, typename ActivationMid, typename ActivationLast>
struct makeActivationSequence {}; // What goes here?

makeActivationSequence<0>(foo, bar) -> []
makeActivationSequence<1>(foo, bar) -> [bar]
makeActivationSequence<2>(foo, bar) -> [foo, bar]
makeActivationSequence<3>(foo, bar) -> [foo, foo, bar]
makeActivationSequence<4>(foo, bar) -> [foo, foo, foo, bar]
...

查看std::index_sequence 的详细信息,我相信类似的东西可能在这里起作用,但我不清楚如何修改该方法以使用两种不同的类型。

另请注意,由于某些工具链问题,我在这里特别限于 C++14,因此利用例如的解决方案if constexpr(如链接的 std::index_sequence 详细信息中)将不起作用。

来自上述 Godbolt 链接的代码,为完整起见,转载如下:

#include <cstddef>
#include <utility>
#include <cstdio>

template <size_t LayerIndex, typename Activation>
 void computeIndexedLayer(
    const Activation& activation) {
        printf("Doing work for layer %zu, activated output %zu\n", LayerIndex, activation(LayerIndex));
    }

template <
    std::size_t... index,
    typename... Activation>
    void computeIndexedLayers(
    std::index_sequence<index...>, // has to come before Activation..., otherwise it'll get eaten
    Activation&&... activation) {
  (void)std::initializer_list<int>{
      (computeIndexedLayer<index + 1>(
           std::forward<Activation>(activation)),
       0)...};
}

template <size_t N, typename ActivationMid, typename ActivationLast>
void computeMlp(ActivationMid&& mid, ActivationLast&& last) {
  computeIndexedLayers(std::make_index_sequence<N>(),
    std::forward<ActivationMid>(mid),
    std::forward<ActivationMid>(mid),
    std::forward<ActivationMid>(mid),
    std::forward<ActivationMid>(mid),
    std::forward<ActivationLast>(last)
    );
}

int main() {
    computeMlp<5>([](const auto& x){ return x + 1;}, [](const auto& x){ return x * 1000;});

    // Doesn't compile with any other choice of N due to mismatched pack lengths
    // computeMlp<4>([](const auto& x){ return x + 1;}, [](const auto& x){ return x * 1000;});
}
YurkoFlisk

您不能从函数返回参数包,因此makeActivationSequence如您所描述的那样是不可能的。但是,您可以将midlast直接传递给computeIndexedLayers,并利用包展开将它们分别与midIndex模板参数包和lastIndex模板参数配对(在这种情况下,正好有一个lastIndex,因此它不是模板参数包,但不难更改/如果需要,可以概括)从两个相应的std::index_sequence参数推导出来。像这样:

#include <cstddef>
#include <utility>
#include <cstdio>

template <size_t LayerIndex, typename Activation>
void computeIndexedLayer(Activation&& activation) {
    printf("Doing work for layer %zu, activated output %zu\n", LayerIndex, activation(LayerIndex));
}

template <std::size_t... midIndex, std::size_t lastIndex,
    typename ActivationMid, typename ActivationLast>
void computeIndexedLayers(
    std::index_sequence<midIndex...> midIdxs,
    std::index_sequence<lastIndex> lastIdxs,
    ActivationMid&& mid, ActivationLast&& last) {
    (void)std::initializer_list<int>{
        (computeIndexedLayer<midIndex + 1>(mid), 0)...,
        (computeIndexedLayer<lastIndex>(std::forward<ActivationLast>(last)), 0)};
}

template <size_t N, typename ActivationMid, typename ActivationLast>
void computeMlp(ActivationMid&& mid, ActivationLast&& last) {
    computeIndexedLayers(std::make_index_sequence<N - 1>(), std::index_sequence<N>{},
        std::forward<ActivationMid>(mid), std::forward<ActivationLast>(last));
}

int main() {
    computeMlp<6>([](const auto& x){ return x + 1;}, [](const auto& x){ return x * 1000;});
}

螺栓链接

另请注意,在computeMlp两者中mid,andlast都被转发,但 at computeIndexedLayersonlylast是。这样做是为了避免潜在的重复从 移动,如果包含某些状态并且不是可移动的类型mid,这可能会导致麻烦。ActivationMid

C++17

由于 C++17 支持折叠表达式,因此可以替换非常丑陋std::initializer_list的 hack :computeIndexedLayers

template <std::size_t... midIndex, std::size_t lastIndex,
    typename ActivationMid, typename ActivationLast>
void computeIndexedLayers(
    std::index_sequence<midIndex...> midIdxs,
    std::index_sequence<lastIndex> lastIdxs,
    ActivationMid&& mid, ActivationLast&& last) {
    (computeIndexedLayer<midIndex + 1>(mid), ...);
    computeIndexedLayer<lastIndex>(std::forward<ActivationLast>(last));
}

C++20

C++20 中的模板化 lambda 让我们完全摆脱computeIndexedLayers并推导出 lambda 的模板参数和参数包,定义并立即调用computeMlp

template <size_t N, typename ActivationMid, typename ActivationLast>
void computeMlp(ActivationMid&& mid, ActivationLast&& last) {
    [&]<std::size_t... midIndex, std::size_t lastIndex>(
        std::index_sequence<midIndex...> midIdxs,
        std::index_sequence<lastIndex> lastIdxs){
            (computeIndexedLayer<midIndex + 1>(mid), ...);
            computeIndexedLayer<lastIndex>(std::forward<ActivationLast>(last));
        }(std::make_index_sequence<N - 1>(), std::index_sequence<N>{});
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

glutkeyboardfunc x,y参数

函数缺少2个必需的位置参数:“ x”和“ y”

Spring登录x,y参数

如何在方法 LinearRegression().fit(X,Y) 的第一个参数“X”中使用 2 个参数?

将 `c(x, y)` 作为函数参数转换为 R 中的 `c("x", "y")` (rlang/tidyevaluation)

如何将点A(x,y2)处的点A(x,y)的像素设置为1?

将R中具有X1,Y1,X2,Y2的数据帧重塑为X,Y1,Y2

Sklearn错误:predict(x,y)接受2个位置参数,但给出了3个

〜x +〜y ==〜(x + y)始终为假?

类型错误:overlaps() 缺少 3 个必需的位置参数:“y1”、“x2”和“y2”

将向量X设置为其他2个向量中最接近向量Y的值

将“如果x不等于y的x”重构为“ x或y”

将形状x的数组重塑为形状(x,y)的数组

if(x!= y)和if(x == y)

类型(x,y)与(x / = y)

X [Y]带有'on'参数的联接语法

Python函数参数([x [,y [,z]]])的含义

Argparse:如果存在“ x”,则必需的参数“ y”

x + = y和x = x + y之差

#定义func(x,y)x + y / x

将场景的(x,y)转换为屏幕的(x,y)

将[2,x,y]矩阵转换为[y + 1,2x],以用于图表

Google脚本:获取第n个长度的数组[[x],[y]]以返回x / y值数组[[x1,y1],[x2,y2]等)

ValueError:c参数具有n个元素,不适用于大小为0的x,大小为0的y

如何将二维元组 [x][y] 转换为 [y][x] 并为每个结果集调用可变参数函数

即使X扩展了Y,也无法将类“ X”转换为类“ Y”吗?

如何将P(x,y)与X,Y,P(X,Y)整合为数组

如何将 (x, y) numpy 数组重塑为 (x, y, 1) 数组?

返回一个包含 2 元组 (x,y) 的列表 x 和 y 是 numpy.ndarray