Это демонстрационное приложение показывает:
- Как запустить 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}