embetter

repository·main·Indexed 19 days ago

https://github.com/koaning/embetter

A library providing scikit-learn compatible embedding tools for text, vision, and multi-modal data. It includes encoders for Sentence-Transformers, CLIP, Timm, and external APIs like OpenAI, Azure OpenAI, and Cohere, as well as utilities for data extraction via ColumnGrabber and KeyGrabber. Designed for proof-of-concepts and bulk-labeling pipelines, it supports incremental learning via partial_fit and provides caching mechanisms for performance.

Tokens
5.7K
Snippets
25
Records
33
Agent score
68%

What's inside embetter

  1. How embetter components work with scikit-learn

    main

    All embetter components are designed to be scikit-learn compatible. This means they can be used directly within sklearn.pipeline.Pipeline or make_pipeline.

    Key characteristics:

    • Stateless: Most components are pretrained and do not require a training step (they are used in fit_transform or transform).
    • Incremental Learning: All encoding tools support the scikit-learn partial_fit mechanic, allowing them to be used with scikit-partial for out-of-core datasets.
  2. Speed up embeddings using Modal and GPU

    main

    To accelerate embedding calculations (especially on large datasets), you can use Modal to run SentenceEncoder or ClipEncoder on remote hardware with a GPU. These encoders automatically detect and utilize available GPUs.

    When using Modal, ensure your container image includes embetter[text] and the necessary dependencies.

    import modal
    
    stub = modal.Stub("example-get-started")
    image = (modal.Image.debian_slim()
             .pip_install("simsity", "embetter[text]", "h5py")
             .run_commands("python -c 'from embetter.text import SentenceEncoder; SentenceEncoder()'"))
    
    @stub.function(image=image, gpu="any")
    def create(data):
        from embetter.text import SentenceEncoder
        return SentenceEncoder().transform(data)
    
    @stub.local_entrypoint()
    def main():
        data = read_text() # User defined
        X = create.call(data)
        print(f"Embedded shape: {X.shape}")
  3. Train lightweight 'Lite Embeddings'

    main

    For applications where speed is critical and heavy pretrained models are overkill, you can use learn_lite_doc_embeddings. This technique uses a combination of TfidfVectorizer and TruncatedSVD to create fast, lightweight representations.

    Key features:

    • Extremely fast training and transformation.
    • Can be saved to disk during the training call using the path parameter.
    • Useful for quick visualization (e.g., with UMAP and plot_text).
    import srsly
    from umap import UMAP
    from cluestar import plot_text
    from embetter.text import learn_lite_doc_embeddings
    
    # Load texts
    texts = [ex['text'] for ex in srsly.read_jsonl("datasets/new-dataset.jsonl")]
    
    # Train embeddings and optionally save to disk
    enc = learn_lite_doc_embeddings(texts, dim=300, path="stored/on/disk.emb")
    
    # Transform and visualize
    X_orig = enc.transform(texts)
    X = UMAP().fit_transform(X_orig)
    plot_text(X, texts)
  4. Cache embeddings using diskcache

    main

    To avoid expensive re-calculations of embeddings (especially when using external providers), use the embetter.utils.cached helper. This integrates with diskcache to store results on disk.

    When using cached, provide a unique string name for the cache. Subsequent calls to transform with the same input will be significantly faster as they fetch from the cache instead of re-encoding. You can also access precalculated embeddings directly using a diskcache.Cache instance with the same name used in cached.

    from embetter.text import SentenceEncoder
    from embetter.utils import cached
    
    # Wrap the encoder with a cache name
    encoder = cached("sentence-enc", SentenceEncoder('all-MiniLM-L6-v2'))
    
    examples = [f"text {i}" for i in range(10_000)]
    
    # First run: slow (writes to cache)
    encoder.transform(examples)
    
    # Second run: fast (reads from cache)
    encoder.transform(examples)
    
    # Direct access via diskcache
    from diskcache import Cache
    cache = Cache("sentence-enc")
    embedding = cache["this is a pretty long text, which is more expensive 0"]
  5. Install embetter

    main

    You can install the core package via pip. To minimize dependencies, you can install specific extras for text, vision, or all components.

    # Install core
    python -m pip install embetter
    
    # Install specific extras
    python -m pip install "embetter[text]"
    python -m pip install "embetter[vision]"
    python -m pip install "embetter[all]"
  6. Create a multi-modal image embedding pipeline

    main

    You can build a pipeline that takes image paths from a DataFrame, loads them as PIL.Image objects, and encodes them using CLIP. This uses ColumnGrabber, ImageLoader, and ClipEncoder.

    import pandas as pd
    from sklearn.pipeline import make_pipeline 
    from sklearn.linear_model import LogisticRegression
    from embetter.grab import ColumnGrabber
    from embetter.vision import ImageLoader
    from embetter.multi import ClipEncoder
    
    # Pipeline: Path -> PIL Image -> CLIP Embeddings
    image_emb_pipeline = make_pipeline(
      ColumnGrabber("img_path"),
      ImageLoader(convert="RGB"),
      ClipEncoder()
    )
    
    dataf = pd.DataFrame({
      "img_path": ["tests/data/thiscatdoesnotexist.jpeg"]
    })
    
    image_emb_pipeline.fit_transform(dataf)
  7. Create a text embedding pipeline

    main

    To create a pipeline that extracts text from a pandas DataFrame and converts it into embeddings using Sentence-Transformers, use ColumnGrabber followed by SentenceEncoder.

    Note: This requires the sbert extra: pip install 'embetter[sbert]'.

    import pandas as pd
    from sklearn.pipeline import make_pipeline 
    from sklearn.linear_model import LogisticRegression
    from embetter.grab import ColumnGrabber
    from embetter.text import SentenceEncoder
    
    # Pipeline to transform text column into embeddings
    text_emb_pipeline = make_pipeline(
      ColumnGrabber("text"),
      SentenceEncoder('all-MiniLM-L6-v2')
    )
    
    dataf = pd.DataFrame({
      "text": ["positive sentiment", "super negative"],
      "label_col": ["pos", "neg"]
    })
    
    # Get embeddings
    X = text_emb_pipeline.fit_transform(dataf, dataf['label_col'])
    
    # Full pipeline for classification
    text_clf_pipeline = make_pipeline(
      ColumnGrabber("text"),
      SentenceEncoder('all-MiniLM-L6-v2'),
      LogisticRegression()
    )
    text_clf_pipeline.fit(dataf, dataf['label_col']).predict(dataf)
  8. Use KeyGrabber to extract data from dictionary keys

    main

    Use embetter.grab.KeyGrabber to extract values associated with specific keys from a collection of dictionaries (or a pandas DataFrame where rows are treated as dictionaries). This is useful when your input data is structured as JSON-like objects and you need to pull out specific fields for embedding.

    from embetter.grab import KeyGrabber
    
    # Example usage (conceptual)
    grabber = KeyGrabber(key='target_field')
    data = grabber(data_list)
  9. Use the batched decorator for efficient processing

    main
    The embetter.utils.batched decorator allows you to wrap a function so that it processes inputs in batches. This is particularly useful when calling embedding models or APIs that perform better (or require) batching to maximize throughput.
  10. Use ColumnGrabber to extract data from pandas columns

    main

    Use embetter.grab.ColumnGrabber to extract specific columns from a pandas DataFrame. This is useful for pipelines where you need to isolate a single column (e.g., a 'text' or 'image' column) to be processed by an embedding model or other transformation steps.

    from embetter.grab import ColumnGrabber
    
    # Example usage (conceptual)
    grabber = ColumnGrabber(column_name='text')
    data = grabber(df)
  11. Use OpenAIEncoder for embeddings

    main

    The OpenAIEncoder allows you to generate embeddings using OpenAI's models. This typically requires an OpenAI API key configured in your environment.

    from embetter.external import OpenAIEncoder
    
    encoder = OpenAIEncoder()
    # Usage depends on the specific model configuration
  12. Use AzureOpenAIEncoder for embeddings

    main

    The AzureOpenAIEncoder is used to generate embeddings via the Azure OpenAI service. This requires Azure-specific configuration such as endpoint and deployment names.

    from embetter.external import AzureOpenAIEncoder
    
    encoder = AzureOpenAIEncoder()