Это похоже на естественный обряд посвящения, когда, когда вы изучаете машинное обучение и сталкиваетесь с Неоправданной эффективностью рекуррентной нейронной сети Андрея Карпати, вы начинаете воображать, какой еще сумасшедший текст вы можете бросить в модель персонажа. Итак, в качестве первого эксперимента по применению машинного обучения в дизайне я решил попробовать обучить модель персонажа RNN с помощью файлов SVG. Файлы взяты из замечательной коллекции SVG-логотипов с открытым исходным кодом Джила Барбары https://github.com/gilbarbara/logos. Я клонировал проект локально, а затем написал простой скрипт для объединения всех файлов в один текстовый файл, где каждый SVG занимает одну строку:

import os
file = open("svgs.txt", "w")
for filename in os.listdir(os.getcwd()):
    if filename.endswith(".svg"):
        with open(filename) as f:
            file.write(f.read().replace('\n', ''))
file.close()

Настроить Torch RNN оказалось несложно благодаря инструкциям и сборкам Docker, доступным здесь: https://github.com/crisbal/docker-torch-rnn

После запуска контейнера я предварительно обработал данные, а затем обучил модель за ночь (в итоге было выполнено около 120 000 итераций). После завершения обучения я мог просто вызвать образец сценария с длиной и вернуть некоторый результат (я выполнил форматирование вручную, но должен иметь возможность написать некоторое регулярное выражение для автоматической обработки в следующий раз).

Вы можете увидеть полный вывод из 10 000 символов здесь:

Https://gist.github.com/nategraves/7f5d70e3670735b20c5a355a87fc752f

Так так так! Если мы простим вывод, начиная с середины «мысли», мы получили то, что выглядит как довольно правдоподобный SVG:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="257px" viewBox="0 0 256 278" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
  <g>
    <path d="M146.959286,104.99079 L100.065984,65.3427687 L115.323144,83.9619482 L115.203471,82.3171837 Z" id="path-10"
    </rect>
  </g>
</svg>

Итак, модель действительно хорошо изучила синтаксис объявления XML и SVG. Он даже довольно близко подошел к согласованию размеров SVG с окном просмотра SVG. Он также изучил общий шаблон группировки элементов в SVG. Единственные проблемы, которые нам нужно исправить, чтобы сделать это действительным, - это добавить смешивание «› »открывающего тега пути и изменить закрывающий тег с« rect »на« path »:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="257px" viewBox="0 0 256 278" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
  <g>
    <path d="M146.959286,104.99079 L100.065984,65.3427687 L115.323144,83.9619482 L115.203471,82.3171837 Z" id="path-10">
    </path>
  </g>
</svg>

Это дает нам наш первый SVG:

Неплохо для первой попытки. Нетрудно увидеть, как эта сеть попала сюда. Многие логотипы, используемые для обучения сети, состояли из одного пути. И в среднем элементы пути составляют ~ 120 символов.

Второй сгенерированный SVG требовал более тщательной обработки, но дал гораздо более сложный результат:

Вы можете увидеть / скачать полный, очищенный файл: здесь. Я заметил кое-что интересное, устраняя проблемы в этом SVG. Вот строки, которые требовали исправления:

<circle fill="#DE40B6" cx="162.451988" cy="35.5460947" rx="3.14914272" ry="3.55655117"></ellipse>
<circle fill="#FFFFFF" cx="137.196373" cy="104.487309" rx="3.2018043" ry="2.55793163"></ellipse>
<path d="M127.541936,0 L188.429891,75.4280389 C187.854857,71.9586689 183.140919,70.3060989 184.064509,67.3892092 C186.119612,65.6950319 183.308987,65.8107254 180.122963,65.4154261 L179.934141,64.2763111 C182.61612,62.7296249 187.673909,59.8673111 192.351349,49.7234236 C192.672142,44.9137868 190.614753,46.0389547 190.003102,45.7803851 C186.367503,46.91378 180.722186,47.6986131 175.750766,47.1311978 C167.171759,48.4975463 165.520194,48.1505825 167.393063,48.5848036 C169.766924,47.7266069 169.795549,45.7857155 169.41797,45.4677119 L165.98752,45.1124994 C164.879217,44.2275554 162.550996,42.9214531 161.989239,41.5766263 C161.668591,40.6237069 161.194214,40.5771044 160.739291,38.2606507" fill="url(#linearGradient-1)"></path>

С RNN выходные данные повторно используются как входные, так что сеть имеет представление об использовании того, что рассматривалось в прошлом, в качестве ориентира для символов, которые она будет выводить в будущем. Моя ставка, в случае с кругами, заключается в том, что все шло отлично до атрибута cy. Если это последний результат, на который вы смотрите, вероятность того, что вы находитесь в круге или эллипсе, составляет 50/50, поскольку оба поддерживают атрибуты cx и cy. Сеть бросила кости, выбрала эллипс и продолжила настройку радиусов x и y и закрытие эллипса. Точно так же путь включает атрибут заливки, который ссылается на несуществующий линейный градиент, который необходимо определить где-нибудь в документе. В тот момент в выходных данных сети у него не было возможности узнать, был ли ранее определен линейный градиент, он просто узнал, что иногда заливки имеют шестнадцатеричный цвет, а иногда - ссылки.

Итак, насколько успешной оказалась модель персонажа? Учитывая все обстоятельства, довольно успешно. Он смог последовательно создавать почти корректную разметку на основе относительно небольшого обучающего набора. Эти результаты достаточно обнадеживают, и я продолжу экспериментировать. Далее, что произойдет, если мы просто будем использовать пути в качестве обучающей выборки.