+2

[LLM 101] Cài đặt kĩ thuật RAG sử dụng Hybrid Search, Embed Caching và Mistral-AI

RAG là gì?

RAG, hay Retrieval Augmented Generation, là một phương pháp kết hợp các yếu tố của truy xuất thông tin và tạo ra ngôn ngữ tự nhiên nhằm cải thiện chất lượng của văn bản được tạo ra, đặc biệt là trong bối cảnh các nhiệm vụ ngôn ngữ phức tạp như trả lời câu hỏi, tóm tắt và hoàn thành văn bản.

Mục tiêu chính của RAG là tăng độ chính xác của quá trình generate bằng cách truy xuất thông tin từ nguồn bên ngoài, giúp cho mô hình NLG (Natural Language Generation) tạo ra các phản hồi có lý hơn và phù hợp với ngữ cảnh. Bằng cách tích hợp thông tin liên quan từ quá trình retrieve vào generative, RAG nhằm mục đích cải thiện độ chính xác, mạch lạc và hữu ích của nội dung được tạo ra.

Các kĩ thuật Advanced RAG

Cache Embedding

Embeddings có thể được lưu trữ hoặc được lưu tạm thời vào bộ nhớ cache để tránh việc tính toán lại chúng.

Việc lưu trữ embeddings có thể được thực hiện bằng cách sử dụng một CacheBackedEmbeddings. Ta sẽ có một Cache Embedder để cache một embedding dưới dạng key-value. Nội dung văn bản sẽ được hash và giá trị hash này được sử dụng làm key ở trong cache.

Để khởi tạo một CacheBackedEmbeddingsfrom_bytes_store. Hàm này nhận vào các tham số sau:

  • underlying_embedder: Embedder được sử dụng để embedding.
  • document_embedding_cache: Cache được sử dụng để lưu trữ embeddings của tài liệu.
  • namespace: (optional, mặc định là ""): Namespace được sử dụng cho cache tài liệu. Namespace này được sử dụng để tránh trùng với các cache khác. Ví dụ, ta có thể đặt thành tên của mô hình embed được sử dụng.

Trong cài đặt dưới đây, chúng ta sẽ sử dụng hệ thống tệp cục bộ (local file system) để lưu trữ các embeddings và FAISS vector store để truy xuất.

store = LocalFileStore("./cache/")

Nếu ta thử tạo lại vector store, nó sẽ nhanh hơn nhiều do không phải tính toán lại embedding nữa. Ta cũng có thể tạo memory cache để embedding.

store = InMemoryStore()

Loại cache này chủ yếu hữu ích cho unit test hoặc làm prototype. Các bạn không nên sử dụng loại cache này nếu thực sự cần lưu trữ các embedding.

Hybrid vector Search

Hybrid Search là sự kết hợp giữa tìm kiếm theo từ khóa (keyword search) và tìm kiếm theo vector. Nó kết hợp ưu điểm của tìm kiếm từ khóa cũng như ưu điểm của việc tìm kiếm theo ngữ nghĩa (theo vector embedding).

Keyword search:

Ở đây chúng ta sử dụng Thuật toán BM25. Nó tạo ra một vector thưa (nhiều giá trị 0). BM25 (Best Match 25) là một thuật toán truy xuất thông tin được sử dụng để xếp hạng và tính điểm sự phù hợp của tài liệu với một truy vấn tìm kiếm cụ thể. Đây là một phần mở rộng của phương pháp TF-IDF (Term Frequency-Inverse Document Frequency).

Các điểm chính về BM25:

  • Term Frequency (TF): Đo lường tần suất của một từ trong một tài liệu.
  • Inverse Document Frequency (IDF): Đo lường mức độ quan trọng của một từ dựa trên tần suất của nó trên toàn bộ bộ sưu tập tài liệu.
  • Trọng số BM25: Kết hợp TF và IDF để tính điểm phù hợp của một tài liệu đối với một truy vấn cụ thể.
  • Các từ truy vấn: BM25 xét sự xuất hiện của các từ truy vấn trong tài liệu và điều chỉnh điểm của chúng tương ứng.
  • Điều chỉnh tham số: BM25 liên quan đến việc điều chỉnh các tham số (k1, b) để tối ưu hóa hiệu suất ranking dựa trên tập dữ liệu.

Tìm kiếm ngữ nghĩa (Semantic search):

Tìm kiếm ngữ nghĩa là một phương pháp tìm kiếm nhằm cải thiện độ chính xác và tính liên quan của các kết quả tìm kiếm bằng cách hiểu context và ý nghĩa của một truy vấn. Không giống như tìm kiếm dựa trên từ khóa truyền thống, chủ yếu dựa vào việc khớp với từ khóa hay không, tìm kiếm ngữ nghĩa cố gắng hiểu ý nghĩa của truy vấn từ người dùng và nội dung của các tài liệu được tìm kiếm.

Tìm kiếm ngữ nghĩa mô phỏng sự hiểu biết của con người về ngôn ngữ và văn cảnh, mang lại kết quả tìm kiếm phù hợp hơn với nhu cầu thông tin của người dùng.

Chúng ta sẽ sử dụng FAISS cho tìm kiếm ngữ nghĩa và BM25 cho tìm kiếm từ khóa để triển khai Hybrid Search bằng cách sử dụng Langchain EnsembleRetriever.

EnsembleRetriever nhận một list các bộ truy xuất (retriever) làm đầu vào và kết hợp các kết quả từ các phương thức get_relevant_documents() của chúng và sắp xếp lại các kết quả dựa trên thuật toán Reciprocal Rank Fusion.

Bằng cách tận dụng những ưu điểm của các thuật toán khác nhau, EnsembleRetriever có thể đạt được hiệu suất tốt hơn so với bất kỳ thuật toán đơn lẻ nào. Cách làm phổ biến nhất là kết hợp một bộ truy xuất thưa (như BM25) với một bộ truy xuất dày (dense retriever) ví dụ như embedding similarity, vì chúng bổ sung các ưu điểm cho nhau.

Sparse retriever hoạt động tốt trong việc tìm kiếm các tài liệu liên quan dựa trên từ khóa, trong khi dense retrieve lại ổn trong việc tìm kiếm các tài liệu liên quan dựa trên sự tương đồng về ngữ nghĩa.

InMemory Caching

Trong phương pháp này, Caching được sử dụng cho user query nếu như response mới của LLM Agent không match với query cũ.

Các thành phần được sử dụng:

  • Embedder : BAAI general embedding
  • Retrieval : FAISS Vectorstore
  • Generation : Mistral-7B-Instruct GPTQ model
  • Infrastructure : Google Colab, A100 GPU
  • Data: Các tài liệu về Financial (bạn cũng có thể sử dụng tập dữ liệu chứa file pdf khác)

Chi tiết cài đặt như sau:

Cài đặt các package:

!pip install -q git+https://github.com/huggingface/transformers
!pip install -qU langchain Faiss-gpu tiktoken sentence-transformers
!pip install -qU trl Py7zr auto-gptq optimum
!pip install -q rank_bm25
!pip install -q PyPdf

Import các package:

import langchain
from langchain.embeddings import CacheBackedEmbeddings,HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain.storage import LocalFileStore
from langchain.retrievers import BM25Retriever,EnsembleRetriever
from langchain.document_loaders import PyPDFLoader,DirectoryLoader
from langchain.llms import HuggingFacePipeline
from langchain.cache import InMemoryCache
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import prompt
from langchain.chains import RetrievalQA
from langchain.callbacks import StdOutCallbackHandler
from langchain import PromptTemplate
#
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline

Load dữ liệu:

dir_loader = DirectoryLoader("/content/Data",
                             glob="*.pdf",
                             loader_cls=PyPDFLoader)
docs = dir_loader.load()
#
print(f"len of documents in :{len(docs)}")

"""
len of documents in :85
"""

Tạo các chunk cho tài liệu:

#
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500,
                                      chunk_overlap=200,)
#
esops_documents = text_splitter.transform_documents(docs)
print(f"number of chunks in barbie documents : {len(esops_documents)}")

""" 
number of chunks in barbie documents : 429
"""

Tạo vectorstore:

store = LocalFileStore("./cache/")
embed_model_id = 'BAAI/bge-small-en-v1.5'
core_embeddings_model = HuggingFaceEmbeddings(model_name=embed_model_id)
embedder = CacheBackedEmbeddings.from_bytes_store(core_embeddings_model,
                                                  store,
                                                  namespace=embed_model_id)
# Create VectorStore
vectorstore = FAISS.from_documents(esops_documents,embedder)

Tạo Sparse Embedding:

bm25_retriever = BM25Retriever.from_documents(esops_documents)
bm25_retriever.k=5

Nhận một đoạn văn có nội dung tương tự theo user query:

query = "Ho to save my excess money?"
embedding_vector = core_embeddings_model.embed_query(query)
print(len(embedding_vector))
#
docs_resp = vectorstore.similarity_search_by_vector(embedding_vector,k=5)
#
for page in docs_resp:
  print(page.page_content)
  print("\n")

""" 
looking at how you could make your money grow if you de -
cided to spend less on other things and save those extra dollars.
If you buy on impulse, make a rule that you’ll always wait 
24 hours to buy anything. Y ou may lose your desire to buy it 
after a day. And try emptying your pockets and wallet of spare 
change at the end of each day.  Y ou’ll be surprised how quickly 
those nickels and dimes add up!
PAY OFF CREDIT CARD OR OTHER HIGH  
INTEREST DEBT


keep pace with an 18 percent interest charge. That’s why you’re 
better off eliminating all credit card debt before investing savings. 
Once you’ve paid off your credit cards, you can budget your 
money and begin to save and invest. Here are some tips for 
avoiding credit card debt:
Put Away the Plastic
 Don’t use a credit card unless your debt is at a manageable level and 
you know you’ll have the money to pay the bill when it arrives.
Know What You Owe


Add up your total retirement savings, both at


first pay down the card that charges the highest rate. Pay as much 
as you can toward that debt each month until your balance is once 
again zero, while still paying the minimum on your other cards.
The same advice goes for any other high interest debt (about 8% or 
above) which does not offer the tax advantages of, for example, a mortgage.
Now, once you have paid off those credit cards and begun to 
set aside some money to save and invest, what are your choices?


earns. Over time, even a small amount saved can add up to big money.
If you are willing to watch what you spend and look for 
little ways to save on a regular schedule, you can make money 
grow. Y ou just did it with one cup of coffee.
If a small cup of coffee can make such a huge difference, start 
looking at how you could make your money grow if you de -
cided to spend less on other things and save those extra dollars.
If you buy on impulse, make a rule that you’ll always wait
"""

Setup Ensemble Retriever (Hybrid Search):

faiss_retriever = vectorstore.as_retriever(search_kwargs={"k":5})
ensemble_retriever = EnsembleRetriever(retrievers=[bm25_retriever,faiss_retriever],
                                       weights=[0.5,0.5])

Download quantized GPTQ Model:

from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline

model_name_or_path = "TheBloke/Mistral-7B-Instruct-v0.1-GPTQ"
# To use a different branch, change revision
# For example: revision="gptq-4bit-32g-actorder_True"
model = AutoModelForCausalLM.from_pretrained(model_name_or_path,
                                             device_map="auto",
                                             trust_remote_code=False,
                                             revision="gptq-8bit-32g-actorder_True")
#
tokenizer = AutoTokenizer.from_pretrained(model_name_or_path, use_fast=True)

Tạo pipeline:

pipe = pipeline(
    "text-generation",
    model=model,
    tokenizer=tokenizer,
    max_new_tokens=512,
    do_sample=True,
    temperature=0.1,
    top_p=0.95,
    top_k=40,
    repetition_penalty=1.1
)

Khởi tạo LLM sử dụng quantized GPTQ model:

from langchain.llms import HuggingFacePipeline
llm = HuggingFacePipeline(pipeline=pipe)

Setup Caching:

langchain.llm_cache = InMemoryCache()

Tạo prompt:

PROMPT_TEMPLATE = '''
You are my financial advisor. You are great at providing tips on investments, savings and on financial markets with your knowledge in finances.
With the information being provided try to answer the question. 
So try to understand in depth about the context and answer only based on the information provided. Dont generate irrelevant answers

Context: {context}
Question: {question}
Do provide only helpful answers

Helpful answer:
'''
#
input_variables = ['context', 'question']
#
custom_prompt = PromptTemplate(template=PROMPT_TEMPLATE,
                            input_variables=input_variables)

Setup Retrieval chain không sử dụng Hybrid Search:

handler = StdOutCallbackHandler()
#
qa_with_sources_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever = vectorstore.as_retriever(search_kwargs={"k":5}),
    verbose=True,
    callbacks=[handler],
    chain_type_kwargs={"prompt": custom_prompt},
    return_source_documents=True
)

Thử thực hiện một truy vấn:

%%time
query = "What are the steps to make a financial planning?"
response = qa_with_sources_chain({"query":query})
print(f"Response generated : \n {response['result']}")
print(f"Source Documents : \n {response['source_documents']}")

"""
OUTPUT
Response generated : 
 
To make a financial plan, follow these steps:

1. Determine your short-term and long-term financial goals.
2. Calculate your current net worth.
3. Identify your sources of income and expenses.
4. Create a budget that includes all of your income and expenses.
5. Set aside money for emergency funds and unexpected expenses.
6. Determine how much you can realistically save and invest each month.
7. Choose investment options that align with your financial goals and risk tolerance.
8. Review and adjust your financial plan regularly to ensure that you are on track to meet your goals.
Source Documents : 
 [Document(page_content='Make your own list and then think about which goals are the \nmost important to you. List your most important goals first. \nDecide how many years you have to meet each specific goal, \nbecause when you save or invest you’ll need to find a savings or \nYOUR FINANCIAL GOALS\nIf you don’t know where you are going, you may end up somewhere you don’t want \nto be. To end up where you want to be, you’ll need a roadmap, a financial plan.\nWhat do you want to save or invest for? By when?', metadata={'source': '/content/Data/sec-guide-to-savings-and-investing.pdf', 'page': 5}), Document(page_content='How do you manage all these financial \nchallenges and at the same time try to “buy” a secure retirement? How do you turn your dreams into reality?\nStart by writing down each of your goals in \nWorksheet 1 – Goals and Priorities in the back of this booklet. You may want to have family members come up with ideas. Don’t leave something out at this stage because you don’t think you can afford it. This is your “wish list.”\nOrganize them into goals you want to accomplish', metadata={'source': '/content/Data/savings-fitness.pdf', 'page': 5}), Document(page_content='and a comfortable retirement. If they can do it, so can you!\nKEYS  TO FINANCIAL SUCCESS\n1 . Make a financial plan.\n2. Pay off any high interest debts.\n3.  Start saving and investing as soon as you’ve paid off your debts.', metadata={'source': '/content/Data/sec-guide-to-savings-and-investing.pdf', 'page': 4}), Document(page_content='Plan and complete the last two columns to help you track your progress.\nnPeriodically review your spending plan.\nnMonitor the performance of investments. \nMake adjustments if necessary.\nnMake sure you contribute more toward your retirement as you earn more.\nnUpdate your various insurance safety nets to reflect changes in income or personal circumstances.\nnKeep your finances in order .\nWhere To Go From Here', metadata={'source': '/content/Data/savings-fitness.pdf', 'page': 34}), Document(page_content='Let’s start with a “spending plan” – a guide for \nhow we want to spend our money. Some people call this a budget, but since we’re thinking of retirement as something to buy, a spending plan seems more appropriate.\nA spending plan is simple to set up. Consider \nthe following steps as a guide as you fill in the information in Worksheet 5 – Cash Flow \nSpending Plan in the back of this booklet.\nIncome\nAdd up your monthly income: wages, average', metadata={'source': '/content/Data/savings-fitness.pdf', 'page': 11})]
CPU times: user 12.7 s, sys: 418 ms, total: 13.1 s
Wall time: 13 s
"""

Tổng kết

Chúng ta có thể thấy dùng Hybrid Search với EnsembleRetriever cung cấp context tốt hơn cho LLM, từ đó tạo ra câu trả lời tốt hơn. Việc lưu trữ câu trả lời và truy vấn giảm thời gian suy luận và giảm chi phí tính toán. Lưu trữ embedding truy vấn cũng giúp tránh việc cần phải tính toán lại chúng.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí