Skip to content

Commit df12352

Browse files
Merge pull request #1954 from redis/DOC-5533-go-vec-set-embed-example
DOC-5533 Go vector set embeddings example
2 parents 23f44ed + d32ddd7 commit df12352

File tree

4 files changed

+539
-56
lines changed

4 files changed

+539
-56
lines changed

content/develop/clients/go/vecsearch.md

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ or JSON fields, Redis can retrieve documents that closely match the query in ter
2929
of their meaning.
3030

3131
In the example below, we use the
32-
[`huggingfaceembedder`](https://pkg.go.dev/github.com/henomis/[email protected]/embedder/huggingface)
33-
package from the [`LinGoose`](https://pkg.go.dev/github.com/henomis/[email protected])
34-
framework to generate vector embeddings to store and index with
32+
[`Hugot`](https://pkg.go.dev/github.com/knights-analytics/hugot)
33+
library to generate vector embeddings to store and index with
3534
Redis Query Engine. The code is first demonstrated for hash documents with a
3635
separate section to explain the
3736
[differences with JSON documents](#differences-with-json-documents).
@@ -47,38 +46,23 @@ for more information.
4746

4847
## Initialize
4948

50-
Start a new Go module with the following command:
51-
52-
```bash
53-
go mod init vecexample
54-
```
55-
56-
Then, in your module folder, install
57-
[`go-redis`]({{< relref "/develop/clients/go" >}})
58-
and the
59-
[`huggingfaceembedder`](https://pkg.go.dev/github.com/henomis/[email protected]/embedder/huggingface)
60-
package:
49+
First, install [`go-redis`]({{< relref "/develop/clients/go" >}})
50+
if you haven't already done so. Then, install
51+
[`Hugot`](https://pkg.go.dev/github.com/knights-analytics/hugot)
52+
using the following command:
6153

6254
```bash
63-
go get github.com/redis/go-redis/v9
64-
go get github.com/henomis/lingoose/embedder/huggingface
55+
go get github.com/knights-analytics/hugot
6556
```
6657

6758
Add the following imports to your module's main program file:
6859

6960
{{< clients-example set="home_query_vec" step="import" lang_filter="Go" >}}
7061
{{< /clients-example >}}
7162

72-
You must also create a [HuggingFace account](https://huggingface.co/join)
73-
and add a new access token to use the embedding model. See the
74-
[HuggingFace](https://huggingface.co/docs/hub/en/security-tokens)
75-
docs to learn how to create and manage access tokens. Note that the
76-
account and the `all-MiniLM-L6-v2` model that we will use to produce
77-
the embeddings for this example are both available for free.
78-
7963
## Add a helper function
8064

81-
The `huggingfaceembedder` model outputs the embeddings as a
65+
The `Hugot` model outputs the embeddings as a
8266
`[]float32` array. If you are storing your documents as
8367
[hash]({{< relref "/develop/data-types/hashes" >}}) objects, then you
8468
must convert this array to a `byte` string before adding it as a hash field.
@@ -119,11 +103,10 @@ and 384 dimensions, as required by the `all-MiniLM-L6-v2` embedding model.
119103

120104
## Create an embedder instance
121105

122-
You need an instance of the `huggingfaceembedder` class to
106+
You need an instance of the `FeatureExtractionPipeline` class to
123107
generate the embeddings. Use the code below to create an
124108
instance that uses the `sentence-transformers/all-MiniLM-L6-v2`
125-
model, passing your HuggingFace access token to the `WithToken()`
126-
method.
109+
model:
127110

128111
{{< clients-example set="home_query_vec" step="embedder" lang_filter="Go" >}}
129112
{{< /clients-example >}}
@@ -134,12 +117,12 @@ You can now supply the data objects, which will be indexed automatically
134117
when you add them with [`HSet()`]({{< relref "/commands/hset" >}}), as long as
135118
you use the `doc:` prefix specified in the index definition.
136119

137-
Use the `Embed()` method of `huggingfacetransformer`
120+
Use the `RunPipeline()` method of `FeatureExtractionPipeline`
138121
as shown below to create the embeddings that represent the `content` fields.
139122
This method takes an array of strings and outputs a corresponding
140-
array of `Embedding` objects.
141-
Use the `ToFloat32()` method of `Embedding` to produce the array of float
142-
values that we need, and use the `floatsToBytes()` function we defined
123+
array of `FeatureExtractionOutput` objects.
124+
The `Embeddings` field of `FeatureExtractionOutput` contains the array of float
125+
values that you need for the index. Use the `floatsToBytes()` function defined
143126
above to convert this array to a `byte` string.
144127

145128
{{< clients-example set="home_query_vec" step="add_data" lang_filter="Go" >}}
@@ -153,7 +136,7 @@ text. Redis calculates the similarity between the query vector and each
153136
embedding vector in the index as it runs the query. It then ranks the
154137
results in order of this numeric similarity value.
155138

156-
The code below creates the query embedding using `Embed()`, as with
139+
The code below creates the query embedding using `RunPipeline()`, as with
157140
the indexing, and passes it as a parameter when the query executes
158141
(see
159142
[Vector search]({{< relref "/develop/ai/search-and-query/query/vector-search" >}})
@@ -163,14 +146,14 @@ for more information about using query parameters with embeddings).
163146
{{< /clients-example >}}
164147

165148
The code is now ready to run, but note that it may take a while to complete when
166-
you run it for the first time (which happens because `huggingfacetransformer`
149+
you run it for the first time (which happens because `Hugot`
167150
must download the `all-MiniLM-L6-v2` model data before it can
168151
generate the embeddings). When you run the code, it outputs the following text:
169152

170153
```
171-
ID: doc:0, Distance:0.114169843495, Content:'That is a very happy person'
172-
ID: doc:1, Distance:0.610845327377, Content:'That is a happy dog'
173-
ID: doc:2, Distance:1.48624765873, Content:'Today is a sunny day'
154+
ID: doc:0, Distance:2.96992516518, Content:'That is a very happy person'
155+
ID: doc:1, Distance:17.3678302765, Content:'That is a happy dog'
156+
ID: doc:2, Distance:43.7771987915, Content:'Today is a sunny day'
174157
```
175158

176159
The results are ordered according to the value of the `vector_distance`
@@ -220,9 +203,9 @@ Apart from the `jdoc:` prefixes for the keys, the result from the JSON
220203
query is the same as for hash:
221204

222205
```
223-
ID: jdoc:0, Distance:0.114169843495, Content:'That is a very happy person'
224-
ID: jdoc:1, Distance:0.610845327377, Content:'That is a happy dog'
225-
ID: jdoc:2, Distance:1.48624765873, Content:'Today is a sunny day'
206+
ID: jdoc:0, Distance:2.96992516518, Content:'That is a very happy person'
207+
ID: jdoc:1, Distance:17.3678302765, Content:'That is a happy dog'
208+
ID: jdoc:2, Distance:43.7771987915, Content:'Today is a sunny day'
226209
```
227210

228211
## Learn more

content/develop/clients/go/vecsets.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
categories:
3+
- docs
4+
- develop
5+
- stack
6+
- oss
7+
- rs
8+
- rc
9+
- oss
10+
- kubernetes
11+
- clients
12+
description: Index and query embeddings with Redis vector sets
13+
linkTitle: Vector set embeddings
14+
title: Vector set embeddings
15+
weight: 35
16+
bannerText: Vector set is a new data type that is currently in preview and may be subject to change.
17+
bannerChildren: true
18+
---
19+
20+
A Redis [vector set]({{< relref "/develop/data-types/vector-sets" >}}) lets
21+
you store a set of unique keys, each with its own associated vector.
22+
You can then retrieve keys from the set according to the similarity between
23+
their stored vectors and a query vector that you specify.
24+
25+
You can use vector sets to store any type of numeric vector but they are
26+
particularly optimized to work with text embedding vectors (see
27+
[Redis for AI]({{< relref "/develop/ai" >}}) to learn more about text
28+
embeddings). The example below shows how to use the
29+
[`Hugot`](https://github.com/knights-analytics/hugot)
30+
library to generate vector embeddings and then
31+
store and retrieve them using a vector set with `go-redis`.
32+
33+
## Initialize
34+
35+
Start by [installing]({{< relref "/develop/clients/go#install" >}}) `go-redis` if you haven't already done so. Note that you need `go-redis`
36+
[v9.10.0](https://github.com/redis/go-redis/releases/tag/v9.10.0)
37+
or later to use vector sets.
38+
39+
Also, install `hugot`:
40+
41+
```bash
42+
go get github.com/knights-analytics/hugot
43+
```
44+
45+
In a new Go file, add the required imports:
46+
47+
{{< clients-example set="home_vecsets" step="import" lang_filter="Go" >}}
48+
{{< /clients-example >}}
49+
50+
These include the `Hugot` library to generate an embedding from a section of text.
51+
This example uses an instance of the `SentenceTransformer` model
52+
[`all-MiniLM-L6-v2`](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2)
53+
for the embeddings. This model generates vectors with 384 dimensions, regardless
54+
of the length of the input text, but note that the input is truncated to 256
55+
tokens (see
56+
[Word piece tokenization](https://huggingface.co/learn/nlp-course/en/chapter6/6)
57+
at the [Hugging Face](https://huggingface.co/) docs to learn more about the way tokens
58+
are related to the original text).
59+
60+
{{< clients-example set="home_vecsets" step="model" lang_filter="Go" >}}
61+
{{< /clients-example >}}
62+
63+
## Create the data
64+
65+
The example data is contained in a map with some brief
66+
descriptions of famous people:
67+
68+
{{< clients-example set="home_vecsets" step="data" lang_filter="Go" >}}
69+
{{< /clients-example >}}
70+
71+
## Add the data to a vector set
72+
73+
The next step is to connect to Redis and add the data to a new vector set.
74+
75+
The code below iterates through the `peopleData` map and adds corresponding
76+
elements to a vector set called `famousPeople`.
77+
78+
Use the
79+
`RunPipeline()` method of `embeddingPipeline` to generate the
80+
embedding as an array of `float32` values, then use a loop like the one
81+
shown below to convert the `float32` array to a `float64` array.
82+
You can then pass this array to the
83+
[`VAdd()`]({{< relref "/commands/vadd" >}}) command to set the embedding.
84+
85+
The call to `VAdd()` also adds the `born` and `died` values from the
86+
original map as attribute data. You can access this during a query
87+
or by using the [`VGetAttr()`]({{< relref "/commands/vgetattr" >}}) method.
88+
89+
{{< clients-example set="home_vecsets" step="add_data" lang_filter="Go" >}}
90+
{{< /clients-example >}}
91+
92+
## Query the vector set
93+
94+
You can now query the data in the set. The basic approach is to use the
95+
`RunPipeline()` method to generate another embedding vector for the query text.
96+
(This is the same method used to add the elements to the set.) Then, pass
97+
the query vector to [`VSim()`]({{< relref "/commands/vsim" >}}) to return elements
98+
of the set, ranked in order of similarity to the query.
99+
100+
Start with a simple query for "actors":
101+
102+
{{< clients-example set="home_vecsets" step="basic_query" lang_filter="Go" >}}
103+
{{< /clients-example >}}
104+
105+
This returns the following list of elements (formatted slightly for clarity):
106+
107+
```
108+
'actors': Masako Natsume, Chaim Topol, Linus Pauling, Marie Fredriksson,
109+
Maryam Mirzakhani, Marie Curie, Freddie Mercury, Paul Erdos
110+
```
111+
112+
The first two people in the list are the two actors, as expected, but none of the
113+
people from Linus Pauling onward was especially well-known for acting (and there certainly
114+
isn't any information about that in the short description text).
115+
As it stands, the search attempts to rank all the elements in the set, based
116+
on the information contained in the embedding model.
117+
You can use the `Count` parameter of `VSimWithArgs()` to limit the list of elements
118+
to just the most relevant few items:
119+
120+
{{< clients-example set="home_vecsets" step="limited_query" lang_filter="Go" >}}
121+
{{< /clients-example >}}
122+
123+
The reason for using text embeddings rather than simple text search
124+
is that the embeddings represent semantic information. This allows a query
125+
to find elements with a similar meaning even if the text is
126+
different. For example, the word "entertainer" doesn't appear in any of the
127+
descriptions but if you use it as a query, the actors and musicians are ranked
128+
highest in the results list:
129+
130+
{{< clients-example set="home_vecsets" step="entertainer_query" lang_filter="Go" >}}
131+
{{< /clients-example >}}
132+
133+
Similarly, if you use "science" as a query, you get the following results:
134+
135+
```
136+
'science': Marie Curie, Linus Pauling, Maryam Mirzakhani, Paul Erdos,
137+
Marie Fredriksson, Freddie Mercury, Masako Natsume, Chaim Topol
138+
```
139+
140+
The scientists are ranked highest but they are then followed by the
141+
mathematicians. This seems reasonable given the connection between mathematics
142+
and science.
143+
144+
You can also use
145+
[filter expressions]({{< relref "/develop/data-types/vector-sets/filtered-search" >}})
146+
with `VSimWithArgs()` to restrict the search further. For example,
147+
repeat the "science" query, but this time limit the results to people
148+
who died before the year 2000:
149+
150+
{{< clients-example set="home_vecsets" step="filtered_query" lang_filter="Go" >}}
151+
{{< /clients-example >}}
152+
153+
Note that the boolean filter expression is applied to items in the list
154+
before the vector distance calculation is performed. Items that don't
155+
pass the filter test are removed from the results completely, rather
156+
than just reduced in rank. This can help to improve the performance of the
157+
search because there is no need to calculate the vector distance for
158+
elements that have already been filtered out of the search.
159+
160+
## More information
161+
162+
See the [vector sets]({{< relref "/develop/data-types/vector-sets" >}})
163+
docs for more information and code examples. See the
164+
[Redis for AI]({{< relref "/develop/ai" >}}) section for more details
165+
about text embeddings and other AI techniques you can use with Redis.
166+
167+
You may also be interested in
168+
[vector search]({{< relref "/develop/clients/go/vecsearch" >}}).
169+
This is a feature of the
170+
[Redis query engine]({{< relref "/develop/ai/search-and-query" >}})
171+
that lets you retrieve
172+
[JSON]({{< relref "/develop/data-types/json" >}}) and
173+
[hash]({{< relref "/develop/data-types/hashes" >}}) documents based on
174+
vector data stored in their fields.

0 commit comments

Comments
 (0)