Turing.jl模型采样参数类型变更问题求助
问题与解决方案
错误现象
使用Turing.jl构建混合多项Logit模型时,运行代码出现两个错误:
MethodError: no method matching float(::Type{Any}):根源是采样语句Beta[:, r] ~ MvNormal(Theta * Z[:, r], Sigma)将Beta[:, r]的类型变更为Vector{Any},导致后续运算类型不兼容。- 手动转换
Beta[:,r]为Float64后,出现TypeError: in typeassert, expected Float64, got a value of type ForwardDiff.Dual{Nothing, Float64, 12}:破坏了Turing依赖的自动微分功能,因为ForwardDiff需要保留Dual类型计算梯度。
错误原因
- 预先用
zeros(K, R)初始化Beta为Float64类型,当对Beta[:,r]执行采样时,Turing会注入自动微分所需的ForwardDiff.Dual类型值,导致数组元素类型混杂,最终被提升为Any,引发类型错误。 - 强制转换
Beta[:,r]为Float64会丢弃Dual类型,导致自动微分无法进行,触发类型断言错误。
修正后的完整代码
using LinearAlgebra, Distributions, StatsBase using Turing, FillArrays, DynamicHMC, LabelledArrays using NNlib, GLM using CSV, DataFrames function generate_hmnl_data(R::Int=100, S::Int=30, C::Int=3, Theta::Array{Float64, 2}=ones(2, 4), Sigma::Array{Float64, 2}=Matrix(Diagonal(fill(0.1, 4)))) K = size(Theta, 2) G = size(Theta, 1) Y = Array{Int64}(undef, R, S) X = randn(R, S, C, K) Z = Array{Float64}(undef, G, R) Z[1, :] .= 1 if G > 1 # 修复维度匹配问题 Z[2:G, :] = reshape(randn(R * (G-1)), G-1, R) end Beta = Array{Float64}(undef, K, R) for r in 1:R Beta[:, r] = rand(MvNormal(Theta' * Z[:, r], Sigma)) for s in 1:S Y[r, s] = sample(1:C, Weights(exp.(X[r, s, :, :] * Beta[:, r]))) end end return (R=R, S=S, C=C, K=K, G=G, Y=Y, X=X, Z=Z, beta_true=Beta, Theta_true=Theta, Sigma_true=Sigma) end d1 = generate_hmnl_data() @model function hmnl(G::Int, Y::Matrix{Int64}, X::Array{Float64}, Z::Matrix{Float64}) R, S, C, K = size(X) # 用filldist简化Theta采样,保证类型稳定 Theta ~ filldist(Normal(0, 10), K, G) Sigma ~ InverseWishart(K, diagm(ones(K))) # 基于Theta的类型初始化Beta,兼容自动微分的Dual类型 Beta = Matrix{eltype(Theta)}(undef, K, R) for r in 1:R Beta[:, r] ~ MvNormal(Theta * Z[:, r], Sigma) for s in 1:S # 无需手动转换类型,直接使用Beta[:,r] ut_rs = X[r, s, :, :] * Beta[:, r] v = softmax(ut_rs) Y[r, s] ~ Categorical(v) end end end sampler = HMC(.05, 10) test_mod = hmnl(d1.G, d1.Y, d1.X, d1.Z) chains = sample(test_mod, sampler, 1_000)
关键修正点
- 修复生成数据函数的维度问题:将
Z[2:G, :] = randn(R * (G-1))改为reshape(randn(R*(G-1)), G-1, R),确保数组维度匹配,避免赋值错误。 - 简化Theta的采样方式:用
filldist(Normal(0,10), K, G)替代双重循环,代码更简洁且类型稳定性更好。 - 正确初始化Beta:基于
Theta的类型(自动微分时为Dual类型,采样时为Float64)初始化Beta,避免类型提升为Any,同时兼容自动微分需求。 - 移除手动类型转换:直接使用
Beta[:,r]参与运算,保留Turing自动微分所需的类型,避免破坏梯度计算。
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

