Shuu12121's picture
Update README.md
c8d5cb6 verified
|
Raw History Blame Contribute Delete
17.2 kB
---
tags:
- sentence-transformers
- feature-extraction
- code-search
- code-embedding
- retrieval
- modernbert
- dense
base_model: Shuu12121/NightOwl-CodeEmbedding
pipeline_tag: feature-extraction
library_name: sentence-transformers
license: apache-2.0
datasets:
- Shuu12121/coir_hard_negative_datasets_v3_kd
- Shuu12121/owl_code_search_hard_negative_datasets_V2_kd
- Shuu12121/codeedit_hard_negative_datasets_kd
- CoIR-Retrieval/apps
- deepmind/code_contests
---
# NightOwl-CodeEmbedding-v2 🦉
`NightOwl-CodeEmbedding-v2` is a compact, 768-dimensional dense embedding model built for
code retrieval, code-edit retrieval, and technical question answering.
It is an updated version of
[`Shuu12121/NightOwl-CodeEmbedding`](https://huggingface.co/Shuu12121/NightOwl-CodeEmbedding)
(v1). Compared with v1, it:
* supports inputs of up to **4,096 tokens** (v1: 1,024), and
* is much stronger on competitive-programming retrieval (**AppsRetrieval 0.392 → 0.518**)
and long multi-turn queries (**CodeFeedbackMT 0.767 → 0.886**),
while staying within about 0.01 of v1 on every other task (see [MTEB Results](#mteb-results)).
The model uses CLS pooling with cosine similarity and does **not** require
`query:` / `passage:` style prefixes.
## What changed from v1
1. **Longer context (1,024 → 4,096 tokens).** v1 was trained with a 1,024-token limit, but
simply raising the limit to 4,096 — with no further training — improves long-input tasks
without hurting any task (MTEB 2.5.1, 11 tasks):
| Task | v1 @ 1,024 | v1 @ 4,096 |
| ---------------------- | ---------: | ---------: |
| CodeFeedbackMT | 0.7669 | 0.8870 |
| CodeTransOceanContest | 0.7595 | 0.7919 |
| CodeTransOceanDL | 0.3606 | 0.3747 |
| AppsRetrieval | 0.3918 | 0.3999 |
| Other 7 tasks | | +0.000 to +0.004 |
| **Mean of 11 tasks** | **0.6987** | **0.7152** |
About 66% of CodeFeedbackMT queries exceed 1,024 tokens, and the question to answer is
usually in the last turn, which v1 truncated away.
2. **Additional fine-tuning + weight interpolation (WiSE-FT).** v1 was fine-tuned further on
competitive-programming data plus a replay of its own training data (see
[Training](#training)). The released weights are the **average of v1 and the fine-tuned
weights (α = 0.5)**. The fully fine-tuned model reached a higher AppsRetrieval score
(0.540) but lost 0.02–0.06 on several other tasks. Interpolating at α = 0.5 keeps most of
the gain while removing almost all of the regressions.
## Highlights
* Compact (150.8M parameters), 768-d single-vector embeddings: plain dot-product / cosine
search that drops straight into any vector-database stack
* **4,096-token context**
* **0.7356 mean NDCG@10 on the 12 MTEB(Code, v1) tasks** (self-evaluated with MTEB 2.5.1;
v1's leaderboard score is 0.7091)
* Covers **eight programming languages**: the six CodeSearchNet languages plus Rust and
TypeScript
* Handles NL-to-code search, code-to-code retrieval, **code-edit retrieval**, and technical QA
* Decontaminated against the benchmark test sets used for evaluation
(see [Data Decontamination](#data-decontamination))
* Drop-in compatible with `sentence-transformers`; Apache-2.0 licensed
## Supported Languages
* Go, Java, JavaScript, PHP, Python, Ruby (CodeSearchNet languages)
* **Rust, TypeScript** (additional)
Performance on languages outside this set is untested and may vary.
## Usage
```python
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("Shuu12121/NightOwl-CodeEmbedding-v2")
print(model.max_seq_length) # 4096
queries = ["Python function that sorts a list in descending order"]
documents = [
"def sort_desc(values): return sorted(values, reverse=True)",
"def average(values): return sum(values) / len(values)",
]
query_embeddings = model.encode(queries)
document_embeddings = model.encode(documents)
# Cosine similarity (embeddings are normalized internally by similarity())
scores = model.similarity(query_embeddings, document_embeddings)
print(scores)
```
If memory or latency matters more than long-input quality, you can lower the limit, e.g.
`model.max_seq_length = 1024`.
## Model Details
| Property | Value |
| ----------------------- | --------------------------------------- |
| Base model | `Shuu12121/NightOwl-CodeEmbedding` (v1) |
| Backbone | `Shuu12121/NightOwl` |
| Architecture | ModernBERT |
| Parameters | 150,779,136 |
| Embedding dimension | 768 |
| Pooling | CLS pooling |
| Maximum sequence length | 4,096 tokens |
| Similarity | Cosine similarity |
| Query/document prefixes | Not required |
| Weight dtype | FP32 |
| Weight memory | 575 MiB |
| License | Apache-2.0 |
## MTEB Results
Evaluation setup:
* MTEB version: `2.5.1`
* Metric: `NDCG@10`
* Hardware: NVIDIA RTX PRO 6000 Blackwell
* Batch size: 64
* `max_seq_length`: 4,096 (v2), 1,024 (v1, its released setting)
Multi-subset task scores are reported as macro averages. v1 was re-evaluated with the same
MTEB version, so the two columns are directly comparable.
| Task | Split | v2 | v1 (1,024) | Diff |
| -------------------------------- | ------: | -----: | ---------: | -----: |
| AppsRetrieval | test | 0.5181 | 0.3918 | +0.1263 |
| COIRCodeSearchNetRetrieval | test | 0.8381 | 0.8426² | – |
| CodeEditSearchRetrieval | train¹ | 0.7480 | 0.7481 | −0.0001 |
| CodeFeedbackMT | test | 0.8856 | 0.7669 | +0.1187 |
| CodeFeedbackST | test | 0.8535 | 0.8526 | +0.0010 |
| CodeSearchNetCCRetrieval | test | 0.9093 | 0.9182 | −0.0089 |
| CodeSearchNetRetrieval | test | 0.8901 | 0.8924 | −0.0022 |
| CodeTransOceanContest | test | 0.8038 | 0.7595 | +0.0443 |
| CodeTransOceanDL | test | 0.3765 | 0.3606 | +0.0159 |
| CosQA | test | 0.4333 | 0.4281 | +0.0051 |
| StackOverflowQA | test | 0.8679 | 0.8661 | +0.0019 |
| SyntheticText2SQL | test | 0.7030 | 0.7015 | +0.0015 |
| **Mean, 11 tasks (excl. COIRCodeSearchNet)** | | **0.7263** | **0.6987** | **+0.0276** |
| **Macro average, all 12 tasks** | | **0.7356** | – | |
| **CoIR macro average, 10 tasks** | | **0.7189** | – | |
¹ `CodeEditSearchRetrieval` does not provide a standard `test` split in MTEB, so the
official `train` split is used for evaluation. These examples were **not** used for
fine-tuning. See [Data Decontamination](#data-decontamination).
² v1 was not re-evaluated on COIRCodeSearchNetRetrieval (about 1M documents). This value is
from v1's model card (MTEB 2.15.1), so it is not strictly comparable.
The CoIR macro average uses the same 10 tasks as v1's card (all 12 except
CodeEditSearchRetrieval and CodeSearchNetRetrieval).
### Leaderboard
v1 is listed on the [MTEB(Code, v1)](https://huggingface.co/spaces/mteb/leaderboard)
leaderboard with a task mean of 70.91. v2's results above are self-evaluated and have not
yet been submitted to the leaderboard.
As with v1, MTEB(Code, v1) is largely **in-domain** for this model (8% zero-shot): it was
trained on most of these task families, so its scores reflect in-domain retrieval rather
than zero-shot transfer. Comparisons with models that are 100% zero-shot (e.g.
`embeddinggemma-300m`, `Qwen3-Embedding`) are not apples-to-apples.
### Comparison with similar-sized models
Leaderboard task means (×100) for compact dense single-vector models. v2's value is
self-evaluated (MTEB 2.5.1) and is shown for reference.
| Model | Params | Emb. dim | Max tokens | Zero-shot | MTEB(Code, v1) ↑ |
| ---------------------------------------------------- | ------: | -------- | ---------: | --------: | ---------------: |
| **`NightOwl-CodeEmbedding-v2`** (this model) | 150.8M | 768 | 4,096 | 8% | **73.56†** |
| `NightOwl-CodeEmbedding` (v1) | 150.8M | 768 | 1,024 | 8% | 70.91 |
| `codefuse-ai/F2LLM-v2-160M` | 159M | 640 | 40,960 | 58% | 70.38 |
| `google/embeddinggemma-300m` | 308M | 768 | 2,048 | 100% | 68.76 |
| `codefuse-ai/F2LLM-v2-80M` | 80M | 320 | 40,960 | 58% | 67.97 |
| `ibm-granite/granite-embedding-311m-multilingual-r2` | 312M | 768 | 8,192 | 100% | 63.84 |
| _Larger single-vector reference_ | | | | | |
| `codefuse-ai/F2LLM-v2-0.6B` | 596M | 1,024 | 40,960 | 58% | 77.41 |
| `Qwen/Qwen3-Embedding-0.6B` | 596M | 1,024 | 32,768 | 100% | 75.42 |
† Self-evaluated with MTEB 2.5.1; not an official leaderboard entry.
## Training
### v1
v1 was fine-tuned from [`Shuu12121/NightOwl`](https://huggingface.co/Shuu12121/NightOwl)
with `CachedMultipleNegativesRankingLoss` (bidirectional query-to-document and
document-to-query) on 2,534,400 samples, each with one positive and 15 hard negatives mined
by `Qwen/Qwen3-Embedding-0.6B` (1 epoch, learning rate 6e-5, batch size 1024,
max sequence length 1,024). The data covers the CoIR task families of MTEB(Code, v1),
custom code–comment pairs in eight languages, and code-edit data derived from
`commitpackft`. See the
[v1 model card](https://huggingface.co/Shuu12121/NightOwl-CodeEmbedding) for details.
### v2
Starting from v1, the model was fine-tuned on:
1. **Competitive programming (problem statement → Python solution):** APPS train and the
CodeContests train split (up to 3 Python solutions per problem).
2. **Code translation (Python → C++):** one (Python, C++) solution pair per CodeContests
problem, targeting CodeTransOceanContest.
3. **Replay of v1's training data** to limit forgetting: samples from
`coir_hard_negative_datasets_v3_kd` (CodeFeedbackMT 4,000, CodeFeedbackST 3,000,
StackOverflowQA 3,000, CosQA 1,000, SyntheticText2SQL 2,000, CodeTransOceanContest 561)
and `codeedit_hard_negative_datasets_kd` (300 per language, 13 languages).
For data 1 and 2, 7 hard negatives per example were mined with `Qwen/Qwen3-Embedding-0.6B`;
solutions of the same problem (or of near-identical problems) were excluded from the
negatives. Data 3 reuses the hard negatives already in the datasets.
| Property | Value |
| -------------------- | ------------------------------------------------------------ |
| Loss | `CachedMultipleNegativesRankingLoss` (scale 100, bidirectional) |
| Negatives per anchor | 7 |
| Batching | one data source per batch (proportional sampling), no duplicates |
| Epochs | 1 |
| Learning rate | 2e-5 (cosine, 10% warmup) |
| Batch size | 64 |
| Max sequence length | 1,024 during training (4,096 at inference) |
| Weight interpolation | 0.5 × v1 + 0.5 × fine-tuned |
Interpolation results (MTEB 2.5.1, 11 tasks, `max_seq_length` 1,024):
| α (share of fine-tuned weights) | 0 (v1) | 0.3 | **0.5** | 0.7 | 1 (fine-tuned) |
| ------------------------------- | -----: | -----: | -------: | -----: | -------------: |
| AppsRetrieval | 0.3918 | 0.4692 | 0.5066 | 0.5285 | 0.5404 |
| Mean of 11 tasks | 0.6987 | 0.7081 | **0.7103** | 0.7076 | 0.6949 |
## Data Decontamination
Overlaps with the evaluation data were removed **before** training.
v1:
* Custom code–comment pair data vs. the **CodeSearchNet test split**
* `commitpackft`-derived code-edit data vs. the **CodeEditSearchRetrieval** evaluation data
v2 (additional data):
* APPS train problems whose statement has cosine similarity ≥ 0.95 to an **APPS test**
problem
* CodeContests problems that are the same Codeforces problem as an **APPS test** problem,
and problems whose statement has cosine similarity ≥ 0.90 to any APPS test or APPS train
problem
* Python → C++ pairs whose Python or C++ code has cosine similarity ≥ 0.95 to a
**CodeTransOceanContest test** query or its answer
Similarities were computed with v1. The replay data is a subset of v1's training data, so it
adds no new overlap.
For `CodeEditSearchRetrieval`, note that MTEB labels the evaluation split `train`. This is
just the official split name for the task; the evaluated examples were not included in this
model's fine-tuning data. The score should therefore be read as **in-domain generalization
on held-out benchmark examples**.
## Base Model: the NightOwl Backbone
The backbone, [`Shuu12121/NightOwl`](https://huggingface.co/Shuu12121/NightOwl), is a
ModernBERT-style code encoder **pre-trained from scratch**, tokenizer included.
* **Code-aware tokenizer:** a custom 50,368-token BPE tokenizer in which whitespace is
tokenized independently of adjacent words, so indentation gets its own tokens and the
vocabulary is not wasted on "indent + word" variants.
* **Two-phase pre-training** with masked-language modeling (`mlm_probability = 0.3`):
standard random-token MLM over code, natural language, and documentation (Phase 1), then
**line-level MLM** on code only, masking whole source lines (Phase 2).
| Property | Value |
| ------------------------------ | ----------------------------------------------------- |
| Architecture | ModernBERT (alternating local/global attention, RoPE) |
| Parameters | ≈150M |
| `hidden_size` / layers / heads | 768 / 19 / 12 |
| Vocabulary | 50,368 (custom code BPE) |
| Max sequence length | 1,024 (Phase 1) → 2,048 (Phase 2) |
See the [v1 model card](https://huggingface.co/Shuu12121/NightOwl-CodeEmbedding) for the
pre-training data and the backbone comparison.
## Intended Use
* Natural language to code search
* Code-to-code retrieval and similar-function search
* Code-edit retrieval (matching edit intents to code changes)
* Retrieval over programming Q&A, long multi-turn technical conversations, and
competitive-programming problems
* Local semantic code search systems
* RAG systems over codebases and developer documentation
## Limitations
* Specialized for code-related retrieval; may underperform general-purpose text embedding
models on unrelated natural-language tasks.
* Inputs longer than 4,096 tokens are truncated. The model was trained on inputs of at most
1,024 tokens and the backbone was pre-trained on at most 2,048; the 4,096 setting is
supported by the evaluation results above, but quality on very long inputs is less tested
than on short ones.
* Slightly lower than v1 on CodeSearchNetCCRetrieval (−0.009) and CodeSearchNetRetrieval
(−0.002).
* MTEB(Code, v1) is largely in-domain for this model (8% zero-shot). On code domains, query
styles, or languages far from the training distribution, expect lower performance than the
benchmark numbers suggest.
* The model produces dense single-vector embeddings. For very fine-grained token-level
matching, late-interaction models or cross-encoder rerankers may be worth considering.
## Recommended Indexing Settings
Encode both queries and documents with normalized embeddings:
```python
embeddings = model.encode(texts, normalize_embeddings=True)
```
With normalized embeddings, dot product is equivalent to cosine similarity.
For codebase search, indexing function-level or class-level chunks usually works best.
## Citation
If you use this model, please cite it together with the base model and Sentence
Transformers.
```bibtex
@misc{nightowl_codeembedding_v2,
title = {NightOwl-CodeEmbedding-v2},
author = {Shuu12121},
year = {2026},
publisher = {Hugging Face},
url = {https://huggingface.co/Shuu12121/NightOwl-CodeEmbedding-v2}
}
```