Что показывает демо-приложение

Урок 1 из 17 курса «Llama: начало работы»: официальный курс Llama Cookbook (Мета Лама) на русском языке. Этот урок бесплатный.

Открыть в Colab

Это демонстрационное приложение показывает:

  • Как запустить Llama 3.1 в облаке, размещенном на Replicate
  • Как использовать LangChain для задания Llama общих вопросов и последующих уточняющих вопросов
  • Как использовать LangChain для загрузки недавней веб-страницы — поста в блоге Hugging Face о Llama 3.1 — и обсуждения её содержимого. Это хорошо известный метод RAG (Retrieval Augmented Generation), позволяющий LLM, таким как Llama 3, отвечать на вопросы о данных, которые не были общедоступны во время обучения Llama 3, или о ваших собственных данных. RAG — один из способов предотвратить галлюцинации LLM

Примечание Для запуска примеров мы будем использовать Replicate. Вам потребуется сначала войти в Replicate с помощью вашей учетной записи GitHub, а затем создать бесплатный API token здесь, который вы сможете использовать некоторое время. Вы также можете использовать других облачных провайдеров Llama 3.1, таких как Groq, Together или Anyscale — дополнительную информацию см. в разделе 2 notebook «Знакомство с Llama».

Начнем с установки необходимых пакетов:

  • sentence-transformers для text embeddings
  • FAISS предоставляет нам возможности базы данных
  • LangChain предоставляет необходимые инструменты RAG для этой демонстрации
!pip install langchain
!pip install sentence-transformers
!pip install faiss-cpu
!pip install bs4
!pip install replicate
!pip install langchain-community
from getpass import getpass
import os

REPLICATE_API_TOKEN = getpass()
os.environ["REPLICATE_API_TOKEN"] = REPLICATE_API_TOKEN

Далее мы вызываем чат-модель Llama 3.1 405b из Replicate. Вы также можете использовать модель Llama 3 8B или 70B, заменив имя model на соответствующий URL(ы) модели.

from langchain_community.llms import Replicate
llm = Replicate(
    model="meta/meta-llama-3.1-405b-instruct",
    model_kwargs={"temperature": 0.0, "top_p": 1, "max_new_tokens":500}
)

После настройки модели вы готовы задавать вопросы. Вот пример простейшего способа задать модели общие вопросы.

question = "who wrote the book Innovator's dilemma?"
answer = llm.invoke(question)
print(answer)

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

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

# история чата не передается, поэтому Llama не имеет контекста и не знает, что речь идет о книге
followup = "tell me more"
followup_answer = llm.invoke(followup)
print(followup_answer)

Чтобы обойти это, нам нужно будет предоставить модели историю чата.

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

# используем ConversationBufferMemory для передачи памяти (истории чата) для уточняющих вопросов
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

memory = ConversationBufferMemory()
conversation = ConversationChain(
    llm=llm, 
    memory = memory,
    verbose=False
)

После настройки повторим предыдущие шаги и зададим модели простой вопрос.

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

# начинаем с исходного вопроса
answer = conversation.predict(input=question)
print(answer)
# передаем контекст (предыдущий вопрос и ответ) вместе с уточняющим вопросом "расскажи подробнее" Llama, которая теперь знает больше
memory.save_context({"input": question},
                    {"output": answer})
followup_answer = conversation.predict(input=followup)
print(followup_answer)

Далее рассмотрим использование Llama 3.1 для ответов на вопросы, используя документы в качестве контекста. Это дает нам возможность обновлять знания Llama 3.1, тем самым предоставляя ей лучший контекст без необходимости fine-tuning.

from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import WebBaseLoader
import bs4

loader = WebBaseLoader(["https://huggingface.co/blog/llama3"])
docs = loader.load()

Нам нужно сохранить наш документ в vector store. LangChain поддерживает более 30 vector store (БД). Для этого примера мы будем использовать FAISS, популярный open-source vector store от Facebook. Для других vector store, особенно если вам нужно хранить большой объем данных, см. здесь.

Мы также импортируем HuggingFaceEmbeddings и RecursiveCharacterTextSplitter для помощи в хранении документов.

# Разделяем документ на чанки с указанным размером чанка
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
all_splits = text_splitter.split_documents(docs)

# Сохраняем документ в vector store с определенной embedding моделью
vectorstore = FAISS.from_documents(all_splits, HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2"))

Чтобы сохранить документы, нам потребуется разбить их на чанки с помощью RecursiveCharacterTextSplitter и создать их векторные представления с использованием HuggingFaceEmbeddings, прежде чем сохранять их в нашу векторную базу данных.

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

Затем мы используем RetrievalQA для извлечения документов из векторной базы данных и предоставления модели большего контекста о Llama 3.1, тем самым расширяя её знания. Версия 3.1 также по-настоящему раскрывает свой потенциал с новым контекстом в 128k!

Для каждого вопроса LangChain выполняет поиск семантического сходства в vector db, затем передает результаты поиска в качестве контекста Llama для ответа на вопрос.

# используем RetrievalQA от LangChain, чтобы связать Llama 3 с загруженными документами, хранящимися в vector db
from langchain.chains import RetrievalQA

qa_chain = RetrievalQA.from_chain_type(
    llm,
    retriever=vectorstore.as_retriever()
)

question = "What's new with Llama 3?"
result = qa_chain({"query": question})
print(result['result'])

Теперь объединим все это, добавив уточняющие вопросы.

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

# контекст не передан, поэтому у Llama 3 недостаточно контекста для ответа, и она дает волю своему воображению
result = qa_chain({"query": "Based on what architecture?"})
print(result['result'])

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

# используем ConversationalRetrievalChain для передачи истории чата для уточняющих вопросов
from langchain.chains import ConversationalRetrievalChain
chat_chain = ConversationalRetrievalChain.from_llm(llm, vectorstore.as_retriever(), return_source_documents=True)
# давайте снова зададим исходный вопрос "Что нового в Llama 3?"
result = chat_chain({"question": question, "chat_history": []})
print(result['answer'])
# на этот раз мы передаем историю чата вместе с уточняющим вопросом, так что все должно пройти хорошо
chat_history = [(question, result["answer"])]
followup = "Based on what architecture?"
followup_answer = chat_chain({"question": followup, "chat_history": chat_history})
print(followup_answer['answer'])
# дальнейшие уточняющие вопросы можно задавать, обновляя chat_history следующим образом:
chat_history.append((followup, followup_answer["answer"]))
more_followup = "What changes in vocabulary size?"
more_followup_answer = chat_chain({"question": more_followup, "chat_history": chat_history})
print(more_followup_answer['answer'])

Примечание: Если результаты могут быть обрезаны, вы можете установить "max_new_tokens" в вызове Replicate выше на большее число (как показано ниже), чтобы избежать обрезания.

model_kwargs={"temperature": 0.01, "top_p": 1, "max_new_tokens": 1000}

Полезные гиды