国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Elasticsearch:語義搜索 - Semantic Search in python

這篇具有很好參考價值的文章主要介紹了Elasticsearch:語義搜索 - Semantic Search in python。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

當(dāng) OpenAI 于 2022 年 11 月發(fā)布 ChatGPT 時,引發(fā)了人們對人工智能和機(jī)器學(xué)習(xí)的新一波興趣。 盡管必要的技術(shù)創(chuàng)新已經(jīng)出現(xiàn)了近十年,而且基本原理的歷史甚至更早,但這種巨大的轉(zhuǎn)變引發(fā)了各種發(fā)展的“寒武紀(jì)大爆炸”,特別是在大型語言模型和生成 transfors 領(lǐng)域。 一些懷疑論者認(rèn)為,這些模型是 “隨機(jī)鸚鵡”,只能生成他們所接受訓(xùn)練的內(nèi)容的排列。 有些人認(rèn)為這些模型是 “黑匣子”,超出了人類理解范圍,甚至可能是“黑魔法”,其工作原理完全深奧。

我對在語義搜索背景下使用機(jī)器學(xué)習(xí)模型的可能性感到特別興奮。 Elasticsearch 是一家基于 Apache Lucene 的高級搜索和分析引擎。 充分了解倒排索引、評分算法、語言分析的特殊性等所有復(fù)雜性,我偶然發(fā)現(xiàn)的一些例子看起來幾乎就像……是的,“黑魔法”。

在我們深入研究 Python 代碼之前,我想回顧一下歷史。 正如我發(fā)現(xiàn)的,機(jī)器學(xué)習(xí)或人工智能主題的困難之一是大量高度具體的術(shù)語,并且缺乏關(guān)于技術(shù)如何工作的直觀心理模型。 例如,如果我通過說它們是 “密集向量(dense vectors)” 來解釋上一段中的術(shù)語 “嵌入(embeddings)”,那就無濟(jì)于事了 —— 不僅你的眼睛會變得呆滯,而且我還必須解釋兩個術(shù)語,而不是解釋其中的一個。

詞匯和語義搜索(lexical and semantic search)

事實上,用數(shù)字表示語言元素是傳統(tǒng)全文檢索的基礎(chǔ)。 現(xiàn)代倒排索引與傳統(tǒng)索引(或書后索引)之間的主要區(qū)別在于,倒排索引存儲的信息不僅僅是術(shù)語的出現(xiàn)。 它還跟蹤它們在文檔中的位置和出現(xiàn)的頻率。 這已經(jīng)允許某些算術(shù)運(yùn)算,例如短語搜索(phrase search,搜索以特定順序出現(xiàn)的術(shù)語)和鄰近搜索(查找出現(xiàn)在彼此一定數(shù)量的位置內(nèi)的術(shù)語)。

使用這些數(shù)字,特別是文檔中術(shù)語出現(xiàn)的頻率,以及整個文檔集合中術(shù)語的總體頻率,是對搜索結(jié)果進(jìn)行評分的傳統(tǒng)方法 TF-IDF(術(shù)語頻率 vs 逆文檔頻率)公式和更復(fù)雜的公式,如 BM-25。 簡而言之,某個術(shù)語在特定文檔中出現(xiàn)的頻率越高,該文檔在相關(guān)文檔列表中的排名就越高。 相反,特定術(shù)語在整個集合中出現(xiàn)的頻率越高,該文檔在列表中的排名就越少。 將有關(guān)術(shù)語的統(tǒng)計信息存儲在集合中可以實現(xiàn)比簡單查找(例如 “此特定文檔包含此特定單詞”)更復(fù)雜的操作。

傳統(tǒng)的 “詞匯(lexical)” 搜索和 “語義(semantic)” 搜索之間的根本區(qū)別在于,詞匯搜索只能找到包含查詢中存在的確切術(shù)語的文檔。 我們所說的 “術(shù)語” 是指搜索引擎識別為具有相同含義的單詞的變體。 當(dāng)然,像 Elasticsearch 這樣的現(xiàn)代搜索引擎擁有復(fù)雜的工具,可以將 “words” 轉(zhuǎn)換為 “terms”,從簡單的工具(如刪除大寫)到更高級的工具,如詞干提?。▌h除后綴、walking ? walk)、詞形還原(將不同的屈折形式減少為基本的,worst ? bad),或同義詞。 這些有助于擴(kuò)大查詢范圍(并找到更多相關(guān)文檔)。

然而,即使進(jìn)行了這些轉(zhuǎn)換,如果文檔中缺少這些特定術(shù)語,你也無法使用 “a domestic animal which catches mice” 之類的查詢來搜索 “cat”。 另一方面,大型語言模型非常有能力為這樣的 “間接” 查詢檢索文檔。 這并不是因為它以天真的擬人化的方式 “理解” 了那個特定的短語。 這是因為它理解與不同想法相對應(yīng)的不同符號系統(tǒng):人類語言。 在這個系統(tǒng)中,占據(jù)最接近符號 “a?domestic animal which catches mice” 的位置的概念,是的,是貓的概念。

因此,在語義搜索中,搜索結(jié)果的相關(guān)性是由系統(tǒng)內(nèi)的語義接近度決定的,而不僅僅是關(guān)鍵字匹配,無論多么復(fù)雜。 顧名思義,“詞匯搜索” 的行為非常類似于在字典(詞典)中搜索單詞定義:如果你知道要搜索的單詞,那么它會非常有效。 否則,你不妨讀整本字典。

使用 Elasticsearch 進(jìn)行語義搜索

有趣的是,語義搜索的支持基礎(chǔ)設(shè)施多年來一直是 Elasticsearch 的一部分 —— dense_vector 映射字段在 2019 年 4 月發(fā)布的 7.0 版本中引入。幾個月后發(fā)布的 7.3 版本增加了對指定維度的支持 type 并將預(yù)定義函數(shù)引入到 script_score 查詢中,從而能夠計算文檔的相似度分?jǐn)?shù)。 2022 年 2 月發(fā)布的 8.0 版本進(jìn)一步改進(jìn)了dense_vector 實現(xiàn),并添加了 “Approximate Nearest Neighbor - 近似最近鄰” 搜索端點,有效地將關(guān)鍵組件捆綁在一起以全面實現(xiàn)語義搜索,包括在集群內(nèi)運(yùn)行第三方模型的能力。 在最新版本 8.8 中,Elastic 不僅專注于改進(jìn)其 AI 功能的通信以響應(yīng)當(dāng)前的興趣浪潮,而且還添加了一些增強(qiáng)功能,例如在密集向量字段中增加更高的維度,從而允許存儲大型嵌入像 OpenAI 開發(fā)的語言模型一樣,并提供了一個自定義的內(nèi)置模型,即 Elastic Learned Sparse Encoder。

在本博客的其余部分中,我想演示如何使用 Sentence Transformers中的模型,使用 James Briggs 文章中的查詢。 希望你會看到 Elasticsearch 是一個非常強(qiáng)大的向量數(shù)據(jù)庫,具有用于執(zhí)行相似性搜索的高效且方便的 API。

但首先,我想談?wù)?“向量” 這個術(shù)語。 (你可能已經(jīng)注意到,我在開頭段落中使用了三次 “dense_vector” 一詞。)如果你像我一樣沒有數(shù)學(xué)背景,那么向量這個詞和概念一開始可能會讓人感到害怕。 當(dāng)通常的解釋是向量是 “具有大小和方向的對象” 時,這并沒有幫助,因為很難在人類語言的背景下為這樣的對象提出合理的心理模型。 一個更有用的模型可能是將 “向量空間” 中的向量視為坐標(biāo)。

由于語義是由符號在共享符號系統(tǒng)中的 “位置” 給出的,所以我們可以給出這個位置的 “坐標(biāo)”。 此外,我們可以使用這些坐標(biāo)的數(shù)字表示,從而開啟算術(shù)運(yùn)算的可能性。 這種數(shù)字表示通常稱為嵌入。 如果我們拋開數(shù)學(xué)理論,物理表示就是一個十進(jìn)制數(shù)列表:[0.01, 0.05, -0.04, 0.06, -0.1, ...]。 列表的長度稱為維度,每個維度代表含義的特定特征。

讓我們使用由達(dá)姆施塔特技術(shù)大學(xué) Ubiquitous Knowledge Processing Lab?提供的 Sentence Transformers 框架中的免費(fèi)、開源、預(yù)訓(xùn)練模型來仔細(xì)研究其機(jī)制。

文本嵌入 - Text embeddings

為了更好地理解嵌入是語義搜索(以及其他自然語言處理任務(wù))的基礎(chǔ),讓我們從 Hugging Face 加載模型并使用它來生成幾個單詞的嵌入。 但首先,讓我們安裝必要的庫并設(shè)置我們的環(huán)境。

%pip -q install \
  python-dotenv ipywidgets tqdm humanize \
  pandas numpy matplotlib altair \
  datasets sentence-transformers \
  elasticsearch

%load_ext dotenv
%dotenv

from tqdm.notebook import tqdm as notebook_tqdm

讓我們下載并初始化全 MiniLM-L6-v2 模型。

from sentence_transformers import SentenceTransformer

MODEL_ID="all-MiniLM-L6-v2"

model = SentenceTransformer(MODEL_ID)
print("Model dimensions:", model.get_sentence_embedding_dimension())

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

正如我們所看到的,該模型有 384 個維度。 這是模型向量空間的 “大小”。 它并不是特別大 —— 許多當(dāng)前模型的嵌入有數(shù)千個維度,但對于我們的目的來說已經(jīng)足夠了。 讓我們編碼,即。 為單詞 “cat” 創(chuàng)建嵌入:

embeddings_for_cat = model.encode("cat")
print(list(embeddings_for_cat)[:5] + ["..."])

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python請注意,輸出被截斷為前 5 個值,以免一長串?dāng)?shù)字淹沒顯示。 (另請注意,將此模型用于單個單詞只是說明性的,因為它針對句子進(jìn)行了優(yōu)化。對于單詞嵌入,使用 Word2Vec 或 GloVe 等模型更為典型。)

讓我們編碼一個不同的單詞 “dog”:

embeddings_for_dog = model.encode("dog")
print(list(embeddings_for_dog)[:5] + ["..."])

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

輸出說明了為此類文本編碼提出合理的心理模型是多么具有挑戰(zhàn)性:作為人類,我們很好地掌握了符號 “cat” 或 dog” 與其代表的家畜之間的關(guān)系。很難很好地理解這樣的數(shù)字表示。

然而,如前所述,數(shù)字表示具有明顯的優(yōu)勢 —— 我們可以對值進(jìn)行數(shù)學(xué)運(yùn)算。 在這種情況下,我們可以嘗試在散點圖中將它們可視化。 讓我們將列表包裝在 Pandas dataframe 中,這樣我們就可以在 Jupyter 筆記本中顯示時利用其豐富的格式,并在后續(xù)步驟中利用其數(shù)據(jù)操作功能。

import pandas as pd

df = pd.DataFrame(embeddings_for_cat, columns=["embedding"])
df

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

?我們可以使用內(nèi)置的繪圖功能來顯示簡單的圖表:

df.reset_index().plot.scatter(x="index", y="embedding");

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

該圖表只為我們提供了非常抽象的數(shù)據(jù) “圖片”; 基本上,值的粗略分布(在 -0.15 到 0.23 的范圍內(nèi))。

就其本身而言,這些數(shù)字毫無意義。 當(dāng)我們將語言理論視為 “不同符號的系統(tǒng)” 時,這實際上是預(yù)料之中的。 任何一個詞單獨(dú)存在都沒有意義; 它的意義來自于與系統(tǒng)中其他詞的關(guān)系。 那么,如果我們嘗試想象 “cat” 和 “dog” 這兩個詞呢?

讓我們創(chuàng)建一個新的 dataframe,使用 “cat” 和 “dog” 作為索引并將嵌入壓縮到單個列。

df = pd.DataFrame(
    [
      [embeddings_for_cat],
      [embeddings_for_dog],
    ],
    index=["cat", "dog"], columns=["embeddings"]
)
df

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

為了繪制數(shù)據(jù),我們需要進(jìn)行一些轉(zhuǎn)換:

# Add a new column to store the original index values (0-383) for each embedding
df["position"] = [list(range(len(df.embeddings[i]))) for i in df.index]

# Convert the `embeddings` and `position` columns from "wide" to "long" format
df_exploded = df.explode(["embeddings", "position"])

# Convert the index into a regular column
df_exploded = df_exploded.reset_index()

# Rename columns for more clarity
df_exploded = df_exploded.rename(columns={"index": "animal", "embeddings": "embedding"})

# Add a new column with numerical values mapped from the `animal` column values
df_exploded["color"] = df_exploded["animal"].map({"cat": 1, "dog": 2})

df_exploded

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

現(xiàn)在我們可以繪制轉(zhuǎn)換后的數(shù)據(jù):

(df_exploded
  .plot
  .scatter(x="position", y="embedding", c="color", colormap="tab10")
  .collections[0].colorbar.remove())

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

像這樣的簡單可視化似乎不會有太大幫助。 然而,它強(qiáng)調(diào)了多維向量空間的一個基本困難。 作為人類,我們非常有能力在 2D 或 3D 空間中可視化物體。 更多維度根本不是我們能夠有效想象的東西,更不用說 “繪制” 了。

我們可以使用的一個技巧是減少維度,在本例中從 384 維減少到 2 維。(再次強(qiáng)調(diào):我們能夠做到這一點是因為我們正在處理語言的數(shù)字表示。)有很多算法可以用于 這樣做 - 我們將使用主成分分析 (principal component analysis - PCA),因為它在 scikit-learn 包中很容易獲得,并且適用于小型數(shù)據(jù)集。 (有關(guān)使用 t-SNE 和 UMAP 算法的示例,請參閱 Plotly 包文檔中的一篇優(yōu)秀文章。)

import numpy as np
from sklearn.decomposition import PCA

# Drop the `position` column as it's no longer needed
df.drop(columns=["position"], inplace=True, errors="ignore")

# Convert embeddings to a 2D array and display their shape
print("Embeddings shape:", np.stack(df["embeddings"]).shape)

# Initialize the PCA reducer to convert embeddings into arrays of length of 2
reducer = PCA(n_components=2)

# Reduce the embeddings, store them in a new dataframe column and display their shape
df["reduced"] = reducer.fit_transform(np.stack(df["embeddings"])).tolist()
print("Reduced embeddings shape:", np.stack(df["reduced"]).shape)

df

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

正如我們所看到的,減少的嵌入只有兩個維度,因此我們可以使用 Vega-Altair 包將它們繪制在笛卡爾平面上作為 x 和 y 坐標(biāo)。 讓我們創(chuàng)建一個函數(shù),以便稍后重用代碼。

import altair as alt

def scatterplot(
    data: pd.DataFrame,
    tooltips=False,
    labels=False,
    width=800,
    height=200,
) -> alt.Chart:
    base_chart = (
        alt.Chart(data)
        .encode(
            alt.X("x", scale=alt.Scale(zero=False)),
            alt.Y("y", scale=alt.Scale(zero=False)),
        )
        .properties(width=width, height=height)
    )

    if tooltips:
        base_chart = base_chart.encode(alt.Tooltip(["text"]))

    circles = base_chart.mark_circle(
        size=200, color="crimson", stroke="white", strokeWidth=1
    )

    if labels:
        labels = base_chart.mark_text(
            fontSize=13,
            align="left",
            baseline="bottom",
            dx=5,
        ).encode(text="text")
        chart = circles + labels
    else:
        chart = circles

    return chart
source = pd.DataFrame(
    {
        "text": df.index,
        "x": df["reduced"].apply(lambda x: x[0]).to_list(),
        "y": df["reduced"].apply(lambda x: x[1]).to_list(),
    }
)

scatterplot(source, labels=True)

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

好的。 該圖表相當(dāng)平庸 —— 只有兩個圓圈,隨機(jī)放置在畫布上。 我們可能期望這些標(biāo)記會彼此靠近地顯示;但事實并非如此。 畢竟,貓和狗有很多共同的特征。 然而,在語言作為一個系統(tǒng)的前提下,我們有限的 “系統(tǒng)” 只包含兩個詞:“cat” 和 “dog”。

作為人類,我們可能會認(rèn)為這些標(biāo)志密切相關(guān):它們都代表有四足的毛茸茸的動物,通常作為寵物飼養(yǎng),都是哺乳動物屬的食肉動物,等等。 但這種直覺來自于我們語言的一個非常大的系統(tǒng),其中有許多其他概念占據(jù)著不同的位置。 引用索緒爾的話,“這些概念純粹是差異性的,不是由它們的積極內(nèi)容來定義,而是由它們與系統(tǒng)其他術(shù)語的關(guān)系來消極定義”。

然后,讓我們嘗試向集合中添加更多單詞,看看圖片是否會以有意義的方式發(fā)生變化。

words = ["cat", "dog", "table", "chair", "pizza", "pasta", "asymptomatic"]

# Create a new dataframe
df = pd.DataFrame(
    [[model.encode(word)] for word in words],
    columns=["embeddings"],
    index=words,
)

# Perform dimensionality reduction
df["reduced"] = reducer.fit_transform(np.stack(df["embeddings"])).tolist()
df

?Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

讓我們再次顯示散點圖。

source = pd.DataFrame(
    {
        "text": df.index,
        "x": df["reduced"].apply(lambda x: x[0]).to_list(),
        "y": df["reduced"].apply(lambda x: x[1]).to_list(),
    }
)

scatterplot(source, labels=True)

?Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

好多了! 我們可以清楚地看到相關(guān)詞的三個 “集群”,dog ? cat,pizza ? pasta,chair ? table。 我們還可以看到,除了這三個集群之外,“asymptomatic”一詞是單獨(dú)存在的。

這是人工智能的 “黑魔法” 嗎? 并不真的是。 全 MiniLM-L6-v2 模型已經(jīng)在 Reddit、Stack Exchange、維基百科、Quora 和其他來源的大量人類編寫的文本上進(jìn)行了訓(xùn)練。 因此,它確實具有這些詞的含義,幾乎字面上 “嵌入” 在它生成的 384 維向量中。

裝載數(shù)據(jù)集

通過更好、更實際地理解文本嵌入的工作原理和原理,我們可以回到本博客的最初動機(jī):使用 Elasticsearch 而不是 Pinecone,重新創(chuàng)建 James Briggs 文章中的語義搜索示例。

我們將使用 Hugging Face 的 datasets 包來加載 Quora 數(shù)據(jù)。 它是一個非常復(fù)雜的數(shù)據(jù) “包裝器”,它提供了方便的功能,例如下載文件的內(nèi)置緩存和高效的處理功能,我們將使用這些功能來操作數(shù)據(jù)。

Hugging Face 數(shù)據(jù)集主要面向為模型訓(xùn)練提供數(shù)據(jù),因此它們被分為 train、test、validation 等 split。 我們的特定數(shù)據(jù)集只有 train split 部分。 讓我們加載它并顯示有關(guān)數(shù)據(jù)集的一些元數(shù)據(jù)。

import humanize
import datasets

dataset = datasets.load_dataset("quora", split="train")

print("Description:", dataset.info.description, "\n")
print("Homepage:", dataset.info.homepage)
print("Downloaded size:", humanize.naturalsize(dataset.info.download_size))
print("Number of examples:", humanize.intcomma(dataset.info.splits["train"].num_examples))
print("Features:", dataset.info.features)

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

正如我們所看到的,該數(shù)據(jù)集包含超過 400,000 個 “question pairs”。 我們來看看前五條記錄。?

dataset[:5]

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

?該數(shù)據(jù)集的主要重點是為重復(fù)檢測提供可靠的數(shù)據(jù):

我們的第一個數(shù)據(jù)集與識別重復(fù)問題的問題有關(guān)。

Quora 的一個重要產(chǎn)品原則是,每個邏輯上不同的問題都應(yīng)該有一個問題頁面。 舉一個簡單的例子,查詢“美國人口最多的州是哪個?” 和“美國哪個州人口最多?” 不應(yīng)該單獨(dú)存在于 Quora 上,因為兩者背后的意圖是相同的。 (...)

我們今天發(fā)布的數(shù)據(jù)集將使任何人都有機(jī)會根據(jù)實際的 Quora 數(shù)據(jù)來訓(xùn)練和測試語義等價模型。 (...)

— Kornél Csernai,第一個 Quora 數(shù)據(jù)集發(fā)布:Question Pairs

因此,數(shù)據(jù)集包含問題對,這些問題對被標(biāo)記為重復(fù)或不重復(fù)。 讓我們使用數(shù)據(jù)集包的實用程序來選擇和過濾數(shù)據(jù),顯示一些重復(fù)問題的示例。

(dataset
  .select(range(1000))
  .filter(lambda record: record["is_duplicate"])[:3])

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

有點矛盾的是,數(shù)據(jù)集不包含 “美國人口最多的州是哪個?” 的問題。 文章中提到。

dataset.filter(lambda record: "What is the most populous state in the USA?" in record["questions"]["text"])[:]

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

?讓我們從清理和轉(zhuǎn)換數(shù)據(jù)集開始,這樣我們就可以將各個問題作為單獨(dú)的文檔加載到 Elasticsearch 中。

首先,我們將刪除 is_duplicate 列并 “展平” questions 屬性,即。 將其展開為單獨(dú)的列。

print("Original dataset:", dataset, "\n")

# Remove the `is_duplicate` column
dataset = dataset.remove_columns("is_duplicate")

# Flatten the dataset
dataset = dataset.flatten()

print("Transformed dataset:", dataset, "\n")

dataset[:5]

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

我們對結(jié)構(gòu)進(jìn)行了一些改進(jìn),但問題文本字段中仍然有兩個問題。 為了有效地索引問題,最好將每個問題存儲為單獨(dú)的行。 我們將使用包提供的強(qiáng)大的 map() 功能,擴(kuò)展 questions.id 和 questions.text 列。?

# Expand the values from the lists into separate lists
def expand_values(batch):
    ids = []
    texts = []

    for id_list, text_list in zip(batch["questions.id"], batch["questions.text"]):
        ids.extend(id_list)
        texts.extend(text_list)

    return {"id": ids, "text": texts}

# Run the "expand_values" function for batches of rows in the dataset
dataset = dataset.map(
    expand_values,
    batched=True,
    remove_columns=dataset.column_names,
    desc="Expand Questions",
)

print("Transformed dataset:", dataset, "\n")

dataset[:5]

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

數(shù)據(jù)集包含兩倍的行數(shù),因為每個問題現(xiàn)在都存儲為單獨(dú)的行。

下一步是刪除重復(fù)的問題。 我們沒有使用 is_duplicate 列進(jìn)行重復(fù)數(shù)據(jù)刪除,因為我們?nèi)匀幌M麑λ袉栴}建立索引,即使它們在語義上相同(“How can I be a good geologist?” 與 “What should I do to be a great geologist?”)。 我們只是想刪除文本完全相同的問題。 我們將再次使用 map() 函數(shù)。

# Create a Python set to keep track of processed questions
seen = set()

# Remove rows with exactly the same text value
def remove_duplicate_rows(batch):
    global seen

    output = {"id": [], "text": []}

    for id, text in zip(batch["id"], batch["text"]):
        if text not in seen:
            seen.add(text)
            output["id"].append(id)
            output["text"].append(text)

    return output

# Run the "remove_duplicate_rows" function for batches of rows in the dataset
dataset = dataset.map(
    remove_duplicate_rows,
    batched=True,
    batch_size=1000,
    remove_columns=dataset.column_names,
    desc="Remove Duplicates",
)

dataset

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

該數(shù)據(jù)集現(xiàn)在包含 537,362 個獨(dú)特的問題。

我們將使用之前用 “cat” 和 “dog” 演示的相同方法為這些問題生成文本嵌入。 稍后,我們會將它們索引到 Elasticsearch 中,以便使用稱為“近似最近鄰居(appoximate nearest neigbors)” 的專門查詢類型來查找語義相似的文檔。

讓我們再次使用 map() 方法處理數(shù)據(jù)集。

import time

%env TOKENIZERS_PARALLELISM=true

# Compute embeddings for batches of question text
def compute_embeddings(batch):
    return { "embeddings": model.encode(sentences=batch["text"]) }

try:
    start = time.perf_counter()
    dataset = dataset.map(
        compute_embeddings,
        batched=True,
        batch_size=1000,
        desc="Compute Embeddings",
    )
except KeyboardInterrupt:
    print("Creating text embeddings interrupted by the user...")

print(
    "Dataset with embeddings:", dataset,
    f"(duration: {humanize.precisedelta(time.perf_counter() - start)})",
    "\n")

# Print a sample of the embeddings for first question
print(list(dataset[:1]["embeddings"][0][:5]) + ["..."])

?Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

正如你所看到的,這是一個資源密集型操作,在配備 M1 Max 芯片的 Apple 筆記本上可能需要 16?多分鐘。 要保留帶有嵌入的完整數(shù)據(jù)集,請使用 save_to_disk() 方法。?

索引數(shù)據(jù)到 Elasticsearch

在下一步中,我們將創(chuàng)建一個具有特定映射的 Elasticsearch 索引,用于將嵌入存儲在密集向量字段類型中,并將問題文本存儲在常規(guī)文本字段中,并使用英語分析器進(jìn)行處理。

如果你想嘗試自己運(yùn)行這些示例,你需要一個 Elasticsearch 集群。 使用此存儲庫中提供的 Docker Compose 文件在本地啟動集群。你可以參考如下的兩篇文章來創(chuàng)建自己的 Elasticsearch 及 Kibana:

  • ?如何在 Linux,MacOS 及 Windows 上進(jìn)行安裝 Elasticsearch

  • Kibana:如何在 Linux,MacOS 及 Windows 上安裝 Elastic 棧中的 Kibana

在安裝的時候,我們選擇使用 Elastic Stack 8.x 的安裝手冊來進(jìn)行安裝。在默認(rèn)的情況下,Elasticsearch 的安裝是帶有 https 的安全訪問。

import os
from elasticsearch import Elasticsearch

INDEX_NAME = "quora-with-embeddings-v1"
# es = Elasticsearch(hosts=os.getenv("ELASTICSEARCH_URL"), request_timeout=300)

CERT_FINGERPRINT="bd0a26dc646ef1cb3cb5e132e77d6113e1b46d56ee390dd3c6f0b2d2b16962c4"

es = Elasticsearch(  ['https://localhost:9200'],
    basic_auth = ('elastic', 'h6y=vgnen2vkbm6D+z6-'),
    ssl_assert_fingerprint = CERT_FINGERPRINT,
    http_compress = True )

if not es.indices.exists(index=INDEX_NAME):
    es.indices.create(
        index=INDEX_NAME,
        mappings={
            "properties": {
                "text": {
                    "type": "text",
                    "analyzer": "english",
                },
                "embeddings": {
                    "type": "dense_vector",
                    "dims": model.get_sentence_embedding_dimension(),
                    "index": "true",
                    "similarity": "cosine",
                },
            }
        },
    )

    print(f"Created Elasticsearch index at {os.getenv('ELASTICSEARCH_URL')}/{INDEX_NAME}?pretty")
else:
    print(f"Skipping index creation, index already exists")

更多關(guān)于如何連接到 Elasticsearch 集群的知識,請參閱文章?“Elasticsearch:關(guān)于在 Python 中使用 Elasticsearch 你需要知道的一切 - 8.x”。你也可以參考文章 “Elasticsearch:如何將整個 Elasticsearch 索引導(dǎo)出到文件 - Python 8.x”。

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

我們可以在在 Kibana 中進(jìn)行查看:

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

現(xiàn)在我們準(zhǔn)備好對數(shù)據(jù)建立索引了。 我們將使用 Elasticsearch 客戶端的 parallel_bulk() 幫助器,因為它是加載數(shù)據(jù)的最方便的方式:它通過在多個線程中運(yùn)行客戶端來優(yōu)化進(jìn)程,并且它接受 Python 迭代器(iterable)或生成器(generator),從而提供 用于索引大型數(shù)據(jù)集的高級接口。 我們將使用數(shù)據(jù)集的 to_iterable_dataset() 方法將其轉(zhuǎn)換為生成器。 這種轉(zhuǎn)換對于大型數(shù)據(jù)集尤其有益,因為它允許更節(jié)省內(nèi)存的處理。

import os
import time
from elasticsearch.helpers import parallel_bulk

if es.count(index=INDEX_NAME)["count"] >= len(dataset):
    print("Skipping indexing, data already indexed.")
else:
    progress = notebook_tqdm(unit="docs", total=len(dataset))
    indexed = 0
    start = time.perf_counter()

    # Remove the "id" column and convert the dataset to generator
    iterable_dataset = dataset.remove_columns(["id"]).to_iterable_dataset()

    try:
        print(f"Indexing dataset to [{INDEX_NAME}]...")

        for ok, result in parallel_bulk(
            es,
            iterable_dataset,
            index=INDEX_NAME,
            thread_count=os.cpu_count()//2,
        ):
            indexed += 1
            progress.update(1)
        print(f"Indexed [{humanize.intcomma(indexed)}] documents in {humanize.precisedelta(time.perf_counter() - start)}")
    except KeyboardInterrupt:
        print(f"Indexing interrupted by the user, indexed [{humanize.intcomma(indexed)}] documents in {humanize.precisedelta(time.perf_counter() - start)}")

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

好的! 看起來我們的文檔已成功建立索引。 讓我們使用 Cat Indices API 檢查索引,顯示文檔數(shù)量和磁盤上索引的大小。

res = es.cat.indices(index=INDEX_NAME, format="json")
print(
    f"Index [{INDEX_NAME}] contains [{humanize.intcomma(res.body[0]['docs.count'])}] documents",
    f"and uses [{res.body[0]['pri.store.size'].upper()}] of disk space"
)

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

搜索數(shù)據(jù)

至此,我們終于可以使用 Elasticsearch 來搜索數(shù)據(jù)了。

我們將定義實用函數(shù)來包裝搜索請求并以格式化的 Pandas 數(shù)據(jù)幀返回結(jié)果。 我們將使用匹配查詢進(jìn)行詞法搜索,使用 knn 選項進(jìn)行語義搜索。

import pandas as pd

# Lexical search with the `match` query
def search_keywords(query, size=10):
    res = es.search(
        index=INDEX_NAME,
        query={"match": {"text": query}},
        size=size,
        source_includes=["text", "embeddings"],
    )

    return pd.DataFrame(
        [
            {"text": hit["_source"]["text"], "embeddings": hit["_source"]["embeddings"], "score": hit["_score"]}
            for hit in res["hits"]["hits"]
        ]
    )

# Semantic search with the `knn` option
# https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html#search-api-knn
def search_embeddings(query, size=10):
    res = es.search(
        index=INDEX_NAME,
        knn={
            "field": "embeddings",
            "query_vector": model.encode(query, normalize_embeddings=True),
            "k": size,
            "num_candidates": 1000,
        },
        size=size,
        source_includes=["text", "embeddings"],
    )

    return pd.DataFrame(
        [
            {"text": hit["_source"]["text"], "embeddings": hit["_source"]["embeddings"], "score": hit["_score"]}
            for hit in res["hits"]["hits"]
        ]
    )

# Returns the dataframe without the "embeddings" column and with a formatted "score" column
def styled(df):
    return (df[["score", "text"]]
        .style
        .set_table_styles([dict(selector="th,td", props=[("text-align", "left")])])
        .hide(axis="index")
        .format({"score": "{:.3f}"})
        .background_gradient(subset=["score"], cmap="Greys"))

# Add the utility function to the dataframe class
pd.DataFrame.styled = styled

讓我們使用原始文章中的查詢 “Which city has the highest population in the world?” 來執(zhí)行詞法搜索。

search_keywords("Which city has the highest population in the world?")

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

我們可以立即觀察到大多數(shù)結(jié)果與我們的查詢不太相關(guān)。 除了 “Which is the most populated city in the world.?” 等項目之外。 和 “What are the most populated cities in the world?”,大多數(shù)結(jié)果與 “most populated city” 的概念幾乎沒有關(guān)系。 我們還可以觀察默認(rèn)評分算法如何增強(qiáng)問題開頭的短語 “Which city (…)”,盡管文本的其余部分不相關(guān)(歷史建筑的數(shù)量、生活水平等)。

讓我們使用相同的查詢執(zhí)行語義搜索,看看是否得到不同的結(jié)果。

search_embeddings("Which city has the highest population in the world?")

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

很明顯,這些結(jié)果與我們的查詢概念更加相關(guān)。 詞匯搜索中最相關(guān)的結(jié)果返回在頂部,接下來的幾個結(jié)果幾乎與 “most populated city” 概念同義,例如。 “l(fā)argest city” 或 “biggest city”。 另請注意,“Which is the largest city in the world by area?” 結(jié)果列在與國家(而非城市)相關(guān)的結(jié)果之后。 這是非常令人期待的:我們的查詢是關(guān)于人口規(guī)模,而不是面積。

讓我們嘗試一些意想不到的事情。 讓我們重新措辭該查詢,使其不包含匹配文檔中的任何重要關(guān)鍵字,省略限定詞 “which”,將 “city” 替換為“urban location”,將 “highest population” 替換為 “excessive concentration of homo sapiens” ,誠然是一個非常不自然的短語。 (此重新表述的所有功勞均歸于詹姆斯·布里格斯(James Briggs),請參閱原始文章的特定版本。)

search_embeddings("Urban locations with the highest concentration of homo sapiens")

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

也許令人驚訝的是,我們得到的結(jié)果大多與我們的查詢相關(guān),尤其是在列表頂部,即使我們的查詢是故意構(gòu)造的,查詢術(shù)語和文檔術(shù)語之間沒有直接重疊。 這有力地展示了語義搜索的最強(qiáng)點。

讓我們嘗試使用詞法搜索執(zhí)行相同的查詢。

search_keywords("Urban locations with the highest concentration of homo sapiens")

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

我們沒有得到與我們的查詢相關(guān)的結(jié)果。 根據(jù)我們對詞匯搜索和語義搜索之間差異的理解,這應(yīng)該不足為奇。 事實上,這種效應(yīng)有一個技術(shù)描述,詞匯不匹配,即查詢術(shù)語與文檔術(shù)語相差太大。 即使前面提到的詞干提取或詞形還原等術(shù)語操作也無法防止這種不匹配。 傳統(tǒng)上,解決方案是向搜索引擎提供同義詞列表。 然而,這很快就會變得復(fù)雜,因為最終我們需要提供完整的同義詞庫。 (此外,由于評分算法通常的工作方式,在計算每個結(jié)果的分?jǐn)?shù)時,它無法區(qū)分單詞及其同義詞。)

讓我們回到原來的查詢,使用稍微不同的措辭,看看我們是否可以可視化嵌入和結(jié)果,類似于我們使用 “cat” 和 “dog” 等單詞進(jìn)行的演示。

df = search_embeddings("What is the most populated city in the world?")
df

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

我們需要使用 explode() 數(shù)據(jù)幀方法再次將數(shù)據(jù)幀從 “寬” 格式轉(zhuǎn)換為 “長” 格式。

# Store the original index values (0-9) as position
df["position"] = [list(range(len(df.embeddings[i]))) for i in df.index]

# Convert the `embeddings` and `position` columns from "wide" to "long" format
source = df.explode(["embeddings", "position"])

# Rename the `embeddings` column to `embedding`
source = source.rename(columns={ "embeddings": "embedding"})

source

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

讓我們使用 Vega-Altair 創(chuàng)建每個結(jié)果的嵌入 “熱圖”。

import altair as alt

alt.Chart(
    source
).encode(
    alt.X("position:N", title="").axis(labels=False, ticks=False),
    alt.Y("text:N", title="", sort=source["score"].unique()).axis(labelLimit=300, tickWidth=0, labelFontWeight="bold"),
    alt.Color("embedding:Q").scale(scheme="goldred").legend(None),
).mark_rect(
    width=3
).properties(width=alt.Step(3), height=alt.Step(25))

Elasticsearch:語義搜索 - Semantic Search in python,elasticsearch,大數(shù)據(jù),搜索引擎,全文檢索,人工智能,python

盡管進(jìn)行 “reading from tea leaves” 類型的分析存在輕微風(fēng)險,但我們?nèi)匀豢梢员鎰e圖表中的特定模式。 請注意前三個結(jié)果的視覺模式非常相似。 第四個結(jié)果在某種程度上打破了這種模式,也許是因為它是關(guān)于人口最多的國家而不是城市。 同樣,與面積最大城市相關(guān)的兩個結(jié)果形成了獨(dú)特的視覺模式。

然而,和以前一樣,我們可以看到理解具有大量維度的可視化是多么具有挑戰(zhàn)性。 讓我們再次嘗試降低維度,并將結(jié)果繪制在二維平面上。文章來源地址http://www.zghlxwxcb.cn/news/detail-625805.html

到了這里,關(guān)于Elasticsearch:語義搜索 - Semantic Search in python的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Elasticsearch:Search tutorial - 使用 Python 進(jìn)行搜索 (三)

    Elasticsearch:Search tutorial - 使用 Python 進(jìn)行搜索 (三)

    這個是繼上一篇文章 “Elasticsearch:Serarch tutorial - 使用 Python 進(jìn)行搜索 (二)” 的續(xù)篇。在今天的文章中,本節(jié)將向你介紹一種不同的搜索方式,利用機(jī)器學(xué)習(xí) (ML) 技術(shù)來解釋含義和上下文。 在機(jī)器學(xué)習(xí)中,嵌入是表示現(xiàn)實世界對象(例如單詞、句子、圖像或視頻)的向量

    2024年02月02日
    瀏覽(27)
  • Elasticsearch:語義搜索即服務(wù)處于卓越搜索的中心

    Elasticsearch:語義搜索即服務(wù)處于卓越搜索的中心

    作者:來自 Elastic?Sherry Ger, Stephen Brown 對于許多企業(yè)來說,搜索卓越中心(center of excellence - COE)向其用戶提供搜索服務(wù),從不同的數(shù)據(jù)源中整理知識,并將搜索功能集成到其內(nèi)部和外部應(yīng)用程序中。Elasticsearch,這個 “支撐著互聯(lián)網(wǎng)上大約 90% 的搜索欄” 的分布式搜索平臺,

    2024年04月11日
    瀏覽(22)
  • Elasticsearch:使用 Transformers 和 Elasticsearch 進(jìn)行語義搜索

    Elasticsearch:使用 Transformers 和 Elasticsearch 進(jìn)行語義搜索

    什么語義搜索( semantic search )呢?根據(jù)搜索查詢的意圖和上下文含義(而不僅僅是)檢索結(jié)果。語義/向量搜索是一種強(qiáng)大的技術(shù),可以大大提高搜索結(jié)果的準(zhǔn)確性和相關(guān)性。 與傳統(tǒng)的基于的搜索方法不同,語義搜索使用單詞的含義和上下文來理解查詢背后的意

    2024年02月08日
    瀏覽(26)
  • Elasticsearch:使用 ELSER 進(jìn)行語義搜索

    Elasticsearch:使用 ELSER 進(jìn)行語義搜索

    Elastic Learned Sparse EncodeR(或 ELSER)是一種由 Elastic 訓(xùn)練的 NLP 模型,使你能夠使用稀疏向量表示來執(zhí)行語義搜索。 語義搜索不是根據(jù)搜索詞進(jìn)行字面匹配,而是根據(jù)搜索查詢的意圖和上下文含義檢索結(jié)果。 本教程中的說明向你展示了如何使用 ELSER 對數(shù)據(jù)執(zhí)行語義搜索。 提示

    2024年02月11日
    瀏覽(30)
  • fastapi結(jié)合Manticore Search、elasticsearch、mysql實現(xiàn)全文搜索

    fastapi結(jié)合Manticore Search、elasticsearch、mysql實現(xiàn)全文搜索

    創(chuàng)建測試表 測試表插入數(shù)據(jù) 表字段描述 字段意義 mysql數(shù)據(jù)同步到es es查看數(shù)據(jù)(Elasticvue插件) ? mysql數(shù)據(jù)同步到Manticore 注:Manticore 和 Mysql 使用pymysql即mysql客戶端 Manticore 數(shù)據(jù)查詢(工具Webyog SQLyog) ? es安全認(rèn)證連接(參考官網(wǎng)) 按fields查詢方法封裝,輸入?yún)?shù)fields 篩選器,

    2024年02月12日
    瀏覽(19)
  • Elasticsearch:使用 ELSER v2 進(jìn)行語義搜索

    Elasticsearch:使用 ELSER v2 進(jìn)行語義搜索

    在我之前的文章 “Elasticsearch:使用 ELSER 進(jìn)行語義搜索”,我們展示了如何使用 ELESR v1 來進(jìn)行語義搜索。在使用 ELSER 之前,我們必須注意的是: 重要 :雖然 ELSER V2 已正式發(fā)布,但 ELSER V1 仍處于 [預(yù)覽] 狀態(tài)。此功能處于技術(shù)預(yù)覽階段,可能會在未來版本中更改或刪除。 E

    2024年02月22日
    瀏覽(25)
  • Elasticsearch:使用 ELSER 文本擴(kuò)展進(jìn)行語義搜索

    Elasticsearch:使用 ELSER 文本擴(kuò)展進(jìn)行語義搜索

    在今天的文章里,我來詳細(xì)地介紹如何使用 ELSER??進(jìn)行文本擴(kuò)展驅(qū)動的語義搜索。 如果你還沒有安裝好自己的 Elasticsearch 及 Kibana,請參考如下的鏈接來進(jìn)行安裝: 如何在 Linux,MacOS 及 Windows 上進(jìn)行安裝 Elasticsearch Kibana:如何在 Linux,MacOS 及 Windows 上安裝 Elastic 棧中的 Kiba

    2024年02月07日
    瀏覽(25)
  • 使用 Elasticsearch、OpenAI 和 LangChain 進(jìn)行語義搜索

    使用 Elasticsearch、OpenAI 和 LangChain 進(jìn)行語義搜索

    在本教程中,我將引導(dǎo)您使用 Elasticsearch、OpenAI、LangChain 和 FastAPI 構(gòu)建語義搜索服務(wù)。 LangChain 是這個領(lǐng)域的新酷孩子。 它是一個旨在幫助你與大型語言模型 (LLM) 交互的庫。 LangChain 簡化了與 LLMs 相關(guān)的許多日常任務(wù),例如從文檔中提取文本或在向量數(shù)據(jù)庫中對它們建立索引

    2024年02月08日
    瀏覽(21)
  • Elasticsearch:語義搜索、知識圖和向量數(shù)據(jù)庫概述

    Elasticsearch:語義搜索、知識圖和向量數(shù)據(jù)庫概述

    結(jié)合對你自己的私有數(shù)據(jù)執(zhí)行語義搜索的概述 語義搜索是一種使用自然語言處理算法來理解單詞和短語的含義和上下文以提供更準(zhǔn)確的搜索結(jié)果的搜索技術(shù)。 這種方法基于這樣的想法:搜索引擎不僅應(yīng)該匹配查詢中的,還應(yīng)該嘗試?yán)斫庥脩羲阉鞯囊鈭D以及所使用的單

    2024年02月16日
    瀏覽(30)
  • Elasticsearch 使用scroll滾動技術(shù)實現(xiàn)大數(shù)據(jù)量搜索、深度分頁問題 和 search

    基于scroll滾動技術(shù)實現(xiàn)大數(shù)據(jù)量搜索 如果一次性要查出來比如10萬條數(shù)據(jù),那么性能會很差,此時一般會采取用scroll滾動查詢,一批一批的查,直到所有數(shù)據(jù)都查詢完為止。 scroll搜索會在第一次搜索的時候,保存一個當(dāng)時的視圖快照,之后只會基于該舊的視圖快照提供數(shù)據(jù)搜

    2024年02月14日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包