我的数字人:投喂中
文中参考文档点击阅读原文打开, 同时推荐2个学习环境:
1、懒人Docker镜像, 已打包200+插件:《最好的PostgreSQL学习镜像》
2、有web浏览器就能用的云起实验室: 《免费体验PolarDB开源数据库》
3、PolarDB开源数据库内核、最佳实践等学习图谱: https://www.aliyun.com/database/openpolardb/activity
我的数字人: 投喂中
背景
系列文章:
《我的数字人: 投喂中》 - 英伟达暴跌1.5万亿,苹果笑了!低配MBP流畅运行LLama3:8b,简单到不需要教程!
经过十几年不懈努力, github已经发表了4000余篇文章, 把这些文章喂给模型后回答一些专业问题, 会不会有更好的效果呢? 试试看吧。
请先参考如下文章在macOS中部署ollam:《低配macbook成功跑AI大模型LLama3:8b, 感谢ollama》
一、用github的文章来喂模型
为了方便使用, 写个python脚本. 嘿嘿, 下面的python脚本也是ollama生成我简单调整了一下.
安装ollama模块
pip3 install ollama
这个python脚本输入问题, 以及相关的参考文本, 让模型参考文本进行回答.
vi exp.py import ollama
import sys
def read_files(file_paths):
file_contents = []
for file_path in file_paths:
with open(file_path, 'r') as f:
for _ in range(14):
next(f)
file_contents.append(f.read())
return '\n'.join(file_contents)
# Get the file path from the command line argument
if len(sys.argv) < 3:
print("Usage: python script.py [prompt] [file1] [file2] ... ")
sys.exit(1)
try:
prompt = sys.argv[1]
file_paths = sys.argv[2:]
data = read_files(file_paths)
output = ollama.generate(
model="llama3:8b",
prompt=f"请先学习后面提供的参考信息, 使用中文回答这个问题(回答前请先复述问题): {prompt} . 以下全部都是参考信息: {data}"
)
print(output['response'])
except Exception as e:
print(f"Error: {e}")
使用举例, 让模型参考 “## 重新发现PostgreSQL之美 - 9 面向多值列的倒排索引GIN|RUM” 的内容来进行回答.
python3 ./exp.py "PostgreSQL gin和rum索引的差别是什么? 列举一些SQL例子, 最后做个总结." /Users/digoal/blog/202105/20210531_02.md
模型回答如下
根据提供的参考信息,我将回答这个问题。
问题:关于 PostgreSQL 的 RUM 模块和 rum_anyarray_addon_ops 操作符的使用和示例。
答案:
RUM(Range Unified Manipulation)是 PostgreSQL 中的一个模块,用于对TSVECTOR和ANYARRAY类型进行范围搜索。rum_anyarray_addon_ops 是 rum 模块中的一个操作符,可以用于将ANYARRAY元素存储到索引中,并支持 prefix 搜索。
示例代码:
create extension rum;
create table tbl (id serial8 primary key, a int[], n int, crt_time timestamp);
create index idx_tbl_1 on tbl using rum (a rum_anyarray_addon_ops, n) with (attach='n', to='a'); explain (analyze, verbose, timing, costs, buffers) select * from tbl where a @> array[1,2,3] and n <= 1;
这段代码首先创建了 RUM 扩展,然后创建了一张名为tbl的表,其中包含一个整数数组列a和一个 timestamp 列crt_time。接着,使用 rum 模块创建了一个索引idx_tbl_1,用于对a列和n列进行范围搜索。
在查询语句中,我们使用了@>操作符来搜索数组a中包含 1、2 和 3 的元素,并且要求n小于或等于 1。结果显示, PostgreSQL 使用的是索引idx_tbl_1来执行查询,而不是全表扫描。
总的来说,rum_anyarray_addon_ops 操作符可以帮助我们快速地对 ANYARRAY 类型进行范围搜索,并且可以减少查询时间和提高数据库性能。
二、会话记忆 & 外脑知识库
上面是需要我们自己去找相关文章, 然后告诉模型, 能不能让模型自己去找相关文章呢?
还有一种情况是, 模型在交互过程中, 怎么把好的问题和答案收集起来, 方便回答时有上下文的记忆呢? 而不是每次都要你提醒上一次问的是什么.
基于以上问题, 我们需要用到向量数据库.
未来可以把好的答案写入向量数据库、好的参考信息写入向量数据库, 根据会话ID和相似向量提取文本, 作为输入再反馈给模型, 实现模型记忆与外脑知识库.
如何把文本向量化?
再次感谢ollama, 已经集成了小型embedding模型(不到1GB, 照样是先pull模型然后就可以使用了.):
https://ollama.com/blog/embedding-models https://ollama.com/library/mxbai-embed-large
在macOS本机安装postgresql, https://www.postgresql.org/ftp/source/
1、安装依赖包 export HOMEBREW_API_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles/api"
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"
export HOMEBREW_BREW_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/brew.git"
export HOMEBREW_CORE_GIT_REMOTE="https://mirrors.tuna.tsinghua.edu.cn/git/homebrew/homebrew-core.git"
export HOMEBREW_PIP_INDEX_URL="https://pypi.tuna.tsinghua.edu.cn/simple"
echo 'export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"' >> ~/.bash_profile
export HOMEBREW_BOTTLE_DOMAIN="https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles"
brew install openssl
brew install pkg-config
2、下载安装icu:
https://github.com/unicode-org/icu/releases
解压后进入 $SRC/icu4c/source 目录安装
./configure --prefix=/usr/local/icu4c
make -j 4
sudo make install
3、下载安装PostgreSQL 16
cd ~/Downloads
curl https://ftp.postgresql.org/pub/source/v16.3/postgresql-16.3.tar.bz2 -o postgresql-16.3.tar.bz2
tar -jxvf postgresql-16.3.tar.bz2
cd postgresql-16.3
export PKG_CONFIG_PATH=/usr/local/icu4c/lib/pkgconfig/
CC=clang CXX=clang++ ./configure --prefix=/Users/digoal/pg16 --with-ssl=openssl
make world -j 4
make install-world
4、配置环境变量
vi ~/pg16env.sh
export PGHOME=/Users/digoal/pg16
export DYLD_LIBRARY_PATH=$PGHOME/lib:$PGHOME/lib/postgresql:$DYLD_LIBRARY_PATH
export PGHOST=127.0.0.1
export PGPORT=1921
export PGUSER=postgres
export PGDATABASE=postgres
export PATH=$PGHOME/bin:$PATH
echo ". /Users/digoal/pg16env.sh" >> ~/.bash_profile
. ~/.bash_profile
which psql
/Users/digoal/pg16/bin/psql
在macOS本机安装psycopg2
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip
CPPFLAGS="-I/opt/homebrew/include/openssl" LDFLAGS="-L/opt/homebrew/lib" pip3 install psycopg2
使用以下镜像拉起PostgreSQL, 内置vector插件用法请参考: https://github.com/pgvector/pgvector
《2023-PostgreSQL Docker镜像学习环境 ARM64版, 已集成热门插件和工具》
进入容器后, 建表如下:
psql alter role postgres encrypted password '123';
create extension vector;
create table tbl (
id serial8 primary key,
prompt text,
answer text,
v_prompt vector,
v_answer vector
);
-- 向量索引请参考手册创建
在macOS本机可连接映射端口
docker ps -a --no-trunc 0.0.0.0:55008->1921/tcp
pull 小型embedding模型
ollama pull mxbai-embed-large
编写一个python脚本, 用于提取blog的markdown文件的标题以及内容, 写入向量数据库中.
vi exp1.py import psycopg2
import sys
import ollama
# Define the PostgreSQL connection settings
HOST = 'localhost'
PORT = 55008
DBNAME = 'postgres'
USER = 'postgres'
PASSWORD = '123'
def read_file(file_name):
try:
with open(file_name, 'r') as f:
lines = [line.strip() for line in f.readlines()]
prompt = lines[0]
answer = '\n'.join(lines[14:])
return prompt, answer
except Exception as e:
print(f"Error reading file: {e}")
sys.exit(1)
def write_to_db(prompt, answer, v_prompt, v_answer, db_config):
try:
conn = psycopg2.connect(**db_config)
cursor = conn.cursor()
cursor.execute("INSERT INTO tbl (prompt, answer, v_prompt, v_answer) VALUES (%s, %s, %s, %s)", (prompt, answer, v_prompt.get("embedding"), v_answer.get("embedding")))
conn.commit()
conn.close()
except Exception as e:
print(f"Error writing to DB: {e}")
sys.exit(1)
finally:
# Close the cursor and connection
cursor.close()
conn.close()
if __name__ == "__main__":
# Get the file path from the command line argument
if len(sys.argv) != 2:
print("Usage: python script.py <file_path>")
sys.exit(1)
file_path = sys.argv[1]
db_config = {
"host": HOST,
"port": PORT,
"database": DBNAME,
"user": USER,
"password": PASSWORD
}
prompt, answer = read_file(file_path)
v_prompt = ollama.embeddings(model='mxbai-embed-large', prompt=prompt)
v_answer = ollama.embeddings(model='mxbai-embed-large', prompt=answer)
write_to_db(prompt, answer, v_prompt, v_answer, db_config)
用法举例
python3 ./exp1.py /Users/digoal/blog/202407/20240717_02.md
写入正常, 生成向量维度1024.
postgres=# select prompt, vector_dims(v_prompt), vector_dims(v_answer) from tbl;
prompt | vector_dims | vector_dims
-----------------------------------------------+-------------+-------------
## DuckDB官方推出"社区扩展插件市场", 未来可期 | 1024 | 1024
(1 row)
实际上llama3也是可以生成embedding的, 只是更慢一点. 另外需要注意的是, 同一张表请使用同一个模型来生成embedding, 而且问题也要使用这个模型来生成然后再去该表进行向量相似搜索. 因为不同的模型哪怕是相同的文字生成的向量的映射也可能不一样, 或者向量维度不一样, 所以不同模型的两个向量其实是没法进行比对的.
如果你有很多的模型, 并且想对比不同模型的效果, 我建议你设计1张映射表, 存储模型和表的关系. 类似这样:
create table t_mapping (id int primary key, model_name text, table_name text);insert into t_mapping values (1, 'llama3:8b', 'tbl1'), (2, 'gemma2:9b', 'tbl2'), (3, 'phi3:medium', 'tbl3');
create table tbl1 (...);
create table tbl2 (...);
create table tbl3 (...);
三、批量入库并根据输入问题检索相关文章
批量写入数据库
vi imp.py import os
import psycopg2
import ollama
# 配置数据库连接信息
conn = psycopg2.connect(
dbname="postgres",
user="postgres",
password="123",
host="127.0.0.1",
port="55008"
)
cursor = conn.cursor()
# 创建表(如果表不存在)
create_table_query = """
create extension if not exists vector;
CREATE TABLE IF NOT EXISTS markdown_files (
id SERIAL PRIMARY KEY,
filename TEXT,
prompt TEXT,
answer TEXT,
v_prompt vector(1024),
v_answer vector(1024)
);
"
""
cursor.execute(create_table_query)
conn.commit() # 指定目录路径
directory_path = "/Users/digoal/blog"
# 遍历目录及其子目录
for root, dirs, files in os.walk(directory_path):
for file in files:
# 检查文件名是否符合 yyyymmdd_nn.md 格式
if file.endswith(".md") and len(file) == 14 and file[:8].isdigit() and file[9:11].isdigit():
file_path = os.path.join(root, file)
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
prompt = lines[0].strip() if len(lines) > 0 else ''
answer = '\n'.join([line.strip() for line in lines])
v_prompt = ollama.embeddings(model='mxbai-embed-large', prompt=prompt)
v_answer = ollama.embeddings(model='mxbai-embed-large', prompt=answer)
# 插入数据到PostgreSQL表中
insert_query = """
INSERT INTO markdown_files (filename, prompt, answer, v_prompt, v_answer)
VALUES (%s, %s, %s, %s, %s);
"""
cursor.execute(insert_query, (file, prompt, answer, v_prompt.get("embedding"), v_answer.get("embedding")))
conn.commit()
# 关闭连接
cursor.close()
conn.close()
python3 ./imp.py
写入成功
postgres=# select count(*) from markdown_files;
count
-------
4551
(1 row) postgres=# select filename, prompt from markdown_files limit 10;
filename | prompt
----------------+------------------------------------------------------------------------------------------------------------------------------------
20170825_01.md | ## 通过空间思想理解GiST索引的构造
20170818_01.md | ## PostgreSQL 单列组合查询优化 - 多个多边形查询优化
20170801_01.md | ## 小微贷款、天使投资(风控助手)业务数据库设计(图式搜索\图谱分析) - 阿里云RDS PostgreSQL, HybridDB for PostgreSQL最佳实践
20170803_01.md | ## 菜鸟末端轨迹 - 电子围栏(解密支撑每天251亿个包裹的数据库) - 阿里云RDS PostgreSQL最佳实践
20170827_01.md | ## 音视图(泛内容)网站透视分析 DB设计 - 阿里云(RDS、HybridDB) for PostgreSQL最佳实践
20170823_01.md | ## 万亿级电商广告 - brin黑科技带你(最低成本)玩转毫秒级圈人(视觉挖掘姊妹篇) - 阿里云RDS PostgreSQL, HybridDB for PostgreSQL最佳实践
20170807_01.md | ## PostgreSQL 读写分离代理 - Crunchy Proxy(base on golang)
20170809_02.md | ## 解密上帝之手 - 阿里云HDB for PostgreSQL数据库metascan特性(存储级、块级、batch级过滤与数据编排)
20170821_01.md | ## Greenplum 内存与负载管理(resource queue)最佳实践
20170802_01.md | ## plpgsql 编程 - JSON数组循环
(10 rows)
创建向量索引
CREATE INDEX ON markdown_files USING hnsw (v_prompt vector_cosine_ops);
CREATE INDEX ON markdown_files USING hnsw (v_answer vector_cosine_ops);
通过文本向量相似检索问题相关文本
vi q.py import psycopg2
import ollama
def get_embedding(text):
# 调用 ollama.embeddings 获取嵌入值
embedding = ollama.embeddings(model='mxbai-embed-large', prompt=text)
return embedding
def query_database(embedding):
# 连接到 PostgreSQL 数据库
conn = psycopg2.connect(
dbname="postgres",
user="postgres",
password="123",
host="localhost",
port="55008"
)
cursor = conn.cursor()
# SQL 查询语句
sql_query = """
SELECT filename, prompt, answer
FROM markdown_files
ORDER BY v_prompt <=> %s::vector(1024)
LIMIT 5;
"""
# 执行查询
cursor.execute(sql_query, (embedding['embedding'],))
results = cursor.fetchall()
# 打印结果
for row in results:
# print(f"Filename: {row[0]}, Prompt: {row[1]}, Answer: {row[2]}")
print(f"Filename: {row[0]}, Prompt: {row[1]}")
# 关闭连接
conn.close()
def main():
# 读取输入文字
text = input("请输入一段文字: ")
# 获取嵌入值
embedding = get_embedding(text)
# 查询数据库并打印结果
query_database(embedding)
if __name__ == "__main__":
main()
测试检索准确性
5c1bf480a210:~ digoal$ python3 q.py
请输入一段文字: 数据库如何高效的管理内存
Filename: 20160608_03.md, Prompt: ## 从尿检取中段谈数据库压测
Filename: 20210129_05.md, Prompt: ## 如何从下往上触达决策者 - 阶级突破
Filename: 20161110_05.md, Prompt: ## 数据仓库架构的变迁
Filename: 20160723_03.md, Prompt: ## 一致性哈希在分布式数据库中的应用探索
Filename: 20210210_03.md, Prompt: ## 喜马拉雅的奶牛
5c1bf480a210:~ digoal$ python3 q.py
请输入一段文字: PostgreSQL rum索引接口和gin索引的差别
Filename: 20201128_02.md, Prompt: ## PostgreSQL RUM 索引原理
Filename: 20170204_01.md, Prompt: ## PostgreSQL GIN索引实现原理
Filename: 20170221_01.md, Prompt: ## PostgreSQL GIN 单列聚集索引 应用
Filename: 20161231_01.md, Prompt: ## 从难缠的模糊查询聊开 - PostgreSQL独门绝招之一 GIN , GiST , SP-GiST , RUM 索引原理与技术背景
Filename: 20210531_02.md, Prompt: ## 重新发现PostgreSQL之美 - 9 面向多值列的倒排索引GIN|RUM
5c1bf480a210:~ digoal$ python3 q.py
请输入一段文字: postgresql brin索引的用法
Filename: 20170219_01.md, Prompt: ## PostgreSQL 聚集存储 与 BRIN索引 - 高并发行为、轨迹类大吞吐数据查询场景解说
Filename: 20160414_01.md, Prompt: ## PostgreSQL 物联网黑科技 - 瘦身几百倍的索引(BRIN index)
Filename: 20160929_01.md, Prompt: ## PostgreSQL 巧妙的数据采样方法
Filename: 20170602_01.md, Prompt: ## PostgreSQL 数据去重方法大全
Filename: 20160529_01.md, Prompt: ## PostgreSQL 批量权限 管理方法
5c1bf480a210:~ digoal$ python3 q.py
请输入一段文字: postgresql bloom索引原理
Filename: 20201128_04.md, Prompt: ## PostgreSQL bloom 索引原理
Filename: 20160523_01.md, Prompt: ## PostgreSQL 9.6 黑科技 bloom 算法索引,一个索引支撑任意列组合查询
Filename: 20160616_01.md, Prompt: ## PostgreSQL 同步流复制原理和代码浅析
Filename: 20160823_02.md, Prompt: ## PostgreSQL 最佳实践 - 冷备份与还原介绍
Filename: 20210605_07.md, Prompt: ## 重新发现PostgreSQL之美 - 14 bloom 布隆过滤器索引
召回准确率还有很大的提升空间
四、未完待续
从向量数据库提取相关知识文章, 然后参考该文章进行回答.
其他相关问题:
文章和问题的关键词提取, 提高召回准确率等 分段切分整理问题. 存储往期会话相关问题 如何微调模型? 我瞎猜会不会是这样的: 准备1类学习素材以及有答案的习题(给模型进行训练), 1类有答案的考试试卷(不给模型训练). 让模型训练学习素材和习题, 然后通过考试评分反馈训练来修正、同时加固训练后的认知确定性(权重). 有点像机器学习里的监督学习?
模型与数据的结合应用项目: https://github.com/mindsdb/mindsdb
ollama 开发参考:
https://pypi.org/project/ollama/ https://github.com/ollama/ollama-python
参考
https://www.timescale.com/blog/use-open-source-llms-in-postgresql-with-ollama-and-pgai
https://ollama.com/blog/embedding-models
https://ollama.com/library/mxbai-embed-large
https://github.com/timescale/pgai/
图片识别和分析
https://ollama.com/library/llava
文本生成图片
https://github.com/Stability-AI/StableDiffusion https://stability.ai/
文本生成图片 - stable-diffusion提示词生成器
https://ollama.com/brxce/stable-diffusion-prompt-generator
phi3 模型微调
https://www.datacamp.com/tutorial/phi-3-tutorial
是不是很有趣?
本期彩蛋-招商中,有需要的小伙伴可联系嵌入...
文章中的参考文档请点击阅读原文获得.
欢迎关注我的github (https://github.com/digoal/blog) , 学习数据库不迷路.
近期正在写公开课材料, 未来将通过视频号推出, 欢迎关注视频号: