我正在用 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;});
}
uj5u.com熱心網友回復:
您不能從函式回傳引數包,因此makeActivationSequence如您所描述的那樣是不可能的。但是,您可以將mid和last直接傳遞給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>{});
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/489100.html
上一篇:如何將具有值作為陣列的哈希轉換為具有來自陣列的鍵和作為鍵陣列的值的哈希?
下一篇:模板類作為函式引數[C ]
