Как создать клон ChatGPT (с переключением контекста и контекста)

Как создать клон ChatGPT (с переключением контекста и контекста)

7 марта 2023 г.

Это руководство является частью моей книги "OpenAI GPT для разработчиков Python".

Цель этой книги — предоставить пошаговое руководство по использованию GPT-3 в ваших проектах через этот API, но не только это — многие другие инструменты и модели, созданные OpenAI, такие как Whisper (автоматическое распознавание речи (ASR) система, обученная на 680 000 часов многоязычных и многозадачных контролируемых данных), CLIP (предварительное обучение контрастному языку и изображению), нейронная сеть, обученная на различных парах (изображение, текст), и DALL·E 2, новая система искусственного интеллекта, которая может создавать реалистичные изображения и произведения искусства из описания на естественном языке.

OpenAI GPT For Python Developers:  https://leanpub.com/openaigptforpythondevelopers/


Проблема

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

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

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

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

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

Нет контекста = Хаос случайности

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

import openai
import os

def init_api():
    with open(".env") as env:
        for line in env:
            key, value = line.strip().split("=")
            os.environ[key] = value

    openai.api_key = os.environ.get("API_KEY")
    openai.organization = os.environ.get("ORG_ID")

init_api()

initial_prompt = """You: Hi there!
You: Hello!
AI: How are you?
You: {}
AI: """

while True:
    prompt = input("You: ")

    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=initial_prompt.format(prompt),
        temperature=1,
        max_tokens=100,
        stop=[" You:", " AI:"]
    )

    print("AI:", response.choices[0].text)

n Следующий тест показывает, что созданный нами чат-агент по умолчанию не имеет памяти:

Вы: Привет

ИИ: Эй! Чем я могу вам помочь сегодня?

Вы: Запомните эту строку "бла" и сохраните ее. Я спрошу вас об этом позже.

ИИ: Конечно, я помню строку "бла".

Вы: Что это была за строка?

AI: Я не понимаю, что вы имеете в виду. Можно уточнить?

Вы: Какую строку я велел вам запомнить?

AI: Извините, я не помню ни одной строки, которую вы мне сказали.

Вы: Я уже говорил вам!

AI: Извините, я не расслышал. Не могли бы вы объяснить контекст последнего сообщения?

История = Контекст

Идея довольно проста: создается переменная истории, в которой мы храним запрос пользователя и текст, сгенерированный моделью. Когда пользователь задает новый вопрос, история вставляется перед новым вопросом.

import openai
import os

def init_api():
    with open(".env") as env:
        for line in env:
            key, value = line.strip().split("=")
            os.environ[key] = value

    openai.api_key = os.environ.get("API_KEY")
    openai.organization = os.environ.get("ORG_ID")

init_api()

initial_prompt = """You: Hi there!
You: Hello!
AI: How are you?
You: {}
AI: """

history = ""

while True:
    prompt = input("You: ")
    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=initial_prompt.format(history + prompt),
        temperature=1,
        max_tokens=100,
        stop=[" You:", " AI:"],
    )

    response_text = response.choices[0].text
    history += "You: "+ prompt + "n" + "AI: " + response_text + "n"

    print("AI: " + response_text)

Вот как прошла та же дискуссия:

Вы: Привет n ИИ: Привет! Как дела? n Вы: Запомните эту строку «бла» и сохраните ее. Я спрошу тебя об этом позже. n AI: Понятно! Что бы вы хотели узнать о «бла»? n Вы: Что это была за строка? n ИИ: Это была «бла» строка. n Вы: Почему? n ИИ: Вы попросили меня запомнить строку «бла» и сохранить ее, что я и сделал.

Проблема с переносом истории

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

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

Память «последним поступил – первым обслужен» (LIFO)

Я не уверен, есть ли у этого подхода техническое название, но я назвал его "последний пришел - первый ушел", поскольку его идея проста:

* Пользователи всегда будут инициировать обсуждения с контекстом.

* Контекст меняется, как и обсуждение.

* Пользователи, скорее всего, будут включать контекст в последние 2–5 запросов.

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

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

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

Давайте посмотрим на код:

import openai
import os

def init_api():
    with open(".env") as env:
        for line in env:
            key, value = line.strip().split("=")
            os.environ[key] = value

    openai.api_key = os.environ.get("API_KEY")
    openai.organization = os.environ.get("ORG_ID")

def save_history_to_file(history):
    with open("history.txt", "w+") as f:
        f.write(history)

def load_history_from_file():
    with open("history.txt", "r") as f:
        return f.read()

def get_relevant_history(history):
    history_list = history.split(separator)
    if len(history_list) > 2:
        return separator.join(history_list[-2:])
    else:
        return history        

init_api()

initial_prompt = """You: Hi there!
You: Hello!
AI: How are you?
You: {}
AI: """

history = ""
relevant_history = ""
separator = "#####"

while True:
    prompt = input("You: ")
    relevant_history = get_relevant_history(load_history_from_file())

    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=initial_prompt.format(relevant_history + prompt),
        temperature=1,
        max_tokens=100,
        stop=[" You:", " AI:"],
    )

    response_text = response.choices[0].text
    history += "nYou: "+ prompt + "n" + "AI: " + response_text + "n" + separator
    save_history_to_file(history)

    print("AI: " + response_text)

Проблема с последним в памяти

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

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

Это может привести к путанице и разочарованию пользователя, что не идеально подходит для взаимодействия с человеком.

Выборочный контекст

Решение, предложенное в этой части, будет работать следующим образом:

* Начальная подсказка сохраняется в текстовом файле.

* Пользователь вводит подсказку.

* Программа создает вложения для всех взаимодействий в файле.

* Программа создает вложения для подсказки пользователя.

* Программа вычисляет косинусное сходство между подсказкой пользователя и всеми взаимодействиями в файле.

* Программа сортирует файл по косинусному сходству.

* Лучшие n взаимодействий считываются из файла и отправляются с подсказкой пользователю.

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

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

def save_history_to_file(history):
    """
    Save the history of interactions to a file
    """
    with open("history.txt", "w+") as f:
        f.write(history)

def load_history_from_file():
    """
    Load all the history of interactions from a file
    """
    with open("history.txt", "r") as f:
        return f.read()

def cos_sim(a, b):
    """    
    Calculate cosine similarity between two strings
    Used to compare the similarity between the user input and a segments in the history
    """
    a = nlp(a)
    a_without_stopwords = nlp(' '.join([t.text for t in a if not t.is_stop]))
    b = nlp(b)
    b_without_stopwords = nlp(' '.join([t.text for t in b if not t.is_stop]))
    return a_without_stopwords.similarity(b_without_stopwords)

def sort_history(history, user_input):   
    """
    Sort the history of interactions based on cosine similarity between the user input and the segments in the history
    History is a string of segments separated by separator
    """
    segments = history.split(separator)     
    similarities = []    

    for segment in segments:
        # get cosine similarity between user input and segment
        similarity = cos_sim(user_input, segment)
        similarities.append(similarity)        
    sorted_similarities = np.argsort(similarities)
    sorted_history = ""
    for i in range(1, len(segments)):
        sorted_history += segments[sorted_similarities[i]] + separator
    save_history_to_file(sorted_history)

def get_latest_n_from_history(history, n):
    """
    Get the latest n segments from the history.
    History is a string of segments separated by separator
    """
    segments = history.split(separator)
    return separator.join(segments[-n:])

Пошаговое объяснение того, что делает функция sort_history:

  1. Разбить историю на сегменты: функция сначала разбивает входную строку истории на сегменты на основе указанного разделителя (в нашем примере мы будем использовать ##### который мы объявим позже). При этом создается список сегментов, представляющих каждое взаимодействие в истории.

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

Хотя мы могли бы использовать встраивание OpenAI, наша цель — снизить затраты на вычисления, выполняя определенные задачи локально, а не полагаясь на API.

3. Сортировать сходства. Функция сортирует сходства в порядке возрастания, используя np.argsort, который возвращает индексы отсортированных сходств в порядке их значений. Это создает список индексов, представляющих сегменты, отсортированные по их сходству с пользовательским вводом.

4. Реконструировать отсортированную историю: мы перебираем отсортированные индексы в обратном порядке и объединяем соответствующие сегменты вместе в новую строку. Это создает новую, отсортированную строку истории, в которой действия, наиболее похожие на ввод пользователя, отображаются первыми.

5. Сохранить отсортированную историю. Наконец, мы сохраняем отсортированную историю в файл с помощью функции save_history_to_file.

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

initial_prompt_1 = """
You: Hi there!
AI: Hello!
#####
You: How are you?
AI: I am fine, thank you.
#####
You: Do you know cars?
AI: Yes I have some knowledge about cars.
#####
You: Do you eat Pizza?
AI: I don't eat pizza. I am an AI that is not able to eat.
#####
You: Have you ever been to the moon?
AI: I have never been to the moon. What about you?
#####
You: What is your name?
AI: My name is Pixel. What is your name?
#####
You: What is your favorite movie?
AI: My favorite movie is The Matrix. Follow the white rabbit :)
#####
"""

initial_prompt_2 ="""You: {}
AI: """
initial_prompt = initial_prompt_1 + initial_prompt_2
separator = "#####"

init_api()
save_history_to_file(initial_prompt_1)

while True:
    prompt = input("You: ")    
    sort_history(load_history_from_file(), prompt)
    history = load_history_from_file()
    best_history = get_latest_n_from_history(history, 5)
    full_user_prompt = initial_prompt_2.format(prompt)
    full_prompt = best_history + "n" + full_user_prompt
    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=full_prompt,
        temperature=1,
        max_tokens=100,
        stop=[" You:", " AI:"],
    )
    response_text = response.choices[0].text.strip()
    history += "n" + full_user_prompt + response_text + "n" + separator + "n"
    save_history_to_file(history)

    print("AI: " + response_text)

Если сложить все воедино, то получится вот что:

import openai
import os
import spacy
import numpy as np

# Load the pre-trained spaCy model
nlp = spacy.load('en_core_web_md')

def init_api():
    with open(".env") as env:
        for line in env:
            key, value = line.strip().split("=")
            os.environ[key] = value

    openai.api_key = os.environ.get("API_KEY")
    openai.organization = os.environ.get("ORG_ID")

def save_history_to_file(history):
    """
    Save the history of interactions to a file
    """
    with open("history.txt", "w+") as f:
        f.write(history)

def load_history_from_file():
    """
    Load all the history of interactions from a file
    """
    with open("history.txt", "r") as f:
        return f.read()

def cos_sim(a, b):
    """    
    Calculate cosine similarity between two strings
    Used to compare the similarity between the user input and a segments in the history
    """
    a = nlp(a)
    a_without_stopwords = nlp(' '.join([t.text for t in a if not t.is_stop]))
    b = nlp(b)
    b_without_stopwords = nlp(' '.join([t.text for t in b if not t.is_stop]))
    return a_without_stopwords.similarity(b_without_stopwords)

def sort_history(history, user_input):   
    """
    Sort the history of interactions based on cosine similarity between the user input and the segments in the history
    History is a string of segments separated by separator
    """
    segments = history.split(separator)     
    similarities = []    

    for segment in segments:
        # get cosine similarity between user input and segment
        similarity = cos_sim(user_input, segment)
        similarities.append(similarity)        
    sorted_similarities = np.argsort(similarities)
    sorted_history = ""
    for i in range(1, len(segments)):
        sorted_history += segments[sorted_similarities[i]] + separator
    save_history_to_file(sorted_history)

def get_latest_n_from_history(history, n):
    """
    Get the latest n segments from the history.
    History is a string of segments separated by separator
    """
    segments = history.split(separator)
    return separator.join(segments[-n:])



initial_prompt_1 = """
You: Hi there!
AI: Hello!
#####
You: How are you?
AI: I am fine, thank you.
#####
You: Do you know cars?
AI: Yes I have some knowledge about cars.
#####
You: Do you eat Pizza?
AI: I don't eat pizza. I am an AI that is not able to eat.
#####
You: Have you ever been to the moon?
AI: I have never been to the moon. What about you?
#####
You: What is your name?
AI: My name is Pixel. What is your name?
#####
You: What is your favorite movie?
AI: My favorite movie is The Matrix. Follow the white rabbit :)
#####
"""

initial_prompt_2 ="""You: {}
AI: """
initial_prompt = initial_prompt_1 + initial_prompt_2
separator = "#####"

init_api()
save_history_to_file(initial_prompt_1)

while True:
    prompt = input("You: ")    
    sort_history(load_history_from_file(), prompt)
    history = load_history_from_file()
    best_history = get_latest_n_from_history(history, 5)
    full_user_prompt = initial_prompt_2.format(prompt)
    full_prompt = best_history + "n" + full_user_prompt
    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=full_prompt,
        temperature=1,
        max_tokens=100,
        stop=[" You:", " AI:"],
    )
    response_text = response.choices[0].text.strip()
    history += "n" + full_user_prompt + response_text + "n" + separator + "n"
    save_history_to_file(history)


Это руководство является частью моей книги "OpenAI GPT для разработчиков Python".

OpenAI GPT For Python Developers:  https://leanpub.com/openaigptforpythondevelopers/

Цель этой книги — предоставить пошаговое руководство по использованию GPT-3 в ваших проектах через этот API, но не только это — многие другие инструменты и модели, созданные OpenAI, такие как Whisper (автоматическое распознавание речи (ASR) система, обученная на 680 000 часов многоязычных и многозадачных контролируемых данных), CLIP (предварительное обучение контрастному языку и изображению), нейронная сеть, обученная на различных парах (изображение, текст), и DALL·E 2, новая система искусственного интеллекта, которая может создавать реалистичные изображения и произведения искусства из описания на естественном языке.

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

Если вы владеете основами языка программирования Python и готовы изучить еще несколько методов, таких как использование Pandas Dataframes и некоторых методов NLP, у вас есть все необходимые инструменты для создания интеллектуальных систем с использованием инструментов OpenAI.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE