{"id":984,"date":"2026-05-27T07:13:09","date_gmt":"2026-05-26T23:13:09","guid":{"rendered":"https:\/\/connectword.dpdns.org\/?p=984"},"modified":"2026-05-27T07:13:09","modified_gmt":"2026-05-26T23:13:09","slug":"design-a-high-precision-retrieve-and-rerank-pipeline-with-zeroentropy-zerank-2-reranker","status":"publish","type":"post","link":"https:\/\/connectword.dpdns.org\/?p=984","title":{"rendered":"Design a High-Precision Retrieve-and-Rerank Pipeline with ZeroEntropy Zerank-2 Reranker"},"content":{"rendered":"<p class=\"wp-block-paragraph\">In this tutorial, we use<a href=\"https:\/\/huggingface.co\/zeroentropy\/zerank-2-reranker\"> <strong>zeroentropy\/zerank-2-reranker<\/strong><\/a>, a 4B Qwen3-based cross-encoder reranker, to improve retrieval quality. We start by setting up the runtime, loading the reranker, and understanding how it scores query-document pairs. Then, we move from simple pairwise scoring to a practical two-stage retrieve-and-rerank pipeline, where a fast bi-encoder first retrieves candidates and zerank-2 reranks them for better precision. We also evaluate the impact using NDCG@10 and test the reranker across finance, legal, and code examples to assess its performance in real-world search and ranking tasks.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">!pip -q install -U \"sentence-transformers&gt;=3.0\" \"transformers&gt;=4.51.0\" accelerate\nimport os, time, numpy as np, torch\nfrom sentence_transformers import CrossEncoder, SentenceTransformer, util\nos.environ[\"TOKENIZERS_PARALLELISM\"] = \"false\"\nif torch.cuda.is_available():\n   device = \"cuda\"\n   dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16\n   print(f\"GPU: {torch.cuda.get_device_name(0)} | dtype: {dtype}\")\nelse:\n   device, dtype = \"cpu\", torch.float32\n   print(\"WARNING: no GPU detected. This 4B model will be very slow on CPU.\")\nRERANKER_ID = \"zeroentropy\/zerank-2-reranker\"\nprint(f\"nLoading {RERANKER_ID} (~8GB on first run)...\")\nreranker = CrossEncoder(\n   RERANKER_ID,\n   model_kwargs={\"torch_dtype\": dtype},\n   device=device,\n)\nprint(\"Reranker loaded.\")\ndef to_prob(logits):\n   return (torch.as_tensor(logits, dtype=torch.float32) \/ 5).sigmoid()<\/code><\/pre>\n<\/div>\n<\/div>\n<p class=\"wp-block-paragraph\">We begin by installing the required libraries and importing the main tools needed for reranking and retrieval. We check whether a GPU is available and select the appropriate device and tensor precision for efficient model execution. We then load the zeroentropy\/zerank-2-reranker model and define a helper function to convert raw logits into probability-style scores.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">print(\"n\" + \"=\"*70 + \"nPART 1: Pairwise scoringn\" + \"=\"*70)\npairs = [\n   (\"What is 2+2?\", \"4\"),\n   (\"What is 2+2?\", \"The answer is definitely 1 million\"),\n   (\"Which planet is the Red Planet?\",\n    \"Mars, known for its reddish appearance, is the Red Planet.\"),\n   (\"Which planet is the Red Planet?\",\n    \"Venus is Earth's twin because of its similar size.\"),\n]\nlogits = reranker.predict(pairs, convert_to_tensor=True)\nprobs = to_prob(logits)\nfor (q, d), lg, p in zip(pairs, logits.tolist(), probs.tolist()):\n   print(f\"logit={lg:+6.2f}  prob={p:5.3f}  | {q[:30]:30s} -&gt; {d[:45]}\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p class=\"wp-block-paragraph\">\u0327We test the reranker on simple query-document pairs to understand how it scores relevant and irrelevant answers. We pass each pair through reranker.predict() and receive raw logits from the model. We convert those logits into probabilities and print the results so we can compare how strongly the model prefers correct responses.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">print(\"n\" + \"=\"*70 + \"nPART 2: model.rank for a single queryn\" + \"=\"*70)\nquery = \"How do I fix a Python list index out of range error?\"\ncandidates = [\n   \"IndexError happens when you access an index beyond the list length; check len() and loop bounds.\",\n   \"Use a try\/except IndexError block, or validate the index with `if i &lt; len(lst)` before access.\",\n   \"To install Python packages, run `pip install &lt;package&gt;` in your terminal.\",\n   \"List comprehensions create new lists: `[x*2 for x in nums]`.\",\n   \"Off-by-one errors in range(len(lst)+1) are a common cause of index out of range.\",\n]\nranking = reranker.rank(query, candidates, convert_to_tensor=True)\nfor rank, r in enumerate(ranking, 1):\n   cid = r[\"corpus_id\"]\n   print(f\"#{rank}  score={float(r['score']):+6.2f}  prob={to_prob(r['score']):.3f}  \"\n         f\"| {candidates[cid][:60]}\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p class=\"wp-block-paragraph\">We use model.rank() to rank multiple candidate answers for a single query. We provide several possible explanations for a Python list index error and let the reranker order them by relevance. We then print each ranked result with its raw score and probability-style score to see which answer the model considers most useful.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">print(\"n\" + \"=\"*70 + \"nPART 3: Two-stage retrieve -&gt; rerank pipelinen\" + \"=\"*70)\ncorpus = [\n   \"The mitochondria is the powerhouse of the cell, producing ATP via respiration.\",\n   \"Photosynthesis converts light energy into chemical energy in chloroplasts.\",\n   \"ATP synthase uses a proton gradient across the inner mitochondrial membrane to make ATP.\",\n   \"DNA replication is semi-conservative and occurs during the S phase of the cell cycle.\",\n   \"The Krebs cycle (citric acid cycle) takes place in the mitochondrial matrix.\",\n   \"Ribosomes translate mRNA into proteins in the cytoplasm.\",\n   \"Glycolysis breaks glucose into pyruvate in the cytosol, yielding a net 2 ATP.\",\n   \"The Golgi apparatus modifies, sorts, and packages proteins for secretion.\",\n   \"Cellular respiration in mitochondria yields far more ATP than glycolysis alone.\",\n   \"Plant cell walls are made primarily of cellulose for structural support.\",\n]\nbi = SentenceTransformer(\"sentence-transformers\/all-MiniLM-L6-v2\", device=device)\ncorpus_emb = bi.encode(corpus, convert_to_tensor=True, normalize_embeddings=True)\ndef two_stage_search(q, top_k_retrieve=6, top_n_final=3):\n   q_emb = bi.encode(q, convert_to_tensor=True, normalize_embeddings=True)\n   hits = util.semantic_search(q_emb, corpus_emb, top_k=top_k_retrieve)[0]\n   cand_ids = [h[\"corpus_id\"] for h in hits]\n   cand_docs = [corpus[i] for i in cand_ids]\n   rr = reranker.rank(q, cand_docs, convert_to_tensor=True)\n   out = []\n   for r in rr[:top_n_final]:\n       global_id = cand_ids[r[\"corpus_id\"]]\n       out.append((global_id, corpus[global_id], float(to_prob(r[\"score\"]))))\n   return cand_ids, out\nq = \"Where in the cell is most ATP actually produced?\"\nretrieved, final = two_stage_search(q)\nprint(f\"Query: {q}n\")\nprint(\"Stage 1 (bi-encoder) top order:\", retrieved)\nprint(\"nStage 2 (zerank-2 reranked) top results:\")\nfor gid, doc, p in final:\n   print(f\"  [doc {gid}] prob={p:.3f} | {doc}\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p class=\"wp-block-paragraph\">We build a two-stage retrieval pipeline that first uses a fast bi-encoder to retrieve candidate documents from a small corpus. We then pass those retrieved candidates to zerank-2 so it can rerank them with deeper query-document understanding. We finally compare the initially retrieved order with the reranked top results to see how reranking improves precision.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">print(\"n\" + \"=\"*70 + \"nPART 4: NDCG@10 evaluationn\" + \"=\"*70)\neval_set = [\n   {\"query\": \"Where is most ATP produced in the cell?\",\n    \"rels\": {0: 2, 2: 3, 4: 2, 6: 1, 8: 3}},\n   {\"query\": \"How do plants capture light energy?\",\n    \"rels\": {1: 3, 9: 1}},\n   {\"query\": \"How are proteins made and packaged in a cell?\",\n    \"rels\": {5: 3, 7: 2}},\n]\ndef dcg(rels):\n   rels = np.asarray(rels, dtype=float)\n   return np.sum((2**rels - 1) \/ np.log2(np.arange(2, rels.size + 2)))\ndef ndcg_at_k(ranked_doc_ids, rel_map, k=10):\n   gains = [rel_map.get(d, 0) for d in ranked_doc_ids[:k]]\n   ideal = sorted(rel_map.values(), reverse=True)[:k]\n   idcg = dcg(ideal)\n   return dcg(gains) \/ idcg if idcg &gt; 0 else 0.0\nbase_scores, rr_scores = [], []\nfor ex in eval_set:\n   q, rel_map = ex[\"query\"], ex[\"rels\"]\n   q_emb = bi.encode(q, convert_to_tensor=True, normalize_embeddings=True)\n   hits = util.semantic_search(q_emb, corpus_emb, top_k=len(corpus))[0]\n   base_order = [h[\"corpus_id\"] for h in hits]\n   base_scores.append(ndcg_at_k(base_order, rel_map))\n   rr = reranker.rank(q, [corpus[i] for i in base_order], convert_to_tensor=True)\n   rr_order = [base_order[r[\"corpus_id\"]] for r in rr]\n   rr_scores.append(ndcg_at_k(rr_order, rel_map))\nprint(f\"{'Query':45s} {'bi-encoder':&gt;12s} {'+ zerank-2':&gt;12s}\")\nfor ex, b, r in zip(eval_set, base_scores, rr_scores):\n   print(f\"{ex['query'][:43]:45s} {b:12.4f} {r:12.4f}\")\nprint(\"-\"*72)\nprint(f\"{'AVERAGE NDCG@10':45s} {np.mean(base_scores):12.4f} {np.mean(rr_scores):12.4f}\")\nprint(f\"nReranking lift: {np.mean(rr_scores)-np.mean(base_scores):+.4f} NDCG@10\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p class=\"wp-block-paragraph\">We evaluate the retrieval pipeline using a small labeled benchmark and the NDCG@10 metric. We first measure the ranking quality of the bi-encoder alone and then measure the quality after applying zerank-2 reranking. We compare the two scores and calculate the reranking lift to assess the improvement achieved by the cross-encoder.<\/p>\n<div class=\"dm-code-snippet dark dm-normal-version default no-background-mobile\">\n<div class=\"control-language\">\n<div class=\"dm-buttons\">\n<div class=\"dm-buttons-left\">\n<div class=\"dm-button-snippet red-button\"><\/div>\n<div class=\"dm-button-snippet orange-button\"><\/div>\n<div class=\"dm-button-snippet green-button\"><\/div>\n<\/div>\n<div class=\"dm-buttons-right\"><a><span class=\"dm-copy-text\">Copy Code<\/span><span class=\"dm-copy-confirmed\">Copied<\/span><span class=\"dm-error-message\">Use a different Browser<\/span><\/a><\/div>\n<\/div>\n<pre class=\" no-line-numbers\"><code class=\" no-wrap language-php\">print(\"n\" + \"=\"*70 + \"nPART 5: Cross-domain rerankingn\" + \"=\"*70)\ndomain_cases = {\n   \"finance\": (\"What does a rising debt-to-equity ratio indicate?\",\n       [\"A higher debt-to-equity ratio means a firm is financing growth with more debt, raising financial risk.\",\n        \"EBITDA measures operating performance before interest, taxes, depreciation and amortization.\",\n        \"The P\/E ratio compares share price to earnings per share.\"]),\n   \"legal\": (\"What is the difference between a misdemeanor and a felony?\",\n       [\"Felonies are serious crimes punishable by over a year in prison; misdemeanors carry lighter penalties.\",\n        \"A tort is a civil wrong causing harm, separate from criminal law.\",\n        \"Habeas corpus protects against unlawful detention.\"]),\n   \"code\": (\"How do I reverse a string in Python?\",\n       [\"Use slicing with a step of -1: `reversed_str = s[::-1]`.\",\n        \"`str.join()` concatenates an iterable of strings with a separator.\",\n        \"`list.sort()` sorts a list in place and returns None.\"]),\n}\nfor domain, (q, docs) in domain_cases.items():\n   best = reranker.rank(q, docs, convert_to_tensor=True)[0]\n   print(f\"[{domain:8s}] {q}n  -&gt; prob={to_prob(best['score']):.3f} | \"\n         f\"{docs[best['corpus_id']][:70]}n\")\nprint(\"=\"*70 + \"nPART 6: Batched throughputn\" + \"=\"*70)\nbig_query = \"What organelle generates cellular energy?\"\nbig_docs = corpus * 5\nt0 = time.time()\n_ = reranker.predict([(big_query, d) for d in big_docs],\n                    batch_size=16, convert_to_tensor=True)\ndt = time.time() - t0\nprint(f\"Scored {len(big_docs)} pairs in {dt:.2f}s ({len(big_docs)\/dt:.1f} pairs\/s)\")\nprint(\"nDone. zerank-2 is non-commercial (CC-BY-NC-4.0); see the model card for licensing.\")<\/code><\/pre>\n<\/div>\n<\/div>\n<p class=\"wp-block-paragraph\">We test zerank-2 across finance, legal, and code examples to see how it handles different domains. We then run a batched throughput test by scoring multiple query-document pairs together. We finish by measuring how many pairs the reranker processes per second, which gives us a practical sense of its runtime performance.<\/p>\n<p class=\"wp-block-paragraph\">In conclusion, we built a complete reranking workflow that shows how zerank-2 improves the quality of retrieved results beyond basic embedding similarity. We saw how raw logits can be converted into probability-style scores, how model.rank helps order candidate passages, and how a reranker can fit naturally into retrieval-augmented generation or semantic search systems. We also benchmarked the reranking lift and measured batched throughput, giving us a practical view of both accuracy and performance. Also, we learned how to use zerank-2 as a strong precision layer for search, RAG, legal retrieval, financial analysis, and code-focused document ranking.<\/p>\n<p class=\"wp-block-paragraph\">\n<hr class=\"wp-block-separator aligncenter has-alpha-channel-opacity is-style-wide\" \/>\n<\/p><p class=\"wp-block-paragraph\">\n<\/p><p class=\"wp-block-paragraph\">Check out\u00a0the\u00a0<strong><a href=\"https:\/\/github.com\/Marktechpost\/AI-Agents-Projects-Tutorials\/blob\/main\/RAG\/zerank_2_reranker_retrieve_and_rerank_pipeline_marktechpost.py\" target=\"_blank\" rel=\"noreferrer noopener\">Full Codes here.<\/a>\u00a0<\/strong>Also,\u00a0feel free to follow us on\u00a0<strong><a href=\"https:\/\/x.com\/intent\/follow?screen_name=marktechpost\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Twitter<\/mark><\/a><\/strong>\u00a0and don\u2019t forget to join our\u00a0<strong><a href=\"https:\/\/www.reddit.com\/r\/machinelearningnews\/\" target=\"_blank\" rel=\"noreferrer noopener\">150k+ ML SubReddit<\/a><\/strong>\u00a0and Subscribe to\u00a0<strong><a href=\"https:\/\/www.aidevsignals.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">our Newsletter<\/a><\/strong>. Wait! are you on telegram?\u00a0<strong><a href=\"https:\/\/t.me\/machinelearningresearchnews\" target=\"_blank\" rel=\"noreferrer noopener\">now you can join us on telegram as well.<\/a><\/strong><\/p>\n<p class=\"wp-block-paragraph\">Need to partner with us for promoting your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar etc.?\u00a0<strong><a href=\"https:\/\/forms.gle\/MTNLpmJtsFA3VRVd9\" target=\"_blank\" rel=\"noreferrer noopener\"><mark>Connect with us<\/mark><\/a><\/strong><\/p>\n<p>The post <a href=\"https:\/\/www.marktechpost.com\/2026\/05\/26\/design-a-high-precision-retrieve-and-rerank-pipeline-with-zeroentropy-zerank-2-reranker\/\">Design a High-Precision Retrieve-and-Rerank Pipeline with ZeroEntropy Zerank-2 Reranker<\/a> appeared first on <a href=\"https:\/\/www.marktechpost.com\/\">MarkTechPost<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>In this tutorial, we use zeroe&hellip;<\/p>\n","protected":false},"author":1,"featured_media":29,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-984","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts\/984","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=984"}],"version-history":[{"count":0,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/posts\/984\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=\/wp\/v2\/media\/29"}],"wp:attachment":[{"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/connectword.dpdns.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}