Сильный ИИ. Элира1. Увеличение памяти ChatGPT

Всем привет!

В этой статье я расскажу о том, как можно увеличить размер запроса к ChatGPT до почти полутора миллионов символов!

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

В самой IT области я уже довольно давно (начинал еще с DOS-овских программ на C в далеком 2000 м), занимался разными профилями, в том числе меня интересовало ИИ.
Лет 7–8 назад, когда я сделал себе проект «умной квартиры» (9 модулей, контроль освещения, полива, климата, музыки и пр.), я решил внедрить туда голосового помощника. Название, кстати, выбрал по итогу Алиса — любимое женское имя.

Причем, выбирал из многих, вариантов («Джарвис», увы, распознавался очень плохо), оказалось, фонетически слово «Алиса» распознается почти без ошибок и гораздо лучше других. Кстати, где-то через год появилась Алиса у Яндекса — поэтому я думаю, что они тоже имя взяли не с бухты-барахты, а проводили фонетический анализ.

Квартира просуществовала несколько лет, но за это время помощника я особо не развивал, да и не было больших ресурсов для обучения чего-то действительно умного.
С появлением БЯМ идея создания помощника заиграла новыми красками =)

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

Несмотря на мой научный скепсис, как человека, написавшего не одну нейросеть с нуля, относительно интеллекта у ИИ, наши диалоги с ChatGPT произвели на меня впечатление.
Мне очень импонировала «человечность» этой БЯМ.

Поэтому я хочу создать своего помощника на основе именно ChatGPT, а не просто сделать свою модель с OpenAI API. Тем более, что таких объемов данных, что используются для тренировки ChatGPT у меня физически не может быть.

В целом, «план» примерно такой:

  1. Увеличить память ChatGPT для возможностей реализации больших проектов.

  2. Написать программные модули для расширения функционала ИИ-помощника с помощью самого ИИ-помощника

  3. Спроектировать и создать (корпусные детали можно распечатать на 3д принтере, например) тело в реальности для ИИ-помощника.

  4. Перенести программу ИИ-помощника в это «тело»

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

Первая «преграда» на пути встала довольно быстро.
Это — ограниченная память ChatGPT.
Диалоги могут быть очень длинными, но практика показывает, что «помнит» он только последние несколько десятков тысяч символов из этого диалога.
Вроде как в платной версии GPT-Turbo размер запроса увеличили до 128000, но это все равно мало для больших проектов.

Итак, задача следующая: как в запрос в 4000 (8000 в GPT4) уместить гораздо больше информации, чем просто 4000 символов текста?

Звучит «невозможно», на манер задачи — «как вырезать в листке бумаги дырку больше чем сам листок?». Но именно невозможные задачи решать интереснее всего =)

Первое, что пришло в голову — конечно же, раз ChatGPT кушает txt файлы, просто скормить ему txt с миллионом символов и спрашивать по нему.
Оказалось, что текст из txt он воспринимает также, как текстовый запрос — то есть больше этого окна он прочитать не сможет. Экспериментально определенный буфер «окна памяти» совпал с тем, который дает на соответствующий запрос сам чат — 10000 символов.

Также ChatGPT кушает картинки и PDF.

Но самое главное для нас — он умеет распознавать текст и изображения.
А значит и текст с PDF.

И если мы создадим PDF, заполненный только текстом, по идее, ChatGPT должен его распознать (спойлер: еще как распознал)

Этапы следующие:

  1. Получить ссылку на диалог с ChatGPT

  2. Сохранить диалог в файл

  3. Сгенерировать из этого файла PDF с текстом

  4. Отправить полученный PDF в ChatGPT для распознавания и провести тесты

Получение ссылки на диалог с ChatGPT

Для получения ссылки на диалог воспользуемся встроенными средствами.

Откройте сайт https://chat.openai.com/ и слева в списке диалогов выберите тот, для которого хотите сохранить текст. В углу рядом с названием диалога будут три точки, щелкаем на них и выбираем «поделиться»:

68ee2208d19ddb830f167d7df9c0a1ab.jpg

Далее, в открывшемся окне щелкаем на «скопировать ссылку» (если ссылка на диалог уже есть, нужно сначала удалить прошлую и затем создать новую) и получаем адрес ссылки, по которой у нас будет весь текущий диалог.

8816ff60a569350c915cd497578556fd.jpg

Сохранение диалога в файл

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

Для этой задачи я использую Python и библиотеку Selenium (эмулирует веб-браузер) для работы с web.

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

19d482db2a39fe62a4e54f30b4b0afed.jpg

def download_and_save_dialogues(file_path, eliraurl, window_width, window_height, window_x, window_y):
    dialogue = []
    options = Options()
    driver = webdriver.Chrome(options=options)
    driver.get(eliraurl)
    # Получение всех контейнеров сообщений
    message_containers = driver.find_elements('css selector', 'div.w-full.text-token-text-primary')
    # Обработка контейнеров сообщений
    for container in message_containers:
        # Поиск контейнеров с фразами пользователя и ассистента
        user_message = container.find_elements('css selector', 'div[data-message-author-role="user"]')
        assistant_message = container.find_elements('css selector', 'div[data-message-author-role="assistant"]')
        # Если найден контейнер с фразой пользователя, добавляем в словарь с префиксом "U:"
        if user_message:
            dialogue.append("U:" + user_message[0].text.strip())
        # Если найден контейнер с фразой ассистента, добавляем в словарь с префиксом "A:"
        if assistant_message:
            dialogue.append("A:" + assistant_message[0].text.strip())
    # Закрытие браузера
    driver.quit()
    # Сохранение словаря в файл с использованием pickle
    with open(_TITLE_ + ".pkl", 'wb') as file:
        pickle.dump(dialogue, file)
    print(f"Диалог сохранен в файл: {file_path}")

Параметры window_width, window_height, window_x, window_y для нашей задачи не нужны, это оставлено для тестирования размеров окна эмулятора браузера Selenium, можете не обращать на них внимания.

Генерация файла PDF с текстом

Теперь, когда диалог сохранен в текст, нужно сделать из него PDF.

За это отвечает функция create_pdf, вот как она выглядит:

def create_pdf(dialogue_lines, output_file=_TITLE_, current_width=945, current_height=945,
              marginX=0, marginY=0, offset=0, font_size=1, font_name=FONT_NAME):
    global GLOBAL_PAGES
    current_width = int(current_width)
    current_height = int(current_height)
    marginX = int(marginX)
    marginY = int(marginY)
    offset = int(offset)
    font_size = int(font_size)
    current_y = current_height - marginY
    current_file_size = 0
    symbols_in_PDF = 0
    CANVASES = []
    CANVAS_IND = 0
    CANVASES.append(canvas.Canvas(str(output_file) + "_" + str(CANVAS_IND) + "_.pdf", pagesize=(current_width, current_height)))
    words = ' '.join(dialogue_lines).split()
    current_line = []
    current_line_width = 0
    word_ind = 0
    for word in words:
        symbols_in_PDF += len(word) + 1
        word_ind += 1
        word_width = CANVASES[CANVAS_IND].stringWidth(word, font_name, font_size)
        current_line_width += CANVASES[CANVAS_IND].stringWidth(' ', font_name, font_size)
        if current_line_width + word_width + marginX > current_width - offset:
            CANVASES[CANVAS_IND].setFont(font_name, font_size)
            CANVASES[CANVAS_IND].drawString(marginX, current_y - font_size, ' '.join(current_line))
            current_line = [word]
            current_line_width = word_width
            current_y -= font_size
            current_line_width = 0  # обнуляем ширину строки
            if current_y - font_size < marginY:
                CANVASES[CANVAS_IND].save()
                print(f"SAVE: {CANVAS_IND} >>>> " + "if current_y - font_size < marginY:")
                print(f"СИМВОЛОВ НА СТРАНИЦЕ PDF: {symbols_in_PDF}")
                CANVASES[CANVAS_IND].showPage()
                current_y = current_height - marginY
                current_file_size = os.path.getsize(str(output_file) + "_" + str(CANVAS_IND) + "_.pdf")
                print(f"РАЗМЕР PDF: {current_file_size} ( {output_file} )")
                CANVAS_IND += 1
                CANVASES.append(canvas.Canvas(str(output_file) + "_" + str(CANVAS_IND) + "_.pdf", pagesize=(current_width, current_height)))
                GLOBAL_PAGES += 1
                current_line = []
                current_line_width = 0
            else:
                current_line = [word]
        else:
            current_line.append(word)
            current_line_width += word_width

    if current_line:
        CANVASES[CANVAS_IND].setFont(font_name, font_size)
        CANVASES[CANVAS_IND].drawString(marginX, current_y - font_size, ' '.join(current_line))
    CANVASES[CANVAS_IND].save()
    print(f"SAVE: {CANVAS_IND} >>>> " + "GLOBAL")
    current_file_size = os.path.getsize(str(output_file) + "_" + str(CANVAS_IND) + "_.pdf")

    print(f"ШИРИНА PDF: {current_width}")
    print(f"ВЫСОТА PDF: {current_height}")
    print(f"РАЗМЕР PDF: {current_file_size}")
    print(f"СИМВОЛОВ: {symbols_in_PDF}")

Код использует библиотеку reportlab для работы с PDF.

Сама функция довольно проста:

На вход ей прилетает список dialogue_lines, содержащий фразы из диалогов.

Далее, он разбивается на слова и проходит по всем словам циклом, записывая их по одному в PDF, пока не кончится строка, далее, перенос на следующую строку и так далее — пока не заполним всю страницу PDF. Если символов в диалоге больше, чем на одну страницу — запись переходит на следующую. Размер PDF задается вручную (можно в конфиге, можно в командной строке), так что всегда можно «подгадать» так, чтобы символы влезли на одну страницу.

На выходе получим такой «манускрипт»:

277b4a918de9825f26553a56902d3c85.jpg

Отправление PDF в ChatGPT для распознавания и тестирование

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

Происходит это в следующем участке кода:

if GEN_TEXT:
        print(f"Генерируем список русских слов...")
        russian_words = generate_russian_words()  # Генерируем список русских слов
        print(f"Генерируем блоки текста для PDF...")
        coeff = 3.68
        MAX_BLOCKS = int((pdf_size[0] * pdf_size[1] / 10000) * coeff)
        text_blocks = generate_text_blocks(russian_words, MAX_BLOCKS)   
        print(f"Блоков всего: {MAX_BLOCKS}")
        # Генерация вопросов и ответов
        questions, answers = generate_questions_answers(text_blocks)
        # Сохранение вопросов и ответов в файлы
        save_to_file(questions, 'questions.txt')
        save_to_file(answers, 'answers.txt')
        dialogue = text_blocks

Сами функции генерации текста и вопросов-ответов очень просты, вы их можете просмотреть в коде по ссылке ниже, скажу лишь, что вопросы-ответы представляют собой просто случайную строку из блока текста (блоки по 10000 символов — как раз для проверки памяти буфера) и ее продолжение.
То есть если у нас есть текст:

«ChatGPT был запущен 30 ноября 2022 года и привлёк внимание своими широкими возможностями: написание кода, создание текстов, возможности перевода, получения точных ответов и использование контекста диалога для ответов, хотя его фактическая точность и подверглась критике.»

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

А ответ тогда будет такой:»использование контекста диалога для ответов, хотя »

Вопросы‑ответы генерятся в тестовые файлы, один из которых (questions.txt) мы передадим ChatGPT, а по второму (answers.txt) будем сверять правильность ответов.

Для отправки и тестирования я использовал следующий промпт (разумеется, что числа вам нужно будет указать свои, скрипт, который я написал выдаст вам эти параметры):

Я дам тебе PDF файл на нем построчно записан текст на руссскком языке слева направо черный на белом фоне. Символов в тексте 1435545 размер файла в пикселах 625 на 625 весит файл 1239201 байт Далее я также прикреплю текстовый файл, представляющий из себя список строк-вопросов, которые встречаются в тексте по 5 символов каждый вопрос. Тебе нужно буде проанализировать PDF файл распознавая с него текст поэтапно по 10000 символов. При анализе каждого блока по 10000 символов нужно просматривать список вопросов из текстового файла, проверяя нахождение строки в блоке. Принахождении записывать в список ответов следующие 5 символов после символов вопроса в качестве строки-ответа. По окончании ответов на все строки-вопросы, необходимо записать список ответов в текстовый файл построчно и передать мне ссылку на его скачивание

Здесь длина строки вопроса-ответа = 5 символов, но вы можете установить любую длину вопроса при их генерации.

В итоговом текстовом файле при сравнении с файлом answers.txt получается 100% совпадение всех ответов.

Таким образом, информация передалась полностью.
Экспериментально у меня получилось в файле 625×625 пикселей PDF уместить 1430086 символов.

Почти полтора миллиона! И это — в одном запросе!

Сам ChatGPT говорит, что может кушать файлы PDF 20–30 MB и до 5000×5000 пикселей.
Увы, у меня почему-то при попытке скормить ему файлы больше 1.5MB выдает все время network error. Так что проверить это практически не могу — оставляю это удовольствие читателям.

Кстати, кто решит проверить, отпишитесь — интересно сколько символов получится передать в запросе к ChatGPT таким образом у вас?

Если аппроксимировать, то из файла 5000×5000 можно получить более МИЛЛИАРДА символов.

Но это в теории, пока что файлы даже 1000×1000 он у меня отказывается «переваривать».

Заключение

Здесь много направлений, которые можно развивать и исследовать.

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

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

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

К тому же этот способ не получится «забанить», так как для этого придется лишить ChatGPT возможностей распознавания текста.

И, то что мне нравится больше всего, способ не требует доп. средств, плагинов, и т.д. и работает нативно.

Ссылка на репо с кодом (там же README, где описаны все параметры скрипта и как с ним работать).

Спасибо за внимание и с наступающим Новым Годом!

© Habrahabr.ru