--- title: "Embeddings Module" description: "Text and graph embedding generation — Sentence-Transformers, FastEmbed, OpenAI, BGE, Ollama — with pooling strategies, caching, and GPU acceleration." icon: "vector-square" --- `semantica.embeddings` converts text and graph structures into dense vectors. These vectors power semantic search, entity resolution, GraphRAG retrieval, and Distance Intelligence across every Semantica module. A single provider-agnostic API abstracts Sentence-Transformers, FastEmbed, OpenAI, BGE, and Ollama behind one interface. ## Why Embeddings Matter Raw text can't be compared mathematically. Embeddings translate meaning into geometry — two semantically similar sentences produce vectors that are close together in high-dimensional space, even when they share no words. Semantica uses embeddings for: - **Semantic search** — find knowledge graph nodes by meaning, not just keywords - **Entity resolution** — detect that "Apple Inc." and "Apple Computer" refer to the same entity - **Deduplication** — `semantic_v2` strategy measures entity similarity via embedding distance - **GraphRAG retrieval** — hybrid vector + graph traversal for grounded LLM answers - **Distance Intelligence** — N×N semantic distance matrices across entity sets - **Semantic chunking** — detect topic shift boundaries in `TextSplitter(method="semantic_transformer")` ## Exported Classes | Class | Role | | --- | --- | | `EmbeddingGenerator` | Provider-agnostic entry point — handles batching, caching, and provider selection | | `TextEmbedder` | Text embedding with disk caching and automatic batch splitting | | `GraphEmbeddingManager` | Embed KG nodes and subgraphs for GraphRAG and distance intelligence | | `OpenAIStore` | OpenAI `text-embedding-3-small` / `text-embedding-3-large` provider | | `BGEStore` | BAAI/bge models via `sentence-transformers` — free, high-quality | | `FastEmbedStore` | ONNX-accelerated local embeddings — no CUDA required | | `LlamaStore` | Local Ollama embedding models | | `MeanPooling` | Default pooling strategy — best for retrieval and clustering | ## What You Get Main entry point — provider-agnostic, handles batching automatically across all backends. Text-specific with automatic batching, disk caching, and progress tracking. Node and subgraph embeddings for structural similarity and GraphRAG context assembly. Full lifecycle: embed → store → search in a single coordinated workflow. `OpenAIStore`, `BGEStore`, `FastEmbedStore`, `LlamaStore`, and `ProviderStoreFactory`. Mean, Max, CLS, Attention, and Hierarchical — control token-to-vector aggregation. ## Installation | Provider | Install Command | API Key Required | | -------- | --------------- | ---------------- | | Sentence-Transformers (default) | `pip install semantica` | No | | FastEmbed | `pip install "semantica[fastembed]"` | No | | BGE | `pip install semantica` | No (uses sentence-transformers) | | OpenAI | `pip install "semantica[llm-openai]"` | Yes — `OPENAI_API_KEY` | | Ollama (LlamaStore) | `pip install "semantica[llm-ollama]"` | No — local server | | All providers | `pip install "semantica[all]"` | Varies | Check which providers are available in your environment: ```python from semantica.embeddings import check_available_providers providers = check_available_providers() # → {"sentence_transformers": True, "fastembed": True, "openai": False, "ollama": True} ``` ## Quick Start ```python from semantica.embeddings import EmbeddingGenerator # Default — Sentence-Transformers, free, runs locally generator = EmbeddingGenerator() # Custom model via config dict generator = EmbeddingGenerator(config={"text": {"method": "sentence_transformers", "model_name": "BAAI/bge-large-en-v1.5"}}) ``` ```python embeddings = generator.generate_embeddings(["Text about AI", "Machine learning concepts"]) ``` ```python # Cosine similarity — 0.0 (unrelated) to 1.0 (identical meaning) score = generator.compare_embeddings(embeddings[0], embeddings[1], method="cosine") print(f"Similarity: {score:.3f}") ``` ```python from semantica.embeddings import VectorEmbeddingManager, TextEmbedder from semantica.vector_store import VectorStore vector_store = VectorStore(backend="faiss", dimension=384) manager = VectorEmbeddingManager( embedder=TextEmbedder(model="sentence-transformers"), vector_store=vector_store, ) ids = manager.embed_and_store(documents, metadata=metadata_list) results = manager.search("machine learning algorithms", top_k=10) for result in results: print(f"Score: {result.score:.3f} — {result.metadata['title']}") ``` ## Supported Models | Provider | Model | Dimension | Speed | Best For | | -------- | ----- | --------- | ----- | -------- | | `sentence-transformers` | `all-MiniLM-L6-v2` | 384 | Fast | Default — good balance of speed and quality | | `sentence-transformers` | `all-mpnet-base-v2` | 768 | Medium | Higher retrieval quality | | `bge` | `BAAI/bge-large-en-v1.5` | 1024 | Medium | State-of-the-art retrieval accuracy | | `bge` | `BAAI/bge-small-en-v1.5` | 384 | Fast | Lightweight, competitive quality | | `fastembed` | `BAAI/bge-small-en-v1.5` | 384 | Very fast | CPU-optimised, low-latency production | | `openai` | `text-embedding-3-small` | 1536 | API | Cost-effective OpenAI embedding | | `openai` | `text-embedding-3-large` | 3072 | API | Highest quality via OpenAI API | | `llama` (Ollama) | Any Ollama model | Varies | Local | Fully local, no API key | ## EmbeddingGenerator ```python from semantica.embeddings import EmbeddingGenerator # Default — Sentence-Transformers with all-MiniLM-L6-v2 generator = EmbeddingGenerator() # Custom model via set_text_model generator.set_text_model("sentence_transformers", "BAAI/bge-large-en-v1.5") embeddings = generator.generate_embeddings(texts) similarity = generator.compare_embeddings(embeddings[0], embeddings[1]) ``` Best for: default prototyping, no API key, good quality. ```python from semantica.embeddings import EmbeddingGenerator generator = EmbeddingGenerator() generator.set_text_model("fastembed", "BAAI/bge-small-en-v1.5") embeddings = generator.generate_embeddings(texts) ``` Best for: CPU-only production, lowest latency without GPU. ```python from semantica.embeddings import OpenAIStore import os store = OpenAIStore(api_key=os.getenv("OPENAI_API_KEY"), model="text-embedding-3-small") embedding = store.embed("Hello world") ``` Best for: highest quality (3-large), or matching an OpenAI LLM pipeline. ```python from semantica.embeddings import LlamaStore store = LlamaStore(model="llama3.2", base_url="http://localhost:11434") embedding = store.embed("Hello world") ``` Best for: air-gapped or privacy-sensitive environments — no data leaves your machine. ```python from semantica.embeddings import EmbeddingGenerator # Set device via text embedder config generator = EmbeddingGenerator(config={"text": {"device": "cuda"}}) # Apple Silicon (M1/M2/M3) generator = EmbeddingGenerator(config={"text": {"device": "mps"}}) ``` GPU reduces embedding time by 5–20× depending on batch size and model. ### Constructor Parameters | Parameter | Type | Default | Description | | --------- | ---- | ------- | ----------- | | `config` | `dict` | `None` | Config dict; `config["text"]` is passed to `TextEmbedder` | | `**kwargs` | | | Additional key/value config merged into `config` | Use `generator.set_text_model(method, model_name)` to switch the embedding model after construction. ## TextEmbedder Specialised for text workloads — adds automatic batching, progress tracking, and disk caching: ```python from semantica.embeddings import TextEmbedder embedder = TextEmbedder( model="sentence-transformers", cache_dir=".emb_cache", # persist embeddings to disk cache_ttl=86400, # cache expiry in seconds (24h); None = never expires batch_size=128, show_progress=True, ) # Single text embedding = embedder.embed("A knowledge graph connects entities with typed relationships.") # Batch — auto-splits into batch_size chunks, shows progress bar embeddings = embedder.embed_batch(texts, show_progress=True) ``` **Key behaviours:** - Cache is keyed on text content + model name — identical texts return cached vectors instantly - Progress bar uses `tqdm` in terminal; switches to `tqdm.notebook` in Jupyter automatically - Large batches (> 10k texts) are chunked internally to avoid OOM on GPU ## Provider Stores Use provider stores directly when you need fine-grained control over a single backend: ```python from semantica.embeddings import ( OpenAIStore, BGEStore, FastEmbedStore, LlamaStore, ProviderStoreFactory, ) import os # OpenAI store = OpenAIStore(api_key=os.getenv("OPENAI_API_KEY"), model="text-embedding-3-small") embedding = store.embed("Hello world") # BGE (Sentence-Transformers wrapper) store = BGEStore(model="BAAI/bge-large-en-v1.5", device="cpu") embedding = store.embed("Hello world") # FastEmbed — ONNX runtime, no CUDA required store = FastEmbedStore(model="BAAI/bge-small-en-v1.5") embedding = store.embed("Hello world") # Ollama — fully local store = LlamaStore(model="llama3.2", base_url="http://localhost:11434") embedding = store.embed("Hello world") # Auto-select from a name string — useful in config-driven pipelines store = ProviderStoreFactory.create(provider="bge", model="BAAI/bge-large-en-v1.5") ``` ## Pooling Strategies Transformer models produce one embedding per token. Pooling aggregates token embeddings into a single vector: ```python from semantica.embeddings import MeanPooling pooler = MeanPooling() pooled = pooler.pool(token_embeddings) # shape: (hidden_dim,) ``` Best for: retrieval, semantic search, and clustering — averages all token contributions. ```python from semantica.embeddings import MaxPooling pooler = MaxPooling() pooled = pooler.pool(token_embeddings) ``` Best for: capturing the presence of any feature — takes the max activation per dimension. ```python from semantica.embeddings import CLSPooling pooler = CLSPooling() pooled = pooler.pool(token_embeddings) ``` Best for: classification-style tasks; models explicitly trained with CLS pooling (BERT). ```python from semantica.embeddings import HierarchicalPooling # Chunk text, mean-pool within chunks, then mean-pool chunks pooler = HierarchicalPooling(chunk_size=512) pooled = pooler.pool(token_embeddings) ``` Best for: long documents exceeding the model's max sequence length — reports, papers, contracts. | Strategy | When to Use | | -------- | ----------- | | `mean` | Default for retrieval, semantic search, and clustering | | `max` | When you want to capture the presence of any feature, not average presence | | `cls` | Classification-style tasks; models explicitly trained with CLS pooling (BERT) | | `attention` | When token importance varies significantly; slower but more accurate | | `hierarchical` | Long documents exceeding model context length; reports, papers, contracts | ```python from semantica.embeddings import PoolingStrategyFactory pooler = PoolingStrategyFactory.create(strategy="mean") ``` ## GraphEmbeddingManager Embed graph nodes and subgraphs for structural similarity and GraphRAG context assembly: ```python from semantica.embeddings import GraphEmbeddingManager, TextEmbedder manager = GraphEmbeddingManager( text_embedder=TextEmbedder(model="sentence-transformers"), graph_store=graph_store, # optional — for persistence ) # Embed all nodes — uses node label + property text node_embeddings = manager.embed_nodes(kg) # Embed a subgraph centred on a node (for GraphRAG context) subgraph_embedding = manager.embed_subgraph( kg, center_node="Apple Inc.", hops=2, # include neighbours up to 2 hops away ) # Find semantically similar nodes by ID similar = manager.find_similar_nodes("apple_inc", top_k=5) for node_id, score in similar: print(f"{node_id}: {score:.3f}") ``` **Key behaviours:** - Node embedding combines the label, type, and all property values into a single text string before embedding - `hops=2` captures the local neighbourhood — increase for richer context, decrease for speed - Results from `find_similar_nodes` are sorted by cosine similarity descending ## Embedding Cache The disk cache avoids recomputing embeddings for unchanged text — critical for large corpora and repeated pipeline runs: ```python from semantica.embeddings import TextEmbedder embedder = TextEmbedder( model="sentence-transformers", cache_dir=".embeddings_cache", cache_ttl=3600, # seconds — None means cache never expires ) # First call: computes and caches embeddings = embedder.embed_batch(texts) # Second call (same texts): returns from cache instantly embeddings = embedder.embed_batch(texts) ``` The Distance Intelligence module (v0.5.0) uses the same cache to avoid recomputing embeddings during N×N matrix calculations across large entity sets. ## Similarity Computation ```python from semantica.embeddings import calculate_similarity # Cosine similarity — direction only, not magnitude; most common for text score = calculate_similarity(embedding_a, embedding_b, method="cosine") # → 0.0 (orthogonal / unrelated) to 1.0 (identical direction) # Euclidean distance converted to similarity score = calculate_similarity(embedding_a, embedding_b, method="euclidean") # Dot product — use when vectors are already normalised (equivalent to cosine) score = calculate_similarity(embedding_a, embedding_b, method="dot") ``` ## Convenience Functions ```python from semantica.embeddings import ( embed_text, generate_embeddings, calculate_similarity, pool_embeddings, check_available_providers, ) # Single text — fastest path emb = embed_text("Hello world", method="sentence_transformers") # Batch embs = generate_embeddings(texts, method="openai") # Check which providers are installed providers = check_available_providers() # → {"sentence_transformers": True, "fastembed": True, "openai": False} ``` ## Tips and Common Pitfalls **Dimension mismatch.** The dimension you pass to `VectorStore(dimension=...)` must exactly match your embedding model's output. `all-MiniLM-L6-v2` → 384, `all-mpnet-base-v2` → 768, `bge-large-en-v1.5` → 1024. Check with `generator.dimension` before creating the store. **Not normalising for cosine similarity.** If you compute cosine similarity directly (dot product), vectors must be L2-normalised first. `EmbeddingGenerator` normalises by default (`normalize=True`). If you disable it, use `calculate_similarity(..., method="cosine")` which normalises internally. **Sequence length limits.** Most models have a 512-token limit. Text beyond that is silently truncated. Use `TextSplitter(method="hierarchical")` + `HierarchicalPooling` for long documents. **Always use the same model for indexing and querying.** Vectors from different models are not comparable — they live in different vector spaces. Switching models requires re-embedding your entire corpus. **Cache invalidation.** The cache key is the text + model name. Switching models requires clearing the cache or using a different `cache_dir` — otherwise you'll get stale vectors silently returned. Store and search the generated embeddings. Chunk text before embedding for better retrieval quality. Distance Intelligence uses graph embeddings for semantic neighbourhoods. Semantic deduplication uses embedding distance for entity resolution.