mirror of
https://github.com/lancedb/lancedb.git
synced 2026-08-31 18:48:25 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| de14120bbe | |||
| fa342e7df4 | |||
| 6689192cee | |||
| dbec598610 | |||
| 8f6e7ce4f3 | |||
| b482f41bf4 |
@@ -224,7 +224,6 @@ This embedding function supports ingesting images as both bytes and urls. You ca
|
||||
!!! info
|
||||
LanceDB supports ingesting images directly from accessible links.
|
||||
|
||||
|
||||
```python
|
||||
|
||||
db = lancedb.connect(tmp_path)
|
||||
@@ -290,4 +289,67 @@ print(actual.label)
|
||||
|
||||
```
|
||||
|
||||
### Imagebind embeddings
|
||||
We have support for [imagebind](https://github.com/facebookresearch/ImageBind) model embeddings. You can download our version of the packaged model via - `pip install imagebind-packaged==0.1.2`.
|
||||
|
||||
This function is registered as `imagebind` and supports Audio, Video and Text modalities(extending to Thermal,Depth,IMU data):
|
||||
|
||||
| Parameter | Type | Default Value | Description |
|
||||
|---|---|---|---|
|
||||
| `name` | `str` | `"imagebind_huge"` | Name of the model. |
|
||||
| `device` | `str` | `"cpu"` | The device to run the model on. Can be `"cpu"` or `"gpu"`. |
|
||||
| `normalize` | `bool` | `False` | set to `True` to normalize your inputs before model ingestion. |
|
||||
|
||||
Below is an example demonstrating how the API works:
|
||||
|
||||
```python
|
||||
db = lancedb.connect(tmp_path)
|
||||
registry = EmbeddingFunctionRegistry.get_instance()
|
||||
func = registry.get("imagebind").create()
|
||||
|
||||
class ImageBindModel(LanceModel):
|
||||
text: str
|
||||
image_uri: str = func.SourceField()
|
||||
audio_path: str
|
||||
vector: Vector(func.ndims()) = func.VectorField()
|
||||
|
||||
# add locally accessible image paths
|
||||
text_list=["A dog.", "A car", "A bird"]
|
||||
image_paths=[".assets/dog_image.jpg", ".assets/car_image.jpg", ".assets/bird_image.jpg"]
|
||||
audio_paths=[".assets/dog_audio.wav", ".assets/car_audio.wav", ".assets/bird_audio.wav"]
|
||||
|
||||
# Load data
|
||||
inputs = [
|
||||
{"text": a, "audio_path": b, "image_uri": c}
|
||||
for a, b, c in zip(text_list, audio_paths, image_paths)
|
||||
]
|
||||
|
||||
#create table and add data
|
||||
table = db.create_table("img_bind", schema=ImageBindModel)
|
||||
table.add(inputs)
|
||||
```
|
||||
|
||||
Now, we can search using any modality:
|
||||
|
||||
#### image search
|
||||
```python
|
||||
query_image = "./assets/dog_image2.jpg" #download an image and enter that path here
|
||||
actual = table.search(query_image).limit(1).to_pydantic(ImageBindModel)[0]
|
||||
print(actual.text == "dog")
|
||||
```
|
||||
#### audio search
|
||||
|
||||
```python
|
||||
query_audio = "./assets/car_audio2.wav" #download an audio clip and enter path here
|
||||
actual = table.search(query_audio).limit(1).to_pydantic(ImageBindModel)[0]
|
||||
print(actual.text == "car")
|
||||
```
|
||||
#### Text search
|
||||
You can add any input query and fetch the result as follows:
|
||||
```python
|
||||
query = "an animal which flies and tweets"
|
||||
actual = table.search(query).limit(1).to_pydantic(ImageBindModel)[0]
|
||||
print(actual.text == "bird")
|
||||
```
|
||||
|
||||
If you have any questions about the embeddings API, supported models, or see a relevant model missing, please raise an issue [on GitHub](https://github.com/lancedb/lancedb/issues).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -31,7 +31,7 @@ class ImageBindEmbeddings(EmbeddingFunction):
|
||||
six different modalities: images, text, audio, depth, thermal, and IMU data
|
||||
|
||||
to download package, run :
|
||||
`pip install imagebind@git+https://github.com/raghavdixit99/ImageBind`
|
||||
`pip install imagebind-packaged==0.1.2`
|
||||
"""
|
||||
|
||||
name: str = "imagebind_huge"
|
||||
|
||||
@@ -113,5 +113,5 @@ class OpenAIEmbeddings(TextEmbeddingFunction):
|
||||
if self.organization:
|
||||
kwargs["organization"] = self.organization
|
||||
if self.api_key:
|
||||
kwargs["api_key"] = self
|
||||
kwargs["api_key"] = self.api_key
|
||||
return openai.OpenAI(**kwargs)
|
||||
|
||||
@@ -271,7 +271,8 @@ class LanceQueryBuilder(ABC):
|
||||
and also the "_distance" column which is the distance between the query
|
||||
vector and the returned vectors.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
# raise NotImplementedError
|
||||
self.to_arrow()
|
||||
|
||||
def to_list(self) -> List[dict]:
|
||||
"""
|
||||
@@ -434,12 +435,12 @@ class LanceVectorQueryBuilder(LanceQueryBuilder):
|
||||
self._vector_column = vector_column
|
||||
self._prefilter = False
|
||||
|
||||
def metric(self, metric: Literal["L2", "cosine"]) -> LanceVectorQueryBuilder:
|
||||
def metric(self, metric: Literal["L2", "cosine", "dot"]) -> LanceVectorQueryBuilder:
|
||||
"""Set the distance metric to use.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
metric: "L2" or "cosine"
|
||||
metric: "L2" or "cosine" or "dot"
|
||||
The distance metric to use. By default "L2" is used.
|
||||
|
||||
Returns
|
||||
|
||||
@@ -68,10 +68,16 @@ class RemoteTable(Table):
|
||||
|
||||
def list_indices(self):
|
||||
"""List all the indices on the table"""
|
||||
print(self._name)
|
||||
resp = self._conn._client.post(f"/v1/table/{self._name}/index/list/")
|
||||
return resp
|
||||
|
||||
def index_stats(self, index_uuid: str):
|
||||
"""List all the indices on the table"""
|
||||
resp = self._conn._client.post(
|
||||
f"/v1/table/{self._name}/index/{index_uuid}/stats/"
|
||||
)
|
||||
return resp
|
||||
|
||||
def create_scalar_index(
|
||||
self,
|
||||
column: str,
|
||||
@@ -290,6 +296,7 @@ class RemoteTable(Table):
|
||||
return LanceVectorQueryBuilder(self, query, vector_column_name)
|
||||
|
||||
def _execute_query(self, query: Query) -> pa.Table:
|
||||
print("query metric", query.metric)
|
||||
if (
|
||||
query.vector is not None
|
||||
and len(query.vector) > 0
|
||||
|
||||
@@ -118,7 +118,8 @@ def _append_vector_col(data: pa.Table, metadata: dict, schema: Optional[pa.Schem
|
||||
functions = EmbeddingFunctionRegistry.get_instance().parse_functions(metadata)
|
||||
for vector_column, conf in functions.items():
|
||||
func = conf.function
|
||||
if vector_column not in data.column_names:
|
||||
no_vector_column = vector_column not in data.column_names
|
||||
if no_vector_column or pc.all(pc.is_null(data[vector_column])).as_py():
|
||||
col_data = func.compute_source_embeddings_with_retry(
|
||||
data[conf.source_column]
|
||||
)
|
||||
@@ -126,9 +127,16 @@ def _append_vector_col(data: pa.Table, metadata: dict, schema: Optional[pa.Schem
|
||||
dtype = schema.field(vector_column).type
|
||||
else:
|
||||
dtype = pa.list_(pa.float32(), len(col_data[0]))
|
||||
data = data.append_column(
|
||||
pa.field(vector_column, type=dtype), pa.array(col_data, type=dtype)
|
||||
)
|
||||
if no_vector_column:
|
||||
data = data.append_column(
|
||||
pa.field(vector_column, type=dtype), pa.array(col_data, type=dtype)
|
||||
)
|
||||
else:
|
||||
data = data.set_column(
|
||||
data.column_names.index(vector_column),
|
||||
pa.field(vector_column, type=dtype),
|
||||
pa.array(col_data, type=dtype),
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
@@ -1514,7 +1522,7 @@ class LanceTable(Table):
|
||||
|
||||
def _execute_query(self, query: Query) -> pa.Table:
|
||||
ds = self.to_lance()
|
||||
|
||||
print("metric:", query.metric)
|
||||
return ds.to_table(
|
||||
columns=query.columns,
|
||||
filter=query.filter,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import sys
|
||||
from typing import List, Union
|
||||
|
||||
import lance
|
||||
import lancedb
|
||||
@@ -23,6 +24,8 @@ from lancedb.embeddings import (
|
||||
EmbeddingFunctionRegistry,
|
||||
with_embeddings,
|
||||
)
|
||||
from lancedb.embeddings.base import TextEmbeddingFunction
|
||||
from lancedb.embeddings.registry import get_registry, register
|
||||
from lancedb.pydantic import LanceModel, Vector
|
||||
|
||||
|
||||
@@ -112,3 +115,34 @@ def test_embedding_function_rate_limit(tmp_path):
|
||||
table.add([{"text": "hello world"}])
|
||||
table.add([{"text": "hello world"}])
|
||||
assert len(table) == 2
|
||||
|
||||
|
||||
def test_add_optional_vector(tmp_path):
|
||||
@register("mock-embedding")
|
||||
class MockEmbeddingFunction(TextEmbeddingFunction):
|
||||
def ndims(self):
|
||||
return 128
|
||||
|
||||
def generate_embeddings(
|
||||
self, texts: Union[List[str], np.ndarray]
|
||||
) -> List[np.array]:
|
||||
"""
|
||||
Generate the embeddings for the given texts
|
||||
"""
|
||||
return [np.random.randn(self.ndims()).tolist() for _ in range(len(texts))]
|
||||
|
||||
registry = get_registry()
|
||||
model = registry.get("mock-embedding").create()
|
||||
|
||||
class LanceSchema(LanceModel):
|
||||
id: str
|
||||
vector: Vector(model.ndims()) = model.VectorField(default=None)
|
||||
text: str = model.SourceField()
|
||||
|
||||
db = lancedb.connect(tmp_path)
|
||||
tbl = db.create_table("optional_vector", schema=LanceSchema)
|
||||
|
||||
# add works
|
||||
expected = LanceSchema(id="id", text="text")
|
||||
tbl.add([expected])
|
||||
assert not (np.abs(tbl.to_pandas()["vector"][0]) < 1e-6).all()
|
||||
|
||||
Reference in New Issue
Block a user