650. Drawing with LLMs | drawing-with-llms
首先,我们要感谢主办方持续努力改进评估指标,以确保顶级解决方案与比赛目标保持一致!
团队成员:
代码: https://github.com/bogoconic1/16th-place-kaggle-drawing-with-llms
总之,我们分配了 36 秒用于 SVG 生成和美学分数计算,剩余 27 秒用于候选选择。
我们尝试了许多开源 SD 模型(例如 Sana, SD 3.5 等),最终坚持使用 SDXL-Flash,因为它是能够生成符合我们预期的“好看”图像的最快模型。
使用默认设置 (1024x1024),SDXL-Flash 生成一张图像需要 8 秒。然而,我们通过生成较低分辨率的图像将速度提高了高达 80%。对于 512x512,我们能够在 30 秒内生成 18 张候选图像,且质量没有太大妥协。本地验证表明,512x512 或 640x640 在速度和准确性之间提供了最佳权衡。在 LB 上,704x704 表现也不错,但由于验证分数较低,我们没有选择它。
此外,我们使用了提示词 f'Cartoon style of "{description}".',因为我们注意到 DOODL 论文的封面显示图像在美学优化后似乎具有卡通风格。在检查 SVG 输出时发现,与普通图像相比,转换过程中的质量损失较小。
公开分享的 bitmap_to_svg_layered 技术每张图像运行约需 4.5 秒,并且在压缩到 10k 字节时有显著的信息丢失。
在进行函数剖析时,我们注意到 cv2 的 KMeans 是主要瓶颈。因此,我们使用 NVIDIA 的 GPU 版 cuML KMeans 加快了速度,这样每张图像转换为 SVG 仅需约 0.3 秒(15 倍加速)。
此外,为了在 10k 字节中塞入更多元素,我们嵌入了以下技术。
<polygon> 转换为 <path><g> 标签内)这使我们能够容纳 60 -> 168 个元素(平均),与公开分享的函数相比提高了 280%,且没有任何质量损失
使用了 @richolson 的 OCR 诱饵来减轻幻觉。我们没有做任何更改,因为 1/150 的失败率被认为是可接受的
为了加快本地美学计算,我们使用 cv2 加快了 apply_median_filter,并使用 NVIDIA CuPy 加快了 ImageProcessor 中的 apply_fft_low_pass(0.5 -> 0.3 秒/图像)
class FasterImageProcessor(metric.ImageProcessor):
def __init__(self, image: Image.Image, seed=None):
super().__init__(image, seed)
def apply_median_filter(self, size=9):
"""Fast median filter using OpenCV (≈10-20× PIL speed)."""
# Pillow → NumPy (BGR order is okay; cv2 works on uint8 directly)
img_array = np.asarray(self.image)
# OpenCV expects odd kernel sizes ≥3
if size % 2 == 0:
size += 1
filtered = cv2.medianBlur(img_array, ksize=size)
self.image = Image.fromarray(filtered)
return self
def apply_fft_low_pass(self, cutoff_frequency=0.5, device=0):
x = cp.asarray(self.image, dtype=cp.float32) # H×W×3
f = cp.fft.fftshift(cp.fft.fft2(x, axes=(0,1)), axes=(0,1))
rows, cols = x.shape[:2]
crow, ccol = rows // 2, cols // 2
r = int(min(crow, ccol) * cutoff_frequency)
y, xg = cp.ogrid[:rows, :cols]
mask = ((y - crow) ** 2 + (xg - ccol) ** 2) <= r * r
f *= mask[..., None] # broadcast over channels
img_back = cp.fft.ifft2(cp.fft.ifftshift(f, axes=(0,1)), axes=(0,1)).real
img_back = cp.clip(img_back, 0, 255).astype(cp.uint8).get()
self.image = Image.fromarray(img_back)
return self
我们通过问一个问题来估计 VQA 分数:'Does <image> portray "{}"? Answer yes or no.'
鉴于
| 方法 | 每张图像耗时 |
|---|---|
| 美学分数 | 0.3s |
| VQA + OCR 分数 | 3s |
我们没有估计所有图像的 VQA,而是采用了一种重排序方法来估计哪些图像更有可能在比赛指标上获得更高分数,这使我们在时间限制内能够生成更多候选 SVG。