{
  "id": 581128,
  "title": "5th place solution - VTracer and DiffVG optimization",
  "content": "<p>I want to thank the organizers for providing such an interesting and challenging competition, especially since the metric is really tricky and so much thought had to be put into it.</p>\n<p>My final solution is based on generating a bunch of images with SDXL-turbo, converting them to SVGs, and then adding an optimized patch to significantly improve the aesthetic score. After that, I use a proxy function for the VQA model to find the best SVG to use in the end.</p>\n<p>Adding this to the end of your SVG file will improve the <em>average</em> aesthetic score to around <strong>0.7</strong>, almost independently of the background:</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F4364590%2F05dfa30c725791cd73d471f62b9d6ba2%2FScreenshot%202025-05-28%20at%2010.34.11.png?generation=1748439279305351&amp;alt=media\" alt=\"Aesthetic patch - 0.7 score\"></p>\n<p>The VQA score will suffer by adding this patch, partly because it uses around 3.5k bytes which cannot be used further in the original image conversion and also because it is a big chunk of noise in your image. However, the overall final score will be higher by quite a bit, especially when you can select which image to use in order to not hurt the VQA score too much.</p>\n<p>Final solution <a href=\"https://www.kaggle.com/code/tomirol/fork-of-sdxl-turbo-vtracer-multi-gpu-10b-448-24x?scriptVersionId=242123343\" target=\"_blank\">here</a> and Github with all the codebase <a href=\"https://github.com/matheuspf/draw\" target=\"_blank\">here</a>.</p>\n<p>Example:</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F4364590%2Fa759e14a2c26c782f6029632bab5839b%2Fkaggle_disp_aest.png?generation=1748437567456130&amp;alt=media\" alt=\"With and without aesthetic patch score comparison\"></p>\n<h4>Generating images</h4>\n<p>Nothing special here, I just used SDXL-turbo to generate a bunch of images. I tried multiple other models, finetuning SDXL-turbo to a preference dataset using the competition score, finetuning using REFL with <a href=\"https://github.com/THUDM/ImageReward\" target=\"_blank\">image-reward</a>, DPO with VQA as reward, etc.</p>\n<p>The best overall generation I could get was using SDXL-turbo with a set of prompts I selected via beam search in a generated dataset. Basically, I ran around 200 different prompts in a dataset and picked the subset that generated the best images when ensembled together. The more prompts I added, the better the results, even when using 20+ prompts.</p>\n<p>Most prompts focus on simple, SVG-like images that can be readily converted to SVG using Vtracer. Also, explicitly adding <code>no text</code> and <code>no other colors</code> to my negative prompts consistently improved the results a little bit.</p>\n<h4>Raster to vector</h4>\n<p>Initially, I was using <a href=\"https://github.com/fogleman/primitive\" target=\"_blank\">primitive</a> to convert the images to SVGs. I still believe the images generated by primitive can be much better than Vtracer when tuned properly, but the CPUs on Kaggle are not good enough to run it on time: it took over 30 seconds to generate 50 SVG <code>&lt;paths&gt;</code> for a single image using <em>all cores</em> (<code>primitive</code> is written in GO and is heavily parallelized).</p>\n<p>On the other hand, Vtracer was able to convert an image using a <em>single core</em> in less than 2 seconds, while the results were almost as good. After adding some polygon simplification, size clipping, and tuning the parameters a bit, I was able to generate good enough SVGs with around 6k bytes per image. Finally, I added a small white OCR decoy with a black background, so it could not be missed.</p>\n<h4>Selecting the best image</h4>\n<p>After generating all the SVGs, the selection of the best one is done using a proxy function over the VQA score. I simply followed the method described in <a href=\"https://arxiv.org/abs/2404.01291\" target=\"_blank\">this paper</a> and used the <strong>\"Yes\"</strong> vs <strong>\"No\"</strong> probability for this question: <code>\"f&lt;image&gt;answer en Question: Does this image contain {description}? Answer Yes or No.\\nAnswer:\"</code>.</p>\n<p>This actually turned out to work better than generating questions, choices, and answers using Qwen-2.5 and running the original VQA score. I believe this is because the proxy function is much more robust when we can only evaluate a few questions, but I could be wrong and my question generation model was just bad.</p>\n<p>Although using other model versions (3b 224, 10b 224 and 3b 448) also correlated with the main metric, the original VQA model (10b 448) was always better, even if much slower and using less prompts because of that.</p>\n<h4>Optimizing the pipeline</h4>\n<p>After I noticed ensembling would be quite powerful, I had to find a way to efficiently use all Kaggle resources. I was heavily underusing GPUs because Vtracer is CPU bound, and Kaggle machines have <strong>really</strong> slow CPUs. For example, generating images with SDXL-turbo was actually faster than running Vtracer on a single thread.</p>\n<p>In the end, I was able to use almost 100% of the resources by running three processes in the background:</p>\n<ul>\n<li>Diffusion model on <code>cuda:0</code></li>\n<li>Vtracer on <code>cpu</code></li>\n<li>Proxy function on <code>cuda:1</code></li>\n</ul>\n<p>and then controlling the flow within the main notebook. For example, whenever a few images are ready from SDXL, we can start converting while we keep generating more images in parallel. The same for scoring when a few SVGs are ready.</p>\n<p>This way, I was able to run <strong>28</strong> different SVGs, but I believe I could push this a bit more because my solution was running in around 55 seconds per example on average.</p>\n<h4>Finding the aesthetic patch</h4>\n<p>In order to find the patch, I built a fully differentiable version of every step of the preprocessing, aesthetic model, and VQA. With this differentiable pipeline, I tried so many things:</p>\n<ul>\n<li>Optimize full score directly with DiffVG (SVG -&gt; score directly)</li>\n<li>Add text to the image and <strong>reduce</strong> the VQA OCR score in order to <em>hide</em> it</li>\n<li>Find a small subset of paths which will optimize the aesthetic score alongside a dataset</li>\n<li>Optimize a proxy function (yes probability) with the VQA model to find the best SVG to use</li>\n<li>Plug the full function (VQA + aesthetic) into a method like <a href=\"https://arxiv.org/abs/2406.04312\" target=\"_blank\">ReNO</a> to optimize images from SDXL-turbo directly</li>\n<li>Directly finetune a Diffusion model using a similar method to <a href=\"https://github.com/THUDM/ImageReward\" target=\"_blank\">image-reward</a> but with the differentiable VQA loss</li>\n</ul>\n<p>All of those solutions <em>do work</em>, but they are either too slow, require too much GPU memory to run on Kaggle, generate SVGs way over 10kb, or are just too unstable to make sure they will work every time. In the end, I tried to do something similar to what other people did in optimizing directly over the generated SVG, but could not get a proxy function over the VQA score to work well unfortunately =/</p>\n<p>The best solution I could find was to just search a small subset of paths that, when added over the background of the actual scene, will improve the average aesthetic score in a dataset. Due to all the preprocessing and especially cropping, the aesthetic score varies quite a bit from image to image, but the average over a significant amount of images will improve measurably. Also, because of the cropping, the position of the patch cannot be so close to the edges; otherwise, the score will be much lower.</p>\n<p>Because the patch uses a lot of bytes, I had to balance patch size and VQA score. The bigger the patch, the less information VTracer could use to convert the SVG. I was able to get patches with over <strong>0.8</strong> aesthetic score by using much more bytes, but then the VQA hit would be too high.</p>\n<p>My final patch has around 100x100 pixels and was found using a simple gradient ascent over the aesthetic score on a small dataset of 150 images. I selected a bunch of background from a previous submission, started the SVG from scratch in a grid like pattern of 96x96 pixels, and run gradient ascent. This <a href=\"https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685\" target=\"_blank\">notebook</a> shows the process, it took a few hours to find a good aesthetic patch and it is extremely sensitive on where in the image it is.</p>\n<h4>Bonus</h4>\n<p>I think the nicest thing I tried was to plug the differentiable pipeline into <a href=\"https://arxiv.org/abs/2406.04312\" target=\"_blank\">ReNO</a> and optimize the diffusion process directly against the full loss. It generated some really nice images and was actually significantly better than a single run of SDXL-turbo, but it was very inefficient and did not work as well as the simple ensembling I did in the end. Also, I could only use the 3B-224 version of the VQA, since the bigger one would not run with gradients enabled on a T4 with 16GB of memory, so the loss did not perfectly correlate with the competition score.</p>\n<p><a href=\"https://www.kaggle.com/code/tomirol/fork-of-sdxl-turbo-reno-vqa?scriptVersionId=242363815\" target=\"_blank\">This notebook</a> shows the improvement from the first generated image from SDXL and the images in between, optimizing the metric alongside the way on the diffusion process itself.</p>",
  "comments": [
    {
      "id": 3213648,
      "author_name": "BeaKaggle",
      "author_url": "",
      "post_date": "2025-05-30T09:31:22.563000",
      "content": "<p>Nice work and explanation <a href=\"https://www.kaggle.com/tomirol\" target=\"_blank\">@tomirol</a>  Thanks.   The link to your notebook for  gradient ascent finding aesthetic patch does not work. Would you provide that notebook? Thank you</p>",
      "votes": 1,
      "replies": [
        {
          "id": 3213772,
          "author_name": "matheus",
          "author_url": "",
          "post_date": "2025-05-30T12:21:07.657000",
          "content": "<p>Hey thanks for pointing it out, just made it public now.</p>",
          "votes": 0,
          "replies": [
            {
              "id": 3214024,
              "author_name": "BeaKaggle",
              "author_url": "",
              "post_date": "2025-05-30T19:07:34.513000",
              "content": "<p>Thanks <a href=\"https://www.kaggle.com/tomirol\" target=\"_blank\">@tomirol</a> </p>",
              "votes": 1,
              "replies": []
            }
          ]
        }
      ]
    },
    {
      "id": 3213344,
      "author_name": "Blansteinia454",
      "author_url": "",
      "post_date": "2025-05-29T21:00:07.970000",
      "content": "<p>now i try make something similar, thnx 4 this</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3212256,
      "author_name": "Md Feroz Ahmed",
      "author_url": "",
      "post_date": "2025-05-29T15:03:49.940000",
      "content": "<p>A very interesting solution .</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 3211745,
      "author_name": "Mikhail Golubchik",
      "author_url": "",
      "post_date": "2025-05-28T19:14:56.137000",
      "content": "<p>A very interesting solution with excelent scor. And thank you for sharing the notebook. The girl in the picture turned out very nice.</p>\n<p>I also tried to somehow improve the aesthetic score. I was trying to pick out certain shapes within a broad frame of the image, but it didn’t work out well. The VQA score dropped significantly, and the increase in AES didn’t make up for it. <br>\nYour approach to increasing aesthetic score is great</p>",
      "votes": 2,
      "replies": [
        {
          "id": 3211777,
          "author_name": "matheus",
          "author_url": "",
          "post_date": "2025-05-28T20:30:35.327000",
          "content": "<p>Hey thanks man! It was quite tricky finding the position to put the patch, because it gets a low score on the borders (due to cropping augmentation) but will lower the VQA score on the center.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 3217666,
      "author_name": "BeaKaggle",
      "author_url": "",
      "post_date": "2025-06-05T09:26:41.110000",
      "content": "<p>Hi <a href=\"https://www.kaggle.com/tomirol\" target=\"_blank\">@tomirol</a>  in order to run your diffvg optimization notebook <a href=\"https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685\" target=\"_blank\">https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685</a> , would you mind to  make the private datasets public? Thank you</p>",
      "votes": 0,
      "replies": [
        {
          "id": 3217962,
          "author_name": "matheus",
          "author_url": "",
          "post_date": "2025-06-05T15:41:41.487000",
          "content": "<p>Hey sorry for the trouble - all datasets should be public now. Thanks for letting me know!</p>",
          "votes": 0,
          "replies": []
        }
      ]
    }
  ],
  "comment_count": 9,
  "messages": [
    {
      "id": 3211481,
      "postDate": "2025-05-28T13:33:35.333Z",
      "content": "<p>I want to thank the organizers for providing such an interesting and challenging competition, especially since the metric is really tricky and so much thought had to be put into it.</p>\n<p>My final solution is based on generating a bunch of images with SDXL-turbo, converting them to SVGs, and then adding an optimized patch to significantly improve the aesthetic score. After that, I use a proxy function for the VQA model to find the best SVG to use in the end.</p>\n<p>Adding this to the end of your SVG file will improve the <em>average</em> aesthetic score to around <strong>0.7</strong>, almost independently of the background:</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F4364590%2F05dfa30c725791cd73d471f62b9d6ba2%2FScreenshot%202025-05-28%20at%2010.34.11.png?generation=1748439279305351&amp;alt=media\" alt=\"Aesthetic patch - 0.7 score\"></p>\n<p>The VQA score will suffer by adding this patch, partly because it uses around 3.5k bytes which cannot be used further in the original image conversion and also because it is a big chunk of noise in your image. However, the overall final score will be higher by quite a bit, especially when you can select which image to use in order to not hurt the VQA score too much.</p>\n<p>Final solution <a href=\"https://www.kaggle.com/code/tomirol/fork-of-sdxl-turbo-vtracer-multi-gpu-10b-448-24x?scriptVersionId=242123343\" target=\"_blank\">here</a> and Github with all the codebase <a href=\"https://github.com/matheuspf/draw\" target=\"_blank\">here</a>.</p>\n<p>Example:</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F4364590%2Fa759e14a2c26c782f6029632bab5839b%2Fkaggle_disp_aest.png?generation=1748437567456130&amp;alt=media\" alt=\"With and without aesthetic patch score comparison\"></p>\n<h4>Generating images</h4>\n<p>Nothing special here, I just used SDXL-turbo to generate a bunch of images. I tried multiple other models, finetuning SDXL-turbo to a preference dataset using the competition score, finetuning using REFL with <a href=\"https://github.com/THUDM/ImageReward\" target=\"_blank\">image-reward</a>, DPO with VQA as reward, etc.</p>\n<p>The best overall generation I could get was using SDXL-turbo with a set of prompts I selected via beam search in a generated dataset. Basically, I ran around 200 different prompts in a dataset and picked the subset that generated the best images when ensembled together. The more prompts I added, the better the results, even when using 20+ prompts.</p>\n<p>Most prompts focus on simple, SVG-like images that can be readily converted to SVG using Vtracer. Also, explicitly adding <code>no text</code> and <code>no other colors</code> to my negative prompts consistently improved the results a little bit.</p>\n<h4>Raster to vector</h4>\n<p>Initially, I was using <a href=\"https://github.com/fogleman/primitive\" target=\"_blank\">primitive</a> to convert the images to SVGs. I still believe the images generated by primitive can be much better than Vtracer when tuned properly, but the CPUs on Kaggle are not good enough to run it on time: it took over 30 seconds to generate 50 SVG <code>&lt;paths&gt;</code> for a single image using <em>all cores</em> (<code>primitive</code> is written in GO and is heavily parallelized).</p>\n<p>On the other hand, Vtracer was able to convert an image using a <em>single core</em> in less than 2 seconds, while the results were almost as good. After adding some polygon simplification, size clipping, and tuning the parameters a bit, I was able to generate good enough SVGs with around 6k bytes per image. Finally, I added a small white OCR decoy with a black background, so it could not be missed.</p>\n<h4>Selecting the best image</h4>\n<p>After generating all the SVGs, the selection of the best one is done using a proxy function over the VQA score. I simply followed the method described in <a href=\"https://arxiv.org/abs/2404.01291\" target=\"_blank\">this paper</a> and used the <strong>\"Yes\"</strong> vs <strong>\"No\"</strong> probability for this question: <code>\"f&lt;image&gt;answer en Question: Does this image contain {description}? Answer Yes or No.\\nAnswer:\"</code>.</p>\n<p>This actually turned out to work better than generating questions, choices, and answers using Qwen-2.5 and running the original VQA score. I believe this is because the proxy function is much more robust when we can only evaluate a few questions, but I could be wrong and my question generation model was just bad.</p>\n<p>Although using other model versions (3b 224, 10b 224 and 3b 448) also correlated with the main metric, the original VQA model (10b 448) was always better, even if much slower and using less prompts because of that.</p>\n<h4>Optimizing the pipeline</h4>\n<p>After I noticed ensembling would be quite powerful, I had to find a way to efficiently use all Kaggle resources. I was heavily underusing GPUs because Vtracer is CPU bound, and Kaggle machines have <strong>really</strong> slow CPUs. For example, generating images with SDXL-turbo was actually faster than running Vtracer on a single thread.</p>\n<p>In the end, I was able to use almost 100% of the resources by running three processes in the background:</p>\n<ul>\n<li>Diffusion model on <code>cuda:0</code></li>\n<li>Vtracer on <code>cpu</code></li>\n<li>Proxy function on <code>cuda:1</code></li>\n</ul>\n<p>and then controlling the flow within the main notebook. For example, whenever a few images are ready from SDXL, we can start converting while we keep generating more images in parallel. The same for scoring when a few SVGs are ready.</p>\n<p>This way, I was able to run <strong>28</strong> different SVGs, but I believe I could push this a bit more because my solution was running in around 55 seconds per example on average.</p>\n<h4>Finding the aesthetic patch</h4>\n<p>In order to find the patch, I built a fully differentiable version of every step of the preprocessing, aesthetic model, and VQA. With this differentiable pipeline, I tried so many things:</p>\n<ul>\n<li>Optimize full score directly with DiffVG (SVG -&gt; score directly)</li>\n<li>Add text to the image and <strong>reduce</strong> the VQA OCR score in order to <em>hide</em> it</li>\n<li>Find a small subset of paths which will optimize the aesthetic score alongside a dataset</li>\n<li>Optimize a proxy function (yes probability) with the VQA model to find the best SVG to use</li>\n<li>Plug the full function (VQA + aesthetic) into a method like <a href=\"https://arxiv.org/abs/2406.04312\" target=\"_blank\">ReNO</a> to optimize images from SDXL-turbo directly</li>\n<li>Directly finetune a Diffusion model using a similar method to <a href=\"https://github.com/THUDM/ImageReward\" target=\"_blank\">image-reward</a> but with the differentiable VQA loss</li>\n</ul>\n<p>All of those solutions <em>do work</em>, but they are either too slow, require too much GPU memory to run on Kaggle, generate SVGs way over 10kb, or are just too unstable to make sure they will work every time. In the end, I tried to do something similar to what other people did in optimizing directly over the generated SVG, but could not get a proxy function over the VQA score to work well unfortunately =/</p>\n<p>The best solution I could find was to just search a small subset of paths that, when added over the background of the actual scene, will improve the average aesthetic score in a dataset. Due to all the preprocessing and especially cropping, the aesthetic score varies quite a bit from image to image, but the average over a significant amount of images will improve measurably. Also, because of the cropping, the position of the patch cannot be so close to the edges; otherwise, the score will be much lower.</p>\n<p>Because the patch uses a lot of bytes, I had to balance patch size and VQA score. The bigger the patch, the less information VTracer could use to convert the SVG. I was able to get patches with over <strong>0.8</strong> aesthetic score by using much more bytes, but then the VQA hit would be too high.</p>\n<p>My final patch has around 100x100 pixels and was found using a simple gradient ascent over the aesthetic score on a small dataset of 150 images. I selected a bunch of background from a previous submission, started the SVG from scratch in a grid like pattern of 96x96 pixels, and run gradient ascent. This <a href=\"https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685\" target=\"_blank\">notebook</a> shows the process, it took a few hours to find a good aesthetic patch and it is extremely sensitive on where in the image it is.</p>\n<h4>Bonus</h4>\n<p>I think the nicest thing I tried was to plug the differentiable pipeline into <a href=\"https://arxiv.org/abs/2406.04312\" target=\"_blank\">ReNO</a> and optimize the diffusion process directly against the full loss. It generated some really nice images and was actually significantly better than a single run of SDXL-turbo, but it was very inefficient and did not work as well as the simple ensembling I did in the end. Also, I could only use the 3B-224 version of the VQA, since the bigger one would not run with gradients enabled on a T4 with 16GB of memory, so the loss did not perfectly correlate with the competition score.</p>\n<p><a href=\"https://www.kaggle.com/code/tomirol/fork-of-sdxl-turbo-reno-vqa?scriptVersionId=242363815\" target=\"_blank\">This notebook</a> shows the improvement from the first generated image from SDXL and the images in between, optimizing the metric alongside the way on the diffusion process itself.</p>",
      "rawMarkdown": "I want to thank the organizers for providing such an interesting and challenging competition, especially since the metric is really tricky and so much thought had to be put into it.\n\nMy final solution is based on generating a bunch of images with SDXL-turbo, converting them to SVGs, and then adding an optimized patch to significantly improve the aesthetic score. After that, I use a proxy function for the VQA model to find the best SVG to use in the end.\n\nAdding this to the end of your SVG file will improve the *average* aesthetic score to around **0.7**, almost independently of the background:\n\n![Aesthetic patch - 0.7 score](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F4364590%2F05dfa30c725791cd73d471f62b9d6ba2%2FScreenshot%202025-05-28%20at%2010.34.11.png?generation=1748439279305351&alt=media)\n\nThe VQA score will suffer by adding this patch, partly because it uses around 3.5k bytes which cannot be used further in the original image conversion and also because it is a big chunk of noise in your image. However, the overall final score will be higher by quite a bit, especially when you can select which image to use in order to not hurt the VQA score too much.\n\nFinal solution [here](https://www.kaggle.com/code/tomirol/fork-of-sdxl-turbo-vtracer-multi-gpu-10b-448-24x?scriptVersionId=242123343) and Github with all the codebase [here](https://github.com/matheuspf/draw).\n\nExample:\n\n![With and without aesthetic patch score comparison](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F4364590%2Fa759e14a2c26c782f6029632bab5839b%2Fkaggle_disp_aest.png?generation=1748437567456130&alt=media)\n\n#### Generating images\n\nNothing special here, I just used SDXL-turbo to generate a bunch of images. I tried multiple other models, finetuning SDXL-turbo to a preference dataset using the competition score, finetuning using REFL with [image-reward](https://github.com/THUDM/ImageReward), DPO with VQA as reward, etc.\n\nThe best overall generation I could get was using SDXL-turbo with a set of prompts I selected via beam search in a generated dataset. Basically, I ran around 200 different prompts in a dataset and picked the subset that generated the best images when ensembled together. The more prompts I added, the better the results, even when using 20+ prompts.\n\nMost prompts focus on simple, SVG-like images that can be readily converted to SVG using Vtracer. Also, explicitly adding `no text` and `no other colors` to my negative prompts consistently improved the results a little bit.\n\n#### Raster to vector\n\nInitially, I was using [primitive](https://github.com/fogleman/primitive) to convert the images to SVGs. I still believe the images generated by primitive can be much better than Vtracer when tuned properly, but the CPUs on Kaggle are not good enough to run it on time: it took over 30 seconds to generate 50 SVG `<paths>` for a single image using *all cores* (`primitive` is written in GO and is heavily parallelized).\n\nOn the other hand, Vtracer was able to convert an image using a *single core* in less than 2 seconds, while the results were almost as good. After adding some polygon simplification, size clipping, and tuning the parameters a bit, I was able to generate good enough SVGs with around 6k bytes per image. Finally, I added a small white OCR decoy with a black background, so it could not be missed.\n\n#### Selecting the best image\n\nAfter generating all the SVGs, the selection of the best one is done using a proxy function over the VQA score. I simply followed the method described in [this paper](https://arxiv.org/abs/2404.01291) and used the **\"Yes\"** vs **\"No\"** probability for this question: `\"f<image>answer en Question: Does this image contain {description}? Answer Yes or No.\\nAnswer:\"`.\n\nThis actually turned out to work better than generating questions, choices, and answers using Qwen-2.5 and running the original VQA score. I believe this is because the proxy function is much more robust when we can only evaluate a few questions, but I could be wrong and my question generation model was just bad.\n\nAlthough using other model versions (3b 224, 10b 224 and 3b 448) also correlated with the main metric, the original VQA model (10b 448) was always better, even if much slower and using less prompts because of that.\n\n#### Optimizing the pipeline\n\nAfter I noticed ensembling would be quite powerful, I had to find a way to efficiently use all Kaggle resources. I was heavily underusing GPUs because Vtracer is CPU bound, and Kaggle machines have **really** slow CPUs. For example, generating images with SDXL-turbo was actually faster than running Vtracer on a single thread.\n\nIn the end, I was able to use almost 100% of the resources by running three processes in the background:\n\n- Diffusion model on `cuda:0`\n- Vtracer on `cpu`\n- Proxy function on `cuda:1`\n\nand then controlling the flow within the main notebook. For example, whenever a few images are ready from SDXL, we can start converting while we keep generating more images in parallel. The same for scoring when a few SVGs are ready.\n\nThis way, I was able to run **28** different SVGs, but I believe I could push this a bit more because my solution was running in around 55 seconds per example on average.\n\n#### Finding the aesthetic patch\n\nIn order to find the patch, I built a fully differentiable version of every step of the preprocessing, aesthetic model, and VQA. With this differentiable pipeline, I tried so many things:\n\n- Optimize full score directly with DiffVG (SVG -> score directly)\n- Add text to the image and **reduce** the VQA OCR score in order to *hide* it\n- Find a small subset of paths which will optimize the aesthetic score alongside a dataset\n- Optimize a proxy function (yes probability) with the VQA model to find the best SVG to use\n- Plug the full function (VQA + aesthetic) into a method like [ReNO](https://arxiv.org/abs/2406.04312) to optimize images from SDXL-turbo directly\n- Directly finetune a Diffusion model using a similar method to [image-reward](https://github.com/THUDM/ImageReward) but with the differentiable VQA loss\n\nAll of those solutions *do work*, but they are either too slow, require too much GPU memory to run on Kaggle, generate SVGs way over 10kb, or are just too unstable to make sure they will work every time. In the end, I tried to do something similar to what other people did in optimizing directly over the generated SVG, but could not get a proxy function over the VQA score to work well unfortunately =/\n\nThe best solution I could find was to just search a small subset of paths that, when added over the background of the actual scene, will improve the average aesthetic score in a dataset. Due to all the preprocessing and especially cropping, the aesthetic score varies quite a bit from image to image, but the average over a significant amount of images will improve measurably. Also, because of the cropping, the position of the patch cannot be so close to the edges; otherwise, the score will be much lower.\n\nBecause the patch uses a lot of bytes, I had to balance patch size and VQA score. The bigger the patch, the less information VTracer could use to convert the SVG. I was able to get patches with over **0.8** aesthetic score by using much more bytes, but then the VQA hit would be too high.\n\nMy final patch has around 100x100 pixels and was found using a simple gradient ascent over the aesthetic score on a small dataset of 150 images. I selected a bunch of background from a previous submission, started the SVG from scratch in a grid like pattern of 96x96 pixels, and run gradient ascent. This [notebook](https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685) shows the process, it took a few hours to find a good aesthetic patch and it is extremely sensitive on where in the image it is.\n\n#### Bonus\n\nI think the nicest thing I tried was to plug the differentiable pipeline into [ReNO](https://arxiv.org/abs/2406.04312) and optimize the diffusion process directly against the full loss. It generated some really nice images and was actually significantly better than a single run of SDXL-turbo, but it was very inefficient and did not work as well as the simple ensembling I did in the end. Also, I could only use the 3B-224 version of the VQA, since the bigger one would not run with gradients enabled on a T4 with 16GB of memory, so the loss did not perfectly correlate with the competition score.\n\n[This notebook](https://www.kaggle.com/code/tomirol/fork-of-sdxl-turbo-reno-vqa?scriptVersionId=242363815) shows the improvement from the first generated image from SDXL and the images in between, optimizing the metric alongside the way on the diffusion process itself.\n\n",
      "votes": 29
    },
    {
      "id": 3213648,
      "postDate": "2025-05-30T09:31:22.563Z",
      "content": "<p>Nice work and explanation <a href=\"https://www.kaggle.com/tomirol\" target=\"_blank\">@tomirol</a>  Thanks.   The link to your notebook for  gradient ascent finding aesthetic patch does not work. Would you provide that notebook? Thank you</p>",
      "rawMarkdown": "Nice work and explanation @tomirol  Thanks.   The link to your notebook for  gradient ascent finding aesthetic patch does not work. Would you provide that notebook? Thank you",
      "votes": 1,
      "replies": [
        {
          "id": 3213772,
          "postDate": "2025-05-30T12:21:07.657Z",
          "content": "<p>Hey thanks for pointing it out, just made it public now.</p>",
          "rawMarkdown": "Hey thanks for pointing it out, just made it public now.",
          "replies": [
            {
              "id": 3214024,
              "postDate": "2025-05-30T19:07:34.513Z",
              "content": "<p>Thanks <a href=\"https://www.kaggle.com/tomirol\" target=\"_blank\">@tomirol</a> </p>",
              "rawMarkdown": "Thanks @tomirol ",
              "votes": 1
            }
          ]
        }
      ]
    },
    {
      "id": 3213344,
      "postDate": "2025-05-29T21:00:07.970Z",
      "content": "<p>now i try make something similar, thnx 4 this</p>",
      "rawMarkdown": "now i try make something similar, thnx 4 this",
      "votes": 1
    },
    {
      "id": 3212256,
      "postDate": "2025-05-29T15:03:49.940Z",
      "content": "<p>A very interesting solution .</p>",
      "rawMarkdown": "A very interesting solution .\n",
      "votes": 1
    },
    {
      "id": 3211745,
      "postDate": "2025-05-28T19:14:56.137Z",
      "content": "<p>A very interesting solution with excelent scor. And thank you for sharing the notebook. The girl in the picture turned out very nice.</p>\n<p>I also tried to somehow improve the aesthetic score. I was trying to pick out certain shapes within a broad frame of the image, but it didn’t work out well. The VQA score dropped significantly, and the increase in AES didn’t make up for it. <br>\nYour approach to increasing aesthetic score is great</p>",
      "rawMarkdown": "A very interesting solution with excelent scor. And thank you for sharing the notebook. The girl in the picture turned out very nice.\n\nI also tried to somehow improve the aesthetic score. I was trying to pick out certain shapes within a broad frame of the image, but it didn’t work out well. The VQA score dropped significantly, and the increase in AES didn’t make up for it. \nYour approach to increasing aesthetic score is great",
      "votes": 2,
      "replies": [
        {
          "id": 3211777,
          "postDate": "2025-05-28T20:30:35.327Z",
          "content": "<p>Hey thanks man! It was quite tricky finding the position to put the patch, because it gets a low score on the borders (due to cropping augmentation) but will lower the VQA score on the center.</p>",
          "rawMarkdown": "Hey thanks man! It was quite tricky finding the position to put the patch, because it gets a low score on the borders (due to cropping augmentation) but will lower the VQA score on the center.",
          "votes": 1
        }
      ]
    },
    {
      "id": 3217666,
      "postDate": "2025-06-05T09:26:41.110Z",
      "content": "<p>Hi <a href=\"https://www.kaggle.com/tomirol\" target=\"_blank\">@tomirol</a>  in order to run your diffvg optimization notebook <a href=\"https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685\" target=\"_blank\">https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685</a> , would you mind to  make the private datasets public? Thank you</p>",
      "rawMarkdown": "Hi @tomirol  in order to run your diffvg optimization notebook https://www.kaggle.com/code/tomirol/sdxl-turbo-vtracer-aest-opt?scriptVersionId=242366685 , would you mind to  make the private datasets public? Thank you",
      "replies": [
        {
          "id": 3217962,
          "postDate": "2025-06-05T15:41:41.487Z",
          "content": "<p>Hey sorry for the trouble - all datasets should be public now. Thanks for letting me know!</p>",
          "rawMarkdown": "Hey sorry for the trouble - all datasets should be public now. Thanks for letting me know!"
        }
      ]
    }
  ],
  "complete": true,
  "source": "live",
  "fetched_at": "2026-10-20T09:10:26Z",
  "competition_id": 89659,
  "competition_slug": "drawing-with-llms",
  "team_id": 13404672,
  "solution_url": "/competitions/drawing-with-llms/writeups/tomirol-5th-place-solution-vtracer-and-diffvg-opti"
}