You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

混合Gamma分布5个参数精确推断:解决curve_fit未拟合出双峰问题

混合Gamma分布的参数精确推断问题

已知离散数据点服从如下混合Gamma分布:

y = w * gamma.pdf(x, alpha1, scale=scale1) + (1-w) * gamma.pdf(x, alpha2, scale=scale2)

需要精确推断这5个参数(w, alpha1, scale1, alpha2, scale2)。使用curve_fit拟合时未得到双峰形态,希望能精准逼近参数,神经网络等方法也可接受。

用户提供的原始代码:

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.optimize import curve_fit
from scipy.stats import gaussian_kde,gamma
data1 = np.random.gamma(4, 1, 200)
data2 = np.random.gamma(6,2, 200)
data = np.concatenate((data1, data2))
x=np.linspace(0,np.max(data)+2,800)
y=gaussian_kde(data)(x) 
initial_params = [0.5, 2, 1, 2, 1]  
params, params_covariance = curve_fit(two_gamma, x, y, p0=initial_params, maxfev=50000)
w, alpha1, scale1, alpha2, scala2 = params
plt.figure(figsize=(10, 6))
sns.histplot(data, bins=20, kde=False, color='y', label='Data density', alpha=0.5, stat='probability')
plt.plot(x, y, marker='o', linestyle='', markersize=1, label='Data distribution')
y_fit=w*gamma.pdf(x, alpha1, scale=scale1)+(1-w)*gamma.pdf(x, alpha2, scale=scala2)
plt.plot(x, y_fit, 'r-', linewidth=1, alpha=0.7,label='Mixture gamma distribution')
plt.legend(fontsize=8, loc='upper right')
plt.title("Expression distribution of gamma mixture")
plt.xlabel("Expression")

问题修正与解决方案

1. 修复curve_fit的使用问题

原始代码存在三个核心问题:未定义拟合函数two_gamma、初始参数偏离真实值、用KDE结果拟合不如直接用原始数据直方图准确。修正后的代码如下:

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.optimize import curve_fit
from scipy.stats import gamma

# 生成数据
data1 = np.random.gamma(4, 1, 200)
data2 = np.random.gamma(6, 2, 200)
data = np.concatenate((data1, data2))

# 定义混合Gamma PDF拟合函数
def two_gamma(x, w, alpha1, scale1, alpha2, scale2):
    return w * gamma.pdf(x, alpha1, scale=scale1) + (1 - w) * gamma.pdf(x, alpha2, scale=scale2)

# 用原始数据的概率密度直方图作为拟合目标
hist, bins = np.histogram(data, bins=30, density=True)
x_hist = (bins[:-1] + bins[1:]) / 2  # 取区间中点

# 设置接近真实值的初始参数,添加参数约束
initial_params = [0.5, 3, 1, 5, 2]  
params, params_covariance = curve_fit(
    two_gamma, 
    x_hist, 
    hist, 
    p0=initial_params,
    bounds=([0, 0, 0, 0, 0], [1, np.inf, np.inf, np.inf, np.inf]),  # w∈[0,1],其余参数>0
    maxfev=100000
)

w, alpha1, scale1, alpha2, scale2 = params
print(f"拟合参数:w={w:.2f}, alpha1={alpha1:.2f}, scale1={scale1:.2f}, alpha2={alpha2:.2f}, scale2={scale2:.2f}")

# 绘图展示
x = np.linspace(0, np.max(data)+2, 800)
y_fit = two_gamma(x, w, alpha1, scale1, alpha2, scale2)

plt.figure(figsize=(10, 6))
sns.histplot(data, bins=30, kde=False, color='y', alpha=0.5, stat='density', label='数据直方图')
plt.plot(x, y_fit, 'r-', linewidth=1.5, alpha=0.7, label='拟合混合Gamma分布')
plt.legend(fontsize=10, loc='upper right')
plt.title("混合Gamma分布拟合结果")
plt.xlabel("数值")
plt.show()

2. 更可靠的方法:EM算法(期望最大化)

混合分布的参数推断,EM算法是标准解法,能有效避免局部最优问题,稳定性远超curve_fit。手动实现的简化版代码如下:

import numpy as np
from scipy.special import psi, polygamma
from scipy.stats import gamma

def em_mixture_gamma(data, n_components=2, max_iter=1000, tol=1e-6):
    n = len(data)
    # 初始化参数
    w = np.ones(n_components) / n_components
    alpha = np.random.uniform(2, 8, n_components)
    scale = np.random.uniform(1, 3, n_components)
    
    log_likelihood_prev = -np.inf
    for _ in range(max_iter):
        # E步:计算每个数据点属于两个分量的后验概率(责任度)
        gamma_pdfs = np.array([gamma.pdf(data, a=alpha[i], scale=scale[i]) for i in range(n_components)])
        responsibilities = (w * gamma_pdfs) / (np.sum(w * gamma_pdfs, axis=0) + 1e-10)
        
        # M步:更新参数
        # 更新混合权重
        w = np.sum(responsibilities, axis=1) / n
        
        # 用牛顿法更新每个Gamma分量的alpha和scale
        for i in range(n_components):
            resp = responsibilities[i]
            sum_resp = np.sum(resp)
            sum_resp_x = np.sum(resp * data)
            sum_resp_log_x = np.sum(resp * np.log(data))
            
            alpha_i = alpha[i]
            # 牛顿法迭代求解alpha的MLE
            for __ in range(10):
                psi_alpha = psi(alpha_i)
                polygamma_alpha = polygamma(1, alpha_i)
                scale_i = sum_resp_x / (sum_resp * alpha_i)
                log_scale = np.log(scale_i)
                numerator = sum_resp * (psi_alpha - log_scale) + sum_resp_log_x - sum_resp * np.log(scale_i)
                denominator = sum_resp * polygamma_alpha
                delta = numerator / denominator
                alpha_i += delta
                if abs(delta) < 1e-8:
                    break
            alpha[i] = alpha_i
            scale[i] = scale_i
        
        # 检查对数似然收敛
        log_likelihood = np.sum(np.log(np.sum(w * gamma_pdfs, axis=0) + 1e-10))
        if abs(log_likelihood - log_likelihood_prev) < tol:
            break
        log_likelihood_prev = log_likelihood
    
    return w, alpha, scale

# 使用EM算法拟合
w_em, alpha_em, scale_em = em_mixture_gamma(data)
print(f"EM算法拟合参数:w={w_em[0]:.2f}, alpha1={alpha_em[0]:.2f}, scale1={scale_em[0]:.2f}, alpha2={alpha_em[1]:.2f}, scale2={scale_em[1]:.2f}")

3. 神经网络方法(可选)

针对大数据量或复杂分布场景,可使用神经网络拟合混合分布,通过负对数似然作为损失函数训练:

import torch
import torch.nn as nn
import torch.optim as optim
from scipy.stats import gamma

class GammaMixtureNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Sequential(
            nn.Linear(1, 64),
            nn.ReLU(),
            nn.Linear(64, 32),
            nn.ReLU(),
            nn.Linear(32, 5)
        )
    
    def forward(self, x):
        params = self.fc(x)
        w = torch.sigmoid(params[:, 0])  # 约束w∈[0,1]
        alpha1 = torch.exp(params[:, 1])  # 约束alpha>0
        scale1 = torch.exp(params[:, 2])
        alpha2 = torch.exp(params[:, 3])
        scale2 = torch.exp(params[:, 4])
        return w, alpha1, scale1, alpha2, scale2

# 准备数据
data_tensor = torch.tensor(data.reshape(-1, 1), dtype=torch.float32)

# 初始化模型与优化器
model = GammaMixtureNet()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练过程
for epoch in range(5000):
    optimizer.zero_grad()
    w, a1, s1, a2, s2 = model(data_tensor)
    
    # 计算负对数似然损失
    pdf1 = torch.tensor(gamma.pdf(data_tensor.numpy(), a=a1.detach().numpy(), scale=s1.detach().numpy()), dtype=torch.float32)
    pdf2 = torch.tensor(gamma.pdf(data_tensor.numpy(), a=a2.detach().numpy(), scale=s2.detach().numpy()), dtype=torch.float32)
    mixture_pdf = w * pdf1 + (1 - w) * pdf2
    loss = -torch.mean(torch.log(mixture_pdf + 1e-10))
    
    loss.backward()
    optimizer.step()
    
    if epoch % 500 == 0:
        print(f"Epoch {epoch}, Loss: {loss.item():.4f}")

# 获取拟合参数
w_nn, a1_nn, s1_nn, a2_nn, s2_nn = model(data_tensor)
print(f"神经网络拟合参数:w={w_nn.mean().item():.2f}, alpha1={a1_nn.mean().item():.2f}, scale1={s1_nn.mean().item():.2f}, alpha2={a2_nn.mean().item():.2f}, scale2={s2_nn.mean().item():.2f}")

内容的提问来源于stack exchange,提问作者NiguLas张三

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:35:00