概率分布导致“以退出代码137完成的过程(被信号9:SIGKILL中断)”

时间:2019-10-21 09:45:41

标签: python statistics cpu probability montecarlo

我正在尝试为我的地质研究创建某种简化的Oracle Crystal Ball应用程序,该应用程序将使用P90(90%置信度)和P10(10%置信度)值作为不同概率情形的输入和收益分布。听起来像蒙特卡洛发行。我刚接触Python,最近才开始,顺便说一句:)

该主题将分为四个关键部分:

  1. 作品范围的一般说明。
  2. 伪编码(不过从未尝试过)。
  3. 实际的Python代码。
  4. 我在这里的原因或逻辑/代码问题。

PART 1.工作范围的一般说明。

  1. 为简单起见,假设我们只有三个类别,每个类别都有P90和P10参数,而它们之间没有任何步骤:

    • cat_1:[1、2]
    • cat_2:[2,4]
    • cat_3:[3,6]
  2. 利用笛卡尔积,我们获得了以下8种可能的情况的列表:

    • [1、2、3],[1、2、6],[1、4、3],[1、4、6],[2、2、3],[2、2、6] ,[2、4、3],[2、4、6]
  3. 在每个列表中乘以参数会得出以下乘积:

    • [6,12,12,24,12,24,24,48]
  4. 测量每种产品的频率会导致:

    • {6:1,12:3,24:3,48:1},或考虑以下百分比:

    • {6:12.5%,12:37.5%,24:37.5%,48:12:5%,},这意味着发生12或24的可能性高于6或48。

  5. 这就是我想要得到的结果:知道乘积能够获得均值,中位数和众数值的可能性。

  6. 对于我的硬件而言,最困难的部分是大量实际情况。共有六类,在P90和P10值之间有小的变化。考虑到公制,P90和P10值的范围可能如下:

    • 平方面积:0.01-100.00 km2,步进0.01;
    • 层厚度:0.10-100.00 m,步长0.1;
    • 孔隙度:0.01-1.00 p.u.,步骤0.01;
    • 饱和度:0.01-1.00 p.u.,步骤0.01;
    • 压力:1-2000 atm,步骤1 atm;
    • 表面:0.01-1.00 p.u.,步长0.01。
  7. 通常情况下,实际的案例研究将使用更窄的范围,例如,squeurea区域为0.1-2.0 km2,厚度为1-10 m,孔隙度为8-15等。尽管如此,即使听起来像是考虑上述步骤,“ google”可能的方案数量。结果,我收到以下通知,这是关键问题:

  

以退出代码137(被信号9:SIGKILL中断)结束的过程。

当计算总量超过〜10MM和〜1分钟(通过实验检查,因此数字比较粗糙)时,就会发生这种情况。

PART2。伪编码。

优良作法是,在进行伪编码时不应抽象化,但是我在该领域的经验为零,因此会尽力而为。

User inputs minimum possible values (P90) for total 6 categories
User inputs maximum possible values (P10) for total 6 categories

Total 6 list are created (square area, layer thickness, porosity etc.), 1 per each category that contain a range of possible values and indicated step (P90_category1, P10_category1, step1)

Use a Cartesian product to create a list_of_tuples with possible scenarios

Convert list_of_tuples to the list_of_lists

Create empty_list
for each element in the list_of_lists:
    calculate its product
    append to the empty_list

Round values in the empty_list

Create a dictionary that counts similar values in the empty_list

Calculate a probability of each value according to its repetition frequency in the dictionary

就是这样。还应用了一些基本的统计数据和绘图,但这并不是关键时刻。

PART 3.实际的Python代码。

最初的P90值(置信度为90%):

P90_area = float(input('P90 area: '))
P90_thickness = float(input('P90 thickness: '))
P90_porosity = float(input('P90 porosity: '))
P90_saturation = float(input('P90 saturation: '))
P90_pressure = float(input('P90 pressure: '))
P90_surface = float(input('P90 surface: '))

然后是P10值(置信度为10%):

P10_area = float(input('P10 area: '))
P10_thickness = float(input('P10 thickness: '))
P10_porosity = float(input('P10 porosity: '))
P10_saturation = float(input('P10 saturation: '))
P10_pressure = float(input('P10 pressure: '))
P10_surface = float(input('P10 surface: '))

使用特定步骤创建从P90到P10的值范围

area_values = np.arange(P90_area, P10_area + 0.01, 0.01)
thickness_values = np.arange(P90_thickness, P10_thickness + 0.1, 0.1)
porosity_values = np.arange(P90_porosity, P10_porosity + 0.01, 0.01)
saturation_range = np.arange(P90_saturation, P10_saturation + 0.01, 0.01)
pressure_range = np.arange(P90_pressure, P10_pressure + 1, 1)
surface_range = np.arange(P90_surface, P10_surface + 0.01, 0.01)

将所有列表合并为笛卡尔积(即[(area1,thickness1,孔隙率1),(area1,thickness1,孔隙率2)等]):

list_of_tuples = list(itertools.product(area_values, thickness_values, porosity_values, saturation_range, pressure_range, surface_range)

将元组列表转换为列表列表:

list_of_lists = [list(elem) for elem in list_of_tuples]

创建一个包含多个值的列表并将其排序('np.prod'返回每个列表的乘积):

multiplied_values = []
for i in list_of_lists:
    i = np.prod(np.array(i))
    multiplied_values.append(i)
multiplied_values = sorted(multiplied_values)

舍入值:

rounded_values = [float(Decimal('%.2f' % elem)) for elem in multiplied_values]

创建一个包含所有相似/唯一对象的字典:

counts = Counter(rounded_values)

通过将值除以列表中元素的总数来计算概率:

probability_mass = {k: v/total for k, v in counts.items()}

它起作用了,这里有简单的统计信息和特定情况的图表:

  • 总计算量:4899510
  • P90是:5.60
  • P10是:43.41
  • P50(最大概率值)是:15.24
  • 平均值为:23.80

Figure. Probability distribution diagram

第一个问题至关重要,因为它阻止了大数据堆栈的计算:

PART 4.关键问题。

问题1。关键问题:

结果,我收到以下通知,这是关键问题:

  

以退出代码137(被信号9:SIGKILL中断)结束的过程。

根据类似的主题,很可能我的脚本由于CPU使用率过高而被操作系统杀死。在运行代码时,我用'top'命令检查了CPU的负载,当CPU可以处理输入参数时,CPU的负载达到了100%,而在中断时,CPU的负载达到了110%。

规格:华硕G531GU笔记本电脑| i7-9750H CPU 2.60GHz | GeForce GTX 1660 TI,6Gb | 16Gb DDR4 | Ubuntu 18 | PyCharm社区IDE。

问题:如果有什么机会,我该如何摆脱这种干扰并让脚本在必要时运行?我愿意等待所需的时间,以便为大型数据堆栈获取正确的分发。为每个参数增加一个步骤是一个硬性选择,我宁愿这样做。

第二季度。概率分布图看起来不像经典的正态分布,而最大概率值和平均值之间的差异非常大。您怎么看,代码的逻辑可能有问题吗?

P.S。我了解此脚本看起来很坎y,希望您的眼睛不会流血)

2 个答案:

答案 0 :(得分:0)

由于您正在尝试计算每种可能的情况,因此此处所需的计算与每个范围中元素的数量成指数关系增长。 我很想为您调试一个完整的代码,但是我需要输入,因此您可以将完整的代码与已经指定的输入一起发布,以便我们知道要使用什么合理的值。

有一点不同,我们可以尝试解决您的原始问题,而不是尝试修复您的代码?当您说“简化的概率分布计算器”时,您是什么意思?在尝试查看如何在Python中实现该过程之前,您能以伪代码编写步骤以使我们理解该过程吗。

根据您对上述问题的回答,我可能建议您采用抽样方法,而不是评估每种可能性。查找蒙特卡洛模拟。如果您有一个要使用新数据更新的先前发行版,并且想知道后验(最终)发行版,那么可以考虑使用贝叶斯方法,特别是Winbugs(不是Python的贝叶斯程序的理想独立程序)。

PS。我知道我的答案可能更适合写为评论,但显然您需要+50的声誉,而我还没有呢:(

答案 1 :(得分:0)

因此,对于输入参数,随机采样和笛卡尔积的均匀分布,我已经完成了您需要的工作。结果看起来像是指数分布。最好用weibull分布进行建模。

我做了一些进一步的分析,因为任何模拟的结果都应该进一步研究以检查模拟是否足够。为此,我对10,100,1000,10000,100000,10000000个样本进行了蒙特卡洛采样以生成直方图。从拟合的weibull的alpha和beta的收敛中可以看出,一百万个样本就足够了。

我确定您会对此有疑问,因此请在下面提问。请注意,直方图以对数对数比例显示,因此在可视化分布(或注释掉xscale和yscale线)时,您需要牢记这一点。

结果如下: https://i.stack.imgur.com/viQ9i.png https://i.stack.imgur.com/0kc4n.png

这是生成输出的代码:

import numpy as np
from tqdm import tqdm
import random
import matplotlib.pyplot as plt
import scipy.stats as ss

#these should be user inputs
area_min = 0.01
area_max = 100
thickness_min = 0.1
thickness_max = 100
porosity_min = 0.01
porosity_max = 1
saturation_min = 0.01
saturation_max = 1
pressure_min = 1
pressure_max = 2000
surface_min = 0.01
surface_max = 1

grid_resolution = 1000 #how finely we will slice each property. I have kept this consistent as it makes more sense to do so when sampling
#With a grid_resolution of 1000, the number of possible combinations here is 1000^6 ==> 10^18 so we will randomly sample the array
#I assume you want to get a probability distribution of these combinations.
area_array = np.linspace(area_min,area_max,grid_resolution)
thickness_array = np.linspace(thickness_min,thickness_max,grid_resolution)
porosity_array = np.linspace(porosity_min,porosity_max,grid_resolution)
saturation_array = np.linspace(saturation_min,saturation_max,grid_resolution)
pressure_array = np.linspace(pressure_min,pressure_max,grid_resolution)
surface_array = np.linspace(surface_min,surface_max,grid_resolution)

#it is important to try different sample sizes to be sure your sample is large enough
samples_to_test = [1,2,3,4,5,6] #log10 scale

xmax = 10**8
alpha_array = []
beta_array = []
plt.figure(figsize=(12,10))
for i,s in enumerate(samples_to_test):
    plt.subplot(231+i)
    samples = 10**s
    product_array = []
    for _ in tqdm(range(samples)):
        area = random.choice(area_array)
        thickness = random.choice(thickness_array)
        porosity = random.choice(porosity_array)
        saturation = random.choice(saturation_array)
        pressure = random.choice(pressure_array)
        surface = random.choice(surface_array)
        product_array.append(area*thickness*porosity*saturation*pressure*surface)

    xvals = np.logspace(1,np.log10(xmax),1000)
    [beta,_,alpha] = ss.weibull_min.fit(data=product_array,floc=0)
    alpha_array.append(alpha)
    beta_array.append(beta)
    weibull_yvals = ss.weibull_min.pdf(xvals,beta,scale=alpha)
    plt.plot(xvals,weibull_yvals)
    print('Weibull fit parameters:\nalpha =',alpha,'\nbeta =',beta)
    [mean,variance] = ss.weibull_min.stats(beta, loc=0, scale=alpha, moments='mv')
    median = ss.weibull_min.median(beta, loc=0, scale=alpha)
    print('Mean =',mean)
    print('Median =',median)
    print('Standard deviation =',variance**0.5)

    plt.hist(product_array,bins=1000,density=True)
    plt.yscale('log')
    plt.xscale('log')
    plt.xlabel('Cartesian Product of parameters')
    plt.ylabel('Probability density ($log_{10}$ scale)')
    plt.title(str('Monte Carlo samples = '+str(samples)))
    plt.xlim(10,xmax)
    plt.ylim(10**-8,0.0001)

plt.suptitle('Probability of of a given cartesian product of the specified parameters\nmeasured using different numbers of Monte Carlo samples')
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.semilogx(10**np.array(samples_to_test),alpha_array,label='alpha')
plt.legend()
plt.subplot(122)
plt.semilogx(10**np.array(samples_to_test),beta_array,label='beta')
plt.legend()
plt.suptitle('Test results for alpha and beta')
plt.show()

Output:
100%|██████████| 10/10 [00:00<?, ?it/s]
Weibull fit parameters:
alpha = 86642.0194345818 
beta = 0.4938259951069627
Mean = 177350.7081149186
Median = 41247.66458603765
Standard deviation = 403557.41514732403
100%|██████████| 100/100 [00:00<00:00, 100246.27it/s]
Weibull fit parameters:
alpha = 177861.91287733015 
beta = 0.6310314479279571
Mean = 251385.7124440623
Median = 99503.40459313976
Standard deviation = 415414.97618995525
100%|██████████| 1000/1000 [00:00<00:00, 199131.37it/s]
Weibull fit parameters:
alpha = 171932.22877129668 
beta = 0.5452693527437176
Mean = 296661.14084923535
Median = 87788.61401806296
Standard deviation = 589615.4680695855
100%|██████████| 10000/10000 [00:00<00:00, 179051.70it/s]
Weibull fit parameters:
alpha = 166909.86147776648 
beta = 0.5172460791589029
Mean = 314175.4976503747
Median = 82176.44526800542
Standard deviation = 670314.3944630618
100%|██████████| 100000/100000 [00:00<00:00, 144477.93it/s]
Weibull fit parameters:
alpha = 167711.26073670806 
beta = 0.5194333533253157
Mean = 313393.61873437575
Median = 82817.74728224205
Standard deviation = 664803.5086740599
100%|██████████| 1000000/1000000 [00:07<00:00, 140706.15it/s]
Weibull fit parameters:
alpha = 168089.6178189406 
beta = 0.5186379527889259
Mean = 314930.2501968761
Median = 82914.8108556469
Standard deviation = 669461.6904337168