Направете своя личен AI с Langchain + LLaMA 2 +Flask

LLMs (Large Language Models) в момента са център на внимание в общността на AI. С появата на GPT-4 LLMs станаха толкова масови, че разработчиците тясно интегрират тези модели в няколко приложения. Въпреки че традиционните LLM са невероятни за повечето случаи на употреба, те са склонни да не успяват, когато искате да ги използвате готови с лични данни. Въпреки че можете да интегрирате вашите лични данни с GPT-4 чрез техните API, това не е най-добрата идея, тъй като не искате вашите чувствителни данни да се изпращат до сървъри на трети страни.

Решаване на проблема със сигурността

ЕДИНСТВЕНИЯТ начин, по който можете да сте сигурни, че вашите данни се обработват сигурно, е да използвате LLM с отворен код, които ви дават пълен контрол и гъвкавост.

Какви са предимствата?

  1. Разминете се с използването дори на модел с параметри 7B чрез подходящо бързо проектиране и фина настройка
  2. Използването на LLM с по-нисък параметър е изчислително по-бързо, а квантуването на модела го прави още по-ефективен, което води до по-ниски оперативни разходи
  3. Имате пълен контрол върху целия процес, така че данните остават на ВАШИТЕ сървъри
  4. Лесно персонализиран, за да отговори на вашите нарастващи изисквания поради природата с отворен код

Съдържание

  1. Създаване на прост интерфейс за чат отпред с 🐍 Flask
  2. Изтегляне на 🦙 LLM
  3. Съпоставяне и обработка на лични данни с 🦜🔗 Langchain
  4. 🦜🔗 Langchain Retrival QA обект за търсене по сходство на векторни бази данни
  5. Създаване на персонализиран шаблон за подкана

Стъпка 1: Създаване на прост интерфейс за чат отпред с 🐍 Flask

За нашия потребителски интерфейс ще използваме Flask, за да създадем просто уеб приложение. Ще има само една динамична страница, където ще взаимодействате с LLM. За да започнете, продължете напред и клонирайте това repo на github за целия код. Предлагам ви да използвате само кода за предния край и персонализирате всичко останало въз основа на вашите нужди!

Кодът е много прост, тъй като използваме само предния край за получаване на потребителската заявка и връщане на резултата. Ако искате да разберете повече за форматирането на отговора, вижте файла main.js в директорията static/.

# Main script
from flask import Flask, render_template, request
from utils import setup_dbqa

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/get", methods=["GET", "POST"])
def chat():
    msg = request.form["msg"]
    input = msg
    try:
        return get_chat_response(input)
    except ValueError:
        return "You have exceeded the token limit! Sorry for the inconvenience!"


# Gets the response by passing the prompt to QA Object
def get_chat_response(input):
    response = dbqa({"query": input})
    return response["result"]


if __name__ == "__main__":
    dbqa = setup_dbqa() #This is the RetreivalQA Object
    app.run(debug=True, port=2000)

Стъпка 2: Изтегляне на LLM

Изборът на LLM за този случай на използване ще бъде моделът за чат с параметри 7B на LLaMA-2. Това е добър избор за изводи върху обобщен набор от лични данни. Чувствайте се свободни да проучите други LLMs, които смятате, че биха подхождали по-добре на вашия случай на употреба.

Например „LLaMA“ фино настроени модели като „Vicuna“ е добър вариант, ако имате много задачи, базирани на инструкции, и „Koala“, ако имате задачи, базирани на диалог. Ако искате да научите повече за това кой LLM отговаря най-добре на вашите нужди, вижте този LLM индекс от Sapling.ai.

В този блог ще направим извод, базиран на процесора. За да направим това, ще използваме GGML формата на LLM, тъй като той значително увеличава изчислителната ефективност чрез използване на различни техники за оптимизация (основно квантуване).

Какво е GGML? Квантуване?

GGML е тензорна библиотека, написана на C++, която използва целочислено квантуване и други алгоритми за оптимизация като ADAM и L-BFGS, за да позволи LLM извод в среда, базирана на процесора.

Основната оптимизация се крие в квантуването, при което теглата на модела, които са числа с плаваща запетая, се компресират до 4-битови или 8-битови цели числа. Това намалява прецизността на теглата, което води до удар в производителността, но драстично подобрява ефективността, тъй като използването на RAM и диск е значително по-ниско. Ако искате да научите повече за квантуването, вижте тази страница.

Изтегляне на LLM във формат GGML

Можете да изтеглите LLM по ваш избор от тук. За този блог ще използвам основната версия LLaMA-7B-Chat. Ако LLM, който искате да използвате, все още не присъства във формат GGML на huggingface, винаги можете да го преобразувате в GGML локално, като следвате това видео и това repo на github.

Използване на LLM в python с 🦜🔗 Langchain

За да използваме LLM с Python, трябва да използваме свързвания на Python, които ви позволяват да предавате данни или да извиквате функции между Python и C++ в този случай. За да направим това, ще използваме библиотеката CTransformers от Langchain.

#======= llm.py ===========
from langchain.llms import CTransformers

# Local CTransformers wrapper for Llama-2-7B-Chat
llm = CTransformers(
    model="/Users/deveshparagiri/Downloads/models/sage-v2-q8_0.bin",
    model_type="llama",  # Model type Llama
    config={"max_new_tokens": 256, "temperature": 0.5},
)

Стъпка 3: Съпоставяне и обработка на лични данни с 🦜🔗 Langchain

Сега, след като LLM е изтеглен, следващата стъпка е да създадем нашата векторна база данни с нашите лични документи. Създайте директория с данни/ в папката на вашия проект и качете всичките си лични документи там. Първо, документите трябва да се заредят и след това да се разделят на правилни части, преди да се създадат векторни вграждания въз основа на тях.

Нарязване

Разделянето на данните е от решаващо значение за извършване на добро търсене на семантично сходство. Най-елементарният начин за групиране би бил просто разделянето на текста на базата на фиксирана дължина (Чънкиране с фиксиран размер). Това обаче не е най-добрият начин да го направим, тъй като трябва да разделяме, като същевременно поддържаме подобие на контекста. Един от начините да направите това е рекурсивно разделяне на парчета, където парчетата са с подобен размер, като същевременно се запазва контекстът. За да научите повече за тънкостите на групирането и неговото въздействие върху изводите, щракнете тук.

Векторна база данни и вграждания

След като бъдат разделени на парчета, те се преобразуват във векторни вграждания с помощта на нашия модел all-MiniLM-L6-v2 и се съхраняват в Milvus, векторна база данни. Ако засега не искате да изберете традиционна векторна база данни, можете също да използвате обвивката FAISS в Langchain, за да индексирате и съхранявате локално вгражданията. За да настроите milvus, уверете се, че сте pip install pymilvus и

Кодът по-долу се изпълнява само веднъж, за да се създаде векторна база данни, която ще се използва като справка от нашия LLM, за да отговори на нашите запитвания.

#========= db_build.py ===========
from langchain.vectorstores import Milvus
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders import PyPDFLoader, DirectoryLoader, 
                                       Docx2txtLoader, CSVLoader

from langchain.embeddings import HuggingFaceEmbeddings
from dbconfig import CONNECTION_HOST, CONNECTION_PORT, COLLECTION_NAME

# Embedding model loading
embeddings = HuggingFaceEmbeddings(
    model_name="sentence-transformers/all-MiniLM-L6-v2", 
    model_kwargs={"device": "cpu"}
)

# Recursive text splitting
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, 
                                                chunk_overlap=50)


def load_data(directory):
    """This function loads and splits the files

    Parameters:
    `directory (str): Path where data is located`

    Returns:
    List: Returns a list of processed Langchain Document objects
    """
    # Inititating all loaders
    pdf_loader = DirectoryLoader(directory, glob="*.pdf", loader_cls=PyPDFLoader)
    docx_loader = DirectoryLoader(directory, glob="*.docx", loader_cls=Docx2txtLoader)
    spotify_loader = CSVLoader(file_path=f"{directory}spotify.csv")

    insta_following_loader = CSVLoader(file_path=f"{directory}insta_following.csv")
    insta_followers_loader = CSVLoader(file_path=f"{directory}insta_followers.csv")

    # Loading all documents
    pdf_documents = pdf_loader.load()
    docx_documents = docx_loader.load()
    spotify_documents = spotify_loader.load()
    insta_following_documents = insta_following_loader.load()
    insta_followers_documents = insta_followers_loader.load()

    # Adding all loaded documents to one single list of Documents
    corpus = pdf_documents
    corpus.extend(docx_documents)
    corpus.extend(spotify_documents)
    corpus.extend(insta_following_documents)
    corpus.extend(insta_followers_documents)
    corpus.extend(spotify_documents)

    # Resetting metadata for all type of documents to make it compatible for vector DB
    for document in corpus:
        document.metadata = {"source": document.metadata["source"]}

    # Splitting all documents
    corpus_processed = text_splitter.split_documents(corpus)
    return corpus_processed

def vectordb_store(corpus_processed):
    """This function takes in the split documents,
    creates vector embeddings, indexes and stores them in Milvus.

    Parameters:
    corpus_processed (List): List of Langchain Document objects

    Returns: Milvus Vector DB Object
    """
    vector_db = Milvus.from_documents(
        corpus_processed,
        embedding=embeddings,
        connection_args={"host": "127.0.0.1", "port": "19530"},
        collection_name=COLLECTION_NAME,
    )
    return vector_db


if __name__ == "__main__":
    vectordb_store(load_data("data/"))

Стъпка 4: 🦜🔗 Langchain Retrival QA обект за търсене на сходство във векторна база данни

Нашата векторна база данни е създадена въз основа на нашите лични документи. Сега трябва да можем да търсим в тази векторна база данни въз основа на нашата заявка към LLM, да намерим най-подходящите данни и да ги изпратим обратно на нашия LLM за извод. За да разберете по-добре целия работен процес, вижте архитектурата на целия процес по-долу.

За да активираме търсенето във vectorDB, ще създадем обект RetreivalQA в Langchain. Ние основно предаваме обекта LLM, източника на vectorDB и подканата (потребителска заявка) към този обект и той връща най-близкия резултат от търсенето. В моя случай връщам само най-подходящото търсене, но можете да получите най-добрите K подходящи търсения, като промените параметъра search_kwargs.

#======= utils.py ============
from langchain.chains import RetrievalQA
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Milvus
from llm import llm
from pymilvus import connections


def build_retrieval_qa(llm, prompt, vectordb):
    """This function builds the RetreivalQA object`

    Parameters:
    llm (Object): The llm object
    prompt (Object): The prompt template
    vectordb (Object): The vector store

    Returns:
    RetreivalQA Object: Returns the best result
    """
    # Only retreiving the first best result
    dbqa = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=vectordb.as_retriever(search_kwargs={"k": 1}),
        return_source_documents=True,
        chain_type_kwargs={"prompt": prompt},
    )
    return dbqa


def setup_dbqa():
    """This function instantiates the RetreivalQA object

    Parameters:
    llm (Object): The llm object
    prompt (Object): The prompt template
    vectordb (Object): The vector store

    Returns:
    RetreivalQA Object: Returns created object
    """
    embeddings = HuggingFaceEmbeddings(
        model_name="sentence-transformers/all-MiniLM-L6-v2",
        model_kwargs={"device": "cpu"},
    )

    connections.connect("default", host="localhost", port="19530")

    vector_db: Milvus = Milvus(
        embedding_function=embeddings,
        connection_args={"host": "127.0.0.1", "port": "19530"},
        collection_name="mystore",
    )
    qa_prompt = set_qa_prompt()
    dbqa = build_retrieval_qa(llm, qa_prompt, vector_db)
    return dbqa

Стъпка 5: Създаване на персонализиран шаблон за подкана

Персонализираният шаблон за подкана е много полезен, като дава представа какъв тип подкана ще получите от потребителя и начина, по който вашият модел трябва да отговори. Например използвах този шаблон за подкана по-долу, но вие можете да създадете свой собствен въз основа на вашите изисквания.

# ================================================================================
# Creating the template based on which the model will reply
# ================================================================================
from langchain import PromptTemplate

qa_template = """
You are Dev's personal A.I assistant named S.A.G.E.
You are a helpful and honest assistant who has access to my personal information. Please ensure that your responses are socially unbiased and positive in nature.
Censor any explicit content.
If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct.
If you don't know the answer to a question, please don't share false information.
Only answer based on what is presented.
Use the following context to answer the user's question.
Context: {context}
Question: {question}
Only return the answer and nothing else.
Answer:
"""

def set_qa_prompt():
    """This function wraps the prompt template in a PromptTemplate object

    Parameters:

    Returns:
    PromptTemplate Object: Returns the prompt template object
    """
    prompt = PromptTemplate(
        template=qa_template, input_variables=["context", "question"]
    )
    return prompt

Това е само най-основният пример за шаблон за подкана. Наистина можете да използвате „бързото инженерство“, за да проектирате курирани отговори. Например, можете да използвате шаблон Few Shot Prompt. Това е начин за обучение на модела върху конкретни изходни данни чрез предоставяне на няколко примера.

from langchain import FewShotPromptTemplate

# create our examples
examples = [
    {
        "query": "How are you?",
        "answer": "I can't complain but sometimes I still do."
    }, {
        "query": "What time is it?",
        "answer": "It's time to get a watch."
    }
]

# create a example template
example_template = """
User: {query}
AI: {answer}
"""

# create a prompt example from above template
example_prompt = PromptTemplate(
    input_variables=["query", "answer"],
    template=example_template
)

# now break our previous prompt into a prefix and suffix
# the prefix is our instructions
prefix = """The following are exerpts from conversations with an AI
assistant. The assistant is typically sarcastic and witty, producing
creative  and funny responses to the users questions. Here are some
examples: 
"""
# and the suffix our user input and output indicator
suffix = """
User: {query}
AI: """

# now create the few shot prompt template
few_shot_prompt_template = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=suffix,
    input_variables=["query"],
    example_separator="\n\n"
)

За по-стриктно обучение на конкретни данни трябва да настроите фино модела на вашите лични данни. Това може да се направи с T4 GPU с един екземпляр в Google Colab (ще отнеме известно време в зависимост от размера на набора от данни), при условие че използвате PEFT (Parameter Efficient Fine Tuning) техники като QLoRA. За кода за фина настройка вижте моя бележник на Google Colab тук. Разгледайте този ресурс, ако искате да научите повече за QLoRA.

Това е всичко за създаване на ваш собствен персонализиран и сигурен AI асистент. Щракнете тук, за да изтеглите целия код на проекта.

На обикновен английски

Благодарим ви, че сте част от нашата общност! Преди да тръгнете: