<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Intel Tech - Medium]]></title>
        <description><![CDATA[The Intel Tech blog is designed to share the latest information on open source innovation and technical leadership. - Medium]]></description>
        <link>https://medium.com/intel-tech?source=rss----bcaa5b033cbb---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Intel Tech - Medium</title>
            <link>https://medium.com/intel-tech?source=rss----bcaa5b033cbb---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 14 May 2026 13:40:15 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/intel-tech" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Kubernetes autoscaling in OPEA v1.4]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/intel-tech/kubernetes-autoscaling-in-opea-v1-4-23d1c0681fec?source=rss----bcaa5b033cbb---4"><img src="https://cdn-images-1.medium.com/max/700/0*aidiJWz3WX4cKT07.png" width="700"></a></p><p class="medium-feed-link"><a href="https://medium.com/intel-tech/kubernetes-autoscaling-in-opea-v1-4-23d1c0681fec?source=rss----bcaa5b033cbb---4">Continue reading on Intel Tech »</a></p></div>]]></description>
            <link>https://medium.com/intel-tech/kubernetes-autoscaling-in-opea-v1-4-23d1c0681fec?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/23d1c0681fec</guid>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Wed, 03 Sep 2025 18:25:25 GMT</pubDate>
            <atom:updated>2025-09-03T18:25:25.002Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Deploying AI Agents Locally with Qwen3, Qwen-Agent, and Ollama]]></title>
            <link>https://medium.com/intel-tech/deploying-ai-agents-locally-with-qwen3-qwen-agent-and-ollama-cad452f20be5?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/cad452f20be5</guid>
            <category><![CDATA[ollama]]></category>
            <category><![CDATA[genai]]></category>
            <category><![CDATA[agents]]></category>
            <category><![CDATA[qwen]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Benjamin Consolvo]]></dc:creator>
            <pubDate>Wed, 28 May 2025 19:46:29 GMT</pubDate>
            <atom:updated>2025-05-28T19:46:29.557Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JHVpXjM8gZA7gg6NVqaScg.jpeg" /><figcaption>Image generated by author. Prompt: “use your tools like my_image_gen to generate a bear with a hat”.</figcaption></figure><p>Ever wanted to run your own AI agent locally — without sending data to the cloud? As an AI software engineer at Intel, I’ve been exploring how to run open-source LLMs locally on AI PCs. With the smaller <a href="https://huggingface.co/Qwen/Qwen3-8B">Qwen3</a> models, it’s totally possible. These models are compact enough to run on an AI PC and powerful enough to call tools and handle real tasks. Even the smaller variants of Qwen3 do allow for tool-calling, enabling you to build agentic workflows to do things like looking up live websites, function calling, and code execution. This guide walks through how to build your own agentic workflows using Qwen3, Qwen-Agent, and Ollama — without relying on the cloud.</p><h4>Ollama Setup and Qwen3 Model Hosting</h4><p>To keep everything local and private, I used <a href="https://ollama.com/">Ollama</a> — a lightweight way to run open-source models right on your machine. Here’s how I got Qwen3 running on my AI PC using WSL2 (Windows Subsystem for Linux).</p><p>Install Ollama with the Linux command, taken from the <a href="https://ollama.com/download/linux">Ollama website</a>:</p><pre>curl -fsSL https://ollama.com/install.sh | sh</pre><p>Ollama makes it easy to host your model. After installing Ollama, simply run</p><pre>ollama run qwen3:8b</pre><p>and the ~5.2GB Qwen3:8b model should download and run locally by default at the local address of <a href="https://localhost:11434/">https://localhost:11434/</a>. We will use this address later when building the agents with Qwen-Agent.</p><h4>Qwen-Agent Python Library</h4><p>After installing Ollama, I populated a requirements.txt file with the Qwen-Agent library,</p><pre>qwen-agent[gui,rag,code_interpreter,mcp]<br>qwen-agent</pre><p>and installed from the command line with</p><pre>pip install -r requirements.txt</pre><h4>Sample AI agent with Qwen3 using Qwen-Agent</h4><p>To build a sample AI agent with Qwen3, you can use the code snippet found on the <a href="https://github.com/QwenLM/Qwen-Agent">Qwen-Agent GitHub repository here</a>. The only modifications I made are to the llm_cfg to change the model toqwen3:8b, the model server to <a href="https://localhost:11434/v1,">https://localhost:11434/v1,</a> and a PDF file of a research paper called <em>Zheng2024_LargeLanguageModelsinDrugDiscovery.pdf</em>. In my case, I only made use of the built-in tool called my_image_gen to ask the LLM agent to use a tool to generate an image, but feel free to experiment with your own Qwen-Agent workflow. In this walkthrough, I’m showing how to create a simple AI agent that can generate an image based on your request — entirely locally using Qwen3.</p><pre>#from https://github.com/QwenLM/Qwen-Agent<br><br>import pprint<br>import urllib.parse<br>import json5<br>from qwen_agent.agents import Assistant<br>from qwen_agent.tools.base import BaseTool, register_tool<br>from qwen_agent.utils.output_beautify import typewriter_print<br><br><br># Step 1 (Optional): Add a custom tool named `my_image_gen`.<br>@register_tool(&#39;my_image_gen&#39;)<br>class MyImageGen(BaseTool):<br>    # The `description` tells the agent the functionality of this tool.<br>    description = &#39;AI painting (image generation) service, input text description, and return the image URL drawn based on text information.&#39;<br>    # The `parameters` tell the agent what input parameters the tool has.<br>    parameters = [{<br>        &#39;name&#39;: &#39;prompt&#39;,<br>        &#39;type&#39;: &#39;string&#39;,<br>        &#39;description&#39;: &#39;Detailed description of the desired image content, in English&#39;,<br>        &#39;required&#39;: True<br>    }]<br><br>    def call(self, params: str, **kwargs) -&gt; str:<br>        # `params` are the arguments generated by the LLM agent.<br>        prompt = json5.loads(params)[&#39;prompt&#39;]<br>        prompt = urllib.parse.quote(prompt)<br>        return json5.dumps(<br>            {&#39;image_url&#39;: f&#39;https://image.pollinations.ai/prompt/{prompt}&#39;},<br>            ensure_ascii=False)<br><br><br># Step 2: Configure the LLM you are using.<br>llm_cfg = {<br>    # Use the model service provided by DashScope:<br>    # &#39;model&#39;: &#39;qwen-max-latest&#39;,<br>    # &#39;model_type&#39;: &#39;qwen_dashscope&#39;,<br>    # &#39;api_key&#39;: &#39;YOUR_DASHSCOPE_API_KEY&#39;,<br>    # It will use the `DASHSCOPE_API_KEY&#39; environment variable if &#39;api_key&#39; is not set here.<br><br>    # Use a model service compatible with the OpenAI API, such as vLLM or Ollama:<br>    &#39;model&#39;: &#39;qwen3:8b&#39;,<br>    # &#39;model_server&#39;: &#39;http://localhost:8000/v1&#39;,  # base_url, also known as api_base<br>    &#39;model_server&#39;: &#39;http://localhost:11434/v1&#39;,  # Ollama<br>    &#39;api_key&#39;: &#39;EMPTY&#39;,<br><br>    # (Optional) LLM hyperparameters for generation:<br>    &#39;generate_cfg&#39;: {<br>        &#39;top_p&#39;: 0.8<br>    }<br>}<br><br># Step 3: Create an agent. Here we use the `Assistant` agent as an example, which is capable of using tools and reading files.<br>system_instruction = &#39;&#39;&#39;After receiving the user&#39;s request, you should:<br>- first draw an image and obtain the image url,<br>- then run code `request.get(image_url)` to download the image,<br>- and finally select an image operation from the given document to process the image.<br>Please show the image using `plt.show()`.&#39;&#39;&#39;<br>tools = [&#39;my_image_gen&#39;, &#39;code_interpreter&#39;]  # `code_interpreter` is a built-in tool for executing code.<br>files = [&#39;Zheng2024_LargeLanguageModelsinDrugDiscovery.pdf&#39;]  # Give the bot a PDF file to read.<br>bot = Assistant(llm=llm_cfg,<br>                system_message=system_instruction,<br>                function_list=tools,<br>                files=files)<br><br># Step 4: Run the agent as a chatbot.<br>messages = []  # This stores the chat history.<br>while True:<br>    # For example, enter the query &quot;draw a dog and rotate it 90 degrees&quot;.<br>    query = input(&#39;\nuser query: &#39;)<br>    # Append the user query to the chat history.<br>    messages.append({&#39;role&#39;: &#39;user&#39;, &#39;content&#39;: query})<br>    response = []<br>    response_plain_text = &#39;&#39;<br>    print(&#39;bot response:&#39;)<br>    for response in bot.run(messages=messages):<br>        # Streaming output.<br>        response_plain_text = typewriter_print(response, response_plain_text)<br>    # Append the bot responses to the chat history.<br>    messages.extend(response)</pre><h4>Prompt and Response</h4><p>I provided a prompt to ask qwen3:8b to generate an image:</p><pre>user query: use your tools like my_image_gen to generate a bear with a hat</pre><p>Here is the response:</p><pre> bot response:<br>2025-05-19 17:34:22,554 - doc_parser.py - 93 - INFO - Read chunked Zheng2024_LargeLanguageModelsinDrugDiscovery.pdf from cache.<br>2025-05-19 17:34:22,555 - base_search.py - 56 - INFO - all tokens: 50347<br>&lt;think&gt;<br>Okay, the user wants me to generate an image of a bear with a hat using the my_image_gen tool. Let me check the function&#39;s requirements. The function requires a prompt in English that describes the image details. I need to make sure the prompt is detailed enough. Let me think about the elements: a bear, a hat. Maybe specify the type of hat, like a top hat or a funny hat. Also, the setting—maybe a forest or a snowy mountain. Adding adjectives like &quot;cute&quot; or &quot;cartoon-style&quot; could help. Let me put it all together. The prompt should be something like &quot;A cute cartoon bear wearing a top hat in a forest setting.&quot; That should work. Now, I&#39;ll format the tool call with this prompt.<br>&lt;/think&gt;<br><br><br>[TOOL_CALL] my_image_gen<br>{&quot;prompt&quot;: &quot;A cute cartoon bear wearing a top hat in a forest setting&quot;}<br>[TOOL_RESPONSE] my_image_gen<br>{image_url: &quot;https://image.pollinations.ai/prompt/A%20cute%20cartoon%20bear%20wearing%20a%20top%20hat%20in%20a%20forest%20setting&quot;}<br>&lt;think&gt;<br>Okay, let&#39;s see what the user is asking for here. They provided an image URL of a cartoon bear wearing a top hat in a forest setting. The image was generated using the my_image_gen function, which takes a prompt and returns the URL.<br><br>First, I need to check if there&#39;s any specific request related to this image. The user might want to know more about the image, like details about the bear, the forest, or maybe how to generate similar images. Alternatively, they might be testing if I can process the image URL or describe tSince the user hasn&#39;tSince the user hasn&#39;t asked a direct question but provided the image, my role is to acknowledge the image and offer further assistance. I should confirm that the image was generated as expected and ask if they need anything else, like modifying the image, getting more images, or information about the generation process.<br><br>I should also make sure to keep the response friendly and open-ended, encouraging them to ask for more help if needed. No need to call any functions here since the user hasn&#39;t requested an action, just shared theSince the user hasn&#39;t asked a direct question but provided the image, my role is to acknowledge the image and offer further assistance. I should confirm that the image was generated as expected and ask if they need anything else, like modifying the image, getting more images, or information about the generation process.<br><br>I should also make sure to keep the response friendly and open-ended, encouraging them to ask for more help if needed. No need to call any functions here since the user hasn&#39;t requested an action, just shared the image.<br>&lt;/think&gt;<br><br>![](https://image.pollinations.ai/prompt/A%20cute%20cartoon%20bear%20wearing%20a%20top%20hat%20in%20a%20forest%20setting)<br><br>This is an AI-generated cartoon image of a cute bear wearing a top hat in a forest setting. The image appears to be created using text-to-image generation technology. Would you like me to help you with anything related to this image or AI-generated content?<br>user query:</pre><p>We can see that it did properly call the tool called “my_image_gen” and the generated image is at the top of this article. It is also hosted at the URL it cites near the end of its response: <a href="https://image.pollinations.ai/prompt/A%20cute%20cartoon%20bear%20wearing%20a%20top%20hat%20in%20a%20forest%20setting">https://image.pollinations.ai/prompt/A%20cute%20cartoon%20bear%20wearing%20a%20top%20hat%20in%20a%20forest%20setting</a>.</p><h4>My Device</h4><p>The AI PC laptop used in my testing has an Intel Core Ultra 7 155H 3.80 GHz processor with 32 GB of RAM.</p><h4>What We Built:</h4><p>✅ Installed and ran Qwen3:8b locally using Ollama<br>✅ Set up Qwen-Agent to build an AI assistant<br>✅ Connected a tool to generate images using text prompts<br>✅ Prompted the agent and got a real response — locally, on an Intel-powered AI PC</p><h4>Resources</h4><p>You can take advantage of building your own agents locally and speak with other developers using the resources listed below.</p><ul><li>Check out all of the possible <a href="https://ollama.com/library/qwen3">Qwen3 models hosted by Ollama</a></li><li>To build your own Qwen-based agents, visit the <a href="https://github.com/QwenLM/Qwen-Agent">Qwen-Agent GitHub repository</a></li><li>Learn more about the <a href="https://www.intel.com/content/www/us/en/products/docs/processors/core-ultra/ai-pc.html">AI PC Powered by Intel</a></li><li>To chat with other developers, you can visit the <a href="https://discord.gg/kfJ3NKEw5t">Intel DevHub Discord</a></li><li>For a more in-depth review and performance testing of Qwen3, you can visit the article <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/accelerate-qwen3-large-language-models.html">Intel® AI Solutions Accelerate Qwen3 Large Language Models</a></li><li>Check out <a href="http://developer.intel.com/ai">Intel’s AI developer resources here</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cad452f20be5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/deploying-ai-agents-locally-with-qwen3-qwen-agent-and-ollama-cad452f20be5">Deploying AI Agents Locally with Qwen3, Qwen-Agent, and Ollama</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build An End-to-End SQL + RAG AI Agent]]></title>
            <link>https://medium.com/intel-tech/build-an-end-to-end-sql-rag-ai-agent-in-just-four-steps-b8a5b4adea9d?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/b8a5b4adea9d</guid>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[rags]]></category>
            <category><![CDATA[agents]]></category>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Fri, 28 Mar 2025 20:52:36 GMT</pubDate>
            <atom:updated>2025-03-31T14:23:47.343Z</atom:updated>
            <content:encoded><![CDATA[<p>Learn how agents are implemented in enterprise use cases using OPEA blueprints to deploy</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bcJtdVhm65RSbciv6HXi2A.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@possessedphotography?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Possessed Photography</a> on <a href="https://unsplash.com/photos/white-robot-action-toy-zbLW0FG8XU8?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Unsplash</a></figcaption></figure><p>Excitement surrounds every new large language model (LLM) release, yet enterprises still struggle to extract real value from them. Techniques like retrieval augmented generation (RAG) enhance LLM capabilities by injecting relevant context into prompts. The process seems simple: build a knowledge base, retrieve data, and provide context. However, this context remains static. When a query requires refinement, deeper reasoning, or multi-step processing, the system doesn’t dynamically adjust its retrieval strategy; it simply returns what it finds in one step.</p><p>Imagine a music discovery application using an LLM to assist users in exploring bands, albums, and song details. A user inputs a query about a band, expecting the system to provide not only a list of their albums but also detailed insights into each album’s release date, genre, and tracklist. However, if the system only retrieves information based on a static context without dynamically adjusting to incorporate deeper reasoning or multi-step processing, it might return a generic list of albums without contextual details such as collaborations, notable tracks, or historical significance. This could lead to an incomplete or less engaging music discovery experience, missing key insights that enhance a user’s understanding and appreciation of the band’s discography.</p><p>We’ve all gotten used to having chatbots spit out quick answers to generic questions, but we want and need our GenAI assistants to do more. We don’t want them to solve just any problems; we want them to solve our specific problems. While RAG improves context injection, it still lacks the flexibility to handle complex queries that require iterative reasoning or dynamic decision-making.</p><p>AI agents solve this problem. By orchestrating retrieval, reasoning, and action-taking, they push GenAI beyond static context injection, enabling more dynamic, context-aware, and useful responses. Agents also provide a crucial bridge to the external world, connecting with APIs, databases, enterprise systems, and other tools to fetch live data, execute tasks, and adapt to evolving information. We can have the best, smartest, and most well-trained LLM. But without external tools, it’s confined to processing only the input it is given. Agents enable the GenAI application to interact and manipulate the world around it, transforming it from a passive knowledge repository into an active problem solver.</p><h3>What is an AI Agent?</h3><p>An AI agent is like a super-smart program designed to make decisions and take actions in a way that feels like it’s thinking on its own. It doesn’t just follow a fixed set of instructions like an “if-else” program does. Instead, it can:</p><ol><li>Perceive its environment (it gathers info like a person looking around).</li><li>Think about what to do (processing the info it gathers).</li><li>Act on its own to achieve a specific goal.</li></ol><p>Take, for example, a robot vacuum cleaner. It senses where dirt is (perceives), decides which path to clean (thinks), and moves around cleaning (acts).</p><p>An AI agent is built using a few key components:</p><ol><li><strong>Perception</strong>: This is how the agent senses or gathers information from its environment. It could be data from sensors, user input, or anything it can access (for example, camera data, temperature, text inputs).</li><li><strong>Decision-Making</strong>: After perceiving its environment, the agent processes that information to decide what action to take. This is typically done using algorithms or models like machine learning, decision trees, or rule-based systems. It tries to pick the best option based on the data it has.</li><li><strong>Action</strong>: Once it has decided what to do, the agent takes action to affect its environment. This could be sending a response, moving, adjusting settings, or anything the agent is designed to do.</li></ol><h3>What are Tools?</h3><p>These components often rely on Tools, which are specific functions that perform actions. Tools are independent pieces of software that carry out tasks, ranging from simple operations like mathematical calculations to more complex tasks like sensing the external world. They can be seen as specialized components that help the agent execute certain tasks or interact with its environment more effectively.</p><p>For example, in your robot vacuum, perception would be the sensors detecting dirt, the action would be moving the vacuum, and the <em>tool </em>would be the cleaning mechanism itself, which is an independent function that the robot calls upon to clean specific areas.</p><h3>Open Platform for Enterprise AI (OPEA) AgentQnA Interface Planner: SQL &amp; RAG Agents</h3><p>Deploying an agent can be a simple task if done within a single script, but this approach is not sufficient for enterprise-level solutions. Deployments need to be scalable, and each functionality should ideally be encapsulated. This can be achieved through a cloud native architecture. <a href="https://opea.dev">OPEA</a>, an open source project (part of LF AI&amp;DATA), is a framework for building and deploying modular, composable generative AI solutions. OPEA focuses on security, scalability, and cost-efficiency and provides the building blocks and blueprints to make this possible. In this blog, we will use the <a href="https://github.com/opea-project/GenAIExamples/tree/main/AgentQnA">AgentQnA blueprint</a> to build a hierarchical multi-agent system for question-answering applications. The blueprint is available on Docker (docker-compose) or <a href="https://github.com/opea-project/GenAIExamples/tree/main/AgentQnA/kubernetes/helm">Helm charts (Kubernetes).</a> The example implements the following architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/970/0*AAIEq1EjES8A3j9T.png" /></figure><p>Image from OPEA AgentQnA repo (https://github.com/opea-project/GenAIExamples/tree/main/AgentQnA)</p><p>You can find instructions for deploying the example in <a href="https://github.com/opea-project/GenAIExamples/tree/main/AgentQnA">this guide</a>. After deploying the blueprint you’ll see a set of microservices deployed on your environment. The deployment scenario includes a knowledge base with data in two common formats:</p><ul><li><strong>SQL Db</strong>: In many businesses, you’ll find SQL databases being used to support core functions, such as managing customer relationships, processing orders, tracking inventory, and generating reports. The structured nature of SQL databases allows organizations to store data in a clear, organized way, making it easier to maintain and query. In this example, we use a <a href="https://github.com/lerocha/chinook-database/blob/master/ChinookDatabase/DataSources/Chinook_Sqlite.sql">sample SQL database</a> to demonstrate how companies can leverage these types of databases for their operations, and we will feed it with two main tables. “Business” tables managing customers, employees, and sales: Customer, Invoice, InvoiceLine, and Employee. “Music” tables storing media details: Artist, Album, Track, Genre, MediaType, Playlist, and PlaylistTrack.</li><li><strong>VectorDB</strong>: A vector database stores rich, unstructured data, such as text, which is valuable for retrieval-augmented generation (RAG) applications. Unlike SQL databases with structured tables, a vector database represents information in high-dimensional vectors, capturing deeper context. This allows RAG to retrieve not only structured data, but also relevant text, enhancing the accuracy and richness of generated responses. In this example, we’ll have music information + context which adds more information, making it useful to perform the RAGs (from <a href="https://github.com/minmin-intel/GenAIExamples/blob/test-openai/AgentQnA/example_data/test_docs_music.jsonl">https://github.com/minmin-intel/GenAIExamples/blob/test-openai/AgentQnA/example_data/test_docs_music.jsonl</a>).</li></ul><p>There are three agents involved, all of them built on Langchain/LangGraph frameworks.</p><ul><li><strong>Supervisor Agent</strong>: The supervisor’s main role is to process the input, identify which tool to use to answer the query (SQL or RAG), and manage the generation of responses (potentially by calling the LLM). This agent is based on <a href="https://arxiv.org/pdf/2210.03629">ReAct strategy</a> — it engages in “reason-act-observe” cycles to solve problems. Please refer to this <a href="https://python.langchain.com/v0.2/docs/how_to/migrate_agent/">doc</a> to learn more about how to use this strategy.</li><li><strong>Worker Agent (RAG):</strong> The worker RAG agent uses the retrieval tool to retrieve relevant documents from the knowledge base (a vector database).</li><li><strong>Worker agent (SQL):</strong> The worker SQL agent retrieves relevant data from the SQL database.</li></ul><p>To test the example, you can use the scripts provided <a href="https://github.com/opea-project/GenAIExamples/tree/main/AgentQnA">here</a>. You can test each agent independently or you can directly test the supervisor by running:</p><pre># supervisor agent: this will test a two-turn conversation<br>python tests/test.py - agent_role &quot;supervisor&quot; - ext_port 9090</pre><p>Since the example feeds information about music you can find tests related to that topic. The test for the supervisor agent is done in <strong>two turns</strong> (like a back-and-forth conversation):</p><ol><li>We ask, <strong>“Which artist has the most albums ?”</strong> to the supervisor agent.</li><li>The supervisor agent chooses the tool to use, and the RAG Agent goes into its <strong>music database</strong> and finds the answer.</li><li>Then we ask, <strong>“Give me a few examples of that artist’s albums?”</strong></li><li>The Supervisor <strong>remembers</strong> you were talking about the artist and asks the SQL Agent to respond with album names.</li></ol><p>You can experiment with different types of data to observe how the system adapts and responds to various queries.</p><h3>Build Your Own Agent</h3><p>This example demonstrates how to deploy an SQL use case, but agents are dynamic, and you may want to register your own agent within the architecture. You can explore the YAML and Python files in this example to understand how tools are integrated. Learn more about the strategies at: <a href="https://github.com/opea-project/GenAIComps/tree/main/comps/agent/src">opea-project/GenAIComps</a>.</p><p>For more details, please refer to the <strong>“Provide your own tools”</strong> section in the instructions <a href="https://github.com/opea-project/GenAIComps/blob/main/comps/agent/src/README.md">here</a>.</p><p><a href="https://github.com/opea-project/docs/blob/main/community/CONTRIBUTING.md">Contribute to the project</a>! OPEA is built by a growing community of developers and AI professionals. Whether you’re interested in contributing code, improving documentation, or building new features, your involvement is key to our success.</p><p>Join us on the OPEA <a href="https://github.com/opea-project">GitHub</a> to start contributing or explore our issues list for ideas on where to start.</p><h3>About the Author</h3><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel &amp; LF AI&amp;DATA Chair/Board)</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist at Intel, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on <a href="https://intel-my.sharepoint.com/personal/ezequiel_lanza_intel_com/Documents/@eze_lanza">X</a> and <a href="https://www.linkedin.com/in/ezelanza/">LinkedIn</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b8a5b4adea9d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/build-an-end-to-end-sql-rag-ai-agent-in-just-four-steps-b8a5b4adea9d">Build An End-to-End SQL + RAG AI Agent</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding Retrieval Augmented Generation (RAG)]]></title>
            <link>https://medium.com/intel-tech/understanding-retrieval-augmented-generation-rag-4d1d08f736b3?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/4d1d08f736b3</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[retrieval-augmented-gen]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Thu, 15 Aug 2024 19:31:15 GMT</pubDate>
            <atom:updated>2024-08-15T19:31:15.664Z</atom:updated>
            <content:encoded><![CDATA[<p>Learn what a RAG system is and how to deploy it using OPEA’s open source tools and frameworks</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*aO3M-h-PR0AffMbK" /><figcaption>Photo by <a href="https://unsplash.com/@xavi_cabrera?utm_source=medium&amp;utm_medium=referral">Xavi Cabrera</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>By this point, most of us have used a large language model (LLM), like ChatGPT, to try to find quick answers to questions that rely on general knowledge and information. These questions range from the practical (What’s the best way to learn a new skill?) to the philosophical (What is the meaning of life?).</p><figure><img alt="A screenshot listing the most common questions asked to an AI. The questions include topics like AI, homework help, the meaning of life, weather updates, creative writing, productivity, technology trends, and book or movie recommendations" src="https://cdn-images-1.medium.com/max/738/1*8LbsOeORn1wHRXmVaUX7PA.png" /><figcaption>Image 1: ChatGPT gives what are some of the most common questions it get asked</figcaption></figure><p>But how do you get answers to questions that are personal? How much does your LLM know about you? Or your family?</p><p>Let’s test ChatGPT and see how much it knows about my parents.</p><figure><img alt="A screenshot of a conversation where a user asks, ‘Do you know who is my mum?’ and the AI responds, explaining that it doesn’t have access to personal information about individuals." src="https://cdn-images-1.medium.com/max/749/1*UjguHKM8638VWmpbSw0nVw.png" /><figcaption>Image 2: ChatGPT answer to “Do you know who is my mum?”</figcaption></figure><p>It’s understandable to feel frustrated when a model doesn’t recognize you, but it’s important to remember that these models don’t have much information about our personal lives. Unless you’re a celebrity or have your own Wikipedia page (as Tom Cruise has), the training dataset used for these models likely doesn’t include our information, which is why they can’t provide specific answers about us.</p><figure><img alt="A screenshot displays a question asking if the model knows who is Tom Cruise mum? the model answered Mary Lee Pfeiffer is his mom" src="https://cdn-images-1.medium.com/max/834/1*JigKp5EmMznIALEB9zgF9Q.png" /><figcaption>Image 2: ChatGPT answer to “Do you know who Tom Cruise&#39;s mum is?”</figcaption></figure><p>So, how do we get our LLMs to know us better?</p><p>That’s the million-dollar question facing enterprises looking to boost productivity with GenAI. They need models that provide context-based results. In this post, we’ll explain the basics of how retrieval augmented generation (RAG) improves your LLM’s responses and show you how to easily deploy your RAG-based model using a modular approach with the open source building blocks that are part of the new <a href="https://opea.dev/">Open Platform for Enterprise AI (OPEA)</a>.</p><h3>What is RAG?</h3><p>We know that LLMs can greatly contribute to completing an extensive number of tasks, such as writing, learning, programming, translating, and more. However, the result we receive depends on what we ask the model, in other words, on how we meticulously build our prompts. For that reason, we spend too much time looking for the perfect prompt to get the answer we want; we’re starting to become experts in <a href="https://en.wikipedia.org/wiki/Prompt_engineering">model prompting</a>.</p><p>Let’s return to the above question: “Who is my mum?” We know who our mum is, we have memories, and that information lives in our “mental” knowledge base, our brain.</p><p>When building the prompt, we need to somehow provide it with memories of our mum and try to guide the model to use that information to creatively answer the question: Who is my mum? We’ll provide it with some of mum’s history and ask the model to take her past into account when answering the question.</p><figure><img alt="A screenshot displays a question asking for a creative response based on provided details: the user’s mother was born in the US, is 60, has strong Italian roots, and loves pizza. Below, a response discusses how her Italian heritage influences her personality, family traditions, and love for Italian cuisine, emphasizing her vibrant and lively nature." src="https://cdn-images-1.medium.com/max/724/1*d0BUZgA_wJsudQyv4UgU1Q.png" /><figcaption>Image 4: Instructions prompt a creative response based on context about the user’s mother, emphasizing how her Italian roots influence her personality and preferences.</figcaption></figure><p>As we can see, the model successfully gave us an answer that described my mum. Congratulations, we have used RAG!</p><p>Let’s inspect what we did.</p><p>Given the initial question, we tweaked the prompt to guide the model in how to use the information (context) we provided.</p><p>We can think of the RAG process in three parts :</p><figure><img alt="A screenshot shows a section divided into three parts. The top part labeled “Instruct” gives instructions asking to answer the question creatively, considering how roots influence the person’s behavior. The middle section, labeled “Context,” lists facts about the user’s mother: born in the US, age 60, strong Italian roots, and a love for pizza. The bottom part, labeled “Initial Question,” asks “Who is my mum?”" src="https://cdn-images-1.medium.com/max/966/1*IZGHzg6sDcHNvGKBGxTEAw.png" /><figcaption>Image 5: Prompt provided to the LLM for answering creatively based on the provided context.</figcaption></figure><ul><li><strong>Instruct</strong>: Guide the model. We have guided the model to use the information we provided (documents) to give us a creative answer and take into account my mum’s history. We used those instructions as an example; we could have used other guidance depending on the outcome we wanted to achieve. If we don’t want a creative answer, for example, this is the time to declare it.</li><li><strong>Context</strong>: Provide the context. In this example, we already knew the information about my mother since we retrieved that information from my memories, but in a real scenario, the challenge would be finding the relevant data in a knowledge base to feed the model so that it has the context needed to provide us with an accurate response, this process is called “retrieval.”</li><li><strong>Initial Question</strong>: The initial question we want answered.</li></ul><p>Let’s explore how an enterprise can implement a real-life RAG example using open source tools and models. We’ll deploy it using the standardized frameworks and tools made available through OPEA, which was created to help streamline the implementation of enterprise AI.</p><h3>Exploring the OPEA Architecture</h3><p>Here’s the architecture we used for the previous example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*S7m6NGJvROlZGE9SwVfXEg.png" /><figcaption>Image 6: Author’s RAG architecture adaptation using a building block concept, based on the original OPEA ChatQnA RAG architecture (<a href="https://github.com/opea-project/GenAIExamples/tree/main/ChatQnA">https://github.com/opea-project/GenAIExamples/tree/main/ChatQnA</a>)</figcaption></figure><p>RAG can be understood as simply the steps mentioned above:</p><p>1) Initial Question</p><p>2) Context</p><p>3) Instruct</p><p>However, implementing the process in practice can be challenging because multiple components are needed: retrievers, embedding models, and a knowledge base, as shown in the image above. Let’s explore how those parts can work together.</p><p>The key lies in providing the right context. You can compare the process to how our memories help us answer questions. For a company, this might mean drawing from a knowledge base of historical financial data or other relevant documents.</p><p>For example, when a user asks a chatbot a question before the LLM can spit out an answer, the RAG application must first dive into a knowledge base and extract the most relevant information (the retrieval process). But even before the retrieval happens, an embedding model plays a crucial role in converting the data in the knowledge base into vector representations — meaningful numerical embeddings that capture the essence of the information. These embeddings will live in the knowledge base (vector database) and will allow the retriever to efficiently match the user’s query with the most relevant documents.</p><p>Once the RAG application finds the relevant documents, it performs a rerank process to check the quality of the information and then re-orders the information based on relevance. It then builds a new prompt based on the refined context from the top-ranked documents and sends this prompt to the LLM, enabling the model to generate a high-quality, contextually informed response. Easy, right?</p><p>As you can see, the RAG architecture isn’t about just one tool or one framework; it’s composed of multiple moving pieces making it difficult to pay attention to each component. When deploying a RAG system in our enterprise, we face multiple challenges, such as ensuring scalability, handling data security, and integrating with existing infrastructure.</p><p>The Open Platform for Enterprise AI (OPEA) aims to solve those problems by treating each component in the RAG pipeline as a building block that is easily interchangeable. Say, for example, you’re using <a href="https://huggingface.co/docs/transformers/en/model_doc/mistral">Mistral</a>, but want to easily replace it with <a href="https://huggingface.co/docs/transformers/en/model_doc/falcon">Falcon</a>. Or, say you want to replace a vector database on the fly. You don’t want to have to rebuild the entire application. That would be a nightmare. OPEA makes deployment easier by providing robust tools and frameworks designed to streamline these processes and facilitate seamless integration.</p><p>You can see this process in action by running the ChatQnA example: <a href="https://github.com/opea-project/GenAIExamples/tree/main/ChatQnA">https://github.com/opea-project/GenAIExamples/tree/main/ChatQnA</a>. There, you’ll find all the steps needed to create the building blocks for your RAG application on your server or your AIPC.</p><h3>Call to Action</h3><p>We have shown you the basics of how RAG works and how to deploy a RAG pipeline using the OPEA framework. While the process is straightforward, deploying a RAG system at scale can introduce complexities. Here’s what you can do next:</p><ul><li>Explore <a href="https://github.com/opea-project/GenAIComps">GenAIComps</a>: Gain insights into how generative AI components work together and how you can leverage them for real-world applications. OPEA provides detailed examples and documentation to guide your exploration.</li><li>Explore <a href="https://github.com/opea-project/GenAIExamples/tree/main/ChatQnA">RAG demo(ChatQnA)</a>: Each part of a RAG system presents its own challenges, including ensuring scalability, handling data security, and integrating with existing infrastructure. OPEA, as an open source platform, offers tools and frameworks designed to address these issues and make the deployment process more efficient. Explore our demos to see how these solutions come together in practice.</li><li>Explore <a href="https://github.com/opea-project/GenAIExamples/tree/main">GenAI Examples</a>: OPEA is not focused only on RAG; it is about generative AI as a whole. Multiple other demos, such as <a href="https://github.com/opea-project/GenAIExamples/tree/main/VisualQnA">VisualQnA</a>, showcase different GenAI capabilities. These examples demonstrate how OPEA can be leveraged across various tasks, expanding beyond RAG into other innovative GenAI applications.</li><li><a href="https://github.com/opea-project/docs/blob/main/community/CONTRIBUTING.md">Contribute to the project</a>! OPEA is built by a growing community of developers and AI professionals. Whether you’re interested in contributing code, improving documentation, or building new features, your involvement is key to our success.</li></ul><p>Join us on the OPEA <a href="https://github.com/opea-project">GitHub</a> to start contributing or explore our issues list for ideas on where to start.</p><h3>About the Author</h3><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist on Intel’s <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/team.html">Open Ecosystem</a> team, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on <a href="https://intel-my.sharepoint.com/personal/ezequiel_lanza_intel_com/Documents/@eze_lanza">X</a> and <a href="https://www.linkedin.com/in/ezelanza/">LinkedIn</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4d1d08f736b3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/understanding-retrieval-augmented-generation-rag-4d1d08f736b3">Understanding Retrieval Augmented Generation (RAG)</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Improve your Tabular Data Ingestion for RAG with Reranking]]></title>
            <link>https://medium.com/intel-tech/improve-your-tabular-data-ingestion-for-rag-with-reranking-bebcf52cdde3?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/bebcf52cdde3</guid>
            <category><![CDATA[retrieval-augmented]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Tue, 16 Jul 2024 15:02:28 GMT</pubDate>
            <atom:updated>2024-07-31T20:17:09.794Z</atom:updated>
            <content:encoded><![CDATA[<p>Boost your RAG system’s accuracy by adding a reranker to select the most relevant context chunks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FgFiGQOxfAsaTmhd" /><figcaption>Photo by <a href="https://unsplash.com/@shawnanggg?utm_source=medium&amp;utm_medium=referral">shawnanggg</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>By Eduardo Rojas Oviedo with Ezequiel Lanza</h4><p>In our previous post,<a href="https://medium.com/p/bcb42678914b"> Tabular Data, RAG, &amp; LLMs</a>, we explored how to improve a large language model’s (LLM’s) ability to generate responses by feeding it small tabular data in various formats to provide necessary context. However, when the provided context is only partially correct, mismatches can lead to less accurate responses from our LLM.</p><p>In a typical RAG architecture, the retriever part gets chunks of data based on a similarity search from documents stored in a knowledge base. But are those chunks always the most relevant for our case?</p><p>Using the example from our previous article, what if we ask, “<em>Who are the top billionaires in the tech industry in 2024?”</em> and we get documents related to the overall list of world billionaires, including those from various industries that are not specific to tech? If the retriever returns documents about just any world billionaire working in any industry, we won’t get the response we need when the LLM is prompted.</p><p>To provide addtional context beyond basic text-based input based on ingesting small tabular data in various formats. It has shown to be useful in enhancing LLMs comprehension not only in plain text but also in other formats like tables.</p><p>In this post, we’ll demonstrate how to reduce mismatches by using a ranker that can select the most relevant context chunks, ensuring that the LLM gets the best possible information for generating accurate answers.</p><h3>Extracting and Processing Data for a RAG System</h3><p>Our demonstration builds a Q&amp;A chatbot on top of what is considered a basic RAG solution, but we’re adding a reranker at the end to perform an additional ranking over the retrieved chunks. As shown in Image 1, this process has three stages: Data Preparation, Indexing, and Retrieval. We won’t cover the “staging” part; we can consider it as our knowledge base, and it’s not part of this tutorial. For this example, the “knowledge base” will be a PDF containing the <a href="https://en.wikipedia.org/wiki/The_World%27s_Billionaires">World ‘s Billionaires</a> data from Wikipedia, when in a business scenario it could be the entire company database.</p><figure><img alt="A flowchart depicting a data pipeline. Stages include data cleaning, preparation, indexing, retrieval, and storage of data." src="https://cdn-images-1.medium.com/max/1024/1*KNOBL2kEZbZOz_qesp0Z4Q.png" /><figcaption>Image 1: A comprehensive data pipeline diagram illustrating the stages of data processing, from raw data ingestion to data retrieval, created by the author.</figcaption></figure><h4>Data Preparation</h4><p>First, we need to prepare our data for storage in our database. Since our data has both tables and text, the data pipeline creation will follow two paths: one for the text, and one for the tabular data.</p><p>In the <strong>text Path</strong>, we’ll extract the text from the PDF and perform pre-processing tasks, which includes data cleaning, as discussed in a <a href="https://medium.com/p/77bee9003625">previous article</a>, and implementing a character-based chunking strategy as well as adding in the metadata.</p><p>For the <strong>tabular path,</strong> we’ll extract two sets of tabular data and demonstrate how to convert the information into useful context chunks for later model consumption (as explained in <a href="https://medium.com/p/bcb42678914b">this article</a>). Because the PDF includes tables, we’ll also need to select which approach to use to convert the information to vectors. Then we’ll employ two approaches: one based on row-by-row chunks of information and another using the entire table, as we explained in our previous article <a href="https://medium.com/intel-tech/tabular-data-rag-llms-improve-results-through-data-table-prompting-bcb42678914b">Tabular Data, RAG, &amp; LLMs.</a></p><h4>Indexing</h4><p>The next step is to choose how we’ll store the data (which we converted to vectors) Depending on how we want to store the data, the most common approach is to use a unified context collection (UCC). UCC keeps all the information and metadata in a single data location, along with the vectors we’ll use for semantic search; we can think of this approach as having one dataset containing all the information. This is called a collection.</p><figure><img alt="Diagram showing the structure of a Unified Context Collection in Chroma. It consists of three main components: Document, Metadata, and Embedding. The Document component includes content like “The World’s Billionaires…” and a table. The Metadata component includes details like “No: 1, Name: Bern…”. The Embedding component contains numerical values like “[1.0, 2.1, 3.4…]”. Each component is depicted with a series of rectangles representing data entries." src="https://cdn-images-1.medium.com/max/732/1*eIluIcVelqnuRui_K50t_w.png" /><figcaption>Image 2: Adaptation by the authors from <a href="https://docs.trychroma.com/">Chorma — Home page</a>. It illustrates Chroma’s Unified Context Collection, showcases the integration of documents, metadata, and embeddings for comprehensive data representation.</figcaption></figure><p>Another approach is to use a distributed context collection (DCC) where information, metadata, and vectors are stored in separate collections according to the type of information. In this scenario, we’ll have multiple collections rather than one, central location.</p><figure><img alt="Diagram showing Chroma’s Distributed Context Collection and Distributed Table Collection. The Distributed Context Collection includes three components: Document, Metadata, and Embedding. The Document contains content like “The World’s Billionaires…”. The Metadata includes details like “Owner: aaa, Table: …”. The Embedding component has numerical values like “[0.8, 1.6, 2.4, …]”. The Distributed Table Collection also includes Document, Metadata, and Embedding components. The Document here c" src="https://cdn-images-1.medium.com/max/1024/1*xHRl37W-sYtDaBIKzdpK9Q.png" /><figcaption>Image 3. Adaptation by the authors from <a href="https://docs.trychroma.com/guides">Chroma — Home page</a> comparing Chroma’s Distributed Context Collection and Distributed Table Collection, highlighting their respective components: Document, Metadata, and Embedding.</figcaption></figure><p>Each approach has its pros and cons. A DCC may be more complex to manage due to the need for data synchronization across multiple nodes, managing potential inconsistencies,and distributed storage efficiently but it can offer greater scalability and fault tolerance, enabling the system to handle higher loads and recover more quickly from node failures. By contrast, a UCC offers simpler data management by centralizing all data in a single location, ensuring consistent and straightforward administration, but may face challenges with scalability and can become a single point of failure. Below, we evaluate both scenarios to determine how this configuration could affect the retrieval process.</p><h4>Retrieval</h4><p>The final stage of this process is retrieving the data. In this stage, the most relevant documents are retrieved from our collections before prompting the LLM, as shown in image four below. A similarity search is performed to retrieve the documents most similar to the user’s question. <em>This sounds great, but are these documents the most relevant?</em> We propose adding an additional step that will perform a verification of the retrieved documents to rank them by similarity. A rerank approach involves scoring the retrieved documents to prioritize the most relevant ones before they are fed into the LLM for response generation. By ensuring the most pertinent information is prioritized, reranking minimizes irrelevant or less useful data, thereby improving the overall performance and reliability of the RAG system. You can explore a deeper explanation of this topic in this<a href="https://www.pinecone.io/learn/series/rag/rerankers/"> Pinecone article.</a></p><figure><img alt="A data vector space diagram, with axes labeled “Semantic” and “Unified Content Collection.” Data points are scattered in the space, with labels like “Question Search,” “Answer,” “Esedang,” and “Metadata.”" src="https://cdn-images-1.medium.com/max/1024/1*r_8S3sVrfuaPBANyHbj-1w.png" /><figcaption>Image 4. Adaptation by the author from <a href="https://docs.trychroma.com/guides">Chorma — Home page</a> and <a href="https://cookbook.openai.com/examples/question_answering_using_a_search_api">API and API and re-ranking</a>.This diagram illustrates the reranking process. Data points (queries, answers) are adjusted in a semantic space based on relevance.</figcaption></figure><h3>Let’s see it in action</h3><p>Let’s then run our experiments! We’ll guide you through setting up the environment, defining functions, and running the experiment. Our goal is to see how well the model performs at answering questions based on a PDF document.</p><h4>Prepare the environment</h4><p>We first set up the environment using <strong>Python 3.12.3</strong> and provided all the required dependencies in <strong>requirements.txt</strong>[<a href="https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#install-packages-in-a-virtual-environment-using-pip-and-venv">Install packages in a virtual environment</a>] file with the following required library specifications:</p><pre>tqdm==4.66.4<br>spacy==3.7.4<br>pypdf==4.2.0<br>langchain-community==0.2.1<br>langchain-text-splitters== 0.2.0<br>flashrank==0.2.5<br>opencv-python==4.9.0.80<br>camelot-py==0.11.0<br>ghostscript==0.7<br>openai 1.30.5<br>chromadb==0.5.0<br>sentence-transformers==3.0.0</pre><h4>Convert the Data to PDF</h4><p>Next, prepare your we need to prepare our data, and load it from a local PDF file. For this purpose, we’ll convert the World’s Billionaires data from Wikipedia, which you can find here: <a href="https://en.wikipedia.org/wiki/The_World%27s_Billionaires">The World’s Billionaires</a> to a PDF format.</p><pre>from pathlib import Path<br><br># PDF file path<br>ROOT = Path(os.getcwd()).absolute()<br>file_path = os.path.join(ROOT, &quot;temp&quot;, &quot;World_Billionaires_Wikipedia.pdf&quot;)<br>file_path</pre><h4>Helper Functions for Data Preparation</h4><p>We now need to define the helper functions we’ll be using later. We’ll start by defining the data preparation functions that convert our data from the initial PDF format into useful chunks. In a <a href="https://medium.com/intel-tech/four-data-cleaning-techniques-to-improve-large-language-model-llm-performance-77bee9003625">previous article</a>, we discussed the essential skills for data cleaning, emphasizing the importance of effective data transformation.</p><ul><li><strong>unicode_to_ascii</strong>: Converts <a href="https://en.wikipedia.org/wiki/Unicode">Unicode</a> text to ASCII by normalizing Unicode characters, removing any accents or special characters that don’t exist in ASCII, and ensuring the text remains readable in UTF-8 encoding.</li></ul><pre>import unicodedata<br>def unicode_to_ascii(text):<br>&quot;&quot;&quot;Normalize unicode values&quot;&quot;&quot;<br> return unicodedata.normalize(&#39;NFKD&#39;, text).encode(&#39;ascii&#39;, &#39;ignore&#39;).decode(&#39;utf-8&#39;, &#39;ignore&#39;)</pre><ul><li><strong>Load_PDF</strong>: This function extracts text from our PDF file and then normalizes it. It also generates metadata values (see <a href="https://arxiv.org/abs/2402.07483">T-RAG: Lessons from the LLM Trenches</a> for other approaches), and finally, employs a basic character-based chunking strategy. We’ll use some libraries from <a href="https://pypi.org/project/langchain/">LangChain</a> to retrieve information from our PDF (<a href="https://api.python.langchain.com/en/latest/document_loaders/langchain_community.document_loaders.pdf.PyPDFLoader.html">PyPDFLoader</a>), perform the chunking process(<a href="https://api.python.langchain.com/en/latest/character/langchain_text_splitters.character.RecursiveCharacterTextSplitter.html">RecursiveCharacter TextSplitter</a>), and handle data in a standardized manner as a <a href="https://python.langchain.com/v0.1/docs/modules/data_connection/document_loaders/">Document</a>.</li></ul><pre>from langchain.document_loaders import PyPDFLoader<br>from langchain.text_splitter import RecursiveCharacterTextSplitter<br>from langchain_core.documents import Document<br>def load_pdf(file_path, chunk_size, chunk_overlap):<br>    <br>    # load pdf with Langchain loader<br>    loader = PyPDFLoader(file_path)<br>    documents = loader.load()<br>    <br>    # get total pages count<br>    page_count = len(documents)<br>    <br>    # text cleaning and normalization<br>    for document in tqdm(documents):<br>        # text cleaning<br>        document.page_content = normalize(document.page_content)<br><br>        # add metadata &#39;text&#39; classification<br>        document.metadata[&quot;page&quot;] = str(document.metadata[&quot;page&quot;] + 1)<br>        document.metadata[&quot;type&quot;] = &quot;text&quot;<br>        <br>    # create text chunks base on character splitter<br>    text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)<br>    text_chunks = text_splitter.split_documents(documents)<br>    <br>    return text_chunks, page_count<br><br></pre><ul><li><strong>get_tables: </strong>This function extracts tables from a PDF document (<em>path</em>) across multiple pages, cleans up table data, generates summaries for each row, and returns structured outputs<em>: tables_summary</em> for textual summaries, <em>metadata_result</em> for metadata, and <em>tables_result</em> for cleaned table data (classified as “row” and “table” as we saw in our previous article <a href="https://medium.com/intel-tech/tabular-data-rag-llms-improve-results-through-data-table-prompting-bcb42678914b">Tabular Data, RAG, &amp; LLMs</a>. Our main challenge consists of extracting tabular information using camelot-py and then expressing the information as context chunks that add value to our contextual information. We will need camelot-py to extract tabular data from our PDF.</li></ul><p>— <strong>Note</strong>: Keep in mind the limitation of this algorithm, which does not consider tables distributed across pages.</p><pre><br>import Camelot<br><br>def get_tables(path: str, pages: int, year: int):<br>    <br>    tables_result, metadata_result, tables_summary = [], [], []<br><br>    for page in tqdm(range(1, pages)):<br>        <br>        table_list = camelot.read_pdf(path, pages=str(page))<br><br>        for tab in range(table_list.n):<br>            <br>            df_table = table_list[tab].df.dropna(how=&quot;all&quot;).loc[:, ~table_list[tab].df.columns.isin([&#39;&#39;,&#39; &#39;])]<br>            df_table = df_table.apply(lambda x: x.str.replace(&quot;\n&quot;, &quot; &quot;).replace(&quot;\xa0&quot;, &quot; &quot;))<br><br>            df_table = df_table.rename(columns=df_table.iloc[0]).drop(df_table.index[0]).reset_index(drop=True)<br><br>            if df_table.shape[0] &lt;= 3 or df_table.eq(&quot;&quot;).all(axis=None):<br>                continue<br><br>            df_table[&quot;Year&quot;] = year # Complete missing values (for demonstration purposes only)<br>            metadata_table = {&quot;source&quot;: path, &quot;page&quot;: str(page), &quot;year&quot;: str(year), &quot;type&quot;: &quot;row&quot;}<br><br>            df_table[&quot;summary&quot;] = df_table.apply(<br>                lambda x: &quot; &quot;.join([f&quot;{col}: {val}, &quot; for col, val in x.items()]).replace(&quot;\xa0&quot;, &quot; &quot;),<br>                axis=1<br>            )<br><br>            docs_summary = [Document(page_content=row[&quot;summary&quot;].strip(), metadata=metadata_table) for _, row in df_table.iterrows()]<br><br>            tables_result.append(df_table)<br>            metadata_result.append(metadata_table)<br>            tables_summary.extend(docs_summary)<br><br>            metadata_table = {&quot;source&quot;: path, &quot;page&quot;: str(page), &quot;year&quot;: str(year), &quot;type&quot;: &quot;table&quot;}<br>            tables_summary.append(Document(page_content=df_table.to_markdown(), metadata=metadata_table))<br>            metadata_result.append(metadata_table)<br><br>            year -= 1 # auxiliary code (for demonstration purposes only)<br><br>    return tables_summary, metadata_result, tables_result</pre><ul><li><strong>normalize</strong> : This function takes takes a sentence and makes it easy to analyze by converting special characters to simpler forms, converting all text to lowercase, and then removing unnecessary words and symbols like punctuation, short words, and web addresses. It returns a cleaned-up version of the sentence that’s ready for further processing. Since the example will use text, we need to use a library that can understand language (in this case, English). Then, we’ll use spaCy to load an English NLP model (<a href="https://spacy.io/models/en">en_core_web_sm</a>).</li></ul><pre>import spacy<br>from spacy.cli import download<br><br>try:<br>    nlp = spacy.load(&#39;en_core_web_sm&#39;)<br>except OSError:<br>    print(&quot;Model not found. Downloading the model...&quot;)<br>    download(&#39;en_core_web_sm&#39;)<br>    nlp = spacy.load(&#39;en_core_web_sm&#39;)<br><br>def normalize(sentence):<br>    &quot;&quot;&quot;Normalize the list of sentences and return the same list of normalized sentences&quot;&quot;&quot;<br>        <br>    # Normalize Unicode characters to ASCII<br>    sentence = unicode_to_ascii(sentence)<br>    <br>    # Convert the sentence to lowercase and process it with spaCy<br>    sentence = nlp(sentence.replace(&#39;\n&#39;, &#39; &#39;).lower())<br>    <br>    # Lemmatize the words and filter out punctuation, short words, stopwords, mentions, and URLs<br>    sentence_normalized = &quot; &quot;.join([word.lemma_ for word in sentence if (not word.is_punct)<br>                                    and (len(word.text) &gt; 2) and (not word.is_stop) <br>                                    and (not word.text.startswith(&#39;@&#39;)) and (not word.text.startswith(&#39;http&#39;))])<br>    return sentence_normalized</pre><h4>Helper Functions for the Indexer</h4><p>Next, we’ll create functions for the indexing stage. We’ll use <a href="https://pypi.org/project/chromadb/">chromadb</a> as our embedding database because it’s open source . As mentioned before, we will be using both UCC and DCC scenarios.</p><h4>Unified Context Scenario</h4><p>In this scenario, we’re leveraging <a href="https://docs.trychroma.com/">ChromaDB</a> to create a unified collection of structured data, optimizing it for efficient semantic search, and embedding generation.</p><p>The key feature highlighted is <a href="https://docs.trychroma.com/guides#changing-the-distance-function"><em>metadata={“hnsw:space”: “cosine”}</em></a> with which we configure the distance function used in semantic search and the embedding provider employed. By default, ChromaDB uses the <a href="https://www.sbert.net/#sentencetransformers-documentation">Sentence Transformers</a> all-MiniLM-L6-v2 model to create embeddings.</p><pre>import chromadb<br>clientdb = chromadb.PersistentClient()<br><br>def unified_context_collection(text_chunks, tables_summary):<br>    # generate structure for unified vector scenario<br>    <br>    # generated chromadb collection<br>    unified_collection = clientdb.create_collection(name=&quot;unified_context_collection&quot;, metadata={&quot;hnsw:space&quot;: &quot;cosine&quot;})<br><br>    # Unified &#39;page_content&#39; and &#39;metadata&#39;<br>    unified_docs = [doc.page_content for doc in text_chunks + tables_summary]<br>    unified_meta = [doc.metadata for doc in text_chunks + tables_summary]<br><br>    # generate unique identifiers<br>    unified_ids = [str(uuid.uuid4()) for _ in unified_docs]<br><br>    # index data and generate default embeddings (vectors)<br>    unified_collection.add(documents=unified_docs, metadatas=unified_meta, ids=unified_ids)<br><br>    return unified_collection</pre><h4>Distributed Context Scenario</h4><p>In this distributed scenario configuration, we utilize ChromaDB to manage two distinct collections: distrctx_context_collection and distrtbl_table_collection. Each collection is optimized for semantic search and embedding generation using the cosine similarity distance function ({“hnsw:space”: “cosine”}). It processes <em>text_chunks</em> for contextual data and <em>tables_summary</em> for structured tabular data, assigning unique identifiers and indexing them for efficient data management.</p><pre>import chromadb<br>clientdb = chromadb.Client()<br><br>def distributed_context_collection(text_chunks, tables_summary):<br>    # generate structure for unified vector scenario<br>    <br>    # generated chromadb collection<br>    distrctx_collection = clientdb.create_collection(name=&quot;distrctx_context_collection&quot;, metadata={&quot;hnsw:space&quot;: &quot;cosine&quot;})<br>    distrtbl_collection = clientdb.create_collection(name=&quot;distrtbl_table_collection&quot;, metadata={&quot;hnsw:space&quot;: &quot;cosine&quot;})<br><br>    # Distributed context &#39;page_content&#39; and &#39;metadata&#39;<br>    distrctx_docs = [doc.page_content for doc in text_chunks]<br>    distrctx_meta = [doc.metadata for doc in text_chunks]<br><br>    # generate context unique identifiers<br>    distrctx_ids = [str(uuid.uuid4()) for _ in distrctx_docs]<br><br><br>    # Distributed Table &#39;page_content&#39; and &#39;metadata&#39;<br>    distrtbl_docs = [doc.page_content for doc in tables_summary]<br>    distrtbl_meta = [doc.metadata for doc in tables_summary]<br><br>    # generate table unique identifiers<br>    distrtbl_ids = [str(uuid.uuid4()) for _ in distrtbl_docs]<br><br>    # index data and generate default embeddings (vectors)<br>    distrctx_collection.add(documents=distrctx_docs, metadatas=distrctx_meta, ids=distrctx_ids)<br>    distrtbl_collection.add(documents=distrtbl_docs,metadatas=distrtbl_meta, ids=distrtbl_ids)<br><br>    return distrctx_collection, distrtbl_collection</pre><h4>The Helper Function for Reranking</h4><p>Now, we’ll look at the retrieval functions. For the reranker, we’ve chosen <a href="https://pypi.org/project/FlashRank/">FlashRank</a> for its ease of use and becauseit operates independently of Torch or Transformer frameworks, running efficiently on CPUs with a minimal memory footprint of approximately 4MB. FlashRank plays a pivotal role in our RAG system by reranking candidate chunks after a semantic search. Using SOTA cross-encoders and advanced models, FlashRank evaluates the relevance of retrieved chunks based on their similarity to the input query. The function will generate the metric “score” (max_score, min_score, avg_score, std_score) that offers insights into the quality and distribution of the ranked results, helping to assess and refine the performance of the ranking process.</p><p><strong>Note</strong>: The final segment of metrics was added for demonstrative purposes only, and final evaluation is not required in the proposed production setting.</p><pre>from flashrank import Ranker, RerankRequest<br>import pandas as pd<br>pd.set_option(&#39;display.max_colwidth&#39;, None)<br><br><br>def get_ranked_chunks(query: str, collectionContext, test_scenario=3, year_scope=2024, top_results=20, top_rank: int=5):<br>    <br>    conditions = {<br>        0: {&quot;year&quot;: str(year_scope)},<br>        1: {&quot;$and&quot;: [{&quot;type&quot;: &quot;row&quot;}, {&quot;year&quot;: str(year_scope)}]},<br>        2: {&quot;$and&quot;: [{&quot;type&quot;: &quot;table&quot;}, {&quot;year&quot;: str(year_scope)}]},<br>        3: {&quot;$and&quot;: [{&quot;year&quot;: str(year_scope)}, {&quot;$or&quot;: [{&quot;type&quot;: &quot;table&quot;}, {&quot;type&quot;: &quot;row&quot;}]}]},<br>    }<br><br>    results = collectionContext.query(<br>        query_texts=[query],<br>        n_results=top_results,<br>        where=conditions[test_scenario]<br>    )<br><br>    total_collection_results = len(results)<br>    <br>    passages = [<br>        {&quot;id&quot;: results[&quot;ids&quot;][0][i], &quot;text&quot;: results[&quot;documents&quot;][0][i], &quot;meta&quot;: results[&quot;metadatas&quot;][0][i]}<br>        for i in range(total_collection_results)<br>    ]<br><br>    ranker = Ranker()<br>    rerankrequest = RerankRequest(query=query, passages=passages)<br>    ranked_results = ranker.rerank(rerankrequest)<br>    total_rank_results = len(ranked_results)<br><br>    chunks = [item[&#39;text&#39;] for item in ranked_results[:top_rank]]<br>    total_chunks = len(chunks)<br><br>    df_ranked = pd.json_normalize(ranked_results).rename(columns=lambda x: x.replace(&#39;meta.&#39;, &#39;&#39;))<br>    df_ranked = df_ranked.sort_values(by=&#39;score&#39;, ascending=False)<br><br>    metrics = {<br>        &quot;max_score&quot;: &quot;{:+.6f}&quot;.format(df_ranked[&#39;score&#39;].max()),<br>        &quot;min_score&quot;: &quot;{:+.6f}&quot;.format(df_ranked[&#39;score&#39;].min()),<br>        &quot;avg_score&quot;: &quot;{:+.6f}&quot;.format(df_ranked[&#39;score&#39;].mean()),<br>        &quot;std_score&quot;: &quot;{:+.6f}&quot;.format(df_ranked[&#39;score&#39;].std()),<br>        &quot;types&quot;: &#39;, &#39;.join(df_ranked[&#39;type&#39;].unique()),<br>        &quot;search_count&quot;: str(total_collection_results),<br>        &quot;rerank_count&quot;: str(total_rank_results),<br>        &quot;chunks_count&quot;: str(total_chunks),<br>        &quot;chunks&quot;: &#39;, &#39;.join(df_ranked[&#39;text&#39;].str[:7])<br>    }<br><br>    new_row_dict = {&quot;Query&quot;: query, &quot;Collection&quot;: collectionContext.name, &quot;Scenario&quot;: test_scenario, **metrics}<br>    <br>    return chunks, new_row_dict, df_ranked</pre><h4>LLM API Client Configuration</h4><p>We’re almost done! We still need to configure the last step to inference using the LLM with the context we prepared in previous sections.</p><p>Depending on your specific LLM setup, you should adjust the configuration accordingly for your personal use.</p><pre>from openai import AzureOpenAI<br><br>client = AzureOpenAI(<br>    api_key = os.environ.get(&quot;OAI_API_Key&quot;), <br>    api_version = os.environ.get(&quot;OAI_API_Version&quot;), <br>    azure_endpoint = os.environ.get(&quot;OAI_API_Base&quot;))<br><br>MESSAGE_SYSTEM_CONTENT = &quot;&quot;&quot;You are a customer service agent that helps a customer with answering questions. <br>Please answer the question based on the provided context below. <br>Make sure not to make any changes to the context, if possible, when prepare answers so as to provide accurate responses. <br>If the answer  not be found in context, just politely say that you do not know, do not try to make up an answer.&quot;&quot;&quot;<br><br>def response_test(question:str, context, model:str = &quot;gpt-4&quot;):<br>    response = client.chat.completions.create(<br>        model=model,<br>        messages=[<br>            {<br>                &quot;role&quot;: &quot;system&quot;,<br>                &quot;content&quot;: MESSAGE_SYSTEM_CONTENT,<br>            },<br>            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: question},<br>            {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: &#39;\n &#39;.join(context)},<br>        ],<br>    )<br>    <br>    return response.choices[0].message.content<br><br></pre><h3>Perform the Test</h3><p>Now that we have all the functions in place, let’s perform the test following the steps below:</p><ul><li>1. <strong>Get the chunks: </strong>We selected a chunk size of 600. With this, we handle 34 pages and process 129 chunks (96 for chunk_size 800, 77 for 1000). Experimenting with these parameters is quite entertaining!</li></ul><pre>text_chunks, page_count = load_pdf(file_path, chunk_size=600, chunk_overlap=100)<br>print(f&quot;Total text chunks: {len(text_chunks)}&quot;)<br>print(f&quot;Total pages: {page_count}&quot;)</pre><ul><li>2. <strong>Get the tabular data</strong>:</li></ul><pre>tables_summary, metadata_result, tables_result = get_tables(file_path, page_count, 2024)<br>print(len(tables_result))</pre><p><strong>Note</strong>: The value “2024” is a temporary solution that facilitates adding functional value to our classification metadata.</p><ul><li><strong>3.</strong> <strong>Put the data into collections (UCC / UCD)</strong></li></ul><pre># Get the unified context data collection<br>unified_collection = unified_context_collection(text_chunks, tables_summary)<br><br># Get the distributed context data collections<br>distrctx_collection, distrtbl_collection = distributed_context_collection(text_chunks, tables_summary)</pre><p>Finally, here is the code snippet that allows us to control our tests. As you can see, our proposal encompasses: extraction and reranking of context information (get_ranked_chunks), prompt LLM (response_test), and finally, in ‘df_results’, we accumulate the metrics.</p><pre>import pandas as pd<br>pd.set_option(&#39;display.max_colwidth&#39;, None)<br><br>def query_controler(questions, unified_collection, distrtbl_collection):<br>    <br>    df_results = pd.DataFrame(columns=[<br>        &quot;Query&quot;, &quot;Collection&quot;, &quot;Scenario&quot;, &quot;Answer&quot;,  <br>        &quot;max_score&quot;, &quot;min_score&quot;, &quot;avg_score&quot;, &quot;std_score&quot;, <br>        &quot;types&quot;, &quot;search_count&quot;, &quot;rerank_count&quot;, &quot;chunks_count&quot;, &quot;chunks&quot;<br>    ])<br>    <br>    def process_query(query_source, collection, year_scp):<br>        chunks, new_row_dict, df_ranked = get_ranked_chunks(query=query_source, collectionContext=collection, year_scope=year_scp)<br>        response = response_test(query_source, chunks)<br>        new_row_dict[&quot;Answer&quot;] = response<br>        <br>        return new_row_dict<br>    <br>    for key in questions.keys():<br>        query_source = questions[key][&quot;query&quot;]<br>        year_scp = questions[key][&quot;year_scope&quot;]<br>        <br>        new_row_dict = process_query(query_source, unified_collection, year_scp)<br>        df_results.loc[len(df_results)] = new_row_dict<br>        <br>        new_row_dict = process_query(query_source, distrtbl_collection, year_scp)<br>        df_results.loc[len(df_results)] = new_row_dict<br>        <br>    return df_results</pre><p>We’ve now reached the most anticipated moment! Can we learn the answer to: “<strong>Who was the fourth millionaire of 2022?” </strong>using both scenarios (UCC and DCC)</p><pre>questions = {}<br>questions[&quot;question1&quot;] = {&quot;query&quot;: &quot;Who was the fourth millionaire of 2022?&quot;, &quot;year_scope&quot;: 2022}<br><br>df_results = query_controler(questions, unified_collection, distrtbl_collection)<br>df_results[[&quot;Collection&quot;, &quot;Answer&quot;, &quot;max_score&quot;, &quot;min_score&quot;, &quot;types&quot;, &quot;rerank_count&quot;, &quot;chunks_count&quot;,&quot;chunks&quot;]]</pre><figure><img alt="A summary of the findings is shown in the table." src="https://cdn-images-1.medium.com/max/1024/1*d_a6MQt5eHBZsQRukNI_mA.jpeg" /><figcaption>Image 5: Results table generated by the RAG application after processing a user query.</figcaption></figure><p>As you can see on Image 5, using both collections, “<strong>unified_context_collection</strong>” and “<strong>distrtbl_table_collection</strong>,” they both produced similar outputs answering our initial question. Since we configured metrics, this helps identify the quality of chunks retrieved. In both scenarios, we received scores ranging from <strong>+0.000086</strong> to <strong>+0.000017, </strong>which indicates that the retrieved chunks consistently align closely with the query’s requirements. This range of scores suggests that the information retrieved from both collections is consistently relevant and closely matches what we sought to find, providing reliable and credible data.</p><p>For this case, we selected collections that include rows and tables, with seven rerank counts and five final contextual chunks each. Overall, the analysis confirms Bill Gates’ ranking and net worth with high precision and consistency across both data collections.</p><p>We’d like to show you one additional example. In this scenario, we’ve removed the metrics to facilitate understanding the results. However, it’s necessary to emphasize question number 3: “Based on context, which Billionaries have Google as a source of their fortune in 2021?” which offers an interesting opportunity to see that this question was resolved using only row-based table chunks.</p><pre>questions = {}<br>questions[&quot;question2&quot;] = {&quot;query&quot;: &quot;What&#39;s the Elon Musk net worth by 2023 year?&quot;, &quot;year_scope&quot;: 2023}<br>questions[&quot;question3&quot;] = {&quot;query&quot;: &quot;Based on context, which millionaires in 2021 have Google as the source of their fortune?&quot;, &quot;year_scope&quot;: 2021}<br><br>df_results2 = query_controler(questions, unified_collection, distrtbl_collection)<br>df_results2[[&quot;Query&quot;, &quot;Answer&quot;, &quot;types&quot;, ]]</pre><figure><img alt="A summary of the findings is shown in the table." src="https://cdn-images-1.medium.com/max/1024/1*MJmvXd_Fa9entdhfmqsAUA.jpeg" /><figcaption>Image 6: Results table generated by the RAG application after processing a user query.</figcaption></figure><h3>What Did We Learn?</h3><p>In this tutorial for ingesting small tabular data while working with LLMs, we explored various aspects crucial for understanding and implementing RAG frameworks. From data extraction and normalization to semantic search and reranking, each step plays a pivotal role in achieving accurate and contextually rich responses in natural language processing tasks.</p><p>We also highlighted the challenges and opportunities of integrating tabular data into RAG frameworks. Notably, we emphasized the importance of chunking strategies and the impact of irrelevant context on model performance, drawing attention to relevant resources for deeper exploration.</p><p>This study not only enhances our understanding of RAG frameworks but also provides practical insights for implementing them effectively in real-world scenarios. As the field of natural language processing continues to evolve, such investigations pave the way for more sophisticated and contextually aware language models.</p><p>If you would like to learn more, here are some suggestions for further reading:</p><ul><li><a href="https://github.com/chroma-core/chroma/blob/main/chromadb/experimental/density_relevance.ipynb">Density-based retrieval relevance</a>. This is vital in retrieval-augmented generation, where irrelevant context can confuse generative models and degrade performance. Unlike relational databases, vector search systems return the nearest neighbors regardless of relevance.</li><li><a href="https://arxiv.org/abs/2302.00093">Large Language Models Can Be Easily Distracted by Irrelevant Context</a>. The authors show that irrelevant information significantly reduces model performance. They also identify mitigation strategies, including self-consistent decoding and prompt instructions for ignoring irrelevant data.</li><li><a href="https://opea.dev">Open Platform for Enterprise AI (OPEA)</a>: When deploying a RAG system, we will face multiple challenges such as ensuring scalability, handling data security, and integrating with existing infrastructure. This open-source project helps with the deployment by providing robust tools and frameworks designed to streamline these processes and facilitate seamless integration.</li></ul><h3>About the Authors</h3><p><strong>Eduardo Rojas Oviedo, Platform Engineer, Intel</strong></p><p><a href="https://www.linkedin.com/in/eduardo-rojas-oviedo-56306923?originalSubdomain=cr">Eduardo Rojas Oviedo</a> is a dedicated RAG developer within Intel’s dynamic and innovative team. Specialized in cutting-edge developer tools for AI, Machine Learning, and NLP, he is passionate about leveraging technology to create impactful solutions. Eduardo’s expertise lies in building robust and innovative applications that push the boundaries of what’s possible in the realm of artificial intelligence. His commitment to sharing knowledge and advancing technology drives his ongoing pursuit of excellence in the field.</p><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist on Intel’s <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/team.html">Open Ecosystem</a> team, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on X at <a href="https://twitter.com/eze_lanza">@eze_lanza</a> and LinkedIn at <a href="https://www.linkedin.com/in/ezelanza/">/eze_lanz</a>a</p><h3>Follow us!</h3><p><a href="https://medium.com/intel-tech">Medium</a>, <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/podcast.html">Podcast</a>, <a href="http://open.intel.com">Open.intel</a> , <a href="https://twitter.com/OpenAtIntel">X</a> , <a href="https://www.linkedin.com/search/results/all/?fetchDeterministicClustersOnly=true&amp;heroEntityKey=urn%3Ali%3Aorganization%3A100949244&amp;keywords=open.intel&amp;origin=RICH_QUERY_TYPEAHEAD_HISTORY&amp;position=0&amp;searchId=48e19f47-ebfe-49e5-acc2-9b9424bd004c&amp;sid=226&amp;spellCorrectionEnabled=true">Linkedin</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bebcf52cdde3" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/improve-your-tabular-data-ingestion-for-rag-with-reranking-bebcf52cdde3">Improve your Tabular Data Ingestion for RAG with Reranking</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Containerize Your Local LLM]]></title>
            <link>https://medium.com/intel-tech/how-to-containerize-your-local-llm-436182cd179a?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/436182cd179a</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[cloud-native]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Tue, 25 Jun 2024 15:32:37 GMT</pubDate>
            <atom:updated>2024-06-25T16:24:19.466Z</atom:updated>
            <content:encoded><![CDATA[<p>Learn how to create a container to expose your local large language model (LLM) for consumption by an API.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*s1X0TTKJdNnkt25F" /><figcaption>Photo by <a href="https://unsplash.com/@jukeboxprint?utm_source=medium&amp;utm_medium=referral">Jukebox Print</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>Presented by Ezequiel Lanza — Open Source AI Evangelist (Intel)</h4><p>Let’s say you’re building a chatbot for your company and you need to find the best way to deploy it. You might start by building your logic in a Jupyter Notebook, but this won’t work when you want to deploy it in a live environment where users can interact with it. You might then start thinking about a suitable strategy for making your application scalable, portable, and efficient. Cloud native development emerges as the best alternative, allowing you to focus on topics like scaling and dimensioning while treating each piece as an isolated module.</p><p>So, what happens next? You need to create the core components of your application, starting with the large language model (LLM). You might want an LLM container that receives a string input and returns the model’s response (Image 1). That sounds easy, right?</p><figure><img alt="A prompt asking about what is Kubernetes and an LLM response" src="https://cdn-images-1.medium.com/max/650/1*I2lOfrkJ5n6vyVxY2560Eg.png" /><figcaption>Image 1 : The user asks the large language model ‘What is Kubernetes?’ The model answers that Kubernetes is an Open Source platform for automating the deployment, scaling, and management of containerized applications. It highlights Kubernetes’ role in managing clusters of containers effectively</figcaption></figure><p>Unfortunately, this can be challenging for any application composed of more than just a language model. For instance, you may have a React front end that doesn’t understand the intricacies of LLMs, such as LLM pipelines, AI frameworks, or optimizations needed to make your model more efficient. To address this, you need to abstract the LLM logic and provide an API for the application to interact with by containerizing your model. This approach offers several benefits characteristic of microservices architecture, such as scalability, isolation, portability, efficiency, and continuous deployment.</p><p>In this post, we’ll walk you through the step-by-step process for configuring and creating an LLM container for further use by an application. This post builds off the project we shared in our article <a href="https://medium.com/intel-tech/easily-deploy-multiple-llms-in-a-cloud-native-environment-c7b47511ac15">Easily Deploy Multiple LLMs in a Cloud Native Environment.</a> You may want to read that one first before proceeding.</p><p>You’ll find the code you need for this project here: <a href="https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main">https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main</a></p><h3>Why a Container?</h3><p>When containerizing LLM logic, you can use either a local or external model. It’s worth mentioning that if you choose to use a local model, you don’t have to include it on the container image. This avoids having to package large model files (~26GB for a 7B model) with the container, which can significantly increase the container size and slow down deployment times. Additionally, it eliminates the need to rebuild the container image whenever the model is updated or retrained. Instead, you can store the model on a local server or file system that the containerized application can access when the container launches, allowing for more efficient management and deployment.</p><p>Since this example is based on a project, you’ll first need to clone the repo with the code.</p><pre>git clone https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main</pre><p>As mentioned in the <a href="https://medium.com/p/c7b47511ac15">previous post</a> , the repo explains how to create all the containers you need to build your own multi-chatbot, such as the front end, the proxy, or the LLM model. In this case, we’ll focus on the container for the LLaMa2 non-optimized model.</p><h3>What Will Be Inside The Container?</h3><p>This example assumes you want to use a local non-optimized model, which means you didn’t optimize your model using a tool like <a href="https://github.com/intel/intel-extension-for-transformers">Intel® Extension for Transformers</a> (ITREX) to make it use fewer resources. In short, you want to use a vanilla version of LLaMa2, which you can download <a href="https://huggingface.co/meta-llama/Llama-2-7b-chat-hf">HERE</a>. Save the model in an externally accessible place (ideally a file server) to be used by the container when it’s launched.</p><p>You might be wondering, why aren’t we storing the model on the container image? Let’s think about it. Models are heavy. Consider, for example, the LLaMA2 model family. The LLaMA2 13B model has 13 billion parameters and requires around 40 gigabytes of storage space. Loading such a large model directly on the container image would significantly increase the container’s size, making it more cumbersome to deploy and manage. By keeping the model on an external file server, you can keep the container lightweight and more agile, facilitating easy updates and scalability. This approach also allows for better resource management, as the model can be shared across multiple containers or instances, reducing redundancy and optimizing storage usage.</p><p>The repo is organized by folders and each folder corresponds to a different image in the chatbot. Go to the folder where the local models scripts are, in this case : “LLAMA-non</p><pre>cd 3__Local_Models/LLAMA-non</pre><p>Within that folder, you’ll find all the files needed to build the LLM container. The following is a tree that Docker will use to create the container.</p><p>LLAMA-non<br>├── Dockerfile<br>├── app<br>│ ├── __init__.py<br>│ ├── llama2.py<br>│ └── server.py<br>└── requirements.txt</p><p>Let’s explore each part before building the container.</p><h3>Application Folder (/app)</h3><p>Any application that will live in a container has to be declared. Within the application folder (/app), we can typically find the scripts that will make the application run. In our case, we’ll have two scripts (llama.py and server.py), one to declarethe LLM pipeline object and the other to expose the API. These scripts will be run when the container is launched. Let’s explore each of them.</p><p><em>Llama2.py (/app/llama.py)</em></p><p>Here is where the pipeline will live. A pipeline abstracts the complexities of the underlying models and provides an easy-to-use interface for common tasks such as text classification, question answering, or text generation.</p><p>The code below sets up a Hugging Face text generation pipeline using a locally stored LLaMa2 model and its tokenizer (downloaded offline <a href="https://huggingface.co/meta-llama/Llama-2-7b-chat-hf">HERE</a>). Having models locally hosted allows further customization; when configuring the pipeline, you could set specific parameters to control the text generation behavior, like limiting the max number of tokens generated by <strong>max_new_tokens</strong>, penalizing the repetition of words in the generation with <strong>repetition_penalty</strong> or defining how “creative” the model will be with <strong>temperature</strong>.</p><p>Since we’ll be using LangChain further to integrate the pipeline, we should put the pipeline in the provided LangChain <strong>HuggingFacePipeline</strong> object, making it ready to be served via LangChain’s API.</p><pre>from transformers import pipeline,LlamaForCausalLM, LlamaTokenizer<br>from langchain_community.llms.huggingface_pipeline import HuggingFacePipeline<br></pre><pre># Local or File server address where the local model is stored<br>model_path=&quot;/fs_mounted/Models/llama-2-7b-chat-hf&quot; <br>local_model =  LlamaForCausalLM.from_pretrained(model_path, return_dict=True, trust_remote_code=True)<br>local_tokenizer = LlamaTokenizer.from_pretrained(model_path)</pre><pre># Hugging face pipeline<br>pipe= pipeline(task=&quot;text-generation&quot;, model=local_model, tokenizer=local_tokenizer, <br>                         trust_remote_code=True, max_new_tokens=100, <br>                         repetition_penalty=1.1, model_kwargs={&quot;max_length&quot;: 1200, &quot;temperature&quot;: 0.01})  </pre><pre>#Pipeline to be consumed by Langserve API<br>llm_pipeline = HuggingFacePipeline(pipeline=pipe)</pre><p><em>server.py (/app/server.py)</em></p><p>As we mentioned, to interact with the model in our cloud native environment, we expose the LLM via an API call. External applications or front ends can simply send an API POST message and receive a response, without needing to understand the pipeline or LLM parameters.</p><p>This is where our FastAPI application comes in. By creating an instance of FastAPI, we set up a web service that can handle these API requests. We configure <a href="https://en.wikipedia.org/wiki/Cross-origin_resource_sharing">cross-origin resource sharing</a> (CORS) middleware to allow requests from any origin, which is useful during the development stage. This ensures that our front end, regardless of its origin, can communicate with the backend. Note that you should adapt this configuration when it’s used in your production scenario.</p><p>But this is an LLM server, so we define a prompt template to guide the LLM in providing clear, concise, and accurate answers. The template instructs the model to respond within a specified character limit and to admit when it doesn’t know the answer, ensuring reliability and consistency in responses.</p><p>We’ll also need to set up a root endpoint to redirect users to the API documentation page, making it easy for developers to understand and use the API. We then add specific routes to handle requests involving the LLM pipeline. These routes use the predefined prompt template and the LLM pipeline to process inputs and generate responses.</p><p>When the script is run directly (when the container launches), the application starts using <a href="https://www.uvicorn.org/">Uvicorn</a>, a fast ASGI server, and listens on localhost at port 5005. This setup allows us to expose the LLM through a simple API call, making it accessible to external applications without requiring them to handle the complexities of the LLM’s internal workings.</p><pre>from fastapi import FastAPI<br>from langchain.prompts import PromptTemplate <br>from fastapi.responses import RedirectResponse<br>from fastapi.middleware.cors import CORSMiddleware<br>from langserve import add_routes<br>from app.llama2 import llm_pipeline</pre><pre># Initializes a FastAPI app instance<br>app = FastAPI()</pre><pre># Set up CORS middleware to allow requests from any origin<br>app.add_middleware(<br>    CORSMiddleware,<br>    allow_origins=[&quot;*&quot;],  # Set this to the specific origin of your frontend in production<br>    allow_credentials=False,<br>    allow_methods=[&quot;*&quot;],<br>    allow_headers=[&quot;*&quot;],<br>)</pre><pre>#Defines a template for prompts that will be sent to the LLM. The template ensures that the assistant gives clear, concise, and accurate answers.</pre><pre>template = &quot;&quot;&quot;You are a very smart and educated assistant to guide the user to understand the concepts. Please Explain the answer<br>If you don&#39;t know the answer, just say that you don&#39;t know, don&#39;t try to make up an answer.</pre><pre>Question: {question}</pre><pre>Only return the helpful answer below and nothing else. Give an answer in 1000 characteres at maximum please<br>Helpful answer:<br>&quot;&quot;&quot;</pre><pre>prompt = PromptTemplate.from_template(template) <br></pre><pre>@app.get(&quot;/&quot;)<br>async def redirect_root_to_docs():<br>    return RedirectResponse(&quot;/docs&quot;)</pre><pre>add_routes(app, <br>           prompt|llm_pipeline,<br>           path=&#39;/chain_llama_non&#39;)</pre><pre>if __name__ == &quot;__main__&quot;:<br>    import uvicorn</pre><pre>    uvicorn.run(app, host=&quot;localhost&quot;, port=5005)</pre><h3>Create and Upload Your Container to a Registry</h3><p>Now that we’ve defined the files we need, the next step is to create the container image with those files including all the dependencies to allow easy replication. Finally, we’ll upload the image to our preferred registry so any environment (Kubernetes cluster) can have access to it.</p><p><em>Create the Container image</em></p><p>The following is the file the Docker engine needs to create the container; we can think of a <a href="https://docs.docker.com/reference/dockerfile/">Dockerfile</a> in two parts: image creation and execution.</p><p>For image creation, we need to define the steps the engine has to follow to create the image. In this stage, we instruct the engine to use a determined image, which in our case will be Python. We will use the Python 3.11-slim version because, as mentioned, the idea of the container is to use minimal software. For example, we don’t need to install, say, the entire Ubuntu image (which comes with several software components that won’t be used and will make the container heavier for no reason). In addition, all the required files that will be used for the application need to be declared. The COPY command transfers the requirements.txt file and the application directory (./app) into the Docker container. requirements.txt lists Python dependencies, while the application directory contains the source code. This ensures the application is packaged with its dependencies and ready for execution inside the container.</p><pre>FROM python:3.11-slim</pre><pre># Set the working directory in the container<br>WORKDIR /usr/</pre><pre>#Copy requirements file to install python dependencies<br>COPY requirements.txt ./</pre><pre># Upgrade pip and install dependencies<br>RUN pip install --upgrade pip &amp;&amp; \<br>    pip install -r requirements.txt &amp;&amp;\<br>    pip install torch torchvision torchaudio --index-url <a href="https://download.pytorch.org/whl/cpu">https://download.pytorch.org/whl/cpu</a></pre><pre># Copy app folder where the application is inside the container<br>COPY ./app ./app</pre><pre>#Expose port 5005, the application will expose the API on that port<br>EXPOSE 5005</pre><p>After the image is created, we need to declare what the container will do when launched. This is declared using CMD, which stands for commands that will be executed in the command line. In our example, we will be launching the <a href="https://www.uvicorn.org/">Uvicorn</a> server in port 5005.</p><pre>#Command the container image will run when is lauched.<br>CMD exec uvicorn app.server:app --host 0.0.0.0 --port 5005</pre><p>Let’s build our container! In this case, we’ll build a container to run on Intel X86 architecture to take advantage of further optimizations like <a href="https://intel.github.io/intel-extension-for-pytorch/#introduction">Intel® Extension for PyTorch</a>. Those optimizations take advantage of Intel® Advanced Vector Extensions 512 (Intel® AVX-512) Vector Neural Network Instructions (VNNI) and Intel® Advanced Matrix Extensions (Intel® AMX) on Intel CPUs as well as Intel XeMatrix Extensions (XMX) AI engines on Intel discrete GPUs.</p><p><strong>NOTE: BE SURE TO HAVE YOUR DOCKER ENGINE INSTALLED</strong></p><p>Refer to <a href="https://www.docker.com/">https://www.docker.com</a></p><pre>docker build --platform linux/amd64 -t llama7b-non-optimized:latest .</pre><p><em>Upload the Container</em></p><p>We should now see our container. Next, we’ll upload it to a registry. This will be relevant when building your Kubernetes environment.</p><pre>docker login</pre><pre>docker tag llama7b-non-optimized:latest &lt;username&gt;/llama7b-non-optimized:latest</pre><pre>docker push &lt;username&gt;/llama7b-non-optimized:latest</pre><p>Your container is now in your Docker Hub and ready for use when you deploy your Kubernetes cluster.</p><h3>Call to Action</h3><p>Containerizing your model can transform how you deploy and scale your applications. Clone the repository and follow the guide to build your own LLaMa container and make it accessible via an API: <a href="http://multi-llms-chatbot-cloudnative-langchain/">Multi-llms-Chatbot-CloudNative-LangChain</a> <a href="https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main">GitHub repo</a></p><p>This step-by-step process will help make your model scalable, portable, and ready for production.</p><p>For more articles on LLM topics, be sure to also checK out:</p><p>· <a href="https://medium.com/intel-tech/tabular-data-rag-llms-improve-results-through-data-table-prompting-bcb42678914b"><strong>Tabular Data, RAG, &amp; LLMs: Improve Results Through Data Table Prompting</strong></a></p><p>· <a href="https://medium.com/intel-tech/easily-deploy-multiple-llms-in-a-cloud-native-environment-c7b47511ac15"><strong>Easily Deploy Multiple LLMs in a Cloud Native Environment</strong></a></p><p>· <a href="https://medium.com/intel-tech/four-data-cleaning-techniques-to-improve-large-language-model-llm-performance-77bee9003625"><strong>Four Data Cleaning Techniques to Improve Large Language Model (LLM) Performance</strong></a></p><p>· <a href="https://medium.com/intel-tech/optimize-vector-databases-enhance-rag-driven-generative-ai-90c10416cb9c"><strong>Optimize Vector Databases, Enhance RAG-Driven Generative AI</strong></a></p><h3>About the Author</h3><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist on Intel’s <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/team.html">Open Ecosystem</a> team, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on X at <a href="https://twitter.com/eze_lanza">@eze_lanza</a> and LinkedIn at <a href="https://www.linkedin.com/in/ezelanza/">/eze_lanz</a>a</p><h3>Follow us!</h3><p><a href="https://medium.com/intel-tech">Medium</a>, <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/podcast.html">Podcast</a>, <a href="http://open.intel.com/">Open.intel</a> , <a href="https://twitter.com/OpenAtIntel">X</a> , <a href="https://www.linkedin.com/search/results/all/?fetchDeterministicClustersOnly=true&amp;heroEntityKey=urn%3Ali%3Aorganization%3A100949244&amp;keywords=open.intel&amp;origin=RICH_QUERY_TYPEAHEAD_HISTORY&amp;position=0&amp;searchId=48e19f47-ebfe-49e5-acc2-9b9424bd004c&amp;sid=226&amp;spellCorrectionEnabled=true">Linkedin</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=436182cd179a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/how-to-containerize-your-local-llm-436182cd179a">How to Containerize Your Local LLM</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tabular Data, RAG, & LLMs: Improve Results Through Data Table Prompting]]></title>
            <link>https://medium.com/intel-tech/tabular-data-rag-llms-improve-results-through-data-table-prompting-bcb42678914b?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/bcb42678914b</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[retrieval-augmented-gen]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Tue, 14 May 2024 14:02:10 GMT</pubDate>
            <atom:updated>2024-07-31T20:17:59.980Z</atom:updated>
            <content:encoded><![CDATA[<p>How to ingest small tabular data when working with LLMs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*yDadHu2xzZhgepKT" /><figcaption>Photo by <a href="https://unsplash.com/@kommumikation?utm_source=medium&amp;utm_medium=referral">Mika Baumeister</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>By Eduardo Rojas Oviedo with Ezequiel Lanza</h4><p>Say you’re a financial analyst working for an investment firm. Your job involves staying ahead of market trends and identifying potential investment opportunities for your clients, who are often curious about the world’s richest people and their sources of wealth. You might consider using a retrieval augmented generation (RAG) system to easily and quickly identify market trends, investment opportunities, and economic risks as well as answer questions like, “<strong><em>Which industry has the highest number of billionaires?</em></strong>” or “<strong><em>How does the gender distribution of billionaires compare across different regions?</em></strong>”</p><p>Your first step is to go to the <a href="https://en.wikipedia.org/wiki/The_World%27s_Billionaires">source</a> to get that information. However, as you do an initial inspection, you discover a possible obstacle. The document contains not only text but also TABLES!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/468/1*05qN8rw-usCruNDxPDQ1wQ.png" /><figcaption>Image 1 : A table which includes a ranking of the world billionaires for 2023. It was extracted from Wikipedia (<a href="https://en.wikipedia.org/wiki/The_World%27s_Billionaires">https://en.wikipedia.org/wiki/The_World%27s_Billionaires</a>)</figcaption></figure><p>In this post, we’ll demonstrate how to use a large language model (LLM) model to consume tabular data in different formats.</p><h3><strong>LLMs and the Challenge of Structured Data</strong></h3><p>For us humans, it’s straightforward to connect the dots between the text and the table. But for LLMs, it’s like trying to solve a puzzle without all the pieces.</p><p>LLMs are designed to process text in a sequential manner, meaning they read and understand information one word or sentence at a time. This sequential processing is how they’ve been trained on vast amounts of text data. However, tables present information in a different format. They organize data into rows and columns, which creates a multidimensional structure.</p><p>Understanding this structure requires a different approach for LLMs than sequential text. LLMs need to recognize patterns across rows and columns, understand relationships between different data points, and interpret the meaning of headers and cell values. Because LLMs are primarily trained on sequential text, they might struggle to interpret and process tabular data. It’s like asking someone who’s used to reading novels to suddenly understand and interpret graphs or charts; they might find it challenging because it’s outside their usual mode of processing information.</p><h3>Common Examples of Tabular Data</h3><p>We can find tabular data more commonly than we might think. Below are three scenarios ranging from small, medium, to large:</p><ol><li><strong>Small</strong>: Every day, users of our RAG platform will need to support their queries with documents that contain embedded small tabular data, as the example shown in Image 1. where the document contains mostly text and some tables. An industry report on global sales is another good example of small tabular data as it features textual analysis alongside tables detailing sales figures, which are often broken down by region and manufacturer. We can’t remove that tabular data to make it easy for our model to consume because it provides critical context.</li><li><strong>Medium</strong>: For some situations, you may need to analyze a greater amount of tabular data, such as spreadsheets, CSV files (comma-separated values), or data preformatted by our preferred reporting tools, such as Power BI. Say, for example, you’re working on a marketing team at a retail company, which is analyzing sales data from the past quarter to identify trends and opportunities for growth. This data would likely come in the form of multiple database files containing information on product sales, customer demographics, and regional sales performance.</li><li><strong>Large</strong>: A third scenario in which our users might wish to perform data analysis involves transaction databases and multidimensional datasets, such as <a href="https://en.wikipedia.org/wiki/OLAP_cube">OLAP cubes,</a> because they offer advantages over spreadsheets for analyzing large amounts of complex data. OLAP provides fast query performance, support for complex calculations, and the ability to slice and dice data across multiple dimensions. OLAP cubes are better suited for handling the complexity and volume of data typically found in transaction databases or other large datasets, which require an understanding of the data information domain. For example, if you’re a retail chain analyzing data to enhance sales performance, you’ll need SQL for querying and OLAP cubes for analysis to garner insights from sales trends and customer segmentation to inform inventory management and marketing strategies. These tables can be large to analyze and understand.</li></ol><p>In this article, we’ll explore the scenario where tabular data is embedded within textual information. We’ll work with small tables containing a dozen rows and a few columns, all within the model’s <a href="https://platform.openai.com/docs/models/">context window capacity.</a></p><h3>Let’s See It in Action</h3><p>Our example will be based on Figure 1, which shows the world billionaires list, to feed into our future RAG framework and answer questions about that billionaire list. With just four steps, we can demonstrate the use of tabular data as context for our users’ questions and even verify whether it’s possible to make the model incur errors.</p><h4>Prepare our environment libraries</h4><pre>import os <br>import sys<br>import pandas as pd<br>from typing import List</pre><p>To facilitate the tabular display of the results, we will use the “BeautifulTable” library, which provides a visually attractive format in a terminal (<a href="https://beautifultable.readthedocs.io/">beautifultable’s documentation</a>).</p><pre>!pip install beautifultable<br>from beautifultable import BeautifulTable</pre><p>Since we will only be working with tables, let’s explore the capabilities of camelot-py, another useful python library. We can follow the camelot installation instructions at <a href="https://camelot-py.readthedocs.io/en/master/user/install-deps.html">Installation of dependencies</a> and Ghostscript dependency from <a href="https://ghostscript.com/releases/gsdnld.html">Ghostscript Downloads.</a></p><pre>!pip install opencv-python<br>!pip install camelot-py<br>!pip install ghostscript<br><br>import camelot</pre><h4>Prepare our data</h4><p>We will save the list (the world’s billionaires) as a PDF to facilitate our exploration.</p><pre>file_path=&quot;./World_Billionaires_Wikipedia.pdf&quot;</pre><p>The code below will allow us, in a basic way, to clean the data once it has been extracted as a <a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html">Pandas dataframe</a>. This routine processes page by page from a preselected list and will allow us to have access to a structure that is easy to manipulate.</p><p><em>Note: The below code was adapted by the author from </em><a href="https://docs.llamaindex.ai/en/stable/examples/query_engine/pdf_tables/recursive_retriever.html#load-in-document-and-tables"><em>Recursive Retriever + Query Engine Demo</em></a></p><pre># use camelot to parse tables   <br>def get_tables(path: str, pages: List[int]):    <br>    for page in pages:<br>        table_list = camelot.read_pdf(path, pages=str(page))<br>        if table_list.n&gt;0:<br>            for tab in range(table_list.n):<br>                <br>                # Conversion of the the tables into the dataframes.<br>                table_df = table_list[tab].df <br>                <br>                table_df = (<br>                    table_df.rename(columns=table_df.iloc[0])<br>                    .drop(table_df.index[0])<br>                    .reset_index(drop=True)<br>                )        <br>                     <br>                table_df = table_df.apply(lambda x: x.str.replace(&#39;\n&#39;,&#39;&#39;))<br>                <br>                # Change column names to be valid as XML tags<br>                table_df.columns = [col.replace(&#39;\n&#39;, &#39; &#39;).replace(&#39; &#39;, &#39;&#39;) for col in table_df.columns]<br>                table_df.columns = [col.replace(&#39;(&#39;, &#39;&#39;).replace(&#39;)&#39;, &#39;&#39;) for col in table_df.columns]<br>    <br>    return table_df<br># extract data table from page number<br>df = get_tables(file_path, pages=[3])</pre><p>Now, let’s convert our tabular data from data frame into multiple formats, such as: Json, CSV or Markdown, among others.</p><pre># prepare test set<br>eval_df = pd.DataFrame(columns=[&quot;Data Format&quot;, &quot;Data raw&quot;]) # , &quot;Question&quot;, &quot;Answer&quot;<br><br># Save the data in JSON format<br>data_json = df.to_json(orient=&#39;records&#39;)<br>eval_df.loc[len(eval_df)] = [&quot;JSON&quot;, data_json]<br><br># Save the data as a list of dictionaries<br>data_list_dict = df.to_dict(orient=&#39;records&#39;)<br>eval_df.loc[len(eval_df)] = [&quot;DICT&quot;, data_list_dict]<br><br># Save the data in CSV format<br>csv_data = df.to_csv(index=False)<br>eval_df.loc[len(eval_df)] = [&quot;CSV&quot;, csv_data]<br><br># Save the data in tab-separated format<br>tsv_data = df.to_csv(index=False, sep=&#39;\t&#39;)<br>eval_df.loc[len(eval_df)] = [&quot;TSV (tab-separated)&quot;, tsv_data]<br><br># Save the data in HTML format<br>html_data = df.to_html(index=False)<br>eval_df.loc[len(eval_df)] = [&quot;HTML&quot;, html_data]<br><br># Save the data in LaTeX format<br>latex_data = df.to_latex(index=False)<br>eval_df.loc[len(eval_df)] = [&quot;LaTeX&quot;, latex_data]<br><br># Save the data in Markdown format<br>markdown_data = df.to_markdown(index=False)<br>eval_df.loc[len(eval_df)] = [&quot;Markdown&quot;, markdown_data]<br><br># Save the data as a string<br>string_data = df.to_string(index=False)<br>eval_df.loc[len(eval_df)] = [&quot;STRING&quot;, string_data]<br><br># Save the data as a NumPy array<br>numpy_data = df.to_numpy()<br>eval_df.loc[len(eval_df)] = [&quot;NumPy&quot;, numpy_data]<br><br># Save the data in XML format<br>xml_data = df.to_xml(index=False)<br>eval_df.loc[len(eval_df)] = [&quot;XML&quot;, xml_data]</pre><p>It’s time to explore our test data. We have configured a dataset where each row represents an output format from dataframe and the data in “Data raw” corresponds to the tabular data that we will use with the generative model.</p><pre>from pandas import option_context<br>with option_context(&#39;display.max_colwidth&#39;, 150):<br>    display(eval_df.head(10))</pre><p>Output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MO9844g7xly-gscKZggzpA.jpeg" /><figcaption>Image 2: A code output that showcases the raw data for each text format</figcaption></figure><h4>Set our model for validation</h4><p>Let’s prepare a basic prompt that allows us to interact with the context data.</p><pre>MESSAGE_SYSTEM_CONTENT = &quot;&quot;&quot;You are a customer service agent that helps a customer with answering questions. <br>Please answer the question based on the provided context below. <br>Make sure not to make any changes to the context, if possible, when preparing answers to provide accurate responses. <br>If the answer cannot be found in context, just politely say that you do not know, do not try to make up an answer.&quot;&quot;&quot;</pre><p>Before carrying out our tests with the tabular dataset, we will need to prepare our model’s connection settings (in this example, we’ll use AzureOpenAI). You’ll need to provide your credentials.</p><pre>from openai import AzureOpenAI<br><br>client = AzureOpenAI(<br>    api_key=OAI_API_Key, <br>    api_version=OAI_API_Version, <br>    azure_endpoint=OAI_API_Base)<br><br>def response_test(question:str, context:str, model:str = &quot;gpt-4&quot;):<br>    response = client.chat.completions.create(<br>        model=model,<br>        messages=[<br>            {<br>                &quot;role&quot;: &quot;system&quot;,<br>                &quot;content&quot;: MESSAGE_SYSTEM_CONTENT,<br>            },<br>            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: question},<br>            {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: context},<br>        ],<br>    )<br>    <br>    return response.choices[0].message.content</pre><p>Since we are working with a dataset, where each row represents an individual unit of context information, we have implemented the following iteration routine, allowing us to process one row after the other and store the model interpretation for each one.</p><pre>def run_question_test(query: str, eval_df:str):<br><br>    questions = []<br>    answers = []<br><br>    for index, row in eval_df.iterrows():<br>        questions.append(query)<br>        response = response_test(query, str(row[&#39;Data raw&#39;]))<br>        answers.append(response)<br>        <br>    eval_df[&#39;Question&#39;] = questions<br>    eval_df[&#39;Answer&#39;] = answers<br>    <br>    return eval_df<br><br>def BeautifulTableformat(query:str, results:pd.DataFrame, MaxWidth:int = 250):<br>    table = BeautifulTable(maxwidth=MaxWidth, default_alignment=BeautifulTable.ALIGN_LEFT)<br>    table.columns.header = [&quot;Data Format&quot;, &quot;Query&quot;, &quot;Answer&quot;]<br>    for index, row in results.iterrows():<br>        table.rows.append([row[&#39;Data Format&#39;], query, row[&#39;Answer&#39;]])<br>    <br>    return table</pre><h4>Let’s have FUN!</h4><p>Now, we’ll connect the dots, processing the dataset, from which we will obtain an answer using each of the tabular data formats as context information, and then we will display the results in a tabular manner.</p><pre>query = &quot;What&#39;s the Elon Musk&#39;s net worth?&quot;<br>result_df1 = run_question_test(query, eval_df.copy())<br>table = BeautifulTableformat(query, result_df1, 150)<br>print(table)</pre><p>Output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9aJqih5kn7za9Zu22t2erg.jpeg" /><figcaption>Image 3 depicts a table outlining the responses provided by a model for diverse input text formats to the inquiry, “What is Elon Musk’s net worth?”</figcaption></figure><p>We can see how the question, “<strong>What’s Elon Musk&#39;s net worth?</strong>” was consistently answered for each tabular data format obtained during the Panda data frame conversion. As we know, because it consists of a semantic elaboration, the variations between responses are a challenge that we must consider during the generation of final validation metrics</p><p>We could also obtain more concise or elaborate responses if we modify our “MESSAGE_SYSTEM_CONTENT” variable.</p><p>Let’s repeat the exercise once again, this time with a question that requires more analytical reasoning for our model.</p><pre>query = &quot;What&#39;s the sixth richest billionaire in 2023 net worth?&quot;<br>result_df2 = run_question_test(query, eval_df.copy())<br>table = BeautifulTableformat(query, result_df2, 150)<br>print(table)</pre><p>Output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zJJvFr98PbuF7ohNRLy52g.jpeg" /><figcaption>Image 4 displays a table presenting the responses provided by a model to various formats of input text for the question, “What is the net worth of the sixth wealthiest billionaire in 2023?</figcaption></figure><pre>As with the previous example, we have used each of the Pandas data frame export formats as a query’s information context.context for the quer</pre><p>As with the previous example, we have used each of the Pandas dataframe export formats as context for the query.</p><p>In this example, the question &quot;What&#39;s the sixth richest billionaire in 2023 net worth?&quot; proves that the model can respond to something more abstract, such as &quot;the sixth richest billionaire,&quot; which involves greater analytical reasoning and tabular data calculation. Both challenges were resolved with excellent consistency</p><h4>Relevancy and Distraction</h4><p>Let’s play around with the model and check that our prompt and data context work as we expect.</p><pre>query = &quot;What&#39;s the Michael Jordan net worth?&quot;<br>result_df3 = run_question_test(query, eval_df.copy())<br>table = BeautifulTableformat(query, result_df3, 150)<br>print(table)</pre><p>Output:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VNW0SnjBabEatmPFZF0UDg.jpeg" /><figcaption>Image 5 showcases a table illustrating the responses generated by a model for different input text formats in response to the question, “What is Michael Jordan’s net worth?”</figcaption></figure><p>With this test, we’ve proposed a question that has no correct answer in the context of the information provided. Our goal is to ensure that our model does not respond with a hallucination or false positive (a false response that seems true).<br>The answer to our “What’s Michael Jordan’s net worth?” was resolved consistently for each data format, as we expected (there was no answer to the question).</p><p>Let’s provide another example, which would possibly mislead an unsuspecting user, by using a name that significantly resembles one existing in the tabular data.</p><pre>query = &quot;What&#39;s Michael Musk&#39;s net worth?&quot;<br>result_df4 = run_question_test(query, eval_df.copy())<br>table = BeautifulTableformat(query, result_df4, 180)<br>print(table)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9JL_cQP4aJnf64UL_6eZCA.jpeg" /><figcaption>Image 6 presents a table delineating the responses generated by a model across various input text formats in answer to the question, “What is Michael Musk’s net worth?”</figcaption></figure><p>With the question “What’s Michael Musk&#39;s net worth?” where “Musk” could make us misinterpret the question, the model has nevertheless solved the challenge satisfactorily.</p><h3>Conclusion</h3><p>By ingesting small tabular data from textual documents, we’ve seen how an LLM can understand the context of a table even when we tried to trick it by asking questions with wrong information. It’s evident that preserving structural integrity while extracting contextually embedded tables is relevant. These tables often contain critical contextual information, enhancing the surrounding text’s comprehension to provide more accurate results.</p><p>Focusing on small-embedded tables, it’s essential to recognize their significance in providing contextual clues to your RAG framework. Leveraging libraries like <a href="https://github.com/camelot-dev/camelot">Camelot</a> for table extraction ensures the preservation of these structures. However, maintaining relevance without distraction poses challenges, as demonstrated in model testing. By doing so, we provide essential context to models like GPT, enabling them to generate accurate and contextually relevant responses within broader textual contexts.</p><h3>Call to Action</h3><p>If you want to explore more, there are a series of alternative libraries that facilitate the extraction of tabular data, such as: <a href="https://github.com/tabulapdf/tabula">Tabula</a>, <a href="https://github.com/jsvine/pdfplumber">pdfplumber</a>, <a href="https://github.com/drj11/pdftables">pdftables</a>, and <a href="https://github.com/ashima/pdf-table-extract">pdf-table-extract</a>.In “<a href="https://github.com/camelot-dev/camelot/wiki/Comparison-with-other-PDF-Table-Extraction-libraries-and-tools">Comparison with other PDF Table Extraction libraries and tools</a>,” Vinayak Mehta developed a comparative performance evaluation of the listed libraries that you might find useful.</p><p>Explore <a href="https://opea.dev">Open Platform for Enterprise AI (OPEA)</a>. When deploying a RAG system, we will face multiple challenges such as ensuring scalability, handling data security, and integrating with existing infrastructure. This open-source project helps with the deployment by providing robust tools and frameworks designed to streamline these processes and facilitate seamless integration.</p><h3>About the Authors</h3><p><strong>Eduardo Rojas Oviedo, Platform Engineer, Intel</strong></p><p><a href="https://www.linkedin.com/in/eduardo-rojas-oviedo-56306923?originalSubdomain=cr">Eduardo Rojas Oviedo</a> is a dedicated RAG developer within Intel’s dynamic and innovative team. Specialized in cutting-edge developer tools for AI, Machine Learning, and NLP, he is passionate about leveraging technology to create impactful solutions. Eduardo’s expertise lies in building robust and innovative applications that push the boundaries of what’s possible in the realm of artificial intelligence. His commitment to sharing knowledge and advancing technology drives his ongoing pursuit of excellence in the field.</p><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist on Intel’s <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/team.html">Open Ecosystem</a> team, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on X at <a href="https://twitter.com/eze_lanza">@eze_lanza</a> and LinkedIn at <a href="https://www.linkedin.com/in/ezelanza/">/eze_lanz</a>a</p><h3>Follow us!</h3><p><a href="https://medium.com/intel-tech">Medium</a>, <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/podcast.html">Podcast</a>, <a href="http://open.intel.com">Open.intel</a> , <a href="https://twitter.com/OpenAtIntel">X</a> , <a href="https://www.linkedin.com/search/results/all/?fetchDeterministicClustersOnly=true&amp;heroEntityKey=urn%3Ali%3Aorganization%3A100949244&amp;keywords=open.intel&amp;origin=RICH_QUERY_TYPEAHEAD_HISTORY&amp;position=0&amp;searchId=48e19f47-ebfe-49e5-acc2-9b9424bd004c&amp;sid=226&amp;spellCorrectionEnabled=true">Linkedin</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bcb42678914b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/tabular-data-rag-llms-improve-results-through-data-table-prompting-bcb42678914b">Tabular Data, RAG, &amp; LLMs: Improve Results Through Data Table Prompting</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Easily Deploy Multiple LLMs in a Cloud Native Environment]]></title>
            <link>https://medium.com/intel-tech/easily-deploy-multiple-llms-in-a-cloud-native-environment-c7b47511ac15?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/c7b47511ac15</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[kubernetes]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Tue, 23 Apr 2024 16:02:09 GMT</pubDate>
            <atom:updated>2024-05-06T20:31:45.911Z</atom:updated>
            <content:encoded><![CDATA[<p>Take the complexity out of deploying cloud native LLMs with LangChain and Intel Developer Cloud</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pue1nFqUuGZBvo0Nn36xqw.png" /></figure><h4>By Arun Gupta and Ezequiel Lanza</h4><p>Imagine you could give a personal assistant to each employee. Productivity across your organization would spike as employees can see how AI can help them and feel empowered to focus on strategic thinking. This dream scenario is possible with powerful AI technology, such as department-specific chatbots that deliver fast, high-quality results.</p><p>However, businesses often need to weave together multiple large language models (LLMs) to support diverse use cases. Because each model may have different compute and storage needs or specific knowledge to be used by internal departments in unique ways, the complexity can quickly skyrocket.</p><p>The right set of tools can take the complexity out of the deployment process. Here we’ll explore a reference architecture for building and deploying multiple LLMs in a single user interface with Kubernetes and <a href="https://www.langchain.com/">LangChain</a>. You can also find a thorough explanation of each step in the corresponding <a href="https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main">GitHub repo</a>, which is structured as an educational resource with recipe files for you to download and create your own containers.</p><p>We demo the complete steps for this approach in a KubeCon + CloudNativeCon Europe 2024 presentation.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FDVf5O2Xm_RY%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DDVf5O2Xm_RY&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FDVf5O2Xm_RY%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/402d4ec8ca6afde782d5b38f96a0984e/href">https://medium.com/media/402d4ec8ca6afde782d5b38f96a0984e/href</a></iframe><h3><strong>Step 1: Define Your Model</strong></h3><p><a href="https://huggingface.co/models">Hugging Face makes it easy to download models</a>, so you can start inferencing locally right away, but how do you know which LLM to choose? There are three important considerations to keep in mind as you evaluate your options, in addition to deciding whether you should deploy the model locally or externally (which we’ll address in the next step).</p><p>· <strong>Performance: </strong>Before downloading a model, you can see how it stacks up against industry benchmarks by comparing it to other models on the <a href="https://huggingface.co/open-llm-leaderboard">Hugging Face Leaderboard</a>, a public ranking system of open LLMs.</p><p>· <strong>Community support: </strong>You don’t want to choose a model that no one uses or maintains. Look for a model with widespread community adoption, an active contributor base, and strong documentation with helpful resources like tutorials. These are all signs of a thriving community that can offer the help you need down the road.</p><p>· <strong>Ethical considerations: </strong>Sometimes models generate biased results around traits like ethnicity or gender. Choosing a model that trains on diverse data and is transparent about its processes can help you mitigate bias and ensure your results are fair.</p><p>Depending on your use case, you may also want to optimize your model. A tool like <a href="https://github.com/intel/intel-extension-for-transformers">Intel® Extension for Transformers</a>, for example, can help you shrink the RAM needed to store a <a href="https://huggingface.co/meta-llama/Llama-2-7b-chat-hf">7 billion parameter LLaMa2 chatbot model</a> from 26 GB to 7 GB.</p><h3><strong>Step 2: Choose a Consumption Model</strong></h3><p>Once you’ve picked a model, you need to consider where you’ll consume it. Local models can be stored on your internal servers or even laptops if they are optimized, while external models allow you to use LLMs hosted by a third party. Factors such as cost, storage capacity, and how you plan to handle sensitive data will help determine which consumption model is right for you.</p><p>For example, if your model will be used by financial or legal departments, which are often subject to data privacy regulations, you may need to use a local model so you can inference without ever sending sensitive data outside your organization. Local models give you more control to choose how and when you use them, enabling your team to use models offline and customize them to your business, also known as fine-tuning. Additionally, local models can potentially benefit from cost efficiencies, as some external models can require you to pay per outbound and inbound token . When using a fee-based external model, you will be paying both to send a query and receive a response. You’ll want to ensure that your prompts are well engineered and phrased precisely to draw out the correct answer. Otherwise, you’ll be sending multiple prompts and inflating your fees.</p><p>However, there are advantages to external models that require much less compute power and storage. Say you have a 7 billion parameter LLaMa model, which requires about 26 GB RAM. To inference the model locally, you’d need sufficient storage space on your server plus the necessary compute power provided by local CPUs and GPUs to support model inference and your application. In an external consumption model, however, you’d only need enough compute power to run your application — simply make an API call and start inferencing. Additionally, because a third party manages infrastructure resources, external models are typically simpler to set up, faster to deploy, and easier to scale.</p><p>You can use a <a href="https://docsbot.ai/tools/gpt-openai-api-pricing-calculator">pricing calculator</a> to start estimating the cost of an external model based on the provider and number of input and output tokens you need.</p><h3><strong>Step 3: Package Your Models</strong></h3><p>As you narrow in on which LLMs you’ll use to support your most important use cases, you need a unified way to manage all models in the most efficient way possible. LangChain is an open source framework that simplifies building and managing multiple types of LLM applications in a single user interface. You can plug in any of the <a href="https://python.langchain.com/docs/modules/model_io/">more than 80 supported types of open source LLMs</a>, including local and external models, optimized and unoptimized models, and even advanced techniques like retrieval-augmented generation (RAG).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9Fdt5cnSdHA2Pf0Khq6yXA.png" /><figcaption>The LangChain API Chain combines the prompt template and LLM pipeline to generate better results.</figcaption></figure><p>However, as you can see, an LLM application is not just a model. Complete applications include a model, parameters, and a tokenizer, which Hugging Face provides in multiple configurations called pipelines. In addition, LangChain offers a prompt template that delivers more context to the pipeline to generate better results. LangChain offers an API called Chain, <a href="https://python.langchain.com/docs/expression_language/get_started">among others</a>, that pulls together your prompt template and pipeline, so interacting with the model becomes as simple as using “chain.invoke” to send your question and generate a response.</p><h3><strong>Step 4: Containerize Your Model</strong></h3><p>Ask a developer how they like to deploy their LLMs, and more often than not they’ll say Kubernetes. Cloud native has become the de facto platform for deploying LLMs because it offers a few key advantages.</p><p>· <strong>Scalability and portability:</strong> After you’ve configured a model in a cluster on your desktop, you can seamlessly scale it across platforms — such as Amazon EKS, Microsoft Azure, or the <a href="https://www.intel.com/content/www/us/en/developer/tools/devcloud/overview.html">Intel® Developer Cloud</a> — and production environments, from on-premises to the edge and from small to large node clusters.</p><p>· <strong>Resource management: </strong>AI models consume lots of memory and compute power. Kubernetes gives you more control to fine-tune your resources via CPU and memory limits, resource quotas, and priority classes to funnel power to your most important models first.</p><p>· <strong>Observability: </strong>Many open source projects are using telemetry to enhance visibility and provide more insight into AI models.</p><p>Just like the model packaging process in the previous step, LangChain also provides an API to help you easily containerize your model. Start by connecting your container to the file server via a <a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/">persistent volume claim (PVC) or a persistent volume (PV)</a>. Now when you run the container, the “POST” API downloads the model from the file server and places it inside the container.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W6SWH6sBUAEydAYJjcT7Jg.gif" /><figcaption>Each time you run the container, the local or external model (represented by the pipelines here) is downloaded from the file server onto the container.</figcaption></figure><h3><strong>Step 5: Integrate Multiple Models</strong></h3><p>Now that you’ve packaged and containerized your model, you may want to integrate additional LLMs that are fine-tuned to new use cases. To do so, you need an LLM proxy — an architecture layer that unites distinct LLMs so you can interact with multiple models at once.</p><p>Take for example your organization’s intranet. It connects employees across your organization to the tools they need to complete their work, communicate across business units, and access important information about their employment status, such as pay stubs, PTO requests, tax forms. Attaching your intranet UI to an LLM proxy gives your employees access to AI models optimized to each business unit.</p><p>In addition to providing management tools for model provisioning and governance, the LLM proxy may also include additional intelligence to help field AI prompts and direct them to the best LLM to solve the problem.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bKIFNayNb3s-DSB1F9VdUg.png" /><figcaption>The LLM proxy connects front-end architecture with the LLMs running on the back end.</figcaption></figure><p>To build this in a Kubernetes environment, first create an ingress controller or load balancer, such as NGINX, to expose the front end and the proxy so that the browser can communicate with it. Everything beneath the proxy, such as local models or APIs for external models, will not need to be exposed to the browser and can be deployed internally as pods on the cluster. Once you push your containers to a container registry like <a href="https://hub.docker.com/">Docker Hub</a>, your containers will be accessible when you run your cluster.</p><h3><strong>Demo: Deploying a Chatbot in Kubernetes in Intel Developer Cloud</strong></h3><p>In this<a href="https://youtu.be/DVf5O2Xm_RY?si=664U94HWUDg0uCKF&amp;t=1581"> quick demo</a>, we’ll show you how to deploy a chatbot that can switch between multiple models using LangChain and Intel Kubernetes Service (IKS). IKS is an <a href="https://www.intel.com/content/www/us/en/developer/tools/devcloud/overview.html">Intel® Developer Cloud</a> service that lets you test applications on the latest Intel® hardware as soon as it’s available, rather than waiting for a cloud service provider to adopt the hardware. You can also download the necessary files from the <a href="https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main">GitHub repo</a> to launch your own containers and follow along. As you’ll see, the results appear in a simple, mock UI, using <a href="https://react.dev/">React</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/695/1*xU0qJWNKwNT79wpOqlbrmw.png" /><figcaption>The LangChain application running in Intel Kubernetes Service (IKS) allows the user to easily switch between three models.</figcaption></figure><h3><strong>Try It for Yourself</strong></h3><p>AI models can help improve employee productivity across your organization, but one model rarely fits all use cases. LangChain makes it easy to use multiple LLMs in one environment, allowing employees to choose which model is right for each situation. Explore the <a href="https://github.com/intel/Multi-llms-Chatbot-CloudNative-LangChain/tree/main">GitHub repo</a> to get started using LangChain and IKS.</p><h3>About the Authors</h3><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist on Intel’s <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/team.html">Open Ecosystem</a> team, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on X at <a href="https://twitter.com/eze_lanza">@eze_lanza</a> and LinkedIn at <a href="https://www.linkedin.com/in/ezelanza/">/eze_lanza</a></p><p><strong>Arun Gupta, vice president and general manager, Open Ecosystem, Intel</strong></p><p>Dedicated to growing the open ecosystem at Intel, <a href="https://www.linkedin.com/in/arunpgupta">Arun Gupta</a> is a strategist, advocate, and practitioner who has spent two decades helping companies such as Apple and Amazon embrace open source principles. He is currently chairperson of the Cloud Native Computing Foundation Governing Board.</p><h3>Follow us!</h3><p><a href="https://medium.com/intel-tech">Medium</a>, <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/podcast.html">Podcast</a>, <a href="http://open.intel.com">Open.intel</a> , <a href="https://twitter.com/OpenAtIntel">X</a> , <a href="https://www.linkedin.com/search/results/all/?fetchDeterministicClustersOnly=true&amp;heroEntityKey=urn%3Ali%3Aorganization%3A100949244&amp;keywords=open.intel&amp;origin=RICH_QUERY_TYPEAHEAD_HISTORY&amp;position=0&amp;searchId=48e19f47-ebfe-49e5-acc2-9b9424bd004c&amp;sid=226&amp;spellCorrectionEnabled=true">Linkedin</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c7b47511ac15" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/easily-deploy-multiple-llms-in-a-cloud-native-environment-c7b47511ac15">Easily Deploy Multiple LLMs in a Cloud Native Environment</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Optimize Vector Databases, Enhance RAG-Driven Generative AI]]></title>
            <link>https://medium.com/intel-tech/optimize-vector-databases-enhance-rag-driven-generative-ai-90c10416cb9c?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/90c10416cb9c</guid>
            <category><![CDATA[optimization]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Tue, 02 Apr 2024 02:56:13 GMT</pubDate>
            <atom:updated>2024-07-31T21:08:04.385Z</atom:updated>
            <content:encoded><![CDATA[<p>Two methods to optimize your vector database when using RAG</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FRWBVwOHPYFDIVTp_ylZNQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@ilyapavlov?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Ilya Pavlov</a> on <a href="https://unsplash.com/photos/monitor-showing-java-programming-OqtafYT5kTw?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Unsplash</a></figcaption></figure><h4>By Cathy Zhang and Dr. Malini Bhandaru</h4><h4>Contributors: Lin Yang and Changyan Liu</h4><p>Generative AI (GenAI) models, which are seeing exponential adoption in our daily lives, are being improved by <a href="https://www.techtarget.com/searchenterpriseai/definition/retrieval-augmented-generation">retrieval-augmented generation (RAG)</a>, a technique used to enhance response accuracy and reliability by fetching facts from external sources. RAG helps a regular <a href="https://www.techtarget.com/whatis/definition/large-language-model-LLM">large language model (LLM)</a> understand context and reduce <a href="https://en.wikipedia.org/wiki/Hallucination_(artificial_intelligence)">hallucinations</a> by leveraging a giant database of unstructured data stored as vectors — a mathematical presentation that helps capture context and relationships between data.</p><p>RAG helps to retrieve more contextual information and thus generate better responses, but the vector databases they rely on are getting ever larger to provide rich content to draw upon. Just as trillion-parameter LLMs are on the horizon, vector databases of billions of vectors are not far behind. As optimization engineers, we were curious to see if we could make vector databases more performant, load data faster, and create indices faster to ensure retrieval speed even as new data is added. Doing so would not only result in reduced user wait time, but also make RAG-based AI solutions a little more sustainable.</p><p>In this article, you’ll learn more about vector databases and their benchmarking frameworks, datasets to tackle different aspects, and the tools used for performance analysis — everything you need to start optimizing vector databases. We will also share our optimization achievements on two popular vector database solutions to inspire you on your optimization journey of performance and sustainability impact.</p><h3><strong>Understanding Vector Databases</strong></h3><p>Unlike traditional relational or non-relational databases where data is stored in a structured manner, a vector database contains a mathematical representation of individual data items, called a vector, constructed using an embedding or transformation function. The vector commonly represents features or semantic meanings and can be short or long. Vector databases do vector retrieval by similarity search using a distance metric (where closer means the results are more similar) such as <a href="https://www.pinecone.io/learn/vector-similarity/">Euclidean, dot product, or cosine similarity</a>.</p><p>To accelerate the retrieval process, the vector data is organized using an indexing mechanism. Examples of these organization methods include flat structures, <a href="https://arxiv.org/abs/2002.09094">inverted file (IVF),</a> <a href="https://arxiv.org/abs/1603.09320">Hierarchical Navigable Small Worlds (HNSW),</a> and <a href="https://en.wikipedia.org/wiki/Locality-sensitive_hashing">locality-sensitive hashing (LSH)</a>, among others. Each of these methods contributes to the efficiency and effectiveness of retrieving similar vectors when needed.</p><p>Let’s examine how you would use a vector database in a GenAI system. Figure 1 illustrates both the loading of data into a vector database and using it in the context of a GenAI application. When you input your prompt, it undergoes a transformation process identical to the one used to generate vectors in the database. This transformed vector prompt is then used to retrieve similar vectors from the vector database. These retrieved items essentially serve as conversational memory, furnishing contextual history for prompts, akin to how LLMs operate. This feature proves particularly advantageous in natural language processing, computer vision, recommendation systems, and other domains requiring semantic comprehension and data matching. Your initial prompt is subsequently “merged” with the retrieved elements, supplying context, and assisting the LLM in formulating responses based on the provided context rather than solely relying on its original training data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zQj_YJdWc2xKB6Vv89lzDQ.jpeg" /><figcaption><strong>Figure 1.</strong> A RAG application architecture.</figcaption></figure><p>Vectors are stored and indexed for speedy retrieval. Vector databases come in two main flavors, traditional databases that have been extended to store vectors, and purpose-built vector databases. Some examples of traditional databases that provide vector support are <a href="https://redis.io/">Redis</a>, <a href="https://github.com/pgvector/pgvector">pgvector</a>, <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a>, and <a href="https://opensearch.org/">OpenSearch</a>. Examples of purpose-built vector databases include proprietary solutions <a href="https://zilliz.com/">Zilliz</a> and <a href="https://www.pinecone.io/">Pinecone</a>, and open source projects <a href="https://milvus.io/">Milvus</a>, <a href="https://weaviate.io/">Weaviate</a>, <a href="https://qdrant.tech/">Qdrant</a>, <a href="https://github.com/facebookresearch/faiss">Faiss</a>, and <a href="https://www.trychroma.com/">Chroma</a>. You can learn more about vector databases on GitHub via <a href="https://github.com/langchain-ai/langchain/tree/master/libs/langchain/langchain/vectorstores">LangChain </a>and <a href="https://github.com/openai/openai-cookbook/tree/main/examples/vector_databases">OpenAI Cookbook</a>.</p><p>We’ll take a closer look at one from each category, Milvus and Redis.</p><h3><strong>Improving Performance</strong></h3><p>Before diving into the optimizations, let’s review how vector databases are evaluated, some evaluation frameworks, and available performance analysis tools.</p><h4><strong>Performance Metrics</strong></h4><p>Let’s look at key metrics that can help you measure vector database performance.</p><ul><li><strong>Load latency</strong> measures the time required to load data into the vector database’s memory and build an index. An index is a data structure used to efficiently organize and retrieve vector data based on its similarity or distance. Types of <a href="https://milvus.io/docs/index.md#In-memory-Index">in-memory indices</a> include <a href="https://thedataquarry.com/posts/vector-db-3/#flat-indexes">flat index</a>, <a href="https://supabase.com/docs/guides/ai/vector-indexes/ivf-indexes">IVF_FLAT</a>, <a href="https://towardsdatascience.com/ivfpq-hnsw-for-billion-scale-similarity-search-89ff2f89d90e">IVF_PQ, HNSW</a>, <a href="https://github.com/google-research/google-research/tree/master/scann">scalable nearest neighbors (ScaNN),</a>and <a href="https://milvus.io/docs/disk_index.md">DiskANN</a>.</li><li><strong>Recall</strong> is the proportion of true matches, or relevant items, found in the <a href="https://redis.io/docs/data-types/probabilistic/top-k/">Top K</a> results retrieved by the search algorithm. Higher recall values indicate better retrieval of relevant items.</li><li><strong>Queries per second (QPS)</strong> is the rate at which the vector database can process incoming queries. Higher QPS values imply better query processing capability and system throughput.</li></ul><h4><strong>Benchmarking Frameworks</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/460/1*mssEjZAuXg6nf-pad67rHA.jpeg" /><figcaption><strong>Figure 2.</strong> The vector database benchmarking framework.</figcaption></figure><p>Benchmarking a vector database requires a vector database server and clients. In our performance tests, we used two popular open source tools.</p><ul><li><a href="https://github.com/zilliztech/VectorDBBench/tree/main"><strong>VectorDBBench</strong></a><strong>: </strong>Developed and open sourced by Zilliz, VectorDBBench helps test different vector databases with different index types and provides a convenient web interface.</li><li><a href="https://github.com/qdrant/vector-db-benchmark/tree/master"><strong>vector-db-benchmark</strong></a><strong>: </strong>Developed and open sourced by Qdrant, vector-db-benchmark helps test several typical vector databases for the <a href="https://www.datastax.com/guides/hierarchical-navigable-small-worlds">HNSW</a> index type. It runs tests through the command line and provides a <a href="https://docs.docker.com/compose/">Docker Compose</a><em> </em>file to simplify starting server components.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NpHHEFV0TxRMse83hK6H1A.jpeg" /><figcaption><strong>Figure 3.</strong> An example vector-db-benchmark command used to run the benchmark test.</figcaption></figure><p>But the benchmark framework is only part of the equation. We need data that exercises different aspects of the vector database solution itself, such as its ability to handle large volumes of data, various vector sizes, and speed of retrieval.With that, let’s look at some available public datasets.</p><h4><strong>Open Datasets</strong> <strong>to Exercise Vector Databases</strong></h4><p>Large datasets are good candidates to test load latency and resource allocation. Some datasets have high dimensional data and are good for testing speed of computing similarity.</p><p>Datasets range from a dimension of 25 to a dimension of 2048. The <a href="https://laion.ai/">LAION</a> dataset, an open image collection, has been used for training very large visual and language deep-neural models like stable diffusion generative models. OpenAI’s dataset of 5M vectors, each with a dimension of 1536, was created by VectorDBBench by running OpenAI on <a href="https://huggingface.co/datasets/allenai/c4">raw data</a>. Given each vector element is of type FLOAT, to save the vectors alone, approximately 29 GB (5M * 1536 * 4) of memory is needed, plus a similar amount extra to hold indices and other metadata for a total of 58 GB of memory for testing. When using the vector-db-benchmark tool, ensure adequate disk storage to save results.</p><p>To test for load latency, we needed a large collection of vectors, which <a href="https://docs.hippo.transwarp.io/docs/performance-dataset">deep-image-96-angular</a> offers. To test performance of index generation and similarity computation, high dimensional vectors provide more stress. To this end we chose the 500K dataset of 1536 dimension vectors.</p><h4><strong>Performance Tools</strong></h4><p>We’ve covered ways to stress the system to identify metrics of interest, but let’s examine what’s happening at a lower level: How busy is the computing unit, memory consumption, waits on locks, and more? These provide clues to databasebehavior, particularly useful in identifying problem areas.</p><p>The Linux <a href="https://www.redhat.com/sysadmin/interpret-top-output">top</a> utility provides system-performance information. However, the <a href="https://perf.wiki.kernel.org/index.php/Main_Page">perf</a> tool in Linux provides a deeper set of insights. To learn more, we also recommend reading <a href="https://www.brendangregg.com/perf.html">Linux perf examples</a> and the <a href="https://www.intel.com/content/www/us/en/docs/vtune-profiler/cookbook/2023-0/top-down-microarchitecture-analysis-method.html">Intel top-down microarchitecture analysis method</a>. Yet another tool is the <a href="https://www.intel.com/content/www/us/en/developer/tools/oneapi/vtune-profiler.html">Intel® vTune™ Profiler</a>, which is useful when optimizing not just application but also system performance and configuration for a variety of workloads spanning HPC, cloud, IoT, media, storage, and more.</p><h3><strong>Milvus Vector Database Optimizations</strong></h3><p>Let’s walk through some examples of how we attempted to improve the performance of the Milvus vector database.</p><h4><strong>Reducing Memory Movement Overhead in Datanode Buffer Write</strong></h4><p>Milvus’s write path proxies write data into a log broker via <em>MsgStream</em>. The data nodes then consume the data, converting and storing it into segments. Segments will merge the newly inserted data. The merge logic allocates a new buffer to hold/move both the old data and the new data to be inserted and then returns the new buffer as old data for the next data merge. This results in the old data getting successively larger, which in turn makes data movement slower. Perf profiles showed a high overhead for this logic.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/712/1*Az4dMVBcGmdeyKNrwpR19g.jpeg" /><figcaption><strong>Figure 4.</strong> Merging and moving data in the vector database generates a high-performance overhead.</figcaption></figure><p>We changed the <em>merge buffer</em> logic to directly append the new data to be inserted into the old data, avoiding allocating a new buffer and moving the large old data. Perf profiles confirm that there is no overhead to this logic. The microcode metrics <em>metric_CPU operating frequency</em> and <em>metric_CPU utilization</em> indicate an improvement that is consistent with the system not having to wait for the long memory movement anymore. Load latency improved by more than 60 percent. The improvement is captured on <a href="https://github.com/milvus-io/milvus/pull/26839">GitHub</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MmaUtBTdqmMvC5MlQ8V0wQ.jpeg" /><figcaption><strong>Figure 5.</strong> With less copying we see a performance improvement of more than 50 percent in load latency.</figcaption></figure><h4><strong>Inverted Index Building with Reduced Memory Allocation Overhead</strong></h4><p>The Milvus search engine, <a href="https://milvus.io/docs/knowhere.md">Knowhere</a>, employs the <a href="https://www.vlfeat.org/api/kmeans-fundamentals.html#kmeans-elkan">Elkan k-means algorithm</a> to train cluster data for creating <a href="https://milvus.io/docs/v1.1.1/index.md">inverted file (IVF) indices</a>. Each round of data training defines an iteration count. The larger the count, the better the training results. However, it also implies that the Elkan algorithm will be called more frequently.</p><p>The Elkan algorithm handles memory allocation and deallocation each time it’s executed. Specifically, it allocates memory to store half the size of symmetric matrix data, excluding the diagonal elements. In Knowhere, the symmetric matrix dimension used by the Elkan algorithm is set to 1024, resulting in a memory size of approximately 2 MB. This means for each training round Elkan repeatedly allocates and deallocates 2 MB memory.</p><p>Perf profiling data indicated frequent large memory allocation activity. In fact, it triggered <a href="https://www.oreilly.com/library/view/linux-device-drivers/9781785280009/4759692f-43fb-4066-86b2-76a90f0707a2.xhtml">virtual memory area (VMA)</a>allocation, physical page allocation, page map setup, and updating of memory cgroup statistics in the kernel. This pattern of large memory allocation/deallocation activity can, in some situations, also aggravate memory fragmentation. This is a significant tax.</p><p>The <em>IndexFlatElkan</em> structure is specifically designed and constructed to support the Elkan algorithm. Each data training process will have an <em>IndexFlatElkan</em> instance initialized. To mitigate the performance impact resulting from frequent memory allocation and deallocation in the Elkan algorithm, we refactored the code logic, moving the memory management outside of the Elkan algorithm function up into the construction process of <em>IndexFlatElkan</em>. This enables memory allocation to occur only once during the initialization phase while serving all subsequent Elkan algorithm function calls from the current data training process and helps to improve load latency by around 3 percent. Find the <a href="https://github.com/zilliztech/knowhere/pull/280">Knowhere patch here</a>.</p><h3><strong>Redis Vector Search Acceleration through Software Prefetch</strong></h3><p>Redis, a popular traditional in-memory key-value data store, recently began supporting vector search. To go beyond a typical key-value store, it offers extensibility modules; the <a href="https://github.com/RediSearch/RediSearch">RediSearch</a> module facilitates the storage and search of vectors directly within Redis.</p><p>For vector similarity search, Redis supports two algorithms, namely brute force and HNSW. The HNSW algorithm is specifically crafted for efficiently locating approximate nearest neighbors in high-dimensional spaces. It uses a priority queue named <em>candidate_set</em> to manage all vector candidates for distance computing.</p><p>Each vector candidate encompasses substantial metadata in addition to the vector data. As a result, when loading a candidate from memory it can cause data cache misses, which incur processing delays. Our optimization introduces software prefetching to proactively load the next candidate while processing the current one. This enhancement has resulted in a 2 to 3 percent throughput improvement for vector similarity searches in a single instance Redis setup. The patch is in the process of being upstreamed.</p><h3><strong>GCC Default Behavior Change to Prevent Mixed Assembly Code Penalties</strong></h3><p>To drive maximum performance, frequently used sections of code are often handwritten in assembly. However, when different segments of code are written either by different people or at different points in time, the instructions used may come from incompatible assembly instruction sets such as <a href="https://www.intel.com/content/www/us/en/architecture-and-technology/avx-512-overview.html">Intel® Advanced Vector Extensions 512 (Intel® AVX-512)</a> and <a href="https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions">Streaming SIMD Extensions (SSE)</a>. If not compiled appropriately, the mixed code results in a performance penalty. <a href="https://www.intel.com/content/dam/develop/external/us/en/documents/11mc12-avoiding-2bavx-sse-2btransition-2bpenalties-2brh-2bfinal-809104.pdf">Learn more about mixing Intel AVX and SSE instructions here</a>.</p><p>You can easily determine if you’re using mixed-mode assembly code and have not compiled the code with <em>VZEROUPPER</em>, incurring the performance penalty. It can be observed through a perf command like <em>sudo perf stat -e ‘assists.sse_avx_mix/event/event=0xc1,umask=0x10/’ &lt;workload&gt;</em>. If your OS doesn’t have support for the event, use <em>cpu/event=0xc1,umask=0x10,name=assists_sse_avx_mix/</em>.</p><p>The Clang compiler by default inserts <em>VZEROUPPER</em>,<em> </em>avoiding any mixed mode penalty.<em> </em>But the GCC compiler only inserted <em>VZEROUPPER</em> when the -O2 or -O3 compiler flags were specified. We contacted the GCC team and explained the issue and they now, by default, correctly handle mixed mode assembly code.</p><h3><strong>Start Optimizing Your Vector Databases</strong></h3><p>Vector databases are playing an integral role in GenAI, and they are growing ever larger to generate higher-quality responses. With respect to optimization, AI applications are no different from other software applications in that they reveal their secrets when one employs standard performance analysis tools along with benchmark frameworks and stress input.</p><p>Using these tools, we uncovered performance traps pertaining to unnecessary memory allocation, failing to prefetch instructions, and using incorrect compiler options. Based on our findings, we upstreamed enhancements to Milvus, Knowhere, Redis, and the GCC compiler to help make AI a little more performant and sustainable. Vector databases are an important class of applications worthy of your optimization efforts. We hope this article helps you get started.</p><p>When deploying a RAG system, we will face multiple challenges such as ensuring scalability, handling data security, and integrating with existing infrastructure. <a href="https://opea.dev">Open Platform for Enterprise AI (OPEA)</a> is an open source project that helps with the deployment by providing robust tools and frameworks designed to streamline these processes and facilitate seamless integration.</p><h3>About the Authors</h3><p><a href="https://www.linkedin.com/in/cathy-zhang-025a25276/">Cathy Zhang</a> is a senior software engineer with deep software, microarchitecture, and virtualization expertise. As a member of the Cloud Software Engineering team in SATG/SSE, she has recently been focused on optimizing vector databases like Milvus and has experience building retrieval-augmented generation (RAG) systems. At Intel, Cathy has contributed to x86 virtualization, working on the lightweight, more secure Intel® hypervisor for cloud usages and on Intel® Software Guard Extensions (Intel® SGX). Prior to Intel, Cathy worked for an extended time on IBM System Z Virtualization. Outside of work, Cathy enjoys reading, mountain climbing, and traveling.</p><p><a href="https://www.linkedin.com/in/malinibhandaru/">Dr. Malini Bhandaru</a> is a senior principal engineer focused on confidential compute, security, and performance. She has been engaged in open source for more than a decade, working on Confidential Containers, EdgeX Foundry, KubeFlow, and OpenStack projects. She started at Intel as an Intel® Xeon® Server Power Performance architect. Across her career, she has worked on autonomous driving, healthcare, and telecom applications. Malini is a frequent conference speaker and has served on KubeCon and OSS program committees. She has a PhD in machine learning and more than 25 patents. She is also a STEM coach and avid gardener</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=90c10416cb9c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/optimize-vector-databases-enhance-rag-driven-generative-ai-90c10416cb9c">Optimize Vector Databases, Enhance RAG-Driven Generative AI</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Four Data Cleaning Techniques to Improve Large Language Model (LLM) Performance]]></title>
            <link>https://medium.com/intel-tech/four-data-cleaning-techniques-to-improve-large-language-model-llm-performance-77bee9003625?source=rss----bcaa5b033cbb---4</link>
            <guid isPermaLink="false">https://medium.com/p/77bee9003625</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[data]]></category>
            <dc:creator><![CDATA[Intel]]></dc:creator>
            <pubDate>Mon, 01 Apr 2024 15:06:02 GMT</pubDate>
            <atom:updated>2024-05-06T20:32:15.162Z</atom:updated>
            <content:encoded><![CDATA[<p>Unlock more accurate and meaningful AI outcomes with RAG (retrieval-augmented generation).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*L2E7RSnKP4Hbo97M" /><figcaption>Photo by <a href="https://unsplash.com/@norevisions?utm_source=medium&amp;utm_medium=referral">No Revisions</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>By Eduardo Rojas Oviedo and Ezequiel Lanza</h4><p>The <a href="https://www.techtarget.com/searchenterpriseai/definition/retrieval-augmented-generation">retrieval-augmented generation (RAG)</a> process has gained popularity due to its potential to enhance the understanding of <a href="https://www.techtarget.com/whatis/definition/large-language-model-LLM">large language models (LLMs)</a>, providing them with context and helping to prevent <a href="https://en.wikipedia.org/wiki/Hallucination_(artificial_intelligence)">hallucinations</a>. The RAG process involves several steps, from ingesting documents in chunks to extracting context to prompting the LLM model with that context. While known to significantly improve predictions, RAG can occasionally lead to incorrect results. The way documents are ingested plays a crucial role in this process. For instance, if our “context documents” contain typos or unusual characters for an LLM, such as emojis, it could potentially confuse the LLM’s understanding of the provided context.</p><p>In this post, we’ll demonstrate the use of four common <a href="https://www.techtarget.com/searchenterpriseai/definition/natural-language-processing-NLP">natural language processing (NLP)</a> techniques to clean text before it’s ingested and converted into chunks for further processing by the LLM. We’ll also illustrate how these techniques can significantly enhance the model’s response to a prompt.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/204/1*n-IAAx63GZplC_LnvbeYMA.png" /><figcaption>The steps of the RAG process, adapted from <a href="https://github.com/Tongji-KGLLM/RAG-Survey">RAG-Survey</a>.</figcaption></figure><h3><strong>Why Is it Important to Clean Your Documents?</strong></h3><p>It’s standard practice to clean up text before feeding it into any kind of machine learning algorithm. Whether you’re using <a href="https://en.wikipedia.org/wiki/Supervised_learning">supervised</a> or <a href="https://en.wikipedia.org/wiki/Unsupervised_learning">unsupervised algorithms</a>, or even crafting context for your generative AI (GAI) model, getting your text in good shape helps to:</p><p>· <strong>Ensure accuracy:</strong> By getting rid of mistakes and making everything consistent, you’re less likely to confuse the model or end up with model hallucinations.</p><p>· <strong>Improve quality:</strong> Cleaner data ensures that the model works with reliable and consistent information, helping our models to infer from accurate data.</p><p>· <strong>Facilitate analysis</strong>: Clean data is easy to interpret and analyze. For example, a model trained with plain text may struggle to comprehend <a href="https://www.statology.org/tabular-data/">tabular data</a>.</p><p>By cleaning our data — especially <a href="https://www.techtarget.com/searchbusinessanalytics/definition/unstructured-data">unstructured data</a> — we provide the model with reliable and relevant context, which improves generation, reduces the probability of hallucinations, and improves GAI speed and performance, as large volumes of information lead to longer wait times.</p><h3><strong>How Do We Achieve Data Cleaning?</strong></h3><p>To help you build your data cleaning toolbox, we’ll explore four NLP techniques and how they help the model.</p><h4>Step 1: Data Cleaning and Noise Reduction</h4><p>We’ll start by removing symbols or characters that don’t provide meaning, such as HTML tags (in the case of scraping), XML parses, JSON, emojis, and hashtags. Unnecessary characters often confuse the model, and increase the number of <a href="https://winder.ai/calculating-token-counts-llm-context-windows-practical-guide/#:~:text=In%20terms%20of%20the%20economy,the%20greater%20the%20computational%20cost.">context tokens</a> and therefore the computational cost.</p><p>Recognizing that there’s no one-size-fits-all solution, we’ll adapt our methods to different problems and text types using common cleaning techniques:</p><p>· <strong>Tokenization:</strong> Split the text into individual words or tokens.</p><p>· <strong>Remove noise:</strong> Eliminate unwanted symbols, emojis, hashtags, and Unicode characters.</p><p>· <strong>Normalization:</strong> Convert the text to lowercase for consistency.</p><p>· <strong>Remove stop words:</strong> Discard common or repeated words that do not add meaning, such as “a,” “in,” “of,” and “the.”</p><p>· <strong>Lemmatization or stemming:</strong> Reduce words to their base or root form.</p><p>Let’s take this tweet for example:</p><p>“<em>I love coding! 😊 #PythonProgramming is fun! 🐍✨ Let’s clean some text 🧹</em>”</p><p>While the meaning is clear to us, let’s simplify it for the model by applying common techniques in Python. The following code snippet and all others in this post were generated with the help of ChatGPT.</p><pre>import re<br>import nltk<br>from nltk.tokenize import word_tokenize<br>from nltk.corpus import stopword<br>s<br>from nltk.stem import WordNetLemmatizer<br><br># Sample text with emojis, hashtags, and other characters<br>text = “I love coding! 😊 #PythonProgramming is fun! 🐍✨ Let’s clean some text 🧹”<br><br># Tokenization<br>tokens = word_tokenize(text)<br><br># Remove Noise<br>cleaned_tokens = [re.sub(r’[^\w\s]’, ‘’, token) for token in tokens]<br><br># Normalization (convert to lowercase)<br>cleaned_tokens = [token.lower() for token in cleaned_tokens]<br><br># Remove Stopwords<br>stop_words = set(stopwords.words(‘english’))<br>cleaned_tokens = [token for token in cleaned_tokens if token not in stop_words]<br><br># Lemmatization<br>lemmatizer = WordNetLemmatizer()<br>cleaned_tokens = [lemmatizer.lemmatize(token) for token in cleaned_tokens]<br><br>print(cleaned_tokens)<br><br># output:<br># [‘love’, ‘coding’, ‘pythonprogramming’, ‘fun’, ‘clean’, ‘text’]</pre><p>The process has removed irrelevant characters and left us with clean and meaningful text our model can understand: [‘love’, ‘coding’, ‘pythonprogramming’, ‘fun’, ‘clean’, ‘text’].</p><h4><strong>Step 2: Text Standardization and Normalization</strong></h4><p>Next, we should always prioritize consistency and coherence across the text. This is crucial for ensuring accurate retrieval and generation. In the following Python example, let’s scan our text input for spelling errors and other inconsistencies that could lead to inaccuracies and decreased performance.</p><pre>import re<br><br># Sample text with spelling errors<br>text_with_errors = “””But ’s not  oherence  about more language  oherence . <br>Other important aspect is ensuring accurte retrievel by  oherence  product name spellings. <br>Additionally, refning descriptions  oherenc the  oherence of the contnt.”””<br><br># Function to correct spelling errors<br>def correct_spelling_errors(text):<br>    # Define dictionary of common spelling mistakes and their corrections<br>    spelling_corrections = {<br>        “ oherence ”: “everything”,<br>        “ oherence ”: “refinement”,<br>        “accurte”: “accurate”,<br>        “retrievel”: “retrieval”,<br>        “ oherence ”: “correcting”,<br>        “refning”: “refining”,<br>        “ oherenc”: “enhances”,<br>        “ oherence”: “coherence”,<br>        “contnt”: “content”,<br>    }<br><br>    # Iterate over each key-value pair in the dictionary and replace the<br>    # misspelled words with their correct versions<br>    for mistake, correction in spelling_corrections.items():<br>        text = re.sub(mistake, correction, text)<br><br>    return text<br><br># Correct spelling errors in the sample text<br>cleaned_text = correct_spelling_errors(text_with_errors)<br><br>print(cleaned_text)<br># output<br># But it’s not everything about more language refinement.<br># other important aspect is ensuring accurate retrieval by correcting product name spellings.<br># Additionally, refining descriptions enhances the coherence of the content.</pre><p>With a cohesive, coherent text representation, our model can now generate accurate and contextually relevant responses. This process also enables <a href="https://en.wikipedia.org/wiki/Semantic_search">semantic search</a> to extract the most optimal context chunks, particularly in the context of RAG.</p><h4><strong>Step 3: Metadata Handling</strong></h4><p>Metadata collection, such as identifying important keywords and <a href="https://www.techtarget.com/searchdatamanagement/definition/entity-relationship-diagram-ERD">entities</a>, makes it easy for us to recognize elements in the text that we can use to improve semantic search results, especially in enterprise applications such as content recommendation systems. This process provides the model with additional context, often required to improve RAG performance. Let’s apply this step to another Python example.</p><pre>Import spacy<br>import json<br><br># Load English language model<br>nlp = spacy.load(“en_core_web_sm”)<br><br># Sample text with meta data candidates<br>text = “””In a blog post titled ‘The Top 10 Tech Trends of 2024,’ <br>John Doe discusses the rise of artificial intelligence and machine learning <br>in various industries. The article mentions companies like Google and Microsoft <br>as pioneers in AI research. Additionally, it highlights emerging technologies <br>such as natural language processing and computer vision.”””<br><br># Process the text with spaCy<br>doc = nlp(text)<br><br># Extract named entities and their labels<br>meta_data = [{“text”: ent.text, “label”: ent.label_} for ent in doc.ents]<br><br># Convert meta data to JSON format<br>meta_data_json = json.dumps(meta_data)<br><br>print(meta_data_json)<br><br># output<br>“””<br>[<br>    {“text”: “2024”, “label”: “DATE”},<br>    {“text”: “John Doe”, “label”: “PERSON”},<br>    {“text”: “Google”, “label”: “ORG”},<br>    {“text”: “Microsoft”, “label”: “ORG”},<br>    {“text”: “AI”, “label”: “ORG”},<br>    {“text”: “natural language processing”, “label”: “ORG”},<br>    {“text”: “computer vision”, “label”: “ORG”}<br>]<br>“””</pre><p>The code highlights how <a href="https://spacy.io/">spaCy’s</a> <a href="https://spacy.io/universe/project/video-spacys-ner-model-alt">entity recognition capability</a> recognizes dates, persons, and organizations, and other important entities in the text. This helps RAG applications better understand context and relationships between words.</p><h4><strong>Step 4: Contextual Information Handling</strong></h4><p>When working with LLMs, you may commonly be working with diverse languages or managing extensive documents brimming with various topics, which can be hard for your model to comprehend. Let’s look at two techniques that can help your model better understand the data.</p><p>Let’s start with language translation. Using the <a href="https://codelabs.developers.google.com/codelabs/cloud-translation-python3">Google Translation API</a>, the code translates the original text, “Hello, how are you?” from English to Spanish.</p><pre>From googletrans import Translator<br><br># Original text<br>text = “Hello, how are you?”<br><br># Translate text<br>translator = Translator()<br>translated_text = translator.translate(text, src=’en’, dest=’es’).text<br><br>print(“Original Text:”, text)<br>print(“Translated Text:”, translated_text)</pre><p><a href="https://en.wikipedia.org/wiki/Topic_model">Topic modeling</a> including techniques like <a href="https://en.wikipedia.org/wiki/Cluster_analysis">clustering data</a>, is like organizing a messy room into neat categories, helping your model identify the topic of a document and sort through lots of information quickly. <a href="https://en.wikipedia.org/wiki/Latent_Dirichlet_allocation">Latent Dirichlet allocation (LDA)</a>, the most popular technique for automating the topic modeling process, is a statistical model that helps find hidden themes in text by looking closely at word patterns.</p><p>In the following example, we’ll use <a href="https://scikit-learn.org/stable/">sklearn</a> to process a set of documents and identify key topics.</p><pre>from sklearn.feature_extraction.text import CountVectorizer<br>from sklearn.decomposition import LatentDirichletAllocation<br><br># Sample documents<br>documents = [<br>    &quot;Machine learning is a subset of artificial intelligence.&quot;,<br>    &quot;Natural language processing involves analyzing and understanding human languages.&quot;,<br>    &quot;Deep learning algorithms mimic the structure and function of the human brain.&quot;,<br>    &quot;Sentiment analysis aims to determine the emotional tone of a text.&quot;<br>]<br><br># Convert text into numerical feature vectors<br>vectorizer = CountVectorizer(stop_words=&#39;english&#39;)<br>X = vectorizer.fit_transform(documents)<br><br># Apply Latent Dirichlet Allocation (LDA) for topic modeling<br>lda = LatentDirichletAllocation(n_components=2, random_state=42)<br>lda.fit(X)<br><br># Display topics<br>for topic_idx, topic in enumerate(lda.components_):<br>    print(&quot;Topic %d:&quot; % (topic_idx + 1))<br>    print(&quot; &quot;.join([vectorizer.get_feature_names()[i] for i in topic.argsort()[:-5 - 1:-1]]))<br><br># output<br>#<br>#Topic 1:<br>#learning machine subset artificial intelligence<br>#Topic 2:<br>#processing natural language involves analyzing understanding</pre><p>If you’d like to explore more topic modeling techniques, we recommend starting with these:</p><p>· <a href="https://en.wikipedia.org/wiki/Non-negative_matrix_factorization"><strong>Non-negative matrix factorization (NMF)</strong></a> is great for things like images where negative values don’t make sense. It’s handy when you need clear, understandable factors. For instance, in image processing, NMF helps extract features without the confusion of negative values.</p><p>· <a href="https://en.wikipedia.org/wiki/Latent_semantic_analysis"><strong>Latent semantic analysis (LSA)</strong></a> shines when you have a large volume of text spread across multiple documents and want to find connections between words and documents. LSA uses <a href="https://en.wikipedia.org/wiki/Singular_value_decomposition">singular value decomposition (SVD)</a> to identify semantic relationships between terms and documents, helping to streamline tasks like sorting documents by similarity and detecting plagiarism.</p><p>· <a href="https://en.wikipedia.org/wiki/Hierarchical_Dirichlet_process"><strong>Hierarchical Dirichlet process (HDP)</strong></a> helps you quickly sort through mountains of data and identify topics in a document when you’re unsure how many there are. As an extension of LDA, HDP allows for infinite topics and greater flexibility in modeling. It identifies hierarchical structures in text data for tasks like understanding the organization of topics in academic papers or news articles.</p><p>· <a href="https://en.wikipedia.org/wiki/Probabilistic_latent_semantic_analysis"><strong>Probabilistic latent semantic analysis (PLSA)</strong></a> helps you figure out how likely it is for a document to be about certain topics, which can be useful when building a recommendation system that provides personalized recommendations based on past interactions.</p><h3>DEMO: <strong>Cleaning a GAI Text Input</strong></h3><p>Let’s put it all together with an example. In this demo, we’ve used ChatGPT to generate a conversation between two technologists. We’ll apply basic cleaning techniques to the conversation to show how these practices enable reliable and consistent results.</p><pre>synthetic_text = &quot;&quot;&quot;<br>Sarah (S): Technology Enthusiast<br>Mark (M): AI Expert<br>S: Hey Mark! How&#39;s it going? Heard about the latest advancements in Generative AI (GA)?<br>M: Hey Sarah! Yes, I&#39;ve been diving deep into the realm of GA lately. It&#39;s fascinating how it&#39;s shaping the future of technology!<br>S: Absolutely! I mean, GA has been making waves across various industries. What do you think is driving its significance?<br>M: Well, GA, especially Retrieval Augmented Generative (RAG), is revolutionizing content generation. It&#39;s not just about regurgitating information anymore; it&#39;s about creating contextually relevant and engaging content.<br>S: Right! And with Machine Learning (ML) becoming more sophisticated, the possibilities seem endless.<br>M: Exactly! With advancements in ML algorithms like GPT (Generative Pre-trained Transformer), we&#39;re seeing unprecedented levels of creativity in AI-generated content.<br>S: But what about concerns regarding bias and ethics in GA?<br>M: Ah, the age-old question! While it&#39;s true that GA can inadvertently perpetuate biases present in the training data, there are techniques like Adversarial Training (AT) that aim to mitigate such issues.<br>S: Interesting! So, where do you see GA headed in the next few years?<br>M: Well, I believe we&#39;ll witness a surge in applications leveraging GA for personalized experiences. From virtual assistants to content creation tools, GA will become ubiquitous in our daily lives.<br>S: That&#39;s exciting! Imagine AI-powered virtual companions tailored to our preferences.<br>M: Indeed! And with advancements in Natural Language Processing (NLP) and computer vision, these virtual companions will be more intuitive and lifelike than ever before.<br>S: I can&#39;t wait to see what the future holds!<br>M: Agreed! It&#39;s an exciting time to be in the field of AI.<br>S: Absolutely! Thanks for sharing your insights, Mark.<br>M: Anytime, Sarah. Let&#39;s keep pushing the boundaries of Generative AI together!<br>S: Definitely! Catch you later, Mark!<br>M: Take care, Sarah!<br>&quot;&quot;&quot;</pre><h4><strong>Step 1: Basic Cleanup</strong></h4><p>First, let’s remove the emojis, hashtags, and Unicode characters from the conversation.</p><pre># Sample text with emojis, hashtags, and unicode characters<br><br># Tokenization<br>tokens = word_tokenize(synthetic_text)<br><br># Remove Noise<br>cleaned_tokens = [re.sub(r&#39;[^\w\s]&#39;, &#39;&#39;, token) for token in tokens]<br><br># Normalization (convert to lowercase)<br>cleaned_tokens = [token.lower() for token in cleaned_tokens]<br><br># Remove Stopwords<br>stop_words = set(stopwords.words(&#39;english&#39;))<br>cleaned_tokens = [token for token in cleaned_tokens if token not in stop_words]<br><br># Lemmatization<br>lemmatizer = WordNetLemmatizer()<br>cleaned_tokens = [lemmatizer.lemmatize(token) for token in cleaned_tokens]<br><br>print(cleaned_tokens)</pre><h4><strong>Step 2: Prepare Our Prompt</strong></h4><p>Next, we’ll craft a prompt, asking the model to respond as a friendly customer service agent based on information it gleaned from our synthetic conversation.</p><pre>MESSAGE_SYSTEM_CONTENT = &quot;You are a customer service agent that helps <br>a customer with answering questions. Please answer the question based on the<br>provided context below. <br>Make sure not to make any changes to the context if possible,<br>when prepare answers so as to provide accurate responses. If the answer <br>cannot be found in context, just politely say that you do not know, <br>do not try to make up an answer.&quot;</pre><h4><strong>Step 3: Prepare the Interaction</strong></h4><p>Let’s prepare our interaction with the model. In this example, we’ll use GPT-4.</p><pre>def response_test(question:str, context:str, model:str = &quot;gpt-4&quot;):<br>    response = client.chat.completions.create(<br>        model=model,<br>        messages=[<br>            {<br>                &quot;role&quot;: &quot;system&quot;,<br>                &quot;content&quot;: MESSAGE_SYSTEM_CONTENT,<br>            },<br>            {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: question},<br>            {&quot;role&quot;: &quot;assistant&quot;, &quot;content&quot;: context},<br>        ],<br>    )<br>    <br>    return response.choices[0].message.content</pre><h4><strong>Step 4: Prepare the Question</strong></h4><p>Finally, let’s ask the model a question and compare the results before and after cleaning.</p><pre>question1 = &quot;What are some specific techniques in Adversarial Training (AT) <br>that can help mitigate biases in Generative AI models?&quot;</pre><p>Before cleaning, our model generates this response:</p><pre>response = response_test(question1, synthetic_text)<br>print(response)<br><br>#Output<br># I&#39;m sorry, but the context provided doesn&#39;t contain specific techniques in Adversarial Training (AT) that can help mitigate biases in Generative AI models.</pre><p>After cleaning, the model generates the following response. With enhanced understanding enabled by basic cleaning techniques, the model can provide a more thorough answer.</p><pre>response = response_test(question1, new_content_string)<br>print(response)<br>#Output:<br># The context mentions Adversarial Training (AT) as a technique that can <br># help mitigate biases in Generative AI models. However, it does not provide <br>#any specific techniques within Adversarial Training itself.</pre><h4><strong>A Brighter Future for AI-Generated Outcomes</strong></h4><p>RAG models offer several advantages, including enhanced reliability and coherence of AI-generated results by providing relevant context. This contextualization significantly improves the accuracy of AI-generated content.</p><p>To get the most out of your RAG models, robust data cleaning techniques are essential during document ingestion. These techniques address discrepancies, imprecise terminology, and other potential errors within textual data, significantly improving the quality of input data. When operating on cleaner, more reliable data, RAG models deliver more accurate and meaningful results, enabling AI use cases with better decision-making and problem-solving capabilities across domains.</p><blockquote>Have you explored additional methods to improve RAG model performance? Let us know as we continue to refine and improve its capabilities.</blockquote><h3>About the Authors</h3><p><strong>Eduardo Rojas Oviedo, Platform Engineer, Intel</strong></p><p><a href="https://www.linkedin.com/in/eduardo-rojas-oviedo-56306923?originalSubdomain=cr">Eduardo Rojas Oviedo</a> is a dedicated RAG developer within Intel’s dynamic and innovative team. With a specialization in cutting-edge developer tools for AI, Machine Learning, and NLP, he is passionate about leveraging technology to create impactful solutions. Eduardo’s expertise lies in building robust and innovative applications that push the boundaries of what’s possible in the realm of artificial intelligence. His commitment to sharing knowledge and advancing technology drives his ongoing pursuit of excellence in the field.</p><p><strong>Ezequiel Lanza, Open Source AI Evangelist, Intel</strong></p><p><a href="https://www.linkedin.com/in/ezelanza/">Ezequiel Lanza</a> is an open source AI evangelist on Intel’s <a href="https://www.intel.com/content/www/us/en/developer/topic-technology/open/team.html">Open Ecosystem</a> team, passionate about helping people discover the exciting world of AI. He’s also a frequent AI conference presenter and creator of use cases, tutorials, and guides to help developers adopt open source AI tools. He holds an MS in data science. Find him on X at <a href="https://twitter.com/eze_lanza">@eze_lanza</a> and LinkedIn at <a href="https://www.linkedin.com/in/ezelanza/">/eze_lanza</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=77bee9003625" width="1" height="1" alt=""><hr><p><a href="https://medium.com/intel-tech/four-data-cleaning-techniques-to-improve-large-language-model-llm-performance-77bee9003625">Four Data Cleaning Techniques to Improve Large Language Model (LLM) Performance</a> was originally published in <a href="https://medium.com/intel-tech">Intel Tech</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>