如何为Flux神经网络模型施加输出正约束以解决定义域错误?
解决Flux神经网络输出为负导致的DomainError问题
问题背景
观测数据满足$y_i\sim N(0,1/w(x_i))$,其中$x_i=i$($i=1,...,100$),真实函数为$w(x_i)=(x_i-50)^2$。用Flux神经网络$\tilde{w}(x_i)$恢复该函数时,因网络输出可能为负,导致正态分布的方差($1/\tilde{w}(x_i)$)不满足正的要求,触发DomainError。
解决方案
以下是三种可行的约束方法,确保$\tilde{w}(x_i)>0$:
方法1:给网络添加正激活函数
直接在输出层后添加激活函数,强制输出为正。常用的是指数激活(exp),或者带小偏移的ReLU:
修改模型定义:
# 用exp激活确保输出严格大于0 model = Chain(Dense(1 => 1), exp) # 备选:用ReLU加小偏移,避免输出为0 # model = Chain(Dense(1 => 1), x -> relu(x) + 1e-6)
损失函数可以保持原逻辑,也可以改成更高效的向量化写法:
# 向量化版本,替代循环提升效率 loss(model, x, y) = -sum(logpdf.(Normal.(0, 1 ./ model(x)), y))
方法2:在损失函数中做参数变换
不修改模型结构,而是在损失函数内对模型输出做变换,转化为正值:
修改损失函数:
loss(model, x, y) = let # 对模型输出取指数,强制w̃>0 w_hat = exp.(model(x)) -sum(logpdf.(Normal.(0, 1 ./ w_hat), y)) end # 备选:用平方加偏移,保证非负且不为0 # loss(model, x, y) = let # w_hat = model(x).^2 .+ 1e-6 # -sum(logpdf.(Normal.(0, 1 ./ w_hat), y)) # end
方法3:自定义带正约束的层
如果需要更好的封装性,可以自定义一个输出自带正约束的层:
struct PositiveDense dense::Dense end # 重载调用逻辑,输出自动取exp (p::PositiveDense)(x) = exp(p.dense(x)) # 构建模型 model = PositiveDense(Dense(1 => 1))
注意事项
- 指数激活要留意模型输出过大的情况,但本问题中真实$w(x)$范围在0到2500,exp变换不会触发数值溢出;
- 用ReLU加偏移时,
1e-6是为了避免输出为0,防止方差无穷大; - 向量化写法比循环更高效,适合处理大规模数据。
内容的提问来源于stack exchange,提问作者Ron Snow
相关产品推荐
相关产品推荐

