我怎样才能使 "euclidean distance calculation" 在 MySQL 中更快?

How can I make "euclidean distance calculation" faster in MySQL?

我正在创建人脸识别系统,但搜索速度很慢。能否分享一下如何加快搜索速度?

100,000个数据项大约需要6秒。

mysql> SHOW VARIABLES LIKE '%version%';
+--------------------------+------------------------------+
| Variable_name            | Value                        |
+--------------------------+------------------------------+
| version                  | 8.0.29                       |
| version_comment          | MySQL Community Server - GPL |
| version_compile_machine  | x86_64                       |
| version_compile_os       | Linux                        |
+--------------------------+------------------------------+
CREATE TABLE `face_feature` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `f1` decimal(9,8) NOT NULL,
  `f2` decimal(9,8) NOT NULL,
  ...
  ...
  `f127` decimal(9,8) NOT NULL,
  `f128` decimal(9,8) NOT NULL,
  PRIMARY KEY (id)
);
mysql> SELECT count(*) FROM face_feature;
+----------+
| count(*) |
+----------+
|   110004 |
+----------+

mysql> SELECT * FROM face_feature LIMIT 1\G;
  id: 1
  f1: -0.07603023
  f2: 0.13605964
  ...
f127: 0.09608927
f128: 0.00082345
SELECT 
  id,
  sqrt(
    power(f1 - (-0.09077361), 2) +
    power(f2 - (0.10373443), 2) +
    ...
    ...
    power(f127 - (0.0778369), 2) +
    power(f128 - (0.00951046), 2)
  ) as distance
FROM 
  face_feature
ORDER BY 
  distance
LIMIT
  1
;
+----+--------------------+
| id | distance           |
+----+--------------------+
|  1 | 0.3376853491771237 |
+----+--------------------+
1 row in set (6.18 sec)

更新 1:

已从 decimal(9,8) 更改为 float(9,8)

然后,从approximately 4sec提高到3.26 sec

mysql> desc face_feature;
+-------+------------+------+-----+---------+----------------+
| Field | Type       | Null | Key | Default | Extra          |
+-------+------------+------+-----+---------+----------------+
| id    | int        | NO   | PRI | NULL    | auto_increment |
| f1    | float(9,8) | NO   |     | NULL    |                |
| f2    | float(9,8) | NO   |     | NULL    |                |
..
| f127  | float(9,8) | NO   |     | NULL    |                |
| f128  | float(9,8) | NO   |     | NULL    |                |
+-------+------------+------+-----+---------+----------------+

更新 2:

已从 POWER(z, 2) 更改为 z*z

然后,结果由3.26 sec改为4.65 sec

SELECT 
  id,
  sqrt(
    ((f1 - (-0.09077361)) * (f1 - (-0.09077361))) +
    ((f2 - (0.10373443)) * (f2 - (0.10373443))) +
    ((f3 - (0.00798536)) * (f3 - (0.00798536))) +
    ...
    ...
    ((f126 - (0.07803915)) * (f126 - (0.07803915))) +
    ((f127 - (0.0778369)) * (f127 - (0.0778369))) +
    ((f128 - (0.00951046)) * (f128 - (0.00951046))
  ) as distance
FROM 
  face_feature
ORDER BY 
  distance
LIMIT
  1
;

更新 3

我正在研究 MySQL GIS 的用法。


更新 4

我也在看 PostgreSQL,因为我找不到在 MySQL.

中处理 128 个维度的方法
  • DECIMAL(9,8) -- 这是很多有效数字。需要那么精确吗?

  • FLOAT -- 约7位有效数字;更快的算法。

  • POWER(z, 2) -- 可能比 z*z 慢很多。 (这可能是最慢的部分。)

  • SQRT -- 在很多情况下,您可以简单地使用正方形。在这种情况下:

    SELECT SQRT(closest)
        FROM ( SELECT -- leave out SQRT
                 ... ORDER BY .. LIMIT 1 )
    

这里有一些其他的想法。它们不一定与正在讨论的查询相关:

  • 精确测试 -- 小心比较 'equal' 舍入误差很可能使事情出乎意料地不相等。不精确的测量增加了这个问题。如果我测量两次,我可能会得到一次 1.23456789 和下一次 1.23456788。 (特别是在那种“精度”水平上。

  • Trade complexity vs speed -- 使用ABS(a - b)作为距离公式;以这种方式找到最接近的 10 个项目,然后使用欧几里德距离获得 'right' 距离。

  • 把脸分成几个区域。查找项目所在的区域,然后仅检查该区域中 128 个点的子集。 (靠近边界 -- 在两个区域中放置一些点。)

  • 跳出框框思考 -- 我不熟悉你的面部识别,所以我 运行 没有数学技巧。

  • 切换到 POINTsSPATIAL 索引。您的任务可能会快几个数量级。 (这对于 128 维 space 可能不实用。)