这是一个令人着迷的问题!有两点使它特别具有挑战性:
- 我们应该如何比较两个点集?机器学习中的经典问题具有固定数量的属性,并且这些属性不可互换:例如,我可能拥有关于具有属性
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)改进如何将所选训练标签转化为预测。