KNN(K-Nearest Neighbors)算法是经典的人工智能算法之一,它通过寻找最近的邻居来实现分类或相似性查找。KNN算法在处理低维小数据集时表现出色,但随着数据维度的增加或数据量的增大,其效率急剧下降。维度的增加导致所谓的“维度灾难”,而数据点数量的增加则导致测试时间变得异常长。本文将不探讨维度灾难,而是聚焦于如何将KNN算法应用于大数据集。
深入分析KNN的时间复杂度,发现KNN的训练时间为O(1),而测试时间为O(nd*m)(其中n代表训练样本数量,d代表数据维度,m代表测试样本数量)。对于大数据集来说,这个时间复杂度会急剧增加。以测试为例,一个训练样本大小为(240000 x 728)和一个测试样本大小为(40000 x 728),计算测试集中每个向量与训练集中每个向量的欧几里得距离需要大约12小时。这突显了寻找更优策略来计算最近邻居的重要性。本文讨论了一种通过近似距离计算来找到最近邻居的策略,即产品量化,它利用查找表的概念,从而节省了冗余计算并大幅提升了速度。
要识别K的最近邻居,首先需要创建一个距离矩阵,其中计算每个测试向量与每个训练向量的距离。然后,这个矩阵可以用来选择具有最低值的K个最近邻居。需要的是类似于下图所示的距离矩阵。初步看来,要计算这个矩阵,需要运行两个循环;一个循环遍历训练样本,另一个循环遍历测试样本。
for test_sample in Test {
for ele in train {
dist[i][j] = dist(ele, test_sample)
}
}
但是,在小标题中提到,这个问题可以不使用任何循环来解决。让回顾一些基本的数学知识来解决这个问题,而不需要循环。欧几里得距离的计算公式为:Sqrt((a1-a2)² + (b1-b2)² + … + (n1-n2)²)。为了填充上述矩阵,可以工作在平方距离上,因为目标是找到最近的邻居,而不是在线性尺度上计算距离。
让使用一些基本的数学来修改距离方程:(a-b)² = a² + b² - 2ab。有趣的是,Python可以通过NumPy比使用循环更快地执行乘法运算。在系统上,对于两个随机矩阵的大小为(100×100),使用循环需要0.47秒,而直接矩阵乘法只需要0.003秒。这意味着矩阵乘法在不使用循环的情况下快了156倍。上述公式也可以很容易地应用于矩阵。下面给出了计算距离矩阵的最快方法。
def compute_distances_no_loops(self, X):
dists = -2 * np.dot(X, self.X_train.T) + np.sum(self.X_train**2, axis=1) + np.sum(X**2, axis=1)[:, np.newaxis]
return dists
这种方法是完美的,但对于大数据集来说,由于极高的内存需求,这是不切实际的。训练样本大小为(240000 x 728),测试样本大小为(40000 x 728),需要72GB的RAM来计算距离矩阵。
可以通过在测试向量的每个样本上运行循环来解决内存问题,将每个测试向量视为一个完整的矩阵,并使用上述公式来计算距离。
def compute_distances_one_loop(self, X):
dists = np.zeros((num_test, num_train))
for i in xrange(num_test):
dists[i, :] = np.sum((self.X_train - X[i, :])**2, axis=1)
return dists
上述技术可能适用于相当大的数据集,但当涉及到实际拥有数十亿行的数据集时,使用这些技术变得不可能。这时,产品量化技术应运而生,用于近似最近邻居。
以下是需要遵循的步骤:
有些人可能仍在思考这如何节省时间。它节省时间是因为它只需要将块与中心点进行比较以分配ID,然后使用距离图来计算距离,距离图的时间复杂度为O(1)。让为情况做一些计算。