【问题标题】:How to initialise a variadic template that relies on previous entries?如何初始化依赖于先前条目的可变参数模板?
【发布时间】:2019-10-08 19:16:03
【问题描述】:

我正在尝试实现神经网络。为此,我认为我可以对大多数超参数使用模板参数。它开始变得困难,我怀疑它可能无法实现我想要的。

我有一个基本的神经元类

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 .

我怎样才能做到这一点?

【问题讨论】:

    标签: c++ variadic-templates


    【解决方案1】:

    我现在想要一个 ... 键...这应该可以满足您的需求:

    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_ptrs(Layer 拥有它的Neurons),但是由于那时没有多态性,您可以自己存储 Neurons 并保留动态分配。

    See it live on Wandbox

    【讨论】:

      猜你喜欢
      • 2017-07-11
      • 2017-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-20
      • 2016-07-14
      • 2014-06-07
      • 1970-01-01
      相关资源
      最近更新 更多