Skip to content

Commit 78ab6eb

Browse files
author
LuizFCDuarte
committed
🚧 First Lasso and ridge lambda selection
1 parent d2dc60b commit 78ab6eb

File tree

1 file changed

+111
-5
lines changed

1 file changed

+111
-5
lines changed

src/models/sarima.jl

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -848,18 +848,21 @@ function objectiveFunctionDefinition!(
848848
auxVariables[i] >= -parametersVectorExtended[i]
849849
end
850850
)
851-
λ = 1 / sqrt(T)
852-
@objective(jumpModel, Min, sum(jumpModel[] .^ 2) + λ * sum(auxVariables))
851+
# Make lambda a model parameter
852+
@variable(jumpModel, λ in Parameter(1.0))
853+
set_parameter_value(jumpModel[], 1 / sqrt(T))
854+
@objective(jumpModel, Min, sum(jumpModel[] .^ 2) + jumpModel[] * sum(auxVariables))
853855
end
854856
elseif objectiveFunction == "ridge"
855857
if length(parametersVectorExtended) == 0
856858
@objective(jumpModel, Min, sum(jumpModel[] .^ 2))
857859
else
858-
λ = 1 / sqrt(T)
860+
@variable(jumpModel, λ in Parameter(1.0))
861+
set_parameter_value(jumpModel[], 1 / sqrt(T))
859862
@objective(
860863
jumpModel,
861864
Min,
862-
sum(jumpModel[] .^ 2) + λ * sum(parametersVectorExtended .^ 2)
865+
sum(jumpModel[] .^ 2) + jumpModel[] * sum(parametersVectorExtended .^ 2)
863866
)
864867
end
865868
elseif objectiveFunction == "ml"
@@ -893,7 +896,43 @@ Optimizes the SARIMA model using the specified objective function.
893896
function optimizeModel!(jumpModel::Model, model::SARIMAModel, objectiveFunction::String)
894897
JuMP.optimize!(jumpModel)
895898

896-
if objectiveFunction == "bilevel"
899+
if objectiveFunction == "lasso" || objectiveFunction == "ridge"
900+
lambdaPath = getLambdaPath(values(model.y), getHyperparametersNumber(model))
901+
# Based on the lambda path, select the best lambda value according to the information
902+
# criteria BIC
903+
bestLambda = 1
904+
best_bic = Inf
905+
for i in eachindex(lambdaPath)
906+
# set the lambda value
907+
set_parameter_value(jumpModel[], lambdaPath[i])
908+
JuMP.optimize!(jumpModel)
909+
910+
# Calculate residuals variance for BIC computation
911+
model_variance = computeSARIMAModelVariance(
912+
jumpModel,
913+
objectiveFunction,
914+
getHyperparametersNumber(model),
915+
1,
916+
)
917+
918+
# Calculate BIC for this lambda
919+
T = length(jumpModel[])
920+
df = sum(value.(jumpModel[]) .!= 0) # Effective degrees of freedom
921+
current_bic = T * log(model_variance) + log(T) * df
922+
if current_bic < best_bic
923+
bestLambda = i
924+
best_bic = current_bic
925+
end
926+
aux_variables = all_variables(jumpModel)
927+
aux_solutions = value.(aux_variables)
928+
set_start_value.(aux_variables, aux_solutions)
929+
end
930+
@info "Best lambda value: $(lambdaPath[bestLambda])"
931+
@info "Best BIC value: $(best_bic)"
932+
set_parameter_value(jumpModel[], lambdaPath[bestLambda])
933+
JuMP.optimize!(jumpModel)
934+
935+
elseif objectiveFunction == "bilevel"
897936

898937
function optimizeMA(coefficients)
899938
maCoefficients = coefficients[1:model.q]
@@ -3818,3 +3857,70 @@ function gridSearch(
38183857
end
38193858
return bestModel
38203859
end
3860+
3861+
"""
3862+
getLambdaPath(yValues::Vector{T}, parametersVector::Vector,
3863+
nλ::Int=10, λminratio::Union{Nothing,Float64}=nothing) where T <: AbstractFloat
3864+
3865+
Generates a path of lambda values following the Lasso.jl approach.
3866+
3867+
# Arguments
3868+
- `yValues`: Vector of response values (differenced time series)
3869+
- `nparameters::Int`: Number of model parameters to be penalized
3870+
- `nλ::Int`: Number of lambda values to try in the path (default: 10)
3871+
- `λminratio::Union{Nothing,Float64}`: Ratio of minimum lambda to maximum lambda (default: 0.0001 for n > p, 0.01 for n ≤ p)
3872+
3873+
# Returns
3874+
- `Vector{T}`: The path of lambda values
3875+
"""
3876+
function getLambdaPath(yValues::Vector{T}, nparameters::Int;
3877+
::Int=10, λminratio::Union{Nothing,Float64}=nothing) where T <: AbstractFloat
3878+
# Number of observations
3879+
n = length(yValues)
3880+
3881+
# Number of parameters
3882+
p = nparameters
3883+
3884+
# Adjust λminratio based on number of observations vs parameters
3885+
if isnothing(λminratio)
3886+
λminratio = n > p ? 0.0001 : 0.01
3887+
end
3888+
3889+
# Calculate mean of series (null model prediction)
3890+
nullPrediction = mean(yValues)
3891+
3892+
# Calculate residuals from null model
3893+
residuals = yValues .- nullPrediction
3894+
3895+
# Create a simple design matrix for AR terms
3896+
# For time series, we'll use lagged values as predictors
3897+
X = zeros(T, n-p, p)
3898+
for i in 1:p
3899+
for j in 1:(n-p)
3900+
if j+p-i > 0 && j+p-i <= length(residuals)
3901+
X[j, i] = residuals[j+p-i]
3902+
end
3903+
end
3904+
end
3905+
3906+
# Calculate X'y (correlation between predictors and response)
3907+
Xy = zeros(T, p)
3908+
for i in 1:p
3909+
if p+1 <= length(residuals)
3910+
Xy[i] = dot(X[:, i], residuals[p+1:end])
3911+
end
3912+
end
3913+
3914+
# Compute λmax as the maximum absolute correlation
3915+
λmax = maximum(abs.(Xy)) / n
3916+
3917+
# Ensure λmax is not zero
3918+
if λmax < eps(T)
3919+
λmax = one(T)
3920+
end
3921+
3922+
# Generate lambda path following Lasso.jl approach
3923+
logλmax = log(λmax)
3924+
λpath = exp.(range(logλmax, stop=logλmax + log(λminratio), length=nλ))
3925+
return λpath
3926+
end

0 commit comments

Comments
 (0)