摸一下elasticsearch8的AI能力:语义搜索/vector向量搜索案例

一、elasticsearch8.x+kibana docker-compose.yml一键安装

ES有RBAC安全验证,需要curl请求es注册用户并配置角色权限,比较麻烦,这里直接关闭,xpack.security.enabled=false 。ES版本8.15.3以上,否则执行搜索时可能会报错。

yaml 复制代码
version: '3.3'

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
      - xpack.security.enabled=false
      - xpack.security.http.ssl.enabled=false
      - xpack.security.transport.ssl.enabled=false
      - ELASTIC_PASSWORD=123456
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
      - "9300:9300"
    networks:
      - elastic

  kibana:
    image: docker.elastic.co/kibana/kibana:8.15.3
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    volumes:
      - /home/kibana.yml:/usr/share/kibana/config/kibana.yml
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch
    networks:
      - elastic

volumes:
  esdata:

networks:
  elastic:
    driver: bridge

/home/kibana.yml 就加了个汉化配置

yaml 复制代码
server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
i18n.locale: "zh-CN"

二、语料向量化模型选择
modelscope下载一个适合的预训练模型用于将商品信息,文章摘要等资源类信息向量化后存入ES。模型都从modelcope找的,hugginface也能找到差不多的,但需要代理且速度很慢。

先试了bert-base-chinesebert-base-uncased,但效果很不好,他们不会认为高兴愉快是近义词,而会认为高兴高处很好很不好是近义词,也就是有字词重叠的词语间向量化后的欧式距离更小。

实现语义搜索应使用sentence BERT类模型,这里找到了nlp_gte_sentence-embedding_chinese-large,效果就很不错。

三、模型及python依赖下载

sh 复制代码
pip3 install modelscope torch transformers  elasticsearch
modelscope download --model iic/nlp_gte_sentence-embedding_chinese-large

如果有如下SSL问题,可以强行改下python requests这个标准库的sessions.py 779行左右的verify=False

四、语义搜索案例

大致逻辑:

  • transformers库加载模型及分词器
  • 将商品信息,文章摘要等资源类信息向量化后存入ES
  • 搜索input案例也使用同样方式向量化调用ES script query API

具体代码如下,去除空行及注释大概就100行

python 复制代码
from transformers import BertTokenizer, BertModel
import torch
import time
from elasticsearch import Elasticsearch

# 初始化模型和分词器
MODEL_PATH = 'C:\\Users\\Administrator\\.cache\\modelscope\\hub\\iic\\nlp_gte_sentence-embedding_chinese-large'
#MODEL_PATH = 'C:\\Users\\Administrator\\.cache\\modelscope\\hub\\tiansz\\bert-base-chinese'
#MODEL_PATH = 'C:\\Users\\Administrator\\.cache\\modelscope\\hub\\AI-ModelScope\\bert-base-uncased'

tokenizer = BertTokenizer.from_pretrained(MODEL_PATH)
model = BertModel.from_pretrained(MODEL_PATH)
es = Elasticsearch([{'scheme':'http','host':'192.168.72.128','port':9200}])

def embed_texts(texts, batch_size=32):
    """
    为一组文本生成BERT嵌入向量。

    :param texts: 需要生成嵌入向量的文本列表
    :param batch_size: 每次处理的文本数量,默认为32
    :return: 嵌入向量列表
    """
    all_embeddings = []
    for i in range(0, len(texts), batch_size):
        batch_texts = texts[i:i+batch_size]
        inputs = tokenizer(batch_texts, return_tensors='pt', padding=True, max_length=512, truncation=True)
        with torch.no_grad():
            outputs = model(**inputs)
            embeddings = outputs.last_hidden_state.mean(dim=1).detach().numpy()
            all_embeddings.extend(embeddings)
    return all_embeddings

def index_document(doc_id, text):
    """
    将文档及其嵌入向量索引到Elasticsearch中。

    :param doc_id: 文档的唯一标识符
    :param text: 文档的文本内容
    """
    try:
        embedding = embed_texts([text])[0]
        doc = {'id': doc_id, 'text': text, 'embedding': embedding.tolist()}
        res = es.index(index='documents', id=doc_id, body=doc)
        if res['result'] != 'created':
            print(f"Failed to index document {doc_id}")
    except Exception as e:
        print(f"Error indexing document {doc_id}: {e}")

def search_similar_documents(query, top_k=10):
    """
    根据查询文本搜索最相似的文档。

    :param query: 查询文本
    :param top_k: 返回的最相似文档数量,默认为3
    :return: 最相似的文档列表
    """
    query_embedding = embed_texts([query])[0]

    script_query = {
        "script_score": {
            "query": {"match_all": {}},
            "script": {
                "source": "cosineSimilarity(params.query_vector, 'embedding') + 1.0",
                "params": {"query_vector": query_embedding.tolist()}
            }
        }
    }
    body = {
        "size": top_k,
        "query": script_query,
        "_source": {"includes": ["text","_score"]}
    }

    response = es.search(index='documents', body=body)
    return response['hits']['hits']

# 示例文档
documents = [
    {"id": 1, "text": "今天天气真好"},
    {"id": 2, "text": "今儿天气很好"},
    {"id": 3, "text": "这里气候很好"},
    {"id": 4, "text": "今天天气很差"},
    {"id": 5, "text": "今天天气很不好"},
    {"id": 6, "text": "今天天气真差"}
]


# 将示例文档索引到Elasticsearch中
for doc in documents:
    index_document(doc['id'], doc['text'])

# 示例查询
#ES是AP模型,需要等下再查,否则查不到数据,着急的话可以去kibana的dev console跑一下`POST documents/_refresh`
time.sleep(3)
results = search_similar_documents('今天天气差极了')
print(results)

效果符合预期,今天天气差极了相关度高的几个sentence的_score最高的排在了最前面,今儿天气很好虽然描述天气好,但至少与天气相关,而这里气候很好描述的某地的气候,确实为最不相关,这种效果是ES传统的term,fuzzy,prefix,phase_match,regex等搜索方式达不到的。

相关推荐
IT古董38 分钟前
【漫话机器学习系列】017.大O算法(Big-O Notation)
人工智能·机器学习
凯哥是个大帅比38 分钟前
人工智能ACA(五)--深度学习基础
人工智能·深度学习
m0_748232921 小时前
DALL-M:基于大语言模型的上下文感知临床数据增强方法 ,补充
人工智能·语言模型·自然语言处理
szxinmai主板定制专家1 小时前
【国产NI替代】基于FPGA的32通道(24bits)高精度终端采集核心板卡
大数据·人工智能·fpga开发
海棠AI实验室1 小时前
AI的进阶之路:从机器学习到深度学习的演变(三)
人工智能·深度学习·机器学习
机器懒得学习1 小时前
基于YOLOv5的智能水域监测系统:从目标检测到自动报告生成
人工智能·yolo·目标检测
QQ同步助手2 小时前
如何正确使用人工智能:开启智慧学习与创新之旅
人工智能·学习·百度
AIGC大时代2 小时前
如何使用ChatGPT辅助文献综述,以及如何进行优化?一篇说清楚
人工智能·深度学习·chatgpt·prompt·aigc
流浪的小新2 小时前
【AI】人工智能、LLM学习资源汇总
人工智能·学习
martian6653 小时前
【人工智能数学基础篇】——深入详解多变量微积分:在机器学习模型中优化损失函数时应用
人工智能·机器学习·微积分·数学基础