<?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"><channel><title><![CDATA[Guillaume Guy]]></title><description><![CDATA[Guillaume Guy]]></description><link>https://www.guillaume.nyc</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Apr 2026 20:22:42 GMT</lastBuildDate><atom:link href="https://www.guillaume.nyc/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What is CLIP and why does it matter?]]></title><description><![CDATA[Paper: Learning Transferable Visual Models From Natural Language Supervision, Radford et al. (2021)
Introduction
As we discussed in previous posts, Contrastive Learning isn't new. For example, FAIR's Moco was published in 2019, along with many other ...]]></description><link>https://www.guillaume.nyc/what-is-clip-and-why-does-it-matter</link><guid isPermaLink="true">https://www.guillaume.nyc/what-is-clip-and-why-does-it-matter</guid><category><![CDATA[CLIP ]]></category><category><![CDATA[#multimodalai]]></category><category><![CDATA[alignment]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Mon, 12 May 2025 14:27:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747060012664/a54aed5d-b172-4715-9248-c6b4183cb17f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://arxiv.org/abs/2103.00020">Paper</a>: Learning Transferable Visual Models From Natural Language Supervision, Radford et al. (2021)</p>
<h1 id="heading-introduction">Introduction</h1>
<p>As we discussed in previous posts, Contrastive Learning isn't new. For example, FAIR's Moco was published in 2019, along with many other papers. However, CLIP introduces something new. Let's take a step back and look at the landscape at the time of its publication.</p>
<p>Around 2021, there were two major areas of work in computer vision:</p>
<ul>
<li><p>Well-structured predictions using classes (e.g., ImageNet)</p>
</li>
<li><p>Self-Supervised Learning, which removes the need for expensive labels</p>
</li>
</ul>
<p>Meanwhile, the team at OpenAI noticed that the web had over a billion untapped images with captions that hadn't been used due to the challenges of handling natural language. For instance, Li et al. (2017) tried to predict phrase n-grams based on photos, but the approach didn't generalize well, leading to low scores on ImageNet.</p>
<p>The question was clear: how can this web data be used to train effective, generalist computer vision models? And can we leverage both text and image modalities?</p>
<h1 id="heading-modality-alignment">Modality alignment</h1>
<p>The main claim of this seminal paper is twofold:</p>
<ul>
<li><p>One can approach the challenge as a modality alignment problem i.e. a shared latent space</p>
</li>
<li><p>Trained on the web, the resulting model gives good zero-shot performance across a variety of tasks i.e. generalize well.</p>
</li>
</ul>
<p>On the first claim, their architecture is relatively simple: they put 2 encoders (one for each modality). Note that they do not do any decoding (i.e. asking to generate the caption from the image). Instead, their objective is to “align” these 2 encoders. For instance, the picture of the Aussie dog (see below) is in the same vicinity in the latent space as the caption “Pepper the aussie pup”. 2 encoders, 1 shared latent space.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746807491411/726d2a90-cf1f-4805-87a3-60b1bc84f082.png" alt class="image--center mx-auto" /></p>
<p>On the 2nd claim, they show that, the model provides rich semantic representation and beats self supervised method such as SimCLRv2 on downstream classification with zero-shot (i.e. no training), which is an incredible claim. Further, with the same number of training examples, CLIP is 10pp more performant.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746807807268/825f74fe-a88b-4476-a457-422c67784312.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-why-does-clip-matter">Why does CLIP matter?</h1>
<p>CLIP is the first computer vision architecture to "mine the web," effectively overcoming the challenges of poor data quality. Its strength lies in its ability to generalize across a wide range of domains, making it a strong choice for your vision backbone.</p>
<p>Secondly, the photo embeddings are semantically supervised, making them suitable for giving vision to LLMs (more details in my blog post <a target="_blank" href="https://www.guillaume.nyc/how-did-llms-gain-vision">here</a>). As a matter of fact, CLIP has become the main (only?) vision backbone for LLMs.</p>
<p>Finally, it democratizes image search. By encoding all images and adding them to a vector store (e.g., <a target="_blank" href="https://github.com/facebookresearch/faiss">FAISS</a>), one can find images similar to a text or image prompt. This technique has become popular because it eliminates the need for complex infrastructure.</p>
<p>It is no surprise that AI labs continue to invest heavily in CLIP-style learning. For instance, Google recently released <a target="_blank" href="https://arxiv.org/abs/2502.14786">SIGLIP-2</a>, which incorporates additional losses to address some weaknesses. For example, CLIP is not well-suited for segmentation and depth estimation tasks, so the authors added <a target="_blank" href="https://arxiv.org/abs/2403.19596">LocCa</a> tasks. FAIR expanded to many modalities with <a target="_blank" href="https://ai.meta.com/research/publications/imagebind-one-embedding-space-to-bind-them-all/">ImageBind</a>.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Go check it out on <a target="_blank" href="https://huggingface.co/blog/siglip2#encode-images-for-downstream-tasks">HuggingFace</a>. SigLIP-2 is open weights and licensed under a permissive Apache-2.</p>
]]></content:encoded></item><item><title><![CDATA[How did LLMs gain Vision?]]></title><description><![CDATA[Introduction
LLMs were created as text-only chat bots as an artifact of their training paradigm: they learn to predict the next token (~word). Photos, videos and other richer media don’t have words and therefore were naturally excluded from the train...]]></description><link>https://www.guillaume.nyc/how-did-llms-gain-vision</link><guid isPermaLink="true">https://www.guillaume.nyc/how-did-llms-gain-vision</guid><category><![CDATA[llm]]></category><category><![CDATA[CLIP ]]></category><category><![CDATA[Vision]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Mon, 05 May 2025 15:11:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746457907765/c19ca555-84a2-4893-b8b3-534f4aea78ad.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>LLMs were created as text-only chat bots as an artifact of their training paradigm: they learn to predict the next token (~word). Photos, videos and other richer media don’t have words and therefore were naturally excluded from the training protocol.</p>
<p>However, most LLMs are now multimodal. How did that happen?</p>
<h1 id="heading-image-as-cross-encoded-signals">Image as Cross-Encoded signals</h1>
<p><a target="_blank" href="https://arxiv.org/abs/2204.14198">Paper</a></p>
<p>One of the early papers to explore multi-modality is Deepmind’s Flamingo from 2021. Their technique consisted in interleaving text and photos, and having the LLM complete the sentence by following the pattern.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746200797427/5a89d7b5-5da9-4ab2-8902-97f4d4e2f6c7.png" alt class="image--center mx-auto" /></p>
<p>Their architecture is relatively simple and can be summarized in 3 points (*):</p>
<ul>
<li><p>Keep the Vision Encoder and the language model frozen (the implicit assumption is that both are “multimodal-ready” since they are both semantically supervised so the only missing part to make the system work is the “bridge” between the 2 “languages” ). This set up reduces greatly the number of learnt parameters</p>
</li>
<li><p>Add a &lt;image&gt; (learnt) token to represent the location of the n-th photo.</p>
</li>
<li><p>For each layer of the LM, add a cross-attention to attend to the photo(s) representation</p>
</li>
</ul>
<p><em>(\</em>) leaving out the Perceiver Resampler, which is an interesting component but not central to the architecture. More details in the paper*</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746200852328/66aae659-5573-4eeb-9006-47b56c317372.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-image-as-a-dense-token">Image as a dense token</h1>
<p><a target="_blank" href="https://arxiv.org/pdf/2407.07726">Paper</a></p>
<p>PaliGemma, a recent 3B model released by Google in 2024, shows another way to integrate vision signals. It uses the output from the Image Encoder as dense tokens, which can be combined with text tokens after a projection. This way, the LLM encounters new dense tokens that aren't part of the token dictionary and can handle them just like text tokens.</p>
<p><img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/paligemma/paligemma_arch.png" alt="Architecture" /></p>
<p>The paper is very accessible and full of useful details for replication. Specifically, the authors added a clear step-by-step detailed recipe:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746202662044/ffc14c5b-7912-4ee8-bfb4-cf8835aaa625.png" alt class="image--center mx-auto" /></p>
<p>The above architecture is interesting in several ways:</p>
<ul>
<li><p>First, it eliminates the cross-attention system, which is computationally demanding because it is added to each layer of the LLM.</p>
</li>
<li><p>Instead, the authors use a simple "Linear Projection," a technique previously used by <a target="_blank" href="https://arxiv.org/pdf/2310.03744">LLaVA</a>.</p>
</li>
<li><p>The authors allowed the Image Encoder to be fine-tuned, stating that it improves performance in the "blind spot" of its contrastive pretraining, specifically in relation and localization (*).</p>
</li>
</ul>
<p>(*) It would be interesting to see if this remains true, as SigLIP-2 tried to address these blind spots by adding extra losses to capture these tasks.What matters when tuning an VLM?</p>
<h1 id="heading-what-matters-when-training-a-vlm">What matters when training a VLM?</h1>
<p><a target="_blank" href="https://arxiv.org/pdf/2405.02246">paper</a></p>
<p>In 2024, the team at HuggingFace aimed to find out what matters when tuning a VLM. They reached the following conclusions:</p>
<ul>
<li><p>Enhancements in pretraining (for LLM and vision encoders) result in better performance in later tasks (Finding #1).</p>
</li>
<li><p>Cross-attention generally provides better performance than fully auto-regressive models, but it requires 10% more computing power. However, this difference disappears when using LoRA adapters (Finding #2).</p>
</li>
</ul>
<p>The paper also contains many other insights, such as the link between image splitting and performance on reading tasks.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>We compared two major early architectures for integrating vision into LLMs that have been popular up to 2024. With "native LLMs," we expect to see more co-trained multimodal models in the future. Last year, GPT-4o pioneered this approach, but the architecture and training details are still unclear. One <a target="_blank" href="https://www.reddit.com/r/MachineLearning/comments/1crzdhd/comment/l42cqhd/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">hypothesis</a> supported by Gwern suggests it uses a <a target="_blank" href="https://arxiv.org/abs/1711.00937">VQ-VAE</a> to tokenize photos and integrate them into their token dictionary. With interleaved data, the LLM's autoregressive training can be applied "natively," meaning the LLM will need to predict tokens in either modality. Only time will tell.</p>
]]></content:encoded></item><item><title><![CDATA[The state of Self-Supervision for Vision]]></title><description><![CDATA[Introduction
To perform vision tasks effectively, it's important to have a strong, general-purpose vision backbone. This allows you to handle many vision tasks, such as:

Image-to-image similarity: For comparison or retrieval.

Vision adapters: Add a...]]></description><link>https://www.guillaume.nyc/the-state-of-self-supervision-for-vision</link><guid isPermaLink="true">https://www.guillaume.nyc/the-state-of-self-supervision-for-vision</guid><category><![CDATA[vision backbone]]></category><category><![CDATA[self supervised learning]]></category><category><![CDATA[Vision]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Thu, 01 May 2025 15:38:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745854244095/ecf5a59a-c51a-4b6b-8fff-bd71bed8eb64.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>To perform vision tasks effectively, it's important to have a strong, general-purpose vision backbone. This allows you to handle many vision tasks, such as:</p>
<ul>
<li><p><strong>Image-to-image similarity:</strong> For comparison or retrieval.</p>
</li>
<li><p><strong>Vision adapters:</strong> Add a classification head to solve specific problems (e.g., cat vs. dog classification).</p>
</li>
<li><p><strong>Fine-tuning:</strong> Adapt the backbone to a specific problem (similar to adapters, usually with higher accuracy but requires recompiling the entire backbone).</p>
</li>
<li><p><strong>Giving vision to LLM:</strong> By feeding the vision hidden state to the LLM (see <a target="_blank" href="https://huggingface.co/blog/paligemma">PaliGemma</a> as an example), the LLM can learn to interpret images.</p>
</li>
</ul>
<p>The backbone serves as a starting point for downstream activities. The better the backbone, the better the performance on these tasks.</p>
<p>Currently, there's a lot of excitement about CLIP, which is a method to semantically supervise the hidden state (using "words"). It tends to deliver great performance, easier integration with LLMs (since LLMs are semantic machines), and zero-shot capabilities (allowing image retrieval based on text-based queries). I will write a post on this topic later, but for now, let's discuss self-supervision (usually shortened to SSL)..</p>
<h1 id="heading-selective-history-of-self-supervision">Selective history of Self-supervision</h1>
<p>Let’s investigate a few seminal papers to see where we’re coming from.</p>
<h2 id="heading-1-unsupervised-visual-representation-learning-by-context-prediction-2015"><strong>1- Unsupervised Visual Representation Learning by Context Prediction, 2015</strong></h2>
<p><a target="_blank" href="https://arxiv.org/pdf/1505.05192">Paper</a></p>
<p>The authors (Doersch et al) formulated the self supervision task as that of a puzzle: Split the image into patches and have the model predict where a given patch should be slotted relative to a reference patch. This is called a “<em>pretext task</em>” (meaning, it’s not the overarching objective of the model, but rather a way to force the model to train on a task that will be useful for other applications).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745855785921/fbfba9ee-a194-4846-b158-c968ce2e533e.png" alt class="image--center mx-auto" /></p>
<p>The model is of a <a target="_blank" href="https://en.wikipedia.org/wiki/Siamese_neural_network">Siamese type</a> as it takes in 2 patches, processes them (with the same weights) and has a late fusion at the end to compute the predicted location of the 2nd patch.</p>
<p>The most fascinating finding of this paper is that the model, despite having no semantic information about the photos or patches, learns <em>mid-level semantic structures</em> (e.g., distinguishing objects and surfaces) even though the training signal is purely geometric. For instance, the below figure shows an uncanny ability to discern fixtures in a city. We can surmise that the model builds its own internal conceptual representation of the world.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745856157931/caad45f0-2e83-435d-92fe-a83de66bd531.png" alt class="image--center mx-auto" /></p>
<p>Finally, the authors shows that these trained models can be helpful for many downstream applications such as object detection and Geometry Estimation (section 4).</p>
<p>However, because the task is local (patch-level), it can bias the model toward capturing only short-range dependencies rather than full object understanding.</p>
<p><strong>Top 1 accuracy on ImageNet-1K</strong>: 51.4% @ 100M params (<a target="_blank" href="https://arxiv.org/pdf/1911.05722">source</a>)</p>
<h2 id="heading-2-moco-momentum-contrast-for-unsupervised-visual-representation-learning-2020">2- MOCO: Momentum Contrast for Unsupervised Visual Representation Learning, 2020</h2>
<p><a target="_blank" href="https://arxiv.org/pdf/1911.05722">Paper</a>, <a target="_blank" href="https://github.com/facebookresearch/moco">code</a></p>
<p>Moco’s core improvement is in the realm of “<em>hard negative mining”</em> strategies, which aim to select negatives that maximize learning efficiency.</p>
<p>To improve contrastive learning, MOCO’s proposes to bring in 2 innovations:</p>
<ul>
<li><p><strong>Momentum Encoder:</strong> A slow-moving copy of the query encoder, updated via exponential moving average, ensuring the encoded keys evolve smoothly rather than changing rapidly.</p>
</li>
<li><p><strong>Queue:</strong> A dynamically maintained FIFO queue of previous embeddings (keys), allowing the dictionary of negatives to be much larger than the minibatch size without prohibitive memory cost. The queue resembles a memory bank but is simpler and fresher: it maintains only a limited number of recent embeddings, without associating them to specific dataset indices.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745868719151/f1c204bc-653c-4559-ba0c-b04f503ed7fc.png" alt class="image--center mx-auto" /></p>
<p>So, let’s sum it up. The model needs positive and negatives</p>
<ul>
<li><p><strong>Positives</strong>: A positive pair consists of two random augmentations of the same image: one passed through the query encoder (actively updated) and one through the momentum encoder (slow-moving copy).</p>
</li>
<li><p><strong>Negative</strong>: Anything in queue</p>
</li>
</ul>
<p>What’s the current challenge?</p>
<ul>
<li>Traditional end-to-end contrastive learning depends on large batch sizes (to supply many negatives), which quickly run into GPU memory limits.</li>
</ul>
<p>What’s MOCO contribution?</p>
<ul>
<li>By decoupling the dictionary size from the minibatch size (via a queue) and ensuring representation consistency (via a momentum encoder), MoCo replicates and exceeds the performance of large-batch end-to-end contrastive learning — scaling up to queues with 65k negatives.</li>
</ul>
<p>Very cool!</p>
<p><strong>Top 1 accuracy on ImageNet-1K</strong>: 65.4% @ 100M params (<a target="_blank" href="https://arxiv.org/pdf/1911.05722">source</a>)</p>
<h2 id="heading-3-simclr-2020">3- SimCLR, 2020</h2>
<p><a target="_blank" href="https://arxiv.org/pdf/2002.05709">Paper</a></p>
<p>Some papers achieve higher performance by adding complexity; the best ones achieve both higher performance and <em>lower</em> complexity. SimCLR belongs to the latter: it discards specialized components like memory banks and momentum encoders, offering a simpler, more scalable framework for contrastive learning.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746112963795/897146b4-33ac-4fb7-ba51-a310794c7515.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Figure: Overall architecture of the SimCLR contrastive learning framework</p>
</blockquote>
<p>What is SimCLR’s contribution?</p>
<ul>
<li><p>First, it introduces a <strong>simplified contrastive learning pipeline</strong>: given an image, two independent but carefully selected augmentations are applied to create a positive pair. A single model (no 2nd momemtum encoder) is trained to distinguish this pair from all others in a large batch (typically 8k examples), treating all other examples as negatives (i.e. contrastive learning).</p>
</li>
<li><p>To further improve learned representations, SimCLR <strong>decouples the encoder</strong> from a lightweight <strong>projection head</strong> used only during training. The contrastive loss is applied to the outputs of this projection head, allowing the encoder to retain richer, more transferable features. This architectural tweak significantly boosts linear evaluation performance (e.g., from ~50% to ~65% Top-1 on ImageNet-1K).</p>
</li>
<li><p>Critically, SimCLR <strong>systematically studies the role of data augmentation</strong>. They find that augmentation strategies are not equally effective: combinations like random cropping plus color jittering dramatically outperform others (e.g., crop+color achieving 56% vs Sobel+rotate yielding only 4%).</p>
</li>
</ul>
<p>Let’s dive in the “set of transformers” mentioned above. The table below is an analysis of different augmentation compositions and shows that they are not all equal!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745938644518/1ec33abb-f171-46a1-8998-83b3a56d6319.png" alt class="image--center mx-auto" /></p>
<p>Overall, SimCLR delivers a substantial performance improvement — almost <strong>10 percentage points</strong> over previous contrastive methods — while simplifying the overall framework.</p>
<p><strong>Top 1 accuracy on ImageNet-1K</strong>: 69% @ 100M params (<a target="_blank" href="https://arxiv.org/pdf/2002.05709">source</a>)</p>
<h2 id="heading-4-dino-2021">4- Dino, <em>2021</em></h2>
<p><a target="_blank" href="https://arxiv.org/pdf/2104.14294">Paper</a>, <a target="_blank" href="https://github.com/facebookresearch/dino">code</a></p>
<p>Finally, let’s talk about Dino from FAIR.</p>
<p>Dino approaches the problem of Self Supervision (SSL) as a distillation problem i.e. having 2 models (teacher + student) where the student learns from the teacher. This paradigm is common in the LLM space when the teacher is a much larger, pretrained model, and needs to be shrunk for inference efficiency purpose (denoted “mini” in the OpenAI parlance). However, in DINO, the teacher starts from scratch too and improves over time. A more descriptive term would therefore be "pair-learning with a centered partner".</p>
<p>Ok, let’s talk about the DINO’s contributions now:</p>
<ul>
<li><p><strong>Self Supervision as a Distillation problem</strong>: The role of the student is to match the Teacher’s distribution of scores. The teacher is also learning through a momentum update (its weights are averaged with the student’s scaled with a small coefficient)</p>
</li>
<li><p><strong>Avoiding collapse without using contrastive loss.</strong> Instead, they “center and sharpen” the teacher: Dino found a surprising simple rule: They simply <strong>center</strong> (subtract the moving average in the latent space) to avoid 1 dimension from dominating and <strong>sharpen</strong> (i.e. softmax with low temperature) to avoid a uniform distribution.</p>
</li>
</ul>
<p>Now, how does the teacher “learn”? Its weights are actually a exponential moving average of the students’ (no SGD update)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746111696013/33b79410-8eba-46b1-8958-a504093b9bb8.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Figure: Diagram of the DINO training protocol</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746111874215/6d575623-0945-4dec-bfc3-7cf27a4735ce.png" alt class="image--center mx-auto" /></p>
<p>One interesting aspect is that the Teacher always keeps an edge on the student as shown in the figure above. The reason might be that the student’s role is to explore while the teacher keeps the student “on the right tracks” (through the momentum mechanism).</p>
<p><strong>Top 1 accuracy on ImageNet-1K</strong>: 75% @ 23M params, ResNet-50 (<a target="_blank" href="https://arxiv.org/pdf/2104.14294">source</a>)</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Doing a large paper review over a large time range always bring an interesting perspective on the progression of the field as the high level view sorts out the important from the unimportant. Looking back at these papers, a few key innovations strike me as important:</p>
<ul>
<li><p>At the highest level, SSL + classification has kept pace with full supervised learning. This is a major advantage as SSL only requires the photos without the need for expensive, and slow, labels. Yet, to my knowledge, only a few companies have applied these techniques.</p>
</li>
<li><p>The Teacher+Student paradigm works stunningly well as an SSL technique.</p>
</li>
<li><p>All reviewed authors take special care in their data augmentation. This aspect is critical to creating good visual representation.</p>
</li>
<li><p>Momentum encoders, used as a teacher in Moco and Dino, are a simple yet useful construct to guide a more eager encoders. This concept may well expand beyond SSL.</p>
</li>
</ul>
<p>Can you represent some of your problems as a Self Supervised Learning?</p>
]]></content:encoded></item><item><title><![CDATA[Don't use raw embeddings]]></title><description><![CDATA[Introduction:
With the rise of Transformers, embeddings are now widely used:

As representations of images or texts that can be used by other models or in a zero-shot manner

As a basic building block for Vector Search in LLM RAG and image search


H...]]></description><link>https://www.guillaume.nyc/dont-use-raw-embeddings</link><guid isPermaLink="true">https://www.guillaume.nyc/dont-use-raw-embeddings</guid><category><![CDATA[vector quantization]]></category><category><![CDATA[vector embeddings]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Tue, 15 Apr 2025 20:51:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745420938501/e976eec4-aab3-490b-818b-26ee4130de74.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction:</h1>
<p>With the rise of Transformers, embeddings are now widely used:</p>
<ul>
<li><p>As representations of images or texts that can be used by other models or in a zero-shot manner</p>
</li>
<li><p>As a basic building block for Vector Search in LLM RAG and image search</p>
</li>
</ul>
<p>However, embeddings are still quite large. OpenAI’s <code>text-embedding-3-large</code> can reach up to d=3072, which means 6kB (stored as float32) per entity. From experience, this is enough to overwhelm SQL engines when performing large JOINs, as this data needs to be sent across the network for a distributed JOIN.</p>
<p>Therefore, it makes sense to try compressing these embeddings into a smaller, yet high-quality, representation.</p>
<h1 id="heading-vector-quantization">Vector Quantization:</h1>
<p>Vector Search has been around a while but became truly popular in 2022 (see <a target="_blank" href="https://trends.google.com/trends/explore?date=all&amp;q=vector%20search&amp;hl=en-US">Trend</a> below). However, with much foresight, Facebook released their vector search codebase <a target="_blank" href="https://engineering.fb.com/2017/03/29/data-infrastructure/faiss-a-library-for-efficient-similarity-search/">FAISS</a> in 2017.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744741292022/85a4f415-b5fb-4a33-b385-2e540d4736b8.png" alt class="image--center mx-auto" /></p>
<p>A common challenge with vector search is storing all vectors in memory, which can be quite costly when the vectors are large. H. Jégou et al. (<a target="_blank" href="https://ieeexplore.ieee.org/document/5432202">paper</a>) introduced Product Quantization in 2010. The main idea is to divide a long vector into smaller chunks (about 4 dimensions each) and apply k-means clustering to each chunk. Each vector is then represented by its closest centroid. With enough centroids, the loss is expected to be minimal.</p>
<p>The illustration below shows how this works. It displays 2 chunks of 4 columns and their closest centroids (125 and 12) for encoding.</p>
<p>To decode at runtime, the vector is reconstructed by looking up the coordinates of the centroids and combining them back into the original vector space.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744741832354/ed411adb-91dd-4264-b661-203c2e2d99dd.png" alt class="image--center mx-auto" /></p>
<p>You can also calculate the space saved:</p>
<ul>
<li><p><strong>Current</strong>: 3072 at float32 = 6kB</p>
</li>
<li><p><strong>Product Quantized</strong> (dim=4, with 256 centroids stored as uint8): (3072 / 4) * 1 byte = 768B (8x smaller)</p>
</li>
</ul>
<p>Product Quantization (PQ) is a simple yet effective technique to save space. Although there are other methods, PQ is still widely used in the industry.</p>
<p>To illustrate, we implemented it in a few lines of Numpy code in this notebook in this <a target="_blank" href="https://gist.github.com/guillaumeguy/3e5555cc5134ad742e2558792bef0808">gist</a>.</p>
<h1 id="heading-cpugpu-friendliness">CPU/GPU Friendliness:</h1>
<p>A keen observer will notice the following:</p>
<ul>
<li><p>Each group of columns can be processed in parallel, making computation highly parallel.</p>
</li>
<li><p>The operations are basic: matrix multiplication and lookups (i.e. <a target="_blank" href="https://en.wikipedia.org/wiki/Gather/scatter_\(vector_addressing\)#Definitions">gather</a>), which are highly optimized on both CPUs and GPUs.</p>
</li>
</ul>
<p>This makes Product Quantization efficient on almost all modern hardware.</p>
<h1 id="heading-going-further">Going Further:</h1>
<p>T. Ge et al. (CVPR 2013) (<a target="_blank" href="https://people.csail.mit.edu/kaiming/publications/pami13opq.pdf">link</a>) improved PQ by adding a rotation step, which significantly reduces the loss. From experience, a cosine similarity metric above 99.X% enable most downstream use cases.</p>
<h1 id="heading-takeaway">Takeaway:</h1>
<p>While embeddings are useful for many applications, they are often large and hard to manage. By quantizing them and using the codes instead of the raw embeddings, you can improve their usability while keeping them close to the original vector. Check it out!</p>
]]></content:encoded></item><item><title><![CDATA[Focus: Shapley value]]></title><description><![CDATA[Game theory is a fascinating topic codifying and quantifying all sorts of interactions between stakeholders in a game. The most popular setup is the prisoner's dilemma but there is much more to it. Today, we will cover the Shapley value as I recently...]]></description><link>https://www.guillaume.nyc/focus-shapley-value</link><guid isPermaLink="true">https://www.guillaume.nyc/focus-shapley-value</guid><category><![CDATA[Shapley value]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Tue, 15 Apr 2025 16:22:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745421387504/1a9871f9-9f33-4429-bb18-eeb9e479b99a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Game theory is a fascinating topic codifying and quantifying all sorts of interactions between stakeholders in a game. The most popular setup is the prisoner's dilemma but there is much more to it. Today, we will cover the <strong>Shapley value</strong> as I recently stumbled across this original yet relatively unknown concept.</p>
<h2 id="heading-problem-at-stake"><strong>Problem at stake</strong>:</h2>
<p><strong>Example 1:</strong> In a salary negotiation, the employee presents their skills and what they bring to the company. But how much are these skills worth?</p>
<p><strong>Example 2:</strong> In a joint venture, each founding company contributes expertise. What’s a fair way to distribute ownership or shares?</p>
<p><strong>Example 3:</strong> Two or three telecom companies want to build a fiber network that would benefit all. What’s a fair way to split the costs?</p>
<p>When we think about these problems, most of us might guess: “You should ask for an X% raise because you deserve it,” but there is actually a theory for this.</p>
<p>Let’s explore the theory behind it.</p>
<h2 id="heading-solution"><strong>Solution</strong>:</h2>
<p>This is known as a cooperative game, and there is only one breakdown function that meets a few conditions (more on this later).</p>
<p>The main idea: “Player A's fair reward is the average of their marginal contributions to the different coalitions leading to the final setup,” where:</p>
<p>For a game with 3 players (A, B, C), we define:</p>
<ul>
<li><p>Final setup: the final set of stakeholders S {A, B, C}</p>
</li>
<li><p>Coalition: a subset of S</p>
</li>
<li><p>Marginal contribution: Adding A to {A, B} is: Value {A, B, C} – Value {B, C}</p>
</li>
</ul>
<p>Now, let’s introduce some values:</p>
<ul>
<li><p>V(A) = 12</p>
</li>
<li><p>V(B) = 8</p>
</li>
<li><p>V(C) = 2</p>
</li>
<li><p>V(A, B) = 22</p>
</li>
<li><p>V(A, C) = 15</p>
</li>
<li><p>V(B, C) = 11</p>
</li>
<li><p>V(A, B, C) = 23</p>
</li>
</ul>
<p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiCHDcQfKDDZQImKzsxc7YN2bCaX7HBnYEpdnqpExA-_e4P5dDZ2KooFKjnn3OhmKnolV9aVHtkK5uRYN6p8l9SW-IA1F0nr0hAKzUj7GNaZNaQZWs1tlGhMPEixTylGm8o8ps6qXqsuLy/s640/page1.PNG" alt /></p>
<p>The Shapley value of A is calculated as:</p>
<p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC7ES0CUuVJIIB15Wgh1ovE1tOr-e2ZeItsKIbX62tVYtBCzSkkMYILbIZtrCNMx1Fdj-KXDxlXYKg574Y1aHxKesdv1ddIKfHrgZiBMmjonQIfEWzSX_ACyWZpKjUoEgpbyuJr8qOzYzs/s640/page2.PNG" alt /></p>
<h2 id="heading-generalization">Generalization</h2>
<p>From there, you can intuit the general formula where n! is the total number of permutations :  </p>
<p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOyHJKMJ_SEL1dBrlr-T3R4Yl-gq_8_roLEAAhq-3XT43rqF1YxldSH2NzMOXNJ5VsRJ1H17TE7UEWJw54W2yR7vlQ7Jug-9NzTiJ7XeYBHxxC9aX-fWU6PJj3k3vXQ7gPFYiAuTmpFrY5/s400/formula.PNG" alt /></p>
<p>Where K \ A notes the coalition K without A.</p>
<p>What current problem can you apply the Shapley value to?</p>
]]></content:encoded></item><item><title><![CDATA[Branch Misprediction in Humans]]></title><description><![CDATA[Branch prediction refers to a CPU's ability to predict the outcome of a boolean statement to streamline processing. This concept parallels human decision-making, where people continuously assess potential outcomes based on experience and inference, o...]]></description><link>https://www.guillaume.nyc/branch-misprediction-in-humans</link><guid isPermaLink="true">https://www.guillaume.nyc/branch-misprediction-in-humans</guid><category><![CDATA[branch prediction]]></category><category><![CDATA[human]]></category><category><![CDATA[cpu]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Tue, 15 Apr 2025 16:17:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744733755552/2c3da3dc-61ab-4f0e-8111-276be66fdf04.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Branch prediction refers to a CPU's ability to predict the outcome of a boolean statement to streamline processing. This concept parallels human decision-making, where people continuously assess potential outcomes based on experience and inference, often leading to errors or mispredictions. Such errors can result from incorrect priors and can be challenging to correct, similar to the sunk cost fallacy in human behavior.</p>
</blockquote>
<h2 id="heading-what-is-branch-prediction">What is branch prediction?</h2>
<p>Brand prediction is a relatively unknown term to non-CS folks. It describes the ability for modern CPU to predict the value of a boolean statement and proceed ahead while verifying the aforementioned statement in parallel. It usually speeds up computations but hurts when the problem is intrinsically difficult to predict (think coin flip).</p>
<p>There is a famous Stack Overflow <a target="_blank" href="https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array/11227902#11227902">answer</a> with 33k upvotes that covers an easy-to-understand use case for those who want to learn more.</p>
<h2 id="heading-how-does-it-relate-to-humans">How does it relate to humans?</h2>
<p>As it turns out, humans are equally trying to predict things in our day to day life. Is this person a threat? Can I park here without getting a ticket? When assessing these questions, one would usually comb through prior experience using some type of Bayesian inference to get to an estimate. For instance, guns increase the risk whereas some locations (e.g. playground) reduce it. Humans always compute the odds in their background process, which only triggers an alarm when the computed risks crosses a certain threshold.</p>
<p>The problem is that Humans make mispredictions too. Priors may be informed by past experience that may not be representative, or worse, built upon some beliefs that are plain false. In such case, one may misconstrue certain animals to be dangerous; and worse, not offer a feedback loop that correct this imperfect belief. As for CPUs, Human branch mispredictions can therefore be costly. And we have a name for it: Sunk cost fallacy (<a target="_blank" href="https://www.blogger.com/blog/post/edit/2513903526613714209/5745606904678810241?hl=en#">wikipedia</a>). And yes, it’s hard to correct.</p>
]]></content:encoded></item><item><title><![CDATA[Should we ship this feature?]]></title><description><![CDATA[Introduction
During my time at tech companies, one of the most challenging tasks for my team has been guiding the Product team in their decision to launch a feature. This process is both highly consequential and fraught with potential pitfalls. Let's...]]></description><link>https://www.guillaume.nyc/should-we-ship-this-feature</link><guid isPermaLink="true">https://www.guillaume.nyc/should-we-ship-this-feature</guid><category><![CDATA[False discovery]]></category><category><![CDATA[experimentation]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Sun, 01 Aug 2021 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745421547789/4d47ef02-2862-4b2d-a0e7-a971ccceaa21.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>During my time at tech companies, one of the most challenging tasks for my team has been guiding the Product team in their decision to launch a feature. This process is both highly consequential and fraught with potential pitfalls. Let's consider an example to ease into the topic:</p>
<p><em>You've worked hard to convince leadership to measure the impact of a cool new feature, and you've set up a well-designed experiment. After four weeks, you observe a statistically significant improvement in your primary metric, while secondary metrics remain neutral. The story seems clear: you should tell the team to ship it, right?</em></p>
<h2 id="heading-the-problem-with-shipping-features">The problem with shipping features</h2>
<p>Once the results are in, the focus often shifts to outcomes, and leadership is incentivized to ship. Consequently, the cost side of the equation receives little attention:</p>
<ul>
<li><p><strong>Tech Debt</strong>: Does this feature increase system complexity? Does it hinder long-term growth by imposing a tax on any new feature? Consider the Net Present Value of such a tax. This aspect is frequently overlooked, leaving some teams burdened with significant disadvantages, resulting in long development cycles when one could reasonably expect 2-3 times faster cycles.</p>
</li>
<li><p><strong>Long-term Effect</strong>: Experiments measure short-term effects on a population. They do not capture behaviors that take time to materialize, such as competitors' reactions, brand effects, or customer word-of-mouth on "circumventing the feature" in cases of fraud.</p>
</li>
<li><p><strong>False Positives</strong>: A low p-value does not address false positives. Instead, it describes the probability of observing a change as extreme as the one you are seeing under the null hypothesis. What is the probability of a false positive given your observations?</p>
</li>
</ul>
<h2 id="heading-thinking-about-tech-debt">Thinking about Tech Debt</h2>
<p>Tech debt is primarily shouldered by Engineering and sometimes by Data Science. A strong partnership with Engineering is necessary to assess it accurately. Here are a few probing questions to consider: Is the system more complex? How so? Can you help me understand your development velocity before and after? Can you quantify it? For example, if you had a single model before and this feature adds another model, training time might increase by 100%, resulting in a ~20% longer development cycle. Is the system more fragile? Do you have more interfaces, special cases, or reliance on data pipelines/APIs that may become obsolete?</p>
<p><em>Fragility is an interesting topic that is easy to underestimate. If you add three components with a 1/1000 chance of failing in a day, the probability that your system will fail at least once over a year is 66% (yes, more likely to happen than not).</em></p>
<h2 id="heading-rethinking-leadership-incentive">Rethinking leadership incentive</h2>
<p>At the core of this issue is incentive: a team is seen as successful if it ships features, and so is a leader of an organization. Since tech debt is difficult to quantify and gradually impacts team productivity, team leadership often disregards it.</p>
<p>While Data Science cannot change the incentive structure alone, it can play a significant role in highlighting these trade-offs by leveraging its institutional status as a "guide to decision making." Most leaders are reasonable and strive to do right by their team. They understand these trade-offs as long as they are presented clearly and early. Tactically, individuals at my level (DS manager) can initiate discussions by requesting an analysis before launching the experiment (by including that component in their experiment spec) and reaching a consensus with PM/Eng on shared launch criteria.</p>
<h2 id="heading-quantifying-false-discovery-rate">Quantifying False Discovery Rate</h2>
<p>To address false positives, you need to dig deeper into the definitions of power and false discovery. You can write four equations with four unknown variables (TP, FP, TN, FN):</p>
<ul>
<li><p>Power: Ability to detect small changes</p>
</li>
<li><p>p-value: Acceptance of False Positives (the higher, the more “accepting of FP” the experimenter is)</p>
</li>
<li><p>Ship rate: % of features shipped</p>
</li>
<li><p>SUM(TP ... FN) = 1</p>
</li>
</ul>
<p>Leading to this matrix:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744738770136/ed497f4d-c427-4a23-8ddf-9511f19fe732.png" alt class="image--center mx-auto" /></p>
<p>Inverting the matrix solves the problem. In particular, we are interested in FP / (FP + TP) which gives you P(FP | shipped), that is, the False Discovery Rate:</p>
<p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3wR0vb_mp1PSSM5COh_xB0vcN5m56xw7VADnnUiAG_N_5j_05jfiFlMQQw6Mt1xpBTxtaoBWimRb7e9FSRAqe4GEjYUTGeaAG8Kp5pfq5DGdWCtXR89GP_JhH4SpxASKFxSnMlSeeT-Ad/w640-h348/Screen+Shot+2021-08-27+at+12.06.56+PM.png" alt /></p>
<p>You can see that you should not be overly concerned about false discoveries unless you are conducting numerous experiments and "seeing what sticks" (a form of p-hacking). At a 50% ship rate, only 1.5% of all experiments shipped are false positives (for p=0.05), which seems reasonable. If your ship rate falls below this threshold, it may be time to recommend a lower p-value threshold.</p>
<p>Code: <a target="_blank" href="https://github.com/guillaumeguy/guillaume.nyc/blob/main/feature_ship/Quantifying%20False%20Positives.ipynb">here</a></p>
<p>Good luck!</p>
]]></content:encoded></item><item><title><![CDATA[My new role at Lyft (2018)]]></title><description><![CDATA[In October, I joined Lyft as Data Science Manager for Core Mapping. I wish I could have posted this update a while ago but a big event got in the way (yes, we went public) ...
While low visibility, Mapping turns out to a big deal for ride-sharing as ...]]></description><link>https://www.guillaume.nyc/my-new-role-at-lyft-2018</link><guid isPermaLink="true">https://www.guillaume.nyc/my-new-role-at-lyft-2018</guid><category><![CDATA[core mapping]]></category><category><![CDATA[lyft]]></category><dc:creator><![CDATA[Guillaume Guy]]></dc:creator><pubDate>Mon, 01 Oct 2018 17:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745421511552/f82a5ee6-f311-4418-921f-d3b8772c5b21.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In October, I joined Lyft as Data Science Manager for Core Mapping. I wish I could have posted this update a while ago but a big event got in the way (yes, we went public) ...</p>
<p>While low visibility, Mapping turns out to a big deal for ride-sharing as it has influence on a lot of other services. In a nutshell, Mapping has an impact on pricing, driver dispatch, scheduled rides and customer XP. Also, it is usually the biggest friction point for seamless pickups.</p>
<h3 id="heading-why-is-mapping-important-for-ride-sharing-company"><strong>Why is Mapping important for ride sharing company ?</strong></h3>
<p>Mapping has traditionally 4 components: Basemap (representation of the world as a graph), Locations (where are drivers, passengers ?), Routing (optimal paths between locations) and ETA (distance and time between locations).</p>
<p>When you open your app, you see the following screen:</p>
<p><a target="_blank" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXFr6t0pDBkH7pFpZ4pIoJ3Cr_h4w1OSiSVFimjmh_db9LOZ4gx75L5mHdh9IVfGVAkLJE1W-HTDj87lgsTpurgiGM-C3IRXioQP4lpY7lV_c6m0EfUGasXBvn0Eg4MNfbrLVGE1F97dkv/s1600/eta_is_important.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXFr6t0pDBkH7pFpZ4pIoJ3Cr_h4w1OSiSVFimjmh_db9LOZ4gx75L5mHdh9IVfGVAkLJE1W-HTDj87lgsTpurgiGM-C3IRXioQP4lpY7lV_c6m0EfUGasXBvn0Eg4MNfbrLVGE1F97dkv/s320/eta_is_important.png" alt /></a></p>
<p>A lot of information displayed is connected to the work by my team:</p>
<ul>
<li><p>The surrounding physical world: road segments, (train) stations, POI</p>
</li>
<li><p>The pick-up ETA (3min) looks at drivers around the PIN and compares it to demand. This gives an estimate how fast we can get a car to you</p>
</li>
<li><p>The drop-off ETA (10:03) estimates when we can get you to your destination</p>
</li>
<li><p>The price ($36.66) which uses a lot of indicators including dropoff ETAs to make the ride fair for both parties: riders and drivers</p>
</li>
<li><p>The polyline (purple) showing the route to your destination</p>
</li>
</ul>
<p>Once you click on "Request", the application will dispatch a driver to you. This decision is central to the efficiency of the platform and therefore very sophisticated. Getting a driver to you ASAP (using our ETAs) is of critical importance to make for the best user experience. Here, there are usually two ways of solving it: the greedy way (dispatching the closest driver) and the optimal way (solving the problem as a global minimization problem).</p>
<h3 id="heading-exciting-problems-in-core-mapping"><strong>Exciting problems in Core Mapping</strong></h3>
<p>What are some of the problems in Mapping that are very interesting in nature ? This is a broad question and I will just sample it down a few:</p>
<p><strong>How does driver location influence ETA?</strong><br />We collect GPS signal from our drivers by way of streaming to our services. There is a chance that it can be wrong: For instance, snapping the driver to the wrong road segment often occurs is urban canyon.</p>
<p>In the example below, what if the driver is falsely matched to Bryan Street but is actually about to enter I-80 ? Dispatching this driver would arguably be disastrous: The driver getting on I-80 may have to drive all the way to Oakland and back ! A small map match variation (in distance) has a non-linear relationship with ETA.</p>
<p><a target="_blank" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU08C-Gsxv4ZaiEgkVHXttX8rDWfKNLg1ivuzo_f6rtIvYASAEVizbg9xzQQs9FYfLjCIGkOX8CW2i0G01e7qlHnocCUM5fp1Qn2YN1VCQqiUwfq2Qw2rMzFxMCYQ3SPK3ng2DbPIUVwD6/s1600/Road+segment.png"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU08C-Gsxv4ZaiEgkVHXttX8rDWfKNLg1ivuzo_f6rtIvYASAEVizbg9xzQQs9FYfLjCIGkOX8CW2i0G01e7qlHnocCUM5fp1Qn2YN1VCQqiUwfq2Qw2rMzFxMCYQ3SPK3ng2DbPIUVwD6/s400/Road+segment.png" alt /></a></p>
<p><strong>How does ETA influence Dispatch?</strong><br />To provide the best user experience, dispatch uses pairwise (DVR,PAX) ETA to make the best Supply &lt;==&gt; Demand match. In the ideal state, dispatch minimizes the SUM(duration) across all dispatches. However, we only measure the dispatches that occurred and have little information about those that did not materialize. Gaining a better understanding of what leads to "Bad dispatch" (Whether false positive or false negative) is a central topic for my team and for Lyft !</p>
<p>As Lyft speeds up into 2020, Mapping will become an even more important topic e.g. for multi modal transportation. Reach out to me if you want to join the team !</p>
]]></content:encoded></item></channel></rss>