От переводчика.
У меня накопилось куча всяких там данных, документов, pdf, doc, видосов на ютюбе, которые я бы хотел проиндексировать, и чтобы можно было по этой базе знаний у нейронки что-нибудь спрашивать.
Так же статья может пригодиться, если вы хотите собрать базу знаний по какой-то компании и затем заставить нейронку отвечать на вопросы пользователей. Например, чтобы ИИ прочитала кучу скучной документации, регламентов работы и прочего.
Пока выбираю, на чем это лучше сделать. Вот наткнулся на нижеследующий вариант, который решил попробовать.
Использование chatGPT с вашими документами, всякими файлами, изображениями и даже видео
Я был очень очарован, когда ChatGPT недавно выпустил свои плагины. Несмотря на множество объявлений microsoft, Google и других технологических компаний, OpenAI опять впереди планеты всей.
Один конкретный случай, который меня очень заинтересовал, заключался в том, что вы можете передавать ChatGPT свои файлы и документы, а потом запрашивать их с помощью плагина извлечения. Я был заинтригован и хотел прикинуть, как реализовать что-то подобное.
В конце концов, это привело меня к LlamaIndex, классному проекту, который обеспечивает интерфейс между LLM по вашему выбору и вашими собственными данными. Я был удивлен и впечатлен различными типами источников данных и широким спектром поддержки, которая доступна, как для разных типов файлов на вашем компьютере, так и для различных источников данных через Интернет. Итак, мне пришлось написать программу, чтобы все это попробовать.
Powerpoint, PDF и Word
Вот пример того, как я использовал LlamaIndex для различных типов документов на моем компьютере. В этом случае я использовал его с Powerpoint, PDF и документом Word соответственно.
Powerpoint, PDF и Word также можно использовать для наборов данных.
CSV
Вот пример использования набора данных CSV, который содержит данные о различных странах, которые я случайным образом нашел в Интернете.
Изображения
Вы даже можете использовать его на изображениях! Я сфотографировал квитанцию из медицинской клиники, которую недавно посетил, используя свой телефон, и заюзал фотку в качестве данных. Я также сфотографировал страницу из эпохального труда "Мифический человеко-месяц, или Как создаются программные системы", которая просто лежала на моем столе (длинная история, в другой раз).
YouTube
Самым большим сюрпризом может быть то, что я могу даже использовать его на видео! Я взял видео с YouTube (недавнее видео с плодовитого канала Max Tech), преобразовал его в MP4 и использовал его в качестве данных.
Просто выдрал субтитры видео с помощью Whisper и, что примечательно, достаточно точно.
Прежде чем показывать код, давайте сделаем небольшую остановку, чтобы понять, как все это работает.
Как работает LlamaIndex
Большие языковые модели (LLM) работают удивительно хорошо, обучаясь на огромных объемах данных. Не смотря на то что LLM работает хорошо, очень часто он просто не имеет доступа к определенным данным и, следовательно, не всегда полезен, так как банально не обладает нужными знаниями.
Для того чтобы ему предоставить эти данные, LLM можно точно настроить. Это означает, что LLM предоставляются дополнительные данные, относящиеся к конкретной задаче. Тонкая настройка предполагает изменение всех параметров модели. Хотя объем данных может быть небольшим, объем изменений довольно значителен, особенно если это большой LLM, такой как GPT-3 с 175 миллиардами параметров.
Другой способ предоставления данных заключается в проектировании подсказки определенным образом, позволяющим предоставлять данные LLM без тонкой настройки. Исследователи ИИ еще не полностью понимают, как в этом случае поведет себя LLM, но они обнаружили, что если они предоставят список пар ввода-вывода и, наконец, предоставят входные данные, LLM может дать красивые ответы. Этот вид инженерии подсказок называется контекстным обучением. Буквально, вы обучаете модель через разработку подсказки определенным образом.
И вот как работает LlamaIndex. Он предоставляет соединители с различными источниками данных, которые могут быть отправлены в индексы, хранящиеся в виде файлов в файловой системе или в векторных базах данных, таких как Pinecone или Weaviate. Вы запрашиваете эти индексы, а затем используете результаты в качестве контекста вместе с вашей подсказкой, которые затем отправляете в LLM, в данном случае GPT-3 OpenAI, по умолчанию.
Переходим к коду
Пока все это звучит довольно просто, не так ли? Я решил назвать предстоящий проект Kancil (Kancil, в честь самого маленького копытного животного в мире, неуловимого мышиного оленя или шевротайна). Ну, это так, чисто для прикола.
Загрузка файлов в индекс
Kancil состоит из 2 частей. Во-первых, загрузка файлов в индекс. Я мог бы сделать его удобным и наворотить еще интегрирование с веб-приложением, но я поленился и в конечном итоге загрузил файлы в каталог с помощью скрипта Python.
У меня есть файл, который я вызываю для загрузки файлов из каталога load (указывается через переменные среды или файл)load.py.env
От переводчика
В переменной окружения вам надо будет прописать значения для:
OPENAI_API_KEY (гуглите, если нету, бесплатно)
LOAD_DIR (папка, откуда брать данные)
INDEX_FILE (путь к файлу, где будет храниться индекс, напр. /tmp/index.json)
gui - папочка gui юзается во фласке
import os
from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader
from dotenv import load_dotenv
load_dotenv()
print("Loading files in the load directory...")
# Load and save the index to disk
documents = SimpleDirectoryReader(
input_dir=os.environ["LOAD_DIR"]).load_data()
index = GPTSimpleVectorIndex.from_documents(documents)
# Save the index to disk
index.save_to_disk(os.environ["INDEX_FILE"])
print("Done.")
Код относительно прост. Я читаю все файлы в каталоге с помощью SimpleDirectoryReaderDocument
, а затем загружаю полученные данные в index. Затем я создаю индекс из документов и сохраняю его на диск. Этот индекс будет использоваться веб-приложением позже.
Запуск веб-приложения
Я использовал Flask для создания веб-приложения. Я просто запускаю простенький сервер localhost на порту 3838, накидал я это с помощью модуля webbrowser и назвал файл kancil.py
import logging
import webbrowser
from contextlib import redirect_stdout
from io import StringIO
from dotenv import load_dotenv
load_dotenv()
from server import server
logger = logging.getLogger(__name__)
if __name__ == '__main__':
# open browser in a separate thread
with redirect_stdout(StringIO()):
webbrowser.open("http://localhost:3838")
# start server
server.run("127.0.0.1", 3838, debug=True)
Само приложение находится в файле server.py . Во-первых, я загружаю сохраненный файл индекса или создаю индекс с нуля, если он еще не существует. Затем, используя индекс, я вызываю метод и отправляю ему запрос:
import os
from llama_index import GPTSimpleVectorIndex, SimpleDirectoryReader
from flask import Flask, render_template, jsonify, request
index = None
# set up the index, either load it from disk to create it on the fly
def initialise_index():
global index
if os.path.exists(os.environ["INDEX_FILE"]):
index = GPTSimpleVectorIndex.load_from_disk(os.environ["INDEX_FILE"])
else:
documents = SimpleDirectoryReader(os.environ["LOAD_DIR"]).load_data()
index = GPTSimpleVectorIndex.from_documents(documents)
# get path for GUI
gui_dir = os.path.join(os.path.dirname(__file__), 'gui')
if not os.path.exists(gui_dir):
gui_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'gui')
# start server
server = Flask(__name__, static_folder=gui_dir, template_folder=gui_dir)
# initialise index
initialise_index()
@server.route('/')
def landing():
return render_template('index.html')
@server.route('/query', methods=['POST'])
def query():
global index
data = request.json
response = index.query(data["input"])
return jsonify({'query': data["input"],
'response': str(response),
'source': response.get_formatted_sources()})
Ответ возвращается в отображаемый пользовательский интерфейс.
Пользовательский интерфейс
Используем довольно простенький index.html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery-3.6.4.min.js') }}"></script>
<script src="{{ url_for('static', filename='kancil.js') }}"></script>
</head>
<body>
<div class="container">
<div class="row pt-3">
<div class="col-md-12">
<label for="prompt" class="form-label fs-3 text-secondary">What would you like to know?</label>
</div>
<form>
<div class="col-md-12">
<div class="input-group mb-3">
<textarea class="form-control" id="prompt" rows="2"></textarea>
<button class="btn btn-outline-secondary" type="button" id="send">ok</button>
</div>
</div>
</form>
</div>
<div class="row pt-3">
<div class="col-md-12">
<div id="response"></div>
</div>
</div>
<div class="row pt-3">
<div class="col-md-12">
<div id="source"></div>
</div>
</div>
</div>
</body>
</html>
Вызов производится в файле kancil.js. Когда кнопка будет нажата, она отправит запрос на наш обработчик в файле server.py, а ответ мы получим на странице index.html
$(document).ready(function(){
t = 0;
$('#send').click(function(e){
e.preventDefault();
var prompt = $("#prompt").val().trimEnd();
console.log(prompt);
if(prompt == ""){
$("#response").text("Please ask a question.");
}
else{
function myTimer() {
$("#response").html("<p>Waiting for response ... " + t + "s</p>");
t++;
}
const myInterval = setInterval(myTimer, 1000);
$.ajax({
url: "/query",
method:"POST",
data: JSON.stringify({input: prompt}),
contentType:"application/json; charset=utf-8",
dataType:"json",
success: function(data){
$("#response").html("<p>" + data.response + "</p>");
$("#response").append("<small class='text-secondary'>Responded in " + t + " seconds</small>");
$("#source").html("<small class='text-secondary'>" + data.source + "</small>");
clearInterval(myInterval);
t = 0;
}
})
}
});
});
Ну вот и готово!
Вы можете задаться вопросом, и все? Где же тут тогда используется GPT-3? Ну, это делает LlamaIndex за кадром. Вы можете настроить его, но по умолчанию он использует API GPT-3 text-davinci-003. Вам необходимо поместить ключ OpenAI API в переменную среды (проще всего просто вставить его в файл), после этого LlamaIndex найдет его и использует для вызова API OpenAI.
Так что да, вот и все.
Вы можете получить код из https://github.com/sausheong/kancil
Техника подсказок
Кажется довольно невероятным, что вы можете буквально спроектировать подсказку таким образом, чтобы вы могли получить ответы от GPT-3 на основе ваших собственных данных. И вам даже не нужно тонко настраивать какие-либо модели. С LlamaIndex это выглядит еще более удивительно, потому что теперь вы можете подключаться к нескольким источникам данных и использовать GPT-3 всего с несколькими строками кода. Ни в коем случае этот метод не идеален, и результаты иногда странные, но все равно круто.
Enjoy!