{"id":379394,"date":"2024-06-21T15:01:07","date_gmt":"2024-06-21T15:01:07","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=379394"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=379394","title":{"rendered":"<span>\u041e\u043d \u043f\u043e\u0431\u0435\u0434\u0438\u043b LLM RAG: \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c BM25+ \u0441 \u0441\u0430\u043c\u044b\u0445 \u0430\u0437\u043e\u0432<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u043c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0411\u043e\u0440\u0438\u0441. \u042f \u0430\u0432\u0442\u043e\u0440 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043a\u0430\u043d\u0430\u043b\u0430 <a href=\"https:\/\/t.me\/boris_again\" rel=\"noopener noreferrer nofollow\">\u0411\u043e\u0440\u0438\u0441 \u043e\u043f\u044f\u0442\u044c<\/a>. \u041f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043c\u043d\u0435 \u043d\u0430 \u0433\u043b\u0430\u0437\u0430 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0438 \u044f \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0432 \u044d\u0442\u043e\u043c \u0437\u0430\u043a\u0430\u043f\u044b\u0432\u0430\u044e\u0441\u044c. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u043f\u043e\u0438\u0441\u043a\u0430 BM25+.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0447\u0430\u043b\u0430\u0441\u044c \u0441 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u044f \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 \u0433\u0440\u043e\u043c\u043a\u0438\u0439 \u0438 \u0437\u0430\u0431\u0430\u0432\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c BM25, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0439 \u0430\u0436 \u0432 \u0432\u043e\u0441\u044c\u043c\u0438\u0434\u0435\u0441\u044f\u0442\u044b\u0435 \u0433\u043e\u0434\u044b, <a href=\"https:\/\/blog.vespa.ai\/announcing-long-context-colbert-in-vespa\/\" rel=\"noopener noreferrer nofollow\">\u043f\u043e\u0431\u0435\u0434\u0438\u043b<\/a> \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0430 LLM.<\/p>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u0430 \u0437\u0432\u0435\u0440\u044c \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u043e\u043d \u0442\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. BM25 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043d\u043e \u0438 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u043d\u0430 Python \u0441 \u043d\u0443\u043b\u044f. \u041c\u044b \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a TF-IDF, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u0432\u0435\u0434\u0435\u043c \u0438\u0437 \u043d\u0435\u0433\u043e BM25+.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0442\u0435\u043c, \u043a\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u043f\u043e\u0438\u0441\u043a\u0435, \u0430 \u0431\u043e\u043b\u0435\u0435 \u043e\u043f\u044b\u0442\u043d\u044b\u0435 \u0440\u0435\u0431\u044f\u0442\u0430 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u043e\u043b\u0438\u0441\u0442\u0430\u0442\u044c \u0434\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430.<\/p>\n<p>\u041a\u043e\u0434 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 <a href=\"https:\/\/colab.research.google.com\/drive\/1q4mq7HN07A9lwMPy7wZdcxNtAvCRZiui?usp=sharing\" rel=\"noopener noreferrer nofollow\">Google Collab<\/a>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0d7\/2bc\/479\/0d72bc47929a0f42fa07a52aaa201cef.png\" alt=\"\u041a\u043e\u0433\u0434\u0430 \u0431\u0435\u0439\u0437\u043b\u0430\u0439\u043d \u043e\u0431\u043e\u0448\u0435\u043b \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b\" title=\"\u041a\u043e\u0433\u0434\u0430 \u0431\u0435\u0439\u0437\u043b\u0430\u0439\u043d \u043e\u0431\u043e\u0448\u0435\u043b \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b\" width=\"2000\" height=\"1235\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0d7\/2bc\/479\/0d72bc47929a0f42fa07a52aaa201cef.png\"\/><\/p>\n<div><figcaption>\u041a\u043e\u0433\u0434\u0430 \u0431\u0435\u0439\u0437\u043b\u0430\u0439\u043d <a href=\"https:\/\/blog.vespa.ai\/announcing-long-context-colbert-in-vespa\/\" rel=\"noopener noreferrer nofollow\">\u043e\u0431\u043e\u0448\u0435\u043b<\/a> \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b<\/figcaption><\/div>\n<\/figure>\n<hr\/>\n<p>\u0414\u0430\u0436\u0435 \u0432 \u044d\u043f\u043e\u0445\u0443 \u043f\u043e\u0438\u0441\u043a\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439, BM25 \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u0434\u0430\u0440 \u0438 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043b\u044c\u043d\u044b\u043c \u0431\u0435\u0439\u0437\u043b\u0430\u043d\u043e\u043c. \u0422\u0430\u043a \u0436\u0435 \u043e\u043d \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435: \u043e\u043d \u0441\u043f\u043e\u0441\u043e\u0431\u0435\u043d \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0412\u044b\u0434\u0430\u0447\u0443 BM25 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u043e\u0434\u0438\u043d \u0438\u0437 \u044d\u0442\u0430\u043f\u043e\u0432 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043e\u0442\u0434\u0430\u0432\u0430\u044f \u0435\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435. \u041c\u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f \u044d\u0442\u043e\u0442 <a href=\"https:\/\/www.kaggle.com\/datasets\/saurabhshahane\/ecommerce-text-classification\" rel=\"noopener noreferrer nofollow\">\u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/a> \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f\u043c\u0438 ecommerce \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430. \u0411\u0443\u0434\u0435\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0441\u0430\u043c\u044b\u0445 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0430\u044f \u0438\u0445, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u0440\u0438\u0434\u0435\u043c \u043a BM25+.<\/p>\n<pre><code class=\"python\">import numpy as np import pandas as pd from collections import Counter from tqdm.auto import tqdm  # https:\/\/www.kaggle.com\/datasets\/saurabhshahane\/ecommerce-text-classification data_url = 'https:\/\/raw.githubusercontent.com\/sugatagh\/E-commerce-Text-Classification\/main\/Dataset\/ecommerceDataset.csv' dataset = pd.read_csv(     data_url,     names = ['label', 'description'] ) dataset = dataset.dropna() dataset = dataset.drop_duplicates()<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0435 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u043e\u0432\u0430\u0440\u0430: <code>description<\/code>. \u0412\u0441\u0435\u0433\u043e \u0443 \u043d\u0430\u0441 \u0442\u0430\u043a\u0438\u0445 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 27802.<\/p>\n<h2>\u0413\u043b\u0443\u043f\u043d\u044b\u0439, \u043d\u0430\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a<\/h2>\n<p>\u0421\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c: \u043f\u043e\u043b\u0443\u0447\u0438\u0432 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430\u0439\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442.<\/p>\n<pre><code class=\"python\">documents = dataset.description.tolist() def search_dummy(documents, query, limit=10):     return [doc for doc in documents if query in doc][:limit]  [doc[:100] for doc in search_dummy(documents, 'smartphone', limit=2)] # \u0412\u044b\u0432\u043e\u0434: # ['Shinco 80 cm (32 Inches) HD Ready Smart LED TV SO32AS (Black) (2019 model) Size name:32 Inches   Thi', #  'Amazon Brand - Solimo 12-inch Wall Clock - Checkered (Silent Movement, Black Frame) Bring function a']<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043f\u043e\u043b\u043e\u043d \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0438 \u0441\u0430\u043c\u0430\u044f \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u0430\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e: <code>search_dummy(documents, 'SmartphonE', limit=2)<\/code> .<\/p>\n<p>\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0442\u0435\u043a\u0441\u0442\u043e\u0432.  \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043e\u0441\u043e\u0431\u043e \u043c\u0443\u0434\u0440\u0438\u0442\u044c, \u0443\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b \u043a\u0440\u043e\u043c\u0435 \u0431\u0443\u043a\u0432 \u0438 \u0447\u0438\u0441\u0435\u043b, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u0441\u0443\u0444\u0444\u0438\u043a\u0441\u044b.<\/p>\n<p>\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0444\u0444\u0438\u043a\u0441\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0441\u0442\u0435\u043c\u043c\u0438\u043d\u0433\u043e\u043c: \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c \u043a\u043e\u0440\u043d\u0435\u0439. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u0442\u0435\u043c\u043c\u0435\u0440 \u041f\u043e\u0440\u0442\u0435\u0440\u0430 \u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430 <code>nltk<\/code>. \u041d\u043e \u0443 \u043d\u0430\u0441 \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u043a\u0443\u0440\u0441 \u043f\u043e NLP.<\/p>\n<pre><code class=\"python\">import string  def stem(word):     for suffix in set(['ing', 'ly', 'ed', 'ious', 'ies', 'ive', 'es', 's', 'ment']):         if word.endswith(suffix):             return word[:-len(suffix)]     return word   def preprocess_document(document):     new_doc = ''.join(         c for c in document if c.isalnum() or c == ' '     ).lower().strip()     new_doc = ' '.join([         stem(word) for word in new_doc.split(' ')     ])     return new_doc  def preprocess_documents(documents):     new_documents = []     for doc in documents:         new_doc = preprocess_document(doc)         new_documents.append(new_doc)     return new_documents  documents_preprocessed = preprocess_documents(documents) documents_preprocessed[:1][0][:50]  # \u0412\u044b\u0432\u043e\u0434: # 'paper plane design fram wall hang motivational off'  def search_dummy(documents, query, limit=10):     query = preprocess_document(query)     return [doc for doc in documents if query in doc][:limit]  search_dummy(documents_preprocessed, 'SmartphonE', limit=1)[0][:50] # \u0412\u044b\u0432\u043e\u0434: # 'shinco 80 cm 32 inches hd ready smart led tv so32a'<\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0440\u0435\u0448\u0435\u043d\u0430. \u041d\u043e \u043b\u044e\u0431\u0430\u044f \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0441\u0440\u0430\u0437\u0443 \u0432\u0441\u0451 \u043b\u043e\u043c\u0430\u0435\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>search_dummy(documents_preprocessed, 'smrtaphone', limit=1)<\/code>  \u043d\u0435 \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e. \u0422\u0430\u043a \u0436\u0435 \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432\u0440\u043e\u0434\u0435 <code>smar<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u0432 ecommerce.<\/p>\n<h2>Term Frequency \u043d\u0430 N-\u0433\u0440\u0430\u043c\u043c\u0430\u0445<\/h2>\n<p>\u0414\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0433\u0443\u0442 N-\u0433\u0440\u0430\u043c\u043c\u044b: \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u0432 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 \u0434\u043b\u0438\u043d\u043e\u0439 N.<\/p>\n<pre><code class=\"python\">from nltk.util import ngrams import nltk  N_GRAM_SIZE = 3   def documents_to_ngrams(documents, n_gram_size=N_GRAM_SIZE, progress=False):     document_ngrams = []     iterator = documents     if progress:         iterator = tqdm(documents) # progress bar, \u0442.\u043a. \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0435\u0431\u044b\u0441\u0442\u0440\u044b\u0439     for doc in iterator:         doc_ngrams = []         for word in doc.split(' '):             word_ngrams = ngrams(word, n_gram_size)             for ngram in word_ngrams:                 doc_ngrams.append(''.join(ngram))         document_ngrams.append(tuple(doc_ngrams))     document_ngrams = tuple(document_ngrams)      return document_ngrams  documents_ngrams = documents_to_ngrams(documents_preprocessed, progress=True) documents_ngrams[0][:5] # \u0412\u044b\u0432\u043e\u0434: # ('pap', 'ape', 'per', 'pla', 'lan')<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0443\u0436\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u0432\u0441\u0451, \u0447\u0442\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u043d\u0443 N-\u0433\u0440\u0430\u043c\u043c\u0443: \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043c\u043d\u043e\u0433\u043e \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439. \u041b\u043e\u0433\u0438\u0447\u043d\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u043c\u0438 \u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b, \u0433\u0434\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0441\u0435\u0433\u043e.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u0438\u0435 <strong>\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong>. \u041d\u0443\u0436\u043d\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0439\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u043d\u043e \u0438 \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0431\u044b\u043b\u0438 \u0441\u0432\u0435\u0440\u0445\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u0430 \u043d\u0435\u043a\u0430\u044f <strong>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0430\u0440\u0435 (\u0437\u0430\u043f\u0440\u043e\u0441, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442) \u0447\u0438\u0441\u043b\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u0435\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0448\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f: \u0447\u0430\u0441\u0442\u043e\u0442\u0430 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439, \u0438\u043b\u0438 <strong>term frequency.<\/strong><\/p>\n<pre><code class=\"python\">def display_search_results(documents, idx_scores, char_limit=100):     for idx, score in idx_scores:         print(f'{score:0.2f}: {documents[idx][:char_limit]}')  def query_to_ngrams(query, n_gram_size=N_GRAM_SIZE):     return documents_to_ngrams([query], n_gram_size=n_gram_size)[0]  def search_tf(documents_ngrams, query, limit=5, n_gram_size=N_GRAM_SIZE):     index = [Counter(doc_ngrams) for doc_ngrams in documents_ngrams]     query = query_to_ngrams(query, n_gram_size)      match_scores = []     for ngram_counts in tqdm(index):         score = 0         for query_ngram in query:             score += ngram_counts.get(query_ngram, 0)         match_scores.append(score)      idx_scores = zip(range(len(documents_ngrams)), match_scores)     idx_scores = sorted(idx_scores, key=lambda pair: -pair[1])      return idx_scores[:limit]  idx_scores = search_tf(documents_ngrams, 'smratphone') display_search_results(documents, idx_scores)  ``` \u0412\u044b\u0432\u043e\u0434: 116.00: Risk Savvy: How to Make Good Decisions About the Author GERD GIGERENZER is director of the Max Planc 116.00: Risk Savvy: How to Make Good Decisions About the Author Gerd Gigerenzer is the author of Gut Feeling 105.00: HP B4B09PA Headphones with Mic Product Description HP Headphones Overview With HP B4B09PA Over ear H 98.00: iBall Rocky Over-Ear Headphones with Mic Product Description  iBall Rocky Headset Over-Ear Headphone 96.00: The Global War on Christians: Dispatches from the Front Lines of Anti-Christian Persecution About th ```<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u043f\u043e\u0438\u0441\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u0441 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\u043c\u0438, \u043d\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0443\u0436\u0430\u0441\u0435\u043d. \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442.<\/p>\n<p><code>search_tf<\/code> \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0438\u0441\u043a: \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 N-\u0433\u0440\u0430\u043c\u043c\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u0440\u0442\u0435\u0436\u0435\u0439 <code>(\u0438\u043d\u0434\u0435\u043a\u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430, \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c)<\/code>, \u0433\u0434\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u043e \u0447\u0438\u0441\u043b\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 N-\u0433\u0440\u0430\u043c\u043c. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e <code>limit<\/code> \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u0443\u0435\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b: \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043d\u044b\u0445 N-\u0433\u0440\u0430\u043c\u043c \u0432 \u043d\u0451\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435 <code>\"smasmart\"<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 N-\u0433\u0440\u0430\u043c\u043c\u044b: <code>{\"sma\": 2, \"mas\": 1, \"asm\": 1, \"mar\": 1, \"art\": 1}<\/code>. \u0422\u0430\u043a\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0441\u043a\u043e\u043b\u044c\u043a\u043e N-\u0433\u0440\u0430\u043c\u043c \u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435. \u0421\u0442\u0440\u043e\u0433\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0440\u0430\u0437, \u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435.<\/p>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0442\u0430\u043a\u043e\u0439 \u043f\u043b\u043e\u0445\u043e\u0439? \u0414\u043b\u0438\u043d\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 \u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0448\u0435 \u0432 \u0432\u044b\u0434\u0430\u0447\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0438\u0433\u0440\u0443\u0448\u0435\u0447\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"python\">def documents_to_index(documents, n_gram_size=N_GRAM_SIZE): \"\"\"\u0412\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438 \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u043d\u0430 N-\u0433\u0440\u0430\u043c\u043c\u044b\"\"\"     documents_preprocessed = [         preprocess_document(doc) for doc in documents     ]      documents_ngrams = documents_to_ngrams(documents_preprocessed, n_gram_size)     return documents_ngrams  dummy_documents = [     'smartphone',     'frying pan', ] dummy_documents_ngrams = documents_to_index(dummy_documents) idx_scores = search_tf(dummy_documents_ngrams, 'smratphone') display_search_results(dummy_documents, idx_scores) ``` \u0412\u044b\u0432\u043e\u0434: 4.00: smartphone 0.00: frying pan ```  dummy_documents = [     'smartphone',     'frying pan',     'headphones for your smartphone', ] dummy_documents_ngrams = documents_to_index(dummy_documents) idx_scores = search_tf(dummy_documents_ngrams, 'smratphone') display_search_results(dummy_documents, idx_scores)  ``` \u0412\u044b\u0432\u043e\u0434: 7.00: headphones for your smartphone 4.00: smartphone 0.00: frying pan ```<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u043d\u0430\u043c \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b N-\u0433\u0440\u0430\u043c\u043c\u0430 <code>sma<\/code> \u0432\u043d\u043e\u0441\u0438\u043b\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0439 \u0432\u043a\u043b\u0430\u0434 \u0432 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0435\u0433\u043e \u0434\u043b\u0438\u043d\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0441\u043b\u043e\u0432\u0435 <code>smart<\/code> \u044d\u0442\u043e \u043e\u0434\u043d\u0430 \u0438\u0437 \u0442\u0440\u0435\u0445 N-\u0433\u0440\u0430\u043c\u043c, \u0430 \u0432 \u0441\u043b\u043e\u0432\u0435 <code>smartphone<\/code> \u043e\u043d\u0430 \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u043d\u044c\u0448\u0438\u0439 \u0432\u0435\u0441.<\/p>\n<pre><code class=\"python\">def search_tf_weighted(documents_ngrams, query, limit=5, n_gram_size=N_GRAM_SIZE):     index = [Counter(doc_ngrams) for doc_ngrams in documents_ngrams]     query = query_to_ngrams(query, n_gram_size)     match_scores = []     for ngram_counts in index:         score = 0         total_ngrams = sum(ngram_counts.values())         if total_ngrams == 0:             continue         for query_ngram in query:             score += ngram_counts.get(query_ngram, 0)         score = score\/total_ngrams         match_scores.append(score)      idx_scores = zip(range(len(documents_ngrams)), match_scores)     idx_scores = sorted(idx_scores, key=lambda pair: -pair[1])      return idx_scores[:limit]  idx_scores = search_tf_weighted(dummy_documents_ngrams, 'smratphone') display_search_results(dummy_documents, idx_scores) ``` \u0412\u044b\u0432\u043e\u0434: 0.50: smartphone 0.39: headphones for your smartphone 0.00: frying pan ```<\/code><\/pre>\n<p>\u0423\u0436\u0435 \u043b\u0443\u0447\u0448\u0435.<\/p>\n<p>\u0414\u0430\u0436\u0435 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u0432\u0441\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c \u0443\u0436\u0435 \u043d\u0435 \u0431\u0435\u0441\u043f\u043e\u043b\u0435\u0437\u0435\u043d:<\/p>\n<pre><code class=\"python\">idx_scores = search_tf_weighted(documents_ngrams, 'smratphone') display_search_results(documents, idx_scores)  ``` \u0412\u044b\u0432\u043e\u0434: 0.23: Apple iPhone Xs (Space Grey, 4GB RAM, 64GB Storage, 12 MP Dual Camera, 458 PPI Display) Size name:64 0.19: AmazonBasics Lighting to USB A Cable for iPhone and iPad - 4 Inches (10 Centimeters) - Black (Ideal  0.18: VALUEACTIVE Screen Guard for Samsung Galaxy S10 Plus Tempered Glass Full Screen Coverage 5D Curved F 0.17: TSIM Lifetime Global SIM Card(1GB) Size:1GB   Voice \/ SMS Slabs 100 minutes\/100 SMS in: Australia, B 0.17: JBL T450BT Extra Bass Wireless On-Ear Headphones with Mic (Black) Colour:Black   Colour:Black Powerf ```  idx_scores = search_tf_weighted(documents_ngrams, 'iphone 7') display_search_results(documents, idx_scores) ``` 0.31: Apple iPhone Xs (Space Grey, 4GB RAM, 64GB Storage, 12 MP Dual Camera, 458 PPI Display) Size name:64 0.24: VALUEACTIVE Screen Guard for Samsung Galaxy S10 Plus Tempered Glass Full Screen Coverage 5D Curved F 0.22: Apple iPhone 7 (Black, 2GB RAM, 32GB Storage) Colour:Black                                           0.21: TSIM Lifetime Global SIM Card(1GB) Size:1GB   Voice \/ SMS Slabs 100 minutes\/100 SMS in: Australia, B 0.20: AmazonBasics Lighting to USB A Cable for iPhone and iPad - 4 Inches (10 Centimeters) - Black (Ideal  ```<\/code><\/pre>\n<p>\u041c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u043f\u043e\u0438\u0441\u043a \u043d\u0430 term frequency \u0438 \u043f\u0440\u043e\u0448\u043b\u0438 \u0442\u0440\u0435\u0442\u044c \u043f\u0443\u0442\u0438 \u0434\u043e BM25+.<\/p>\n<p>\u041d\u043e \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u0431\u043e\u043b\u0435\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u044b\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u043f\u0440\u0438\u043b\u0430\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0445:<\/p>\n<pre><code class=\"python\">idx_scores = search_tf_weighted(documents_ngrams, 'the heavy simple beautiful black iphone') display_search_results(documents, idx_scores)  ``` \u0412\u044b\u0432\u043e\u0434: 0.86: Dick Francis's Refusal Review Praise for Dick Francis\u2019s Refusal \u201cTo the delight of Dick\/Felix Franci 0.62: Apple iPhone Xs (Space Grey, 4GB RAM, 64GB Storage, 12 MP Dual Camera, 458 PPI Display) Size name:64 0.38: Betting on Horse Racing For Dummies (For Dummies Series) From the Back Cover Cash in big on multiple 0.38: Seagate 2TB Backup Plus Slim (Blue) USB 3.0 External Hard Drive for PC\/Mac with 2 Months Free Adobe  0.30: ahhaaaa Boy's Cotton Waistcoat, Shirt And Trouser Set This product is made from cotton. Ahhaaaa brin ```<\/code><\/pre>\n<p>\u041f\u043e\u0438\u0441\u043a \u0441\u043d\u043e\u0432\u0430 \u0441\u043b\u043e\u043c\u0430\u043b\u0441\u044f. \u0412\u0441\u0451 \u0434\u0435\u043b\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430 \u0432\u0441\u0435 \u0441\u043b\u043e\u0432\u0430 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u043e \u0432\u0430\u0436\u043d\u044b, \u0447\u0442\u043e <code>beautiful<\/code>, \u0447\u0442\u043e <code>the<\/code>, \u0447\u0442\u043e <code>iphone<\/code>. <\/p>\n<h2>Inverse Document Frequency<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u0435\u043c \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a \u043a\u0430\u0436\u0434\u043e\u0439 N-\u0433\u0440\u0430\u043c\u043c\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0432\u0435\u0441, \u0447\u0442\u043e\u0431\u044b \u043e\u043d \u0431\u044b\u043b \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u043e\u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0432\u043d\u0435\u0435. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 Inverse Document Frequency (IDF): \u043c\u0435\u0440\u0430 \u0442\u043e\u0433\u043e, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0434\u043a\u043e N-\u0433\u0440\u0430\u043c\u043c\u0430 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0441\u0440\u0435\u0434\u0438 \u0432\u0441\u0435\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u0418\u043d\u0442\u0443\u0438\u0442\u0438\u0432\u043d\u043e, \u0447\u0442\u043e \u0447\u0435\u043c \u0440\u0435\u0436\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f N-\u0433\u0440\u0430\u043c\u043c\u0430, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e\u043d\u0430 \u043d\u0435\u0441\u0435\u0442. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 <code>the<\/code> \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u043a\u0430\u0436\u0434\u043e\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u0442\u043e \u044d\u0442\u0443 N-\u0433\u0440\u0430\u043c\u043c\u0443 \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438. \u0418, \u043d\u0430\u043f\u0440\u043e\u0442\u0438\u0432, \u0435\u0441\u043b\u0438 <code>sma<\/code> \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u0442\u043e \u0435\u0451 \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0430\u043c\u044b\u0439 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442.<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c IDF \u043f\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0444\u043e\u0440\u043c\u0443\u043b\u0435 \u0438\u0437 \u0412\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u0438:<\/p>\n<pre><code class=\"python\">from collections import defaultdict  def calculate_idf(documents_ngrams):     ngram_appearance = {}     for doc_ngrams in documents_ngrams:         for ngram in set(doc_ngrams):             if ngram not in ngram_appearance:                 ngram_appearance[ngram] = 0             ngram_appearance[ngram] += 1     idf = {}     N = len(documents_ngrams)     for ngram, appearance_count in ngram_appearance.items():         idf[ngram] = np.log((1+N)\/(1 + appearance_count))     return idf  dummy_documents = [     'smartphone',     'frying pan',     'headphones for your smartphone', ] dummy_documents_ngrams = documents_to_index(dummy_documents) dummy_idf = calculate_idf(dummy_documents_ngrams) dummy_idf  ``` \u0412\u044b\u0432\u043e\u0434: {'pho': 0.28768207245178085,  'mar': 0.28768207245178085,  'tph': 0.28768207245178085,  'sma': 0.28768207245178085,  'one': 0.28768207245178085,  'art': 0.28768207245178085,  'hon': 0.28768207245178085,  'rtp': 0.28768207245178085,  'pan': 0.6931471805599453,  'fry': 0.6931471805599453,  'adp': 0.6931471805599453,  'our': 0.6931471805599453,  'for': 0.6931471805599453,  'you': 0.6931471805599453,  'ead': 0.6931471805599453,  'dph': 0.6931471805599453,  'hea': 0.6931471805599453} ```<\/code><\/pre>\n<p>\u0412\u0438\u0434\u043d\u043e, \u0447\u0442\u043e N-\u0433\u0440\u0430\u043c\u043c\u044b \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0435\u0441\u044f \u043a \u0441\u043a\u043e\u0432\u043e\u0440\u043e\u0434\u043a\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u0432\u044b\u0441\u043e\u043a\u0438\u0439 \u0432\u0435\u0441, \u0442\u0430\u043a \u043a\u0430\u043a \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u0440\u0435\u0436\u0435.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0440\u0430\u0432\u043d\u0430 \u0441\u0443\u043c\u043c\u0435 \u0447\u0430\u0441\u0442\u043e\u0442 N-\u0433\u0440\u0430\u043c\u043c \u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u043e\u043c\u043d\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u043d\u0430 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u0438\u0445 N-\u0433\u0440\u0430\u043c\u043c.<\/p>\n<p>\u041e\u043d\u0430 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f TF-IDF:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"score(D, Q) = \\sum_{i=1}^{n} IDF(q_i) TF(q_i, D)\" alt=\"score(D, Q) = \\sum_{i=1}^{n} IDF(q_i) TF(q_i, D)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/63a\/e25\/2ee\/63ae252eeab0747b0069ee9bf43d14c4.svg\" width=\"314\" height=\"57\"\/><\/p>\n<p>\u0413\u0434\u0435 <img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"D\" alt=\"D\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f14\/5a9\/04d\/f145a904d77a3e54fcf5688130d00a9c.svg\" width=\"16\" height=\"17\"\/> \u044d\u0442\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442, <img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"Q\" alt=\"Q\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a13\/0b2\/293\/a130b229308ab683e950eb46bc523330.svg\" width=\"15\" height=\"20\"\/> \u044d\u0442\u043e \u0437\u0430\u043f\u0440\u043e\u0441, \u0430 <img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"q_i\" alt=\"q_i\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3cc\/9ba\/029\/3cc9ba029cf74169ddddf9aafa1afe39.svg\" width=\"15\" height=\"15\"\/> \u044d\u0442\u043e N-\u0433\u0440\u0430\u043c\u043c\u0430 \u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430. \u0417\u0430\u043c\u0435\u0442\u044c\u0442\u0435, \u0447\u0442\u043e IDF \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430, \u0442.\u043a. \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u043c \u0432 \u043d\u0430\u0448\u0435\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 N-\u0433\u0440\u0430\u043c\u043c\u044b.<\/p>\n<p>IDF \u0438 TF \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u044d\u0442\u043e\u0442 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u043f\u043e\u0438\u0441\u043a\u0430:<\/p>\n<pre><code class=\"python\">def search_tf_idf(     documents_ngrams,     query,     limit=5,     n_gram_size=N_GRAM_SIZE, ):     index = [Counter(doc_ngrams) for doc_ngrams in documents_ngrams]     idf = calculate_idf(documents_ngrams)     query = query_to_ngrams(query, n_gram_size)      match_scores = []     for ngram_counts in tqdm(index):         score = 0         total_ngrams = sum(ngram_counts.values())         if total_ngrams == 0:             continue         for query_ngram in query:             tf_score = ngram_counts.get(query_ngram, 0)\/total_ngrams             idf_score = idf.get(query_ngram, 1e-3)             score += tf_score * idf_score         match_scores.append(score)      idx_scores = zip(range(len(documents_ngrams)), match_scores)     idx_scores = sorted(idx_scores, key=lambda pair: -pair[1])      return idx_scores[:limit]  dummy_documents = [     'smartphone',     'frying pan',     'headphones for your smartphone', ] dummy_documents_ngrams = documents_to_index(dummy_documents) idx_scores = search_tf_idf(dummy_documents_ngrams, 'smratphone') display_search_results(dummy_documents, idx_scores) ``` \u0412\u044b\u0432\u043e\u0434: 0.14: smartphone 0.11: headphones for your smartphone 0.00: frying pan ```<\/code><\/pre>\n<p>\u041f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0443\u0436\u0435 \u043b\u0443\u0447\u0448\u0435, \u043d\u043e \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043d\u0435 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e:<\/p>\n<pre><code class=\"python\">idx_scores = search_tf_idf(documents_ngrams, 'health smartwatch') display_search_results(documents, idx_scores) ``` \u0412\u044b\u0432\u043e\u0434: 0.96: A History of American Literature Review \"Richard Gray's real achievement is somehow to have compress 0.88: Samsung Gear Sport Smartwatch (Black) Colour:Black   Sports watch design in premium stainless steel  0.85: American Pastoral Amazon.com Review Philip Roth's 22nd book takes a life-long view of the American e 0.53: M3 Intelligence Bluetooth Health Wrist Smart Band Watch Monitor\/Smart Bracelet\/Health Bracelet\/Activ 0.52: Textbook of Elements of Veterinary Public Health Veterinary Public Health is a multidisciplinary fie ```<\/code><\/pre>\n<h2>BM25+<\/h2>\n<p>\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 BM25+ \u044d\u0442\u043e \u0447\u0430\u0441\u0442\u043d\u044b\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 TF-IDF \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0432\u044b\u0431\u043e\u0440\u043e\u043c TF \u0438 IDF. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043a \u043d\u0435\u043c\u0443 \u0442\u0430\u043a \u0434\u043e\u043b\u0433\u043e \u0434\u043e\u0431\u0438\u0440\u0430\u043b\u0438\u0441\u044c.<\/p>\n<p>\u0415\u0433\u043e \u0437\u0430\u0434\u0443\u043c\u043a\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u0432\u0435\u0441\u0430, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0442\u0430\u043a \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u0434\u043b\u0438\u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043e\u043d \u0438\u043c\u0435\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u0437 \u0422\u0435\u043e\u0440\u0438\u0438 \u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438: IDF \u0432 BM25+ \u044d\u0442\u043e \u043f\u043e \u0441\u0443\u0442\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043f\u043e \u0428\u0435\u043d\u043d\u043e\u043d\u0443, \u0442\u043e \u0435\u0441\u0442\u044c \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0434\u0430\u043d\u043d\u0430\u044f N-\u0433\u0440\u0430\u043c\u043c\u0430.<\/p>\n<p>\u041f\u043e\u043c\u0438\u043c\u043e \u044d\u0442\u043e\u0433\u043e BM25+ \u043b\u0443\u0447\u0448\u0435 \u0432\u0441\u0435\u0433\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430 \u0441\u043b\u043e\u0432\u0430\u0445, \u0430 \u043d\u0435 N-\u0433\u0440\u0430\u043c\u043c\u0430\u0445.<\/p>\n<p>\u0424\u043e\u0440\u043c\u0443\u043b\u044b (\u0441\u0442\u0440\u0430\u0448\u043d\u044b\u0435):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"TF(q_i, D) = \\frac{(f(q_i, D) \\times (k_1 + 1)}{(f(q_i, D) \\times (k_1 + 1) + (1 - b + b \\cdot \\frac{|D|}{avgdl})} + \\delta\" alt=\"TF(q_i, D) = \\frac{(f(q_i, D) \\times (k_1 + 1)}{(f(q_i, D) \\times (k_1 + 1) + (1 - b + b \\cdot \\frac{|D|}{avgdl})} + \\delta\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/772\/8f7\/439\/7728f74396e58c445620859152cadfbe.svg\" width=\"483\" height=\"63\"\/><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"IDF(q_i) = ln(\\frac{N - n(q_i) + 0.5}{n(q_i) + 0.5} + 1)\" alt=\"IDF(q_i) = ln(\\frac{N - n(q_i) + 0.5}{n(q_i) + 0.5} + 1)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/753\/444\/34c\/75344434c95d1d1b375f3b3d66fa359d.svg\" width=\"304\" height=\"50\"\/><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"score(D, Q) = \\sum_{i=1}^{n} IDF(q_i) TF(q_i, D)\" alt=\"score(D, Q) = \\sum_{i=1}^{n} IDF(q_i) TF(q_i, D)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1ee\/e6a\/f6b\/1eee6af6bff3b382f55498416b27c90c.svg\" width=\"314\" height=\"57\"\/><\/p>\n<p>\u0417\u0434\u0435\u0441\u044c:<\/p>\n<ul>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"D\" alt=\"D\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/76f\/e86\/c5b\/76fe86c5b6c5aee2b51882d975081a17.svg\" width=\"16\" height=\"17\"\/> &#8212; \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"Q\" alt=\"Q\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dcd\/9da\/96b\/dcd9da96b39b8f7a1c144936ae30af8c.svg\" width=\"15\" height=\"20\"\/> &#8212; \u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"f(q_i, D)\" alt=\"f(q_i, D)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b46\/171\/938\/b461719389ea1d4fe069b2ad2d043d51.svg\" width=\"66\" height=\"22\"\/> &#8212; \u0447\u0438\u0441\u043b\u043e \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0439 \u0441\u043b\u043e\u0432\u0430 <img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"q_i\" alt=\"q_i\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/32d\/aac\/5c9\/32daac5c9c173eb64eece3c84a24e56f.svg\" width=\"15\" height=\"15\"\/> \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"|D|\" alt=\"|D|\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/975\/8f9\/cc9\/9758f9cc9a655b549e84596f265a74ff.svg\" width=\"27\" height=\"22\"\/> &#8212; \u0434\u043b\u0438\u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"avgdl\" alt=\"avgdl\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1e5\/f9a\/23f\/1e5f9a23f0fd9efc36363cb8564bb905.svg\" width=\"45\" height=\"21\"\/> &#8212; \u0441\u0440\u0435\u0434\u043d\u044f\u044f \u0434\u043b\u0438\u043d\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"N\" alt=\"N\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/795\/c0b\/91b\/795c0b91b6224069b8ea359a5a0dbe86.svg\" width=\"17\" height=\"17\"\/> &#8212; \u0447\u0438\u0441\u043b\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0432 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"n(q_i)\" alt=\"n(q_i)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b88\/893\/d12\/b88893d1278536907113bcbe0f590ae8.svg\" width=\"42\" height=\"22\"\/> &#8212; \u0447\u0438\u0441\u043b\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 \u0441\u043b\u043e\u0432\u043e <img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"q_i\" alt=\"q_i\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ccc\/346\/12d\/ccc34612d1750ab2ef323f6847020645.svg\" width=\"15\" height=\"15\"\/>.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"k_1\" alt=\"k_1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f9e\/414\/c6c\/f9e414c6c47263955d00aadc9e9746d0.svg\" width=\"19\" height=\"19\"\/> &#8212; \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 [1.2, 2.0].<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"b\" alt=\"b\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/34e\/2b5\/f9b\/34e2b5f9b0f47c7cde0183c7cbd59f21.svg\" width=\"8\" height=\"17\"\/> &#8212; \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440, \u043e\u0431\u044b\u0447\u043d\u043e 0.75.<\/p>\n<\/li>\n<li>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula inline\" source=\"\\delta\" alt=\"\\delta\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/389\/f91\/320\/389f9132012c1551cd9a15999e7c915c.svg\" width=\"9\" height=\"18\"\/> &#8212; \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440, \u043e\u0431\u044b\u0447\u043d\u043e 1.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e \u0441\u0443\u0442\u0438 <strong>TF \u044d\u0442\u043e \u0432\u0435\u0441 \u0441\u043b\u043e\u0432\u0430 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435<\/strong>, \u0430 <strong>IDF \u044d\u0442\u043e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u0441\u043b\u043e\u0432\u0430<\/strong>. \u041d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430 \u0437\u0430\u043d\u0438\u043c\u0430\u044e\u0442 \u0440\u0435\u0434\u043a\u0438\u0435 \u0441\u043b\u043e\u0432\u0430.<\/p>\n<p>\u0421\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0440\u0435\u0433\u0443\u043b\u0438\u0440\u0443\u044e\u0442 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. \u0418\u0445 \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043a\u0430\u043a \u0435\u0441\u0442\u044c \u0438\u043b\u0438 \u043f\u043e\u0434\u0431\u0438\u0440\u0430\u0442\u044c \u043f\u043e\u0434 \u0437\u0430\u0434\u0430\u0447\u0443.<\/p>\n<h2>\u041a\u0430\u043a \u0432\u0435\u0434\u0443\u0442 \u0441\u0435\u0431\u044f IDF \u0438 TF \u0432 BM25+?<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0433\u043b\u044f\u0434\u0438\u043c \u043d\u0430 TF \u0438 IDF, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u0432 \u0447\u0435\u043c \u0441\u0443\u0442\u044c \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0430\u043a\u0438\u0445 \u0432\u0435\u0441\u043e\u0432.<\/p>\n<p>IDF:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/eeb\/bf4\/c03\/eebbf4c03b0df39aac4d946111403cc2.png\" alt=\"IDF \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0447\u0438\u0441\u043b\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 \u0441\u043b\u043e\u0432\u043e.\" title=\"IDF \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0447\u0438\u0441\u043b\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 \u0441\u043b\u043e\u0432\u043e.\" width=\"700\" height=\"525\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eeb\/bf4\/c03\/eebbf4c03b0df39aac4d946111403cc2.png\"\/><\/p>\n<div><figcaption>IDF \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0447\u0438\u0441\u043b\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0445 \u0441\u043b\u043e\u0432\u043e.<\/figcaption><\/div>\n<\/figure>\n<p>\u0418\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u0440\u0435\u0434\u043a\u0438\u0445 \u0441\u043b\u043e\u0432 \u043e\u0447\u0435\u043d\u044c \u0432\u044b\u0441\u043e\u043a\u0430\u044f. \u0420\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043b\u043e\u0432\u0430, \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u0432 \u043e\u0434\u043d\u043e\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u0438 \u0441\u043b\u043e\u0432\u0430, \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0449\u0435\u043c\u0441\u044f \u0432 \u0434\u0432\u0443\u0445, \u043e\u0433\u0440\u043e\u043c\u043d\u0430\u044f. \u041e\u0434\u043d\u0430\u043a\u043e \u0447\u0435\u043c \u0447\u0430\u0449\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u043e\u0432\u043e, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u0438\u0435: \u043c\u0435\u0436\u0434\u0443 \u0441\u043b\u043e\u0432\u043e\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c\u0441\u044f \u0432 40,000 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445 \u0438 60,000 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435\u0442 \u0440\u0430\u0437\u043d\u0438\u0446\u044b.<\/p>\n<p>TF:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/3da\/512\/1ad\/3da5121ad1106105118d10ff6d5ff41c.png\" alt=\"TF \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0439 \u0441\u043b\u043e\u0432\u0430 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0438 \u0434\u043b\u0438\u043d\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430.\" title=\"TF \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0439 \u0441\u043b\u043e\u0432\u0430 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0438 \u0434\u043b\u0438\u043d\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430.\" width=\"700\" height=\"525\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3da\/512\/1ad\/3da5121ad1106105118d10ff6d5ff41c.png\"\/><\/p>\n<div><figcaption>TF \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u0439 \u0441\u043b\u043e\u0432\u0430 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0438 \u0434\u043b\u0438\u043d\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430.<\/figcaption><\/div>\n<\/figure>\n<p>\u0412\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0447\u0435\u043c \u0447\u0430\u0449\u0435 \u0441\u043b\u043e\u0432\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u0442\u0435\u043c \u0432\u044b\u0448\u0435 \u0435\u0433\u043e \u0432\u0435\u0441. \u041e\u0434\u043d\u0430\u043a\u043e \u0435\u0441\u0442\u044c \u044d\u0444\u0444\u0435\u043a\u0442 \u043d\u0430\u0441\u044b\u0449\u0435\u043d\u0438\u044f: \u043c\u0435\u0436\u0434\u0443 10 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f\u043c\u0438 \u0438 20 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f\u043c\u0438 \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f. \u0422\u0430\u043a \u0436\u0435 \u0432\u0435\u0441 \u0432\u044b\u0448\u0435, \u0435\u0441\u043b\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439. \u0412\u0441\u0451 \u044d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u0432\u043b\u0438\u044f\u043d\u0438\u0435 \u0434\u043b\u0438\u043d\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f BM25+<\/h2>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0451 \u043f\u043e \u0444\u043e\u0440\u043c\u0443\u043b\u0430\u043c. \u0412 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0432\u0441\u0451 \u043a\u0440\u0430\u0441\u0438\u0432\u043e, \u043e\u0431\u0435\u0440\u043d\u0443\u0432 \u043a\u043e\u0434 \u0432 \u043a\u043b\u0430\u0441\u0441, \u0438 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435.<\/p>\n<pre><code class=\"python\">def documents_to_words(documents):     documents_words = tuple([doc.split(' ') for doc in documents])     documents_words = tuple(documents_words)     return documents_words  def bm25_documents_to_index(documents):     documents_preprocessed = [         preprocess_document(doc) for doc in documents     ]      documents_words = documents_to_words(documents_preprocessed)     return documents_words  def bm25_query_to_wrods(query):     return bm25_documents_to_index([query])[0]  def idf_bm25(     number_documents_containing_ngram,     total_documents, ):     x = (total_documents - number_documents_containing_ngram + 0.5)\/(number_documents_containing_ngram + 0.5)     return np.log(x + 1)  def tf_bm25(ngram_tf, document_length, average_document_length, k1=1.5, b=0.75, delta=1):     numerator = ngram_tf*(k1+1)     denominator = ngram_tf + (k1 * (1 - b + b * document_length\/average_document_length))     return numerator\/denominator + delta  def bm25_score(     ngram_idf,     ngram_tf,     document_length,     average_document_length,     k1=1.5,     b=0.75, ):     numerator = ngram_tf*(k1+1)     denominator = ngram_tf + (k1 * (1 - b + b * document_length\/average_document_length))     return ngram_idf * tf_bm25(ngram_tf, document_length, average_document_length, k1=k1, b=b)   class SearchBM25:     def __init__(self):         self.documents = None         self.documents_ngrams = None         self.tf = None         self.idf = None      def calculate_tf(self, documents_ngrams):         tf = [Counter(doc_ngrams) for doc_ngrams in documents_ngrams]          return tf      def calculate_idf(self, tf, documents_ngrams):         idf = {}          documents_containing = {}          for doc_tf in tqdm(tf):             for ngram in doc_tf.keys():                 if not ngram in documents_containing:                     documents_containing[ngram] = 0                 documents_containing[ngram] += 1          for ngram in tqdm(documents_containing.keys()):             idf[ngram] = idf_bm25(                 number_documents_containing_ngram=documents_containing[ngram],                 total_documents=len(documents_ngrams),             )         return idf      def fit(         self,         documents,     ):         self.documents = documents          self.documents_ngrams = bm25_documents_to_index(             documents,         )          self.tf = self.calculate_tf(self.documents_ngrams)         self.idf = self.calculate_idf(self.tf, self.documents_ngrams)      def search_bm25(         self,         query,         limit,         only_documents=None,     ):         avg_document_length = sum([             len(doc) for doc in self.documents_ngrams         ])\/len(self.documents_ngrams)         query = bm25_query_to_wrods(query)         indexes = []         match_scores = []          document_indexes = range(len(self.tf)) if only_documents is None else only_documents         for i in document_indexes:             document_tf = self.tf[i]              document_length = sum(document_tf.values())             if document_length == 0:                 continue              score = 0             for query_ngram in query:                 ngram_score = bm25_score(                     self.idf.get(query_ngram, 1e-6),                     document_tf.get(query_ngram, 1e-6),                     document_length=document_length,                     average_document_length=avg_document_length,                 )                 score += ngram_score             match_scores.append(score)             indexes.append(i)          idx_scores = zip(indexes, match_scores)         idx_scores = sorted(idx_scores, key=lambda pair: -pair[1])         return idx_scores[:limit]       def search(self, query, limit=5):         idx_scores = self.search_bm25(             query,             limit=limit,         )         return idx_scores[:limit]      def search_and_display(self, query, limit=5, char_limit=100):         idx_scores = self.search(query, limit=limit)         display_search_results(self.documents, idx_scores, char_limit=char_limit)   index = SearchBM25() index.fit(documents)  index.search_and_display('frying') ``` \u0412\u044b\u0432\u043e\u0434: 16.30: Taylor Candy and Deep Fry Stainless Steel Thermometer TAYLOR 5983N Candy\/Jelly Deep Fry Thermometer 16.02: Bhavya enterprises Stainless Steel Deep Fat Fryer 8+8 L (Silver) Frying machine used in commercial s 15.96: Hk Villa 2 In 1 Multifuctional Steaming Device egg pan Frying Egg Boiling Roasting Heating Electric  15.71: HomeFast Multifunction Non-Stick Electric Frying Pan Egg Omelette Pancakes Steak Egg Boiler Electric 15.66: Vishal Smart Mall Multifunction Non-Stick Electric Frying Pan Egg Omelette Pancakes Steak Egg Boiler ```  index.search_and_display('iphone 6s') ``` 21.93: Mpow iPhone 6 6s iPhone 7 8 Armband, [Ultra Comfortable] Adjustable Sports Armband Sweatproof Runnin 21.39: MADANYU The Man in Suit for Real Man Designer Printed Hard Back Shell Case for Apple iPhone 6S \/ App 21.22: POPIO Tempered Glass Screen Protector For iPhone 6 \/ iPhone 6S \/ iPhone 7 \/ iPhone 8 (Pack Of 2) Col 21.21: UNMCORE IPhone 8 Pin Lightning To USB Fast Data Charging Sync Cable Wall Charger With USB Power Adap 21.00: Apple iPhone 6 (Gold, 1GB RAM, 32GB Storage) Colour:Gold   Apple iPhone 6 (Gold, 32GB) ```<\/code><\/pre>\n<p>\u041d\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u043f\u043e\u0438\u0441\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u043e, \u043d\u043e \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0445 \u0445\u0443\u0436\u0435 TF-IDF.<\/p>\n<p>BM25+ \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0447\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u0435\u043d \u043a \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u0435\u0434\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0442\u0435\u043a\u0441\u0442\u0430. \u0414\u0440\u0443\u0433\u0430\u044f \u0435\u0433\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0432 \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u043d \u043d\u0435\u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432 \u043a \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\u043c \u0438 \u043d\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u0435\u043d \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<h2>\u0414\u0432\u0443\u0445\u044d\u0442\u0430\u043f\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a: TF-IDF + BM25+<\/h2>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 TF-IDF \u0438 BM25+. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0434\u0432\u0443\u0445\u044d\u0442\u0430\u043f\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a: \u0441\u043d\u0430\u0447\u0430\u043b\u0430 TF-IDF \u043d\u0430 N-\u0433\u0440\u0430\u043c\u043c\u0430\u0445 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432-\u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u0432, \u0437\u0430\u0442\u0435\u043c BM25+ \u0431\u0443\u0434\u0435\u0442 \u0440\u0435-\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043b\u0443\u0447\u0448\u0438\u0435. <\/p>\n<pre><code class=\"python\">from scipy.stats import gmean  class SearchTFIDF:     def __init__(self, n_gram_size=N_GRAM_SIZE):         self.n_gram_size = n_gram_size          self.documents = None         self.documents_ngrams = None      def fit(         self,         documents,     ):         self.documents = documents          self.documents_ngrams = documents_to_index(             documents,             n_gram_size=self.n_gram_size,         )      def search(self, query, limit=5):         idx_scores = search_tf_idf(             self.documents_ngrams,             query,             limit=limit,             n_gram_size=self.n_gram_size,         )         return idx_scores[:limit]      def search_and_display(self, query, limit=5):         idx_scores = self.search(query, limit=limit)         display_search_results(self.documents, idx_scores)          class TwoStageSearch:     def __init__(self, n_gram_size=3):         self.n_gram_size = n_gram_size         self.documents = None         self.tfidf_index = None         self.bm25_index = None      def fit(         self,         documents,     ):         self.documents = documents          self.tfidf_index = SearchTFIDF(n_gram_size=self.n_gram_size)         self.tfidf_index.fit(self.documents)          self.bm25_index = SearchBM25()         self.bm25_index.fit(self.documents)      def search(self, query, limit_stage1=100, limit_stage2=5):         idx_scores_stage1 = self.tfidf_index.search(query, limit=limit_stage1)         idx_scores_stage1 = [p for p in idx_scores_stage1 if p[1] > 1e-05]         idx_to_score_stage1 = {             idx: score for idx, score in idx_scores_stage1         }         only_document_indexes = list(idx_to_score_stage1.keys())         idx_scores_stage2 = self.bm25_index.search_bm25(query, limit=limit_stage2, only_documents=only_document_indexes)          aggregated_scores = {             idx: gmean([score, idx_to_score_stage1[idx]]) for idx, score in idx_scores_stage2         }         idx_scores = [(idx, idx_to_score_stage1[idx], score, aggregated_scores[idx]) for idx, score in idx_scores_stage2]          idx_scores = sorted(idx_scores, key=lambda x: (-round(x[-1],3), -round(x[-2],3), -round(x[-3], 3)))          return idx_scores      def display_search_results(self, idx_scores, char_limit=100):         for idx, score_stage1, score_stage2, score_combined in idx_scores:             print(f'{score_stage1:0.2f}|{score_stage2:0.2f}|{score_combined:0.2f}: {self.documents[idx][:char_limit]}')      def search_and_display(self, query, limit_stage1=100, limit_stage2=5, char_limit=100):         idx_scores = self.search(query, limit_stage1=limit_stage1, limit_stage2=limit_stage2)         self.display_search_results(idx_scores, char_limit=char_limit) <\/code><\/pre>\n<p>\u041a\u043e\u0434 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0443\u0433\u0430\u044e\u0449\u0438\u043c, \u043d\u043e \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f:<\/p>\n<ol>\n<li>\n<p>\u0421\u0442\u0440\u043e\u0438\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u044b \u0434\u043b\u044f TF-IDF \u0438 BM25+<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438 \u043f\u043e\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c TF-IDF \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c 100 \u0441\u0430\u043c\u044b\u0445 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c BM25+ \u043d\u0430 \u044d\u0442\u0438\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c 5 \u0441\u0430\u043c\u044b\u0445 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0445.<\/p>\n<\/li>\n<li>\n<p>\u0427\u0442\u043e\u0431\u044b \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043c\u044b \u0443\u0441\u0440\u0435\u0434\u043d\u044f\u0435\u043c \u043e\u0446\u0435\u043d\u043a\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u043e\u0442 TF-IDF \u0438 BM25+ \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0441\u0440\u0435\u0434\u043d\u0438\u043c (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0430\u0440\u0438\u0444\u043c\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u043d\u043e \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e \u043a \u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043e\u0446\u0435\u043d\u043a\u0438 \u043c\u043e\u0433\u0443\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u0448\u043a\u0430\u043b\u0430\u0445).<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u043e \u0443\u0441\u0440\u0435\u0434\u043d\u0435\u043d\u043d\u044b\u043c \u043e\u0446\u0435\u043d\u043a\u0430\u043c. \u041f\u0440\u0438 \u043d\u0438\u0447\u044c\u0435\u0439 \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u043e \u043e\u0446\u0435\u043d\u043a\u0430 \u043e\u0442 TF-IDF.<\/p>\n<\/li>\n<\/ol>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c.<\/p>\n<pre><code class=\"python\">index = TwoStageSearch() index.fit(documents) index.search_and_display('iph')  ``` \u0412\u044b\u0432\u043e\u0434: 0.12|0.00|0.00: Apple iPhone 8 (Space Grey, 2GB RAM, 64GB Storage) 0.09|0.00|0.00: Natation 3D VR Box Virtual Reality Glasses (VR_Headset) (VR Basic) 0.08|0.00|0.00: Tidy Up! Wire Bin (Black) 0.06|0.00|0.00: Orion Premium Telescope Accessory Kit - 1.25\" Orion Orion 08890 1.25-Inch Premium Telescope Accessor 0.04|0.00|0.00: Apple iPhone 6S (Rose Gold, 2GB RAM, 32GB Storage) ```  index.search_and_display('iphone xs 64GB') ``` \u0412\u044b\u0432\u043e\u0434: 0.50|7.29|1.91: Apple iPhone 8 Plus (Space Grey, 64GB) Colour:Space Grey                                             0.36|7.49|1.64: SKYVIK Beam QI Certified 7.5W &amp; 10W Fast Wireless Charger for iPhone X XS Max XR iPhone 8 &amp; 8 Plus S 0.29|7.40|1.46: Apple iPhone 8 (Space Grey, 2GB RAM, 64GB Storage) 0.19|8.58|1.28: STORETHATSAYS Vivo V9 Compatible Camera Tripod Portable &amp; Foldable Stand with Mobile Clip Holder Com 0.19|8.56|1.28: STORETHATSAYS Google Pixel 2 and 2 XL Compatible Camera Tripod Portable &amp; Foldable Stand with Mobile ```  index.search_and_display('iphone charger') ``` \u0412\u044b\u0432\u043e\u0434: 0.81|15.16|3.50: SKYVIK Beam QI Certified 7.5W &amp; 10W Fast Wireless Charger for iPhone X XS Max XR iPhone 8 &amp; 8 Plus S 0.36|16.79|2.47: Belkin Boost Up Wireless Charging Pad 7.5W - Wireless Charger Optimized for iPhone, Compatible with  0.31|14.71|2.13: Baseus [Certified] Fast Qi Wireless Charger Leather Pad Stand Baseus Wireless Charger For Iphone X 8 0.31|13.87|2.06: Syncwire Lightning Charger Cable Cord for iPhone 5 - iPhoneX Smartphones, iPad Mini, iPad Air, iPad  0.26|14.87|1.97: Baseus B\u00ae Smart 2 in 1 Wireless Charger for Apple IWatch and Qi Enabled Phone Charger for Apple iPho ```<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u043f\u043e\u0438\u0441\u043a \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432 \u043a \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\u043c \u0438 \u0432\u044b\u0434\u0430\u0435\u0442 \u043d\u0435\u0431\u0435\u0441\u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0434\u043e \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0435 \u0435\u043c\u0443 \u0435\u0449\u0451 \u0434\u0430\u043b\u0435\u043a\u043e.<\/p>\n<hr\/>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c BM25+ \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0435\u0433\u043e \u0434\u043b\u044f \u0440\u0435-\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430 \u043f\u043e\u0438\u0441\u043a\u0430. \u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f BM25+:<\/p>\n<ul>\n<li>\n<p>\u041e\u043d \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0440\u043e \u0441\u043c\u044b\u0441\u043b, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0430 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u044f\u0445.<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a \u0441\u043b\u0435\u0434\u0441\u0442\u0432\u0438\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0433\u043e \u043f\u0443\u043d\u043a\u0442\u0430, \u043e\u043d \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442 \u043f\u0440\u043e \u0441\u0438\u043d\u043e\u043d\u0438\u043c\u044b.<\/p>\n<\/li>\n<li>\n<p>\u0411\u0435\u0437 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0439 \u043e\u0442 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0430 \u043f\u043e \u0432\u0441\u0435\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432. \u041d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u043e\u0439 Approximate Nearest Neighbors \u043f\u043e\u0438\u0441\u043a \u043d\u0435 \u0441\u0442\u0440\u0430\u0434\u0430\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439.<\/p>\n<\/li>\n<\/ul>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u0443\u043f\u0440\u0430\u0436\u043d\u044f\u0442\u044c\u0441\u044f, \u0442\u043e \u0432\u043e\u0442 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u0434\u0435\u0439, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c:<\/p>\n<ul>\n<li>\n<p>\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043a \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043d\u0435 \u043f\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u0432\u0441\u0451 \u0446\u0435\u043b\u0438\u043a\u043e\u043c.<\/p>\n<\/li>\n<li>\n<p>\u0423\u0441\u043a\u043e\u0440\u0438\u0442\u044c! \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u044b\u0448\u0435 \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043d\u0430\u0438\u0432\u043d\u0430, \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043e\u0433\u0440\u043e\u043c\u043d\u043e\u0435.<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0443\u043c\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d\u0438\u0437\u0430\u0442\u043e\u0440, \u0447\u0435\u043c \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u043d\u0430 \u0441\u043b\u043e\u0432\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 FastText.<\/p>\n<\/li>\n<li>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u0440\u0438\u043a\u0443 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0430 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f NDCG, \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u044b\u0434\u0430\u0447\u0438. \u0417\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u043e\u0431\u0440\u0430\u0442\u044c \u043d\u0430\u0438\u043b\u0443\u0447\u0448\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b BM25+.<\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f \u044d\u0442\u043e\u0442 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b, \u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u0435\u0449\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u044f \u043f\u0438\u0448\u0443: \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0439\u0442\u0435\u0441\u044c \u043d\u0430 \u043c\u043e\u0439 \u043a\u0430\u043d\u0430\u043b \u0432 <a href=\"https:\/\/t.me\/boris_again\/1652\" rel=\"noopener noreferrer nofollow\">\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u0435<\/a>, \u0433\u0434\u0435 \u044f \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0447\u0430\u0441\u0442\u043e (\u0434\u043b\u044f \u0441\u0432\u043e\u0435\u0439 \u043f\u0441\u0438\u0445\u0438\u043a\u0438) \u0432\u044b\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u043f\u0440\u043e IT, \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0435 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u0438 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/823568\/\"> https:\/\/habr.com\/ru\/articles\/823568\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u043c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0411\u043e\u0440\u0438\u0441. \u042f \u0430\u0432\u0442\u043e\u0440 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043a\u0430\u043d\u0430\u043b\u0430 <a href=\"https:\/\/t.me\/boris_again\" rel=\"noopener noreferrer nofollow\">\u0411\u043e\u0440\u0438\u0441 \u043e\u043f\u044f\u0442\u044c<\/a>. \u041f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043c\u043d\u0435 \u043d\u0430 \u0433\u043b\u0430\u0437\u0430 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u0447\u0442\u043e-\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u0438 \u044f \u0433\u043b\u0443\u0431\u043e\u043a\u043e \u0432 \u044d\u0442\u043e\u043c \u0437\u0430\u043a\u0430\u043f\u044b\u0432\u0430\u044e\u0441\u044c. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u043f\u043e\u0438\u0441\u043a\u0430 BM25+.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043d\u0430\u0447\u0430\u043b\u0430\u0441\u044c \u0441 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u044f \u043d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 \u0433\u0440\u043e\u043c\u043a\u0438\u0439 \u0438 \u0437\u0430\u0431\u0430\u0432\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442: \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c BM25, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u044b\u0439 \u0430\u0436 \u0432 \u0432\u043e\u0441\u044c\u043c\u0438\u0434\u0435\u0441\u044f\u0442\u044b\u0435 \u0433\u043e\u0434\u044b, <a href=\"https:\/\/blog.vespa.ai\/announcing-long-context-colbert-in-vespa\/\" rel=\"noopener noreferrer nofollow\">\u043f\u043e\u0431\u0435\u0434\u0438\u043b<\/a> \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u0435\u043a\u0442\u043e\u0440\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0430 LLM.<\/p>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u0430 \u0437\u0432\u0435\u0440\u044c \u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u043e\u043d \u0442\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. BM25 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u043e \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043d\u043e \u0438 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0435\u0433\u043e \u043d\u0430 Python \u0441 \u043d\u0443\u043b\u044f. \u041c\u044b \u043d\u0430\u0447\u043d\u0435\u043c \u0441 \u0441\u0430\u043c\u043e\u0433\u043e \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430, \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a TF-IDF, \u0430 \u0437\u0430\u0442\u0435\u043c \u0432\u044b\u0432\u0435\u0434\u0435\u043c \u0438\u0437 \u043d\u0435\u0433\u043e BM25+.<\/p>\n<p>\u0421\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0442\u0435\u043c, \u043a\u0442\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043e \u043f\u043e\u0438\u0441\u043a\u0435, \u0430 \u0431\u043e\u043b\u0435\u0435 \u043e\u043f\u044b\u0442\u043d\u044b\u0435 \u0440\u0435\u0431\u044f\u0442\u0430 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u043e\u043b\u0438\u0441\u0442\u0430\u0442\u044c \u0434\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430.<\/p>\n<p>\u041a\u043e\u0434 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 <a href=\"https:\/\/colab.research.google.com\/drive\/1q4mq7HN07A9lwMPy7wZdcxNtAvCRZiui?usp=sharing\" rel=\"noopener noreferrer nofollow\">Google Collab<\/a>.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u041a\u043e\u0433\u0434\u0430 \u0431\u0435\u0439\u0437\u043b\u0430\u0439\u043d <a href=\"https:\/\/blog.vespa.ai\/announcing-long-context-colbert-in-vespa\/\" rel=\"noopener noreferrer nofollow\">\u043e\u0431\u043e\u0448\u0435\u043b<\/a> \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0432\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b<\/figcaption><\/div>\n<\/figure>\n<hr\/>\n<p>\u0414\u0430\u0436\u0435 \u0432 \u044d\u043f\u043e\u0445\u0443 \u043f\u043e\u0438\u0441\u043a\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u044f\u0437\u044b\u043a\u043e\u0432\u044b\u0445 \u043c\u043e\u0434\u0435\u043b\u0435\u0439, BM25 \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u0435\u0440\u0436\u0438\u0442 \u0443\u0434\u0430\u0440 \u0438 \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0441\u0438\u043b\u044c\u043d\u044b\u043c \u0431\u0435\u0439\u0437\u043b\u0430\u043d\u043e\u043c. \u0422\u0430\u043a \u0436\u0435 \u043e\u043d \u043f\u043e\u043b\u0435\u0437\u0435\u043d \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435: \u043e\u043d \u0441\u043f\u043e\u0441\u043e\u0431\u0435\u043d \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0412\u044b\u0434\u0430\u0447\u0443 BM25 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u043e\u0434\u0438\u043d \u0438\u0437 \u044d\u0442\u0430\u043f\u043e\u0432 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0432 \u0442\u043e\u043c \u0447\u0438\u0441\u043b\u0435 \u043e\u0442\u0434\u0430\u0432\u0430\u044f \u0435\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044f\u043c \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0434\u0430\u043d\u043d\u044b\u0435. \u041c\u043d\u0435 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f \u044d\u0442\u043e\u0442 <a href=\"https:\/\/www.kaggle.com\/datasets\/saurabhshahane\/ecommerce-text-classification\" rel=\"noopener noreferrer nofollow\">\u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/a> \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f\u043c\u0438 ecommerce \u0442\u043e\u0432\u0430\u0440\u043e\u0432. \u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u043c\u0430\u0433\u0430\u0437\u0438\u043d\u0430. \u0411\u0443\u0434\u0435\u043c \u0434\u0432\u0438\u0433\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0441\u0430\u043c\u044b\u0445 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432, \u043f\u043e\u0441\u0442\u0435\u043f\u0435\u043d\u043d\u043e \u0443\u043b\u0443\u0447\u0448\u0430\u044f \u0438\u0445, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u0440\u0438\u0434\u0435\u043c \u043a BM25+.<\/p>\n<pre><code class=\"python\">import numpy as np import pandas as pd from collections import Counter from tqdm.auto import tqdm  # https:\/\/www.kaggle.com\/datasets\/saurabhshahane\/ecommerce-text-classification data_url = 'https:\/\/raw.githubusercontent.com\/sugatagh\/E-commerce-Text-Classification\/main\/Dataset\/ecommerceDataset.csv' dataset = pd.read_csv(     data_url,     names = ['label', 'description'] ) dataset = dataset.dropna() dataset = dataset.drop_duplicates()<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0435 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u0430 \u043a\u043e\u043b\u043e\u043d\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u043e\u0432\u0430\u0440\u0430: <code>description<\/code>. \u0412\u0441\u0435\u0433\u043e \u0443 \u043d\u0430\u0441 \u0442\u0430\u043a\u0438\u0445 \u0442\u043e\u0432\u0430\u0440\u043e\u0432 27802.<\/p>\n<h2>\u0413\u043b\u0443\u043f\u043d\u044b\u0439, \u043d\u0430\u0438\u0432\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a<\/h2>\n<p>\u0421\u0430\u043c\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c: \u043f\u043e\u043b\u0443\u0447\u0438\u0432 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430\u0439\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442.<\/p>\n<pre><code class=\"python\">documents = dataset.description.tolist() def search_dummy(documents, query, limit=10):     return [doc for doc in documents if query in doc][:limit]  [doc[:100] for doc in search_dummy(documents, 'smartphone', limit=2)] # \u0412\u044b\u0432\u043e\u0434: # ['Shinco 80 cm (32 Inches) HD Ready Smart LED TV SO32AS (Black) (2019 model) Size name:32 Inches   Thi', #  'Amazon Brand - Solimo 12-inch Wall Clock - Checkered (Silent Movement, Black Frame) Bring function a']<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u043f\u043e\u043b\u043e\u043d \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0438 \u0441\u0430\u043c\u0430\u044f \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u0430\u044f \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0435 \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e: <code>search_dummy(documents, 'SmartphonE', limit=2)<\/code> .<\/p>\n<p>\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0442\u0435\u043a\u0441\u0442\u043e\u0432.  \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u043e\u0441\u043e\u0431\u043e \u043c\u0443\u0434\u0440\u0438\u0442\u044c, \u0443\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b \u043a\u0440\u043e\u043c\u0435 \u0431\u0443\u043a\u0432 \u0438 \u0447\u0438\u0441\u0435\u043b, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0435 \u0441\u0443\u0444\u0444\u0438\u043a\u0441\u044b.<\/p>\n<p>\u0423\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0444\u0444\u0438\u043a\u0441\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0437\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0441\u0442\u0435\u043c\u043c\u0438\u043d\u0433\u043e\u043c: \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435\u043c \u043a\u043e\u0440\u043d\u0435\u0439. \u041d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u0442\u0435\u043c\u043c\u0435\u0440 \u041f\u043e\u0440\u0442\u0435\u0440\u0430 \u0438\u0437 \u043f\u0430\u043a\u0435\u0442\u0430 <code>nltk<\/code>. \u041d\u043e \u0443 \u043d\u0430\u0441 \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u043a\u0443\u0440\u0441 \u043f\u043e NLP.<\/p>\n<pre><code class=\"python\">import string  def stem(word):     for suffix in set(['ing', 'ly', 'ed', 'ious', 'ies', 'ive', 'es', 's', 'ment']):         if word.endswith(suffix):             return word[:-len(suffix)]     return word   def preprocess_document(document):     new_doc = ''.join(         c for c in document if c.isalnum() or c == ' '     ).lower().strip()     new_doc = ' '.join([         stem(word) for word in new_doc.split(' ')     ])     return new_doc  def preprocess_documents(documents):     new_documents = []     for doc in documents:         new_doc = preprocess_document(doc)         new_documents.append(new_doc)     return new_documents  documents_preprocessed = preprocess_documents(documents) documents_preprocessed[:1][0][:50]  # \u0412\u044b\u0432\u043e\u0434: # 'paper plane design fram wall hang motivational off'  def search_dummy(documents, query, limit=10):     query = preprocess_document(query)     return [doc for doc in documents if query in doc][:limit]  search_dummy(documents_preprocessed, 'SmartphonE', limit=1)[0][:50] # \u0412\u044b\u0432\u043e\u0434: # 'shinco 80 cm 32 inches hd ready smart led tv so32a'<\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0440\u0435\u0448\u0435\u043d\u0430. \u041d\u043e \u043b\u044e\u0431\u0430\u044f \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0441\u0440\u0430\u0437\u0443 \u0432\u0441\u0451 \u043b\u043e\u043c\u0430\u0435\u0442, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>search_dummy(documents_preprocessed, 'smrtaphone', limit=1)<\/code>  \u043d\u0435 \u0432\u0435\u0440\u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e. \u0422\u0430\u043a \u0436\u0435 \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432\u0440\u043e\u0434\u0435 <code>smar<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0447\u0430\u0441\u0442\u043e \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u0432 ecommerce.<\/p>\n<h2>Term Frequency \u043d\u0430 N-\u0433\u0440\u0430\u043c\u043c\u0430\u0445<\/h2>\n<p>\u0414\u043b\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u0438\u0445 \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0433\u0443\u0442 N-\u0433\u0440\u0430\u043c\u043c\u044b: \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u0432 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 \u0434\u043b\u0438\u043d\u043e\u0439 N.<\/p>\n<pre><code class=\"python\">from nltk.util import ngrams import nltk  N_GRAM_SIZE = 3   def documents_to_ngrams(documents, n_gram_size=N_GRAM_SIZE, progress=False):     document_ngrams = []     iterator = documents     if progress:         iterator = tqdm(documents) # progress bar, \u0442.\u043a. \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0435\u0431\u044b\u0441\u0442\u0440\u044b\u0439     for doc in iterator:         doc_ngrams = []         for word in doc.split(' '):             word_ngrams = ngrams(word, n_gram_size)             for ngram in word_ngrams:                 doc_ngrams.append(''.join(ngram))         document_ngrams.append(tuple(doc_ngrams))     document_ngrams = tuple(document_ngrams)      return document_ngrams  documents_ngrams = documents_to_ngrams(documents_preprocessed, progress=True) documents_ngrams[0][:5] # \u0412\u044b\u0432\u043e\u0434: # ('pap', 'ape', 'per', 'pla', 'lan')<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043c \u0443\u0436\u0435 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u0432\u0441\u0451, \u0447\u0442\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u043d\u0443 N-\u0433\u0440\u0430\u043c\u043c\u0443: \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043c\u043d\u043e\u0433\u043e \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439. \u041b\u043e\u0433\u0438\u0447\u043d\u043e \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u043c\u0438 \u0442\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b, \u0433\u0434\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u0441\u0435\u0433\u043e.<\/p>\n<p>\u0417\u0434\u0435\u0441\u044c \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u0438\u0435 <strong>\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong>. \u041d\u0443\u0436\u043d\u043e \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0430\u0439\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b, \u043d\u043e \u0438 \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0431\u044b\u043b\u0438 \u0441\u0432\u0435\u0440\u0445\u0443. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u0430 \u043d\u0435\u043a\u0430\u044f <strong>\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f<\/strong>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0430\u0440\u0435 (\u0437\u0430\u043f\u0440\u043e\u0441, \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442) \u0447\u0438\u0441\u043b\u043e, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435, \u0447\u0435\u043c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u0435\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u0448\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f: \u0447\u0430\u0441\u0442\u043e\u0442\u0430 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439, \u0438\u043b\u0438 <strong>term frequency.<\/strong><\/p>\n<pre><code class=\"python\">def display_search_results(documents, idx_scores, char_limit=100):     for idx, score in idx_scores:         print(f'{score:0.2f}: {documents[idx][:char_limit]}')  def query_to_ngrams(query, n_gram_size=N_GRAM_SIZE):     return documents_to_ngrams([query], n_gram_size=n_gram_size)[0]  def search_tf(documents_ngrams, query, limit=5, n_gram_size=N_GRAM_SIZE):     index = [Counter(doc_ngrams) for doc_ngrams in documents_ngrams]     query = query_to_ngrams(query, n_gram_size)      match_scores = []     for ngram_counts in tqdm(index):         score = 0         for query_ngram in query:             score += ngram_counts.get(query_ngram, 0)         match_scores.append(score)      idx_scores = zip(range(len(documents_ngrams)), match_scores)     idx_scores = sorted(idx_scores, key=lambda pair: -pair[1])      return idx_scores[:limit]  idx_scores = search_tf(documents_ngrams, 'smratphone') display_search_results(documents, idx_scores)  ``` \u0412\u044b\u0432\u043e\u0434: 116.00: Risk Savvy: How to Make Good Decisions About the Author GERD GIGERENZER is director of the Max Planc 116.00: Risk Savvy: How to Make Good Decisions About the Author Gerd Gigerenzer is the author of Gut Feeling 105.00: HP B4B09PA Headphones with Mic Product Description HP Headphones Overview With HP B4B09PA Over ear H 98.00: iBall Rocky Over-Ear Headphones with Mic Product Description  iBall Rocky Headset Over-Ear Headphone 96.00: The Global War on Christians: Dispatches from the Front Lines of Anti-Christian Persecution About th ```<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0448 \u043f\u043e\u0438\u0441\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 \u0441 \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0430\u043c\u0438, \u043d\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0443\u0436\u0430\u0441\u0435\u043d. \u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442.<\/p>\n<p><code>search_tf<\/code> \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u0438\u0441\u043a: \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 N-\u0433\u0440\u0430\u043c\u043c\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u043e\u0440\u0442\u0435\u0436\u0435\u0439 <code>(\u0438\u043d\u0434\u0435\u043a\u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430, \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c)<\/code>, \u0433\u0434\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u043e \u0447\u0438\u0441\u043b\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 N-\u0433\u0440\u0430\u043c\u043c. \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0441\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e <code>limit<\/code> \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>\u0412\u043d\u0443\u0442\u0440\u0438 \u044d\u0442\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u0443\u0435\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b: \u0441\u0447\u0438\u0442\u0430\u0435\u0442 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437\u043d\u044b\u0445 N-\u0433\u0440\u0430\u043c\u043c \u0432 \u043d\u0451\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442\u0441\u044f. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435 <code>\"smasmart\"<\/code> \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 N-\u0433\u0440\u0430\u043c\u043c\u044b: <code>{\"sma\": 2, \"mas\": 1, \"asm\": 1, \"mar\": 1, \"art\": 1}<\/code>. \u0422\u0430\u043a\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u044b\u0441\u0442\u0440\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u0441\u043a\u043e\u043b\u044c\u043a\u043e N-\u0433\u0440\u0430\u043c\u043c \u0438\u0437 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435. \u0421\u0442\u0440\u043e\u0433\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0440\u0430\u0437, \u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435.<\/p>\n<p>\u041f\u043e\u0447\u0435\u043c\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0442\u0430\u043a\u043e\u0439 \u043f\u043b\u043e\u0445\u043e\u0439? \u0414\u043b\u0438\u043d\u043d\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0445 \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0439 \u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0448\u0435 \u0432 \u0432\u044b\u0434\u0430\u0447\u0435 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430.<\/p>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0438\u0433\u0440\u0443\u0448\u0435\u0447\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"python\">def documents_to_index(documents, n_gram_size=N_GRAM_SIZE): \"\"\"\u0412\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0447\u0442\u043e\u0431\u044b \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438 \u0440\u0430\u0437\u0431\u0438\u0435\u043d\u0438\u0435 \u043d\u0430 N-\u0433\u0440\u0430\u043c\u043c\u044b\"\"\"     documents_preprocessed = [         preprocess_document(doc) for doc in documents     ]      documents_ngrams = documents_to_ngrams(documents_preprocessed, n_gram_size)     return documents_ngrams  dummy_documents = [     'smartphone',     'frying pan', ] dummy_documents_ngrams = documents_to_index(dummy_documents) idx_scores = search_tf(dummy_documents_ngrams, 'smratphone') display_search_results(dummy_documents, idx_scores) ``` \u0412\u044b\u0432\u043e\u0434: 4.00: smartphone 0.00: frying pan ```  dummy_documents = [     'smartphone',     'frying pan',     'headphones for your smartphone', ] dummy_documents_ngrams = documents_to_index(dummy_documents) idx_scores = search_tf(dummy_documents_ngrams, 'smratphone') display_search_results(dummy_documents, idx_scores)  ``` \u0412\u044b\u0432\u043e\u0434: 7.00: headphones for your smartphone 4.00: smartphone 0.00: frying pan ```<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443, \u043d\u0430\u043c \u0431\u044b \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b N-\u0433\u0440\u0430\u043c\u043c\u0430 <code>sma<\/code> \u0432\u043d\u043e\u0441\u0438\u043b\u0430 \u0431\u043e\u043b\u044c\u0448\u0438\u0439 \u0432\u043a\u043b\u0430\u0434 \u0432 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0435\u0433\u043e \u0434\u043b\u0438\u043d\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0432 \u0441\u043b\u043e\u0432\u0435 <code>smart<\/code> \u044d\u0442\u043e \u043e\u0434\u043d\u0430 \u0438\u0437 \u0442\u0440\u0435\u0445 N-\u0433\u0440\u0430\u043c\u043c, \u0430 \u0432 \u0441\u043b\u043e\u0432\u0435 <code>smartphone<\/code> \u043e\u043d\u0430 \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u043d\u044c\u0448\u0438\u0439 \u0432\u0435\u0441.<\/p>\n<pre><code class=\"python\">def search_tf_weighted(documents_ngrams, query, limit=5, n_gram_size=N_GRAM_SIZE):     index = [Counter(doc_ngrams) for doc_ngrams in documents_ngrams]     query = query_to_ngrams(query, n_gram_size)     match_scores = []     for ngram_counts in index:         score = 0         total_ngrams = sum(ngram_counts.values())         if total_ngrams == 0:             continue         for query_ngram in query:             score +=<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-379394","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/379394","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=379394"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/379394\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=379394"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=379394"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=379394"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}