如何初始化依赖于先前条目的可变参数模板?
How to initialise a variadic template that relies on previous entries?
我正在尝试实现一个神经网络。为此,我认为我可以对大多数超参数使用模板参数。它开始变得困难,我怀疑可能无法实现我想要的。
我有一个基本的神经元类
class NeuronBase { /* virtual output method */ };
从那我有一个输入神经元
class InputNeuron : public NeuronBase { /* overrides output */ };
以及大多数层中使用的正确神经元类型
template<std::size_t NumInputs>
class Neuron : public NeuronBase
{
public:
Neuron(std::array<NeuronBase*,NumInputs> prevLayer)
:
m_inputLayer(prevLayer)
{}
/* override output method */
protected:
std::array<NeuronBase*,NumInputs> m_inputLayer;
};
这些包含在一个图层中:
template<std::size_t NumNeurons>
class Layer
{
public:
typedef std::array<NeuronBase*,NumNeurons> NeuronArray;
Layer(NeuronArray neurons)
:
m_neurons(neurons)
{}
NeuronArray& GetNeurons() const { return m_neurons; }
protected:
NeuronArray m_neurons;
};
这些层由网络封装:
template<std::size_t NumInputs, std::size_t NumOutputs, std::size_t... HiddenSizes>
class NeuralNetwork
{
public:
NeuralNetwork()
:
m_hiddenLayers(/* WTF do I do?? */)
{
}
protected:
std::array<InputNeuron,NumInputs> m_inputLayer;
Layer<NumOutputs> m_outputLayer;
std::tuple<Layer<HiddenSizes>...> m_hiddenLayers;
};
将像这样创建:
int main()
{
NeuralNetwork<126,7,252,63> nn;
return 0;
}
这导致一个具有 126 个输入、7 个输出和两个隐藏层的网络,第一个隐藏层 252 个,第二个隐藏层有 63 个神经元。
我省略了一堆你通常会在神经网络中看到的东西。
我遇到的问题是初始化网络中的层。m_hiddenLayers
需要用第一个元素引用输入层进行初始化,其余元素使用std::tuple
中的前一项进行初始化,并且它们也需要使用正确的类型创建,即Neuron
而不是NeuronBase
.
我怎样才能做到这一点?
我现在希望...键...这应该可以满足您的需求:
template<std::size_t NumInputs, std::size_t NumOutputs, std::size_t... HiddenSizes>
class NeuralNetwork
{
public:
NeuralNetwork()
: m_hiddenLayers{makeHiddenLayers()}
, m_outputLayer{makeOutputLayer()}
{
}
private:
// Creates an array of pointers to each neuron of the input layer
template <std::size_t LayerSize, std::size_t... LayerIdx>
auto makeLayerPtrs(
std::array<InputNeuron, LayerSize> &inputLayer,
std::index_sequence<LayerIdx...>
) {
return std::array<NeuronBase *, LayerSize>{&inputLayer[LayerIdx]...};
}
// Creates an array of pointers to each neuron of the input layer
template <std::size_t LayerSize>
auto makeLayerPtrs(std::array<InputNeuron, LayerSize> &inputLayer) {
return makeLayerPtrs(inputLayer, std::make_index_sequence<LayerSize>{});
}
// Creates an array of pointers to each neuron of the given layer
template <std::size_t LayerSize, std::size_t... LayerIdx>
auto makeLayerPtrs(Layer<LayerSize> &layer, std::index_sequence<LayerIdx...>) {
return std::array<NeuronBase *, LayerSize>{layer.GetNeurons()[LayerIdx].get()...};
}
// Creates an array of pointers to each neuron of the given layer
template <std::size_t LayerSize>
auto makeLayerPtrs(Layer<LayerSize> &layer) {
return makeLayerPtrs(layer, std::make_index_sequence<LayerSize>{});
}
// Creates an array of pointers to each neuron of the given layer
template <std::size_t LayerNumber>
auto makeLayerPtrs() {
if constexpr (LayerNumber == 0) {
return makeLayerPtrs(m_inputLayer);
} else {
return makeLayerPtrs(std::get<LayerNumber - 1>(m_hiddenLayers));
}
}
// Creates a Neuron with the given array of pointers to the previous layer
template <auto Dummy, std::size_t LayerSize>
auto makeNeuron(std::array<NeuronBase *, LayerSize> const &previousLayerPtrs) {
return std::make_unique<Neuron<LayerSize>>(previousLayerPtrs);
}
// Creates one hidden layer
template <std::size_t LayerNumber, std::size_t LayerSize, std::size_t... LayerIdx>
auto makeHiddenLayer(std::index_sequence<LayerIdx...>) {
// Implicit shift to 1-based indexing of hidden layers
auto const previousLayerPtrs = makeLayerPtrs<LayerNumber>();
return Layer<LayerSize>{{makeNeuron<LayerIdx>(previousLayerPtrs)...}};
}
// Creates all of the hidden layers
template <std::size_t... HiddenLayersIdx>
auto makeHiddenLayers(std::index_sequence<HiddenLayersIdx...>) {
return std::tuple{
makeHiddenLayer<HiddenLayersIdx, HiddenSizes>(
std::make_index_sequence<HiddenSizes>{}
)...
};
}
// Creates all of the hidden layers
auto makeHiddenLayers() {
return makeHiddenLayers(std::make_index_sequence<sizeof...(HiddenSizes)>{});
}
// Creates the output layer
template <std::size_t... LayerIdx>
auto makeOutputLayer(std::index_sequence<LayerIdx...>) {
auto const lastHiddenLayerPtrs = makeLayerPtrs<sizeof...(HiddenSizes)>();
return Layer<NumOutputs>{{makeNeuron<LayerIdx>(lastHiddenLayerPtrs)...}};
}
// Creates the output layer
auto makeOutputLayer() {
return makeOutputLayer(std::make_index_sequence<NumOutputs>{});
}
protected:
// Caution : reordered members
std::array<InputNeuron,NumInputs> m_inputLayer;
std::tuple<Layer<HiddenSizes>...> m_hiddenLayers;
Layer<NumOutputs> m_outputLayer;
};
我依赖于(未经研究的(假设,即人们可以在部分构造的std::tuple
上调用std::get
。我希望我正确理解了你的问题,并且每个神经元都应该连接到前一层的所有神经元。
我已经在对我来说有意义的地方插入了std::unique_ptr
s(一个拥有其Neuron
的Layer
(,但由于此时没有多态性,您可以只存储Neuron
本身并节省动态分配。
在Wandbox上观看直播
相关文章:
- 在不传递参数数量且只有3个点的情况下,如何使用变差函数
- 如何使用可变参数模板强制转换每个变体类型
- 关于如何在具有单个参数的变体构造中选择替代方案?
- 调用参数排列不变函数 f(i++, i++)
- 参数归纳与标准::变体
- 模板化回调参数的逆变,如 C# 中的逆变
- 如何在没有参数包的情况下编写变差函数
- 通过具有嵌套类的工厂类获取多个变异类模板参数包
- 获取模板参数的成员变量值列表
- 保留短 lambda 用作函数的中间参数,使用 clang 格式保持不变
- 如何定义变体<x,y,z>提取模板参数的子类型
- 正确对齐内存模板,参数顺序不变
- 递归中不同参数类型的变元模板函数
- 通过函数指针传递给变差函数的参数会更改其值
- 提升预定义为带有参数的全局 lambda 的变体访问者
- 使用可变参数模板参数提升变体访问者
- boost ::变体 - 为什么模板参数比const字符串参数具有更高的优先级
- 将变参数包中的值加载到临时数组中
- 使用额外参数提升变体访客
- 从变长参数列表中提取std::string