#注:文章摘要 by ChatGPT, 头图 by DALLE

很久没有写过有趣的代码了,最近由于OpenAI开放了价格很低的API,所以很多和我一样对AGI一无所知的人都企图来完成一些好玩的东西。不过能看到的大部分应用都是谈天或许翻译之类的,感觉没什么新意,所以上周先写了个不同一点的玩具:将AI接入本地电脑的东西。

最近在想,运用LLM强壮的才能,在一定程度上其实能够完成人的数字永生。趁周末预备把我的博客内容提取一下,完成一个与自己对话的东西。这时分我发现了一个严重的障碍便是我的博客已经好多年没有更新了,而且也没什么有营养的内容。不过这个难题好处理,由于我立刻就想到 @屈屈 的博客内容质量很高,内容量也满足我完成一个demo,所以我先完成一个JerryAI。

先看下作用:

如何利用OpenAI与乔布斯进行跨时空对话

完成原理

TL;DR: 先将文章内容经过 OpenAI 的 Embedding 模型转化成向量数据,然后与问题进行向量间隔核算,最后将topK答案扔给 OpenAI 润色输出。

下面是主要的步骤,也能够参阅官方的这个cookbook。

第一步,获取内容源

下载数据这一步就不具体说明晰,咱们留意运用自己的博客、微博等内容源进行测试,不要逮着一个同学薅羊毛。

下载完之后能够做一些简略处理,主要是需求先去除图片、视频标签及无关的换行空白等,终究构成纯文本文件。

第二步,优化内容token长度

import os
import tiktoken
tokenizer = tiktoken.get_encoding("cl100k_base")
posts = []
# 假如咱们第一步先将文章内容保存到了本地./posts 目录下
for file in os.listdir('posts/'):
with open('posts/' + file, 'r', encoding='UTF-8') as f:
    text = f.read()
    [title, content] = text.split('@@@')
    tokens = len(tokenizer.encode(content))
    posts.append({'title': title, 'content': content, 'tokens': tokens})

tiktoken 是 OpenAI 提供的一个东西,能够快速标记文本成模型 token 以核算其长度。

核算完之后能够统计一下每篇文章的 token 长度,由于 OpenAI 的不同模型对输入 token 长度有不同约束,假如超出了后续需求运用的模型约束,在这儿就需求将内容做下分割。一起也要考虑到 system 和 user 的 prompt 将会占用的长度。

下面是检测和分割内容的主要代码,假如你运用的是微博这种很短的内容能够疏忽这一步。

def split_text(text, max_tokens = max_tokens):
    sentences = text.split('。') #按句号分割,以防将同一个句子分割到了不同的text断中
    n_tokens = [len(tokenizer.encode(" " + sentence)) for sentence in sentences]
    chunks = []
    tokens_so_far = 0
    chunk = []
    for sentence, token in zip(sentences, n_tokens):
        if tokens_so_far + token > max_tokens:
            chunks.append("。".join(chunk) + "。")
            chunk = []
            tokens_so_far = 0
        if token > max_tokens:
            continue
        chunk.append(sentence)
        tokens_so_far += token + 1
    return chunks
ebd_posts = []
for post in posts:
    if post['content'] is None:
        continue
    if post['tokens'] > max_tokens:
        splited_posts = split_text(post['content'])
        for _post in splited_posts:
            ebd_posts.append((post['title'], _post))
    else:
        ebd_posts.append((post['title'], post['content']))

第三步,打标

把文本转化为向量矩阵,这一步是整个过程最中心的,也是所有搜索、推荐、智能问答必不可少的。可是凭借 OpenAI 的 Embeddings 接口,咱们能够运用一行代码就能完成,而且本钱非常低。

import pandas as pd
import openai
df = pd.DataFrame(ebd_posts, columns = ['title', 'content'])
df['tokens'] = df.content.apply(lambda x: len(tokenizer.encode(x)))
# 以下这行代码会屡次恳求 OpenAI 接口,会有较长的等待时间,假如数据量大能够自行做优化处理
df['embeddings'] = df.content.apply(lambda x: openai.Embedding.create(input=x, engine='text-embedding-ada-002')['data'][0]['embedding'])
df.to_csv('data/embeddings.csv')
df.head()

一起,咱们将打标后的数据保存在本地,便利后续查询。由于咱们这儿仅仅一个 demo 演示,所以运用本地文件存储,假如内容量比较大的,主张运用向量数据库

第四步,核算匹配内容

在这一步咱们先将问题也向量化,然后与本地内容进行间隔核算,获取到匹配度更高的内容。

import pandas as pd
import numpy as np
import openai
from openai.embeddings_utils import distances_from_embeddings
max_len = 4000 #4096 for gpt-3.5-turbo
df=pd.read_csv('data/embeddings.csv', index_col=0)
df['embeddings'] = df['embeddings'].apply(eval).apply(np.array)
df.head()
def match_text(question):
    context = []
    cur_len = 0
    # 问题打标
    q_embeddings = openai.Embedding.create(input=question, engine='text-embedding-ada-002')['data'][0]['embedding']
    # 运用 OpenAI 提供的东西函数做向量匹配,假如是存储的向量数据库,查询核算更便利
    df['distances'] = distances_from_embeddings(q_embeddings, df['embeddings'].values, distance_metric='cosine')
    for i, row in df.sort_values('distances', ascending=True).iterrows():
        cur_len += row['tokens']
        if cur_len > max_len:
            break
        context.append(row['content'])
    return context

补充一句,由于 OpenAI 的 Embedding 模型是依据普遍性数据练习,假如你的内容或问答过于专业,有可能就会呈现查询数据不精确的状况,这种时分能够考虑自己练习 Embedding 模型,比如可参阅 text2vec 这个项目。

第五步,优化答复内容

上面的步骤已经能依据问题获取到答案了,可是返回的内容是原始的文本片段,不够友爱,咱们能够经过将匹配的内容发送给 OpenAI 来润色。

import openai
def q(question=''):
    answers = match_text(question)
    context = '\n\n###\n\n'.join(answers)
    try:
        # prompt 能够自己调整优化,其他参数也能够依据需求修改
        completion = openai.ChatCompletion.create(
            model='gpt-3.5-turbo',
            messages=[
                {'role': 'system', 'content': f"你叫xxx,是一个热心和咱们分享和交流的人,你拿手的范畴有xxxx。"},
                {'role': 'user', 'content': f"依据context提供的信息答复我提出的问题。\n\nContext: {context}\n\n---\n\nQuestion: {question}\nAnswer:"}
            ]
        )
        return completion['choices'][0]['message']['content']
    except Exception as e:
        print(e)
        return ''

最后的考虑

由于 GPT-3.5 的模型是一个文本模型,并没有逻辑和推理的才能,所以现有的完本钱质还仅仅一个搜索匹配的东西,加上对话功用也只能说是多了一些总结和表达的才能,但作用还是比较惊艳的。假如想达到更好的作用,还能够自己做 Fine-tuning,运用更简略,仅仅价格会贵一些。

按 OpenAI 的介绍,GPT-4 有更强的推理才能。回到咱们的标题,假如咱们将乔布斯的传记、讲演等内容交给 OpenAI,经过不断的练习调优,一定是能够做到与乔布斯进行跨时空的对话,甚至能够用他的考虑方法来处理新的问题,完成思维的延续。再加上多模态的才能,或许运用这个项目,还能够输出乔布斯的语音。

而要完成上面的构想,在工程层面,只需求写很少的代码。最近在依据 OpenAI 做开发的时分,在惊喜的一起,又有一些丢失,发现从代码上来说,没什么完成难度,也会发现大部分的应用可仿制本钱非常低。

a world where all of us have access to help with almost any cognitive task.

生产力在前进,保持考虑,继续行动吧。