ИИ уже может автоматизировать написание HTML и CSS

внешний интерфейс искусственный интеллект

Нажмите «CSDN» выше и выберите «Официальный аккаунт Zhiding».

Критический момент, первая доставка!

[Примечание редактора CSDN] Месяц назад мы опубликовали статью под названием "Через три года AI будет революционизировать предельное развитие?", которая представила проект, который на тот момент занимал ТОП-1 в таблице лидеров GitHub - Screenshot-to-code-in-Keras. В этом проекте нейронная сеть автоматически превращает эскиз проекта в Код HTML и CSS, а его автор Эмиль Валлнер сказал: «Через три года искусственный интеллект произведет революцию в разработке интерфейса».

Сразу после этого Флага он вызвал очень оживленные дискуссии дома и за границей, с радостями и печалями, похвалами и противодействием. В связи с этим Эмиль Валлнер написал серию статей с очень тщательной практикой, особенно в статье «Превращение дизайн-макетов в код с помощью глубокого обучения», где он подробно рассказал, как он построил мощную генерацию кода переднего плана на основе таких статей, как как модель pix2code и подробно описывает процесс прототипирования дизайна веб-сайта HTML и CSS с использованием LSTM и CNN.

Ниже приводится полный текст:

В течение следующих трех лет глубокое обучение изменит фронтенд-разработку, позволив быстро создавать прототипы и снизив входной барьер для разработки программного обеспечения.

В прошлом году в этой области произошел прорыв: Тони Белтрамелли опубликовал документ pix2code [1], а Airbnb запустила sketch2code [2].

В настоящее время самым большим препятствием для автоматизации фронтенд-разработки является вычислительная мощность. Однако теперь мы можем использовать алгоритмы глубокого обучения вместе с синтетическими обучающими данными для изучения автоматизации разработки пользовательского интерфейса.

В этой статье мы покажем, как обучить нейронную сеть писать базовый код HTML и CSS на основе схемы проекта. Вот краткий обзор процесса:

  • Предоставить схему конструкции обученной нейронной сети

  • Нейронная сеть преобразует дизайнерские чертежи в HTML-код

Пожалуйста, нажмите на большую картинку: https://blog.floydhub.com/generate_html_markup-b6ceec69a7c9cfd447d188648049f2a4.gif

  • визуализировать экран

Мы построим эту нейронную сеть за три итерации.

Сначала строим упрощенную версию, осваивая инфраструктуру. Вторая версия — HTML, где мы сосредоточимся на автоматизации каждого шага и объясним слои нейронной сети. В последней версии — Boostrap мы создадим общую модель для изучения слоев LSTM.

Вы можете получить доступ к нашему коду через Github[3] и блокнот FloydHub[4] Jupyter. Все блокноты FloydHub размещаются в каталоге «floydhub», а локальные материалы — в каталоге «local».

Эти модели были созданы из бумаги pix2code Белтрамелли и «Учебника по аннотации изображений» Джейсона Браунли [5]. Код написан на Python и Keras (фреймворк верхнего уровня TensorFlow).

Если вы новичок в глубоком обучении, я рекомендую вам ознакомиться с Python, алгоритмом обратного распространения ошибки и свёрточными нейронными сетями. Вы можете прочитать мои три предыдущие статьи:

  • Начните первую неделю глубокого обучения [6]

  • Изучите историю глубокого обучения с помощью программирования [7]

  • Использование нейронных сетей для раскрашивания черно-белых фотографий [8]

основная логика

Наша цель может быть сформулирована так: Построить нейронную сеть, которая может генерировать код HTML и CSS, соответствующий дизайну.

При обучении нейросети можно дать несколько скриншотов вместе с соответствующим HTML.

Нейронная сеть обучается, предсказывая соответствие HTML-тегов один за другим. При прогнозировании следующего тега нейросеть смотрит на скриншот и все правильные HTML-теги до этого момента.

Следующий Google Sheet дает простые обучающие данные:

https://docs.google.com/spreadsheets/d/1xXwarcQZAHluorveZsACtXRdmNFbwGtN3WMNhcTdEyQ/edit?usp=sharing

Конечно, есть и другие способы [9] для обучения нейронной сети, но создание модели, которая предсказывает слово за словом, является наиболее распространенной практикой, поэтому мы также используем этот метод в этом руководстве.

Обратите внимание, что каждый прогноз должен быть основан на одном и том же снимке экрана, поэтому, если нейронной сети нужно предсказать 20 слов, ей нужно просмотреть один и тот же снимок экрана 20 раз. Оставив на мгновение в стороне то, как работают нейронные сети, давайте сначала разберемся с входными и выходными данными нейронных сетей.

Давайте сначала посмотрим на «предыдущие теги HTML». Предположим, нам нужно обучить нейросеть предсказывать такое предложение: «Я умею программировать». Далее он получает «Я могу» и продолжает предсказывать «код». То есть каждый раз нейросеть принимает все предыдущие слова, но должна только предсказать следующее слово.

Нейронная сеть создает функции из данных, она должна связать входные данные с выходными данными через созданные функции, ей нужно построить представление, чтобы понять, что находится на снимке экрана, и синтаксис HTML, который она предсказывает. Знания, накопленные в ходе этого процесса, можно использовать для предсказания следующей метки.

Использование обученной модели для практических приложений очень похожа на процесс обучения модели. Модель будет генерировать текст один за другим в соответствии с одним и тем же скриншотом. Разница состоит в том, что вам не нужно предоставлять правильные теги HTML, модель просто требует тегов, созданных до сих пор и предсказывает следующий тег. Прогнозы начинаются с метки «Пуск» и прекращены, когда прогнозируется метка «END», или максимальный лимит превышен. Следующий лист Google дает еще один пример:

https://docs.google.com/spreadsheets/d/1yneocsAb_w3-ZUdhwJ1odfsxR2kr-4e_c5FabQbNJrs/edit#gid=0

Привет мир версия

Попробуем создать версию «hello world». Мы передаем нейросети скриншот веб-страницы с надписью «Hello World» и учим ее генерировать HTML-код.

Пожалуйста, нажмите на большую картинку: https://blog.floydhub.com/hello_world_generation-039d78c27eb584fa639b89d564b94772.gif

Сначала нейронная сеть преобразует дизайн в ряд значений пикселей, каждый из которых содержит три канала (красный, синий и зеленый) со значениями в диапазоне 0-255.

Я использую однократное кодирование [10] здесь, чтобы описать, как нейронная сеть понимает HTML-код. Кодировка предложения «Я умею кодировать» показана на следующем рисунке:

В приведенном выше примере добавлены теги «начало» и «конец». Эти метки могут сообщить нейронной сети, где начать делать прогнозы, а где прекратить делать прогнозы.

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

Предложения имеют ту же логику, что и слова, но они также должны гарантировать, что входные данные имеют одинаковую длину. Максимальное количество слов — это размер словарного запаса, а максимальное количество предложений — максимальная длина предложения. Если длина предложения меньше максимальной длины, оно дополняется пустыми словами — все пустые слова — это нулевые слова.

Как показано на изображении выше, слова расположены справа налево, что заставляет каждое слово менять положение в каждом тренировочном раунде. Это позволяет модели запоминать порядок слов, а не запоминать положение каждого слова.

На рисунке ниже показаны четыре прогноза, причем каждая строка представляет один прогноз. В левой части уравнения находится изображение, представленное значениями красного, зеленого и синего каналов, а также предшествующим словом. За скобками указано каждое предсказание, а последний красный квадрат обозначает конец.

#Length of longest sentencemax_caption_len = 3#Size of vocabulary vocab_size = 3# Load one screenshot for each word and turn them into digits images = []for i in range(2):    images.append(img_to_array(load_img('screenshot.jpg', target_size=(224, 224))))images = np.array(images, dtype=float)# Preprocess input for the VGG16 modelimages = preprocess_input(images)#Turn start tokens into one-hot encodinghtml_input = np.array(            [[[0., 0., 0.], #start             [0., 0., 0.],             [1., 0., 0.]],             [[0., 0., 0.], #start <HTML>Hello World!</HTML>             [1., 0., 0.],             [0., 1., 0.]]])#Turn next word into one-hot encodingnext_words = np.array(            [[0., 1., 0.], # <HTML>Hello World!</HTML>             [0., 0., 1.]]) # end# Load the VGG16 model trained on imagenet and output the classification featureVGG = VGG16(weights='imagenet', include_top=True)# Extract the features from the imagefeatures = VGG.predict(images)#Load the feature to the network, apply a dense layer, and repeat the vectorvgg_feature = Input(shape=(1000,))vgg_feature_dense = Dense(5)(vgg_feature)vgg_feature_repeat = RepeatVector(max_caption_len)(vgg_feature_dense)# Extract information from the input seqence language_input = Input(shape=(vocab_size, vocab_size))language_model = LSTM(5, return_sequences=True)(language_input)# Concatenate the information from the image and the inputdecoder = concatenate([vgg_feature_repeat, language_model])# Extract information from the concatenated outputdecoder = LSTM(5, return_sequences=False)(decoder)# Predict which word comes nextdecoder_output = Dense(vocab_size, activation='softmax')(decoder)# Compile and run the neural networkmodel = Model(inputs=[vgg_feature, language_input], outputs=decoder_output)model.compile(loss='categorical_crossentropy', optimizer='rmsprop')# Train the neural networkmodel.fit([features, html_input], next_words, batch_size=2, shuffle=False, epochs=1000)скопировать код

В версии hello world мы использовали 3 токена, а именно «начало», «

Hello World!

» и «конец». Токен может представлять что угодно, это может быть символ, слово или предложение. Преимущество выбора символов в качестве токенов заключается в том, что требуемый словарный запас меньше, но это ограничивает обучение нейронной сети. Выбор слов в качестве токенов имеет наилучшую производительность.

Далее делаем прогнозы:

# Create an empty sentence and insert the start tokensentence = np.zeros((1, 3, 3)) # [[0,0,0], [0,0,0], [0,0,0]]start_token = [1., 0., 0.] # startsentence[0][2] = start_token # place start in empty sentence# Making the first prediction with the start tokensecond_word = model.predict([np.array([features[1]]), sentence])# Put the second word in the sentence and make the final predictionsentence[0][1] = start_tokensentence[0][2] = np.round(second_word)third_word = model.predict([np.array([features[1]]), sentence])# Place the start token and our two predictions in the sentence sentence[0][0] = start_tokensentence[0][1] = np.round(second_word)sentence[0][2] = np.round(third_word)# Transform our one-hot predictions into the final tokensvocabulary = ["start", "<HTML><center><H1>Hello World!</H1></center></HTML>", "end"]for i in sentence[0]:    print(vocabulary[np.argmax(i)], end=' ')скопировать код

выходной результат

  • 10 эпох:start start start

  • 100 эпох:start <HTML><center><H1>Hello World!</H1></center></HTML> <HTML><center><H1>Hello World!</H1></center></HTML>

  • 300 эпох:start <HTML><center><H1>Hello World!</H1></center></HTML> end

Среди ошибок, которые я сделал

  • Сначала сделайте рабочую первую версию, а затем соберите данные.В начале этого проекта мне удалось загрузить старый архив всего сайта хостинга Geocities, который содержал 38 миллионов сайтов. Из-за мощного потенциала нейронных сетей я не учел огромную нагрузку по суммированию словаря размером в 100 000 слов.

  • Для обработки терабайтов данных требуется хорошее оборудование или много терпения.После пары проблем с моим Mac мне пришлось использовать мощный удаленный сервер. Чтобы обеспечить бесперебойный рабочий процесс, вы должны быть готовы арендовать майнер с 8 процессорами и пропускной способностью 1G.

  • Главное — разобраться с входными и выходными данными.Ввод X — это снимок экрана и предыдущие теги HTML. А выход Y — это следующая метка. Как только я понял входные и выходные данные, мне было легко понять все остальное. Также становится легче экспериментировать с различными архитектурами.

  • Будьте сосредоточены и не поддавайтесь искушению.Поскольку этот проект затрагивает многие области глубокого обучения, во многих местах я не могу удержаться. Я потратил неделю на написание RNN с нуля, увлекся встраиванием векторных пространств и попал в ловушку экстремальных реализаций.

  • Сети преобразования изображения в код — это не что иное, как замаскированные модели аннотаций изображений.Даже если я понимаю это, я игнорирую многие статьи по аннотации изображений, потому что они недостаточно круты. Наличие некоторых знаний об этом может помочь нам ускорить изучение проблемного пространства.

Запустить код на FloydHub

FloydHub — это учебная платформа для глубокого обучения. Я обнаружил эту платформу, когда впервые начал изучать глубокое обучение, и с тех пор я использую ее для обучения и управления своими экспериментами по глубокому обучению. Вы можете установить и запустить модели в течение 10 минут, и это лучший выбор для запуска моделей на графических процессорах в облаке.

Если вы никогда не использовали FloydHub, обратитесь к официальному «2-минутному руководству по установке» или моему «5-минутному руководству по началу работы» [11].

Клонируйте репозиторий кода:

git clone https://github.com/emilwallner/Screenshot-to-code-in-Keras.gitскопировать код

Войдите в систему и инициализируйте инструменты командной строки FloydHub:

cd Screenshot-to-code-in-Kerasfloyd loginfloyd init s2cскопировать код

Запустите ноутбуки Jupyter на облачной машине с графическим процессором FloydHub:

floyd run --gpu --env tensorflow-1.4 --data emilwallner/datasets/imagetocode/2:data --mode jupyterскопировать код

Все записные книжки хранятся в каталоге «FloydHub», а локальные материалы — в «локальном» каталоге. После запуска вы можете найти первую записную книжку в следующем файле:

floydhub/Helloworld/helloworld.ipynb

Если вы хотите узнать подробные параметры команды, обратитесь к моему сообщению:

https://blog.floydhub.com/colorizing-b&w-photos-with-neural-networks/

HTML-версия

В этом выпуске мы автоматизируем некоторые этапы модели Hello World. В этом разделе мы сосредоточимся на том, как заставить модель работать с произвольно большими объемами входных данных, а также на ключевых частях построения нейронной сети.

Эта версия еще не способна предсказывать HTML с любого веб-сайта, но здесь мы постараемся решить ключевые технические проблемы и сделать большой шаг к конечному успеху.

Обзор

Мы можем расширить предыдущую пояснительную диаграмму следующим образом:

На изображении выше есть две основные части. Во-первых, это кодирующая часть. Часть кодирования отвечает за установление характеристик изображения и характеристик предыдущей метки. Функция — это наименьшая единица данных, созданная нейронной сетью для соединения схемы дизайна и HTML-кода. В конце части кодирования мы объединяем особенности изображения с каждым словом предыдущей метки.

Другой основной частью является часть декодирования. Часть декодирования отвечает за получение функций агрегированной схемы проекта и HTML-кода, а также за создание функций следующего тега. Эта функция передается через полностью подключенную нейронную сеть для прогнозирования следующей метки.

Особенности оформления чертежей

Поскольку нам нужно добавлять скриншот для каждого слова, это становится узким местом в обучении нейронной сети. Таким образом, вместо того, чтобы использовать изображение напрямую, мы извлекаем из него информацию, необходимую для создания меток.

Извлеченная информация кодируется и сохраняется в функциях изображения. Эту работу может выполнить предварительно обученная сверточная нейронная сеть (CNN). Модель можно обучить на данных ImageNet.

Последний слой CNN — это слой классификации, мы можем извлечь признаки изображения из предыдущего слоя.

В итоге мы можем получить 1536 изображений размером 8x8 пикселей в качестве признаков. Хотя нам сложно понять смысл этих признаков, нейронные сети могут извлекать из них объект и расположение элементов.

Характеристики тегов HTML

В версии hello world мы используем однократное кодирование для представления HTML-тегов. В этой версии мы будем использовать встраивание слов в качестве входных данных, а выходные данные по-прежнему будут кодироваться горячим способом.

Мы продолжаем анализировать предложения так же, как и раньше, но с изменением способа сопоставления каждого токена. В то время как предыдущее горячее кодирование рассматривает каждое слово как отдельную единицу, здесь мы преобразуем каждое слово во входных данных в ряд чисел, которые представляют отношения между тегами HTML.

Вложения слов в приведенном выше примере являются 8-мерными, но в действительности, в зависимости от размера словаря, их размерность будет между 50 и 500.

8 чисел для каждого слова представляют веса, очень похожие на исходную нейронную сеть. Они представляют отношения между словами (Миколов и др., 2013 [12]).

Выше описан процесс создания функции тега HTML. Нейронные сети используют эту функцию для установления связей между входными и выходными данными. Пока не беспокойтесь о деталях, мы подробно обсудим это в следующем разделе.

кодирующая часть

Нам нужно передать результат встраивания слов в LSTM и вернуть последовательность функций меток, которые мы передаем в плотный слой с распределенным временем, который вы можете представить как плотный слой с несколькими входами и выходами.

При этом признаки изображения сначала нужно сгладить, независимо от того, какова исходная структура значений, они будут преобразованы в огромный список значений, затем через плотный слой построить признаки более высокого уровня; наконец, эти особенности связаны с особенностями HTML-тегов.

Это может быть немного сложно понять, поэтому давайте разберем это по порядку.

Функции HTML-тегов

Сначала мы передаем результат встраивания слов в слой LSTM. Как показано на рисунке ниже, все предложения дополняются до максимальной длины, равной трем токенам.

Чтобы смешать эти сигналы и найти шаблоны более высокого уровня, мы добавляем плотный слой TimeDistributed для дальнейшей обработки функций HTML-тегов, сгенерированных слоем LSTM. Плотный слой TimeDistributed — это плотный слой с несколькими входами и выходами.

особенности изображения

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

Точно так же, чтобы смешивать сигналы и извлекать информацию из более высоких слоев, мы добавляем плотный слой. Поскольку вход только один, мы можем использовать обычные плотные слои. Чтобы подключиться к функции тега HTML, нам нужно скопировать функцию изображения.

В приведенном выше примере у нас есть три функции HTML-тега, поэтому количество конечных функций изображения также равно трем.

Объединение функций изображения и функций HTML-тегов

Все предложения дополнены, чтобы сформировать три функции. Теперь, когда у нас есть готовые функции изображения, мы можем теперь добавить функции изображения к их соответствующим функциям тега HTML.

После завершения добавления мы получаем 3 функции метки изображения, которые являются входной информацией, которую мы должны предоставить части декодирования.

Часть расшифровки

Затем мы используем комбинированную функцию изображения и метки, чтобы предсказать следующую метку.

В приведенном ниже примере мы используем три пары функций метки графика и выводим функции следующей метки.

Обратите внимание, что значение последовательности слоя LSTM является ложным, поэтому нам не нужно возвращать длину входной последовательности, а нужно только предсказать функцию, которая является функцией следующей метки, которая содержит окончательную информацию прогноза.

окончательный прогноз

Плотный слой работает аналогично традиционной нейронной сети с прямой связью, соединяя 512 цифр следующей функции метки с 4 окончательными прогнозами. Нашими словами: начало, привет, мир и конец.

Среди них функция активации softmax плотного слоя генерирует распределение вероятностей 0-1, а сумма всех предсказанных значений равна 1. Например, предсказание словаря может быть [0,1, 0,1, 0,1, 0,7], тогда результат предсказания на выходе будет следующим: четвертое слово — следующая метка. Затем вы можете преобразовать однократное кодирование [0, 0, 0, 1] в значение карты, что приведет к «концу».

# Load the images and preprocess them for inception-resnetimages = []all_filenames = listdir('images/')all_filenames.sort()for filename in all_filenames:    images.append(img_to_array(load_img('images/'+filename, target_size=(299, 299))))images = np.array(images, dtype=float)images = preprocess_input(images)   # Run the images through inception-resnet and extract the features without the classification layerIR2 = InceptionResNetV2(weights='imagenet', include_top=False)features = IR2.predict(images)   # We will cap each input sequence to 100 tokensmax_caption_len = 100# Initialize the function that will create our vocabulary tokenizer = Tokenizer(filters='', split=" ", lower=False)    # Read a document and return a stringdef load_doc(filename):    file = open(filename, 'r')    text = file.read()    file.close()    return text    # Load all the HTML filesX = []all_filenames = listdir('html/')all_filenames.sort()for filename in all_filenames:X.append(load_doc('html/'+filename))    # Create the vocabulary from the html filestokenizer.fit_on_texts(X)    # Add +1 to leave space for empty wordsvocab_size = len(tokenizer.word_index) + 1# Translate each word in text file to the matching vocabulary indexsequences = tokenizer.texts_to_sequences(X)# The longest HTML filemax_length = max(len(s) for s in sequences)    # Intialize our final input to the modelX, y, image_data = list(), list(), list()for img_no, seq in enumerate(sequences):    for i in range(1, len(seq)):        # Add the entire sequence to the input and only keep the next word for the output        in_seq, out_seq = seq[:i], seq[i]        # If the sentence is shorter than max_length, fill it up with empty words        in_seq = pad_sequences([in_seq], maxlen=max_length)[0]        # Map the output to one-hot encoding        out_seq = to_categorical([out_seq], num_classes=vocab_size)[0]        # Add and image corresponding to the HTML file        image_data.append(features[img_no])        # Cut the input sentence to 100 tokens, and add it to the input data        X.append(in_seq[-100:])        y.append(out_seq)    X, y, image_data = np.array(X), np.array(y), np.array(image_data)    # Create the encoderimage_features = Input(shape=(8, 8, 1536,))image_flat = Flatten()(image_features)image_flat = Dense(128, activation='relu')(image_flat)ir2_out = RepeatVector(max_caption_len)(image_flat)    language_input = Input(shape=(max_caption_len,))language_model = Embedding(vocab_size, 200, input_length=max_caption_len)(language_input)language_model = LSTM(256, return_sequences=True)(language_model)language_model = LSTM(256, return_sequences=True)(language_model)language_model = TimeDistributed(Dense(128, activation='relu'))(language_model)    # Create the decoderdecoder = concatenate([ir2_out, language_model])decoder = LSTM(512, return_sequences=False)(decoder)decoder_output = Dense(vocab_size, activation='softmax')(decoder)    # Compile the modelmodel = Model(inputs=[image_features, language_input], outputs=decoder_output)model.compile(loss='categorical_crossentropy', optimizer='rmsprop')    # Train the neural networkmodel.fit([image_data, X], y, batch_size=64, shuffle=False, epochs=2)    # map an integer to a worddef word_for_id(integer, tokenizer):    for word, index in tokenizer.word_index.items():        if index == integer:            return word    return None    # generate a description for an imagedef generate_desc(model, tokenizer, photo, max_length):    # seed the generation process    in_text = 'START'    # iterate over the whole length of the sequence    for i in range(900):        # integer encode input sequence        sequence = tokenizer.texts_to_sequences([in_text])[0][-100:]        # pad input        sequence = pad_sequences([sequence], maxlen=max_length)        # predict next word        yhat = model.predict([photo,sequence], verbose=0)        # convert probability to integer        yhat = np.argmax(yhat)        # map integer to word        word = word_for_id(yhat, tokenizer)        # stop if we cannot map the word        if word is None:            break        # append as input for generating the next word        in_text += ' ' + word        # Print the prediction        print(' ' + word, end='')        # stop if we predict the end of the sequence        if word == 'END':            break    return    # Load and image, preprocess it for IR2, extract features and generate the HTMLtest_image = img_to_array(load_img('images/87.jpg', target_size=(299, 299)))test_image = np.array(test_image, dtype=float)test_image = preprocess_input(test_image)test_features = IR2.predict(np.array([test_image]))generate_desc(model, tokenizer, np.array(test_features), 100)скопировать код

выходной результат

Сгенерируйте ссылку на сайт:

  • 250 эпох: https://emilwallner.github.io/html/250_epochs/

  • 350 эпох: https://emilwallner.github.io/html/350_epochs/

  • 450 эпох: https://emilwallner.github.io/html/450_epochs/

  • 550 эпох: https://emilwallner.github.io/html/450_epochs/

Если вы не видите страницу, нажав на ссылку выше, вы можете выбрать «Просмотр исходного кода». Ниже приводится ссылка на оригинальный веб-сайт только для ознакомления:

https://emilwallner.github.io/html/Original/

ошибки, которые я сделал

  • По сравнению с CNN, LSTM намного сложнее, чем я думал.Для лучшего понимания я развернул все LSTM. О RNN вы можете узнать из этого видео (http://course.fast.ai/lessons/lesson6.html). Кроме того, прежде чем понять принцип, пожалуйста, ознакомьтесь с входными и выходными характеристиками.

  • Легче создать словарь с нуля, чем урезать большой словарный запас.Словарь может включать в себя все, что угодно, например шрифты, размеры div, шестнадцатеричные цвета, имена переменных и общие слова.

  • Большинство кодовых баз могут нормально анализировать текстовые документы, но не код.Потому что все слова в документе разделены пробелами, а код разный, поэтому приходится разбираться, как парсить код самому.

  • Возможно, не стоит извлекать признаки из модели, обученной в Imagenet.Поскольку в Imagenet мало изображений веб-страниц, уровень потерь в нем на 30 % выше, чем в модели pix2code, обученной с нуля. Если тренировать модель вроде inception-resnet на скриншотах веб-страниц, я не знаю, какие будут результаты.

Начальная версия

В последней версии, версии Bootstrap, мы использовали набор данных с веб-сайта начальной загрузки, сгенерированный из статьи pix2code. Используя загрузчик Twitter (https://getbootstrap.com/), мы можем комбинировать HTML и CSS и уменьшать размер словаря.

Мы можем предоставить скриншот, который он никогда раньше не видел, и научить его генерировать соответствующий HTML-код. Мы также можем углубиться в то, как он узнает этот снимок экрана и HTML-код.

Оставляя в стороне код HTML для начальной загрузки, здесь мы обучаем его с помощью 17 упрощенных токенов, которые затем транслируются в HTML и CSS. Этот набор данных [13] включает 1500 тестовых скриншотов и 250 проверочных скриншотов. На каждом скриншоте в среднем 65 токенов, содержащих 96925 обучающих выборок.

Модифицируя модель бумаги pix2code для предоставления входных данных, наша модель может предсказывать состав веб-страниц с точностью 97% (мы использовали жадный поиск BLEU по 4 энграммам, подробнее об этом позже).

комплексный подход

Модели аннотаций изображений могут извлекать функции из предварительно обученных моделей, но после нескольких экспериментов я обнаружил, что сквозной подход pix2code может лучше извлекать функции для наших моделей, поскольку предварительно обученные модели не используют веб-страницы Данные были обучены , и это должно было быть засекречено.

В этой модели мы заменяем предварительно обученные функции изображения облегченной сверточной нейронной сетью. Мы не использовали max-pooling для увеличения плотности информации, но увеличили шаг, чтобы обеспечить положение и цвет переднего элемента.

Есть две основные модели, которые поддерживают этот подход: сверточные нейронные сети (CNN) и рекуррентные нейронные сети (RNN). Наиболее распространенной рекуррентной нейронной сетью является LSTM, поэтому я выбрал RNN.

По CNN есть много руководств, о которых я рассказывал в других статьях. Здесь я в основном объясняю LSTM.

Понимание временных шагов в LSTM

Одна из самых сложных вещей для понимания в LSTM — это временной шаг. Исходную нейронную сеть можно рассматривать как имеющую только два временных шага. Если ввод «Привет» (первый временной шаг), он предсказывает «Мир» (второй временной шаг), но не может предсказать больше временных шагов. В приведенном ниже примере входные данные имеют четыре временных шага, по одному для каждого слова.

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

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

На следующем рисунке показана обработка каждого временного шага в LSTM с простой легендой.

Чтобы лучше понять эту логику, я предлагаю вам следовать этому замечательному руководству Эндрю Траска [14] и попытаться создать RNN с нуля.

Понимание единиц в слое LSTM

Количество единиц в слое LSTM определяет его объем памяти, а также размер каждого выходного объекта. Опять же, особенность — это длинный список значений, используемых для передачи информации от слоя к слою.

Каждый модуль на уровне LSTM отвечает за отслеживание различных фрагментов информации в грамматике. На изображении ниже показан пример ячейки, содержащей информацию для строки макета "div". Мы упростили код HTML и использовали его для обучения модели начальной загрузки.

Каждая ячейка LSTM имеет состояние ячейки. Вы можете думать о состоянии клетки как о памяти клетки. Веса и функции активации могут изменять состояние различными способами. Таким образом, уровень LSTM может точно настроить информацию, которую каждый ввод должен сохранять и отбрасывать.

При передаче выходных функций на вход также необходимо передать состояние ячейки, и каждая ячейка LSTM должна передать свое собственное значение состояния ячейки. Чтобы понять, как взаимодействуют части LSTM, я предлагаю вам прочитать:

Учебник Колы: https://colah.github.io/posts/2015-08-Understanding-LSTMs/

Реализация Jayasiri Numpy: http://blog.varunajayasiri.com/numpy_lstm.html

Лекции и статьи Карпая: https://www.youtube.com/watch?v=yCC09vCHzF8; https://karpathy.github.io/2015/05/21/rnn-efficientness/

dir_name = 'resources/eval_light/'    # Read a file and return a stringdef load_doc(filename):    file = open(filename, 'r')    text = file.read()    file.close()    return text    def load_data(data_dir):    text = []    images = []    # Load all the files and order them    all_filenames = listdir(data_dir)    all_filenames.sort()    for filename in (all_filenames):        if filename[-3:] == "npz":            # Load the images already prepared in arrays            image = np.load(data_dir+filename)            images.append(image['features'])        else:            # Load the boostrap tokens and rap them in a start and end tag            syntax = '<START> ' + load_doc(data_dir+filename) + ' <END>'            # Seperate all the words with a single space            syntax = ' '.join(syntax.split())            # Add a space after each comma            syntax = syntax.replace(',', ' ,')            text.append(syntax)    images = np.array(images, dtype=float)    return images, text    train_features, texts = load_data(dir_name)# Initialize the function to create the vocabulary tokenizer = Tokenizer(filters='', split=" ", lower=False)# Create the vocabulary tokenizer.fit_on_texts([load_doc('bootstrap.vocab')])    # Add one spot for the empty word in the vocabulary vocab_size = len(tokenizer.word_index) + 1# Map the input sentences into the vocabulary indexestrain_sequences = tokenizer.texts_to_sequences(texts)# The longest set of boostrap tokensmax_sequence = max(len(s) for s in train_sequences)# Specify how many tokens to have in each input sentencemax_length = 48    def preprocess_data(sequences, features):    X, y, image_data = list(), list(), list()    for img_no, seq in enumerate(sequences):        for i in range(1, len(seq)):            # Add the sentence until the current count(i) and add the current count to the output            in_seq, out_seq = seq[:i], seq[i]            # Pad all the input token sentences to max_sequence            in_seq = pad_sequences([in_seq], maxlen=max_sequence)[0]            # Turn the output into one-hot encoding            out_seq = to_categorical([out_seq], num_classes=vocab_size)[0]            # Add the corresponding image to the boostrap token file            image_data.append(features[img_no])            # Cap the input sentence to 48 tokens and add it            X.append(in_seq[-48:])            y.append(out_seq)    return np.array(X), np.array(y), np.array(image_data)    X, y, image_data = preprocess_data(train_sequences, train_features)    #Create the encoderimage_model = Sequential()image_model.add(Conv2D(16, (3, 3), padding='valid', activation='relu', input_shape=(256, 256, 3,)))image_model.add(Conv2D(16, (3,3), activation='relu', padding='same', strides=2))image_model.add(Conv2D(32, (3,3), activation='relu', padding='same'))image_model.add(Conv2D(32, (3,3), activation='relu', padding='same', strides=2))image_model.add(Conv2D(64, (3,3), activation='relu', padding='same'))image_model.add(Conv2D(64, (3,3), activation='relu', padding='same', strides=2))image_model.add(Conv2D(128, (3,3), activation='relu', padding='same'))    image_model.add(Flatten())image_model.add(Dense(1024, activation='relu'))image_model.add(Dropout(0.3))image_model.add(Dense(1024, activation='relu'))image_model.add(Dropout(0.3))    image_model.add(RepeatVector(max_length))    visual_input = Input(shape=(256, 256, 3,))encoded_image = image_model(visual_input)    language_input = Input(shape=(max_length,))language_model = Embedding(vocab_size, 50, input_length=max_length, mask_zero=True)(language_input)language_model = LSTM(128, return_sequences=True)(language_model)language_model = LSTM(128, return_sequences=True)(language_model)    #Create the decoderdecoder = concatenate([encoded_image, language_model])decoder = LSTM(512, return_sequences=True)(decoder)decoder = LSTM(512, return_sequences=False)(decoder)decoder = Dense(vocab_size, activation='softmax')(decoder)    # Compile the modelmodel = Model(inputs=[visual_input, language_input], outputs=decoder)optimizer = RMSprop(lr=0.0001, clipvalue=1.0)model.compile(loss='categorical_crossentropy', optimizer=optimizer)    #Save the model for every 2nd epochfilepath="org-weights-epoch-{epoch:04d}--val_loss-{val_loss:.4f}--loss-{loss:.4f}.hdf5"checkpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1, save_weights_only=True, period=2)callbacks_list = [checkpoint]    # Train the modelmodel.fit([image_data, X], y, batch_size=64, shuffle=False, validation_split=0.1, callbacks=callbacks_list, verbose=1, epochs=50)скопировать код

точность теста

Трудно найти разумный способ измерения точности. Вы можете сравнивать слова по одному, но если одно из предсказаний окажется не на месте, точность может быть 0%, если слово удалить для синхронизации предсказания, точность будет 99/100.

Я взял оценку BLEU, которая является лучшим выбором для тестирования моделей машинного перевода и маркировки изображений. Он делит предложение на четыре n-грамма, постепенно расширяя последовательность от 1 слова до 4 слов. В приведенном ниже примере «кошка» в предсказании на самом деле должна быть «кодом».

Чтобы рассчитать окончательный балл, вам сначала нужно умножить баллы каждого n-грамма на 25% и просуммировать их, то есть (4/5) * 0,25 + (2/4) * 0,25 + (1/3) * 0,25. + (0/2) * 0,25 = 02 + 1,25 + 0,083 + 0 = 0,408, полученную сумму нужно умножить на штрафной коэффициент длины предложения. Поскольку предсказанная длина предложения в этом примере верна, это окончательный результат.

Увеличение количества n-грамм может увеличить сложность. Модель с 4 n-граммами лучше всего подходит для человеческого перевода. Чтобы узнать больше о BLEU, я предлагаю вам запустить несколько примеров с приведенным ниже кодом и прочитать эту вики-страницу [15].

#Create a function to read a file and return its contentdef load_doc(filename):    file = open(filename, 'r')    text = file.read()    file.close()    return text    def load_data(data_dir):    text = []    images = []    files_in_folder = os.listdir(data_dir)    files_in_folder.sort()    for filename in tqdm(files_in_folder):        #Add an image        if filename[-3:] == "npz":            image = np.load(data_dir+filename)            images.append(image['features'])        else:        # Add text and wrap it in a start and end tag            syntax = '<START> ' + load_doc(data_dir+filename) + ' <END>'            #Seperate each word with a space            syntax = ' '.join(syntax.split())            #Add a space between each comma            syntax = syntax.replace(',', ' ,')            text.append(syntax)    images = np.array(images, dtype=float)    return images, text    #Intialize the function to create the vocabularytokenizer = Tokenizer(filters='', split=" ", lower=False)#Create the vocabulary in a specific ordertokenizer.fit_on_texts([load_doc('bootstrap.vocab')])    dir_name = '../../../../eval/'train_features, texts = load_data(dir_name)    #load model and weights json_file = open('../../../../model.json', 'r')loaded_model_json = json_file.read()json_file.close()loaded_model = model_from_json(loaded_model_json)# load weights into new modelloaded_model.load_weights("../../../../weights.hdf5")print("Loaded model from disk")    # map an integer to a worddef word_for_id(integer, tokenizer):    for word, index in tokenizer.word_index.items():        if index == integer:            return word    return Noneprint(word_for_id(17, tokenizer))    # generate a description for an imagedef generate_desc(model, tokenizer, photo, max_length):    photo = np.array([photo])    # seed the generation process    in_text = '<START> '    # iterate over the whole length of the sequence    print('\nPrediction---->\n\n<START> ', end='')    for i in range(150):        # integer encode input sequence        sequence = tokenizer.texts_to_sequences([in_text])[0]        # pad input        sequence = pad_sequences([sequence], maxlen=max_length)        # predict next word        yhat = loaded_model.predict([photo, sequence], verbose=0)        # convert probability to integer        yhat = argmax(yhat)        # map integer to word        word = word_for_id(yhat, tokenizer)        # stop if we cannot map the word        if word is None:            break        # append as input for generating the next word        in_text += word + ' '        # stop if we predict the end of the sequence        print(word + ' ', end='')        if word == '<END>':            break   return in_text           max_length = 48     # evaluate the skill of the modeldef evaluate_model(model, descriptions, photos, tokenizer, max_length):    actual, predicted = list(), list()    # step over the whole set    for i in range(len(texts)):        yhat = generate_desc(model, tokenizer, photos[i], max_length)        # store actual and predicted        print('\n\nReal---->\n\n' + texts[i])        actual.append([texts[i].split()])        predicted.append(yhat.split())    # calculate BLEU score    bleu = corpus_bleu(actual, predicted)    return bleu, actual, predicted    bleu, actual, predicted = evaluate_model(loaded_model, texts, train_features, tokenizer, max_length)    #Compile the tokens into HTML and cssdsl_path = "compiler/assets/web-dsl-mapping.json"compiler = Compiler(dsl_path)compiled_website = compiler.compile(predicted[0], 'index.html')    print(compiled_website )print(bleu)скопировать код

выход

ссылка на пример вывода

Сайт 1:

  • Сгенерированный веб-сайт: https://emilwallner.github.io/bootstrap/pred_1/

  • Оригинальный сайт: https://emilwallner.github.io/bootstrap/real_1/

Сайт 2:

  • Сгенерированный веб-сайт: https://emilwallner.github.io/bootstrap/pred_2/

  • Оригинальный сайт: https://emilwallner.github.io/bootstrap/real_2/

Сайт 3:

  • Сгенерированный веб-сайт: https://emilwallner.github.io/bootstrap/pred_3/

  • Оригинальный сайт: https://emilwallner.github.io/bootstrap/real_3/

Сайт 4:

  • Сгенерированный сайт: https://emilwallner.github.io/bootstrap/pred_4/

  • Оригинальный сайт: https://emilwallner.github.io/bootstrap/real_4/

Сайт 5:

  • Сгенерированный сайт: https://emilwallner.github.io/bootstrap/pred_5/

  • Оригинальный сайт: https://emilwallner.github.io/bootstrap/real_5/

ошибки, которые я сделал

  • Научитесь понимать слабые стороны вашей модели и избегайте слепого тестирования вашей модели.Когда я только начинал, я пробовал случайные вещи, такие как нормализация пакетов, двунаправленная сеть, а также пытался реализовать внимание. Посмотрев на тестовые данные и увидев, что они не точно предсказывают цвет и положение, я начал понимать, что это слабость CNN. Поэтому я отказался от maxpooling и вместо этого увеличил шаг. В результате потери теста снизились с 0,12 до 0,02, а показатель BLEU увеличился с 85% до 97%.

  • Используйте только соответствующие предварительно обученные модели.Когда набор данных был небольшим, я думал, что предварительно обученная модель изображения повысит эффективность. Экспериментальные результаты показывают, что хотя сквозная модель медленнее и требует больше памяти для обучения, точность можно повысить на 30%.

  • Будьте готовы к некоторым отличиям при запуске модели на удаленном сервере.При работе на моем Mac файлы читаются в алфавитном порядке. Но на удаленном сервере читается случайным образом. В итоге скриншот и код не соответствовали проблеме. Несмотря на то, что сходимость по-прежнему возможна, после того, как я устранил эту проблему, точность тестовых данных увеличилась на 50%.

  • Важно понимать функции библиотеки.Пустые токены в словаре должны содержать пробелы. Сначала я не добавил пробел, и в итоге у него не было токена. Я не осознавал проблемы, пока несколько раз не посмотрел на окончательный вывод и не заметил, что он никогда не предсказывал токен. После проверки выясняется, что токена нет в словаре. Кроме того, держите словарный запас в том же порядке для обучения и тестирования.

  • Экспериментируйте с легкими моделями.Замена LSTM на GRU может сократить время на эпоху на 30% без особого влияния на производительность.

Следующий шаг

Глубокое обучение очень подходит для фронтенд-разработки, потому что генерировать данные легко, а современные алгоритмы глубокого обучения могут охватывать большую часть логики.

Одним из наиболее интересных аспектов является использование механизмов внимания в LSTM [16]. Это не только повышает точность, но и помогает нам увидеть, на что CSS обращает внимание при создании HTML-кода.

Внимание также является ключевым фактором в общении между HTML-кодом, таблицами стилей, сценариями и даже серверной частью. Слои внимания могут отслеживать параметры и помогать нейронным сетям взаимодействовать между разными языками программирования.

Но в краткосрочной перспективе самой большой проблемой является поиск масштабируемого способа генерации данных. Это позволяет постепенно добавлять шрифты, цвета, слова и анимацию.

На сегодняшний день многие люди изо всех сил пытались реализовать наброски и превратить их в шаблоны для приложений. Менее чем за два года мы смогли нарисовать приложение на бумаге и за секунду получить соответствующий интерфейсный код. Команда дизайнеров Airbnb [17] и Уизард [18] создали два прототипа.

Вот несколько экспериментов, которые стоит попробовать.

эксперимент

Начиная:

  • запустить все модели

  • попробуйте разные гиперпараметры

  • Попробуйте разные архитектуры CNN

  • Добавить двунаправленную модель LSTM

  • Реализуйте модель [19], используя другой набор данных (вы можете смонтировать этот набор данных через параметр FloydHub "--data": emilwallner/datasets/100k-html:data)

Расширенный эксперимент

  • Создавайте генераторы, которые могут стабильно генерировать произвольные приложения/веб-страницы с определенным синтаксисом.

  • Сгенерируйте данные проекта для модели приложения. Автоматически преобразовывайте скриншоты приложений или веб-страниц в проекты и используйте GAN для внесения изменений.

  • Фокус изображения при каждом прогнозе наблюдается через слой внимания, аналогичный этой модели: https://arxiv.org/abs/1502.03044.

  • Фреймворк для создания модульного подхода. Например, одна модель отвечает за кодирование шрифтов, одна за цвета, а третья за макет и использует часть декодирования для их объединения. Вы можете начать со статических изображений.

  • Предоставьте нейронной сети простые компоненты HTML, обучите ее с помощью анимации, созданной с помощью CSS. Если дополнительный модуль внимания может наблюдать, что источник ввода сфокусирован еще лучше.

Наконец, большое спасибо Тони Белтрамелли и Джону Голду за их исследования и идеи, а также за ответы на различные вопросы. Спасибо Джейсону Браунли за его великолепное руководство по Keras (я включил несколько фрагментов из его руководства в базовую реализацию Keras) и Белтрамелли за данные. Спасибо также Цинпин Хоу, Чарли Харрингтону, Саю Сундарараджу, Яннесу Клаасу, Клаудио Кабралу, Алену Демене. Просмотрите эту статью вместе с Диланом Джианом.

Ссылки по теме

[1] документ pix2code: https://arxiv.org/abs/1705.07962

[2] sketch2code: https://airbnb.design/sketching-interfaces/

[3] https://github.com/emilwallner/Screenshot-to-code-in-Keras/blob/master/README.md

[4] https://www.floydhub.com/emilwallner/projects/picturetocode

[5] https://machinelearningmastery.com/blog/page/2/

[6] https://blog.floydhub.com/my-first-weekend-of-deep-learning/

[7] https://blog.floydhub.com/coding-the-history-of-deep-learning/

[8] https://blog.floydhub.com/colorizing-b&w-photos-with-neural-networks/

[9] https://machinelearningmastery.com/deep-learning-caption-generation-models/

[10] https://machinelearningmastery.com/how-to-one-hot-encode-sequence-data-in-python/

[11] https://www.youtube.com/watch?v=byLQ9kgjTdQ&t=21s

[12] https://arxiv.org/abs/1301.3781

[13] https://github.com/tonybeltramelli/pix2code/tree/master/datasets

[14] https://iamtrask.github.io/2015/11/15/anyone-can-code-lstm/

[15] https://en.wikipedia.org/wiki/BLEU

[16] https://arxiv.org/pdf/1502.03044.pdf

[17] https://airbnb.design/sketching-interfaces/

[18] https://www.uizard.io/

[19] http://lstm.seas.harvard.edu/latex/