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

Вот краткий обзор пользовательского интерфейса для сбора отзывов людей, который мы будем создавать в этом пошаговом руководстве:

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

Давайте погрузимся! 🏄‍♂️

Инструменты

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

  • oxen для беспрепятственного скрытого сбора данных и управления версиями
  • gradio для создания простого пользовательского интерфейса для маркировки данных исключительно на Python
  • huggingface + torch для создания изображений-кандидатов с помощью Stable Diffusion.
  • Экземпляр Lambda labs для экономичных и простых в настройке вычислений на GPU. Для этого руководства мы использовали экземпляр Lambda 1xA10, который на момент написания стоил 0,60 доллара США в час, и выполнили один шаг вывода из 4 изображений примерно за 30 секунд.

Настройка пользовательского интерфейса

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

Полный код этого туториала доступен здесь.

import gradio as gr

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

На следующих шагах вы можете запустить свое приложение в режиме горячей перезагрузки, запустив gradio app.py в терминале.

Подсказка ввода: текстовое поле и кнопка

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

with gr.Blocks() as demo: 
    prompt = gr.components.Textbox(label="Enter Prompt")
    generate = gr.Button("Generate candidate images")

Рендеринг изображений и голосование пользователей

Нам нужны четыре панели изображений, четыре кнопки «за» и четыре кнопки «против», все они будут иметь одинаковый стиль и управляться ими. Чтобы не повторяться, мы можем рендерить их итеративно и сохранять в dict (или list), как показано ниже.

Мы также будем содержать их в Row, чтобы отображать их горизонтально.

images = {}
upvote_buttons = {}
downvote_buttons = {}
# with gr.Blocks() as demo...
    for i in range(1, 5):
        with gr.Column():
            images[i] = gr.components.Image(label=f"Candidate Image {i}", type="pil").style(full_width=False)
            with gr.Row():
                upvote_buttons[i] = gr.Button(value="👍")
        downvote_buttons[i] = gr.Button(value="👎")

Теперь у нас есть основные статические элементы интерфейса — давайте соединим точки и начнем визуализировать некоторые фиктивные данные!

Добавление интерактивности

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

def generate_images(prompt):
    dummy_img = PIL.Image.open("a-dummy-image")
    return [dummy_img]*4

def save_image_to_repo(img_prompt, img, button):
    print(f"Applying {button} rating to image {img} with prompt {img_prompt}")
    
# Add event handlers
# with gr.Blocks() as demo:
# ...
generate.click(generate_images, inputs=prompt, outputs=list(images.values())

for i in range(len(1,5)):
    upvote_buttons[i].click(save_image_to_repo, inputs=[prompt, images[i], upvote_buttons[i]])
    downvote_buttons[i].click(save_image_to_repo, inputs=[prompt, images[i], downvote_buttons[i]])

Для обработчика на generate: передайте один ввод, текстовую подсказку, и ожидайте четыре изображения в качестве вывода, передавая каждое в один из четырех блоков gr.components.Image в нашем макете.

Для upvote_buttons и downvote_buttons звоните save_image_to_repo, мы передаем:

  • prompt - для сохранения на волах в качестве обучающих данных
  • images[i] - изображение, которое вызвало нажатие кнопки
  • upvote_buttons[i] — значение самой кнопки, поэтому мы можем сказать, была ли это 👍 или 👎 при сохранении этих данных в oxen.

Фальшивые изображения теперь отображаются после нажатия кнопки «Создать», и данные изображения правильно направляются при голосовании «за» / «против».

Вперед к созданию реальных изображений!

Генерация изображений с помощью стабильной диффузии

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

from diffusers import StableDiffusionPipeline

# Set up pipeline for use in our generate_images fn
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4")

# Use GPU for inference
pipe.to("cuda")

# Update generate_images to run an inference step and return 4 images
def generate_images(prompt):
    images = pipe(prompt, num_images_per_prompt=4).images
  return images

… и это все, что нужно! Проверьте это, сгенерировав изображение из любого подсказки, которое вы можете придумать, отметив, что это может занять некоторое время в зависимости от характеристик графического процессора вашей машины. Нашему экземпляру Lambda Labs (спецификации указаны ранее) потребовалось около 30 секунд для создания четырех изображений.

Вы можете проверить его ход, выполнив 50 шагов вывода в терминале, где запущено ваше gradio приложение.

### Сбор данных отзывов людей с помощью Oxen

Мы начнем с создания репозитория на OxenHub, в котором будут храниться данные изображений и человеческие оценки, используя следующую структуру:

[repo-name]
    - images
        - 0001.png
        - 0002.png
        - # etc. 
    - train.csv

Где train.csv включает столбцы для prompt, path изображения и rating, назначенные пользователем в нашем приложении gradio. См. пример здесь.

После создания этого репозитория мы можем настроить наш скрипт на Python, чтобы мы могли удаленно вносить в него коммиты!

Аутентификация с помощью волов

  1. Создайте аккаунт на oxen.ai
  2. Создайте репозиторий для хранения данных обратной связи:

3. Получите ключ API Oxen на вкладке Профиль на oxen.ai.

4. Если его еще нет, создайте каталог .oxen в своем домашнем каталоге.

5. Затем в записной книжке или интерактивной оболочке Python выполните следующее:

# Only needs to be run once - no need to include in app 
import oxen
oxen.auth.create_user_config(name="your name", email="[email protected]")
oxen.auth.add_host_auth(host="hub.oxen.ai", token=YOUR_TOKEN)

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

Запись в репозиторий

Настройте объект RemoteRepo следующим образом, чтобы упростить добавление без необходимости локального клонирования репо:

from oxen import RemoteRepo
repo = RemoteRepo("your-namespace/your-repo-name") # ba/stable-diffusion-human-feedback
repo.checkout("branch-name", create=True) # For new branch
IMAGE_FOLDER = "images"

Затем адаптируйте обработчик save_image_to_repo для удаленного добавления данных как в папку images, так и в фрейм данных train.csv при каждом нажатии кнопки.

def get_rating(label):
    # Helper to get plaintext rating from clicked button
  if label == '👍':
      return 'good'
  elif label == '👎':
      return 'bad'
  else:
      raise ValueError(f"Unexpected rating label: {label}")

def save_image_to_repo(img_prompt, img, button):
    # Randomize to avoid collisions 
    filename = f"{shortuuid.uuid()}.png"
    # Temporarily save image to disk
    img.save(filename)
    # Create new row to be added to df 
    row = {"prompt": img_prompt, "path": f"{IMAGE_DEST}/{filename}", "rating": get_rating(button)}
    
    # Remotely stage files to repo 
    try:  
        repo.add(filename, IMAGE_DEST)
        repo.add_df_row("train.csv",  row)
        repo.commit(f"Add from RLHF: {img_prompt}")
    except Exception as e:
        # Unstage changes if any part of add-commit flow was unsuccessful
        repo.remove(filename)
        repo.restore_df("train.csv")
        # Clean up temp file
    os.remove(filename)

Проверьте это!

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

Спасибо за прочтение!

В OxenAI мы хотим видеть, что вы строите! Свяжитесь с нами по адресу [email protected], подпишитесь на нас в Твиттере @oxendrove, изучите документацию или Зарегистрируйтесь в Oxen сегодня.

И помните — за каждую звезду на GitHub вол получает крылья.