<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Stefano Lottini</title>
    <description>The latest articles on DEV Community by Stefano Lottini (@hemidactylus).</description>
    <link>https://dev.to/hemidactylus</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F645785%2F003ff5da-6a7c-4c59-a414-1d500ed13b10.png</url>
      <title>DEV Community: Stefano Lottini</title>
      <link>https://dev.to/hemidactylus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hemidactylus"/>
    <language>en</language>
    <item>
      <title>The Subtleties of Vector Similarity Scales (part 4)</title>
      <dc:creator>Stefano Lottini</dc:creator>
      <pubDate>Mon, 04 Mar 2024 22:35:39 +0000</pubDate>
      <link>https://dev.to/datastax/the-subtleties-of-vector-similarity-scales-part-4-2hjd</link>
      <guid>https://dev.to/datastax/the-subtleties-of-vector-similarity-scales-part-4-2hjd</guid>
      <description>&lt;p&gt;Here’s the fourth and final installment of this series on learnings from building a vector database. We covered the &lt;a href="https://dev.to/datastax/what-we-learned-when-we-built-a-vector-database-and-our-customers-started-using-it-part-1-1c6h"&gt;basic notions of vectors and interacting with them&lt;/a&gt;, &lt;a href="https://dev.to/datastax/vector-similarities-how-similar-are-they-part-2-37g2"&gt;the behavior of vector similarities&lt;/a&gt;, and &lt;a href="https://dev.to/datastax/using-vectors-with-apache-cassandra-and-datastax-astra-db-8lb"&gt;their usage with Apache Cassandra and DataStax Astra DB&lt;/a&gt;. Here, we’ll explore the pitfalls associated with rescaling similarities, and bring it to life with an end-to-end migration example.&lt;/p&gt;

&lt;p&gt;In this series (as is the case for most applications out there), we’ve preferred to work with "similarities" rather than "distances.” This is because the former lend themselves more easily to be cast into the notion of a "score" bounded between known values.&lt;/p&gt;

&lt;p&gt;For most applications, knowing that zero means "least similar" and one means "most similar" is all that counts. However, a few points should be kept in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The choice of scaling this score between zero and one is just for convenience; there is nothing special in this, except the fact that it "feels natural." And, sure enough, this is what Apache Cassandra and &lt;a href="https://www.datastax.com/products/datastax-astra?utm_source=dev-to&amp;amp;utm_medium=byline&amp;amp;utm_campaign=vectors&amp;amp;utm_term=all-plays&amp;amp;utm_content=what-we-learned" rel="noopener noreferrer"&gt;DataStax Astra DB&lt;/a&gt; do, as can be checked by looking at the definitions given in &lt;a href="https://dev.to/datastax/what-we-learned-when-we-built-a-vector-database-and-our-customers-started-using-it-part-1-1c6h"&gt;part 1&lt;/a&gt;. These achieve a final result bound to lie between zero and one, albeit through very different formulae for the Cosine and the Euclidean cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alternate cosine similarity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When working with the cosine similarity, however, it is important to note that another, different scale is very common in textbooks and references (such as &lt;a href="https://en.wikipedia.org/wiki/Cosine_similarity#Definition" rel="noopener noreferrer"&gt;Wikipedia&lt;/a&gt;). Especially in more mathematical-oriented applications, one often prefers the following definition (denoted with a superscript star in this writeup):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgary5m8eq6u9d0gv9ejm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgary5m8eq6u9d0gv9ejm.png" alt="Definition of the " width="484" height="129"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This definition is such that identically-oriented vectors have &lt;strong&gt;S*cos = 1&lt;/strong&gt; all right, while exactly opposed vectors yield &lt;strong&gt;S*cos = -1&lt;/strong&gt;. In other words, the two cosine similarities are related by the simple linear rescaling &lt;strong&gt;Scos = (1+S*cos)/2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The meaning of the scale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At this point it is clear that the &lt;em&gt;numeric values&lt;/em&gt; of similarities have no intrinsic meaning by themselves. They are very useful to anchor comparisons, such as when determining – and then applying – a cutoff threshold, but not much more. Stated differently, just knowing that "vectors &lt;strong&gt;v1&lt;/strong&gt; and &lt;strong&gt;v2&lt;/strong&gt; have a similarity of 0.8" is of little importance without a comparison context. This is even more true across measures: a 0.8 with Euclidean, for example, has nothing to do with a 0.8 from cosine (earlier I gave an explicit function to translate values, but that holds on the sphere only).&lt;/p&gt;

&lt;p&gt;Mathematically speaking, one could have chosen any of the infinite ways to construct a well-behaving "similarity function" of two vectors; while there is no strong formal principle to favor one over the other, all these candidate similarities may well yield different numeric values for the same pair of vectors. This is the reason for the claim that similarity &lt;em&gt;values&lt;/em&gt; are an arbitrary, conventional notion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intermezzo on vector embeddings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The special case of vectors from embedding models comes with special problems and caveats, and is not the main scope of this article. Yet two things are worth mentioning here: first, that the same two sentences will have &lt;em&gt;different values&lt;/em&gt; for the similarity if using different models (even when using the Cosine similarity throughout!). And, second, one should not expect by any means that "extremely different sentences" will result in vectors with zero similarity. The latter is a somewhat common misconception, possibly fueled by an erroneous interpretation of this score as a "semantic-relatedness percentage." The truth is, with most embedding models one would have a hard time coming up with two sentences whose similarity (&lt;strong&gt;Scos&lt;/strong&gt;) goes below 0.75 or so. The lesson here is: &lt;em&gt;rescale your expectations accordingly&lt;/em&gt;. There'll be a follow-up article specifically targeted at embeddings-related issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The pesky dot, again&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I just mentioned how the various similarities are engineered to be all bound in the very handy &lt;strong&gt;[0:1]&lt;/strong&gt; interval. Well, strictly speaking, that’s a lie: for the dot-product similarity is designed just to be used as a replacement for the cosine where they coincide (i.e. on unit-norm vectors). So, once again, if you use the dot-product for arbitrary vectors, which at this point you will surely see as a weird choice anyway, &lt;em&gt;do not expect&lt;/em&gt; the similarities to be bounded in any way. In fact, as the formulae given earlier would show, your dot-product similarity between arbitrary vectors can be anything from negative infinity all the way to positive infinity!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Similarity of one&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One must not assume that similarity of one means coinciding vectors. This is true only for either Euclidean similarities or on the unit sphere. The counterexample is that of the cosine similarity between two vectors, one a multiple of the other (even worse, for the dot-product off the sphere, you have seen how 1 is not a "special" value at all).&lt;/p&gt;

&lt;h2&gt;
  
  
  Case study: Migration of a vector app
&lt;/h2&gt;

&lt;p&gt;One of the lessons from this (admittedly a bit theoretical) exposition is that you should always read the fine print when it comes to vector stores and the specific mathematical definitions that are used for the similarities.&lt;/p&gt;

&lt;p&gt;To illustrate this point in a practical manner, let’s look at what kind of care should be taken concerning similarities when migrating a typical vector-powered application between vector stores. Let's say you are moving from Chroma to Cassandra / Astra DB. Your application stores vectors and runs ANN search queries, possibly involving a cutoff threshold, previously determined through dedicated analysis, on the results' "scores" (whatever they are). &lt;strong&gt;Our task now is to ensure the application behaves exactly the same after the migration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: below you'll see a detailed investigation on how Chroma behaves. This has been chosen just as a representative example, the main point being that such level of care should be exercised when migrating vector-based workloads between any two databases!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fulfilling the stated goal requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using the same "kind of similarity" (what I called &lt;em&gt;measure&lt;/em&gt; earlier)&lt;/li&gt;
&lt;li&gt;being aware of the precise definition for the similarity (different scales and such), and correcting for any difference&lt;/li&gt;
&lt;li&gt;of course, adapting the code to using another library!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third point is not really in scope for this illustrative example; we are most interested in the previous steps. Let's start!&lt;/p&gt;

&lt;p&gt;The Chroma-backed "app" you're migrating is the following Python script. It creates a vector store (with Cosine measure), puts a few vectors in it, and runs an ANN search to print the resulting matches, the associated number, and whether these are "close enough to the query" (for some unspecified purpose). &lt;em&gt;All vectors are guaranteed to have unit norm.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import chromadb
chroma_client = chromadb.Client()

# Creating a Vector store
cos_coll = chroma_client.create_collection(
    name="cosine_coll",
    metadata={"hnsw:space": "cosine"},
)

# Saving vector entries
cos_coll.add(
    documents=["3 o-clock", "6 o'clock", "9 o'clock"],
    embeddings=[[1, 0], [0, -1], [-1, 0]],
    ids=["3:00", "6:00", "9:00"],
)

# Running ANN search
cos_matches = cos_coll.query(
    query_embeddings=[[1, 0]],
    n_results=3
)

chroma_threshold = 1.5

# Printing the results and their "distance"
match_ids = cos_matches["ids"][0]
match_distances = cos_matches["distances"][0]
for m_id, m_distance in zip(match_ids, match_distances):
    status = "ok" if m_distance &amp;lt;= chroma_threshold else "NO!"
    print(f"d([1,0], '{m_id})' = {m_distance}: {status}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For illustrative purposes, the script inserts two-dimensional vectors arranged as the hour hand of a clock at various times, the query vectors being the "3 o'clock" right-pointing direction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50kn734w8ht414md2rq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50kn734w8ht414md2rq1.png" alt="The three sample vectors in the " width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caption: The "clock model" illustrates the vectors used in the "sample application". The red vectors are the inserted rows, and the blue vector is the query vector used throughout.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Running the above program (as tested with &lt;code&gt;chromadb==0.4.21&lt;/code&gt;) has this output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;d([1,0], '3:00') = 0.0: ok
d([1,0], '6:00') = 1.0: ok
d([1,0], '9:00') = 2.0: NO!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you notice anything here? Well, the number Chroma returns with the matches is &lt;em&gt;not a similarity at all&lt;/em&gt;, rather a distance! Indeed, it &lt;em&gt;increases&lt;/em&gt; from the closest to the farthest match. This can be verified on the &lt;a href="https://docs.trychroma.com/usage-guide#changing-the-distance-function" rel="noopener noreferrer"&gt;Chroma docs page&lt;/a&gt;, where all relevant formulae are provided. This is very useful information if one is to port an application to a different vector store!&lt;/p&gt;

&lt;p&gt;One finds out that, regardless of the measure, Chroma always works in terms of a distance, and that the Cosine choice is no exception, with a "Cosine distance" defined as:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2a8y8ub5mdvpuxa3otlu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2a8y8ub5mdvpuxa3otlu.png" alt="Chroma's definition of " width="689" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In other words, one can relate this quantity to the familiar similarity through &lt;strong&gt;dcos&lt;sup&gt;Chroma&lt;/sup&gt;(v1, v2) = 1 - S*cos(v1, v2) = 2 - 2 Scos(v1, v2)&lt;/strong&gt;, equivalent to the inverse mapping &lt;strong&gt;Scos = 1- dcos&lt;sup&gt;Chroma&lt;/sup&gt; / 2&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But there is more in the way of translations: indeed, the inequalities in the original code have to be &lt;em&gt;reversed&lt;/em&gt; to keep their meaning. Where the Chroma code has &lt;code&gt;distance &amp;lt;= chroma_threshold&lt;/code&gt;, for example, you'll need to place a condition such as &lt;code&gt;similarity &amp;gt; cass_threshold&lt;/code&gt; in the ported code, where &lt;code&gt;cass_threshold = 1 - chroma_threshold / 2&lt;/code&gt;, following the mapping above.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Side note&lt;/strong&gt;: When possible, it’s better to translate thresholds rather than similarities/distances. This can be done "at coding time," generally minimizing the chance of errors/inconsistencies, and in some cases (e.g. when using higher abstractions around a vector store) might be the only feasible choice.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, it should be noted that whereas in Chroma the default measure is Euclidean, Cassandra and Astra DB employ cosine when not explicitly chosen: it may be safer and less prone to surprises to always spell it out when creating vector stores.&lt;br&gt;
So, the "application," once migrated to Astra DB, is comprised of a CQL schema creation script, looking like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Table creation (CQL)
CREATE TABLE cos_table (
  id TEXT PRIMARY KEY, my_vector VECTOR&amp;lt;FLOAT, 2&amp;gt;
);

// Vector index creation (CQL)
CREATE CUSTOM INDEX cos_table_v_index ON cos_table(my_vector)
  USING 'StorageAttachedIndex'
  WITH OPTIONS = {'similarity_function': 'COSINE'};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;plus the "app" itself, the following Python script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Connecting to DB
from cassandra.cluster import Cluster
cluster = Cluster(...)  # connection to DB
session = cluster.connect()

# Saving vector entries
session.execute("""
    INSERT INTO cos_table (id, my_vector)
    VALUES ('3:00', [1, 0]);
""")
session.execute("""
    INSERT INTO cos_table (id, my_vector)
    VALUES ('6:00', [0, -1]);
""")
session.execute("""
    INSERT INTO cos_table (id, my_vector)
    VALUES ('9:00', [-1, 0]);
""")

# Running ANN search
ann_query = """
SELECT
  id,
  my_vector,
  similarity_cosine([1, 0], my_vector) as sim
FROM cos_table
ORDER BY my_vector ANN OF [1, 0]
LIMIT 3;"""
cos_matches = session.execute(ann_query)

chroma_threshold = 1.5
cass_threshold = 1 - chroma_threshold / 2

# Printing the results and their "similarity"
for match in cos_matches:
    # While we're at it, we recast to Chroma distance
    chroma_dist = 1 - match.sim
    #
    status = "ok" if match.sim &amp;gt; cass_threshold else "NO!"
    print(
        f"d([1,0], '{match.id})' = {match.sim}: {status} "
        f"(d_chroma = {chroma_dist})"
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of this, as expected, will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;d([1,0], '3:00') = 1: ok (d_chroma = 0)
d([1,0], '6:00') = 0.5: ok (d_chroma = 1)
d([1,0], '9:00') = 0: NO! (d_chroma = 2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see, one has to pay some attention to avoid getting caught in the subtleties of distances, similarities, and definitions. It's definitely better to always read the fine print and play with a toy model to check one's assumptions on known cases (such as the "clock model" vectors used above).&lt;/p&gt;

&lt;p&gt;Were the original application &lt;strong&gt;using the Euclidean measure&lt;/strong&gt; (but still working on the unit sphere), one would be in for another surprise: namely, what Chroma calls "Euclidean distance" is actually the &lt;em&gt;squared distance&lt;/em&gt;! In other words, &lt;strong&gt;deucl&lt;sup&gt;Chroma&lt;/sup&gt;(v1, v2) = δ&lt;sup&gt;2&lt;/sup&gt;eucl(v1, v2)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once this bit is acknowledged, the rest proceeds in the same manner as seen above. Distances (Chroma) grow when similarities (Cassandra / Astra DB) decrease, inequalities have to be reversed, and the following mapping needs to be used: &lt;strong&gt;Seucl = 1 / (1 + deucl&lt;sup&gt;Chroma&lt;/sup&gt;)&lt;/strong&gt;, i.e. &lt;strong&gt;deucl&lt;sup&gt;Chroma&lt;/sup&gt; = (1/Seucl) - 1&lt;/strong&gt;. Note that a consequence is that, on the sphere, the Chroma Euclidean distance ranges from zero (most similar) to &lt;em&gt;four&lt;/em&gt; (most dissimilar, i.e. antipodal vectors on the sphere).&lt;/p&gt;

&lt;p&gt;The sheer amount of possible ways to quantify the position of two vectors, with different stores and different similarities, is enough to make you feel a bit dizzy – the lesson here is that one should make no unwarranted assumptions and verify definitions thoroughly. Test with known vectors, check the docs for formulae! To complete the exercise, here is a complete "translation map" between all distances/similarities encountered in this migration example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9tm0d71qzgm30ti5muf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp9tm0d71qzgm30ti5muf.png" alt="Conversion table between all similarities and " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the table above, which expresses each quantity as a function of any other, the white cells are always valid, while the darkened ones are relations that hold only on the sphere (i.e. where it makes sense to recast Euclidean notions to Cosine, and vice versa, unambiguously).&lt;/p&gt;

&lt;p&gt;You can also check the values these quantities assume with the three "clock" vector positions that were used in the example code (remember these are unit-norm vectors):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0yw57rshfalyljaul9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0yw57rshfalyljaul9c.png" alt="Image description" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embedded in LangChain&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your original code to migrate might be using a framework rather than directly accessing the Chroma primitives, for example it might be a &lt;a href="https://python.langchain.com" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; application leveraging the &lt;code&gt;langchain.vectorstores.Chroma&lt;/code&gt; vector store abstraction. As can be verified by inspecting the plugin source code (or running suitable test code, although this turns out to be more convoluted due to LangChain's choice of abstractions around embeddings), essentially the same API as before is exposed through the LangChain object, so that one should specify the cosine measure by passing a specific parameter when creating the store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from langchain_community.vectorstores import Chroma
my_store = Chroma(
    ...,
    collection_metadata={"hnsw:space": "cosine"},
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "score" returned by methods such as &lt;code&gt;similarity_search_with_score&lt;/code&gt;, likewise, is the very "distance" coming from the Chroma methods, so the same conversions seen above are required.&lt;br&gt;
Likewise, when using the &lt;code&gt;langchain.vectorstores.Cassandra&lt;/code&gt; class, the "score" will be exactly the similarity &lt;strong&gt;Seucl&lt;/strong&gt; seen earlier and bound in the &lt;strong&gt;[0:1]&lt;/strong&gt; interval.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This technical deep dive has highlighted the definitions, the quirks and the caveats to keep in mind when approaching the concept of similarity when querying vector stores. As you have seen, subtleties abound. Luckily, awareness of the underlying mathematical structure helps avoiding fruitless pursuits and actively counterproductive choices.&lt;/p&gt;

&lt;p&gt;So, armed with all this knowledge … why not create a &lt;a href="https://www.datastax.com/products/datastax-astra?utm_source=dev-to&amp;amp;utm_medium=byline&amp;amp;utm_campaign=vectors&amp;amp;utm_term=all-plays&amp;amp;utm_content=what-we-learned" rel="noopener noreferrer"&gt;free account on Astra DB&lt;/a&gt; and start playing with vector search?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>vectordatabase</category>
      <category>ai</category>
    </item>
    <item>
      <title>Using Vectors with Apache Cassandra and DataStax Astra DB (part 3)</title>
      <dc:creator>Stefano Lottini</dc:creator>
      <pubDate>Mon, 26 Feb 2024 15:28:38 +0000</pubDate>
      <link>https://dev.to/datastax/using-vectors-with-apache-cassandra-and-datastax-astra-db-8lb</link>
      <guid>https://dev.to/datastax/using-vectors-with-apache-cassandra-and-datastax-astra-db-8lb</guid>
      <description>&lt;p&gt;Welcome to part three of my series on learnings from building a vector database. In &lt;a href="https://dev.to/datastax/what-we-learned-when-we-built-a-vector-database-and-our-customers-started-using-it-part-1-1c6h"&gt;part one&lt;/a&gt;, we covered some of the basics about vectors. &lt;a href="https://dev.to/datastax/vector-similarities-how-similar-are-they-part-2-37g2"&gt;Part two&lt;/a&gt; was a deep dive into different similarities, on and off the unit sphere. Here, we’ll explore the use of vectors with Apache Cassandra and &lt;a href="https://www.datastax.com/products/datastax-astra?utm_source=dev-to&amp;amp;utm_medium=byline&amp;amp;utm_campaign=vectors&amp;amp;utm_term=all-plays&amp;amp;utm_content=what-we-learned" rel="noopener noreferrer"&gt;DataStax Astra DB&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Similarity search and approximate nearest neighbor
&lt;/h2&gt;

&lt;p&gt;The key interrogation one wants to run in a vector database is "similarity search": given a certain query vector &lt;strong&gt;V&lt;/strong&gt;, one wants the entries whose vectors have the highest similarity to &lt;strong&gt;V&lt;/strong&gt;. The next problem is, how to make vector search efficient when there are potentially many millions of vectors in the database. Luckily, there are several advanced algorithms to do that within a DB engine, usually involving the so-called Approximate Nearest Neighbour (ANN) search one way or the other. In practice this means that one trades the (very small) likelihood of missing some matches for a (substantial) gain in performance.&lt;/p&gt;

&lt;p&gt;I won't spend many words here on the topic of how to equip Cassandra with state-of-the-art ANN search: if you are curious, check out &lt;a href="https://www.datastax.com/blog/5-vector-search-challenges-and-how-we-solved-them-in-apache-cassandra?utm_source=dev-to&amp;amp;utm_medium=byline&amp;amp;utm_campaign=vectors&amp;amp;utm_term=all-plays&amp;amp;utm_content=what-we-learned" rel="noopener noreferrer"&gt;this excellent article&lt;/a&gt; that tells the story of how we did it! I'll just mention that, by maintaining a vector-specialized index alongside the table, Cassandra / Astra DB can offer flexible, performant and massively scalable vector search. Crucially, this index (a certain type of &lt;a href="https://cassandra.apache.org/doc/latest/cassandra/developing/cql/indexing/sai/sai-overview.html" rel="noopener noreferrer"&gt;SAI index&lt;/a&gt;, in Cassandra parlance) needs a specific choice of measure right from the start.&lt;/p&gt;

&lt;p&gt;I'll now take a closer look at how the interaction with Cassandra – or equivalently Astra DB – works. You will see vector-search queries expressed in the &lt;a href="https://docs.datastax.com/en/astra-serverless/docs/vector-search/cql.html" rel="noopener noreferrer"&gt;Cassandra Query Language&lt;/a&gt; (CQL). CQL is a powerful way to interact with the database, and vector-related workloads are no exception.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This section assumes CQL is used throughout to interact with the database (the reason is that CQL is permissive enough to let you violate the querying best practices as outlined below).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, I personally think CQL is great, but I appreciate that not everyone might have the time, the motivation, or the need to learn how to use it.&lt;/p&gt;

&lt;p&gt;For this reason, there are several higher-level abstractions that effectively "hide" the CQL foundations and allow one to focus on the vector-related application itself. I'll just mention, for Python users, &lt;a href="https://cassio.org" rel="noopener noreferrer"&gt;CassIO&lt;/a&gt;, which in turn powers LangChain's and LlamaIndex's plugins for Cassandra. For an easier-to-use and cross-language experience, take a look at the REST-based &lt;a href="https://www.datastax.com/blog/general-availability-data-api-for-enhanced-developer-experience?utm_source=dev-to&amp;amp;utm_medium=byline&amp;amp;utm_campaign=vectors&amp;amp;utm_term=all-plays&amp;amp;utm_content=what-we-learned" rel="noopener noreferrer"&gt;Data API&lt;/a&gt; available in Astra DB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Index time versus query time
&lt;/h2&gt;

&lt;p&gt;The two basic operations when using a vector-enabled database table are: &lt;strong&gt;inserting entries&lt;/strong&gt;, and &lt;strong&gt;running ANN searches&lt;/strong&gt;. Actually, there is another important ingredient that comes before any other: &lt;strong&gt;creating the table&lt;/strong&gt;, along with the required SAI index for vector search. Here is how the typical usage might unfold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creation of table and index&lt;/strong&gt; This is where you choose a measure for the table (e.g. Euclidean).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing entries to the table&lt;/strong&gt; (i.e. rows with their vector). All vectors must have the dimensionality specified when creating the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Querying with ANN search&lt;/strong&gt; "Give me the &lt;strong&gt;k&lt;/strong&gt; entries whose vector is the most similar to a query vector &lt;strong&gt;V&lt;/strong&gt;".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Further writing and querying&lt;/strong&gt; in any order (etc, etc …)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The simple sequence above stresses a key fact: you commit to a measure right when you create a table. Unless you drop the index and re-create it anew, any ANN search you'll run will use that measure. In other words, &lt;strong&gt;the measure used for computing similarities is fixed at index-time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is the CQL code for a minimal creation of a vector table with the related index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE my_v_table
  (id TEXT PRIMARY KEY, my_vector VECTOR&amp;lt;FLOAT, 3&amp;gt;);
CREATE CUSTOM INDEX my_v_index ON my_v_table(my_vector)
  USING 'StorageAttachedIndex'
  WITH OPTIONS = {'similarity_function': 'EUCLIDEAN'};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the sake of completeness, here is the CQL to insert a row in the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO my_v_table
  (id, my_vector)
VALUES
  ('mill_falcon', [8, 3.5, -4.2]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, this is how an ANN search is expressed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT id, my_vector
  FROM my_v_table
  ORDER BY my_vector ANN OF [5.0, -1.0, 6.5]
  LIMIT 5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you see, no measure is specified at query-time anymore. The CQL for vector search admits other bits and options I won't mention here (feel free to visit the &lt;a href="https://docs.datastax.com/en/astra-serverless/docs/vector-search/cql.html" rel="noopener noreferrer"&gt;docs page&lt;/a&gt;), but there is an important thing you can add. Observe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
  id,
  my_vector,
  similarity_euclidean(my_vector, [5.0, -1.0, 6.5]) as sim
FROM my_v_table
  ORDER BY my_vector ANN OF [5.0, -1.0, 6.5]
  LIMIT 5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This last statement can be phrased as:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Give me the five most-similar rows to a query vector &lt;strong&gt;V&lt;/strong&gt; and, for each of them, tell me also the euclidean similarity between the row and &lt;strong&gt;V&lt;/strong&gt; itself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The new bit is that one asks for an additional &lt;code&gt;sim&lt;/code&gt; column (the alias is strongly suggested here, to avoid unwieldy column names) containing the numeric value of the similarity between the row and the provided vector. This similarity is not stored anywhere on the DB — and how could it be, as its value depends on the vector provided in the query itself?&lt;/p&gt;

&lt;p&gt;Now pay attention: nothing prevents one from using &lt;em&gt;two different vectors&lt;/em&gt; in the query, or even to ask for a similarity computed with &lt;em&gt;a different measure&lt;/em&gt; than the one chosen earlier for the index (hence used for searching)!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Warning: WEIRD QUERY
//   (check the vector values and the chosen measure).
// Why should you do this?
SELECT
  id,
  my_vector,
  similarity_dot_product(my_vector, [-7, 0, 11]) as weird_sim
FROM my_v_table
  ORDER BY my_vector ANN OF [5.0, -1.0, 6.5]
  LIMIT 5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In words: &lt;em&gt;give me the five rows closest to &lt;strong&gt;V&lt;/strong&gt;, according to the &lt;strong&gt;Euclidean&lt;/strong&gt; similarity, and compute their &lt;strong&gt;Dot-product&lt;/strong&gt; similarity to this other vector &lt;strong&gt;W&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Pretty odd, no? The point is, this is something CQL does not actively prevent, but that does not mean you &lt;em&gt;should&lt;/em&gt; do it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using two different similarities at index-time and query-time is probably never a sensible choice.&lt;/li&gt;
&lt;li&gt;Using two different vectors for the ANN part and the sim column (i.e. &lt;strong&gt;W != V&lt;/strong&gt;) is something that hardly makes sense; in particular, the returned rows would not be sorted highest-to-lowest-similarity anymore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, &lt;em&gt;the measure for the &lt;code&gt;sim&lt;/code&gt; column is specified at query-time&lt;/em&gt;. Moreover, &lt;em&gt;the query vector and the one for the &lt;code&gt;sim&lt;/code&gt; column are passed as two separate parameters&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The practical advice is then this: &lt;strong&gt;do not pass two different vectors, and do not mix similarities between index and query.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As usual, the confusion from mixing similarities is greatly reduced if working on the sphere (the ordering of results, at least, would not "look weird").&lt;/p&gt;

&lt;p&gt;Now, there &lt;em&gt;might&lt;/em&gt; be very specific use cases that could knowingly take advantage of this kind of CQL freedom. But, frankly, if you are into that kind of thing, you already know the topic covered in this section (while you're at it, why don't you drop a comment below on your use case? I'd be curious to hear about it).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: when using the Data API, as opposed to plain CQL, you do not retain the freedom to ask for a similarity other than the one configured for the table index, as well as the freedom to use &lt;strong&gt;W != V&lt;/strong&gt; in vector searches. And, as was argued in this section, this is not a bad thing at all!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9ww42lzq0od25nk4xm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9ww42lzq0od25nk4xm0.png" alt="Questionable ways to run vector searches with Cassandra" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caption: Bad (or at least questionable) practices with vector searches in CQL. Left: &lt;code&gt;SELECT similarity_euclidean(my_vector, V) as sim ... ORDER BY my_vector ANN OF V&lt;/code&gt; on a table created with the Cosine similarity. The returned rows will be &lt;strong&gt;r1&lt;/strong&gt; and &lt;strong&gt;r2&lt;/strong&gt; in that order, but since (Euclidean!) &lt;strong&gt;δ1 &amp;gt; δ2&lt;/strong&gt;, then the &lt;code&gt;sim&lt;/code&gt; column will come back in increasing order. Right: &lt;code&gt;SELECT similarity_euclidean(my_vector, W) as sim ... ORDER BY my_vector ANN OF V&lt;/code&gt; on a table created with Euclidean. The closest-to-farthest sorting of the rows is referred to &lt;strong&gt;V&lt;/strong&gt;, but the similarity is calculated on &lt;strong&gt;W&lt;/strong&gt;: so, one gets back rows &lt;strong&gt;r1&lt;/strong&gt; and &lt;strong&gt;r2&lt;/strong&gt; in that order, but the &lt;code&gt;sim&lt;/code&gt; column lists increasing values again.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Null vectors and cosine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A close look at how the similarities are computed will reveal a property specific to the cosine choice: for the null vector, i.e. a vector with all components equal to zero, the definition makes no sense at all!&lt;/p&gt;

&lt;p&gt;Beneath the "technical" problem of a division by zero (&lt;strong&gt;|v|=0&lt;/strong&gt;) arising in the similarity formula, indeed, lies the deeper mathematical reason: a segment with zero length has no defined direction to speak of.&lt;/p&gt;

&lt;p&gt;What does this imply? In practice, if you plan to use the cosine similarity, you must ensure that only non-null vectors are inserted. But then again, if you choose this similarity, you could as well rescale each incoming vector to unit length and live in the comfort of the sphere … which is something that can be done on all vectors &lt;em&gt;except the null vector&lt;/em&gt;! So I've come full circle.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note that the null vector has no problem whatsoever with the Euclidean distance, nor with the Dot-product (but the latter, as remarked earlier, probably makes not much sense out of the sphere).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, by now you must be curious as to how Cassandra / Astra DB handles this situation. Here are the various cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running a search such as &lt;code&gt;SELECT ... ANN OF [0, 0, ...]&lt;/code&gt; raises a database error such as &lt;em&gt;Operation failed - received 0 responses and 2 failures: UNKNOWN from ...&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Inserting a row, e.g. &lt;code&gt;INSERT INTO ... VALUES (..., [0, 0, ...], ...)&lt;/code&gt; raises the same error from the DB: &lt;em&gt;Operation failed - received 0 responses and 2 failures: UNKNOWN from ...&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Using the null vector for a &lt;em&gt;similarity column&lt;/em&gt; computed during the query, such as &lt;code&gt;SELECT ... similarity_cosine(my_vector, [0, 0, ...]) ...&lt;/code&gt;, returns a &lt;code&gt;sim&lt;/code&gt; column of all &lt;strong&gt;NaN&lt;/strong&gt; (not-a-number) values. This remains true also when running queries through the Python Cassandra driver.&lt;/li&gt;
&lt;li&gt;The most interesting case is when the table &lt;em&gt;already&lt;/em&gt; contains some null-vector rows &lt;em&gt;before&lt;/em&gt; the vector SAI index is created. In that case, no errors are raised, but &lt;strong&gt;the rows will be silently left out of the index and never reached by ANN searches&lt;/strong&gt;. This can be tricky, and should be kept in mind.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: when using the Data API, the underlying behavior for Cosine-based collections is the same as outlined above – except, the last case is not really possible as the index is created as soon as the collection is available.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The first three installments of this mini-series cover most of what you'll need to know your way around the mathematics, and the practical aspects, of similarity computations between vectors. However, it turns out that a few obstacles may get in your way if you want to perform something as mundane as, say, switching between vector stores in an existing GenAI application. In the next, and last, article, we’ll look at a complete example of such a migration, paying close attention to the hidden surprises along the way. See you next time!&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>vectordatabase</category>
      <category>ai</category>
    </item>
    <item>
      <title>Vector Similarities: How Similar Are They? (part 2)</title>
      <dc:creator>Stefano Lottini</dc:creator>
      <pubDate>Tue, 20 Feb 2024 15:29:18 +0000</pubDate>
      <link>https://dev.to/datastax/vector-similarities-how-similar-are-they-part-2-37g2</link>
      <guid>https://dev.to/datastax/vector-similarities-how-similar-are-they-part-2-37g2</guid>
      <description>&lt;p&gt;In a &lt;a href="https://dev.to/datastax/what-we-learned-when-we-built-a-vector-database-and-our-customers-started-using-it-part-1-1c6h"&gt;previous post&lt;/a&gt;, we discussed the ins and outs of vectors and how to interact with them. &lt;/p&gt;

&lt;p&gt;Here, I’ll provide a more detailed account of vector similarities and how they behave. Let's start with the Euclidean and the Cosine similarities.&lt;/p&gt;

&lt;p&gt;Let’s say you want to measure how close two points (two vectors) are. How do you proceed? If your plan is to place a ruler between them and, well, measure the distance, then  congratulations: you just invented what is called "Euclidean" (or &lt;strong&gt;L2&lt;/strong&gt;) distance. Let's call it &lt;strong&gt;δeucl&lt;/strong&gt;, a number ranging from zero to infinity (you have encountered it already in the introductory table above).&lt;/p&gt;

&lt;p&gt;The next step is to make this quantity into a similarity proper: the choice made in Apache Cassandra and in &lt;a href="https://www.datastax.com/products/datastax-astra?utm_source=dev-to&amp;amp;utm_medium=byline&amp;amp;utm_campaign=vectors&amp;amp;utm_term=all-plays&amp;amp;utm_content=what-we-learned" rel="noopener noreferrer"&gt;DataStax Astra DB&lt;/a&gt;, &lt;strong&gt;Seucl = 1/(1+δeucl&lt;sup&gt;2&lt;/sup&gt;)&lt;/strong&gt;, guarantees a quantity in the zero-to-one range and is also computationally not too expensive. Moreover, one gets &lt;strong&gt;Seucl=1&lt;/strong&gt; for two &lt;em&gt;identical&lt;/em&gt; vectors, while very, very far-apart vectors would have a similarity approaching zero. So far for the Euclidean similarity.&lt;/p&gt;

&lt;p&gt;Of the (few) other choices, the Cosine similarity is the most common (and is available in Cassandra / Astra DB). The key aspect is that it discards the &lt;em&gt;length&lt;/em&gt; part of the vectors and just looks at how well their &lt;em&gt;directions&lt;/em&gt; are aligned to each other. Consider the angle formed between the two vectors: when this angle is zero, you have two arrows pointing exactly in the same direction, i.e. two vectors with similarity of one. As the angle increases, the arrows are less and less aligned, and their cosine similarity decreases, down to the extreme case of two perfectly opposed vectors, which corresponds to similarity zero. It takes a moment of thinking to notice that this definition, possibly a bit weird when the vectors may have different norms (i.e. lengths), "feels correct" when all vectors are of unit length.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important note:&lt;/strong&gt; rescaling the similarities so that they range between zero and one is a somewhat arbitrary, but practically useful, choice. Elsewhere, you may see a slightly different definition for the cosine similarity, &lt;strong&gt;S*cos&lt;/strong&gt;, chosen instead to range from -1 to +1. There are pros and cons in both choices of &lt;strong&gt;S*cos&lt;/strong&gt; and &lt;strong&gt;Scos&lt;/strong&gt;, and a good suggestion is to always be aware of the specifics of the similarity function your system is using (for more on the subtleties of the scale of similarities, check the last sections of this writeup).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The comfort of the sphere
&lt;/h2&gt;

&lt;p&gt;There should be a saying along the lines of, "On the sphere, life is easy."&lt;/p&gt;

&lt;p&gt;The essence of vector search is as follows: given a "query vector," one wants to find, among those stored in the database, the vectors that are most similar to it, sorted by decreasing similarity. Of course, this is based on the similarity measure you choose to adopt! So, what are the changes in the results for different similarities?&lt;/p&gt;

&lt;p&gt;You have just seen the Cosine and the Euclidean similarities. There is no doubt they do different things. But here is an important fact: &lt;em&gt;on the sphere, no matter what similarity you use, you get the same top results in the same order.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The reason is perhaps best given with a picture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7ofmbtn5qr4dol78sf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7ofmbtn5qr4dol78sf2.png" alt="Image description" width="495" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caption: On the unit sphere, no matter whether using the Euclidean or Cosine similarity, &lt;strong&gt;B&lt;/strong&gt; is more similar to &lt;strong&gt;A&lt;/strong&gt; than to &lt;strong&gt;C&lt;/strong&gt;. In other words, smaller angles subtend shorter segments: &lt;strong&gt;angle(AB) &amp;lt; angle(AC) iff AB &amp;lt; AC&lt;/strong&gt;. (Related: it so happens that most sentence embedding vectors are indeed of unit length, and when they are not, you are probably better off by normalizing the vector to unit length yourself: &lt;strong&gt;v ⇒ v/|v|&lt;/strong&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here, then, is the main takeaway for vectors on a sphere: regardless of whether you use the Euclidean or the Cosine similarity, a vector search returns the same top matches, arranged in the same order. The only difference is in the exact &lt;em&gt;values&lt;/em&gt; for the similarities. But this is a difference fully under our control: you see, on the sphere it can be proven that there is a mathematically precise relation between the similarities for a given pair of vectors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhe100he307tiyuhteov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhe100he307tiyuhteov.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Caption: On the unit sphere, and only in that case, the Cosine and Euclidean similarities can be related to each other directly, without the need for additional information on the vectors. The relations, pictured here, are as follows: &lt;strong&gt;Seucl = 1/(5-4Scos)&lt;/strong&gt; and conversely &lt;strong&gt;Scos = (5Seucl-1)/(4Seucl)&lt;/strong&gt;. It is important to notice that these are increasing functions, implying that switching between measures does not alter the "more similar than…" relation. Note also that, on the sphere, the Euclidean similarity has its minimal value at 0.2 (corresponding to the fact that two points on the unit sphere cannot have a distance of more than &lt;strong&gt;δeucl,max = 2&lt;/strong&gt;).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The fact that the transformations between &lt;strong&gt;Seucl&lt;/strong&gt; and &lt;strong&gt;Scos&lt;/strong&gt; are strictly increasing functions is at the heart of the "indifference to the measure used" mentioned above. What about thresholds, for instance if your Cosine-powered search needs to cut away vectors with similarity below a certain &lt;strong&gt;Scos&lt;sup&gt;min&lt;/sup&gt;&lt;/strong&gt;? You can translate thresholds as you'd do similarities, and run the search with the Euclidean measure provided you translate cuts to &lt;strong&gt;Seucl&lt;sup&gt;min&lt;/sup&gt; = 1/(5-4Scos&lt;sup&gt;min&lt;/sup&gt;)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So here is another takeaway. When you are on a sphere, there is no substantial difference between the Cosine and Euclidean approaches – you should just pick the less CPU-intensive solution to reap a performance boost. And, as it turns out, the least CPU cycles are spent with ... the Dot-product measure, which you'll examine next!&lt;/p&gt;

&lt;p&gt;To summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There's not much to learn by "experimenting with different indexes" on a sphere&lt;/li&gt;
&lt;li&gt;There's nothing deep to be found in "testing different similarities" on a sphere&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Dot-product and the sphere
&lt;/h2&gt;

&lt;p&gt;As we anticipated, Dot-product is the odd one of the bunch. But, to one's relief, on the unit sphere, &lt;em&gt;and only there&lt;/em&gt;, its results coincide exactly with the Cosine similarity.&lt;br&gt;
The reason becomes clear by looking at the definitions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mbu698boeypgk7m3eep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mbu698boeypgk7m3eep.png" alt="Image description" width="800" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fleyb48gk46iqbrdgjwpo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fleyb48gk46iqbrdgjwpo.png" alt="Image description" width="597" height="107"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One quickly notices that on the sphere the norms can indeed be omitted, since in that case we have &lt;strong&gt;|v|=1&lt;/strong&gt; for all vectors &lt;strong&gt;v&lt;/strong&gt;: it follows that &lt;strong&gt;Scos = Sdot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can then treat the Dot-product similarity as a fully valid substitute for the Cosine similarity, with the advantage that the former requires the least amount of computation. In short, &lt;strong&gt;if you can work on the sphere&lt;/strong&gt;, i.e. if you can ensure all vectors have unit norm, &lt;strong&gt;there is no real reason to use anything other than Dot-product&lt;/strong&gt; in your vector search tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leaving the sphere (i.e. arbitrary vectors)
&lt;/h2&gt;

&lt;p&gt;One lesson so far is that you probably should work on the sphere when it makes sense to do so. If you are working with embedding vectors (whether computed for texts, images or anything really), probably this is the case. Note that you may need to normalize the vectors yourself, since some embedding models may output arbitrary-norm vectors.&lt;/p&gt;

&lt;p&gt;There are situations, however, in which the length of vectors also carries important information: an example may be two-dimensional vectors expressing positions on a map for a geo-search-based application. Typically, in these cases you should use the Euclidean measure.&lt;/p&gt;

&lt;p&gt;When working with arbitrary vectors, there are real differences between the Cosine and the Euclidean measures: in particular, you cannot just "translate similarities to similarities" without knowing the values of the two vectors themselves.&lt;/p&gt;

&lt;p&gt;In other words, you will get &lt;em&gt;different&lt;/em&gt; top results when running the same query on the same dataset depending on the measure employed. Let's capture the reason with a drawing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9yz7zaeo11idnayuxrd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9yz7zaeo11idnayuxrd.png" alt="Image description" width="559" height="592"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Caption: Unless your vectors are all of unit length, Euclidean and Cosine similarities tell a different story. Which is the vector closest to &lt;strong&gt;A&lt;/strong&gt;: &lt;strong&gt;B&lt;/strong&gt; or &lt;strong&gt;C&lt;/strong&gt;? (Solution: according to the Euclidean measure, &lt;strong&gt;C&lt;/strong&gt; is most similar to &lt;strong&gt;A&lt;/strong&gt;, while the Cosine similarity has &lt;strong&gt;B&lt;/strong&gt; ranking first.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, with arbitrary vectors, the question whether to use Euclidean or Cosine similarity is more substantial. But, as anticipated earlier, if you choose Cosine you might as well have normalized your vectors at ingestion time to have them on a sphere ... and have in practice used Dot-product! Otherwise, yours is one of those cases for which only the Euclidean choice makes sense: for example, your vectors are the three-dimensional coordinates of inhabited planets and your app helps galactic hitchhikers locate the civilizations closest to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  That pesky Dot-product
&lt;/h2&gt;

&lt;p&gt;If you are not on the sphere, the Dot-product similarity is not just a "faster Cosine." In this case, the recommendation is not to use it altogether.&lt;/p&gt;

&lt;p&gt;The fundamental reason that makes Dot-product stand apart is that this measure, as a candidate way to assess distances, fails our intuition. (This reasoning requires us to speak of "distances," contrary to our earlier resolution – don't worry, we can keep this a very short and tangential digression.)&lt;/p&gt;

&lt;p&gt;Here is something we expect from any notion of "distance":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;if &lt;strong&gt;A&lt;/strong&gt; is very close to &lt;strong&gt;B&lt;/strong&gt;, and &lt;strong&gt;B&lt;/strong&gt; is very far from &lt;strong&gt;C&lt;/strong&gt;, then &lt;strong&gt;A&lt;/strong&gt; is very far from &lt;strong&gt;C&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It turns out that the Dot-product admits counter examples that falsify the above intuition (which can be shown to be a consequence of a stricter requirement called "triangle inequality," when taking a particular limiting case). One such counterexample is &lt;strong&gt;A = [1, 0]; B = [1, -1/M]; C = [1, M]&lt;/strong&gt; for sufficiently large values of &lt;strong&gt;M&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can get an intuitive picture of this problem as follows: the Dot-product measure looks at something like "shadows of the vectors as cast with the light source (the query vector) positioned at a certain specific position". Two vectors, whose shadows almost overlap on one wall, might turn out to be in fact not close at all if the light source is moved elsewhere (i.e. searching with a different query vector) and the shadow is cast on another wall.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1mmnfd48q628ttavbhx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1mmnfd48q628ttavbhx.png" alt="Image description" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caption: With the Dot-product measure, the notion of "similar vectors" depends on what query vector is the comparison anchored to. In the above depiction, the query vector is the light source, which projects objects to one wall or another – with dramatic effects on whether they appear to be close to each other or not. The concept of "being close to each other", therefore, is defined not just by the two items being compared (the rabbit and the pear in the cartoon).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfxzarjdg9c3fjm2oail.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfxzarjdg9c3fjm2oail.png" alt="Image description" width="727" height="539"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Caption: The quirks of Dot-product, seen in a more formal but otherwise analogous depiction to the "room with a pear and a rabbit" cartoon. If you look at the Dot product with the &lt;strong&gt;x&lt;/strong&gt; axis, i.e. the &lt;strong&gt;[1, 0]&lt;/strong&gt; axis, the two vectors turn out to be much dissimilar – while if you refer them to the &lt;strong&gt;[0, 1]&lt;/strong&gt; query vector they'll look very similar. The notion of "similar vectors" is not an intrinsic property of the vector pair itself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This oddball character of the Dot-product has three important consequences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, as its expected usage is that of a "faster replacement for Cosine on a sphere", as you step outside of the sphere, your Dot-product similarities are not in the zero-to-one interval anymore. This is a consequence of its mathematical definition: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77c9ki8jlhnbmi1uqxgc.png" alt="Image description" width="597" height="107"&gt; &lt;/li&gt;
&lt;li&gt;Second, there is generally no optimization effort aimed at the off-sphere usage of Dot-product. Should you insist on using this measure for arbitrary vectors (and it turns out &lt;a href="https://github.com/hemidactylus/astra_misc_resources/blob/main/dot-product-for-weighted-ranking/Dot-product-for-weigthed-1.ipynb" rel="noopener noreferrer"&gt;there are cases&lt;/a&gt; where this might have an appeal, if you know what you are doing), the performance implications may be somewhat of an uncharted territory.&lt;/li&gt;
&lt;li&gt;Third, to further elaborate on the previous point, the core idea of the indexes that make vector search so fast rely on precomputed data structures, stored in the index for use by all queries, that provide information on "what is close to what." But here one has a case where closeness changes drastically depending on the query vector being used! So, the effectiveness of such a precomputed structure is greatly hampered and each vector-search query needs much more effort in order to be executed satisfactorily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary table
&lt;/h2&gt;

&lt;p&gt;As we're about to change subject, let's wrap what we've seen so far:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;measure&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;domain&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;notes&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Euclidean&lt;/td&gt;
&lt;td&gt;sphere (unit-norm vectors)&lt;/td&gt;
&lt;td&gt;Consider switching to Cosine or Dot-product (just the numeric similarities change, ordering unchanged)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cosine&lt;/td&gt;
&lt;td&gt;sphere (unit-norm vectors)&lt;/td&gt;
&lt;td&gt;If you know you're on a sphere, switch to Dot-product (the only change is faster computation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dot-product&lt;/td&gt;
&lt;td&gt;sphere (unit-norm vectors)&lt;/td&gt;
&lt;td&gt;The best-performance measure on the sphere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Euclidean&lt;/td&gt;
&lt;td&gt;arbitrary vectors&lt;/td&gt;
&lt;td&gt;Use this if every aspect of the vector (incl. the norm) carries useful information&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cosine&lt;/td&gt;
&lt;td&gt;arbitrary vectors&lt;/td&gt;
&lt;td&gt;Consider normalizing vectors to unit norm at ingestion time and, once on the sphere, switching to Dot-product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dot-product&lt;/td&gt;
&lt;td&gt;arbitrary vectors&lt;/td&gt;
&lt;td&gt;Probably not what you want (ensure there's a strong reason to fall in this case); performance may be tricky&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The upcoming installment of this series is devoted to the specific choices made in Astra DB and Cassandra with regard to similarities: how to create tables, how to perform queries, what are the associated best practices and pitfalls. Keep reading to find out more – see you next time!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>vectordatabase</category>
      <category>cosine</category>
    </item>
    <item>
      <title>What We Learned When We Built a Vector Database-and Our Customers Started Using It (part 1)</title>
      <dc:creator>Stefano Lottini</dc:creator>
      <pubDate>Thu, 15 Feb 2024 00:55:30 +0000</pubDate>
      <link>https://dev.to/datastax/what-we-learned-when-we-built-a-vector-database-and-our-customers-started-using-it-part-1-1c6h</link>
      <guid>https://dev.to/datastax/what-we-learned-when-we-built-a-vector-database-and-our-customers-started-using-it-part-1-1c6h</guid>
      <description>&lt;p&gt;Everyone is talking about vectors these days. Cosines, ANN searches, normalizations, sentence embeddings—there’s so much to know, it can feel a bit overwhelming at times!&lt;/p&gt;

&lt;p&gt;In the last half year or so, the engineering team at DataStax has been busy delivering a performant, efficient, and scalable vector database experience. Our work spanned many areas, including – crucially – offering guidance to customers and helping them figure out how to get the best out of their vector-based applications.&lt;/p&gt;

&lt;p&gt;On this journey (which, as the saying goes, is far from over), we noticed recurring pitfalls, dead ends, and, generally speaking, "things one should've known earlier," on topics ranging from the basics all the way to weird corner cases one does not often think about. &lt;/p&gt;

&lt;p&gt;This four-part series of articles is an attempt to collect some of these findings for the benefit of the reader and their future vector-based endeavors. Although these posts are mostly about "mathematical" properties of vectors (which are valid irrespective of the backend you’re using), a few of the remarks will be specific to Apache Cassandra&lt;sup&gt;®&lt;/sup&gt;'s and DataStax Astra DB's stance on vectors.&lt;/p&gt;

&lt;p&gt;This first article covers general properties of vectors and tips to interact with them, independent of their origin and purpose. The second article will examine the various notions of vector similarity and their properties. In article three, we’ll take a closer look at the usage of Cassandra and Astra DB with vectors. Finally, we’ll offer a couple of quirks and corner cases to keep in mind in your Cassandra- or Astra DB-enabled vector applications and a small example of a full "migration between vector stores," with an emphasis on the pitfalls with distances and similarities.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is a vector?&lt;/strong&gt;&lt;br&gt;
A vector is a quantity used in geometry and most sciences to denote a phenomenon with a direction and a length (the mathematically rigorous readers, I am sure, will pardon this very practical definition). To describe the blowing of the wind at a given point, for instance, you might use a vector (the vector "length" would be then the wind intensity). With a certain choice of a reference basis (e.g. the x, y and z axes for three-dimensional vectors), a vector can be formulated as a list made of numbers (its three x, y, z components), as many numbers as is its &lt;strong&gt;dimensionality&lt;/strong&gt;. While typical vectors one can imagine in the "real world around us" are 3-dimensional or less, nothing prevents you from thinking of 10-dimensional, 768-dimensional, 1536-dimensional vectors (… or even infinite-dimensional vectors. But I digress).&lt;/p&gt;

&lt;p&gt;A vector, in short, is a way to denote a point belonging to a given space. You can picture a vector as an arrow, whose tip is the denoted point. It is an expectation from vector geometry that the "length" (or norm) of a vector be defined regardless which direction it is oriented, i.e. a vector space implies some meaningful notion of "rotation" for its vectors. &lt;/p&gt;

&lt;p&gt;Don’t confuse the length with the count of the components (i.e. the dimension, which is always a positive integer).&lt;br&gt;
Take &lt;em&gt;all&lt;/em&gt; possible 2-dimensional &lt;strong&gt;(x, y)&lt;/strong&gt; vectors: the points they describe form the whole of a flat plane! Now take only the vectors of length equal to one ("unit vectors"): their points form a circle (of radius one). Likewise, the unit length vectors with dimension three are a sphere in space. &lt;/p&gt;

&lt;p&gt;Below, I’ll speak of "spheres" in a broad sense, regardless of the dimension (circles, spheres and "hyperspheres" alike): so, when I say "vectors on a sphere," I actually just mean "vectors of some dimensionality whose length is equal to one."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Of similar and dissimilar vectors&lt;/strong&gt;&lt;br&gt;
Comparing numbers is easy: 10 is similar to 10.3 and very different from, say, 9979. Just look at (the absolute value of) their difference! What about vectors: how can you say whether two vectors are "similar", or "close", to each other?&lt;br&gt;
It turns out that with vectors there are several ways ("measures") to decide what "close to each other" might mean. &lt;/p&gt;

&lt;p&gt;The subtle differences between the available definitions are enough to warrant a deeper investigation... and a dedicated section later in this post!&lt;/p&gt;

&lt;p&gt;Even though these definitions differ in their mathematical formulation, they are all ways to quantify, with a number, the degree of similarity between vectors. Conceptually, higher similarity means the vectors are closer to each other, or – equivalently – their distance is &lt;em&gt;lower&lt;/em&gt;. This fact holds regardless of the "measure" being adopted.&lt;/p&gt;

&lt;p&gt;Here is a brief overview of the measures I will consider (which happen to be the choices available on Cassandra and Astra DB). Choosing to commit to a measure or another is largely based on the nature of the data you want to represent as vectors, and how you do it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Euclidean similarity: "how close to each other the tips of the two arrows are"&lt;/li&gt;
&lt;li&gt;Cosine similarity: "by how much are the two arrows pointing to the same &lt;em&gt;direction&lt;/em&gt; (regardless of the vectors' lengths)"&lt;/li&gt;
&lt;li&gt;Dot-product similarity: this is the oddball of the bunch. You'll see more about "Dot" later on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F114wgkjfylbkexw7lypw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F114wgkjfylbkexw7lypw.png" alt="Image description" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caption: Vectors in two dimensions, on the whole plane (left) and limited to a circle of radius one (a "two-dimensional sphere"). Vectors can be thought of as lists of numbers, but they are usually represented as arrows anchored at an origin point. The "angular distance" on the right is related to the Cosine similarity between two vectors: smaller angle means higher similarity. Likewise, vectors with a smaller Euclidean distance (pictured) are more similar.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are a few other definitions of vector similarity available; most are variations of the Euclidean family, but you need not be concerned with them here.&lt;/p&gt;

&lt;p&gt;Note that generally one prefers to think in terms of "similarity" rather than "distance," as the former lends itself to fewer mathematical complications and more immediate practical applicability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's get the terms right
&lt;/h2&gt;

&lt;p&gt;In a sense, this article is a mathematical essay in disguise. That means that even if I do my best to keep the number of formulae to a minimum, I still need to use the right concepts precisely and with rigor. For this reason, let's start with a few definitions.&lt;/p&gt;

&lt;p&gt;For this series of articles, I will consistently use the following terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dimension&lt;/strong&gt; or &lt;strong&gt;dimensionality&lt;/strong&gt;: denoted by &lt;strong&gt;d&lt;/strong&gt;, this amounts to how many numbers (&lt;em&gt;components&lt;/em&gt;) form the vector, or equivalently the number of independent directions in the vector space. For example, vectors denoting positions on a sheet of paper have &lt;strong&gt;d=2&lt;/strong&gt;, while for locations in the space around you vectors with &lt;strong&gt;d=3&lt;/strong&gt; are needed. &lt;strong&gt;d&lt;/strong&gt; is much higher than that for most AI-related vector applications. Comparing vectors with different dimensions just makes no sense.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Length&lt;/strong&gt; or &lt;strong&gt;norm&lt;/strong&gt;: for a vector &lt;strong&gt;v&lt;/strong&gt;, this is denoted by &lt;strong&gt;|v|&lt;/strong&gt;. It is the length of the arrow from tip to tail. One can calculate it as the square root of the sum of the squares of all the components:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgoz4c3u83elnbycu26za.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgoz4c3u83elnbycu26za.png" alt="Image description" width="273" height="161"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Measure&lt;/strong&gt;: a mental model of "what it means for vectors to be close/distant." One can consider a Cosine measure, an Euclidean measure, and so on. Choosing a measure does not mean committing to a precise formula just yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As mentioned above, I prefer using the notion of "similarity" over that of "distance" where possible. Cassandra and Astra DB never expose anything that is a "distance," only similarities; moreover, not all measures offer a natural and simple-to-understand "distance" to think about.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Similarity&lt;/strong&gt;: a numeric way to quantify how much two vectors &lt;strong&gt;v1&lt;/strong&gt; and &lt;strong&gt;v2&lt;/strong&gt; are close to each other, computed with some formula &lt;strong&gt;S(v1, v2)&lt;/strong&gt;. One expects that &lt;strong&gt;S(v1, v2) = S(v2, v1)&lt;/strong&gt;; one also requires this quantity to be higher for pairs of vectors that are more similar to each other. It is desirable (and often verified) that this quantity be bound within a known range: for Cassandra and Astra DB, similarities are chosen so as to lie between zero (most dissimilar, or very high-distance, vectors) and one (most similar). As you will see, though, there is a notable exception to this general rule!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unit sphere&lt;/strong&gt;: The set of all vectors with unit length (all the vectors &lt;strong&gt;v&lt;/strong&gt; for which &lt;strong&gt;|v| = 1&lt;/strong&gt;). When I say that vectors are "on a sphere", it is implied that they are on the unit sphere. This special (and very common) case unlocks a few nice properties.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the mathematically inclined, here are the relevant formulae behind the different similarities. In these, &lt;strong&gt;xi&lt;/strong&gt; with &lt;strong&gt;i=1,... d&lt;/strong&gt; denote the components of vector &lt;strong&gt;x&lt;/strong&gt; (i.e. each one of the numbers in the list representing &lt;strong&gt;x&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg20l82jo0fqvl5irnx0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg20l82jo0fqvl5irnx0t.png" alt="Image description" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every list of numbers is a vector
&lt;/h2&gt;

&lt;p&gt;An implied assumption when thinking of vectors is that rotations in their space should "make sense" (pardon the non-rigorous parlance—you're not reading a linear algebra textbook). This essentially amounts to their components being "of the same kind", and is critical for a proper interpretation of the "similarity" between vectors (whatever its precise definition).&lt;/p&gt;

&lt;p&gt;Suppose you associate a list of numbers such as &lt;code&gt;[price, average_rating]&lt;/code&gt; to searchable ecommerce items. These are heterogeneous quantities. As a consequence, it is not well defined what we mean by "two items having distance 5 from each other": the lack of a natural way to rotate vectors in this space invalidates the notion of similarity as well. What a downer!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F201rc739dw9xvs2m1r20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F201rc739dw9xvs2m1r20.png" alt="Image description" width="738" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caption: Which item is "closer" to the mousepad: the t-shirt or the paperweight? Certainly geometry alone cannot easily answer such a question. In other words, what we are saying is that the problem of comparing a distance of $5 on the money axis to a distance of 5 on the rating axis is&lt;/em&gt; not just &lt;em&gt;a geometry problem.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A practical rule then is the following: applying vector search methods is a sensible approach, generally, if and only if the dimensions are of the same kind – in other words, if one can think of summing the numbers in the list in a meaningful way. &lt;/p&gt;

&lt;p&gt;This is the case, for instance, for a vector expressing a position on a map as &lt;code&gt;[meters_north_of_home, meters_west_of_home]&lt;/code&gt;, but also – crucially – for the sentence embedding vectors that are being used so fruitfully in the GenAI world nowadays.&lt;/p&gt;

&lt;p&gt;There are, indeed, advanced, "non-conventional" cases where one might concoct and use such a pseudo-vector – &lt;a href="https://github.com/hemidactylus/astra_misc_resources/blob/main/dot-product-for-weighted-ranking/Dot-product-for-weigthed-1.ipynb" rel="noopener noreferrer"&gt;here's an example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Coming up next, we’ll explore one of the most important choices one must face when designing a vector-powered application: which &lt;em&gt;similarity measure&lt;/em&gt; should be adopted? We briefly covered the Euclidean, Cosine and Dot-product options (their virtues and their differences) but are these similarities so dissimilar after all?  Stay tuned to find out – see you next time!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>vector</category>
      <category>database</category>
    </item>
  </channel>
</rss>
