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

如何为lmfit曲线拟合确定初始参数:DMA流变测试数据非线性拟合自适应初始参数求解问询

自动确定DMA流变学拟合初始参数的可行方案

首先,你的需求非常合理——手动试错设置初始参数不仅低效,还容易错过最优解。针对你的DMA数据拟合(从代码里的calc_gmm和参数t_i、g_i能看出是广义Maxwell模型),我推荐两种实用的自动初始化方法,包括你提到的蒙特卡洛方法,同时会给你代码的改进建议。

一、基于数据特征的初始参数估计(快速且可靠)

广义Maxwell模型的储能模量$G'(\omega)$和损耗模量$G''(\omega)$表达式是:

$G'(\omega) = G_0 + \sum_{i=1}^n \frac{g_i (\omega t_i)^2}{1 + (\omega t_i)^2}$
$G''(\omega) = \sum_{i=1}^n \frac{g_i \omega t_i}{1 + (\omega t_i)^2}$

我们可以从数据的关键特征直接推导初始参数,完全贴合物理意义:

  • $G_0$的初始值:取储能模量在低频段的最小值(因为当$\omega \to 0$时,$G' \to G_0$)
  • 松弛时间$t_i$的初始值:松弛时间对应模量曲线的转折频率($\omega = 1/t_i$),可以取频率轴的对数均分点,覆盖整个测试频率区间的松弛过程
  • 模量分量$g_i$的初始值:将储能模量的总变化量(高频最大值 - $G_0$)平均分配给每个$g_i$,保证初始值的合理性

这种方法不需要随机采样,计算快,能大幅减少拟合迭代次数,是首选方案。

二、蒙特卡洛初始化方法(全局搜索最优初始点)

如果你担心陷入局部最优解,蒙特卡洛方法是很好的补充。核心思路是在参数的合理范围内随机生成大量初始参数组,分别进行快速拟合(限制迭代次数),然后选择卡方值最小的一组作为正式拟合的初始参数。

具体实现步骤:

  1. 定义每个参数的合理范围:
    • $G_0$:0到储能模量低频最小值的1.2倍
    • $t_i$:对应测试频率的倒数范围($1/\omega_{max}$到$1/\omega_{min}$)
    • $g_i$:0到$(G'_{max} - G_0)$的1.5倍
  2. 随机生成N组参数(比如N=100),每组都用随机值初始化
  3. 对每组参数进行简短拟合(比如设置max_nfev=50),计算卡方值
  4. 挑选卡方值最小的那组参数作为正式拟合的初始值

三、针对你现有代码的改进建议

下面是结合上述方法修改后的代码,同时优化了循环终止逻辑(原来的逻辑有点绕):

import numpy as np
import lmfit
import matplotlib.pyplot as plt

def init_params_from_data(dframe, n):
    """从数据特征初始化广义Maxwell模型参数"""
    array_omega = np.array(dframe['Angular Frequency']).flatten()
    array_G_storage = np.array(dframe['Storage Modulus']).flatten()
    
    # 初始化G0:取10%最低频率数据中的储能模量最小值
    low_freq_mask = array_omega < np.percentile(array_omega, 10)
    G0_init = np.min(array_G_storage[low_freq_mask]) if len(array_G_storage[low_freq_mask])>0 else 0.1
    
    # 初始化松弛时间t_i:对数均分频率对应的倒数
    valid_omega = array_omega[array_omega > 0]
    log_omega_min, log_omega_max = np.log10(np.min(valid_omega)), np.log10(np.max(valid_omega))
    log_omega_points = np.linspace(log_omega_min, log_omega_max, n+2)[1:-1]  # 去掉两端边界值
    t_init = 1 / (10 ** log_omega_points)
    
    # 初始化g_i:平均分配储能模量的总变化量
    G_prime_max = np.max(array_G_storage)
    g_init = np.full(n, (G_prime_max - G0_init)/n) if (G_prime_max - G0_init) >0 else np.full(n, 10)
    
    # 创建lmfit参数对象
    params = lmfit.Parameters()
    params.add('n', value=n, vary=False, min=1, max=10)
    params.add('G0', value=G0_init, min=0)
    for i in range(n):
        params.add(f't_{i}', value=t_init[i], min=1/np.max(valid_omega), max=1/np.min(valid_omega))
        params.add(f'g_{i}', value=g_init[i], min=0)
    
    return params

def monte_carlo_init(dframe, n, num_trials=100):
    """蒙特卡洛方法选择最优初始参数"""
    array_omega = np.array(dframe['Angular Frequency']).flatten()
    array_G_storage = np.array(dframe['Storage Modulus']).flatten()
    array_G_loss = np.array(dframe['Loss Modulus']).flatten()
    
    best_chisqr = np.inf
    best_params = None
    valid_omega = array_omega[array_omega > 0]
    G_prime_max = np.max(array_G_storage)
    
    for _ in range(num_trials):
        params = lmfit.Parameters()
        params.add('n', value=n, vary=False, min=1, max=10)
        
        # 随机初始化G0
        low_freq_mask = array_omega < np.percentile(array_omega, 10)
        G0_max = np.min(array_G_storage[low_freq_mask])*1.2 if len(low_freq_mask)>0 else 1.0
        params.add('G0', value=np.random.uniform(0, G0_max), min=0)
        
        # 随机初始化t_i
        t_min = 1/np.max(valid_omega) if np.max(valid_omega)>0 else 0.01
        t_max = 1/np.min(valid_omega) if np.min(valid_omega)>0 else 100
        for i in range(n):
            params.add(f't_{i}', value=np.random.uniform(t_min, t_max), min=t_min, max=t_max)
        
        # 随机初始化g_i
        g_max = (G_prime_max - params['G0'].value)*1.5 if (G_prime_max - params['G0'].value) >0 else 10
        for i in range(n):
            params.add(f'g_{i}', value=np.random.uniform(0, g_max), min=0)
        
        # 快速拟合,限制迭代次数
        res = lmfit.minimize(min_function, params, args=(array_omega, array_G_storage, array_G_loss), max_nfev=50)
        
        # 更新最优参数
        if res.chisqr < best_chisqr:
            best_chisqr = res.chisqr
            best_params = res.params.copy()
    
    return best_params

def calc_gmm(dframe):
    array_omega = np.array(dframe['Angular Frequency']).flatten()
    array_G_storage = np.array(dframe['Storage Modulus']).flatten()
    array_G_loss = np.array(dframe['Loss Modulus']).flatten()
    
    fig, (ax1,ax2) = plt.subplots(nrows=2, ncols=1)
    best_overall_chisqr = np.inf
    best_n = 1
    
    # 遍历不同的n值(1到10)
    for n in range(1, 11):
        # 方法1:用数据特征初始化参数(推荐)
        params = init_params_from_data(dframe, n)
        # 方法2:用蒙特卡洛初始化,替换上面一行即可
        # params = monte_carlo_init(dframe, n)
        
        # 正式拟合
        res = lmfit.minimize(min_function, params, args=(array_omega, array_G_storage, array_G_loss))
        
        # 绘制拟合曲线
        ax1.plot(array_omega, array_G_storage + res.residual, label=f'n: {n}')
        ax2.plot(array_omega, array_G_loss + res.residual, label=f'n: {n}')
        
        # 判断是否继续增加n:卡方值下降不足20%则停止
        if res.chisqr < best_overall_chisqr * 0.8:
            best_overall_chisqr = res.chisqr
            best_n = n
        else:
            print(f"n={n}时卡方值下降不足,停止迭代,最优n为{best_n}")
            break
    
    # 完善图表标注
    ax1.set_xlabel('Angular Frequency')
    ax1.set_ylabel('Storage Modulus')
    ax1.legend()
    ax2.set_xlabel('Angular Frequency')
    ax2.set_ylabel('Loss Modulus')
    ax2.legend()
    plt.tight_layout()
    plt.show()
    
    # 返回最优拟合结果
    return res.params, best_overall_chisqr

四、额外提示

  • 参数数量n的自动选择:用卡方值下降比例作为终止条件(比如下降小于10%就停止),避免不必要的计算
  • 物理约束:给参数设置合理的上下限非常重要,比如松弛时间t_i不能超出测试频率对应的倒数范围,保证拟合结果符合物理意义
  • 蒙特卡洛效率:如果数据量很大,可以减少试次数(比如50次),或者先用电导法估计初始参数,再用蒙特卡洛微调

内容的提问来源于stack exchange,提问作者Maxim Princen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 14:22:47