逼近两组点之间的边界(二维):拟合区域

计算科学 优化 几何学 距离测量
2021-12-01 10:40:00

给定两组点pin,ipout,j在我直观地称为“区域”的内部和外部,我想估计和描述这个区域的边界。欢迎两种答案:

  • 边界的参数化描述
  • 预测新点是否在区域内的估计函数。

到目前为止,我发现了很多可能相关的聚类算法,但我不知道如何在这里应用它们。在这里,输入/输出是输入的一部分,而不是结果。

我是从实验环境中得出这个问题的。一个很好的后续问题是“下一步在哪里探测”(即,在哪个新位置会输入/输出知识最能改变模型?在下图中:左下角)

我的第一个天真的方法类似于“一个将某种符号距离最小化到所有点的圆圈”,但我想要更复杂的形状。

还有一些想法:

  • 我现在只对 2D 感兴趣
  • 我相当精通优化,甚至欢迎指向合适的参数化“区域描述”的指针。例如,我知道 两个序列,但它们似乎没有适当的界限。
  • 优化参数的数量应该是可扩展的:对于只有一个点和一个点,即使返回一个圆也是过拟合的,但对于下面的数据,更复杂的区域形状是有意义的。
  • 会有异常值
  • 仅针对凸区域的算法是可以接受的,但最好使用更通用的算法。
  • 点的权重(不确定性)可用。

更新

我最初的问题在某些方面不清楚。感谢第一个答案,我现在可以更清楚地指定一些事情:

  • “我直观地称之为区域”有两个重要的属性:它是有限的和连接的
  • 会有异常值:某些点被错误分类,模型不能过拟合以包含所有点。因此,“参数的数量应该是可扩展的”以避免/调整过度拟合,就像一个通常不适合多项式一样nn1点。

示例数据

2个回答

理论

在这种情况下,聚类不太可能起作用,因为您的红点被绿点彼此隔开。你可以使用更多的集群,但这需要大量的人工检查和摆弄。

解决这类问题的标准方法是使用非线性支持向量机简单描述的想法是,尽管您的数据在二维中可能无法分离,但将数据投影到更高维度始终可以保证一个干净的分隔符,然后可以使用该分隔符对未来的兴趣点进行分类。此外,我们可以选择此分隔符,使分隔符与任何数据点之间的距离最大化

这里有几张图片展示了这个想法:

支持向量机中的内核技巧

支持向量机中的内核技巧

结果

我使用WebPlotDigitizer以任意但保留纵横比的单位提取数据,然后使用径向基函数内核通过支持向量机运行它。这给出了以下分隔符:

使用径向基函数内核 (RBF) 的支持向量机 (SVM)

请注意,正如所承诺的,这将您的数据清晰地分为两个类。分隔符本身是灰色实线,而分隔符周围的边距(分隔符和数据之间的距离)用虚线表示。分隔符和这些边距之间的距离是该内核与所有其他选择相比的最大值。引起边缘的数据点被圈出。

如果我们添加一百个新点并使用我们的 SVM 对它们进行分类,我们会得到:

使用带有新点的径向基函数 (RBF) 的支持向量机 (SVM)

执行

我使用 Python 来实现上述想法。

import matplotlib.pyplot as plt
import numpy as np
from sklearn import svm

# Your data
data = [
  [ 0.7490599550363091 ,  9.24443743612264   , 0],
  [ 4.12034765121126   ,  8.885539916739752  , 0],
  [ 7.6539862433328665 ,  9.612037680925432  , 0],
  [ 7.914094745271155  ,  0.9253511681985884 , 0],
  [ 8.994538029638242  ,  0.5077641947442704 , 0],
  [ 9.371974940345583  ,  9.608153819994236  , 0],
  [12.320128383367402  ,  6.088728506174032  , 0],
  [12.411426660363531  ,  4.715352126892222  , 0],
  [12.558352293037347  , 11.229593835418646  , 0],
  [13.114336626127825  ,  5.703722810531428  , 0],
  [13.381386496538893  ,  9.854563219073547  , 0],
  [13.886274645038084  ,  8.480251836234228  , 0],
  [14.13510342320811   ,  6.595572357695316  , 0],
  [14.463275899337807  ,  9.915985760466931  , 0],
  [15.05984795641495   ,  7.296033868971857  , 0],
  [ 3.096302985685499  ,  6.907935469254409  , 1],
  [ 4.878692156862327  ,  7.159379502874164  , 1],
  [ 7.4911533079089345 ,  8.366972559074298  , 1],
  [ 7.986689890548966  ,  3.89506631355318   , 1],
  [ 8.498712223311847  ,  4.883868537295852  , 1],
  [ 9.869150457208352  ,  5.679125024633843  , 1],
  [10.527327159481406  ,  2.3884159656508306 , 1],
  [10.671071331605198  ,  7.848836741512311  , 1],
  [12.788477939489049  ,  3.1497246315161327 , 1],
  [12.86531503216689   ,  7.524534353757313  , 1],
]

# Load data into numpy
data = np.array(data)

# Separate data into x and y values; predictors and observations
data_x = data[:,0:2]  # x-value/predictor
data_y = data[:,2]    # y-value/observation

# In these "learning" style problems you often want to avoid over-fitting, so
# it might make sense to split your data into training and test sets. Here we
# simply use all data as the training data.
x_train = data_x[:,:]
y_train = data_y[:]

# Using the RBF (radial basis function) kernel with an SVM projects the points
# into a higher-dimensional space where a clean boundary is possible and then
# lowers that boundary back into the space the points live in.
model = svm.SVC(kernel='rbf', C=1e6)
model.fit(x_train, y_train)

def plot_model(data_x, data_y, predict_x=None, predict_y=None) -> None:
  # Let's plot some stuff
  fig, ax = plt.subplots(figsize=(12, 7))

  # Create grid to evaluate model
  xx = np.linspace(-1, max(data_x[:,0]) + 1, len(data_x))
  yy = np.linspace(0, max(data_x[:,1]) + 1, len(data_x))
  YY, XX = np.meshgrid(yy, xx)
  xy = np.vstack([XX.ravel(), YY.ravel()]).T

  # Assigning different colors to the classes
  colors = data_y
  colors = np.where(colors == 1, '#8C7298', '#4786D1')
  ax.scatter(data_x[:,0], data_x[:,1],c=colors)

  if predict_x is not None:
    assert predict_y is not None
    colors = predict_y
    colors = np.where(colors == 1, '#8C7298', '#4786D1')
    ax.scatter(predict_x[:,0], predict_x[:,1],c=colors,s=4)

  # Get the separator
  Z = model.decision_function(xy).reshape(XX.shape)

  # Draw the decision boundary and margins
  ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5, linestyles=['--', '-', '--'])

  # Highlight support vectors with a circle around them
  ax.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=100, linewidth=1, facecolors='none', edgecolors='k')

plot_model(data_x, data_y)
plt.show()

# Generate some previously-unseen data
new_x = np.random.uniform(low=min(data_x[:,0]), high=max(data_x[:,0]), size=(100,1))
new_y = np.random.uniform(low=min(data_x[:,1]), high=max(data_x[:,1]), size=(100,1))
new_data = np.hstack((new_x, new_y))
predictions_for_new_data = model.predict(new_data)

plot_model(data_x, data_y, new_data, predictions_for_new_data)
plt.show()

我不知道以下是否是个好主意。但这是一个想法,我希望它有所帮助。

这个问题可以改写为“找函数f:R2RzR英石f(x,y)z0如果点(x,y)是“内部”,否则为负。为基础f您可以选择最高 10 次的勒让德多项式的张量积作为示例。此时,您有一个 LP 问题来确定基函数的系数和常数z,您可以使用许多求解器来求解。那么预测就像简单的评估一样f在某一点上并与z.

使用 Richard 的想法(请参阅他们在https://scicomp.stackexchange.com/a/40497/33791上的回答),我从您提供的图表中提取了点。我假设左下角是(0,0)右上角是(1,1). 如果不是,请考虑我使用的是标准化数据。

我在 MATLAB 中实现了我的想法,如果有兴趣,我可以清理并分享代码。这是结果:

区域内部为黄色,区域外部为蓝色。 绿色内'点和红色外'点。

我得到的表面可能有一些不受欢迎的特性。例如,数据并未表明右上角和左上角部分是内部的。但我的解决方案预测这些区域将位于该地区的内部。

另一方面,这种方法可以清楚地生成非常复杂的、非凸的和不连贯的区域,这很好。它还可以处理不确定性。例如,在我的模型中:

  • 如果f(x,y)ϵ>0然后(x,y)在里面,
  • 如果ϵ>f(x,y)>0然后(x,y)很可能在里面,
  • 如果0>f(x,y)>ϵ然后(x,y)很可能在外面,并且
  • 如果0>ϵf(x,y)然后(x,y)在外面。

您可以在绘图的工件中看到这一点,因为边界非常清晰。但是通过设置比我更大的容差,应该可以有更柔和的渐变,因此边界更模糊。