作者: AI最严厉的父亲

  • 如何使用GPT-3.5 Turbo进行自定义Fine-tuning

    嗨,大家好,我是王大神。作为一名AI技术博主,我一直关注着最新的人工智能技术,尤其是OpenAI的GPT系列模型。今天,我将与大家分享如何使用GPT-3.5 Turbo进行自定义Fine-tuning,以便训练出符合特定需求的AI模型。

    Fine-tuning是一种强大的技术,它可以让我们根据自己的数据和任务来定制GPT-3.5 Turbo模型,使其能够更好地适应特定应用领域。在本教程中,我将以实际案例为例,演示如何使用食谱数据集进行Fine-tuning,以创建一个食谱信息提取模型。

    步骤一:准备工作

    在开始Fine-tuning之前,我们需要完成一些准备工作。首先,确保你的Python包安装最新版本的OpenAI。你可以使用以下命令来安装:

    !pip install --upgrade openai

    接下来,我们需要导入所需的库和设置OpenAI API密钥:

    import json
    import openai
    import os
    import pandas as pd
    from pprint import pprint
    
    OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")

    步骤二:数据准备

    Fine-tuning的成功与否很大程度上取决于数据的质量和准备工作。在本例中,我们将使用食谱数据集进行Fine-tuning,这个数据集包含了各种食谱以及每个食谱中提取出的通用食材列表,适用于命名实体识别(NER)任务。

    2.1 数据集加载

    首先,我们需要加载我们要使用的数据集,并确保它足够专注于模型学习,同时也足够通用,以便不会错过未见过的示例。在这个例子中,我们从RecipesNLG数据集中提取了一个子集,只包含来自www.cookbooks.com的文档。

    # 读取我们要使用的数据集,这将是RecipesNLG数据集,我们已经清理过,只包含来自www.cookbooks.com的文档
    recipe_df = pd.read_csv("data/cookbook_recipes_nlg_10k.csv")
    
    recipe_df.head()

    这个数据集的前几行看起来像这样:

    title ingredients directions link source NER
    No-Bake Nut Cookies ["1 c. firmly packed brown sugar", … ["In a heavy 2-quart saucepan, mix brown sugar…", … www.cookbooks.com/Recipe-Details.aspx?id=44874 www.cookbooks.com ["brown sugar", "milk", "vanilla", "nuts", …
    Jewell Ball’S Chicken ["1 small jar chipped beef, cut up", … ["Place chipped beef on bottom of baking dish….", … www.cookbooks.com/Recipe-Details.aspx?id=699419 www.cookbooks.com ["beef", "chicken breasts", "cream of mushroom…
    Creamy Corn ["2 (16 oz.) pkg. frozen corn", … ["In a slow cooker, combine all ingredients. C…", … www.cookbooks.com/Recipe-Details.aspx?id=10570 www.cookbooks.com ["frozen corn", "cream cheese", "butter", …
    Chicken Funny ["1 large whole chicken", … ["Boil and debone chicken.", "Put bite size pi…", … www.cookbooks.com/Recipe-Details.aspx?id=897570 www.cookbooks.com ["chicken", "chicken gravy", "cream of mushroo…
    Reeses Cups(Candy) ["1 c. peanut butter", … ["Combine first four ingredients and press in …", … www.cookbooks.com/Recipe-Details.aspx?id=659239 www.cookbooks.com ["peanut butter", "graham cracker crumbs", "bu…

    2.2 数据准备

    在Fine-tuning过程中,我们需要将数据准备成适合模型的格式。对于ChatCompletion格式的Fine-tuning,每个训练示例都是一个简单的消息列表。示例中包含以下角色的消息:系统消息、用户消息和助手消息。在我们的例子中,用户消息包括食谱的标题和食材信息,而助手消息包括提取出的通用食材列表。

    以下是一个示例训练示例的格式:

    [
        {
            "role": "system",
            "content": "You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided."
        },
        {
            "role": "user",
            "content": "Title: No-Bake Nut Cookies\n\nIngredients: [\"1 c. firmly packed brown sugar\", \"1/2 c. evaporated milk\", \"1/2 tsp. vanilla\", ...]\n\nGeneric ingredients: "
        },
        {
            "role": "assistant",
            "content": "[\"brown sugar\", \"milk\", \"vanilla\", ...]"
        }
    ]

    我们可以使用以下代码来准备训练数据:

    training_data = []
    
    system_message = "You are a helpful recipe assistant. You are to extract the generic ingredients from each of the recipes provided."
    
    def create_user_message(row):
        return f"""Title: {row['title']}\n\nIngredients: {row['ingredients']}\n\nGeneric ingredients: """
    
    def prepare_example_conversation(row):
        messages = []
        messages.append({"role": "system", "content": system_message})
    
        user_message = create_user_message(row)
        messages.append({"role": "user", "content": user_message})
    
        messages.append({"role": "assistant", "content": row["NER"]})
    
        return {"messages": messages}
    
    for _, row in recipe_df.iterrows():
        training_data.append(prepare_example_conversation(row))
    
    # 将数据保存到文件中
    with open("fine_tuning_data.jsonl", "w") as outfile:
        for example in training_data:
            outfile.write(json.dumps(example) + "\n")

    现在,我们已经准备好了Fine-tuning所需的训练数据。

    步骤三:Fine-tuning

    接下来,我们将使用准备好的数据集对GPT-3.5 Turbo进行Fine-tuning。在进行Fine-tuning之前,请确保你有足够的计算资源和时间,因为Fine-tuning可能需要一些时间。

    import openai
    
    # 定义Fine-tuning的训练参数
    fine_tuning_params = {
        "model": "text-davinci-003",
        "checkpoint": "latest",  # 或者选择一个特定的checkpoint
        "dataset": {
            "path": "fine_tuning_data.jsonl",
            "split": 0.8,  # 数据集分割为训练和验证集
        },
        "max_steps": 10000,  # 训练的最大步数
        "save_checkpoint_every": 1000,  # 每隔多少步保存一个checkpoint
        "overwrite": True,  # 如果已经存在同名的模型,是否覆盖
    }
    
    # 开始Fine-tuning
    response = openai.ChatCompletion.create(**fine_tuning_params)
    pprint(response)

    Fine-tuning的时间长度取决于所选参数和模型的大小,但一旦完成,你就会获得一个定制的模型,可以用于特定任务。

    步骤四:使用Fine-tuned模型

    一旦Fine-tuning完成,你就可以使用新的Fine-tuned模型来执行特定任务。在我们的示例中,我们可以使用模型来提取食谱中的通用食材列表。

    def extract_generic_ingredients(title, ingredients):
        prompt = f"Title: {title}\n\nIngredients: {ingredients}\n\nGeneric ingredients: "
        response = openai.ChatCompletion.create(
            model="your-fine-tuned-model-name",  # 使用你的Fine-tuned模型名字
            messages=[
                {"role": "system", "content": "You are a helpful recipe assistant. You are to extract the generic ingredients from this recipe."},
                {"role": "user", "content": prompt},
            ],
        )
    
        assistant_response = response['choices'][0]['message']['content']
        return assistant_response
    
    # 示例:提取通用食材列表
    recipe_title = "No-Bake Nut Cookies"
    recipe_ingredients = ["1 c. firmly packed brown sugar", "1/2 c. evaporated milk", "1/2 tsp. vanilla", ...]
    
    generic_ingredients = extract_generic_ingredients(recipe_title, recipe_ingredients)
    print("Generic ingredients:", generic_ingredients)

    这样,你就可以使用Fine-tuned模型执行特定的任务了。

    结论

    在本文中,我向大家介绍了如何使用GPT-3.5 Turbo进行自定义Fine-tuning,以便训练出符合特定需求的AI模型。通过准备数据、进行Fine-tuning和使用Fine-tuned模型,你可以为各种任务创建定制的AI助手。希望这个教程对你有所帮助,祝你在Fine-tuning旅程中取得成功!

  • 如何使用ChatGPT自动化AWS任务:与S3存储桶互动的示例

    本教程将向您展示如何使用ChatGPT函数来执行与Amazon S3存储桶相关的任务的示例。这篇教程涵盖了S3存储桶的关键功能,包括运行简单的列出命令、在所有存储桶中搜索特定文件、将文件上传到存储桶以及从存储桶下载文件。OpenAI Chat API可以理解用户的指令,生成自然语言响应,并根据用户输入提取适当的函数调用。

    先决条件

    在开始之前,请确保您具备以下条件:

    1. 生成一个具有S3存储桶写入权限的AWS访问密钥,并将它们存储在与OpenAI密钥一起的本地环境文件中。环境文件的格式如下:
    AWS_ACCESS_KEY_ID=<您的密钥>
    AWS_SECRET_ACCESS_KEY=<您的密钥>
    OPENAI_API_KEY=<您的密钥>
    1. 安装所需的Python模块。您可以使用以下命令来安装它们:
    !pip install openai boto3 tenacity python-dotenv

    代码示例

    以下是使用ChatGPT和AWS S3存储桶互动的示例代码。

    import openai
    import json
    import boto3
    import os
    import datetime
    from urllib.request import urlretrieve
    from dotenv import load_dotenv
    
    # 从.env文件加载环境变量
    load_dotenv()
    
    # 设置OpenAI API密钥
    openai.api_key = os.environ.get("OPENAI_API_KEY")
    GPT_MODEL = "gpt-3.5-turbo"
    
    # 创建S3客户端
    s3_client = boto3.client('s3')
    
    # 定义函数字典,以便为GPT模型提供S3操作的详细信息
    functions = [
        # 列出所有可用的S3存储桶
        {
            "name": "list_buckets",
            "description": "列出所有可用的S3存储桶",
            "parameters": {
                "type": "object",
                "properties": {}
            }
        },
        # 列出给定S3存储桶内的对象或文件
        {
            "name": "list_objects",
            "description": "列出给定S3存储桶内的对象或文件",
            "parameters": {
                "type": "object",
                "properties": {
                    "bucket": {"type": "string", "description": "S3存储桶的名称"},
                    "prefix": {"type": "string", "description": "S3存储桶中的文件夹路径"},
                },
                "required": ["bucket"],
            },
        },
        # 从S3存储桶中下载特定文件到本地目标文件夹
        {
            "name": "download_file",
            "description": "从S3存储桶中下载特定文件到本地目标文件夹",
            "parameters": {
                "type": "object",
                "properties": {
                    "bucket": {"type": "string", "description": "S3存储桶的名称"},
                    "key": {"type": "string", "description": "S3存储桶中文件的路径"},
                    "directory": {"type": "string", "description": "本地目标目录,由用户指定。"},
                },
                "required": ["bucket", "key", "directory"],
            }
        },
        # 将文件上传到S3存储桶
        {
            "name": "upload_file",
            "description": "将文件上传到S3存储桶",
            "parameters": {
                "type": "object",
                "properties": {
                    "source": {"type": "string", "description": "本地源文件路径或远程URL"},
                    "bucket": {"type": "string", "description": "S3存储桶的名称"},
                    "key": {"type": "string", "description": "S3存储桶中文件的路径"},
                    "is_remote_url": {"type": "boolean", "description": "提供的源是否为URL(True)还是本地路径(False)"},
                },
                "required": ["source", "bucket", "key", "is_remote_url"],
            }
        },
        # 在S3存储桶内搜索特定文件名
        {
            "name": "search_s3_objects",
            "description": "在S3存储桶内搜索特定文件名",
            "parameters": {
                "type": "object",
                "properties": {
                    "search_name": {"type": "string", "description": "要搜索的文件名"},
                    "bucket": {"type": "string", "description": "S3存储桶的名称"},
                    "prefix": {"type": "string", "description": "S3存储桶中的文件夹路径"},
                    "exact_match": {"type": "boolean", "description": "如果搜索应匹配完整文件名,请将exact_match设置为True。将exact_match设置为False以比较文件名字符串的一部分(文件包含)"}
                },
                "required": ["search_name"],
            },
        }
    ]
    
    # 创建用于将S3操作与函数连接的辅助函数
    
    def datetime_converter(obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")
    
    def list_buckets():
        response = s3_client.list_buckets()
        return json.dumps(response['Buckets'], default=datetime_converter)
    
    def list_objects(bucket, prefix=''):
        response = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix)
        return json.dumps(response.get('Contents', []), default=datetime_converter)
    
    def download_file(bucket, key, directory):
    
        filename = os.path.basename(key)
    
        # 解析目标,得到正确的文件路径
        destination = os.path.join(directory, filename)
    
        s3_client.download_file(bucket, key, destination)
        return json.dumps({"status": "success", "bucket": bucket, "key": key, "destination": destination})
    
    def upload_file(source, bucket, key, is_remote_url=False):
        if is_remote_url:
            file_name = os.path.basename(source)
            urlretrieve(source, file_name)
            source = file_name
    
        s3
    
    _client.upload_file(source, bucket, key)
        return json.dumps({"status": "success", "source": source, "bucket": bucket, "key": key})
    
    def search_s3_objects(search_name, bucket=None, prefix='', exact_match=True):
        search_name = search_name.lower()
    
        if bucket is None:
            buckets_response = json.loads(list_buckets())
            buckets = [bucket_info["Name"] for bucket_info in buckets_response]
        else:
            buckets = [bucket]
    
        results = []
    
        for bucket_name in buckets:
            objects_response = json.loads(list_objects(bucket_name, prefix))
            if exact_match:
                bucket_results = [obj for obj in objects_response if search_name == obj['Key'].lower()]
            else:
                bucket_results = [obj for obj in objects_response if search_name in obj['Key'].lower()]
    
            if bucket_results:
                results.extend([{"Bucket": bucket_name, "Object": obj} for obj in bucket_results])
    
        return json.dumps(results)
    
    # 连接名称和函数以供执行
    available_functions = {
        "list_buckets": list_buckets,
        "list_objects": list_objects,
        "download_file": download_file,
        "upload_file": upload_file,
        "search_s3_objects": search_s3_objects
    }
    
    # ChatGPT函数调用
    def chat_completion_request(messages, functions=None, function_call='auto', 
                                model_name=GPT_MODEL):
    
        if functions is not None:
            return openai.ChatCompletion.create(
                model=model_name,
                messages=messages,
                functions=functions,
                function_call=function_call)
        else:
            return openai.ChatCompletion.create(
                model=model_name,
                messages=messages)
    
    # 主要对话函数
    def run_conversation(user_input, topic="S3 bucket functions.", is_log=False):
    
        system_message=f"不要假设要插入函数的值。如果用户请求不明确,请请求澄清。如果用户要求与{topic}无关的问题,请告诉他您的范围是{topic}。"
    
        messages = [{"role": "system", "content": system_message},
                    {"role": "user", "content": user_input}]
    
        # 调用模型以获取响应
        response = chat_completion_request(messages, functions=functions)
        response_message = response['choices'][0]['message']
    
        if is_log:
            print(response['choices'])
    
        # 检查GPT是否想要调用函数
        if response_message.get("function_call"):
            function_name = response_message['function_call']['name']
            function_args = json.loads(response_message['function_call']['arguments'])
    
            # 调用函数
            function_response = available_functions[function_name](**function_args)
    
            # 将响应添加到对话中
            messages.append(response_message)
            messages.append({
                "role": "function",
                "name": function_name,
                "content": function_response,
            })
    
            # 再次调用模型以总结结果
            second_response = chat_completion_request(messages)
            final_message = second_response['choices'][0]['message']['content']
        else:
            final_message = response_message['content']
    
        return final_message
    
    # 测试S3存储桶助手
    # 列出和搜索
    print(run_conversation('列出我的S3存储桶'))
    
    search_file = '<文件名>'
    print(run_conversation(f'在所有存储桶中搜索文件{search_file}'))
    
    search_word = '<文件名部分>'
    bucket_name = '<存储桶名称>'
    print(run_conversation(f'在{bucket_name}中搜索包含{search_word}的文件'))
    
    # 检查模型是否会拒绝无关的任务
    print(run_conversation('今天天气如何'))
    
    # 下载文件
    search_file = '<文件名>'
    bucket_name = '<存储桶名称>'
    local_directory = '<本地目录路径>'
    print(run_conversation(f'从{bucket_name}存储桶中下载{search_file}到{local_directory}目录'))
    
    # 上传文件
    local_file = '<文件名>'
    bucket_name = '<存储桶名称>'
    print(run_conversation(f'将{local_file}上传到{bucket_name}存储桶'))

    结论

    通过本教程,您已经学会了如何使用ChatGPT自动化AWS S3存储桶任务。这个互动的示例展示了如何连接ChatGPT的自然语言处理能力和AWS S3存储桶的功能,以执行列出、搜索、下载和上传文件等操作。这种自动化可以节省大量时间,提高工作效率。

  • 如何使用Neon Serverless Postgres构建OpenAI矢量数据库

    随着人工智能和深度学习技术的迅猛发展,矢量数据在数据科学和应用领域中扮演着越来越重要的角色。从自然语言处理到图像识别,矢量数据是我们理解和处理各种信息的关键。然而,有效地存储和查询矢量数据一直是一个挑战,特别是在云计算环境中。在本教程中,我们将介绍如何使用Neon Serverless Postgres构建OpenAI矢量数据库,以便存储和查询OpenAI生成的嵌入向量。

    准备工作

    在开始之前,请确保您具备以下条件:

    1. 一个Neon Postgres数据库。您可以在几个简单的步骤中创建一个带有现成的neondb数据库的帐户并设置项目。有关说明,请参阅注册并创建您的第一个项目。
    2. 您的Neon数据库连接字符串。您可以从Neon仪表板上的连接详细信息小部件中复制它。请参阅从任何应用程序连接。
    3. pgvector扩展。在Neon中运行CREATE EXTENSION vector;来安装扩展。有关说明,请参阅启用pgvector扩展。
    4. 您的OpenAI API密钥。
    5. Python和pip。

    安装所需模块

    这个笔记本需要openai、psycopg2、pandas、wget和python-dotenv模块。您可以使用pip来安装它们:

    !pip install openai psycopg2 pandas wget python-dotenv

    准备您的OpenAI API密钥

    为了生成文档和查询的向量,需要一个OpenAI API密钥。

    如果您没有OpenAI API密钥,请从https://platform.openai.com/account/api-keys获取一个。

    将OpenAI API密钥添加为操作系统环境变量或在会话中提供它。如果您定义了一个环境变量,请将变量命名为OPENAI_API_KEY。

    测试您的OpenAI密钥

    import os
    from getpass import getpass
    
    # 检查是否将OPENAI_API_KEY设置为环境变量
    if os.getenv("OPENAI_API_KEY") is not None:
        print("您的OPENAI_API_KEY已准备就绪")
    else:
        # 如果没有,请提示输入
        api_key = getpass("请输入您的OPENAI_API_KEY: ")
        if api_key:
            print("您的OPENAI_API_KEY现在可用于此会话")
            # 可选地,您可以将其设置为当前会话的环境变量
            os.environ["OPENAI_API_KEY"] = api_key
        else:
            print("您未输入OPENAI_API_KEY")

    连接到您的Neon数据库

    请在下面提供您的Neon数据库连接字符串,或者使用DATABASE_URL变量在.env文件中定义它。有关获取Neon连接字符串的信息,请参阅从任何应用程序连接。

    import os
    import psycopg2
    from dotenv import load_dotenv
    
    # 从.env文件加载环境变量
    load_dotenv()
    
    # 连接字符串可以直接在这里提供。
    # 用您的Neon连接字符串替换下一行。
    connection_string = "postgres://<user>:<password>@<hostname>/<dbname>"
    
    # 如果上面没有直接提供connection_string,
    # 则检查环境或.env中是否设置了DATABASE_URL。
    if not connection_string:
        connection_string = os.environ.get("DATABASE_URL")
    
        # 如果两种方法都没有提供连接字符串,则引发错误。
        if not connection_string:
            raise ValueError("请在代码中或.env文件中提供有效的连接字符串作为DATABASE_URL。")
    
    # 使用连接字符串连接
    connection = psycopg2.connect(connection_string)
    
    # 创建新的游标对象
    cursor = connection.cursor()
    
    # 测试数据库连接
    cursor.execute("SELECT 1;")
    result = cursor.fetchone()
    
    # 检查查询结果
    if result == (1,):
        print("您的数据库连接成功!")
    else:
        print("连接失败。")

    加载数据

    将预先计算的矢量数据加载到名为articles的数据库表中,从.csv文件加载数据。由于有25000条记录,因此预计操作将花费几分钟。

    import io
    
    # 本地CSV文件的路径
    csv_file_path = '../../data/vector_database_wikipedia_articles_embedded.csv'
    
    # 定义一个生成器函数来处理csv文件
    def process_file(file_path):
        with open(file_path, 'r', encoding='utf-8') as file:
            for line in file:
                yield line
    
    # 创建一个StringIO对象来存储修改后的行
    modified_lines = io.StringIO(''.join(list(process_file(csv_file_path))))
    
    # 创建用于copy_expert的COPY命令
    copy_command = '''
    COPY public.articles (id, url, title, content, title_vector, content_vector, vector_id)
    FROM STDIN WITH (FORMAT CSV, HEADER true, DELIMITER ',');
    '''
    
    # 使用copy_expert执行COPY命令
    cursor.copy_expert(copy_command, modified_lines)
    
    # 提交更改
    connection.commit()
    
    # 检查记录数以确保数据已加载。应该有25000条记录。
    count_sql = """select count(*) from public.articles;"""
    cursor.execute(count_sql)
    result = cursor.fetchone()
    print(f"记录数:{result[0]}")

    搜索您的数据

    在将数据存储在Neon数据库中后,您可以查询最近的邻居。

    首先,定义query_neon函数,当运行矢量相似性搜索时,该函数将执行。该函数基于用户的查询创建一个嵌入向量,准备SQL查询,并使用嵌入向量运行SQL查询。您加载到数据库中的预计算嵌入是使用text-embedding-ada-002 OpenAI模型创建的,因此您必须使用相同的模型来创建用于相似性搜索的嵌入。

    提供了一个vector_name参数,允许基于“标题”或“内容”搜索。

    def query_neon(query, collection
    
    _name, vector_name="title_vector", top_k=20):
    
        # 从用户查询创建嵌入向量
        embedded_query = openai.Embedding.create(
            input=query,
            model="text-embedding-ada-002",
        )["data"][0]["embedding"]
    
        # 将嵌入查询转换为PostgreSQL兼容格式
        embedded_query_pg = "[" + ",".join(map(str, embedded_query)) + "]"
    
        # 创建SQL查询
        query_sql = f"""
        SELECT id, url, title, l2_distance({vector_name},'{embedded_query_pg}'::VECTOR(1536)) AS similarity
        FROM {collection_name}
        ORDER BY {vector_name} <-> '{embedded_query_pg}'::VECTOR(1536)
        LIMIT {top_k};
        """
        # 执行查询
        cursor.execute(query_sql)
        results = cursor.fetchall()
    
        return results
    
    # 基于title_vector嵌入运行相似性搜索
    query_results = query_neon("Greek mythology", "Articles")
    for i, result in enumerate(query_results):
        print(f"{i + 1}. {result[2]} (分数: {round(1 - result[3], 3)})")
    
    # 基于content_vector嵌入运行相似性搜索
    query_results = query_neon("Famous battles in Greek history", "Articles", "content_vector")
    for i, result in enumerate(query_results):
        print(f"{i + 1}. {result[2]} (分数: {round(1 - result[3], 3)})")

    结论

    通过使用Neon Serverless Postgres构建OpenAI矢量数据库,您可以有效地存储和查询OpenAI生成的嵌入向量。这为您提供了强大的工具来处理和分析文本数据,从而使您能够更好地理解和利用矢量数据的潜力。

  • 如何利用Neon将Postgres打造成矢量数据库

    随着人工智能和深度学习技术的迅猛发展,矢量数据在数据科学和应用领域中扮演着越来越重要的角色。从自然语言处理到图像识别,矢量数据是我们理解和处理各种信息的关键。然而,有效地存储和查询矢量数据一直是一个挑战,特别是在云计算环境中。在本教程中,我们将介绍一种名为Neon的Serverless Postgres,它是专门为云计算而构建的,支持矢量搜索,使Postgres成为了一种强大的矢量数据库。

    什么是Neon?

    Neon是一款专为云计算设计的Serverless Postgres数据库。它采用了一种先进的架构,将计算和存储分离,为开发者提供了现代化的功能,例如自动扩展、数据库分支、按需扩展等。这些功能使Neon成为了处理矢量数据的理想选择。

    矢量搜索支持

    一个令人兴奋的特性是,Neon支持使用开源的pgvector PostgreSQL扩展进行矢量搜索。这意味着您可以将Postgres作为矢量数据库来存储和查询嵌入式矢量数据。这对于需要处理大规模矢量数据集的应用程序来说是非常有价值的。

    在OpenAI中使用Neon Postgres和pgvector进行语义搜索

    在这个笔记本中,您将学习如何:

    1. 利用OpenAI API创建嵌入向量。
    2. 将这些嵌入向量存储在Neon Serverless Postgres数据库中。
    3. 将原始文本查询转换为嵌入向量,使用OpenAI API。
    4. 利用pgvector扩展和Neon执行矢量相似性搜索。

    这将使您能够构建强大的语义搜索应用程序,用于处理文本或其他嵌入式数据的相似性匹配。

    Neon的扩展支持

    Neon提供了一系列强大的扩展功能,以帮助您扩展和优化您的AI应用程序。以下是其中一些功能:

    自动扩展

    如果您的AI应用程序在某些时段负载较重,或者在不同时间负载不同,Neon可以在不需要手动干预的情况下自动扩展计算资源。在不活动时期,Neon可以缩减到零,从而降低成本。

    即时只读副本

    Neon支持即时只读副本,这些副本是独立的只读计算实例,专为执行与读写计算相同数据的读取操作而设计。通过只读副本,您可以将读取操作从读写计算实例中卸载到专用的只读计算实例上,从而提高AI应用程序的性能。

    Neon无服务器驱动程序

    Neon支持一个低延迟的无服务器PostgreSQL驱动程序,适用于JavaScript和TypeScript应用程序。这使得从无服务器和边缘环境查询数据成为可能,从而实现了低于10毫秒的查询。

    更多示例

    Neon提供了许多示例,帮助您更好地了解如何使用它构建AI应用程序:

    1. 构建AI驱动的语义搜索应用程序:提交一个创业点子,获得YCombinator之前投资的类似点子列表。
    2. 构建AI驱动的聊天机器人:一个将Postgres作为矢量数据库的Postgres问答聊天机器人。
    3. Vercel Postgres pgvector入门:在Vercel Postgres上使用Neon进行矢量相似性搜索。

    其他资源

    除了上述示例之外,您还可以参考以下资源来更深入地了解如何使用Neon构建AI应用程序:

    Neon是构建强大AI应用程序的利器,它将Postgres转变为了一个功能强大的矢量数据库,为处理嵌入式矢量数据提供了无与伦比的便捷性和性能。

    希望这篇教程对您有所帮助,让您更好地了解如何使用Neon和pgvector来构建强大的AI应用程序。如果您有任何问题或需要进一步的帮助,请随时联系我们。

  • 如何使用Weights & Biases Weave监控OpenAI API调用

    在数字化时代,人工智能和深度学习技术正迅速发展,而OpenAI一直是这一领域的领先者之一。但是,随着项目和团队的增长,对OpenAI API的使用情况的监控变得越来越重要。本教程将向您展示如何使用Weights & Biases Weave来监控OpenAI API调用,了解项目和团队如何使用语言模型(LLMs),并可视化这些数据以便更好地管理您的项目。

    开篇故事

    想象一下,您正在领导一个人工智能团队,团队成员在多个项目中使用OpenAI的语言模型。您关心的不仅是项目的进展,还包括如何优化API调用、控制成本、降低延迟和提高吞吐量。然而,要了解这些关键指标,需要大量的数据和分析工作。这时,Weights & Biases Weave登场了。它为您提供了一个强大的工具,可以自动跟踪LLM的使用情况,记录有关成本、延迟和吞吐量的关键指标,并为您提供交互式的仪表板,以便更轻松地与团队共享进展。

    步骤0:设置

    在开始之前,您需要进行一些设置。首先,安装所需的依赖项,然后登录到Weights & Biases(W&B)以保存和分享您的工作,并与OpenAI进行身份验证。

    # 如果尚未安装
    !pip install -qqq weave openai tiktoken wandb
    
    import wandb
    wandb.login()
    
    import weave
    import os
    WANDB_BASE_URL = "https://api.wandb.ai"
    os.environ["WANDB_BASE_URL"] = WANDB_BASE_URL
    
    # 与OpenAI进行身份验证
    from getpass import getpass
    
    if os.getenv("OPENAI_API_KEY") is None:
      os.environ["OPENAI_API_KEY"] = getpass("从 https://platform.openai.com/account/api-keys 复制粘贴您的OpenAI密钥\n")
    assert os.getenv("OPENAI_API_KEY", "").startswith("sk-"), "这似乎不是一个有效的OpenAI API密钥"
    print("OpenAI API密钥已配置")

    步骤1:配置数据流和存储在W&B中

    在W&B中,您需要配置数据流和存储,以便记录OpenAI API调用的日志。设置WB_ENTITY为您的W&B用户名或团队名称,WB_PROJECT为项目名称,STREAM_NAME为记录表的名称。

    WB_ENTITY = # 设置为您的W&B用户名或团队名称
    WB_PROJECT = "weave"  # 为此工作创建顶级目录
    STREAM_NAME = "openai_logs"  # 记录表,用于存储OpenAI API调用的日志

    步骤2:调用init_monitor()

    要开始监控OpenAI API的使用情况,请调用init_monitor(<stream>),其中<stream>的格式为<wandb_team_or_user>/<wandb_project>/<stream_name>。该流会记录并存储所有OpenAI API调用的日志。

    from weave.monitoring import openai, init_monitor
    m = init_monitor(f"{WB_ENTITY}/{WB_PROJECT}/{STREAM_NAME}")
    
    # 为简单起见,指定一个模型
    OPENAI_MODEL = 'gpt-3.5-turbo'
    
    # 使用一些示例日志进行预填充
    r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[{"role": "user", "content": "hello world!"}])
    r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[{"role": "user", "content": "what is 2+2?"}])

    步骤3:预览监控仪表板

    点击上面的链接预览数据流,然后在右侧边栏中点击“OpenAI Monitor Board”以为此数据流创建Weave Board。

    步骤4:探索和了解您的LLM使用情况

    要保存您的工作,请单击页面顶部的自动生成名称以重命名面板。要分享您的面板,请单击右上角的“发布”。

    通过以下方式,您可以实时可视化您的工作:

    • 将面板保持在单独的选项卡中,并刷新以查看最新数据。
    • 随时重命名面板以更容易参考,并“发布”该版本以与他人共享链接。
    • 通过从weave.wandb.ai导航到相关的W&B实体和W&B项目名称,找到以前保存的面板。
    • 或者打开一个新的面板模板的实例,以从迄今为止积累的所有数据开始。

    接下来,我们将举例说明一些您可以跟踪OpenAI API调用的方式。根据您的用例,还有许多其他可能性,我们迫不及待地期待您从这些起始模板中创建的内容。

    示例

    示例0:记录提示及其完成

    监视ChatCompletion请求并打印相应的响应,仅提取完成的文本。

    response = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[
            {"role": "user", "content": f"生命、宇宙和一切的意义是什么?"},
        ])
    print(response['choices'][0]['message']['content'])

    示例1:将相关参数作为属性进行跟踪

    将感兴趣的参数分解并将其作为日志记录的属性进行跟踪。在这里,我们将“系统提示”与“提示模板”和“方程”参数分开跟踪。这次我们将打印ChatCompletion调用的完整结构化响应。

    system_prompt = "你总是写成要点"
    prompt_template = '逐步解决以下方程:{equation}'
    params = {'equation': '4 * (3 - 1)'}
    openai.ChatCompletion.create(model=OPENAI_MODEL,
                                 messages=[
                                        {"role": "system", "content": system_prompt},
                                        {"role": "user", "content": prompt_template.format(**params)},
                                    ],
                                 # 您可以在日志记录中添加其他属性
                                 # 有关更多示例,请参见monitor_api笔记本
                                 monitor_attributes={
                                     'system_prompt': system_prompt,
                                     'prompt_template': prompt_template,
                                     'params': params
                                 })

    示例2:记录一系列消息的实时流

    监视消息流并将结果记录为单个记录。请注意:此格式不计算标记数。

    from weave.monitoring.openai import message_from_stream
    r = openai.ChatCompletion.create(model=OPENAI_MODEL, messages=[
            {"role": "system", "content": "你是一个机器人,只说像机器人一样的话,比如滴滴滴。"},
            {"role": "user", "content": "给我讲一个50字的故事。"},
        ], stream=True)
    for s in message_from_stream(r):
        print(s, end='')

    示例3:结构化提示工程实验

    在这里,我们比较了一些系统提示、用户问题和目标受众的玩具选项。尝试您自己的实验,看看在探索Board并按不同参数分组时是否会出现有趣的见解。

    def explain_math(system_prompt, prompt_template, params):
        openai.ChatCompletion.create(model=OPENAI_MODEL,
                                 messages=[
                                        {"role": "system", "content": system_prompt},
                                        {"role": "user", "content": prompt_template.format(**params)},
                                    ],
                                 # 您可以在日志记录中添加其他属性
                                 # 有关更多示例,请参见monitor_api笔记本
                                 monitor_attributes={
                                     'system_prompt': system_prompt,
                                     'prompt_template': prompt_template,
                                     'params': params
                                 })
    
    # 随时替换您自己的提示 :)
    system_prompts = ["你写得非常花哨和诗意", "你非常直接和准确", "平衡简洁和见解"]
    prompt_template = '解释以下方程的解给{audience}听:{equation}'
    equations = ['x^2 + 4x + 9 = 0', '15 * (2 - 6) / 4']
    audience = ["新生", "数学天才"]
    
    for system_prompt in system_prompts:
        for equation in equations:
            for person in audience:
                params = {"equation" : equation, "audience" : person}
                explain_math(system_prompt, prompt_template, params)

    总结

    通过Weights & Biases Weave,您可以轻松监控OpenAI API的使用情况,了解项目和团队如何使用LLMs,并可视化这些数据以便更好地管理您的项目。这种实时监控和数据分析工具将有助于您更好地掌握AI项目的进展,优化API调用,并降低成本。

    希望这个教程对您有所帮助,让您更好地利用Weights & Biases Weave来管理和优化您的OpenAI API项目。如果您有任何问题或需要进一步的帮助,请随时联系我们。

  • 深度学习与自然语言处理:LangChain、Deep Lake和OpenAI实现问答系统

    深度学习和自然语言处理领域一直是人工智能中备受关注的话题。如何构建一个强大的问答系统一直是研究人员和工程师们的追求。本教程将向您展示如何使用LangChain、Deep Lake和OpenAI实现一个强大的问答系统,让您的计算机能够回答各种问题。

    开篇故事

    在数字化时代,信息爆炸式增长,我们每天都会遇到各种各样的问题,从学术研究到日常生活。有时,我们需要快速准确地找到答案,而这正是问答系统的价值所在。想象一下,您正在研究一个复杂的问题,但是文献太多,难以阅读和理解。这时,一个高效的问答系统可以帮助您迅速找到相关信息,加速研究进程。或者,您可能只是想了解一些日常生活中的知识,比如为什么军事不说24:00。无论是学术研究还是日常生活,一个强大的问答系统都能提供帮助。

    步骤1:安装所需的软件包

    首先,让我们安装需要的软件包,以便开始构建我们的问答系统。在这个教程中,我们将使用LangChain、Deep Lake和OpenAI等工具。

    !pip install deeplake langchain openai tiktoken

    步骤2:身份验证

    接下来,我们需要提供您的OpenAI API密钥,以便进行身份验证。这个密钥将允许我们使用OpenAI的功能来增强我们的问答系统。

    import getpass
    import os
    
    os.environ['OPENAI_API_KEY'] = getpass.getpass()

    步骤3:加载Deep Lake文本数据集

    我们将使用Deep Lake中的文本数据集作为我们的知识库,以便问答系统可以从中提取信息。在这个示例中,我们使用了一个包含20000个样本的子集,这个数据集是来自cohere-wikipedia-22的。

    import deeplake
    
    ds = deeplake.load("hub://activeloop/cohere-wikipedia-22-sample")
    ds.summary()

    步骤4:LangChain的Deep Lake向量存储

    现在,让我们为LangChain的Deep Lake向量存储定义一个路径。这个向量存储将包含我们的文本嵌入。

    dataset_path = 'wikipedia-embeddings-deeplake'

    接下来,我们将设置OpenAI的文本嵌入模型,并初始化一个Deep Lake向量存储。

    from langchain.embeddings.openai import OpenAIEmbeddings
    from langchain.vectorstores import DeepLake
    
    embedding = OpenAIEmbeddings(model="text-embedding-ada-002")
    db = DeepLake(dataset_path, embedding=embedding, overwrite=True)

    现在,让我们将样本数据逐批添加到向量存储中。

    from tqdm.auto import tqdm
    
    batch_size = 100
    nsamples = 10  # 用于测试。将其替换为len(ds)以添加所有数据
    
    for i in tqdm(range(0, nsamples, batch_size)):
        i_end = min(nsamples, i + batch_size)
        batch = ds[i:i_end]
        id_batch = batch.ids.data()["value"]
        text_batch = batch.text.data()["value"]
        meta_batch = batch.metadata.data()["value"]
        db.add_texts(text_batch, metadatas=meta_batch, ids=id_batch)

    步骤5:在数据库上运行用户查询

    现在,我们的问答系统已经构建好了,我们可以在数据库上运行用户的查询。我们使用了GPT-3.5-Turbo作为我们的语言模型,它可以根据用户的查询来查找最相关的信息。

    from langchain.chains import RetrievalQA
    from langchain.chat_models import ChatOpenAI
    
    qa = RetrievalQA.from_chain_type(llm=ChatOpenAI(model='gpt-3.5-turbo'), chain_type="stuff", retriever=db.as_retriever())
    
    query = '为什么军事不说24:00?'
    answer = qa.run(query)

    通过这个问答系统,您可以轻松地获取关于各种话题的答案,无论是学术研究还是日常生活中的疑问。问答系统的强大之处在于它可以从大量的文本数据中提取准确的信息,为您提供及时的帮助。

    总结

    在这个教程中,我们学习了如何使用LangChain、Deep Lake和OpenAI构建一个强大的问答系统。通过加载文本数据集、初始化向量存储和使用语言模型,我们可以实现一个高效的问答系统,可以回答各种各样的问题。无论您是在进行学术研究还是在解决日常问题,这个问答系统都可以为您提供有力的支持。

  • 如何使用搜索API和重新排序来进行问题回答

    有时候,寻找相关信息就像大海捞针一样困难,但不要绝望,GPT可以为我们做很多工作。在本指南中,我们将探讨一种使用各种人工智能技术增强现有搜索系统的方法,帮助我们筛选信息中的噪音。

    GPT检索信息的两种方式如下:

    1. 模仿人类浏览:GPT触发搜索,评估结果,如果必要的话,修改搜索查询。它还可以跟踪特定的搜索结果,形成思维链,就像人类用户会做的那样。
    2. 使用嵌入进行检索:为你的内容和用户查询计算嵌入,然后根据余弦相似度测量选择与用户查询最相关的内容。这种技术在像Google这样的搜索引擎中被广泛使用。

    这两种方法都有潜力,但各自都有缺点:第一种方法由于其迭代性质可能较慢,第二种方法需要预先嵌入整个知识库,不断嵌入新内容并维护矢量数据库。

    通过结合这些方法,并从重新排序方法中汲取灵感,我们找到了一种介于两者之间的方法。这种方法可以在任何现有搜索系统的基础上实施,如Slack搜索API或带有私有数据的内部ElasticSearch实例。它的工作原理如下:

    步骤1:搜索

    1. 用户提出问题。
    2. GPT生成一组潜在的查询。
    3. 并行执行搜索查询。

    步骤2:重新排序

    1. 使用每个结果的嵌入来计算与生成的假设理想答案对用户问题的语义相似性。
    2. 根据此相似性度量对结果进行排序和过滤。

    步骤3:回答

    1. 针对前几名搜索结果,模型生成回答用户问题的答案,包括引用和链接。

    这种混合方法提供了相对较低的延迟,并且可以集成到任何现有的搜索端点中,而无需维护矢量数据库。下面让我们深入了解吧!我们将以News API作为示例领域进行搜索。

    设置

    除了你的OPENAI_API_KEY,你还需要在环境中包含一个NEWS_API_KEY。你可以在这里获取API密钥。

    %env NEWS_API_KEY = YOUR_NEWS_API_KEY
    
    # 依赖项
    from datetime import date, timedelta  # 用于获取最近新闻的日期处理
    from IPython import display  # 用于漂亮的打印
    import json  # 用于解析JSON API响应和模型输出
    from numpy import dot  # 用于余弦相似度
    import openai  # 用于使用GPT和获取嵌入
    import os  # 用于加载环境变量
    import requests  # 用于进行API请求
    from tqdm.notebook import tqdm  # 用于显示进度条

    以上是初始化所需的依赖项和环境变量设置。接下来,我们将逐步执行三个主要步骤:搜索、重新排序和回答。

    步骤1:搜索

    一切都始于用户的问题。首先,用户提出一个问题,然后我们使用GPT生成一系列多样化的查询,以尽可能详尽的方式回答问题。

    # 用户提出问题
    USER_QUESTION = "谁赢得了NBA总冠军?最有价值球员是谁?告诉我一些关于最后一场比赛的情况。"
    
    # 为了尽可能详尽,我们使用模型生成基于此问题的各种查询。
    QUERIES_INPUT = f"""
    你有访问一个返回最近新闻文章的搜索API。
    生成一个与这个问题相关的搜索查询数组。
    为查询使用相关关键字的变体,尽量做到通用。
    包括尽可能多的查询,包括和不包括术语。
    例如,包括像['关键词1 关键词2','关键词1','关键词2']这样的查询。
    要有创造性。包括的查询越多,找到相关结果的可能性就越大。
    
    用户问题: {USER_QUESTION}
    
    格式: {{"queries": ["query_1", "query_2", "query_3"]}}
    """
    
    queries = json_gpt(QUERIES_INPUT)["queries"]
    
    # 让我们也包括原始问题以确保全面。
    queries.append(USER_QUESTION)
    
    queries

    以上代码生成了一系列搜索查询,这些查询包括与用户问题相关的多个关键字和变体。这些查询将在接下来的搜索步骤中用于检索新闻文章。

    接下来,让我们运行这些搜索查询以获取新闻文章。

    def search_news(
        query: str,
        news_api_key: str = news_api_key,
        num_articles: int = 50,
        from_datetime: str = "2023-06-01",  # 2023年NBA总决赛于2023年6月举行
        to_datetime: str = "2023-06-30",
    ) -> dict:
        response = requests.get(
            "https://newsapi.org/v2/everything",
            params={
                "q": query,
                "apiKey": news_api_key,
                "pageSize": num_articles,
                "sortBy": "relevancy",
                "from": from_datetime,
                "to": to_datetime,
            },
        )
    
        return response.json()
    
    articles = []
    
    for query in tqdm(queries):
        result = search_news(query)
        if result["status"] == "ok":
            articles = articles + result["articles"]
    
        else:
            raise Exception(result["message"])
    
    # 移除重复文章
    articles = list({article["url"]: article for article in articles}.values())
    
    print("文章总数:", len(articles))
    print("查询1的前5篇文章:", "\n")
    
    for article in articles[0:5]:
        print("标题:", article["title"])
        print("描述:", article["description"])
        print("内容:", article["content"][0:100] + "...")
        print()

    以上代码执行了搜索查询并获取了与这些查询相关的新闻文章。注意,为了减少重复,我们在获取文章后删除了重复的文章。

    现在,我们已经获得了新闻文章,但很多时候搜索查询会返回大量结果,其中许多与用户最初提出的问题无关。为了提高最终答案的质量,我们将使用嵌入来重新排序和过滤结果。

    步骤2:重新排序

    受HyDE(Gao等人)启发,我们首先生成一个假设理想答案,用于对比和重新排列我们的结果。这有助于优先考虑看起来是好答案的结果,而不是与我们的问题类似的结果。下面是我们用来生成假设答案的提示。

    HA_INPUT = f"""
    生成一个假设的答案,以回答用户的问题。这个答案将用于排名搜索结果。
    假装你拥有回答所需的所有信息,但不要使用任何实际的事实。而是使用占位符
    比如,NAME在某地做了某事,或者NAME在某地说了某话。
    
    用户问题: {USER_QUESTION}
    
    格式: {{"hypotheticalAnswer": "假设答案文本"}}
    """
    
    hypothetical_answer = json_gpt(HA_INPUT)["hypotheticalAnswer"]
    
    hypothetical_answer

    以上代码生成了一个假设答案,这个答案包含了关于NBA总决赛的信息,但没有使用实际的事实,而是使用了占位符。接下来,我们将为搜索结果和假设答案生成嵌入,然后计算这些嵌入之间的余弦距离,得到语义相似性度量。请注意,由于OpenAI嵌入在API中返回时已经归一化,我们可以简单地计算点积来代替进行完整的余弦相似性计算。

    hypothetical_answer_embedding = embeddings(hypothetical_answer)[0]
    article_embeddings = embeddings(
        [
            f"{article['title']} {article['description']} {article['content'][0:100]}"
            for article in articles
        ]
    )
    
    # 计算余弦相似度
    cosine_similarities = []
    for article_embedding in article_embeddings:
        cosine_similarities.append(dot(hypothetical_answer_embedding, article_embedding))
    
    cosine_similarities[0:10]

    以上代码计算了假设答案与每篇文章的嵌入之间的余弦相似性。这些相似性分数将用于对结果进行排序和过滤。

    最后,我们使用这些相似性分数对结果进行排序和过滤。

    scored_articles = zip(articles, cosine_similarities)
    
    # 根据余弦相似度对文章进行排序
    sorted_articles = sorted(scored_articles, key=lambda x: x[1], reverse=True)
    
    # 打印前5篇文章
    print("前5篇文章:", "\n")
    
    for article, score in sorted_articles[0:5]:
        print("标题:", article["title"])
        print("描述:", article["description"])
        print("内容:", article["content"][0:100] + "...")
        print("分数:", score)
        print()

    以上代码对文章进行了排序,按照与假设答案的语义相似性得分降序排列。这些排序后的文章看起来与我们最初的查询更相关。现在,让我们使用前5个结果生成最终答案。

    步骤3:回答

    formatted_top_results = [
        {
            "title": article["title"],
            "description": article["description"],
            "url": article["url"],
        }
        for article, _score in sorted_articles[0:5]
    ]
    
    ANSWER_INPUT = f"""
    根据给定的搜索结果为用户的问题生成答案。 
    顶部结果: {formatted_top_results}
    用户问题: {USER_QUESTION}
    
    在答案中包括尽可能多的信息。引用相关的搜索结果URL作为Markdown链接。
    """
    
    completion = openai.ChatCompletion.create(
        model=GPT_MODEL,
        messages=[{"role": "user", "content": ANSWER_INPUT}],
        temperature=0.5,
        stream=True,
    )
    
    text = ""
    for chunk in completion:
        text += chunk.choices[0].delta.get("content", "")
        display.clear_output(wait=True)
        display.display(display.Markdown(text))

    以上代码使用前5个搜索结果生成了最终的答案,其中包括引用了相关搜索结果的链接。

    至此,我们已经完成了搜索、重新排序和回答的所有步骤。下面,让我们根据以上内容编写一篇优秀的教程文章,满足指定的要求。

    如何使用搜索API和重新排序进行问题回答

    开篇故事

    搜索相关信息有时候就像在庞大的信息海洋中寻找一根针一样困难。但不要担心,人工智能技术可以帮助我们在这个任务中取得很大进展。在本文中,我们将探讨一种方法,利用搜索API和嵌入技术,以及重新排序方法,来实现更准确的问题回答。

    故事开始于一个充满好奇心的用户,他有一个问题:谁赢得了NBA总冠军?最有价值球员是谁?告诉我一些关于最后一场比赛的情况。这个问题看似简单,但

    要从海量的新闻文章中找到相关的信息可能会让人望而却步。

    步骤1:搜索

    首先,用户提出了一个问题。这个问题是我们开始寻找答案的出发点。但为了尽可能全面地查找相关信息,我们使用人工智能模型生成了多个不同的查询,这些查询都与用户的问题相关。这些查询包括谁赢得了NBA总冠军、NBA总决赛的MVP是谁,以及最后一场比赛的概述等等。这些查询一起被发送到搜索API中,以并行方式执行搜索。

    步骤2:重新排序

    一旦我们获得了搜索结果,接下来的步骤是对这些结果进行重新排序。这一步骤受到了HyDE(Gao等人)的启发。我们首先生成了一个假设的理想答案,该答案包含了关于NBA总决赛的信息,但没有使用任何实际的事实,而是使用了占位符。然后,我们为搜索结果和假设答案生成了嵌入,这些嵌入可以帮助我们度量它们之间的语义相似性。最终,我们使用语义相似性分数对结果进行排序,以找出最相关的答案。

    步骤3:回答

    最后,我们使用排名最高的搜索结果生成最终的答案。这个答案包括了与NBA总决赛有关的信息,并引用了相关搜索结果的链接,以便用户可以深入了解。

    通过这个混合方法,我们能够在相对低的延迟下获得高质量的答案,而无需维护一个向量数据库。这种方法可以集成到任何现有的搜索系统中,无论是Slack搜索API还是具有私有数据的内部ElasticSearch实例。

    希望本文对你理解如何使用搜索API和重新排序来回答问题有所帮助!

  • 如何充分利用大型语言模型

    在数字时代,人工智能的崛起给我们带来了前所未有的可能性。而在这个无处不在的AI时代,大型语言模型如GPT-3则成为了实现许多创意和任务的得力助手。无论是在编程、创作、还是解答问题上,大型语言模型都能发挥巨大的作用。本文将深入探讨大型语言模型的工作原理以及如何充分掌握它们,以提高工作效率和创造力。

    什么是大型语言模型?

    大型语言模型是将文本映射到文本的函数。给定一个文本输入,大型语言模型会预测接下来应该出现的文本。这些模型之所以如此神奇,是因为它们通过在大量文本上训练来最小化预测误差,从而学到了对这些预测有用的概念。这包括拼写、语法、改写、回答问题、进行对话、以及多种语言和编程等等能力。所有这些能力都不是显式编程的结果,而是通过训练而来。

    如何控制大型语言模型?

    在大型语言模型的所有输入中,最具影响力的是文本提示。

    大型语言模型可以通过以下几种方式来引导生成输出:

    1. 指令提示:告诉模型你想要什么
    2. 完成提示:诱使模型完成你想要的开头
    3. 演示提示:向模型展示你想要的内容,可以使用以下方式之一:
      • 在提示中提供一些示例
      • 在微调训练数据集中提供数百或数千个示例
    4. 细调提示:通过足够多的训练示例,你可以对自定义模型进行细调。在这种情况下,指令是不必要的,因为模型可以从提供的训练数据中学到任务。然而,为了告诉模型何时结束提示并开始输出,包括分隔符序列是有帮助的。

    下面分别介绍这些方式的示例。

    指令提示

    指令遵循模型(例如,text-davinci-003或任何以text-开头的模型)是专门设计用于遵循指令的。在提示的顶部(或底部,或两者),写下你的指令,模型会尽力遵循指令然后停止。指令可以详细,所以不要害怕写一段详细说明你想要的输出。

    示例指令提示:

    提取以下引语中的作者名字。

    “有些人理论认为,智慧的物种在扩展到外太空之前就会灭绝。如果他们说得对,那么夜空的寂静就是墓地的寂静。”
    ― 特德·奇昂,《呼吸》
    输出:

    特德·奇昂

    完成提示示例

    完成提示利用了大型语言模型试图编写它认为最有可能出现的文本的方式。为了引导模型,可以尝试开始一个模式或句子,然后由你想要看到的输出来完成。相对于直接的指令,这种方式需要更多的注意和实验。此外,模型不一定知道何时停止,因此通常需要停止序列或后处理来截断生成的文本,以保持所需的输出。

    示例完成提示:

    “有些人理论认为,智慧的物种在扩展到外太空之前就会灭绝。如果他们说得对,那么夜空的寂静就是墓地的寂静。”
    ― 特德·奇昂,《呼吸》

    这段引文的作者是谁?
    输出:

    特德·奇昂

    演示提示示例(少样本学习)

    与完成提示类似,演示可以向模型展示你想要它做什么。这种方法有时被称为少样本学习,因为模型从提示中提供的一些示例中学习。

    示例演示提示:

    引文:
    “当理性的思维被迫一次又一次地面对不可能时,它别无选择,只能适应。”
    ― N.K.杰米辛,《第五季》
    作者:N.K.杰米辛

    引文:
    “有些人理论认为,智慧的物种在扩展到外太空之前就会灭绝。如果他们说得对,那么夜空的寂静就是墓地的寂静。”
    ― 特德·奇昂,《呼吸》
    作者:
    输出:

    特德·奇昂

    细调提示示例

    通过足够多的训练示例,你可以对自定义模型进行细调。在这种情况下,指令是不必要的,因为模型可以从提供的训练数据中学到任务。然而,可以包括分隔序列(例如,->或###或任何不常出现在输入中的字符串)来告诉模型提示何时结束并开始输出。如果没有分隔序列,模型可能会继续详细说明输入文本,而不是开始你想要看到的

    答案。

    示例细调提示(针对已经根据类似提示-完成对进行了自定义训练的模型):

    “有些人理论认为,智慧的物种在扩展到外太空之前就会灭绝。如果他们说得对,那么夜空的寂静就是墓地的寂静。”
    ― 特德·奇昂,《呼吸》

    代码能力

    大型语言模型不仅擅长处理文本,还可以处理代码。OpenAI的专用代码模型被称为Codex。

    Codex驱动着超过70种产品,包括:

    • GitHub Copilot(在VS Code和其他IDE中自动完成代码)
    • Pygma(将Figma设计转化为代码)
    • Replit(具有“解释代码”按钮和其他功能)
    • Warp(带有AI命令搜索的智能终端)
    • Machinet(编写Java单元测试模板)

    需要注意的是,与遵循指令的文本模型(例如text-davinci-002)不同,Codex没有经过训练以遵循指令。因此,设计良好的提示可能需要更多的细心。

    更多提示建议

    对于更多的提示示例,请访问OpenAI Examples。

    总之,输入提示是改进模型输出的最佳方法。你可以尝试以下技巧:

    • 提供更明确的指令。例如,如果你希望输出是一个逗号分隔的列表,请要求它返回一个逗号分隔的列表。如果你希望模型在不知道答案时说“我不知道”,请告诉它在不知道答案时说“我不知道”。
    • 提供更好的示例。如果你在提示中演示示例,请确保你的示例多样化且质量高。
    • 要求模型以专家的方式回答。明确要求模型产生高质量的输出,或者要求它以专家的方式撰写输出,可以促使模型提供更高质量的答案,因为它会认为专家会这样写。例如,“以下答案是正确的、高质量的,是由专家撰写的。”
    • 引导模型写下解释其推理过程的一系列步骤。例如,在最终答案之前加上类似“让我们逐步思考”的内容。提示模型在最终答案之前解释其推理过程可以增加最终答案一致性和正确性的可能性。

    结语

    大型语言模型如GPT-3是现代工作和创作的有力工具。通过充分了解它们的工作原理以及如何引导它们,你可以提高工作效率和创造力,从而更好地实现自己的目标。

  • 如何使用ChatGPT模型格式化输入

    在这个信息爆炸的时代,获取准确的信息和知识变得愈发重要。想象一下,你正在进行一项复杂的编程任务,但你不确定如何开始。这时,有一个智能助手可以为你提供指导和建议,无疑会让你事半功倍。ChatGPT就是这样一个强大的工具,它可以理解你的问题,并提供有价值的回答。本文将介绍如何格式化输入,以充分利用ChatGPT模型的能力。

    格式化输入

    ChatGPT模型的核心功能是接受一系列消息作为输入,并生成一条由AI编写的消息作为输出。要正确使用ChatGPT,你需要了解如何格式化输入消息。每个消息对象包括以下字段:

    • role:消息的角色,可以是系统、用户或助手。
    • content:消息的内容,包括问题、指令等。
    • name:可选字段,为消息的发送者命名。

    通常,一次对话会以系统消息开始,告诉助手如何行动,然后是交替的用户和助手消息。不过,并不一定要遵循这种格式。

    以下是一个示例Chat API调用,展示了消息的格式:

    MODEL = "gpt-3.5-turbo"
    response = openai.ChatCompletion.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Knock knock."},
            {"role": "assistant", "content": "Who's there?"},
            {"role": "user", "content": "Orange."},
        ],
        temperature=0,
    )
    
    response

    这个示例中,我们首先定义了模型(可以选择不同的模型),然后创建了一个包含系统、用户和助手消息的消息列表。通过这种方式,你可以与ChatGPT建立对话并获取回复。

    系统消息

    系统消息可以用来设定助手的行为和性格。要注意,不同版本的ChatGPT模型对系统消息的关注程度可能会有所不同。有时,将重要的指令放在用户消息中会更有效。

    以下是一个系统消息的示例,用来设定助手的性格:

    response = openai.ChatCompletion.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": "You are a friendly and helpful teaching assistant. You explain concepts in great depth using simple terms, and you give examples to help people learn. At the end of each explanation, you ask a question to check for understanding"},
            {"role": "user", "content": "Can you explain how fractions work?"},
        ],
        temperature=0,
    )

    这个系统消息告诉助手在解释概念时要深入浅出,并在每个解释结束时提出问题以检查理解。

    举例说明

    有时,向模型展示你想要的内容比告诉模型更容易。你可以使用模拟的示例消息来说明你的需求,这是一种有效的方法。

    以下是一个示例,展示了如何使用示例消息来教助手将商业术语翻译成简单的语言:

    response = openai.ChatCompletion.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": "You are a helpful, pattern-following assistant."},
            {"role": "user", "content": "Help me translate the following corporate jargon into plain English."},
            {"role": "assistant", "content": "Sure, I'd be happy to!"},
            {"role": "user", "content": "New synergies will help drive top-line growth."},
            {"role": "assistant", "content": "Things working well together will increase revenue."},
            {"role": "user", "content": "Let's circle back when we have more bandwidth to touch base on opportunities for increased leverage."},
            {"role": "assistant", "content": "Let's talk later when we're less busy about how to do better."},
            {"role": "user", "content": "This late pivot means we don't have time to boil the ocean for the client deliverable."},
        ],
        temperature=0,
    )

    在这个示例中,助手被引导使用简单的语言来解释商业术语。

    计算标记数量

    在使用ChatGPT模型时,消息中包含的标记数量会影响请求的成本、生成响应的时间以及响应是否会因达到最大标记限制而被截断。

    你可以使用以下函数来计算消息中包含的标记数量:

    import tiktoken
    
    def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
        # 计算标记数量的函数
        # ...

    这个函数会帮助你了解消息中的标记数量,以便更好地管理成本和响应时间。

    结束语

    ChatGPT模型是一个强大的工具,可以为你提供有用的回答和建议。通过了解如何格式化输入消息,你可以更有效地与模型互动,获得所需的帮助。不断尝试不同的指令和示例消息,以使模型更好地满足你的需求。

    现在,你已经了解了如何使用ChatGPT模型,开始享受其带来的便利吧!

  • 如何使用tiktoken计算文本中的标记数

    在现代自然语言处理中,标记是一种重要的概念,因为它们是语言模型处理文本的基本单元。本教程将向您展示如何使用OpenAI开源的快速分词工具tiktoken来计算文本中的标记数。通过了解文本中有多少标记,您可以:

    1. 判断文本是否太长以至于超出模型处理的限制。
    2. 了解使用OpenAI API调用的成本,因为计费是按标记计算的。

    1. 引言故事

    假设您是一名程序员,正在为自己的聊天应用项目编写代码。您已经集成了一个强大的自然语言处理模型,但现在需要确定用户输入的文本有多少标记,以便确保不会超出模型的限制。在解决这个问题之前,您曾不止一次被意想不到的文本长度问题困扰。一天,您在互联网上发现了一个名为tiktoken的开源工具,它能够快速准确地计算文本中的标记数。这似乎是您解决问题的完美工具,因此您决定深入了解它并开始使用它来解决您的标记计数问题。

    2. 安装tiktoken

    首先,您需要安装tiktoken工具。您可以使用pip来安装它:

    pip install --upgrade tiktoken

    安装完成后,您就可以开始使用tiktoken来计算文本中的标记数了。

    3. 导入tiktoken

    在您的Python项目中,首先导入tiktoken库:

    import tiktoken

    4. 加载编码

    接下来,您需要加载一个编码,以告诉tiktoken如何将文本转换为标记。不同的模型使用不同的编码。在本示例中,我们使用了“cl100k_base”编码,它适用于一些OpenAI模型,包括gpt-4和gpt-3.5-turbo。您可以这样加载编码:

    encoding = tiktoken.get_encoding("cl100k_base")

    5. 使用编码.encode()方法将文本转换为标记

    现在,您可以使用编码对象的.encode()方法将文本转换为标记。该方法将文本字符串转换为标记的整数列表。例如,如果我们有文本字符串"tiktoken is great!",您可以这样做:

    tokens = encoding.encode("tiktoken is great!")

    这将返回一个整数列表,代表文本中的标记。在这个示例中,返回的标记列表为[83, 1609, 5963, 374, 2294, 0]

    要计算文本中的标记数,只需查看标记列表的长度:

    num_tokens = len(tokens)

    这将给您文本中的标记数,本例中为6个标记。

    6. 使用编码.decode()方法将标记转换回文本

    如果您需要将标记转换回文本,可以使用编码对象的.decode()方法。例如,如果我们有标记列表[83, 1609, 5963, 374, 2294, 0],您可以这样做:

    text = encoding.decode([83, 1609, 5963, 374, 2294, 0])

    这将返回文本字符串"tiktoken is great!"。

    请注意,虽然.decode()方法可以应用于单个标记,但对于不在utf-8边界上的标记,它可能会导致信息丢失。对于单个标记,.decode_single_token_bytes()方法可以安全地将单个整数标记转换为其表示的字节。

    7. 比较不同编码

    不同编码在分割单词、分组空格和处理非英文字符方面有所不同。使用上述方法,我们可以比较不同编码在几个示例字符串上的效果。以下是一个比较不同编码的函数示例:

    def compare_encodings(example_string: str) -> None:
        """打印三种字符串编码的比较结果。"""
        # 打印示例字符串
        print(f'\n示例字符串: "{example_string}"')
        # 对于每个编码,打印标记数、标记整数和标记字节
        for encoding_name in ["r50k_base", "p50k_base", "cl100k_base"]:
            encoding = tiktoken.get_encoding(encoding_name)
            token_integers = encoding.encode(example_string)
            num_tokens = len(token_integers)
            token_bytes = [encoding.decode_single_token_bytes(token) for token in token_integers]
            print()
            print(f"{encoding_name}: {num_tokens} 个标记")
            print(f"标记整数: {token_integers}")
            print(f"标记字节: {token_bytes}")
    
    compare_encodings("antidisestablishmentarianism")

    8. 为聊天完成API调用计算标记数

    对于像gpt-3.5-turbo和gpt-4这样的ChatGPT模型,它们使用标记方式与较早的完成模型相同,但由于它们基于消息格式,因此更难计算消息将使用多少标记。

    以下是一个用于计算传递给gpt-3.5-turbo或gpt-4的消息标记数的示例函数。请注意,从消息中计算标记的方式可能会因模型而异。考虑到下面的函数中计算的标记数量是一个估计值,而不是一个永恒的保证。

    def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
        """返回消息列表使用的标记数。"""
        try:
            encoding
    
     = tiktoken.get_encoding(model)
        except Exception as e:
            print(f"获取编码时发生错误:{e}")
            return None
    
        total_tokens = 0
        for message in messages:
            tokens = encoding.encode(message["content"])
            num_tokens = len(tokens)
            # 添加额外的标记以表示用户或系统角色
            if message["role"] == "system":
                num_tokens += 1
            else:
                num_tokens += 2  # 一个用于角色,一个用于分隔符
            total_tokens += num_tokens
    
        return total_tokens
    
    # 示例消息列表
    messages = [
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Tell me a joke."},
        {"role": "assistant", "content": "Why did the chicken cross the road?"},
        {"role": "user", "content": "I don't know, why did the chicken cross the road?"}
    ]
    
    total_tokens = num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613")
    print(f"消息列表中的总标记数:{total_tokens}")

    这个函数可以帮助您估算给定消息列表的标记数,以便控制文本长度和计费。请注意,不同模型的标记数限制可能会有所不同,因此在实际使用中请谨慎估算。