CoolFace
Modelpublic

CoRover/Hybrid-Finsler-Assymetric-Embeddings

sourceHugging Facemitupdated 5mo agoView on Hugging Face
1likes
Model Card

Hybrid Finsler Flow Embedding

A fine-tuned embedding model that adds directional asymmetry to standard sentence embeddings using Finsler geometry (Randers metric). Built on top of all-MiniLM-L6-v2, it learns which direction text flows — enabling retrieval systems that answer "what comes next?" instead of just "what is similar?"

The Core Idea

Standard RAG uses cosine similarity: sim(A, B) == sim(B, A). This is symmetric — it finds relevant chunks but has no notion of order or progression.

Finsler geometry introduces asymmetric distance:

d(A → B) ≠ d(B → A)

If A naturally leads to B (e.g., "boil the water" → "add pasta"), then d(A→B) < d(B→A). This directional signal is encoded in a learned drift vector (omega) per text, using the Randers metric:

d(x → y) = ||y - x|| + ⟨omega_x, y - x⟩

Architecture

Input text
    │
    ▼
MiniLM Encoder (trainable, 384-dim)
    │
    ├──► Drift Head    → omega  (384-dim, ||omega|| < 0.95)
    ├──► Base output   → h      (384-dim, for FAISS retrieval)
    └──► Semantic Head → s      (128-dim, L2-normalized, for contrastive loss)

What each output is for

OutputDimUse
h384FAISS cosine retrieval (stage 1 candidate fetch)
omega384Randers distance computation (stage 2 reranking)
s128Semantic similarity (training only)

Training Loss (3 components)

L = L_asymmetry + λ_sem * L_semantic + λ_sep * L_separation
ComponentPurpose
L_asymmetryd(A→B) << d(B→A) for sequential pairs
L_semanticInfoNCE contrastive loss on s — preserves semantic space
L_separationPushes unrelated pairs apart in h space

The key insight over a pure Finsler model: L_semantic prevents the drift head from distorting the base embedding space, keeping h suitable for standard cosine retrieval.

Checkpoints

FileEpochsNotes
hybrid_finsler_final.pt80Recommended
hybrid_finsler_epoch50.pt50Intermediate checkpoint

Quick Start

Install dependencies

bash
pip install torch transformers faiss-cpu numpy huggingface_hub

Download model files

python
from huggingface_hub import snapshot_download

snapshot_download(
    repo_id="CoRover/Hybrid-Finsler-Assymetric-Embeddings",
    local_dir="./finsler_model"
)

Then cd finsler_model or adjust paths below accordingly.

FinslerRAG pipeline (recommended)

python
import sys
sys.path.append(".")
from run_finsler import FinslerRAG

rag = FinslerRAG("hybrid_finsler_final.pt")

docs = [
    {"text": "Bring water to a rolling boil.",     "metadata": {"step": 1}},
    {"text": "Add salt to the boiling water.",      "metadata": {"step": 2}},
    {"text": "Add pasta to the boiling water.",     "metadata": {"step": 3}},
    {"text": "Cook pasta for 8-10 minutes.",        "metadata": {"step": 4}},
    {"text": "Drain the pasta and add sauce.",      "metadata": {"step": 5}},
]
rag.index_documents(docs)

# What comes next after boiling water?
results = rag.query_progressive("I have boiled the water, now what?", top_k=3)
for r in results:
    print(f"Step {r['metadata']['step']}: {r['text']}")
# Step 2: Add salt to the boiling water.
# Step 3: Add pasta to the boiling water.
# Step 4: Cook pasta for 8-10 minutes.

Load model directly (embeddings only)

python
import sys, torch
from transformers import AutoTokenizer
sys.path.append(".")
from run_finsler import HybridFinslerModel

model = HybridFinslerModel(device="cpu")
model.load_state_dict(torch.load("hybrid_finsler_final.pt", map_location="cpu"))
model.eval()

tokenizer = AutoTokenizer.from_pretrained("sentence-transformers/all-MiniLM-L6-v2")

def encode(text):
    tok = tokenizer(text, return_tensors="pt", truncation=True, max_length=128)
    with torch.no_grad():
        h, omega, s = model(tok.input_ids, tok.attention_mask)
    return h.squeeze().numpy(), omega.squeeze().numpy()

h_a, omega_a = encode("Boil the water")
h_b, omega_b = encode("Add pasta to the water")

import numpy as np
def randers(h_src, omega_src, h_tgt):
    diff = h_tgt - h_src
    return np.linalg.norm(diff) + np.dot(omega_src, diff)

d_forward  = randers(h_a, omega_a, h_b)   # small  (natural direction)
d_backward = randers(h_b, omega_b, h_a)   # large  (unnatural direction)
print(f"d(A→B) = {d_forward:.4f}")
print(f"d(B→A) = {d_backward:.4f}")
print(f"Asymmetric: {abs(d_forward - d_backward) > 1e-4}")

Query Modes

FinslerRAG.query() supports 4 retrieval modes:

ModeFormulaBest for
"cosine"1 - cosine_sim(h_q, h_chunk)Standard semantic search
"randers"d(query → chunk)Directional / "what follows"
"hybrid"α * cosine_dist + (1-α) * randers_fwdBalanced relevance + direction
"progressive"Cosine filter → Randers sortOrdered next-step retrieval
python
# Hybrid with custom alpha (default alpha=0.5)
results = rag.query("my query", mode="hybrid", alpha=0.7, top_k=5)

# Progressive: returns relevant chunks in chronological order
results = rag.query_progressive("my query", top_k=5, forward_only=True)

query_progressive result fields

python
{
    "text":             "Add pasta to the boiling water.",
    "metadata":         {"step": 3},
    "score":            float,          # randers_forward (lower = further ahead)
    "cosine_score":     float,          # FAISS cosine similarity
    "randers_forward":  float,          # d(query → chunk)
    "randers_backward": float,          # d(chunk → query)
    "is_forward":       bool,           # randers_forward < randers_backward
    "direction_ratio":  float,          # randers_backward / randers_forward
    "progression_rank": int,            # 1 = earliest next step
}

Save and Load Index

python
# Save
rag.save("my_index/")

# Load in a new session (no need to re-encode documents)
rag2 = FinslerRAG("hybrid_finsler_final.pt")
rag2.load("my_index/")
results = rag2.query("my query")

Benchmarks

Evaluated on WikiHow procedural pairs (held-out articles not seen during training).

Temporal Direction Classification (TDC)

Task: Given a pair (A, B), classify forward vs. reversed procedural step. Decision rule: "forward" if d(A→B) < d(B→A). 300 forward + 300 backward pairs.

ModelAccuracyF1
Random (chance)0.49670.4664
Hybrid Finsler (Randers)0.73330.7333
Pure Finsler0.96700.9670

Next Step Retrieval (NSR)

Task: Retrieve the correct next step from 20 candidates (1 true + 19 distractors). 200 evaluation queries.

ModelR@1R@5MRR
Baseline (Cosine)0.66500.92000.7654
Hybrid Finsler (Cosine)0.39500.92000.5993
Hybrid Finsler (Randers)0.46000.87000.6348
Pure Finsler0.40500.93500.6210
Hybrid (Randers) trades R@1 for directional ordering — best used with cosine pre-filtering (see query_progressive).

Asymmetry Ratio

500 in-distribution forward pairs (sᵢ, sᵢ₊₁). Ratio = d(sᵢ₊₁→sᵢ) / d(sᵢ→sᵢ₊₁).

ModelMean B/F RatioCorrect Direction (>1×)
Baseline (Cosine)1.00×50.0%
Hybrid Finsler (Cosine)1.00×50.0%
Hybrid Finsler (Randers)6.31×69.6%
Pure Finsler14.34×96.8%

Step Ordering Accuracy (SOA)

Task: Kendall's τ between model-induced ordering and ground-truth step order. 5 held-out articles.

MethodKendall's τ
Baseline (Cosine)0.4667
Hybrid Finsler (Randers)0.4400
Hybrid Finsler (Cosine)0.4133

Semantic Clustering Quality

K-Means (k=10) on embeddings for 200 steps from 10 held-out articles.

ModelARINMI
Baseline (Cosine)0.40170.5637
Hybrid Finsler (h only)0.11640.3332
Hybrid Finsler (h+ω)0.11830.3477
Pure Finsler0.08470.2780
Semantic clustering is limited — a known trade-off between flow geometry and topical clustering in a shared embedding space.

Recommended Model by Use Case

Use CaseRecommended
Temporal direction classificationPure Finsler (strongest direction)
Step ordering / procedural flowHybrid Finsler (Randers) ← this model
RAG where direction mattersHybrid Finsler (Randers) with cosine filtering
General semantic searchBaseline (Cosine / standard MiniLM)
Topic-based document clusteringBaseline (Cosine / standard MiniLM)

When to use this model

Good fit:

  • —Procedural documents (tutorials, recipes, workflows, SOPs)
  • —Sequential knowledge bases where order matters
  • —RAG systems that need to answer "what should I do next?"
  • —Any domain where A → B has a different meaning than B → A

Not the best fit:

  • —Pure semantic similarity tasks (use standard MiniLM instead)
  • —Non-sequential corpora (FAQs, encyclopedias, product descriptions)

Technical Details

PropertyValue
Base modelsentence-transformers/all-MiniLM-L6-v2
Hidden dim384
Omega dim384 (constrained: `\\omega\\< 0.95`)
Semantic head dim128
Training epochs80
Retrieval backendFAISS IndexFlatIP (cosine)
DeviceCPU / CUDA
Model size~88 MB

Citation

If you use this model, please cite:

bibtex
@misc{hybrid-finsler-flow,
  title  = {Hybrid Finsler Flow Embedding},
  author = {Abhilash Dable},
  year   = {2025},
  note   = {Fine-tuned MiniLM with Randers metric for directional RAG}
}