embedpick 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mehmet Alper Tuğtekin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: embedpick
3
+ Version: 0.1.0
4
+ Summary: Benchmark embedding models on your own data, with a keyword-search baseline
5
+ Author: Mehmet Alper Tuğtekin
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Mehmetalpertugtekin/embedpick
8
+ Project-URL: Repository, https://github.com/Mehmetalpertugtekin/embedpick
9
+ Project-URL: Issues, https://github.com/Mehmetalpertugtekin/embedpick/issues
10
+ Keywords: embeddings,benchmark,semantic-search,information-retrieval,sentence-transformers,turkish-nlp
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Classifier: Topic :: Text Processing :: Indexing
22
+ Classifier: Natural Language :: Turkish
23
+ Classifier: Natural Language :: English
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: pandas>=1.3
28
+ Requires-Dist: numpy>=1.21
29
+ Requires-Dist: rank-bm25>=0.2
30
+ Requires-Dist: sentence-transformers>=2.2
31
+ Requires-Dist: faiss-cpu>=1.7
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0; extra == "dev"
34
+ Provides-Extra: ui
35
+ Requires-Dist: gradio>=4.0; extra == "ui"
36
+ Dynamic: license-file
37
+
38
+ # embedpick
39
+ ![tests](https://github.com/Mehmetalpertugtekin/embedpick/actions/workflows/tests.yml/badge.svg)
40
+
41
+ Benchmark embedding models on **your own data**, not on someone else's leaderboard.
42
+
43
+ [Türkçe README](README.tr.md)
44
+
45
+ ## Why
46
+
47
+ Picking an embedding model usually goes like this: open Hugging Face, sort by
48
+ downloads, take the top result. The scores on the model card were measured on
49
+ general-purpose English benchmarks. Your data is not that.
50
+
51
+ embedpick answers a narrower and more useful question: *given my documents and
52
+ my queries, which model actually finds the right thing, and what does it cost
53
+ me in time and memory?*
54
+
55
+ It reports quality, speed and memory side by side, and — unlike most benchmark
56
+ tooling — it puts a plain keyword-search baseline in the same table.
57
+
58
+ ## The baseline is the point
59
+
60
+ Every run includes BM25, a classic keyword-matching algorithm with no neural
61
+ network involved. If an embedding model cannot beat it, running that model is
62
+ wasted compute.
63
+
64
+ Results on the included Turkish customer-support dataset (72 documents,
65
+ 26 queries, k=5):
66
+
67
+ | Model | recall@5 | MRR@5 | docs/sec | dim | rel. speed |
68
+ |---|---|---|---|---|---|
69
+ | BM25 (baseline) | 0.564 | 0.564 | ~226,000 | — | — |
70
+ | paraphrase-multilingual-MiniLM-L12-v2 | 0.615 | 0.596 | 340 | 384 | 1.00x |
71
+ | multilingual-e5-small | 0.692 | 0.708 | 304 | 384 | 0.89x |
72
+ | trmteb/turkish-embedding-model | **0.846** | **0.865** | 103 | 768 | 0.30x |
73
+
74
+ Read that first two rows carefully. MiniLM buys a 5-point recall gain over
75
+ keyword matching, and pays roughly 600x the indexing time for it. On this
76
+ dataset that trade is hard to justify.
77
+
78
+ The Turkish-specific model is a different story: +28 points over the baseline
79
+ is a real jump, and worth the 3x slowdown against the other neural models.
80
+
81
+ So the lesson is not "embeddings are overrated." It is: **a badly chosen
82
+ embedding model is not better than keyword search, and nobody checks.**
83
+
84
+ ## Findings from the sample dataset
85
+
86
+ **Documented configuration is not always the right configuration.** The e5
87
+ model card specifies `query: ` and `passage: ` prefixes on input text. Applying
88
+ them on this dataset *lowered* recall@5 from 0.750 to 0.692 and MRR from 0.792
89
+ to 0.708 — same model, same data, same hardware.
90
+
91
+ A plausible reason: these documents are short, five to eight words each, so an
92
+ English prefix takes up a large share of every one of them. Whatever the cause,
93
+ the recommended setting cost about six points here, and nothing short of
94
+ measuring on your own data would have shown it.
95
+
96
+ embedpick applies known prefixes by default, following the model authors'
97
+ instructions, and `--no-presets` turns them off so you can check. Across 26
98
+ queries the gap is worth roughly one and a half queries, so read the direction
99
+ as suggestive rather than settled.
100
+
101
+ **BM25 and embeddings fail on different queries.** On `ödeme yaparken sorun`
102
+ ("problem while paying") BM25 scored 1.00 and MiniLM scored 0.00. On
103
+ `sahte ürün şüphesi` ("suspected counterfeit") it was the reverse. They are
104
+ complementary, which is the empirical case for hybrid retrieval.
105
+
106
+ **Small corpora hide everything.** On a 16-document pilot all three models
107
+ scored 1.0 and looked identical. At 72 documents a 23-point spread appeared.
108
+ A benchmark that cannot separate models tells you nothing about them.
109
+
110
+ **Absolute timings are unreliable; ratios are not.** Across repeated runs on
111
+ the same machine, `docs/sec` varied by up to 4x while the ratio between models
112
+ stayed within about 20%. Report `rel_speed` when comparing across machines.
113
+
114
+ ## Install
115
+
116
+ ```bash
117
+ git clone https://github.com/Mehmetalpertugtekin/embedpick.git
118
+ cd embedpick
119
+ python -m venv .venv
120
+ .venv\Scripts\activate # Windows
121
+ source .venv/bin/activate # macOS / Linux
122
+ pip install -r requirements.txt
123
+ ```
124
+
125
+ Python 3.9+.
126
+
127
+ ## Usage
128
+
129
+ ```bash
130
+ # run with the bundled sample dataset
131
+ python -m embedpick
132
+
133
+ # your own data
134
+ python -m embedpick --corpus my_docs.csv --queries my_queries.csv
135
+
136
+ # pick models, evaluate top 10, show per-query detail
137
+ python -m embedpick --models intfloat/multilingual-e5-base -k 10 --verbose
138
+
139
+ # save the table
140
+ python -m embedpick --out results.csv
141
+ ```
142
+
143
+ `python -m embedpick --help` lists every flag.
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ pip install -r requirements-dev.txt
149
+ python -m pytest
150
+ ```
151
+
152
+ Tests do not download any models, so they run in seconds. Make sure your
153
+ virtual environment is active first — a missing `rank_bm25` in the test output
154
+ usually means you installed into the system Python by mistake.
155
+
156
+ ## Data format
157
+
158
+ **corpus.csv** — the documents to search over:
159
+
160
+ ```csv
161
+ id,text
162
+ 1,Kargom hala elime ulaşmadı
163
+ 2,Sipariş takip numaram sistemde görünmüyor
164
+ ```
165
+
166
+ **queries.csv** — queries and their correct answers:
167
+
168
+ ```csv
169
+ query,relevant_ids
170
+ kargo gecikmesi,1;5
171
+ paketim nerede,1;2
172
+ ```
173
+
174
+ Separate multiple correct answers with `;`. A query can have any number of
175
+ them. Files must be UTF-8.
176
+
177
+ Twenty to thirty queries is usually enough to see real differences. Include
178
+ queries that share no words with their correct answer — those are the ones
179
+ that test semantic matching rather than lucky keyword overlap.
180
+
181
+ ## Metrics
182
+
183
+ **recall@k** — of the correct documents, what fraction appeared in the top k.
184
+ Use this when you show the user a list of results.
185
+
186
+ **MRR@k** — 1.0 if the first correct answer is at rank 1, 0.5 at rank 2, 0.33
187
+ at rank 3, averaged over queries. Use this when you show one answer, or feed
188
+ the top hit to an LLM.
189
+
190
+ These can disagree, and the disagreement is informative. In an earlier run
191
+ e5 had higher recall than MiniLM but lower MRR: it found more, but ranked
192
+ worse. Which model is "better" depends on what you are building.
193
+
194
+ **docs/sec** — indexing throughput. Machine-dependent, see the caveat above.
195
+
196
+ **dim** and **index_mb** — vector width and index size. A 768-dimensional
197
+ model needs twice the memory of a 384-dimensional one at the same corpus size.
198
+
199
+ ## Label checking
200
+
201
+ Two things happen automatically:
202
+
203
+ - If `relevant_ids` points at a document id that is not in the corpus, the run
204
+ stops and names the query. Otherwise that query silently scores zero forever
205
+ and you blame the model.
206
+ - If *no* method — including BM25 — finds a query's answer, embedpick flags it.
207
+ When everything fails on the same query, the label is usually wrong. This
208
+ caught a mislabelled query during development.
209
+
210
+ ## Sample dataset
211
+
212
+ `embedpick/sample_data/` contains 72 synthetic Turkish customer-support messages across nine
213
+ themes (shipping, returns, payment, account, product quality, promotions,
214
+ warranty, order management, support) and 26 labelled queries. It is written,
215
+ not scraped, so it carries no licensing or privacy constraints.
216
+
217
+ Several queries deliberately share no vocabulary with their answers — for
218
+ example `güvenlik ihlali şüphesi` ("suspected security breach") maps to
219
+ *"Hesabıma başkası girmiş olabilir"* ("someone else may have accessed my
220
+ account"). Keyword search cannot solve these; that is the point.
221
+
222
+ ## Caveats
223
+
224
+ - Timing on a 72-document corpus is close to measurement noise, particularly
225
+ for BM25. Treat the throughput column as indicative.
226
+ - BM25 and neural encoders scale differently. The ratio at 72 documents is not
227
+ the ratio at 100,000.
228
+ - Results are from one machine, CPU only, no GPU.
229
+
230
+ ## Roadmap
231
+
232
+ - Hybrid retrieval (BM25 + embedding score fusion) as a fourth row
233
+ - Hub detection: flag documents that surface for nearly every query
234
+ - Markdown report export
235
+ - Optional Gradio interface
236
+
237
+ ## License
238
+
239
+ MIT
@@ -0,0 +1,202 @@
1
+ # embedpick
2
+ ![tests](https://github.com/Mehmetalpertugtekin/embedpick/actions/workflows/tests.yml/badge.svg)
3
+
4
+ Benchmark embedding models on **your own data**, not on someone else's leaderboard.
5
+
6
+ [Türkçe README](README.tr.md)
7
+
8
+ ## Why
9
+
10
+ Picking an embedding model usually goes like this: open Hugging Face, sort by
11
+ downloads, take the top result. The scores on the model card were measured on
12
+ general-purpose English benchmarks. Your data is not that.
13
+
14
+ embedpick answers a narrower and more useful question: *given my documents and
15
+ my queries, which model actually finds the right thing, and what does it cost
16
+ me in time and memory?*
17
+
18
+ It reports quality, speed and memory side by side, and — unlike most benchmark
19
+ tooling — it puts a plain keyword-search baseline in the same table.
20
+
21
+ ## The baseline is the point
22
+
23
+ Every run includes BM25, a classic keyword-matching algorithm with no neural
24
+ network involved. If an embedding model cannot beat it, running that model is
25
+ wasted compute.
26
+
27
+ Results on the included Turkish customer-support dataset (72 documents,
28
+ 26 queries, k=5):
29
+
30
+ | Model | recall@5 | MRR@5 | docs/sec | dim | rel. speed |
31
+ |---|---|---|---|---|---|
32
+ | BM25 (baseline) | 0.564 | 0.564 | ~226,000 | — | — |
33
+ | paraphrase-multilingual-MiniLM-L12-v2 | 0.615 | 0.596 | 340 | 384 | 1.00x |
34
+ | multilingual-e5-small | 0.692 | 0.708 | 304 | 384 | 0.89x |
35
+ | trmteb/turkish-embedding-model | **0.846** | **0.865** | 103 | 768 | 0.30x |
36
+
37
+ Read that first two rows carefully. MiniLM buys a 5-point recall gain over
38
+ keyword matching, and pays roughly 600x the indexing time for it. On this
39
+ dataset that trade is hard to justify.
40
+
41
+ The Turkish-specific model is a different story: +28 points over the baseline
42
+ is a real jump, and worth the 3x slowdown against the other neural models.
43
+
44
+ So the lesson is not "embeddings are overrated." It is: **a badly chosen
45
+ embedding model is not better than keyword search, and nobody checks.**
46
+
47
+ ## Findings from the sample dataset
48
+
49
+ **Documented configuration is not always the right configuration.** The e5
50
+ model card specifies `query: ` and `passage: ` prefixes on input text. Applying
51
+ them on this dataset *lowered* recall@5 from 0.750 to 0.692 and MRR from 0.792
52
+ to 0.708 — same model, same data, same hardware.
53
+
54
+ A plausible reason: these documents are short, five to eight words each, so an
55
+ English prefix takes up a large share of every one of them. Whatever the cause,
56
+ the recommended setting cost about six points here, and nothing short of
57
+ measuring on your own data would have shown it.
58
+
59
+ embedpick applies known prefixes by default, following the model authors'
60
+ instructions, and `--no-presets` turns them off so you can check. Across 26
61
+ queries the gap is worth roughly one and a half queries, so read the direction
62
+ as suggestive rather than settled.
63
+
64
+ **BM25 and embeddings fail on different queries.** On `ödeme yaparken sorun`
65
+ ("problem while paying") BM25 scored 1.00 and MiniLM scored 0.00. On
66
+ `sahte ürün şüphesi` ("suspected counterfeit") it was the reverse. They are
67
+ complementary, which is the empirical case for hybrid retrieval.
68
+
69
+ **Small corpora hide everything.** On a 16-document pilot all three models
70
+ scored 1.0 and looked identical. At 72 documents a 23-point spread appeared.
71
+ A benchmark that cannot separate models tells you nothing about them.
72
+
73
+ **Absolute timings are unreliable; ratios are not.** Across repeated runs on
74
+ the same machine, `docs/sec` varied by up to 4x while the ratio between models
75
+ stayed within about 20%. Report `rel_speed` when comparing across machines.
76
+
77
+ ## Install
78
+
79
+ ```bash
80
+ git clone https://github.com/Mehmetalpertugtekin/embedpick.git
81
+ cd embedpick
82
+ python -m venv .venv
83
+ .venv\Scripts\activate # Windows
84
+ source .venv/bin/activate # macOS / Linux
85
+ pip install -r requirements.txt
86
+ ```
87
+
88
+ Python 3.9+.
89
+
90
+ ## Usage
91
+
92
+ ```bash
93
+ # run with the bundled sample dataset
94
+ python -m embedpick
95
+
96
+ # your own data
97
+ python -m embedpick --corpus my_docs.csv --queries my_queries.csv
98
+
99
+ # pick models, evaluate top 10, show per-query detail
100
+ python -m embedpick --models intfloat/multilingual-e5-base -k 10 --verbose
101
+
102
+ # save the table
103
+ python -m embedpick --out results.csv
104
+ ```
105
+
106
+ `python -m embedpick --help` lists every flag.
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ pip install -r requirements-dev.txt
112
+ python -m pytest
113
+ ```
114
+
115
+ Tests do not download any models, so they run in seconds. Make sure your
116
+ virtual environment is active first — a missing `rank_bm25` in the test output
117
+ usually means you installed into the system Python by mistake.
118
+
119
+ ## Data format
120
+
121
+ **corpus.csv** — the documents to search over:
122
+
123
+ ```csv
124
+ id,text
125
+ 1,Kargom hala elime ulaşmadı
126
+ 2,Sipariş takip numaram sistemde görünmüyor
127
+ ```
128
+
129
+ **queries.csv** — queries and their correct answers:
130
+
131
+ ```csv
132
+ query,relevant_ids
133
+ kargo gecikmesi,1;5
134
+ paketim nerede,1;2
135
+ ```
136
+
137
+ Separate multiple correct answers with `;`. A query can have any number of
138
+ them. Files must be UTF-8.
139
+
140
+ Twenty to thirty queries is usually enough to see real differences. Include
141
+ queries that share no words with their correct answer — those are the ones
142
+ that test semantic matching rather than lucky keyword overlap.
143
+
144
+ ## Metrics
145
+
146
+ **recall@k** — of the correct documents, what fraction appeared in the top k.
147
+ Use this when you show the user a list of results.
148
+
149
+ **MRR@k** — 1.0 if the first correct answer is at rank 1, 0.5 at rank 2, 0.33
150
+ at rank 3, averaged over queries. Use this when you show one answer, or feed
151
+ the top hit to an LLM.
152
+
153
+ These can disagree, and the disagreement is informative. In an earlier run
154
+ e5 had higher recall than MiniLM but lower MRR: it found more, but ranked
155
+ worse. Which model is "better" depends on what you are building.
156
+
157
+ **docs/sec** — indexing throughput. Machine-dependent, see the caveat above.
158
+
159
+ **dim** and **index_mb** — vector width and index size. A 768-dimensional
160
+ model needs twice the memory of a 384-dimensional one at the same corpus size.
161
+
162
+ ## Label checking
163
+
164
+ Two things happen automatically:
165
+
166
+ - If `relevant_ids` points at a document id that is not in the corpus, the run
167
+ stops and names the query. Otherwise that query silently scores zero forever
168
+ and you blame the model.
169
+ - If *no* method — including BM25 — finds a query's answer, embedpick flags it.
170
+ When everything fails on the same query, the label is usually wrong. This
171
+ caught a mislabelled query during development.
172
+
173
+ ## Sample dataset
174
+
175
+ `embedpick/sample_data/` contains 72 synthetic Turkish customer-support messages across nine
176
+ themes (shipping, returns, payment, account, product quality, promotions,
177
+ warranty, order management, support) and 26 labelled queries. It is written,
178
+ not scraped, so it carries no licensing or privacy constraints.
179
+
180
+ Several queries deliberately share no vocabulary with their answers — for
181
+ example `güvenlik ihlali şüphesi` ("suspected security breach") maps to
182
+ *"Hesabıma başkası girmiş olabilir"* ("someone else may have accessed my
183
+ account"). Keyword search cannot solve these; that is the point.
184
+
185
+ ## Caveats
186
+
187
+ - Timing on a 72-document corpus is close to measurement noise, particularly
188
+ for BM25. Treat the throughput column as indicative.
189
+ - BM25 and neural encoders scale differently. The ratio at 72 documents is not
190
+ the ratio at 100,000.
191
+ - Results are from one machine, CPU only, no GPU.
192
+
193
+ ## Roadmap
194
+
195
+ - Hybrid retrieval (BM25 + embedding score fusion) as a fourth row
196
+ - Hub detection: flag documents that surface for nearly every query
197
+ - Markdown report export
198
+ - Optional Gradio interface
199
+
200
+ ## License
201
+
202
+ MIT
@@ -0,0 +1,7 @@
1
+ """embedpick — kendi verinizde embedding modeli kıyaslama aracı."""
2
+
3
+ from .benchmark import run_benchmark
4
+ from .data import load_corpus, load_queries, validate
5
+
6
+ __version__ = "0.1.0"
7
+ __all__ = ["run_benchmark", "load_corpus", "load_queries", "validate"]
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -0,0 +1,97 @@
1
+ """Benchmark akışını yürütür ve sonuç tablosunu üretir."""
2
+
3
+ import pandas as pd
4
+
5
+ from .metrics import consensus_failures, recall_at_k, score_all
6
+ from .retrievers import BM25Retriever, EmbeddingRetriever
7
+
8
+
9
+ def run_benchmark(
10
+ corpus,
11
+ queries,
12
+ model_names,
13
+ k=5,
14
+ repeats=5,
15
+ include_baseline=True,
16
+ use_presets=True,
17
+ verbose=False,
18
+ ):
19
+ retrievers = []
20
+ if include_baseline:
21
+ retrievers.append(BM25Retriever())
22
+
23
+ for name in model_names:
24
+ if use_presets:
25
+ retrievers.append(EmbeddingRetriever(name))
26
+ else:
27
+ # Önekler kapalı: modelin belgelenmiş kullanım kuralı yok sayılıyor.
28
+ # Yanlış yapılandırmanın maliyetini ölçmek için.
29
+ retrievers.append(EmbeddingRetriever(name, query_prefix="", doc_prefix=""))
30
+
31
+ satirlar = []
32
+ sorgu_bazli = {}
33
+
34
+ for retriever in retrievers:
35
+ print(f"\n-> {retriever.label}")
36
+ maliyet = retriever.index(corpus, repeats)
37
+ bulunanlar = retriever.search(queries["query"].tolist(), k)
38
+
39
+ recall, mrr = score_all(bulunanlar, queries, k, verbose=verbose)
40
+
41
+ sorgu_bazli[retriever.label] = {
42
+ q: recall_at_k(bulunan, dogru, k)
43
+ for q, bulunan, dogru in zip(
44
+ queries["query"], bulunanlar, queries["relevant_ids"]
45
+ )
46
+ }
47
+
48
+ satirlar.append(
49
+ {
50
+ "model": retriever.label,
51
+ f"recall@{k}": round(recall, 3),
52
+ f"mrr@{k}": round(mrr, 3),
53
+ "docs_per_sec": round(maliyet["docs_per_sec"], 1),
54
+ "dim": maliyet["dim"],
55
+ "index_mb": round(maliyet["index_mb"], 2),
56
+ }
57
+ )
58
+
59
+ _add_relative_speed(satirlar)
60
+ return pd.DataFrame(satirlar), sorgu_bazli
61
+
62
+
63
+ def _add_relative_speed(satirlar):
64
+ """Mutlak hız makineye ve o anki işlemci durumuna göre kat kat oynar.
65
+ Aynı çalıştırma içindeki oranlar ise sabit kalır."""
66
+ sinirsel = [r for r in satirlar if r["dim"] > 0]
67
+ if not sinirsel:
68
+ return
69
+
70
+ en_hizli = max(r["docs_per_sec"] for r in sinirsel)
71
+ for r in satirlar:
72
+ r["rel_speed"] = f"{r['docs_per_sec'] / en_hizli:.2f}x" if r["dim"] > 0 else "-"
73
+
74
+
75
+ def report(df, sorgu_bazli, k):
76
+ print()
77
+ print(df.to_string(index=False))
78
+
79
+ print(
80
+ "\nNot: docs_per_sec makineye ve o anki işlemci durumuna göre değişir."
81
+ "\n Makineler arası kıyas için rel_speed sütununu kullanın"
82
+ "\n (en hızlı sinirsel model = 1.00x)."
83
+ )
84
+
85
+ # Tek yöntem varken "hiçbiri bulamadı" demek anlamsız; uyarının değeri
86
+ # birden fazla yöntemin aynı sorguda batmasından geliyor.
87
+ ortak = consensus_failures(sorgu_bazli) if len(sorgu_bazli) > 1 else []
88
+ if ortak:
89
+ print(
90
+ f"\nHiçbir yöntemin ilk {k} sonuçta bulamadığı sorgular:"
91
+ )
92
+ for q in ortak:
93
+ print(f" - {q}")
94
+ print(
95
+ " Bütün yöntemler aynı sorguda batıyorsa sorun genelde modelde değil,"
96
+ "\n etiketlerdedir. Bu sorguların relevant_ids değerlerini gözden geçirin."
97
+ )
@@ -0,0 +1,111 @@
1
+ """embedpick komut satırı arayüzü."""
2
+
3
+ import argparse
4
+ import sys
5
+ from importlib import resources
6
+
7
+ from .benchmark import report, run_benchmark
8
+ from .data import load_corpus, load_queries, validate
9
+ from .retrievers import DEFAULT_MODELS
10
+
11
+
12
+ def sample_path(name):
13
+ """Pakete gömülü örnek veri dosyasının yolu.
14
+
15
+ Paket pip ile kurulduğunda çalışma dizininde data/ klasörü olmaz,
16
+ o yüzden varsayılan veri kurulum dizininden okunuyor.
17
+ """
18
+ return str(resources.files("embedpick") / "sample_data" / name)
19
+
20
+
21
+ def build_parser():
22
+ p = argparse.ArgumentParser(
23
+ prog="embedpick",
24
+ description=(
25
+ "Kendi verinizde embedding modellerini kıyaslayın. "
26
+ "Kalite, hız ve bellek maliyetini tek tabloda gösterir."
27
+ ),
28
+ )
29
+ p.add_argument(
30
+ "--corpus",
31
+ help="Aranacak dokümanlar (sütunlar: id,text). "
32
+ "Verilmezse pakete gömülü örnek veri kullanılır.",
33
+ )
34
+ p.add_argument(
35
+ "--queries",
36
+ help="Sorgular ve doğru cevaplar (sütunlar: query,relevant_ids). "
37
+ "Verilmezse pakete gömülü örnek veri kullanılır.",
38
+ )
39
+ p.add_argument(
40
+ "--models",
41
+ nargs="+",
42
+ default=DEFAULT_MODELS,
43
+ help="Hugging Face model adları, boşlukla ayrılmış",
44
+ )
45
+ p.add_argument("-k", type=int, default=5, help="İlk kaç sonuç değerlendirilsin")
46
+ p.add_argument(
47
+ "--repeats", type=int, default=5, help="Hız ölçümü kaç kez tekrarlansın"
48
+ )
49
+ p.add_argument(
50
+ "--no-baseline",
51
+ action="store_true",
52
+ help="BM25 kelime bazlı baseline'ı atla (önerilmez)",
53
+ )
54
+ p.add_argument(
55
+ "--no-presets",
56
+ action="store_true",
57
+ help=(
58
+ "Bilinen model öneklerini uygulama. e5 gibi önek bekleyen modeller "
59
+ "düşük skor alır; yanlış yapılandırmanın maliyetini ölçmek için."
60
+ ),
61
+ )
62
+ p.add_argument(
63
+ "--verbose",
64
+ action="store_true",
65
+ help="Her sorgunun sonucunu tek tek yazdır",
66
+ )
67
+ p.add_argument("--out", help="Sonuç tablosunu CSV olarak kaydet")
68
+ return p
69
+
70
+
71
+ def main(argv=None):
72
+ args = build_parser().parse_args(argv)
73
+
74
+ corpus_path = args.corpus or sample_path("corpus.csv")
75
+ queries_path = args.queries or sample_path("queries.csv")
76
+
77
+ if args.corpus is None and args.queries is None:
78
+ print("Kendi veriniz verilmedi, pakete gömülü örnek veri kullanılıyor.")
79
+
80
+ try:
81
+ corpus = load_corpus(corpus_path)
82
+ queries = load_queries(queries_path)
83
+ validate(corpus, queries)
84
+ except (FileNotFoundError, ValueError) as exc:
85
+ print(f"Hata: {exc}", file=sys.stderr)
86
+ return 1
87
+
88
+ print(f"{len(corpus)} doküman, {len(queries)} sorgu, k={args.k}")
89
+
90
+ df, sorgu_bazli = run_benchmark(
91
+ corpus,
92
+ queries,
93
+ model_names=args.models,
94
+ k=args.k,
95
+ repeats=args.repeats,
96
+ include_baseline=not args.no_baseline,
97
+ use_presets=not args.no_presets,
98
+ verbose=args.verbose,
99
+ )
100
+
101
+ report(df, sorgu_bazli, args.k)
102
+
103
+ if args.out:
104
+ df.to_csv(args.out, index=False, encoding="utf-8")
105
+ print(f"\nTablo kaydedildi: {args.out}")
106
+
107
+ return 0
108
+
109
+
110
+ if __name__ == "__main__":
111
+ raise SystemExit(main())