这是一个有趣的问题!有两件事使它特别具有挑战性:
- 我们应该如何比较两个点集?机器学习中的经典问题具有固定数量的属性,并且这些属性不可互换:例如,我可能拥有不同人的数据,具有属性
age
和height
(以厘米为单位)。每个样本都有一个条目,当然(age, height) = (22, 180)
与(age, height) = (180, 22)
. 你的问题也不是真的。一个点集有 3 到 10 个点,在比较两个点集时,我们输入点的顺序不应该有区别。
- 我们如何做出预测?假设我们找到了一种从我们的训练集中选择与上面的点集相似的点集的方法。我们面临的问题是我们的预测必须是您图片中的 7 个点之一;但是这些点中没有一个可能包含在相似的点集中。
让我概述一个处理这两个挑战的算法。预测精度不是很好;但也许你看到了如何改进它的方法。至少它预测了一些东西,对吧?
1. 模拟样品
为了能够测试算法,我编写了生成样本和标签的函数。
生成样本:
每个样本包含 3 到 10 个点。点的数量是随机的,从均匀分布中抽取。每个点的形式为(x_coordinate, y_coordinate)
。坐标也是随机的,从正态分布中得出。
import numpy as np
from random import randint
def create_samples(number_samples, min_points, max_points):
def create_single_sample(min_points, max_points):
n = randint(min_points, max_points)
return np.array([np.random.normal(size=2) for _ in range(n)])
return np.array([create_single_sample(min_points, max_points) for _ in range(number_samples)])
生成标签:作为一个玩具示例,让我们假设选择点的规则是:始终选择最接近 的点(0, 0)
,其中“最接近”应该根据欧几里得范数来理解。
def decision_function_minnorm(sample):
norms = np.apply_along_axis(np.linalg.norm, axis=1, arr=sample)
return sample[norms.argmin()]
def create_labels(samples, decision_function):
return np.array([decision_function(sample) for sample in samples])
我们现在可以创建我们的训练和测试集:
n_train, n_test = 1000, 100
dec_fun = decision_function_minnorm
X_train = create_samples(number_samples=n_train, min_points=3, max_points=10)
X_test = create_samples(number_samples=n_test, min_points=3, max_points=10)
y_train = create_labels(X_train, dec_fun)
y_test = create_labels(X_test, dec_fun)
2. 通过 Hausdorff 距离比较点集
让我们解决第一个问题:我们应该如何比较不同的点集?点集中的点数不同。还要记住,我们写下点的顺序无关紧要:与点集比较[(0,0), (1,1), (2,2)]
应该产生与与点集比较相同的结果[(2,2), (0,0), (1,1)]
。我的方法是通过它们的Hausdorff 距离来比较点集:
def hausdorff(A, B):
def dist_point_to_set(x, A):
return min(np.linalg.norm(x - a) for a in A)
def dist_set_to_set(A, B):
return max(dist_point_set(a, B) for a in A)
return max(dist_set_to_set(A, B), dist_set_to_set(B, A))
3.通过k近邻进行预测并取平均值
我们现在有了点集之间距离的概念。这使得使用 k 最近邻分类成为可能:给定一个测试点集,我们k
在训练样本中找到相对于测试点集具有最小 Hausdorff 距离的点集,并获得它们的标签。现在来了第二个问题:我们如何将这些k
标签变成对测试点集的预测?我采用了最简单的方法:平均标签并预测测试点集中最接近平均值的点。
def predict(x, num_neighbors):
# Find num_neighbors closest points in X_train.
distances_to_train = np.array([hausdorff(x, x_train) for x_train in X_train])
neighbors_idx = np.argpartition(distances_to_train, -num_neighbors)[-num_neighbors:]
# Get labels of the neighbors and calculate the average.
targets_neighbors = y_train[neighbors_idx]
targets_mean = sum(targets_neighbors) / num_neighbors
# Find point in x that is closest to targets_mean and use it as prediction.
distances_to_mean = np.array([np.linalg.norm(p - targets_mean) for p in x])
closest_point = x[distances_to_mean.argmin()]
return closest_point
4. 测试
一切就绪,可以测试我们算法的性能。
num_neighbors = 70
successes = 0
for i, x in enumerate(X_test):
print('%d/%d' % (i+1, n_test))
prediction = predict(x, num_neighbors)
successes += np.array_equal(prediction, y_test[i])
对于给定的决策函数 和num_neighbors = 70
,我们得到 84% 的预测准确率。这不是非常好,而且它当然是特定于我们的决策功能,这似乎很容易预测。
要看到这一点,请定义一个不同的决策函数:
decision_function_maxaverage(sample):
avgs = (sample[:, 0] + sample[:, 1]) / 2
return sample[norms.argmin()]
通过使用此功能可dec_fun = decision_function_maxaverage
将预测准确率降低到 45%。这表明考虑生成标签的决策规则是多么重要。如果您知道人们为什么选择某些点,这将帮助您找到最佳算法。
改进该算法的一些方法:(1) 使用不同的距离函数而不是 Hausdorff 距离,(2) 使用比 k 最近邻更复杂的方法,(3) 改进如何将所选训练标签转化为预测。