我想实现各种数学函数,例如Sigmoid(逻辑),ReLU等。我想一起实现它们的导数。
鉴于这些数学函数都是非常简单的一线函数,我想将它们内联。此外,我希望能够按名称将功能组织为单个实体,并能够像这样调用常规激活或派生功能:
Sigmoid.Activate(0.5)
Sigmoid.Derivate(0.5)
最后,我希望能够将它们存储为对象的字段,因此,如果要为神经网络合并神经元,我将能够拥有一个名为activation
的字段,并且可以这样称呼它:
this->activation.Derivate(this->input);
在寻找合适的方法来实现这一目标时,我感到矛盾。我读到,一般而言,静态内联函数是一种代码味道。但是,为每个使用函数的对象创建单独的实例似乎是多余的,而且浪费内存。实际上,完全拥有任何实例似乎是浪费的,因为我本质上只是想将两个函数归为一个名称。我已经考虑过为每个函数使用名称空间,但是那样我将无法指定对象应使用哪个函数。
静态类似乎是唯一有效的解决方案。有没有更优雅的方法可以做到这一点?
答案 0 :(得分:2)
这可能不符合您的所有条件,但是,我认为这很有用。类并不是所有事物的解决方案,所以为什么不将类的activation
字段作为返回(值,派生)对的函数指针类型:
// Example library
using value_derivative = std::pair<double,double>;
enum ReturnType {
VALUE,
DERIVATIVE
};
using ActivationFunction = std::function<value_derivative(double)>;
value_derivative sigmoid(double z) {
double ez = exp(-z);
double val = 1.0/(1+ez);
return std::make_pair(val, val*(1.0-val));
}
// Example usage
ActivationFunction activation = sigmoid;
auto act = activation(1);
double value = std::get<VALUE>(act);
double derivative = std::get<DERIVATIVE>(act);
std::tie(value,derivative) = activation(2);
请注意,这样做的附带好处是,当激活函数具有一个很好的微分方程时,在计算值时使用中间项来计算导数通常会更简单。
答案 1 :(得分:2)
如果需要能够在运行时上为Neuron
的字段分配一对函数,那么您将至少需要一个多态对象的实例。 std::function
对此非常方便,但是您可以使用抽象基类实现类似的功能。您可以使用简单的结构将两个std::function
组合在一起。
struct Activation {
std::function<double(double)> activate;
std::function<double(double)> derivative;
};
然后,您可以为Sigmoid拥有此结构的单个实例:
const Activation& sigmoid() {
static const Activation activation {
[](double z){ return 1.0/(1.0+exp(-z)); },
[](double z){ auto a=exp(z); auto b=1.0+a; return a/(b*b); }
};
return activation;
}
然后,您可以在Neuron的字段中存储对此实例的引用:
class Neuron {
const Activation& activation;
public:
Neuron(const Activation& activation) : activation(activation) {}
void doSomething() {
std::cout << activation.derivative(0.5) << "\n";
}
};
int main() {
Neuron n(sigmoid());
n.doSomething();
}
答案 2 :(得分:0)
使用函子可以将这些功能存储为对象。这些是()运算符已重载的类,因此您可以像调用函数一样调用该类:
class my_functor {
return_type operator() (param_type param) { /* .. */ }
}
这样做的一个优点是您还可以在类内部存储状态。您可以将其称为:
std::cout << this->instance_of_my_functor(42);
由于功能存储状态的功能,所以功能优于函数指针,您可能会发现这是有益的。也可以将继承与它们一起使用,并根据需要覆盖运算符。