PixGrid 测试算法设计复盘

Last updated on March 12, 2024 am

今晚处理完毕了 PixGrid 生产服务的最后一部分补强加固。作为 Shugetsu Soft 内部短期孵化机制成功进阶 Incubating 的项目,我们对这个项目在我们已有的 Pixiv 插画数据库基础上深度发掘所能产生的价值抱有厚望。

这篇文章主要对成品展品回报率的内部量化测试流程进行开发复盘;对于主项目的架构和开发复盘,敬请关注 Rorical 的博客。

Image Retrieval

以图搜图任务在学术上被归类为“Image Retrieval”,即“以给定输入查找图像信息”。有别于“查找图像文件”,其重点是算法对图像内容的抽象理解和特征提取。本领域除了“以图搜图”,还包含“以文搜图”等其他类型信息的转译。

Attack & Defence

针对 Image Retrieval 模型的“攻击”本质上是在不断劣化输入图片的过程中优化模型特征提取的准确性。考虑到模型应用的实用性,攻击算法通常会模仿常见的图片劣化路径。本次测试中,我们所设计的攻击路径如下:

  • 去色(grayscale):将图片直接做灰阶处理。考验模型对非色彩的高频细节抽象能力;
  • 高斯模糊(gaussian_blur):使用对称高斯核,标准差 1.5 像素对图片进行高斯模糊。模拟图片升采样过程中传统方法所造成的高频细节丢失;
  • 亮度偏移(brightness_shift):对图片的每个色彩通道增加 0.25 倍的亮度。丢弃亮区的色彩细节;
  • 灰滤镜(alpha_blend):向图片上叠加透明度 25%,色彩为 # CDD0D3 的颜色层。模拟通过拍摄/扫描所得到图片褪色/洗色的情况;
  • JPEG 压缩(jpeg_compression):质量为 40% 的标准 JPEG 压缩。常见的有损过度处理;
  • 像素翻转(bit_flip):以 5% 的概率随机对图片像素进行色彩翻转。模拟图片损坏的情况;
  • 黑白噪点(salt_and_pepper_noise):以 5% 的概率随机将图片像素设置为纯白或纯黑色。模拟图片损坏的情况;
  • 随机裁切(random_crop):将图片随机裁切至原大小的 65% - 85% 之间。常见的图片信息损失;
  • 摩尔纹 (colored_moire):对 RGB 通道进行错频,叠加一个错角度的交错黑白滤镜,再使用双线性降采样还原图片。会在原图上叠加一层独特的高频色彩纹理,模拟对光栅显示屏拍摄时产生的摩尔纹干扰现象;
    • 步骤1
      所用的蒙版
      步骤2
      步骤3
  • Bad Skia 算法错误(bad_skia):即经典的 “压图变绿”问题。根源在于 JPEG 压缩中, RGB 转 YUV 时精度不足导致的错误舍入。核心的错误算法模拟(需要在 uint8 像素上进行计算):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # Convert to int32 to prevent overflow during calculations
    r, g, b = r.astype(np.int32), g.astype(np.int32), b.astype(np.int32)

    # YUV to RGB conversion

    y = np.clip(((77 * r + 150 * g + 29 * b) >> 8), 0, 255)
    u = np.clip((((-43 * r - 85 * g + 128 * b) >> 8) - 1), -128, 127)
    v = np.clip((((128 * r - 107 * g - 21 * b) >> 8) - 1), -128, 127)

    # RGB to YUV conversion
    r1 = np.clip(((65536 * y + 91881 * v) >> 16), 0, 255)
    g1 = np.clip(((65536 * y - 22553 * u - 46802 * v) >> 16), 0, 255)
    b1 = np.clip(((65536 * y + 116130 * u) >> 16), 0, 255)

Architecture

架构上没什么好说的,使用 Python 实现主要是因为方便。由于查询本身相对很慢(过网络栈,内部又是一个遍历向量数据库的查询,因为内存不足查询操作还要落盘),生成测试样例的倍数增长又会让一个适中的测试样本变得很大,因此做了多线程处理,每个测试单元的所有变种都是并发发出,测试单元又维持在一个并发测试池中滚动收集数据。Python 写多线程真的非常非常恶心,不仅是难以实现,还有难以 Debug 的问题,写起来真的很反人类。

生成测试样例的流程部分用了统一结构,可以挂载任意相同结构的处理函数,主测试脚本会自动遍历流程类下的所有测试函数进行测试。为了尽量快的运行一些样例生成函数,所有的图片运算都用了 NumPy,尽可能使用并行写入像素而非遍历像素,几个关键的热点函数从分钟级处理降低到了秒级。结合并发测试池,平均每个测试组的回收速度比单个测试的极限时间还要快。

Result

以下是程序的原始输出(套了一个 time 计算总时长):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
This test runs through 832 units of test. The result is:
Process Success Rate Average Time
--------------------- -------------- --------------
alpha_blend 82.33% 4666.55 ms
bad_skia 48.08% 5432.43 ms
bit_flip 73.92% 5607.46 ms
brightness_shift 80.05% 5326.15 ms
colored_moire 78.73% 5413.38 ms
gaussian_blur 83.29% 5103.56 ms
grayscale 4.93% 5101.79 ms
jpeg_compression 82.81% 5020.87 ms
random_crop 52.64% 5227.77 ms
salt_and_pepper_noise 54.21% 5495.04 ms
vanilla 83.53% 5366.52 ms

real 112m13.582s
user 25m8.228s
sys 22m28.905s

总体来讲其实我不是特别满意。但是作为一个孵化中的项目,我们并没有下真正的努力在优化/微调模型上(并且也没有用于训练的资金——我们在考虑下一步使用 Google 的 Academic TPU Pod 名额),作为一个未经修改的 CLIP 模型应用,对于特定场景(例如切图、偏色等)的准确度高于传统方案(例如 IQDB 或 SauceNAO)已经非常实用了。

我们有注意到去色处理异常的识别率。我们推测这是因为 CLIP 模型训练中强依赖于色彩抽象而非结构抽象所致。这个问题应该也会在项目的下一阶段加以解决。


PixGrid 测试算法设计复盘
http://elfile4138.moe/2024/03/PixGrid-Test/
Author
Matrew File
Posted on
March 11, 2024
Updated on
March 12, 2024
Licensed under