簡體   English   中英

對非線性 object function 使用 `Optim` package 太慢

[英]Using `Optim` package for a non-linear object function is too slow

我想提高優化 function 的性能。

我使用Optim package 來優化非線性 function 和 BFGS 算法。

我放了一個 object function (非線性由於link_approx ,它會生成三次樣條)

並將其梯度向量轉化為optimize

然而,結果證明它比 R 編程補碼慢 4 倍。

我將容差(收斂標准)設置為與 R 相同。

(如果需要我可以附上代碼)

Z::Matrix{Float64}; X::Matrix{Float64}; Y::Matrix{Float64}; B::Matrix{Float64}; G::Matrix{Float64}


using Splines2
using LinearAlgebra
using Optim

function link_approx(x_v::Array)
    local est; local der
    est = bs(x_v, knots = knots, order = 4)[:, 3:end-3] * fit[:theta]
    der = bs(x_v, knots = knots, order = 3)[:, 3:end-3] * coef
    return Dict{Symbol, Array{Float64}}(:est => est, :der => der)
end

@time for j in 1:r

# for update G

    function grad!(storage, gamma)
      local linkfit
      linkfit = link_approx(Y*gamma)

      output = (transpose(Y) * ((X*B[:,j] + linkfit[:est] - Z[:,j]) .* linkfit[:der])./n - U0[:,j] - U2[:,j] - U3[:,j] 
+ rho*(pennum * gamma - C0[:,j] - C2[:,j] - C3[:,j]))

      for i in 1:size(Y)[2]
        storage[i] = output[i]
      end
    end
  
    function obj(gamma)
      return norm(Z[:,j] - X*B[:,j] - link_approx(Y*gamma)[:est], 2)^2/(2*n) - transpose(U0[:,j] + U2[:,j] + U3[:,j])*(gamma) 
+ rho*(norm(gamma - C0[:,j], 2)^2 + norm(gamma - C2[:,j], 2)^2*lowrank_G + norm(gamma - C3[:,j], 2)^2*sparse_G)/2
    end
  
    temp = optimize(obj, grad!, G[:,j], BFGS(), Optim.Options(iterations = Int(5e1)))
    G[:,j] = Optim.minimizer(temp)

end

2.419329 seconds (32.44 M allocations: 824.036 MiB, 3.52% gc time, 3.57% compilation time)

(梯度由B樣條曲線的導數公式計算)

我認為它的梯度向量或重復編譯存在問題。

我不知道如何在高維情況下對梯度的存儲進行評估。

由於它的維度超過 100,所以我使用了 for 循環。

這是我的代碼的一個工作示例。

using Splines2
using LinearAlgebra
using Optim
using Distributions



## basic settings (its codes are irrelevant to questions)
n=200
p=100
q=100
r=10

X = randn(n, p)
Y = randn(n, q)
B = ones(p, r) ./ p
G = ones(q, r) ./ q
Z = (X * B + Y * G)
Z_c = Z .- mean(Z, dims = 1)
Z = Z_c ./ mapslices(std, Z_c, dims = 1)
theta = convert(Array{Float64}, LinRange(-3.8, 4, 5))
knots = convert(Array{Float64}, LinRange(-7.2, 7.5, 9))
coef = (3*diff(vcat(0, theta, 0)) ./ (knots[4:end] - knots[1:end-3]))[2:end-1]

function link_approx(x_v::Array)
    local est; local der
    est = bs(x_v, knots = knots, order = 4)[:, 3:end-3] * theta
    der = bs(x_v, knots = knots, order = 3)[:, 3:end-3] * coef
    return Dict{Symbol, Array{Float64}}(:est => est, :der => der)
end


## Why it takes so long time?

@time for j in 1:r
# for update G
    function grad!(storage, gamma)
      local linkfit
      linkfit = link_approx(Y*gamma)

      output = transpose(Y) * ((X*B[:,j] + linkfit[:est] - Z[:,j]) .* linkfit[:der])./n 

      for i in 1:size(Y)[2]
        storage[i] = output[i]
      end
    end
  
    function obj(gamma)
      return norm(Z[:,j] - X* B[:,j] - link_approx(Y*gamma)[:est], 2)^2/(2*n)
    end
  
    temp = optimize(obj, grad!, G[:,j], BFGS(), Optim.Options(iterations = Int(5e1)))
    G[:,j] = Optim.minimizer(temp)

end

這些代碼在我的筆記本電腦上花費了大約 5 秒(設置第 9 代。英特爾 CPU i7-9750H,16GB 內存)。

我想grad! 在我的代碼中有問題。

我應該使用一個 for 循環來填充它的存儲空間,以避免我的知識錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM