lmfit 阶梯函数和步长

如何解决lmfit 阶梯函数和步长

我想在图像中放入 2D 形状。过去,我在 Python 中使用 lmfit 成功完成了此操作,并将 2D 函数/数据包装到 1D。在那种情况下,2D 模型是一个平滑函数(具有高斯轮廓的环)。现在我正在尝试做同样的事情,但使用“非平滑功能”并且它没有按预期工作。

这就是我想要做的(猜测和安装是一样的):

enter image description here

我有意改变了猜测的参数,以便轻松查看它是否按预期移动,但没有任何反应。

我注意到,如果我使用 2D 高斯函数而不是瑞士国旗,这是一个平滑的函数,这可以正常工作(请参阅下面的 MWE):

enter image description here

所以我猜测问题与瑞士国旗功能不流畅有关。我试图通过添加高斯滤波器(模糊)使其平滑,但它仍然不起作用,即使瑞士国旗图变得非常模糊。

一段时间后,我想到可能使用 lmfit(o 无论在后台的人)的步长太小,无法对瑞士国旗产生任何变化。我想尝试将步长增加到 1,但我不知道该怎么做。

这是我的 MWE(对不起,它仍然很长):

import numpy as np
import myplotlib as mpl # https://github.com/SengerM/myplotlib
import lmfit

def draw_swiss_flag(fig,center,side,**kwargs):
    fig.plot(
        np.array(2*[side] + 2*[side/2] + 2*[-side/2] + 2*[-side] + 2*[-side/2] + 2*[side/2] + 2*[side]) + center[0],np.array([0] + 2*[side/2] + 2*[side] + 2*[side/2] + 2*[-side/2] + 2*[-side] + 2*[-side/2] + [0]) + center[1],**kwargs,)

def swiss_flag(x,y,center: tuple,side: float):
    # x,y numpy arrays.
    if x.shape != y.shape:
        raise ValueError(f'<x> and <y> must have the same shape!')
    flag = np.zeros(x.shape)
    flag[(center[0]-side/2<x)&(x<center[0]+side/2)&(center[1]-side<y)&(y<center[1]+side)] = 1
    flag[(center[1]-side/2<y)&(y<center[1]+side/2)&(center[0]-side<x)&(x<center[0]+side)] = 1
    return flag

def gaussian_2d(x,side):
    return np.exp(-(x-center[0])**2/side**2-(y-center[1])**2/side**2)

def wrapper_for_lmfit(x,x_pixels,y_pixels,function_2D_to_wrap,*params):
    pixel_number = x # This is the pixel number in the data array
    # x_pixels and y_pixels are the number of pixels that the image has. This is needed to make the mapping.
    if (pixel_number > x_pixels*y_pixels - 1).any():
        raise ValueError('pixel_number (x) > x_pixels*y_pixels - 1')
    x = np.array([int(p%x_pixels) for p in pixel_number])
    y = np.array([int(p/x_pixels) for p in pixel_number])
    return function_2D_to_wrap(x,*params)

data = np.genfromtxt('data.txt') # Read data
data -= data.min().min()
data = data/data.max().max()

guessed_center = (data.sum(axis=0).argmax()+11,data.sum(axis=1).argmax()+11) # I am adding 11 in purpose.
guessed_side = 19

model = lmfit.Model(lambda x,xc,yc,side: wrapper_for_lmfit(x,data.shape[1],data.shape[0],swiss_flag,(xc,yc),side))
params = model.make_params()
params['xc'].set(value = guessed_center[0],min = 0,max = data.shape[1])
params['yc'].set(value = guessed_center[1],max = data.shape[0])
params['side'].set(value = guessed_side,min = 0)
fit_results = model.fit(data.ravel(),params,x = [i for i in range(len(data.ravel()))])

mpl.manager.set_plotting_package('matplotlib')
fit_plot = mpl.manager.new(
    title = 'Data vs fit',aspect = 'equal',)
fit_plot.colormap(data)
draw_swiss_flag(fit_plot,guessed_center,guessed_side,label = 'Guessed')
draw_swiss_flag(fit_plot,(fit_results.params['xc'],fit_results.params['yc']),fit_results.params['side'],label = 'Fitted')

swiss_flag_plot = mpl.manager.new(
    title = 'Swiss flag plot',)
xx,yy = np.meshgrid(np.array([i for i in range(data.shape[1])]),np.array([i for i in range(data.shape[0])]))
swiss_flag_plot.colormap(
    z = swiss_flag(xx,yy,center = (fit_results.params['xc'],side = fit_results.params['side']),)

mpl.manager.show()

thisdata.txt的内容。

解决方法

看来你的代码没问题。问题是,正如您已经猜到的,lmfit 使用的算法不能很好地处理非平滑数据。

默认情况下 lmfit 使用 leas squares 方法。让我们将其更改为方法 'differential_evolution'

params['side'].set(value=guessed_side,min=0,max=len(data))
fit_results = model.fit(data.ravel(),params,x=[i for i in range(len(data.ravel()))],method='differential_evolution'
                        )

请注意,我需要为最大值添加一些有限值,以防止出现“differential_evolution 需要对所有不同参数进行有限界”消息。

切换到进化算法后,拟合现在看起来像这样:

enter image description here

,

lmfit 中的所有拟合算法(以及 scipy.optimize 就此而言),包括“全局优化器”,真正适用于连续变量(双精度)。当试图找到最优参数值时,大多数算法会在值中做一个非常小的步骤(在 ~1.e-7 级别)来确定导数,然后将使用该导数对最优值进行下一次猜测价值。

您看到的问题是您的模型函数使用参数值作为离散值 - 作为使用 int() 的数组的索引。如果对参数值进行了微小的更改,则不会检测到结果的任何变化 - 算法将决定拟合结果不依赖于该值的微小变化。

所谓的“全局求解器”,如微分进化、盆地跳跃、shgo,认为导数方法会导致“假最小值”,因此会“喷洒参数空间”,其中包含大量候选值,然后使用不同的策略来优化这些结果中的最佳结果以找到最佳值。一般来说,这些运行速度要慢得多(OTOH 运行时很便宜!)并且非常适合可能有多个“最小值”并且您真的想找到其中最好的问题,或者对起始值进行合理猜测的问题很辛苦。

对于您的问题,很明显您可以猜测起始值(中心像素必须在图像上,例如,所以可能猜测“中间”),并且从图像中似乎很可能没有可能会发现很多错误的最小值。这意味着可能不需要全局求解器的费用。

另一种方法是让您的形状对象以图像中的任何连续中心为中心,而不仅仅是以整数像素为中心。当然,您必须将其映射到离散图像,但不需要完全打开/关闭。使用像 scipy.special.erf()erfc() 这样的 sigmoidal 函数将允许您仍然有从“开”到“关”的过渡,但宽度小但有限,渗入相邻像素。这足以让拟合找到中心位置的连续(因此,亚像素!)值。在 1-d 中,这可能看起来像 ::

from scipy.special import erf
def smoothed_window(x,edge1,edge2,width):
    return (erf((x-edge1)/width) + erf((edge2-x)/width))/2.0

对于整数 x 值,0.5(即半个像素)的 width 几乎肯定会允许拟合找到 edge1edge2 的子整数值。 (另外:在代码中或在参数级别强制固定 width 参数或强制其为正数)。

我没有尝试将其扩展到您更复杂的“瑞士国旗”功能,但它应该是可能的,并且也适用于拟合中心值。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res