Передняя часть паникует? Автоматически генерируйте HTML-код с помощью глубокого обучения

Python внешний интерфейс
Нас всегда интересовало, как использовать прототип страницы интерфейса для генерации соответствующего кода.Автор этой статьи строит мощную модель генерации кода интерфейса на основе таких документов, как pix2code, и подробно объясняет, как использовать LSTM и CNN для написания прототипа дизайна веб-сайтов HTML и CSS.


Ссылка на проект:GitHub.com/Дочь Эмиля Уолла…


В ближайшие три года глубокое обучение изменит фронтенд-разработку. Это ускорит прототипирование и снизит порог для разработки программного обеспечения.


В прошлом году Тони Белтрамелли опубликовал документ «pix2code: Генерация кода из снимка экрана графического пользовательского интерфейса», а Airbnb также выпустила Sketch2code (https://airbnb.design/sketching-interfaces/).


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


1) Введите расчетную схему в обученную нейронную сеть.


2) Нейронная сеть преобразует изображение в язык разметки HTML.



3) Рендеринг вывода



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


Кодовый адрес:

  • https://github.com/emilwallner/Screenshot-to-code-in-Keras
  • https://www.floydhub.com/emilwallner/projects/picturetocode


Все блокноты FloydHub находятся в каталоге floydhub, а локальные блокноты — в локальном каталоге.

Построение модели в этой статье основано на статье Белтрамелли «pix2code: Генерация кода из снимка экрана графического пользовательского интерфейса» и руководстве Джейсона Браунли по созданию описания изображения и выполняется с использованием Python и Keras.


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


Наша цель — построить нейронную сеть, способную генерировать язык разметки HTML/CSS, соответствующий скриншотам.


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


Вот простой пример обучающих данных:docs.Google.com/spreadsheet….


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


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

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


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


Нам не нужно вводить правильную разметку HTML, сеть возьмет разметку, которую она сгенерировала до сих пор, и предскажет следующую. Предсказание начинается с «начального тега» и заканчивается «конечным тегом» или при достижении максимального предела.



Привет, мировое издание


Теперь давайте создадим нашу реализацию Hello World. Мы загрузим в нейронную сеть скриншот со словами «Hello World!» и обучим ее генерировать соответствующий язык разметки.




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

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

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

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

#Length of longest sentence
    max_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 model
    images = preprocess_input(images)

    #Turn start tokens into one-hot encoding
    html_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 encoding
    next_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 feature
    VGG = VGG16(weights='imagenet', include_top=True)
    # Extract the features from the image
    features = VGG.predict(images)

    #Load the feature to the network, apply a dense layer, and repeat the vector
    vgg_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 input
    decoder = concatenate([vgg_feature_repeat, language_model])
    # Extract information from the concatenated output
    decoder = LSTM(5, return_sequences=False)(decoder)
    # Predict which word comes next
    decoder_output = Dense(vocab_size, activation='softmax')(decoder)
    # Compile and run the neural network
    model = Model(inputs=[vgg_feature, language_input], outputs=decoder_output)
    model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

    # Train the neural network
    model.fit([features, html_input], next_words, batch_size=2, shuffle=False, epochs=1000)

В версии Hello World мы используем три символа «начало», «Hello World» и «конец». Модели на уровне символов требуют меньшего словаря и ограниченных нейронных сетей, в то время как символы на уровне слов могут работать здесь лучше.


Вот код для выполнения прогноза:

# Create an empty sentence and insert the start token
    sentence = np.zeros((1, 3, 3)) # [[0,0,0], [0,0,0], [0,0,0]]
    start_token = [1., 0., 0.] # start
    sentence[0][2] = start_token # place start in empty sentence

    # Making the first prediction with the start token
    second_word = model.predict([np.array([features[1]]), sentence])

    # Put the second word in the sentence and make the final prediction
    sentence[0][1] = start_token
    sentence[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_token
    sentence[0][1] = np.round(second_word)
    sentence[0][2] = np.round(third_word)

    # Transform our one-hot predictions into the final tokens
    vocabulary = ["start", "<HTML><center><H1>Hello World!</H1></center></HTML>", "end"]
    for i in sentence[0]:
        print(vocabulary[np.argmax(i)], end=' ')

вывод

  • 10 epochs: start start start
  • 100 epochs: start <HTML><center><H1>Hello World!</H1></center></HTML> <HTML><center><H1>Hello World!</H1></center></HTML>
  • 300 epochs: start <HTML><center><H1>Hello World!</H1></center></HTML> end

Яма, через которую я прошел:

  • Создайте первую версию перед сбором данных. На ранних этапах этого проекта мне удалось получить устаревший архив сайта, размещенного на Geocities, который насчитывает 38 миллионов сайтов. Но я игнорирую огромный объем работы, необходимый для сокращения словарного запаса размером в 100 КБ.
  • Обучение на терабайте данных требует отличного оборудования или огромного терпения. После нескольких проблем с моим Mac я использовал мощный удаленный сервер. Я рассчитываю арендовать 8 современных процессоров и 1 внутренний канал GPS для запуска моего рабочего процесса.
  • Прежде чем понять входные и выходные данные, другие части, кажется, понимают. Вход X — это снимок экрана и ранее отмеченная метка, а выход Y — следующая отмеченная метка. Когда я это понимаю, в других вопросах разобраться легче. Кроме того, будет проще попробовать другие архитектуры.
  • Сеть преобразования изображения в код на самом деле является моделью, которая автоматически описывает изображения. Несмотря на то, что я понимаю это, я все еще скучаю по многим статьям об автоматическом суммировании изображений, потому что они выглядят недостаточно круто. Как только я понял это, мое понимание проблемного пространства стало намного глубже.

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


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


Адрес FloydHub:www.floydhub.com/


Скопируйте репо:

https://github.com/emilwallner/Screenshot-to-code-in-Keras.git

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

cd Screenshot-to-code-in-Keras
floyd login
floyd init s2c

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

floyd run --gpu --env tensorflow-1.4 --data emilwallner/datasets/imagetocode/2:data --mode jupyter

Все блокноты размещены в каталоге floydbub. Как только мы запустим модель, первую записную книжку можно будет найти в папке floydhub/Helloworld/helloworld.ipynb. См. флаги ранее в этом проекте для более подробной информации.


HTML-версия


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

Обзор

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

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


Особенности дизайн-прототипов


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

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


функция маркера


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

Размерность приведенного выше встраивания слов равна 8, но размерность общего встраивания слов будет варьироваться от 50 до 500 в зависимости от размера словаря. Приведенные выше восемь значений для каждого слова аналогичны весам в нейронных сетях, которые стремятся характеризовать связи между словами (Миколов и др., 2013). Вот как мы начали развертывать функции разметки, функции, на которых нейронная сеть обучается для соединения входных данных с выходными данными.


Кодер


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

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


функция маркера


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

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


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


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

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


Каскадные функции изображения и функции маркировки


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


Как и выше, после копирования функций изображения в соответствующие функции-маркеры мы получаем новую функцию разметки изображения (image-markup функции), которые являются входными значениями, которые мы подаем в декодер.


декодер


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

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

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


Плотный слой будет работать как традиционная сеть прямой связи, соединяя 512 значений в следующей функции метки с последними четырьмя предсказаниями, четырьмя словами, которые у нас есть в словаре: начало, привет, мир и конец. Функция softmax в конце плотного слоя создает распределение вероятностей для четырех классов, например [0,1, 0,1, 0,1, 0,7] будет предсказывать четвертое слово в качестве следующей метки.

# Load the images and preprocess them for inception-resnet
    images = []
    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 layer
    IR2 = InceptionResNetV2(weights='imagenet', include_top=False)
    features = IR2.predict(images)


    # We will cap each input sequence to 100 tokens
    max_caption_len = 100
    # Initialize the function that will create our vocabulary 
    tokenizer = Tokenizer(filters='', split=" ", lower=False)

    # Read a document and return a string
    def load_doc(filename):
        file = open(filename, 'r')
        text = file.read()
        file.close()
        return text

    # Load all the HTML files
    X = []
    all_filenames = listdir('html/')
    all_filenames.sort()
    for filename in all_filenames:
        X.append(load_doc('html/'+filename))

    # Create the vocabulary from the html files
    tokenizer.fit_on_texts(X)

    # Add +1 to leave space for empty words
    vocab_size = len(tokenizer.word_index) + 1
    # Translate each word in text file to the matching vocabulary index
    sequences = tokenizer.texts_to_sequences(X)
    # The longest HTML file
    max_length = max(len(s) for s in sequences)

    # Intialize our final input to the model
    X, 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 encoder
    image_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 decoder
    decoder = concatenate([ir2_out, language_model])
    decoder = LSTM(512, return_sequences=False)(decoder)
    decoder_output = Dense(vocab_size, activation='softmax')(decoder)

    # Compile the model
    model = Model(inputs=[image_features, language_input], outputs=decoder_output)
    model.compile(loss='categorical_crossentropy', optimizer='rmsprop')

    # Train the neural network
    model.fit([image_data, X], y, batch_size=64, shuffle=False, epochs=2)

    # map an integer to a word
    def 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 image
    def 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 HTML
    test_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)

вывод

Адреса сайтов, сгенерированных обучением разных эпох:

Яма, через которую я прошел:

  • Я думаю, что понять LSTM немного сложнее, чем CNN. Их становится немного легче понять, когда я расширяю LSTM. Кроме того, мы можем сосредоточиться на функциях ввода и вывода, прежде чем пытаться понять LSTM.
  • Гораздо проще построить словарь с нуля, чем сжимать огромный словарный запас. Такие сборки включают шрифты, размеры тегов div, шестнадцатеричные цвета для имен переменных и общие слова.
  • Большинство библиотек созданы для разбора текстовых документов. В документации по использованию библиотеки нам скажут, как разбивать по пробелам, а не по коду, нам нужно настроить способ парсинга.
  • Мы можем извлекать признаки из моделей, предварительно обученных в ImageNet. Однако потери примерно на 30% выше по сравнению с моделью pix2code, обученной de novo. Кроме того, я заинтересован в использовании сети inception-resnet, предварительно обученной на основе веб-снимков экрана.

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


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


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


Вместо обучения на загрузочных токенах мы используем 17 упрощенных токенов, которые скомпилированы в HTML и CSS. Набор данных (https://github.com/tonybeltramelli/pix2code/tree/master/datasets) включает 1500 тестовых скриншотов и 250 проверочных скриншотов. В среднем на каждый скриншот приходится 65 токенов, всего 96925 обучающих выборок.


Мы немного изменили модель в документе pix2code, чтобы прогнозировать компоненты сети с точностью 97%.

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


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

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


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


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


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

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

Понимание единиц в иерархии LSTM


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

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

dir_name = 'resources/eval_light/'

    # Read a file and return a string
    def 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 indexes
    train_sequences = tokenizer.texts_to_sequences(texts)
    # The longest set of boostrap tokens
    max_sequence = max(len(s) for s in train_sequences)
    # Specify how many tokens to have in each input sentence
    max_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 encoder
    image_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 decoder
    decoder = 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 model
    model = 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 epoch
    filepath="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 model
    model.fit([image_data, X], y, batch_size=64, shuffle=False, validation_split=0.1, callbacks=callbacks_list, verbose=1, epochs=50)

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


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


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

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


Вы можете увеличить количество n-грамм, модель с 4 n-граммами лучше всего подходит для человеческого перевода. Я предлагаю вам прочитать код ниже:

#Create a function to read a file and return its content
    def 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 vocabulary
    tokenizer = Tokenizer(filters='', split=" ", lower=False)
    #Create the vocabulary in a specific order
    tokenizer.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 model
    loaded_model.load_weights("../../../../weights.hdf5")
    print("Loaded model from disk")

    # map an integer to a word
    def word_for_id(integer, tokenizer):
        for word, index in tokenizer.word_index.items():
            if index == integer:
                return word
        return None
    print(word_for_id(17, tokenizer))

    # generate a description for an image
    def 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 model
    def 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 css
    dsl_path = "compiler/assets/web-dsl-mapping.json"
    compiler = Compiler(dsl_path)
    compiled_website = compiler.compile(predicted[0], 'index.html')

    print(compiled_website )
    print(bleu)

вывод

Ссылка на образец вывода:

Яма, через которую я прошел:

  • Понимание слабых сторон модели вместо тестирования случайных моделей. Сначала я использую случайные вещи, такие как нормализация пакетов, двунаправленная сеть и пытаюсь реализовать механизм внимания. Посмотрев на тестовые данные и зная, что он не может предсказать цвет и положение с высокой точностью, я понял, что у CNN есть слабость. Это привело меня к использованию увеличенного шага вместо максимального объединения. Потери при проверке уменьшились с 0,12 до 0,02, а показатель BLEU увеличился с 85% до 97%.
  • Используйте предварительно обученную модель только в том случае, если они связаны. Я думаю, что в случае небольших данных предварительно обученная модель изображения улучшит производительность. Из моих экспериментов сквозная модель обучается медленнее и требует больше памяти, но повышает точность на 30%.
  • Когда вы запускаете модель на удаленном сервере, нам нужно подготовиться к нескольким отличиям. На моем Mac он читает документы в алфавитном порядке. Но на сервере он расположен рандомно. Это создает несоответствие между кодом и скриншотом.

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


Фронтенд-разработка — идеальное место для приложений глубокого обучения. Данные легко генерировать, и современные алгоритмы глубокого обучения могут отображать большую часть логики. Одной из самых интересных областей является применение механизмов внимания к LSTM. Это не только повышает точность, но также позволяет нам визуализировать, где фокусируется CNN при создании маркеров. Внимание также является ключом к взаимодействию между разметкой, определяемыми шаблонами, сценариями и, в конечном счете, терминалами. Уровень внимания отслеживает переменные, чтобы сеть могла взаимодействовать между языками программирования.


Но в ближайшем будущем наибольшее влияние окажут масштабируемые подходы к синтетическим данным. Затем вы можете шаг за шагом добавлять шрифты, цвета и анимацию. До сих пор большая часть прогресса происходила в эскизах и превращалась в трафаретные аппликации. Менее чем за два года мы создадим скетч, который за секунду найдет соответствующий интерфейс. Команда дизайнеров Airbnb и Уизард создали два рабочих прототипа. Вот некоторые возможные пробные процедуры:


эксперимент

Начинать

  • запустить все модели
  • попробуйте разные гиперпараметры
  • Протестируйте другую архитектуру CNN
  • Добавьте двунаправленную модель LSTM
  • Внедрение моделей с различными наборами данных

Дальнейшие эксперименты

  • Создайте стабильный генератор случайных приложений/веб-страниц с соответствующим синтаксисом.
  • Данные от эскизов до прикладных моделей. Автоматически преобразовывайте скриншоты приложений/веб-страниц в эскизы и используйте GAN для создания разнообразия.
  • Примените слой внимания, чтобы визуализировать каждый прогнозируемый фокус изображения, как в этой модели.
  • Создайте основу для модульного подхода. Например, у вас есть модели кодировщика для шрифтов, одна для цвета, а другая для типографики, и используйте декодер для их интеграции. Стабильные функции изображения — хорошее начало.
  • Скармливайте простые HTML-компоненты нейронной сети и научите ее анимировать с помощью CSS. Было бы интересно использовать метод внимания и визуализировать фокус двух входных источников. 

Оригинальная ссылка:blog.Floyd hub.com/turning-things…