11

我最近观看了一个视频,它激励我编写自己的神经网络系统,并且我希望网络中的节点数量可以调整。依赖于可变模板类型的类

起初,我在运行时通过解析一个数组节点来实现这一点,但我想知道是否可以在编译时做到这一点。这是我希望完成的事情的一个例子。

template<int FirstNodes, int SecondNodes, int... OtherNodes> 
class Net 
{ 
    tuple<Eigen::Matrix<float, FirstNodes, SecondNodes>, ...> m_weights; 
    // More matricies with the values from the OtherNodes 
}; 

至于更详细的例子,Net<784, 16, 16, 10> n; n.m_weight的类型应该是

tuple<Eigen::Matrix<float, 784, 16>, 
    Eigen::Matrix<float, 16, 16>, 
    Eigen::Matrix<float, 16, 10>> 

据我了解C++和constexpr,这应该是可能的。

我要补充一点,我能够做的

template<int FirstNodes, int SecondNodes, int... OtherNodes> 
class Net 
{ 
public: 
    Net() 
    { 
     auto nodes = {FirstNodes, SecondNodes, OtherNodes...}; 

     auto i = nodes.begin(); 
     do 
     { 
      // Eigen::Matrix<float, Dynamic, Dynamic> 
      Eigen::MatrixXf m(*(i++), *i); 
     } while (i+1 != nodes.end()); 
    } 
}; 

但后来我只是再次使用动态matricies,这是不是我所希望的。

任何意见或工作示例将不胜感激。

+0

的方式,行'M(*(我+ +),* I)'调用未定义的行为达到C++ 14,在C++ 17 –

+0

*无耻的自我推广*您可能会发现[不明这个回购](https://github.com/liammcinroy/MetaTemplateNeuralNet)有趣 –

回答

6

你需要某种类型的转换,鉴于N整数的列表返回tupleN - 1矩阵。下面是一个C++ 17的解决方案:

template <int A, int B, int... Is> 
auto make_matrix_tuple() 
{ 
    if constexpr(sizeof...(Is) == 0) 
    { 
     return std::tuple<Eigen::Matrix<float, A, B>>{}; 
    } 
    else 
    { 
     return std::tuple_cat(make_matrix_tuple<A, B>(), 
          make_matrix_tuple<B, Is...>()); 
    } 
} 

live example on wandbox


在C++ 11,你可以递归实现这种转变:

template <int... Is> 
struct matrix_tuple_helper; 

template <int A, int B, int... Rest> 
struct matrix_tuple_helper<A, B, Rest...> 
{ 
    using curr_matrix = Eigen::Matrix<float, A, B>; 
    using type = 
     decltype(
      std::tuple_cat(
       std::tuple<curr_matrix>{}, 
       typename matrix_tuple_helper<B, Rest...>::type{} 
      ) 
     ); 
}; 

template <int A, int B> 
struct matrix_tuple_helper<A, B> 
{ 
    using curr_matrix = Eigen::Matrix<float, A, B>; 
    using type = std::tuple<curr_matrix>; 
}; 

template <int... Is> 
using matrix_tuple = typename matrix_tuple_helper<Is...>::type; 

C++ 14方法:

struct matrix_tuple_maker 
{ 
    template <int A, int B, int C, int... Is> 
    static auto get() 
    { 
     return std::tuple_cat(get<A, B>(), get<B, C, Is...>()); 
    } 

    template <int A, int B> 
    static auto get() 
    { 
     return std::tuple<Eigen::Matrix<float, A, B>>{}; 
    } 
}; 

static_assert(std::is_same_v< 
    decltype(matrix_tuple_maker::get<784, 16, 16, 10>()), 
    std::tuple<Eigen::Matrix<float, 784, 16>, 
       Eigen::Matrix<float, 16, 16>, 
       Eigen::Matrix<float, 16, 10>> 
    >); 
1

在我看来,你需要整数的两个列表,出来的1

阶段如果你定义一个简单的模板整数容器(在C++ 14可以使用std::integer_sequence

template <int...> 
struct iList 
{ }; 

可以定义基类如下(对不起:使用Eigen::Matrixfoo代替)

template <typename, typename, typename = std::tuple<>> 
struct NetBase; 

// avoid the first couple 
template <int ... Is, int J0, int ... Js> 
struct NetBase<iList<0, Is...>, iList<J0, Js...>, std::tuple<>> 
    : NetBase<iList<Is...>, iList<Js...>, std::tuple<>> 
{ }; 

// intermediate case 
template <int I0, int ... Is, int J0, int ... Js, typename ... Ts> 
struct NetBase<iList<I0, Is...>, iList<J0, Js...>, std::tuple<Ts...>> 
    : NetBase<iList<Is...>, iList<Js...>, 
      std::tuple<Ts..., foo<float, I0, J0>>> 
{ }; 

// avoid the last couple and terminate 
template <int I0, typename ... Ts> 
struct NetBase<iList<I0>, iList<0>, std::tuple<Ts...>> 
{ using type = std::tuple<Ts...>; }; 

和0简直成了(观察出相夫妇整数列表中的)

template <int F, int S, int... Os> 
struct Net : NetBase<iList<0, F, S, Os...>, iList<F, S, Os..., 0>> 
{ }; 

下面是一个完整的编译例子

#include <tuple> 

template <int...> 
struct iList 
{ }; 

template <typename, int, int> 
struct foo 
{ }; 

template <typename, typename, typename = std::tuple<>> 
struct NetBase; 

// avoid the first couple 
template <int ... Is, int J0, int ... Js> 
struct NetBase<iList<0, Is...>, iList<J0, Js...>, std::tuple<>> 
    : NetBase<iList<Is...>, iList<Js...>, std::tuple<>> 
{ }; 

// intermediate case 
template <int I0, int ... Is, int J0, int ... Js, typename ... Ts> 
struct NetBase<iList<I0, Is...>, iList<J0, Js...>, std::tuple<Ts...>> 
    : NetBase<iList<Is...>, iList<Js...>, 
      std::tuple<Ts..., foo<float, I0, J0>>> 
{ }; 

// avoid the last couple and terminate 
template <int I0, typename ... Ts> 
struct NetBase<iList<I0>, iList<0>, std::tuple<Ts...>> 
{ using type = std::tuple<Ts...>; }; 

template <int F, int S, int... Os> 
struct Net : NetBase<iList<0, F, S, Os...>, iList<F, S, Os..., 0>> 
{ }; 

int main() 
{ 
    static_assert(std::is_same< 
     typename Net<784, 16, 16, 10>::type, 
     std::tuple<foo<float, 784, 16>, foo<float, 16, 16>, 
       foo<float, 16, 10>>>{}, "!"); 
} 
0

这是另一个C++ 14解决方案。我认为它值得张贴,因为它是非递归和可读的。

#include <tuple> 
#include <utility> 

template<class, int, int> struct Matrix {}; 

template<int... matsizes, std::size_t... matinds> 
constexpr auto make_net(
    std::integer_sequence<int, matsizes...>, 
    std::index_sequence<matinds...> 
) { 
    constexpr int sizes[] = {matsizes...}; 
    return std::tuple< Matrix<float, sizes[matinds], sizes[1+matinds]>... >{}; 
} 

template<int... matsizes> 
constexpr auto make_net(
    std::integer_sequence<int, matsizes...> sizes 
) { 
    static_assert(sizes.size() >= 2, ""); 
    constexpr auto number_of_mats = sizes.size() - 1; 
    return make_net(sizes, std::make_index_sequence<number_of_mats>{}); 
} 

int main() { 
    auto net = make_net(std::integer_sequence<int, 784, 16, 16, 10>{}); 
    using Net = decltype(net); 

    static_assert(
    std::is_same< 
     std::tuple< 
     Matrix<float, 784, 16>, 
     Matrix<float, 16, 16>, 
     Matrix<float, 16, 10> 
     >, 
     Net 
    >{}, "" 
); 

    return 0; 
}