在亚马逊云科技AWS上利用ElasticSearch和RAG搭建个性化推荐系统

news2025/1/24 14:36:50

简介:

小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案,帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践,并应用到自己的日常工作里。

本次介绍用当下热门的RAG和大语言模型,结合亚马逊云科技热门AI模型管理服务Amazon SageMaker,手把手开发一个用户的个性化推荐系统。本文将通过利用Bloom-7B向量化模型将用户数据文档生成向量,并保存到向量数据库ElasticSearch中,最后利用RAG和部署在SageMaker上的Falcon-7B大语言模型在ElasticSearch中进行语义搜索生成用户个性化建议。我将带领大家编写一步一步的细节代码和展示实际的生成式AI实操项目,0基础学会AI核心技能。本架构设计全部采用了云原生Serverless架构,提供可扩展和安全的AI应用解决方案。本方案架构图如下:

方案实操背景知识

Amazon SageMaker

Amazon SageMaker 是一款亚马逊云科技AWS上的机器学习模型托管服务,旨在帮助开发者和数据科学家快速构建、训练和部署机器学习模型。SageMaker 提供了丰富的工具和功能,如内置的 Jupyter Notebook、自动模型调优和托管训练环境,使整个机器学习工作流程更加简化和高效。

特别值得一提的是 SageMaker 的 JumpStart 功能。JumpStart 提供了一系列预训练模型和机器学习解决方案,用户可以通过简单的界面快速部署这些模型,大大缩短了从实验到生产的时间。这对于那些需要快速验证模型效果或不具备大量机器学习知识的开发者来说尤为有用。

Amazon OpenSearch Service

Amazon OpenSearch Service 是一个亚马逊云科技托管的搜索和分析引擎,基于开源的 Elasticsearch 和 Kibana 构建。OpenSearch Service 提供了强大的数据索引和搜索能力,适用于日志分析、实时应用监控和复杂数据分析等场景。

此外,OpenSearch Service 还支持向量化数据库功能。通过将嵌入的向量数据存储在 OpenSearch Service 中,开发者可以实现高效的相似性搜索和推荐系统。向量化数据库能够处理高维度的数据查询,特别适合需要处理自然语言处理(NLP)和图像识别等任务的应用。

本方案包括的内容:

使用 Amazon SageMaker JumpStart 快速部署向量化模型

在 SageMaker Studio 中使用 Jupyter notebook 处理原始数据

在 Amazon OpenSearch Service 中创建索引并存储向量化数据

使用 OpenSearch Service 探索数据

多场景测试利用检索增强生成(RAG)构建的个性化推荐应用

项目搭建具体步骤:

1. 首先我们进入亚马逊云科技控制台,点击OpenSearch服务

2. 创建一个OpenSeach实例,点击左侧的domain查看创建好的实例

3.  查看OpenSearch实例登录界面的URL

4. 接下来我们进入SageMaker服务,点击Studio ,并打开Studio

5. 接下来我们进入JumpStart功能,点击HuggingFace,在这里我们可以直接在服务器上部署HuggingFace上的开源大模型

 6. 我们点击模型“Bloom 7B1 Embedding FP16”向量模型

 7. 点击Deploy部署向量模型

8. 配置参数后部署

 9. 模型部署时间约为5分钟,这时我们点击Studio Classic进入Jupyter Notebook

 10. 点击Open打开Notebook

11. 我们打开一个新的Notebook,开始编写我们的RAG个性化推荐系统服务,首先我们安装并导入必要模块

%%capture
## Code Cell 1 ##
!pip install opensearch-py-ml accelerate tqdm --quiet
!pip install sagemaker --upgrade --quiet

## Code Cell 3 ##

import boto3
import os
import time
import json
import pandas as pd
from tqdm import tqdm
import sagemaker
from opensearchpy import OpenSearch, RequestsHttpConnection
from sagemaker import get_execution_role

12. 接下来我们从文件中导入我们推荐系统的源数据文件

## Code Cell 4 ##

# Read in the Tab delimited data file and print the shape of the resulting data frame.
pd.options.mode.chained_assignment = None
df = pd.read_csv('booksummaries.txt',sep='\t')
print(df.shape)

# Add columns headers to the data frame to be able to analyze.
df.columns = ['WikiPediaId','FreeBaseId','title','author','pub_date','genres','plot_summary']

# Display entries with null data in any column (NaN).
df[df.isnull().any(axis=1)]

13.  接下来我们运行下面的代码对数据进行处理,删除不需要的列、字符,并丢弃确实数据的行

## Code Cell 5 ##

# Let's drop any rows that contain null values in any column. In a real production application you would want to replace NaN values with correct data.
df_1 = df.dropna()

# clean-up Freebase markup and other unwanted characters in the Genres columm. 
df_1.genres.replace(to_replace='\"\/m\/.{4,7}\"\:', value='', regex=True,inplace=True)  # remove Freebase markup
df_1.genres.replace(to_replace='\\\\u00e0\sclef', value='', regex=True,inplace=True)    # remove utf-8 special characters
df_1.genres.replace(to_replace='\{\s"|"\}', value='', regex=True,inplace=True)          # Remove {" or "}
df_1.genres.replace(to_replace='"', value='', regex=True,inplace=True)                  # Remove double quotes

# Only use the first value as the genre
df_2 = df_1['genres'].str.split(',', expand=True, n=1)
df_2.rename( columns={0:'genre'}, inplace=True )
df_2.drop(columns=[1],inplace=True)


df_3 = pd.concat([df_1, df_2], axis=1)

# Trim the size of the plot summary to 500 characters.
df_3['plot_summary'] = df_3['plot_summary'].apply(lambda x: ' '.join(x[:500].split(' ')[:-1]) if len(x) > 500 else x)
df_3['book_summary'] = df_3[['title','author','genre','plot_summary']].agg(' '.join, axis=1)

# Sort by the author column, and to keep the lab within a reasonable time-frame drop the last 5000 rows of data.
# Drop other columns not needed.
df_3.sort_values(by=['author'],inplace = True)
df_3.drop(df_3.tail(5000).index,inplace = True)
df_3.drop(columns=['genres','WikiPediaId','FreeBaseId','plot_summary'],inplace=True)

# Create a dictionary of the remaining data to be used for further processing.
wm_list = df_3.to_dict('records')

# Let's look at the data now that it has been cleansed and trimmed.
df_3

 14. 接下来我们通过授权,建立和OpenSearch集群的连接

## Update the below <StackName> placeholder with the value from your Lab you copied in an earlier step. 
cloudformation_stack_name = 'LabStack-be9ce4b8-cc71-4dda-bfee-6024c60bb194-udbRjk1euELCeKsbKMeE2y-0'

region = 'us-east-1' 

cfn = boto3.client('cloudformation')

def get_cfn_outputs(stackname):
    outputs = {}
    for output in cfn.describe_stacks(StackName=stackname)['Stacks'][0]['Outputs']:
        outputs[output['OutputKey']] = output['OutputValue']
    return outputs

outputs = get_cfn_outputs(cloudformation_stack_name)
aos_host = outputs['OSDomainEndpoint']

outputs


## To authenticate to the OpenSearch domain we need to retrieve username and password stored in Secrets Manager.
secrets = boto3.client('secretsmanager')
os_domain_secret = secrets.list_secrets(
    Filters=[
        {
            'Key':'name',
            'Values': ['DomainMasterUser']
        }
    ]
)['SecretList'][0]['Name']

aos_credentials = json.loads(secrets.get_secret_value(SecretId=os_domain_secret)['SecretString'])

auth = (aos_credentials['username'], aos_credentials['password'])
print(auth)

## The below client will be used in a later step below.
aos_client = OpenSearch(
    hosts = [{'host': aos_host, 'port': 443}],
    http_auth = auth,
    use_ssl = True,
    verify_certs = True,
    connection_class = RequestsHttpConnection
)

15. 下面我们定义调用SageMaker将文字转化为向量的函数"query_endpoint_with_json_payload"

## Code Cell 8 ##

embedding_endpoint_name = 'jumpstart-dft-hf-textembedding-bloo-20240804-035651'

def query_endpoint_with_json_payload(encoded_json, endpoint_name, content_type="application/json"):
    client = boto3.client("runtime.sagemaker")
   
    response = client.invoke_endpoint(
        EndpointName=endpoint_name, ContentType=content_type, Body=encoded_json
    )

    response_json = json.loads(response['Body'].read().decode("utf-8"))
    embeddings = response_json["embedding"]
    if len(embeddings) == 1:
        return [embeddings[0]]
    return embeddings

 16. 接下来我们定义OpenSearch中的索引,用于更精准的搜索文档里的内容,这里我们定义了保存字段的类型,以及索引算法k-NN

## Code Cell 11 ##

knn_index = {
    "settings": {
        "index.knn": True,
        "index.knn.space_type": "cosinesimil",
        "analysis": {
          "analyzer": {
            "default": {
              "type": "standard",
              "stopwords": "_english_"
            }
          }
        }
    },
    "mappings": {
        "properties": {
            "booksummary_vector": {
                "type": "knn_vector",
                "dimension": 4096,
                "store": True
            },
            "book_summary": {
                "type": "text",
                "store": True
            },
            "author": {
                "type": "text",
                "store": True
            },
            "title": {
                "type": "text",
                "store": True
            },
            "pub_date": {
                "type": "text",
                "store": True
            },
            "genre": {
                "type": "text",
                "store": True
            },
        }
    }
}

 17. 我们可以用如下代码,验证和获取我们之前定义的索引

## Code Cell 13 ##

aos_client.indices.get(index=index_name)

18. 接下来我们把之前处理过的文档内的数据导入到OpenSearch索引当中

## Code Cell 14 ##

def os_import(record, aos_client, index_name):
    book_summary = record["book_summary"]
    search_vector = embed_phrase(book_summary)
    aos_client.index(index=index_name,
             body={"booksummary_vector": search_vector[0], 
                   "book_summary": record["book_summary"],
                   "author":record["author"],
                   "genre":record["genre"],
                   "pub_date":record["pub_date"],
                   "title":record["title"]
                  }
            )

print("Loading records...")
for record in tqdm(wm_list): 
    os_import(record, aos_client, index_name)
print("Records loaded.")

19. 打开我们之前在第3步获取的OpenSearch URL,并输入用户名和密码

20. 进入OpenSearch界面中的Stack Management,点击创建索引类型

 21. 为索引类型起一个名字“book_knowledge_base”

 22. 点击创建

 23. 在OpenSearch中点击Discover即可看到我们导入的文档数据

24. 接下来我们定义在OpenSearch中进行近似语义搜索的函数“retrieve_opensearch_with_semantic_search”

## Code Cell 16 ##

def retrieve_opensearch_with_semantic_search(phrase, n=2):
    
    search_vector = embed_phrase(phrase)[0]

    osquery={
        "_source": {
            "exclude": [ "booksummary_vector" ]
        },
        
      "size": n,
      "query": {
        "knn": {
          "booksummary_vector": {
            "vector":search_vector,
            "k":n
          }
        }
      }
    }

    res = aos_client.search(index=index_name, 
                           body=osquery,
                           stored_fields=["title","author","pub_date", "genre", "book_summary"],
                           explain = True)
    top_result = res['hits']['hits'][1]
    
    result = {
        "title":top_result['_source']['title'],
        "author":top_result['_source']['author'],
        "pub_date":top_result['_source']['pub_date'],
        "genre":top_result['_source']['genre'],
        "book_summary":top_result['_source']['book_summary'],
    }
    
    return result

25. 接下来我们运行该函数进行测试,搜索我们输入的内容在文档数据源中进行语义搜索,找到推荐的书籍。我们的问题是:“一本由埃德加·赖斯·巴勒斯(Edgar Rice Burroughs)撰写的、属于科幻类型且涉及人猿泰山的书。”

## Code Cell 17 ##

example_request = retrieve_opensearch_with_semantic_search(question_on_book)
print(question_on_book)
print(example_request)

26.  接下来我们定义函数,利用RAG在OpenSearch中进行语义搜索找到相关结果,在用大模型生成最终回复

## Code Cell 21 ##

def generate_prompt_to_llm(original_question_on_book):
    retrieved_documents = retrieve_opensearch_with_semantic_search(original_question_on_book)
    print("retrieved relevant book per your query is : \n" + str(retrieved_documents))
    print("------------")
    one_shot_description_example = "{'book_summary': 'Tarzan tracks down a man who has been mistaken for him. The man is under the delusion that he is Tarzan, and he is living in a lost city inhabited by people descended from early Portuguese explorers. The plot devices of a lost city and a Tarzan double or impostor had been used by Burroughs in some previous Tarzan novels.', 'author': 'Edgar Rice Burroughs', 'title': 'Tarzan and the Madman', 'genre': 'Science fiction', 'pub_date': '1964'}"
    one_shot_response_example = "It's a real page turning story about Tarzan and how a madman has been impersonating him. The author is Edgar Rice Burroughs and it's a science fiction book with adventure and lots of fun. It was published in the year 1964."
    prompt = (
        f" Make a book recommendation that is similar to the {original_question_on_book} The recommendation must include the title of the book, the author and genre: \n"
        f"Data: {one_shot_description_example} \n Recommendation: {one_shot_response_example} \n"
        f"Data: {retrieved_documents} \n Recommendation:"
    )
    return prompt

27. 最后我们定义函数设计大模型输入和输出的格式,得到最终的推荐结果。

## Code Cell 22 ##

def generate_llm_input(data, **kwargs):
    default_kwargs = {
        "num_beams": 5,
        "no_repeat_ngram_size": 3,
        "do_sample": True,
        "max_new_tokens": 100,
        "temperature": 0.9,
        "watermark": True,
        "top_k": 200,
        "max_length": 200,
        "early_stopping": True
    }
    
    default_kwargs = {**default_kwargs, **kwargs}
    
    input_data = {
        "inputs": data,
        "parameters": default_kwargs
    }
    
    return input_data

def query_llm_with_rag(description, **kwargs):
    prompt = generate_prompt_to_llm(description)
    query_payload = generate_llm_input(prompt, **kwargs)
    response = query_llm_endpoint_with_json_payload(json.dumps(query_payload).encode("utf-8"), endpoint_name=llm_endpoint_name)
    return response

## Code Cell 23 ##

recommendation = query_llm_with_rag(question_on_book)
print(question_on_book)
print(recommendation)

以上就是在亚马逊云科技上使用RAG和部署在SageMaker上大语言模型构建个性化推荐服务系统的步骤。欢迎大家关注小李哥,未来获取更多国际前沿的生成式AI开发方案!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1977873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ThreadLocal源码分析

1.前言 1.1 ThreadLocal基本原理 ThreadLocal 是 Java 中的一个非常有用的类&#xff0c;它提供了一种线程局部变量&#xff0c;即每个线程都可以访问到自己独立初始化过的变量副本&#xff0c;这个变量对其他线程是不可见的。最常见的用法就是用户请求携带用户ID请求某个接口…

机器学习 第9章-聚类

机器学习 第9章-聚类 9.1 聚类任务 在“无监督学习”(unsupervised learning)中&#xff0c;训练样本的标记信息是未知的&#xff0c;目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律&#xff0c;为进一步的数据分析提供基础。此类学习任务中研究最多、应用最广…

【Git】如何优雅地使用Git中的tag来管理项目版本

目录 tagtag 和 branch区别操作命令打tag&#xff0c;当前分支标记tag提交到远程服务器删除本地tag删除远程tag切换到特定的tag查看所有tag查看标签详细信息 好书推荐 tag Git中的tag&#xff08;标签&#xff09;用于给项目在特定时间点&#xff08;某个版本发布&#xff09;…

周鸿祎回应将成三六零第一大股东:会和公司一起走下去

在数字化浪潮席卷全球的今天&#xff0c;网络安全已成为国家安全的重要组成部分。三六零&#xff0c;作为国内网络安全领域的佼佼者&#xff0c;其每一步动态都牵动着业界的神经。近日&#xff0c;随着公司控股股东天津奇信志成的解散清算&#xff0c;周鸿祎的持股比例上升&…

【数据泄露】据称一名威胁行为者正在出售某个加密货币交易平台数据库

该威胁行为者表示&#xff1a; “你好&#xff0c; 我想出售一个加密货币交易平台的数据库 (.csv)。该平台很活跃&#xff0c;并且对实际使用它的用户来说很合法。 行数&#xff1a;150,000 价格&#xff1a;1,500 美元” 泄露数据&#xff1a;user_id、user_name、user_ro…

【Java】Java学生信息管理系统(源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

SpringBoot项目详细整合邮箱服务

springboot 版本&#xff1a;2.6.3 Java版本&#xff1a;1.8 一、应用场景 用户注册发送激活邮箱 注册登录时用邮箱接收验证码 用户密码重置 向用户发送相关系统消息 … 二、添加依赖 <!-- 邮箱 --> <dependency><groupId>org.springframework.b…

【单片机毕业设计选题24098】-校园智能绿植浇灌系统

系统功能: 1. 手机通过蓝牙模块显示系统信息 2. OLED模块显示系统信息 3. DHT11采集温湿度 4. 继电器模块控制水泵 5. 按键调节湿度设定阈值 6. 土壤湿度传感器模块采集土壤湿度 7. 光照传感器采集光照值 系统分为手动和自动模式&#xff0c;上电默认为自动模式&#x…

scratch魔法门 2024年6月scratch四级 中国电子学会图形化编程 少儿编程等级考试四级真题和答案解析

目录 scratch魔法门 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、pyt…

2024/8/4 维高-STD60N驱动器(伺服)---客户反馈:电机异响

步进电机 MHS1A86-60B85B &#xff0c;额定电流6A 步骤一&#xff1a;设置额定电流 std60n驱动器拔码全部为off&#xff08;后台设置&#xff09;&#xff0c;伺服后台连上后设置h00-11按电机铭牌进行 设置下额定电流 步骤二&#xff1a;最好设置峰值电流一…

Jenkins未授权访问漏洞

Jenkins未授权访问漏洞 默认情况下 Jenkins面板中用户可以选择执行脚本界面来操作一些系统层命令&#xff0c;攻击者可通过未授权访问漏洞或者暴力破解用户密码等进入后台管理服务&#xff0c;通过脚本执行界面从而获取服务器权限。 漏洞复现 步骤一:使用以下fofa语法进行产品…

GUI:Tkinter(一)

Tkinter文档 一&#xff0c;Tkinter基本流程 1. 创建应用程序主窗口对象 from tkinter import * window Tk() window.mainloop()#开启主循环 2. 在主窗口中&#xff0c;添加各种可视化组件&#xff0c;比如&#xff1a;按钮&#xff08;Button&#xff09;、文本框&#x…

Java中Lambda表达式的使用

Lambda的背景 Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda 表达式&#xff08;Lambda expressio…

用户看广告获取密码访问网页内容流量主模式源码

简介&#xff1a; 全开源付费进群流量主模式&#xff0c;用户看广告获取密码访问网页内容&#xff0c;网站生成内容&#xff0c;用户需要浏览内容跳转至小程序&#xff0c;观看广告后获取密码&#xff0c;输入密码查看网页内容。 与之前得9.9付费进群区别就是内容体现在了网页…

【信创】国产操作系统上使用的万能打印驱动 _ 统信 _ 麒麟 _ 中科方德

往期好文&#xff1a;统信UOS服务器离线安装postgresql数据库 Hello&#xff0c;大家好&#xff01;今天给大家带来一款在信创终端操作系统上使用的万能打印驱动——瑞印万能打印驱动。这款驱动程序是国产操作系统下的通用打印驱动程序&#xff0c;具有很高的兼容性和广泛的功能…

图论:1857. 有向图中最大颜色值(拓扑排序+动态规划)

文章目录 1.问题分析2.代码解析2.1 代码步骤1. 初始化数据结构2. 构建图和入度数组3. 初始化队列4. 拓扑排序和动态规划5. 检查是否存在环并返回结果 3. 问题扩展1. 最长路径问题&#xff08;DAG&#xff09;2. 最短路径问题&#xff08;DAG&#xff09;3. 最大路径和问题4. 路…

科普文:微服务之Spring Cloud 客户端负载均衡组件LoadBalancer替代Ribbon

概叙 负载均衡 负载均衡的两个基本点&#xff1a; 选择哪个服务器来处理客户端请求。将客户端请求转发出去。 一个核心原理&#xff1a;通过硬件或软件的方式维护一个服务列表清单。当用户发送请求时&#xff0c;会将请求发送给负载均衡器&#xff0c;然后根据负载均衡算法从…

中仕公考:2024年重庆市属事业单位招聘公告

本次公开招聘市属事业单位工作人员218名&#xff0c;报考者可登录重庆市人力资源和社会保障局官网“事业单位公开招聘”栏查阅。 (一)可报考的条件 1.具有中华人民共和国国籍; 2.遵守中华人民共和国宪法和法律&#xff0c;具有良好的品行; 3.身体健康&#xff0c;符合事业单…

OGG同步目标端中文乱码处理

现象说明&#xff1a; 源端字符集&#xff1a;AMERICAN_AMERICA.ZHS16GBK 目标端字符集&#xff1a;AMERICAN_AMERICA.AL32UTF8 源端同步过来的数据显示中文乱码。 查询数据库表中含有乱码的字段&#xff1a; select * from xx.xxxx a where to_char(a.crtopetime,yyyy-mm-…

一些不被人熟知,但又很好用的HTML属性

HTML&#xff08;超文本标记语言&#xff09;具有多种属性&#xff0c;可用于增强我们的网页的结构和功能。 下面我就给大家介绍一下&#xff0c;一些很好用的HTML属性&#xff0c;但是不被人熟知的HTML属性 contenteditable&#xff1a; 这个属性使我们的元素变的可编辑。用…