12、机器学习中的贝叶斯方法与先验分布选择
机器学习中的贝叶斯方法与先验分布选择
1. 稀疏预测模型与最小二乘法预测
在预测时,有一种策略是尽量预测接近 0,即不采取任何立场,只有当我们非常有信心时才进行操作。这种模型被称为稀疏预测模型,当我们对不确定性感到不安时,就会选择不行动。与之对比,最小二乘法预测很少会预测为 0。
当信号变得越来越极端,我们对回报的正负越来越有信心时,稀疏预测模型的位置会与最小二乘法线收敛,这是检验模型合理性的一个好方法。
稀疏预测模型并非根据平方误差损失定义来最佳拟合数据,这方面最小二乘法模型更胜一筹。稀疏预测模型是根据股票损失定义的损失来寻找最佳预测,而最小二乘法模型则是根据平方误差损失来寻找数据的最佳拟合。
2. Kaggle 观测暗世界竞赛案例
2.1 竞赛背景
宇宙中存在比可见物质多近 7 倍的暗物质,它不发光也不吸光。大量暗物质聚集形成暗物质晕,会使经过其附近的背景星系光线弯曲,导致星系在天空中呈现为椭圆。竞赛要求预测暗物质可能存在的位置。
2.2 获胜者解决方案步骤
- 构建先验分布 :为暗物质晕的位置 $p(x)$ 构建先验分布,即在查看数据之前形成对晕位置的预期。
- 构建概率模型 :给定暗物质晕位置 $x$,构建关于星系观测椭圆率数据的概率模型 $p(e|x)$。
- 使用贝叶斯规则 :得到暗物质晕位置的后验分布,利用数据猜测暗物质晕可能存在的位置。
- 最小化预期损失 :关于晕位置预测的后验分布最小化预期损失,即调整预测以使其在给定误差度量下尽可能准确,公式为 $\hat{x} = \arg \min_{prediction} E_{p(x|e)}[L(prediction, x)]$。
这个问题的损失函数非常复杂,大约有 160 行代码,其目的是在欧几里得距离意义下测量预测的准确性,且不存在偏移偏差。
2.3 数据情况
数据集由 300 个单独的文件组成,每个文件代表一片天空。每片天空中有 300 到 720 个星系,每个星系有 $x$ 和 $y$ 位置(范围从 0 到 4200)以及椭圆率测量值 $e_1$ 和 $e_2$。
以下是读取数据的代码示例:
from draw_sky2 import draw_sky
n_sky = 3 # choose a file/sky to examine
data = np.genfromtxt("data/Train_Skies/Train_Skies/\
Training_Sky%d.csv"%(n_sky),
dtype=None,
skip_header=1,
delimiter=",",
usecols=[1,2,3,4])
print "Data on galaxies in sky %d."%n_sky
print "position_x, position_y, e_1, e_2 "
print data[:3]
fig = draw_sky(data)
plt.title("Galaxy positions and ellipticities of sky %d."%n_sky)
plt.xlabel("$x$ position")
plt.ylabel("$y$ position");
2.4 先验设定
每片天空中有 1、2 或 3 个暗物质晕。获胜者 Tim 的解决方案中,晕位置的先验分布是均匀的:
$x_i \sim Uniform(0, 4200)$
$y_i \sim Uniform(0, 4200), i = 1, 2, 3$
大多数天空有一个大晕,其他晕(如果存在)较小。大晕质量服从 40 到 180 之间的对数均匀分布:
$m_{large} = \log Uniform(40, 180)$
在 PyMC 中的实现如下:
exp_mass_large = pm.Uniform("exp_mass_large", 40, 180)
@pm.deterministic
def mass_large(u = exp_mass_large):
return np.log(u)
对于较小的晕,质量设为 20 的对数,这样做是为了加快算法收敛,因为小晕对星系的影响较小。
Tim 假设每个星系的椭圆率取决于晕的位置、星系与晕的距离以及晕的质量。星系椭圆率向量 $e_i$ 是晕位置 $(x, y)$、距离和晕质量的子变量。他认为以下关系是合理的:
$e_i|(x, y) \sim Normal(\sum_{j = halo \ positions} d_{i,j}m_j f(r_{i,j}), \sigma^2)$
其中 $d_{i,j}$ 是切向方向,$m_j$ 是晕 $j$ 的质量,$f(r_{i,j})$ 是星系 $i$ 与晕 $j$ 欧几里得距离的递减函数。
Tim 定义的函数 $f$ 为:
大晕:$f(r_{i,j}) = \frac{1}{\min(r_{i,j}, 240)}$
小晕:$f(r_{i,j}) = \frac{1}{\min(r_{i,j}, 70)}$
这个模型简单且能防止过拟合。
2.5 训练与 PyMC 实现
对于每片天空,运行贝叶斯模型来找到晕位置的后验分布,该模型不使用其他天空或已知晕位置的数据,但模型是通过比较不同天空创建的。
以下是实现代码:
def euclidean_distance(x, y):
return np.sqrt(((x - y) **2).sum(axis=1))
def f_distance(gxy_pos, halo_pos, c):
# foo_position should be a 2D numpy array.
return np.maximum(euclidean_distance(gxy_pos, halo_pos), c)[:,None]
def tangential_distance(glxy_position, halo_position):
# foo_position should be a 2D numpy array.
delta = glxy_position - halo_position
t = (2*np.arctan(delta[:,1]/delta[:,0]))[:,None]
return np.concatenate([-np.cos(t), -np.sin(t)], axis=1)
import pymc as pm
# Set the size of the halo’s mass.
mass_large = pm.Uniform("mass_large", 40, 180, trace=False)
# Set the initial prior position of the halos; it’s a 2D Uniform
# distribution.
halo_position = pm.Uniform("halo_position", 0, 4200, size=(1,2))
@pm.deterministic
def mean(mass=mass_large, h_pos=halo_position, glx_pos=data[:,:2]):
return mass/f_distance(glx_pos, h_pos, 240)*\
tangential_distance(glx_pos, h_pos)
ellpty = pm.Normal("ellipticity", mean, 1./0.05, observed=True,
value=data[:,2:] )
mcmc = pm.MCMC([ellpty, mean, halo_position, mass_large])
map_ = pm.MAP([ellpty, mean, halo_position, mass_large])
map_.fit()
mcmc.sample(200000, 140000, 3)
通过代码运行可以得到后验分布的热图,红色区域表示晕的后验分布位置。
我们还可以绘制星系位置、椭圆率和晕的图,并与真实晕位置对比:
t = mcmc.trace("halo_position")[:].reshape( 20000,2)
fig = draw_sky(data)
plt.title("Galaxy positions and ellipticities of sky %d."%n_sky)
plt.xlabel("$x$ position")
plt.ylabel("$y$ position")
scatter(t[:,0], t[:,1], alpha=0.015, c="r")
plt.xlim(0, 4200)
plt.ylim(0, 4200);
halo_data = np.genfromtxt("data/Training_halos.csv",
delimiter=",",
usecols=[1,2,3,4,5,6,7,8,9],
skip_header=1)
fig = draw_sky(data)
plt.title("Galaxy positions and ellipticities of sky %d."%n_sky)
plt.xlabel("$x$ position")
plt.ylabel("$y$ position" )
plt.scatter(t[:,0], t[:,1], alpha=0.015, c="r")
plt.scatter(halo_data[n_sky-1][3], halo_data[n_sky-1][4],
label="true halo position",
c="k", s=70)
plt.legend(scatterpoints=1, loc="lower left")
plt.xlim(0, 4200)
plt.ylim(0, 4200);
print "True halo location:", halo_data[n_sky][3], halo_data[n_sky][4]
接下来可以使用损失函数优化位置,简单策略是选择均值:
mean_posterior = t.mean(axis=0).reshape(1,2)
from DarkWorldsMetric import main_score
_halo_data = halo_data[n_sky-1]
nhalo_all = _halo_data[0].reshape(1,1)
x_true_all = _halo_data[3].reshape(1,1)
y_true_all = _halo_data[4].reshape(1,1)
x_ref_all = _halo_data[1].reshape(1,1)
y_ref_all = _halo_data[2].reshape(1,1)
sky_prediction = mean_posterior
print "Using the mean:"
main_score(nhalo_all, x_true_all, y_true_all, \
x_ref_all, y_ref_all, sky_prediction)
random_guess = np.random.randint(0, 4200, size=(1,2))
print "Using a random location:", random_guess
main_score(nhalo_all, x_true_all, y_true_all, \
x_ref_all, y_ref_all, random_guess)
为了处理最多两个额外小晕的情况,我们可以创建一个自动化 PyMC 的函数:
from pymc.Matplot import plot as mcplot
def halo_posteriors(n_halos_in_sky, galaxy_data,
samples = 5e5, burn_in = 34e4, thin = 4):
# Set the size of the halo’s mass.
mass_large = pm.Uniform("mass_large", 40, 180)
mass_small_1 = 20
mass_small_2 = 20
masses = np.array([mass_large,mass_small_1, mass_small_2],
dtype=object)
# Set the initial prior positions of the halos; it’s a 2D Uniform
# distribution.
halo_positions = pm.Uniform("halo_positions", 0, 4200,
size=(n_halos_in_sky,2))
fdist_constants = np.array([240, 70, 70])
@pm.deterministic
def mean(mass=masses, h_pos=halo_positions, glx_pos=data[:,:2],
n_halos_in_sky = n_halos_in_sky):
_sum = 0
for i in range(n_halos_in_sky):
_sum += mass[i] / f_distance( glx_pos,h_pos[i, :],
fdist_constants[i])*\
tangential_distance( glx_pos, h_pos[i, :])
return _sum
ellpty = pm.Normal("ellipticity", mean, 1. / 0.05, observed=True,
value = data[:,2:])
map_ = pm.MAP([ellpty, mean, halo_positions, mass_large])
map_.fit(method="fmin_powell")
mcmc = pm.MCMC([ellpty, mean, halo_positions, mass_large])
mcmc.sample(samples, burn_in, thin)
return mcmc.trace("halo_positions")[:]
n_sky =215
data = np.genfromtxt("data/Train_Skies/Train_Skies/\
Training_Sky%d.csv"%(n_sky),
dtype=None,
skip_header=1,
delimiter=",",
usecols=[1,2,3,4])
# There are 3 halos in this file.
samples = 10.5e5
traces = halo_posteriors(3, data, samples=samples,
burn_in=9.5e5,
thin=10)
通过上述代码可以得到不同天空中暗物质晕位置的预测结果,并通过损失函数评估预测的准确性。
3. 贝叶斯先验分布类型
贝叶斯先验可以分为两类:
| 先验类型 | 特点 | 示例 |
| ---- | ---- | ---- |
| 客观先验 | 旨在让数据对后验产生最大影响 | 平坦先验 |
| 主观先验 | 允许从业者在先验中表达自己的观点 | 无 |
3.1 客观先验
平坦先验是一种客观先验,它是在未知量的整个范围内的均匀分布,使用平坦先验意味着对每个可能的值给予相等的权重,遵循无差别原则。但在受限空间上的平坦先验不是客观先验,例如已知二项式模型中 $p > 0.5$,则 $Uniform(0.5, 1)$ 不是客观先验。除了平坦先验,其他客观先验不太明显,但都具有反映客观性的重要特征,不过很少有客观先验是真正客观的。
以下是整个流程的 mermaid 流程图:
graph TD;
A[构建先验分布] --> B[构建概率模型];
B --> C[使用贝叶斯规则得到后验分布];
C --> D[最小化预期损失];
D --> E[训练与实现];
E --> F[评估预测准确性];
在机器学习中,损失函数是统计学中有趣的部分,它直接连接推理和问题所在的领域。在分析中应尽早设定损失函数,并使其推导公开且合乎逻辑,避免因结果不符合期望而随意更改损失函数。同时,选择合适的先验分布在贝叶斯方法中至关重要,需要根据具体情况权衡客观先验和主观先验的使用。
机器学习中的贝叶斯方法与先验分布选择
4. 先验分布的影响及与线性回归的关系
随着数据集的增大,先验的影响会逐渐减小。当数据量非常大时,后验分布主要由数据决定,先验的作用变得微不足道。这是因为大量的数据提供了足够的信息,使得先验的初始假设对最终结果的影响被削弱。
先验分布与线性回归中的惩罚项存在有趣的关系。在贝叶斯线性回归中,先验可以看作是对模型参数的一种约束,类似于线性回归中的惩罚项。例如,在岭回归中,我们通过添加 $L_2$ 惩罚项来限制模型参数的大小,防止过拟合。在贝叶斯框架下,这可以通过选择一个合适的先验分布来实现,如高斯先验。高斯先验会对参数值较大的情况给予较低的概率,从而起到类似于惩罚项的作用。
5. 选择合适先验分布的重要性及注意事项
选择合适的先验分布对于贝叶斯方法的成功应用至关重要。不合适的先验可能导致模型出现偏差,无法准确反映数据的真实特征。例如,如果我们选择了一个过于“强”的先验,即对某些参数值赋予了过高的概率,那么即使数据提供了相反的证据,后验分布仍然会受到先验的强烈影响,导致结果不准确。
在选择先验分布时,需要注意以下几点:
1. 基于领域知识 :如果我们对问题有一定的了解,可以根据领域知识来选择先验分布。例如,在预测暗物质晕的位置时,我们知道晕的位置在一定范围内,因此可以选择均匀分布作为先验。
2. 数据量的考虑 :如前面所述,当数据量较小时,先验的影响较大;当数据量较大时,先验的影响会减小。因此,在数据量较小时,需要更加谨慎地选择先验分布。
3. 避免主观偏见 :虽然主观先验允许我们表达自己的观点,但在实际应用中,应尽量避免过度的主观偏见。如果先验分布的选择过于依赖个人的主观判断,可能会导致模型结果的不稳定性。
6. 实际应用中的案例分析
为了更好地理解先验分布的选择和贝叶斯方法的应用,我们可以通过一个实际案例进行分析。假设我们要预测某地区的房价,我们可以使用贝叶斯线性回归模型。
首先,我们需要选择合适的先验分布。根据以往的经验,我们知道房价的影响因素(如房屋面积、房间数量等)的系数通常不会太大,因此可以选择一个高斯先验来约束这些系数。
以下是实现该模型的代码示例:
import pymc as pm
import numpy as np
# 模拟数据
np.random.seed(123)
n = 100
X = np.random.randn(n, 2)
true_beta = np.array([2, 3])
noise = np.random.randn(n) * 0.5
y = np.dot(X, true_beta) + noise
# 定义先验分布
beta = pm.Normal("beta", mu=0, tau=1, size=2)
tau = pm.Gamma("tau", alpha=0.1, beta=0.1)
# 定义线性回归模型
@pm.deterministic
def y_hat(beta=beta, X=X):
return np.dot(X, beta)
y_obs = pm.Normal("y_obs", mu=y_hat, tau=tau, observed=True, value=y)
# 运行 MCMC 采样
mcmc = pm.MCMC([beta, tau, y_hat, y_obs])
mcmc.sample(10000, 5000, 2)
# 输出结果
beta_samples = mcmc.trace("beta")[:]
print("Estimated beta:", beta_samples.mean(axis=0))
在这个案例中,我们选择了高斯先验来约束模型的系数,通过 MCMC 采样得到了系数的后验分布。最后,我们可以根据后验分布的均值来估计模型的系数。
7. 总结与展望
在机器学习中,贝叶斯方法通过引入先验分布,为我们提供了一种更加灵活和强大的建模方式。损失函数作为连接推理和问题领域的桥梁,在模型中起着重要的作用。同时,选择合适的先验分布是贝叶斯方法的关键,需要综合考虑领域知识、数据量等因素。
未来,随着数据量的不断增加和计算能力的提升,贝叶斯方法有望在更多领域得到应用。例如,在医疗领域,贝叶斯方法可以用于疾病诊断和治疗方案的选择;在金融领域,可以用于风险评估和投资决策。同时,研究人员也在不断探索更加有效的先验分布选择方法和贝叶斯算法,以提高模型的性能和准确性。
以下是一个总结先验分布选择要点的列表:
1. 先考虑领域知识,结合实际情况选择先验。
2. 根据数据量大小调整先验的影响。
3. 避免主观偏见,确保先验的合理性。
4. 可以通过实际案例验证先验选择的有效性。
另外,下面是一个关于贝叶斯建模流程的 mermaid 流程图:
graph TD;
A[问题定义] --> B[选择先验分布];
B --> C[构建模型];
C --> D[数据收集];
D --> E[运行 MCMC 采样];
E --> F[分析后验分布];
F --> G[评估模型性能];
G --> H[调整模型或先验];
H --> C;
通过以上的分析和案例,我们可以看到贝叶斯方法在机器学习中的重要性和应用潜力。在实际应用中,我们需要根据具体问题选择合适的先验分布和损失函数,以构建准确、可靠的模型。
更多推荐
所有评论(0)