mbudisic commited on
Commit
ff721db
Β·
1 Parent(s): a66160a

initial blogpost

Browse files
Files changed (1) hide show
  1. BLOGPOST.md +236 -0
BLOGPOST.md ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## πŸš€ Enhancing RAG: A Look at Our Initial Chunking Strategy and Timestamp Alignment 🧠
2
+
3
+ Hello everyone. πŸ‘‹ If you've worked with Retrieval Augmented Generation (RAG), you might have encountered situations where the AI's responses, while promising, didn't quite hit the mark. Perhaps they lacked specificity, missed crucial nuances, or included irrelevant information. A common factor influencing this is the **chunking strategy** – how we break down source documents for the model.
4
+
5
+ RAG significantly expands the capabilities of Large Language Models (LLMs) by allowing them to access custom knowledge bases. However, the effectiveness of the "retrieval" phase is heavily dependent on how this knowledge is segmented, or "chunked." An unsuitable chunking approach can lead to confusing or incomplete information being fed to the LLM. Conversely, a well-thought-out strategy can greatly improve precision and relevance.
6
+
7
+ This post will walk through the initial chunking methodology implemented in our [PsTuts RAG project](https://github.com/mbudisic/pstuts-rag/blob/main/). This system is designed to answer Adobe Photoshop queries using video tutorial transcripts. We'll examine why basic chunking can be insufficient and how our first iteration of semantic chunking, with a strong emphasis on aligning with original video timestamps, aims to provide a more robust solution. πŸ’‘
8
+
9
+ ---
10
+
11
+ ### πŸ€” The Challenge of Chunking: Balancing Size and Meaning
12
+
13
+ Consider how you would process a detailed instructional video. Would you prefer:
14
+
15
+ 1. Arbitrarily segmented, fixed-length transcript portions, potentially interrupting sentences or ideas?
16
+ 2. Complete thoughts or distinct instructional steps, even if their textual length varies?
17
+
18
+ Most would likely opt for the second choice. LLMs, in their own way, benefit from similarly coherent inputs.
19
+
20
+ **Common Chunking Methods & Their Limitations:**
21
+
22
+ * **Fixed-Size Chunking:** This approach divides text into segments of a predefined length (e.g., 200 words). While straightforward to implement, it often results in fragmented ideas, as natural semantic boundaries are ignored. Context can be lost, and the resulting chunks may not represent complete thoughts.
23
+ * **Sentence Splitting:** Dividing text by sentences is an improvement. However, a single sentence may not always encapsulate a full idea, particularly in complex material. Furthermore, several sentences might be closely related, forming a single, cohesive semantic unit that is best kept together.
24
+ * **Paragraph Splitting:** This method gets closer to ideal, as paragraphs often group related ideas. However, a single paragraph might still cover multiple distinct sub-topics, or one complex idea might be articulated across several shorter paragraphs.
25
+
26
+ The fundamental issue with these methods is their reliance on structural cues rather than semantic content. They don't deeply analyze the *meaning* of the text being divided. For a RAG system, this can mean that retrieved chunks offer only partial views of the necessary information or mix relevant details with irrelevant ones, leading to less than optimal LLM outputs.
27
+
28
+ ---
29
+
30
+ ### ✨ Introducing Semantic Chunking: Prioritizing Coherent Context
31
+
32
+ Semantic chunking aims to divide text based on its underlying meaning. Instead of relying on fixed lengths or simple punctuation, this method seeks to identify natural breakpoints where topics shift or distinct concepts are concluded. The objective is to produce chunks that are internally consistent and rich in context.
33
+
34
+ This is like identifying distinct "scenes" in a film script, rather than just cutting it every X lines. Each scene (or semantic chunk) carries a more complete segment of the narrative.
35
+
36
+ For the PsTuts RAG project, which uses video tutorial transcripts, this approach is particularly important. A Photoshop tutorial often involves multi-step processes. A semantic chunk ideally encapsulates one complete step, making it a well-defined unit of information for the LLM.
37
+
38
+ **Conceptual Basis:**
39
+ Semantic chunking frequently employs embedding models. These models transform text into numerical vectors that represent its meaning. By assessing the similarity between vectors of adjacent sentences or sentence groups, the system can detect points where semantic similarity decreases, suggesting a topic shift and a suitable location for a chunk boundary.
40
+
41
+ The [Langchain library](https://python.langchain.com/docs/get_started/introduction), a comprehensive framework for developing LLM applications, provides tools such as the `SemanticChunker`, which we utilize in our system.
42
+
43
+ ---
44
+
45
+ ### πŸ› οΈ Our First Iteration: Semantic Chunking with Timestamp Preservation
46
+
47
+ Let's examine the specifics of our initial implementation for the PsTuts video transcripts. A primary driver for this first version of our chunking strategy was the need to connect semantically coherent text segments back to their precise timings in the original videos. This feature is highly beneficial for users who may wish to navigate directly to the relevant moment in a tutorial.
48
+
49
+ You can explore the implementation details in our GitHub repository: [`mbudisic/pstuts-rag`](https://github.com/mbudisic/pstuts-rag/blob/main/).
50
+
51
+ **Step 1: Loading the Source Data πŸ“œ**
52
+
53
+ The process begins with ingesting our data. The video transcripts are initially in JSON format, with each entry containing a spoken sentence and its corresponding start and end timestamps.
54
+
55
+ We employ two primary loaders, located in `pstuts_rag/pstuts_rag/loader.py` ([view on GitHub](https://github.com/mbudisic/pstuts-rag/blob/main/pstuts_rag/pstuts_rag/loader.py)):
56
+
57
+ 1. `VideoTranscriptChunkLoader`: This loader processes the JSON input to create individual `Document` objects for each sentence (or small verbatim segment from the transcript). Critically, it retains the `time_start` and `time_end` metadata for every sentence. These serve as our fundamental temporal reference points.
58
+
59
+ ```python
60
+ # Excerpt from VideoTranscriptChunkLoader in loader.py
61
+ # ...
62
+ # for transcript in transcripts:
63
+ # yield Document(
64
+ # page_content=transcript["sent"],
65
+ # metadata=metadata
66
+ # | {
67
+ # "time_start": transcript["begin"],
68
+ # "time_end": transcript["end"],
69
+ # },
70
+ # )
71
+ ```
72
+
73
+ 2. `VideoTranscriptBulkLoader`: This loader adopts a broader perspective. For each video, it concatenates all its sentences into a single `Document`. This provides the complete, continuous text of each tutorial, which serves as the input for the semantic chunker. Feeding the chunker individual sentences would deprive it of the wider context needed to identify meaningful breakpoints that span multiple sentences.
74
+
75
+ ```python
76
+ # Excerpt from VideoTranscriptBulkLoader in loader.py
77
+ # ...
78
+ # yield Document(
79
+ # page_content="\n".join(
80
+ # t["sent"] for t in video["transcripts"]
81
+ # ),
82
+ # metadata=metadata,
83
+ # )
84
+ ```
85
+
86
+ This dual loading approach provides two representations of our data: a fine-grained, sentence-level view with precise timestamps, and a comprehensive, full-transcript view suitable for semantic division.
87
+
88
+ **Step 2: Semantic Segmentation 🧠πŸ”ͺ**
89
+
90
+ This stage is handled primarily by the `chunk_transcripts` function in `pstuts_rag/pstuts_rag/datastore.py` ([view on GitHub](https://github.com/mbudisic/pstuts-rag/blob/main/pstuts_rag/pstuts_rag/datastore.py)).
91
+
92
+ We provide the `docs_full_transcript` (from `VideoTranscriptBulkLoader`) to Langchain's `SemanticChunker`. We utilize `OpenAIEmbeddings` (specifically `text-embedding-3-small`) for this process, as these embeddings enable the chunker to interpret the semantic content of the text.
93
+
94
+ ```python
95
+ # Excerpt from chunk_transcripts in datastore.py
96
+ # ...
97
+ # text_splitter = SemanticChunker(semantic_chunker_embedding_model)
98
+ # docs_group = await asyncio.gather(
99
+ # *[
100
+ # text_splitter.atransform_documents(d)
101
+ # for d in batch(docs_full_transcript, size=2) # Batching for efficiency
102
+ # ]
103
+ # )
104
+ # # Flatten the nested list of documents
105
+ # docs_chunks_semantic: List[Document] = []
106
+ # for group in docs_group:
107
+ # docs_chunks_semantic.extend(group)
108
+ # ...
109
+ ```
110
+
111
+ The `SemanticChunker` intelligently divides the long transcript of each video into a series of smaller, semantically related chunks. Each of these `docs_chunks_semantic` aims to represent a distinct idea or step from the tutorial. The user described this goal as creating "semantic Kamradt chunks"β€” a term reflecting the aspiration for these idea-units, though "Kamradt chunking" itself isn't a standard Langchain term.
112
+
113
+ At this stage, our semantic chunks are textually defined. However, they lack a crucial piece of information for video-based content: **timing**. A user inquiring, "How do I use the clone stamp tool?" would benefit not only from the textual explanation but also from knowing *where* in the video that explanation is located.
114
+
115
+ **Step 3: Aligning Semantic Chunks with Timestamps πŸ•°οΈπŸ”—**
116
+
117
+ This is a key part of our initial strategy: establishing a connection between the semantically defined chunks and the original, timestamped sentences. The goal is to determine which of our original, granular sentences (from `VideoTranscriptChunkLoader`) constitute each new semantic chunk.
118
+
119
+ Within the `chunk_transcripts` function (`datastore.py`), we iterate through each semantic chunk (`docs_chunks_semantic`). For each one, we reference our collection of original, timestamped sentences (`docs_chunks_verbatim`):
120
+
121
+ ```python
122
+ # Excerpt from chunk_transcripts in datastore.py
123
+ # ...
124
+ # # Create a lookup dictionary for faster access to verbatim chunks by video_id
125
+ # video_id_to_chunks: Dict[int, List[Document]] = {}
126
+ # for chunk_v in docs_chunks_verbatim:
127
+ # video_id: int = chunk_v.metadata["video_id"]
128
+ # if video_id not in video_id_to_chunks:
129
+ # video_id_to_chunks[video_id] = []
130
+ # video_id_to_chunks[video_id].append(chunk_v)
131
+
132
+ # for chunk_s in docs_chunks_semantic: # Our new semantic chunk
133
+ # video_id = chunk_s.metadata["video_id"]
134
+ # # Only check verbatim chunks from the same video
135
+ # potential_subchunks = video_id_to_chunks.get(video_id, [])
136
+ # subchunks = [
137
+ # c
138
+ # for c in potential_subchunks
139
+ # if c.page_content in chunk_s.page_content # Direct search for sentence text
140
+ # ]
141
+ # ...
142
+ ```
143
+
144
+ The line `if c.page_content in chunk_s.page_content` is pivotal. It operates on the premise that the text of an original, timestamped sentence will be present within the text of the larger semantic chunk it belongs to. This "direct search" is effective because the semantic chunk is typically a concatenation or superset of several original sentences.
145
+
146
+ After identifying all original sentences (`subchunks`) that comprise a given semantic chunk, we extract their timestamps:
147
+
148
+ ```python
149
+ # Excerpt from chunk_transcripts in datastore.py
150
+ # ...
151
+ # times = [
152
+ # (t.metadata["time_start"], t.metadata["time_end"])
153
+ # for t in subchunks
154
+ # ]
155
+ # chunk_s.metadata["speech_start_stop_times"] = times # Store all individual sentence times
156
+
157
+ # if times: # Check if times list is non-empty
158
+ # chunk_s.metadata["start"], chunk_s.metadata["stop"] = (
159
+ # times[0][0], # Start time of the first sentence in the semantic chunk
160
+ # times[-1][-1], # End time of the last sentence in the semantic chunk
161
+ # )
162
+ # else:
163
+ # chunk_s.metadata["start"], chunk_s.metadata["stop"] = None, None
164
+ # ...
165
+ ```
166
+
167
+ As a result, each semantic chunk is enriched with:
168
+ * `speech_start_stop_times`: A list of (start, end) time tuples for every original sentence it incorporates.
169
+ * `start`: The start time of the very first sentence within the semantic chunk.
170
+ * `stop`: The end time of the very last sentence within the semantic chunk.
171
+
172
+ This metadata is highly valuable. When our RAG system retrieves a semantic chunk, it receives not only a coherent piece of information but also precise timing data, allowing the user to navigate directly to that segment in the source video.
173
+
174
+ **Step 4: Preparing for Retrieval by Storing in a Vector Database πŸ’Ύ**
175
+
176
+ Once our documents are chunked and timestamped, they are vectorized (again, using `OpenAIEmbeddings`) and stored in our chosen vector database, Qdrant. This process is overseen by the `DatastoreManager` class, also found in `pstuts_rag/pstuts_rag/datastore.py`.
177
+
178
+ ```python
179
+ # Excerpt from DatastoreManager in datastore.py
180
+ # ...
181
+ # async def populate_database(self, raw_docs: List[Dict[str, Any]]) -> int:
182
+ # # Perform chunking (which includes timestamp association)
183
+ # self.docs: List[Document] = await chunk_transcripts(
184
+ # json_transcripts=raw_docs,
185
+ # semantic_chunker_embedding_model=self.embeddings,
186
+ # )
187
+ # # ... then perform embedding and upload to Qdrant ...
188
+ # ...
189
+ ```
190
+
191
+ The `DatastoreManager` is responsible for generating vector embeddings for these enriched chunks and indexing them in Qdrant, making them efficiently searchable for the RAG system.
192
+
193
+ ---
194
+
195
+ ### πŸ† Benefits of This Initial Approach
196
+
197
+ Why adopt this specific methodology for our first iteration?
198
+
199
+ 1. **Improved Relevance:** Semantic chunks aim to provide LLMs with more complete and contextually sound information. This can lead to more relevant and accurate responses compared to simpler chunking methods.
200
+ 2. **Enhanced User Navigation:** For video content, associating chunks with timestamps is a significant usability improvement. Users can be directed to the precise moment in a tutorial where the information is presented, saving time and effort.
201
+ 3. **Efficient Use of Context Window:** LLMs operate with a finite context window. Semantically coherent chunks help make better use of this limited space by providing meaningful information rather than fragmented text.
202
+ 4. **Foundation for Reduced Errors:** When LLMs receive better, more focused context, they may be less prone to generating incorrect or unsupported information. The retrieved chunks serve as stronger grounding.
203
+ 5. **Adaptability for Complex Material:** As the length and complexity of source documents (or videos) grow, the advantages of a semantic approach to chunking generally become more apparent.
204
+
205
+ In the PsTuts RAG system, this initial strategy means that user queries can be answered with text that not only reflects the tutorial content accurately but is also linked directly to the corresponding segments in the video.
206
+
207
+ ---
208
+
209
+ ### πŸ€” Points to Consider and Future Refinements
210
+
211
+ While this initial approach offers advantages, there are several aspects to consider for ongoing development:
212
+
213
+ * **Computational Resources:** Semantic chunking, particularly when using embeddings, generally requires more computational resources upfront compared to fixed-size splitting. The use of `asyncio` and batch processing in our implementation helps manage this.
214
+ * **Choice of Embedding Model:** The effectiveness of semantic chunking is influenced by the chosen embedding model. Our current use of `text-embedding-3-small` represents a balance between performance and cost; however, different datasets or requirements might benefit from exploring other models.
215
+ * **Chunker Parameter Tuning:** The `SemanticChunker` in Langchain offers parameters (such as breakpoint thresholds) that can be adjusted. Optimizing these for specific datasets may involve some experimentation.
216
+ * **Assumption of Textual Containment:** The timestamp association method relies on the `page_content` of the original sentences being directly present within the `page_content` of the semantic chunk. This generally holds for how `SemanticChunker` aggregates text. However, alternative chunking methods that perform more aggressive summarization might require a different association logic.
217
+ * **Iteration and Evaluation:** This is our first iteration of the chunking strategy. Future work will involve evaluating its performance with robust metrics (e.g., RAGAS) and exploring alternative or more advanced chunking techniques to further enhance retrieval quality.
218
+
219
+ ---
220
+
221
+ ### 🎬 Conclusion: A Pragmatic First Step in Chunking for RAG
222
+
223
+ The method used to prepare and structure data for RAG systems is a critical determinant of their performance. While simpler chunking techniques are quicker to implement, investing in a more meaning-oriented strategy like semantic chunking, especially when combined with essential domain-specific features like timestamp association, can significantly improve the utility of a RAG application.
224
+
225
+ For developers building RAG systems, particularly those dealing with multimedia or structured content where source linking and contextual integrity are important, it is worthwhile to look beyond basic splitting methods. Exploring semantic chunking and carefully considering how to preserve and leverage metadata can lead to a more effective and user-friendly system.
226
+
227
+ Our initial approach for the PsTuts RAG project, focusing on semantic coherence and robust timestamp linking, represents a pragmatic first step. The journey from raw transcript to a usefully chunked and timestamped piece of information in our vector store involves several coordinated steps, each aimed at maximizing the relevance and utility of the data for the end-user.
228
+
229
+ We encourage you to review the implementation in the [PsTuts RAG project on GitHub](https://github.com/mbudisic/pstuts-rag/blob/main/). As we continue to develop this project, we will be exploring further refinements to this process.
230
+
231
+ What are your experiences with chunking for RAG? Any particular challenges or successes you'd like to share? We welcome your thoughts in the comments section. πŸ‘‡
232
+
233
+ #RAG #AI #LLM #SemanticChunking #VectorDatabase #Qdrant #Langchain #Python #Developer #DataScience #MachineLearning #PsTutsRAG
234
+
235
+ ---
236
+ *Note: The term "Kamradt chunking" was used by the project owner to describe the desired outcome of creating semantically coherent, idea-based chunks, similar to the objective of Langchain's `SemanticChunker`.*