Допустим, у вас есть много PDF-файлов в вашем облачном хранилище Google (GCS) и вы хотите использовать векторную базу данных, чтобы дать вашей большой языковой модели (LLM) больше контекста для более точных и актуальных ответов, вам сначала нужно извлечь, очистить и преобразовать эти PDF-файлы в формат, понятный LLM (например, JSON). Библиотека Unstructured может помочь.
Unstructured.io предлагает мощный набор инструментов, который обрабатывает этапы приема и предварительной обработки данных, позволяя вам сосредоточиться на более интересных последующих этапах конвейера машинного обучения. Unstructured имеет более дюжины коннекторов данных, которые легко интегрируются с различными источниками данных, включая AWS S3, Discord, Slack, Wikipedia и другие.
В этом руководстве мы рассмотрим пошаговое руководство о том, как получить ваши данные из GCS, предварительно обработать эти данные и загрузить их в векторную базу данных для извлечения дополненной генерации (RAG).
Предпосылки:
- Установите Unstructured из PyPI или GitHub repo
- Установите неструктурированные коннекторы Google Cloud здесь
- Получите неструктурированный ключ API здесь
- Получите ключ API OpenAI здесь
- Получите API-ключ Pinecone здесь
- Ведро Google Cloud Storage (GCS), полное документов, которые вы хотите обработать
- Базовые знания операций с командной строкой
Включить доступ к GCS:
Чтобы получить доступ к своим файлам из GCS, вам необходимо предоставить доступ к чтению сегментов в вашей учетной записи GCS. Если вы не знакомы с этим процессом, я приведу базовый пример, но если вы уже знакомы с шагами, не стесняйтесь переходить к следующему разделу.
- Перейдите в свою консоль Google Cloud (ссылка)
- Нажмите на проект, из которого вы планируете получить данные.
- В левой части экрана нажмите IAM & Admin -> Учетные записи служб.
- Нажмите «+ Создать сервисный аккаунт» и заполните поля
- После того, как вы создали новую учетную запись службы, нажмите на нее и перейдите в «KEYS».
- Нажмите «ДОБАВИТЬ КЛЮЧ» -> «Создать новый ключ» -> JSON.
- Это загрузит файл JSON с вашими ключами. Убедитесь, что эти ключи хранятся надлежащим образом, так как они представляют угрозу безопасности.
- Поместите файл JSON в безопасное место и по пути, к которому вы сможете получить доступ позже.
Запуск неструктурированного API с помощью коннектора GCS:
Когда ваш ключ неструктурированного API и корзина GCS готовы, пришло время запустить неструктурированный API. Чтобы запустить команду `unstructured-ingest`, вам необходимо установить неструктурированную библиотеку с открытым исходным кодом, которую можно легко получить из этого репозитория GitHub.
Следуйте инструкциям в репозитории, чтобы установить библиотеку, и обязательно установите следующие дополнительные зависимости:
!pip install "unstructured[gcs,all-docs]" langchain openai pinecone-client
После того, как вы установили дополнительные зависимости, можно запускать API. Для этого руководства я решил запустить API в блокноте Python.
# Add the environment variable for the GCP service account credentials os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = '/Path/to/your/keyfile.json' command = [ "unstructured-ingest", "gcs", "--remote-url", "gs://<YOUR-BUCKET>/", "--structured-output-dir", "/YOUR/OUTPUT/PATH", "--num-processes", "2", "--api-key", "<UNSTRUCTURED-API-KEY>", "--partition-by-api" ]
Объяснение:
- os.environ[‘GOOGLE_APPLICATION_CREDENTIALS’] =/path/to/your/keyfile.json — добавьте переменную среды для учетных данных учетной записи службы GCP.
- «gcs» — указывает коннектор данных
- Установите выходной каталог, используя параметр `-structured-output-dir`
- Укажите `—num-processes`, чтобы распределить рабочую нагрузку между несколькими процессами.
- Включите `-api-key` из предыдущего шага в ваш вызов API.
- Используйте флаг `-partition-by-api`, чтобы указать, что раздел работает через API, а не через библиотеку.
Мы с гордостью представляем наш бесплатный API, созданный специально для энтузиастов и разработчиков, увлекающихся прототипированием. Мы выделили определенное количество преданных работников, чтобы обеспечить бесперебойную работу и удовлетворить потребности нашего сообщества пользователей. Тем не менее, мы понимаем потребности крупных корпораций и выполнение тяжелых задач. Имея это в виду, мы рады сообщить, что на горизонте появится API корпоративного уровня.
Просмотрите выходные данные API:
После завершения команды вы найдете структурированные выходные данные в указанном выходном каталоге. Эти файлы содержат информацию, извлеченную из ваших неструктурированных данных, готовую для анализа или дальнейшей обработки.
Когда мы создаем системы RAG, наша модель LLM работает только в том контексте, который им предоставляется. По этой причине мы хотим удалить ненужные текстовые элементы, найденные в наших файлах. К счастью, Unstructured классифицирует текст по типам элементов, и когда Unstructured находит элементы, которые не может классифицировать, он помечает их как UncategorizedText. Часто этот UncategorizedText бесполезен и отвлекает нашего LLM. В этом уроке мы удалим эти элементы UncategorizedText с помощью следующей вспомогательной функции.
def remove_element_from_json(filepath, item_to_remove): # Read the file and load its contents as JSON with open(filepath, 'r') as file: data = json.load(file) # Filter out the elements with the specified 'type' updated_data = [item for item in data if item['type'] != item_to_remove] # Write the updated data back to the file with open(filepath, 'w') as file: json.dump(updated_data, file, indent=4)
Теперь вы можете вызвать вышеупомянутую вспомогательную функцию, указав путь к файлу документа и тип элемента, который вы хотите удалить.
filepath = '/YOUR/OUTPUT/PATH' remove_element_from_json(filepath, "UncategorizedText")
Загрузить файлы Json в Langchain:
Следующим шагом будет загрузка очищенных и обработанных структурированных данных в загрузчики документов LangChain. В этом случае мы будем использовать UnstructuredFileLoader от LangChain.
from langchain.document_loaders import UnstructuredFileLoader # Now you will load to files outputted from the Unstructured API. You'll find the files in the output directory. loader = UnstructuredFileLoader( "/YOUR/OUTPUT/PATH", post_processors=[clean_extra_whitespace], ) docs = loader.load()
Разбить текстовые файлы на части:
Далее нам нужно будет разбить исходные документы на фрагменты, чтобы наши модели больших языков (LLM) могли обрабатывать потенциально длинные документы. Текущее ограничение LLM заключается в том, что они могут обрабатывать только определенный объем контекста. Мы разбиваем данные на куски, чтобы фрагменты текста были для них удобоваримыми.
Для целей этого урока я решил использовать LangChain RecursiveCharacterTextSplitter, однако у них есть множество различных методов фрагментации, которые можно найти здесь.
text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200 ) docs = text_splitter.split_documents(docs)
Настройка шишки:
После того, как мы разбили наши данные, мы можем встроить их и сохранить в векторной базе данных. В этой демонстрации я собираюсь использовать сосновую шишку, однако существует множество различных вариантов, каждый из которых имеет свои сильные стороны.
# Storing API keys like this poses a security risk. Only do this in prototyping os.environ['PINECONE_API_KEY'] = "<PINECONE-API-KEY>" # Pinecone Environment Variable os.environ['PINECONE_ENV'] = "<PINECONE-LOCATION>" pinecone.init( api_key=os.getenv("PINECONE_API_KEY"), # find at app.pinecone.io environment=os.getenv("PINECONE_ENV"), # next to api key in console ) index_name = "gcs-demo" # First, check if our index already exists. If it doesn't, we create it if index_name not in pinecone.list_indexes(): # we create a new index pinecone.create_index( name=index_name, metric='cosine', dimension=1536 )
Создайте вспомогательные функции:
Следующий шаг — после того, как мы инициализировали нашу базу данных Pinecone, нам нужно создать вспомогательную функцию, чтобы получить все соответствующие фрагменты информации из нашей базы данных векторов, чтобы позже использовать их в нашем LLM (вдохновение).
limit = 3600 embed_model = "text-embedding-ada-002" def retrieve(query): res = openai.Embedding.create( input=[query], engine=embed_model ) # retrieve from Pinecone xq = res['data'][0]['embedding'] # get relevant contexts res = index.query(xq, top_k=3, include_metadata=True) contexts = [ x['metadata']['text'] for x in res['matches'] ] # build our prompt with the retrieved contexts included prompt_start = ( "Given the contextual information and not prior knowledge, answer the question. If the answer is not in the context, inform the user that you can't answer the question.\n\n"+ "Context:\n" ) prompt_end = ( f"\n\nQuestion: {query}\nAnswer:" ) # append contexts until hitting limit for i in range(1, len(contexts)): if len("\n\n---\n\n".join(contexts[:i])) >= limit: prompt = ( prompt_start + "\n\n---\n\n".join(contexts[:i-1]) + prompt_end ) break elif i == len(contexts)-1: prompt = ( prompt_start + "\n\n---\n\n".join(contexts) + prompt_end ) return prompt
Теперь, когда у нас есть вспомогательная функция для извлечения соответствующих фрагментов информации, нам нужна вспомогательная функция, которая будет принимать наше расширенное приглашение и передавать его в наш LLM.
def complete(prompt): # query text-davinci-003 res = openai.Completion.create( engine='text-davinci-003', prompt=prompt, temperature=0, max_tokens=400, top_p=1, frequency_penalty=0, presence_penalty=0, stop=None ) return res['choices'][0]['text'].strip()
Запрос векторной базы данных:
С нашими двумя вспомогательными функциями мы теперь можем использовать нашу базу данных векторов для извлечения соответствующих фрагментов информации, чтобы помочь нашему LLM ответить на вопросы, связанные с предметной областью.
query = "A question you'd like to ask about your PDF" # first we retrieve relevant items from Pinecone query_with_contexts = retrieve(query) query_with_contexts
Как только мы получили необходимый контекст, мы можем передать его в наш LLM, чтобы ответить на наш вопрос.
# then we complete the context-infused query complete(query_with_contexts)
Вывод:
В этом блоге мы перешли от неструктурированных необработанных PDF-файлов, хранящихся в корзине GCS, к их предварительной обработке и загрузке в векторную базу данных, чтобы наш LLM мог выполнять запросы. В этом процессе есть много потенциальных улучшений, включая методы фрагментации, но я надеюсь, что теперь вы можете взять любой формат файла и построить вокруг него конвейер RAG. Полный код можно найти здесь.
Если у вас есть вопросы, присоединяйтесь к нашей группе сообщества Slack, чтобы общаться с другими пользователями, задавать вопросы, делиться своим опытом и получать последние обновления. Нам не терпится увидеть, что вы построите!
Первоначально опубликовано на https://medium.com 14 августа 2023 г.