我有下表存储有关图像的数据:
images
- id (int)
- sample_1_1 (int)
- sample_1_2 (int)
- sample_1_3 (int)
- sample_2_1 (int)
- sample_2_2 (int)
- sample_2_3 (int)
- ... # Up until sample_25_3
任务是计算收集的数据之间的距离。目前,我正在使用75维(即正确,3 * 25 = 75)欧几里德距离计算编程为数据库中的存储过程:
CREATE DEFINER=`root`@`localhost`
FUNCTION `distanceBetween`(compareId INT, toId INT) RETURNS double
READS SQL DATA
DETERMINISTIC
BEGIN
DECLARE distance DOUBLE;
SELECT euclidDistance(
i1.sample_1_1, i1.sample_1_2, i1.sample_1_3,
i2.sample_1_1, i2.sample_1_2, i2.sample_1_3,
...
) INTO distance
FROM images i1, (SELECT * FROM images WHERE id = toId) i2
WHERE i1.id = compareId;
RETURN distance;
END
用另一个子程序计算2 75-dim之间的实际距离。载体:
CREATE DEFINER=`root`@`localhost`
FUNCTION `euclidDistance`(
img1_sample1_1 INT, img1_sample1_2 INT, img1_sample1_3 INT,
img2_sample1_1 INT, img2_sample1_2 INT, img2_sample1_3 INT,
...
) RETURNS double
RETURN SQRT(
quadDiff(img1_sample1_1, img2_sample1_1)
+ quadDiff(img1_sample1_2, img2_sample1_2)
+ quadDiff(img1_sample1_3, img2_sample1_3)
+ ...
)
另一个子程序来计算两个值之间的平方差:
CREATE DEFINER=`root`@`localhost`
FUNCTION `quadDiff`(var1 INT, var2 INT) RETURNS int(11)
RETURN POW(var1 - var2, 2)
函数本身非常精细,并且返回确定性结果,这些结果在数学上和逻辑上都是正确的。
当我想要将“最近”的图像传送到给定图像时出现问题 - 这意味着与任何给定图像的距离最小的图像。为此,我使用另一个程序:
CREATE DEFINER=`root`@`localhost`
PROCEDURE `getSimilarImages`(imageId INT, `limit` INT)
BEGIN
SELECT i2.id, i2.filename, distanceBetween(i1.id, i2.id) distance
FROM images i1, (SELECT * FROM images WHERE id != imageId AND duplicateImageId IS NULL) i2
WHERE i1.id = imageId
ORDER BY distance
LIMIT 10;
END
该数据库目前有大约30.000张图像。这意味着CALL getSimilarImages(123, 10);
需要大约 12 秒才能完成。对于任何应用程序来说,这都太长了,无论是基于Web还是基于应用程序。
所以,我想加快速度。我有什么选择?您是否认为有可能优化图像比较或计算距离的过程?
我想过缓存程序的结果,但我不知道如何这样做。我还可以在添加新图像时将每个图像与每个其他图像进行比较,但这会使图像添加一个非常长的过程,这也是不可接受的。
如果它有帮助,我可以提供有关系统设置的更多信息,但我感谢您提供的任何指示。目前的情况并不好,我真的需要做点什么,因为图像数据库只会随着系统每小时的增长而增长。
答案 0 :(得分:4)
正如您所发现的那样,您的ORDER BY距离(a,b)操作正在计算75维距离的全部,并且不出所料,这需要很长时间。它必须计算整个批次,以便它可以进行ORDER操作。哎哟。
以下是关于欧几里德距离的观察结果可能对您有所帮助:边界框是一个有用的近似值。当您使用GetSimilarImages时,您是否只包含与您正在使用的imageId的特定阈值距离内的图像?
假设您只关心imageId rad
距离内的图像。然后你可以像这样重写你的GetSimilarImages查询。
PROCEDURE `getSimilarImages`(imageId INT, `limit` INT, rad INT)
BEGIN
SELECT i2.id, i2.filename, distanceBetween(i1.id, i2.id) distance
FROM images i1,
(SELECT * FROM images WHERE id != imageId AND duplicateImageId IS NULL) i2
WHERE i1.id = imageId
AND i1.img_sample_1_1 BETWEEN i2.img_sample_1_1 - rad
AND i2.img_sample_1_1 + rad
AND i1.img_sample_1_2 BETWEEN i2.img_sample_1_2 - rad
AND i2.img_sample_1_2 + rad
AND i1.img_sample_1_3 BETWEEN i2.img_sample_1_3 - rad
AND i2.img_sample_1_3 + rad
ORDER BY distance
LIMIT 10;
END
在这个示例代码中,我随意选择了75个维度中的前三个用于边界框包含代码(三个BETWEEN
子句)。要使此优化起作用,您需要至少在用于包含边界框的一些维度上创建索引。
选择三个维度或选择任何特定维度没有什么特别之处。如果您从数据中了解到您的某些尺寸在您的图像之间有更好的区别,则可以选择这些尺寸。您可以根据需要选择多个尺寸。但是,当然还有索引开销。
答案 1 :(得分:1)
编写UDF C函数代码或调用调用GPU函数的本机C函数。
答案 2 :(得分:0)
此案例的优化提示:
在列id
和duplicateImageId
上添加索引,最好是clustured index
。
尽量避免在巨大的表images
上进行多次选择。
当您在函数内部调用函数时,可以通过减少函数调用次数来略微提高性能。所有这些函数调用都需要在内存堆栈中维护,这会消耗硬件资源。
在优化代码中进行的每次更改后的性能基准。
CREATE PROCEDURE getSimilarImages(imageId INT unsigned, limit INT unsigned)
BEGIN
SELECT i2.id, i2.filename, euclidDistance(
i1.sample_1_1, i1.sample_1_2, i1.sample_1_3,
i2.sample_1_1, i2.sample_1_2, i2.sample_1_3,
...
) AS distance
FROM images i1, (SELECT id, filename
FROM images
WHERE id <> imageId AND
duplicateImageId IS NULL
) i2
WHERE i1.id = imageId
ORDER BY distance
LIMIT 10;
END;
CREATE FUNCTION euclidDistance(
img1_sample1_1 INT, img1_sample1_2 INT, img1_sample1_3 INT,
img2_sample1_1 INT, img2_sample1_2 INT, img2_sample1_3 INT,
...
) RETURNS double
RETURN SQRT(
POW(img1_sample1_1 - img2_sample1_1, 2)
+ POW(img1_sample1_2 - img2_sample1_2, 2)
+ POW(img1_sample1_3 - img2_sample1_3, 2)
+ ...
);
希望这有帮助。