PostgreSQL笔记43:pgvector向量数据存储机制与索引限制深度解析

向量数据库:PGVector PGVector 是一个基于 PostgreSQL 的扩展插件,为用户提供了一套强大的向量存储和查询的功能 阅读详情

纲要

  • pgvector 扩展概述
    • 核心向量数据类型:vector、halfvec、sparsevec、bit
    • 适用版本说明
  • vector 类型的底层存储结构
    • varlena 可变长度头部
    • 源码结构体 Vector 字段解析
    • 存储空间计算公式
  • PostgreSQL TOAST 机制
    • TOAST 的四种存储策略:PLAIN、EXTENDED、EXTERNAL、MAIN
    • pgvector 各版本的默认存储策略演变
  • 索引维度限制的根因
    • PostgreSQL 8KB 数据页与索引页限制
    • 索引条目不超过页面的三分之一规则
    • vector 类型索引上限 2000 维的技术原因
    • halfvec 与 bit 类型的高维索引支持
  • TOAST 对查询优化器的影响
    • 行外存储导致优化器低估成本
    • 并行查询规划数量下降的实证分析
    • parallel_setup_cost 与 parallel_tuple_cost 的调优
  • 最佳实践与存储策略选择
    • 使用 PLAIN 存储避免 TOAST
    • 存储策略修改后的表重写操作
    • 分区表与冷热分离
    • 批量导入工具选型

pgvector 向量数据类型概述

pgvector 是 PostgreSQL 生态中最主流的向量相似性搜索扩展,以插件(extension)形式提供向量存储与 ANN(Approximate Nearest Neighbor)索引能力。其核心优势在于将向量数据直接存储在 PostgreSQL 内部,充分利用数据库自身的 ACID 事务、MVCC、WAL 日志、备份恢复等成熟基础设施,避免数据孤岛与跨系统数据搬运。

pgvector 提供了四种向量数据类型:

类型元素精度每维字节数最大存储维度最大索引维度
vector(n)单精度浮点(float32)4 字节16,0002,000
halfvec(n)半精度浮点(float16)2 字节16,0004,000
sparsevec(n)单精度浮点 + 整数索引8 字节/非零元素16,000 非零元素1,000 非零元素
bit(n)二进制1/8 字节64,00064,000

适用版本说明:halfvec、sparsevec 和 bit 类型自 pgvector 0.7.0 版本起引入。本文以 vector 类型为主要分析对象,其行为适用于 pgvector 0.5.0 及以上版本。

vector 类型的底层存储结构

源码结构体定义

vector 类型在 pgvector 源码中的定义如下(src/vector.h):

typedef struct Vector
{
    int32 vl_len_;      /* varlena 头部(请勿直接操作) */
    int16 dim;          /* 向量维度 */
    int16 unused;       /* 保留字段,暂未使用 */
    float x[FLEXIBLE_ARRAY_MEMBER]; /* 浮点数组,每个元素 4 字节 */
} Vector;

该结构体包含四个字段:

  1. vl_len_:varlena 可变长度头部,占用 4 字节。这是 PostgreSQL 中所有变长数据类型的通用头部,存储了数据的总长度信息。
  2. dim:向量的维度,占用 2 字节(int16)。
  3. unused:保留字段,占用 2 字节,为后续扩展预留。
  4. x[]:柔性数组成员,存储实际的浮点向量元素。每个元素为 C 语言的 float 类型,占用 4 字节。

存储空间计算

每个 vector 类型值的磁盘存储空间为:

Storage = 4 × dim + 8  bytes \text{Storage} = 4 \times \text{dim} + 8 \text{ bytes} Storage=4×dim+8 bytes

其中 8 字节固定开销包含 4 字节的 varlena 头部和 4 字节的元数据(dim + unused)。例如:

  • 768 维向量: 4 × 768 + 8 = 3 , 080 4 \times 768 + 8 = 3,080 4×768+8=3,080 字节
  • 1536 维向量: 4 × 1536 + 8 = 6 , 152 4 \times 1536 + 8 = 6,152 4×1536+8=6,152 字节
  • 2000 维向量: 4 × 2000 + 8 = 8 , 008 4 \times 2000 + 8 = 8,008 4×2000+8=8,008 字节

TOAST 机制及其对向量存储的影响

TOAST 基本原理

PostgreSQL 使用固定的数据页大小(默认为 8KB),且不允许一个元组(tuple)跨多个数据页存储。当字段值过大无法放入单个数据页时,PostgreSQL 会自动将其压缩或拆分存储到辅助表(TOAST 表)中,仅在主表中保留一个 18 字节的指针。这一机制称为 TOAST(The Oversized-Attribute Storage Technique)。

要支持 TOAST,数据类型必须使用 varlena(可变长度)表示。pgvector 的 vector 类型正是基于 varlena 构建的。

TOAST 的四种存储策略

PostgreSQL 为 TOAST 数据类型提供了四种存储策略:

策略行为适用场景
PLAIN禁止压缩和行外存储,数据必须内联存储小字段,避免额外 I/O
EXTENDED允许压缩和行外存储(先压缩,再视情况行外存储)大多数 TOAST 数据类型的默认策略
EXTERNAL允许行外存储,但禁止压缩需要避免压缩开销的场景
MAIN尽量内联存储,仅在必要时行外存储经常访问的大字段

pgvector 存储策略的版本演变

pgvector 的默认存储策略在不同版本中有所变化:

  • 0.4.0 之前:vector 类型使用 PLAIN 存储策略。
  • 0.4.0 及之后:vector 类型默认使用 EXTENDED 存储策略。

这意味着在较新版本中,当向量维度超过约 768 维( 4 × 768 + 8 = 3 , 080 4 \times 768 + 8 = 3,080 4×768+8=3,080 字节,超过 TOAST 阈值)时,向量数据会被自动压缩或移出行外存储。

查看与修改存储策略

使用 \d+ 命令或查询 pg_attribute 系统表可查看列的存储策略:

-- 查看表的列存储策略
SELECT attname, attstorage
FROM pg_attribute
WHERE attrelid = 'test_vector'::regclass AND attnum > 0;

attstorage 字段取值含义:

  • p:PLAIN
  • e:EXTENDED
  • x:EXTERNAL
  • m:MAIN

修改列的存储策略:

ALTER TABLE test_vector ALTER COLUMN embedding SET STORAGE PLAIN;

注意:修改存储策略后,现有数据不会自动迁移。需要使用 VACUUM FULL 或 CLUSTER 重写表,才能将已行外存储的数据重新内联。

索引维度限制的根因分析

2000 维上限的技术来源

pgvector 官方文档明确限制 vector 类型的索引维度不超过 2000。这一限制的根本原因在于 PostgreSQL 的索引页结构与 TOAST 机制之间的冲突。

PostgreSQL 的 B-tree 索引要求索引条目不能超过数据页大小的约三分之一。默认数据页为 8KB,三分之一约为 2.7KB。一个 2000 维的 vector 占用 4 × 2000 + 8 = 8 , 008 4 \times 2000 + 8 = 8,008 4×2000+8=8,008 字节,远超 2.7KB 的限制。

为什么 TOAST 不能用于索引

TOAST 机制虽然可以处理超大数据,但TOAST 不能用于索引。索引条目必须能够直接从索引页中读取并比较,而 TOAST 指针指向的行外数据无法在索引扫描过程中高效访问。因此,索引必须存储完整的向量值,而非 TOAST 指针。

这一约束使得索引向量的大小必须适配索引页的容量限制,最终形成了 2000 维的上限。

高维向量的索引替代方案

对于需要索引超过 2000 维向量的场景,pgvector 0.7.0 提供了以下替代方案:

  • halfvec:半精度浮点,每维 2 字节,索引上限 4000 维
  • bit:二进制向量,每 8 维仅需 1 字节,索引上限 64000 维
  • 二进制量化(Binary Quantization):可将高维 float32 向量量化为二进制向量,索引上限 64000 维

TOAST 对查询优化器的影响

优化器对 TOAST 数据的认知不足

PostgreSQL 的查询优化器在估算顺序扫描成本时,不会充分考虑 TOAST 行外存储带来的额外 I/O 开销。由于 TOAST 数据与主表数据分开存储,优化器倾向于低估扫描 TOAST 数据的实际成本。

并行查询规划的实证分析

以下是一个典型的性能对比场景:

向量维度数据大小TOAST 状态优化器规划的并行工作进程数
128 维520 字节内联存储16
1536 维6,152 字节行外存储(TOAST)4

1536 维向量由于超出数据页容量而被移入 TOAST 表,优化器因无法准确评估 TOAST 访问成本,仅规划了 4 个并行工作进程,导致查询效率严重下降。

手动调优并行查询

通过调整以下参数,可以强制优化器使用更多的并行工作进程:

-- 降低并行扫描的成本估算
SET parallel_setup_cost = 1;
SET parallel_tuple_cost = 0.001;

-- 或调整单次扫描的最小数据量阈值
SET min_parallel_table_scan_size = 0;

注意:上述调整属于"以资源换时间"的策略,会增加 CPU 和 I/O 资源消耗,需根据实际硬件条件谨慎使用。

最佳实践与存储策略选择

优先使用 PLAIN 存储

对于维度不超过 2000 的向量,建议将存储策略设置为 PLAIN,强制数据内联存储,避免 TOAST 带来的额外 I/O 和优化器成本估算偏差。

CREATE TABLE embeddings (
    id BIGSERIAL PRIMARY KEY,
    embedding vector(768) NOT NULL
);

-- 将向量列设置为 PLAIN 存储
ALTER TABLE embeddings ALTER COLUMN embedding SET STORAGE PLAIN;

-- 重写表以应用新的存储策略
VACUUM FULL embeddings;

分区表实现冷热分离

利用 PostgreSQL 分区表,可将热数据(近期频繁查询)与冷数据(历史归档)分别存储在不同的表空间上:

-- 按月分区
CREATE TABLE embeddings_partitioned (
    id BIGSERIAL,
    created_at TIMESTAMPTZ NOT NULL,
    embedding vector(768) NOT NULL
) PARTITION BY RANGE (created_at);

-- 热数据分区(SSD 表空间)
CREATE TABLE embeddings_2026_08 PARTITION OF embeddings_partitioned
    FOR VALUES FROM ('2026-08-01') TO ('2026-09-01')
    TABLESPACE ssd_tablespace;

-- 冷数据分区(HDD 表空间)
CREATE TABLE embeddings_2025_01 PARTITION OF embeddings_partitioned
    FOR VALUES FROM ('2025-01-01') TO ('2025-02-01')
    TABLESPACE hdd_tablespace;

批量数据导入

pgvector 可以充分利用 PostgreSQL 的原生批量导入工具:

# 使用 COPY 命令导入 CSV 格式的向量数据
psql -d mydb -c "\COPY embeddings(embedding) FROM '/data/vectors.csv' WITH CSV"

# 使用 pg_bulkload 进行高性能批量加载
pg_bulkload -d mydb -i /data/vectors.csv -O embeddings -l load.log

预过滤与后过滤策略

在实际查询中,往往需要结合元数据过滤条件:

  • 预过滤(Pre-filter):先通过元数据条件(如 WHERE category = 'A')缩小数据集,再执行 ANN 搜索。适用于过滤条件选择性高的场景。
  • 后过滤(Post-filter):先执行 ANN 搜索获取 Top-K 候选,再应用元数据过滤。pgvector 0.8.0 及以上版本支持迭代索引扫描,自动读取更多候选直到满足过滤条件。

API 速览

pgvector 核心数据类型

vector(n)

创建指定维度的向量类型列:

CREATE TABLE items (
    id BIGSERIAL PRIMARY KEY,
    embedding vector(1536)
);

距离运算符

运算符距离类型运算符类
<->欧几里得距离(L2)vector_l2_ops
<#>负内积vector_ip_ops
<=>余弦距离vector_cosine_ops

相似性查询

-- 查询与目标向量最相似的 10 条记录(余弦距离)
SELECT id, embedding
FROM items
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;

索引创建

IVFFlat 索引

CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

HNSW 索引

CREATE INDEX ON items USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);

注意:HNSW 索引的维度上限为 2000(vector 类型)。

存储策略管理

查看当前存储策略

SELECT attname, attstorage
FROM pg_attribute
WHERE attrelid = 'items'::regclass AND attname = 'embedding';

修改存储策略

ALTER TABLE items ALTER COLUMN embedding SET STORAGE PLAIN;

重写表以应用存储策略变更

VACUUM FULL items;
-- 或
CLUSTER items USING items_pkey;

系统信息查询

查看 pgvector 版本

SELECT * FROM pg_extension WHERE extname = 'vector';

查看数据页大小

SHOW block_size;

Demo 简单示例

项目结构

pgvector-storage-demo/
├── package.json
├── index.js
├── .env
└── sql/
    └── init.sql

运行说明

  1. 安装依赖:
npm init -y
npm install pg dotenv
  1. 配置环境变量(.env 文件):
PGHOST=localhost
PGPORT=5432
PGDATABASE=vectordb
PGUSER=postgres
PGPASSWORD=your_password
  1. 初始化数据库(sql/init.sql):
CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE embeddings (
    id BIGSERIAL PRIMARY KEY,
    embedding vector(768) NOT NULL,
    metadata JSONB,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 设置为 PLAIN 存储以优化性能
ALTER TABLE embeddings ALTER COLUMN embedding SET STORAGE PLAIN;

-- 创建 HNSW 索引
CREATE INDEX ON embeddings USING hnsw (embedding vector_cosine_ops);
  1. 运行示例:
node index.js

代码说明(index.js)

const { Client } = require('pg');
require('dotenv').config();

// 生成随机向量(模拟 embedding)
function generateRandomVector(dim) {
    const vec = [];
    for (let i = 0; i < dim; i++) {
        vec.push(Math.random() * 2 - 1); // [-1, 1] 范围
    }
    return vec;
}

async function main() {
    const client = new Client({
        host: process.env.PGHOST,
        port: parseInt(process.env.PGPORT),
        database: process.env.PGDATABASE,
        user: process.env.PGUSER,
        password: process.env.PGPASSWORD,
    });

    await client.connect();

    const DIM = 768;
    const NUM_VECTORS = 1000;

    // 1. 批量插入向量数据
    console.log(`插入 ${NUM_VECTORS} 条 ${DIM} 维向量...`);
    const vectors = [];
    for (let i = 0; i < NUM_VECTORS; i++) {
        vectors.push(generateRandomVector(DIM));
    }

    // 使用参数化批量插入
    for (let i = 0; i < vectors.length; i++) {
        await client.query(
            'INSERT INTO embeddings (embedding) VALUES ($1)',
            [`[${vectors[i].join(',')}]`]
        );
    }

    // 2. 生成查询向量
    const queryVector = generateRandomVector(DIM);

    // 3. 执行相似性搜索(余弦距离)
    console.log('执行相似性搜索...');
    const result = await client.query(
        `SELECT id, embedding <=> $1::vector AS distance
         FROM embeddings
         ORDER BY embedding <=> $1::vector
         LIMIT 10`,
        [`[${queryVector.join(',')}]`]
    );

    console.log('Top 10 最相似向量:');
    result.rows.forEach((row, idx) => {
        console.log(`  #${idx + 1}: id=${row.id}, distance=${row.distance.toFixed(6)}`);
    });

    // 4. 查看 TOAST 存储状态
    const toastInfo = await client.query(`
        SELECT attname, attstorage,
               CASE attstorage
                   WHEN 'p' THEN 'PLAIN'
                   WHEN 'e' THEN 'EXTENDED'
                   WHEN 'x' THEN 'EXTERNAL'
                   WHEN 'm' THEN 'MAIN'
               END AS storage_strategy
        FROM pg_attribute
        WHERE attrelid = 'embeddings'::regclass AND attname = 'embedding'
    `);
    console.log('向量列存储策略:', toastInfo.rows[0]);

    // 5. 查看表大小
    const sizeInfo = await client.query(`
        SELECT pg_size_pretty(pg_total_relation_size('embeddings')) AS total_size
    `);
    console.log('表总大小:', sizeInfo.rows[0].total_size);

    await client.end();
}

main().catch(console.error);

技术点总结

本 Demo 演示了以下核心技术:

  1. vector 类型的创建与使用:在 PostgreSQL 中创建向量列并插入数据
  2. 批量数据插入:使用参数化查询批量写入向量数据
  3. 余弦距离相似性搜索:使用 <=> 运算符进行 ANN 查询
  4. 存储策略查看:通过 pg_attribute 系统表确认列的 TOAST 存储策略
  5. 表空间占用监控:使用 pg_total_relation_size 查看存储空间

PostgreSQL 原生指令对照

上述 Node.js 操作对应的 PostgreSQL 原生 SQL 指令:

-- 插入单条向量
INSERT INTO embeddings (embedding) VALUES ('[0.1, 0.2, ...]'::vector);

-- 相似性搜索(余弦距离)
SELECT id, embedding <=> '[0.1, 0.2, ...]'::vector AS distance
FROM embeddings
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;

-- 查看存储策略
SELECT attname, attstorage FROM pg_attribute
WHERE attrelid = 'embeddings'::regclass AND attname = 'embedding';

-- 查看表大小
SELECT pg_size_pretty(pg_total_relation_size('embeddings'));

多语言示例

基于前文 Node.js 的 Demo,本节提供 Go、Python 和 Java 三种语言的完整实现。所有示例均针对 PostgreSQL + pgvector 环境,执行相同的向量插入与相似性搜索操作。

Go 语言示例

使用 pgx 驱动(推荐)和 dotenv 配置管理。

项目结构
go-pgvector-demo/
├── go.mod
├── go.sum
├── main.go
├── .env
└── sql/
    └── init.sql
运行说明
  1. 初始化模块:
go mod init go-pgvector-demo
go get github.com/jackc/pgx/v5
go get github.com/joho/godotenv
  1. 环境变量(.env):
PGHOST=localhost
PGPORT=5432
PGDATABASE=vectordb
PGUSER=postgres
PGPASSWORD=your_password
  1. 初始化数据库(sql/init.sql 同上文 Node.js 部分)。

  2. 运行:

go run main.go
完整代码(main.go)
package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "os"
    "strconv"
    "strings"
    "time"

    "github.com/jackc/pgx/v5"
    "github.com/joho/godotenv"
)

// generateRandomVector 生成指定维度的随机向量,元素在 [-1, 1] 范围
func generateRandomVector(dim int) []float32 {
    vec := make([]float32, dim)
    for i := range vec {
        vec[i] = rand.Float32()*2 - 1
    }
    return vec
}

// vectorToString 将 []float32 转换为 PostgreSQL vector 字符串格式 '[0.1,0.2,...]'
func vectorToString(vec []float32) string {
    parts := make([]string, len(vec))
    for i, v := range vec {
        parts[i] = strconv.FormatFloat(float64(v), 'f', 6, 32)
    }
    return "[" + strings.Join(parts, ",") + "]"
}

func main() {
    // 加载 .env
    if err := godotenv.Load(); err != nil {
        log.Println("未找到 .env 文件,使用系统环境变量")
    }

    // 构建连接字符串
    connStr := fmt.Sprintf(
        "host=%s port=%s dbname=%s user=%s password=%s",
        os.Getenv("PGHOST"),
        os.Getenv("PGPORT"),
        os.Getenv("PGDATABASE"),
        os.Getenv("PGUSER"),
        os.Getenv("PGPASSWORD"),
    )

    ctx := context.Background()

    // 连接数据库
    conn, err := pgx.Connect(ctx, connStr)
    if err != nil {
        log.Fatalf("无法连接数据库: %v", err)
    }
    defer conn.Close(ctx)

    const dim = 768
    const numVectors = 1000
    rand.Seed(time.Now().UnixNano())

    // 1. 批量插入向量(使用 COPY 更高效,但为保持示例清晰使用循环)
    fmt.Printf("插入 %d 条 %d 维向量...\n", numVectors, dim)
    for i := 0; i < numVectors; i++ {
        vec := generateRandomVector(dim)
        vecStr := vectorToString(vec)
        _, err := conn.Exec(ctx, "INSERT INTO embeddings (embedding) VALUES ($1)", vecStr)
        if err != nil {
            log.Fatalf("插入失败: %v", err)
        }
    }

    // 2. 生成查询向量
    queryVec := generateRandomVector(dim)
    queryStr := vectorToString(queryVec)

    // 3. 执行相似性搜索(余弦距离)
    fmt.Println("执行相似性搜索...")
    rows, err := conn.Query(ctx,
        `SELECT id, embedding <=> $1::vector AS distance
         FROM embeddings
         ORDER BY embedding <=> $1::vector
         LIMIT 10`,
        queryStr,
    )
    if err != nil {
        log.Fatalf("查询失败: %v", err)
    }
    defer rows.Close()

    fmt.Println("Top 10 最相似向量:")
    idx := 1
    for rows.Next() {
        var id int64
        var distance float32
        if err := rows.Scan(&id, &distance); err != nil {
            log.Fatalf("扫描失败: %v", err)
        }
        fmt.Printf("  #%d: id=%d, distance=%.6f\n", idx, id, distance)
        idx++
    }

    // 4. 查看存储策略
    var attname string
    var attstorage string
    err = conn.QueryRow(ctx,
        `SELECT attname, attstorage
         FROM pg_attribute
         WHERE attrelid = 'embeddings'::regclass AND attname = 'embedding'`,
    ).Scan(&attname, &attstorage)
    if err != nil {
        log.Fatalf("查询存储策略失败: %v", err)
    }
    fmt.Printf("向量列存储策略: attname=%s, attstorage=%s\n", attname, attstorage)

    // 5. 查看表大小
    var totalSize string
    err = conn.QueryRow(ctx,
        `SELECT pg_size_pretty(pg_total_relation_size('embeddings'))`,
    ).Scan(&totalSize)
    if err != nil {
        log.Fatalf("查询表大小失败: %v", err)
    }
    fmt.Printf("表总大小: %s\n", totalSize)
}
PostgreSQL 原生指令对照(Go 版本使用相同的 SQL)

Python 示例

使用 psycopg2(或 psycopg2-binary)和 python-dotenv。

项目结构
py-pgvector-demo/
├── requirements.txt
├── main.py
├── .env
└── sql/
    └── init.sql
运行说明
  1. 安装依赖:
pip install psycopg2-binary python-dotenv
  1. 环境变量(.env 同上)。

  2. 运行:

python main.py
完整代码(main.py)
import os
import random
import psycopg2
from psycopg2 import sql
from dotenv import load_dotenv

load_dotenv()

def generate_random_vector(dim):
    """生成 [-1, 1] 范围的随机向量"""
    return [random.uniform(-1, 1) for _ in range(dim)]

def vector_to_string(vec):
    """将 Python list 转换为 PostgreSQL vector 字符串"""
    return "[" + ",".join(f"{v:.6f}" for v in vec) + "]"

def main():
    # 连接数据库
    conn = psycopg2.connect(
        host=os.getenv("PGHOST"),
        port=os.getenv("PGPORT"),
        dbname=os.getenv("PGDATABASE"),
        user=os.getenv("PGUSER"),
        password=os.getenv("PGPASSWORD"),
    )
    conn.autocommit = True
    cur = conn.cursor()

    DIM = 768
    NUM_VECTORS = 1000

    # 1. 批量插入向量
    print(f"插入 {NUM_VECTORS} 条 {DIM} 维向量...")
    for _ in range(NUM_VECTORS):
        vec = generate_random_vector(DIM)
        vec_str = vector_to_string(vec)
        cur.execute("INSERT INTO embeddings (embedding) VALUES (%s)", (vec_str,))

    # 2. 生成查询向量
    query_vec = generate_random_vector(DIM)
    query_str = vector_to_string(query_vec)

    # 3. 执行相似性搜索(余弦距离)
    print("执行相似性搜索...")
    cur.execute(
        """
        SELECT id, embedding <=> %s::vector AS distance
        FROM embeddings
        ORDER BY embedding <=> %s::vector
        LIMIT 10
        """,
        (query_str, query_str),
    )
    results = cur.fetchall()
    print("Top 10 最相似向量:")
    for idx, (id_, dist) in enumerate(results, 1):
        print(f"  #{idx}: id={id_}, distance={dist:.6f}")

    # 4. 查看存储策略
    cur.execute(
        """
        SELECT attname, attstorage
        FROM pg_attribute
        WHERE attrelid = 'embeddings'::regclass AND attname = 'embedding'
        """
    )
    attname, attstorage = cur.fetchone()
    print(f"向量列存储策略: attname={attname}, attstorage={attstorage}")

    # 5. 查看表大小
    cur.execute("SELECT pg_size_pretty(pg_total_relation_size('embeddings'))")
    total_size = cur.fetchone()[0]
    print(f"表总大小: {total_size}")

    cur.close()
    conn.close()

if __name__ == "__main__":
    main()

Java 示例

使用 JDBC 驱动(postgresql)和 dotenv 库(或直接使用环境变量)。这里使用 dotenv-java 读取 .env。

项目结构
java-pgvector-demo/
├── pom.xml
├── src/
│   └── main/
│       └── java/
│           └── Main.java
├── .env
└── sql/
    └── init.sql
运行说明
  1. Maven 依赖(pom.xml):
<dependencies>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.7.3</version>
    </dependency>
    <dependency>
        <groupId>io.github.cdimascio</groupId>
        <artifactId>java-dotenv</artifactId>
        <version>5.2.2</version>
    </dependency>
</dependencies>
  1. 环境变量(.env 同上)。

  2. 编译运行:

mvn compile exec:java -Dexec.mainClass="Main"
完整代码(Main.java)
import io.github.cdimascio.dotenv.Dotenv;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {

    private static final Random RANDOM = new Random();

    // 生成随机向量
    private static List<Float> generateRandomVector(int dim) {
        List<Float> vec = new ArrayList<>(dim);
        for (int i = 0; i < dim; i++) {
            vec.add(RANDOM.nextFloat() * 2 - 1); // [-1, 1]
        }
        return vec;
    }

    // 将 List<Float> 转换为 PostgreSQL vector 字符串
    private static String vectorToString(List<Float> vec) {
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < vec.size(); i++) {
            if (i > 0) sb.append(",");
            sb.append(String.format("%.6f", vec.get(i)));
        }
        sb.append("]");
        return sb.toString();
    }

    public static void main(String[] args) {
        // 加载 .env
        Dotenv dotenv = Dotenv.configure().load();

        String host = dotenv.get("PGHOST");
        String port = dotenv.get("PGPORT");
        String dbname = dotenv.get("PGDATABASE");
        String user = dotenv.get("PGUSER");
        String password = dotenv.get("PGPASSWORD");

        String url = String.format("jdbc:postgresql://%s:%s/%s", host, port, dbname);

        final int DIM = 768;
        final int NUM_VECTORS = 1000;

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            conn.setAutoCommit(true);

            // 1. 批量插入向量
            System.out.printf("插入 %d 条 %d 维向量...\n", NUM_VECTORS, DIM);
            String insertSql = "INSERT INTO embeddings (embedding) VALUES (?)";
            try (PreparedStatement pstmt = conn.prepareStatement(insertSql)) {
                for (int i = 0; i < NUM_VECTORS; i++) {
                    List<Float> vec = generateRandomVector(DIM);
                    String vecStr = vectorToString(vec);
                    pstmt.setString(1, vecStr);
                    pstmt.addBatch();
                    // 每 100 条提交一次
                    if (i % 100 == 0) {
                        pstmt.executeBatch();
                    }
                }
                pstmt.executeBatch();
            }

            // 2. 生成查询向量
            List<Float> queryVec = generateRandomVector(DIM);
            String queryStr = vectorToString(queryVec);

            // 3. 执行相似性搜索(余弦距离)
            System.out.println("执行相似性搜索...");
            String searchSql =
                "SELECT id, embedding <=> ?::vector AS distance " +
                "FROM embeddings " +
                "ORDER BY embedding <=> ?::vector " +
                "LIMIT 10";
            try (PreparedStatement pstmt = conn.prepareStatement(searchSql)) {
                pstmt.setString(1, queryStr);
                pstmt.setString(2, queryStr);
                try (ResultSet rs = pstmt.executeQuery()) {
                    System.out.println("Top 10 最相似向量:");
                    int idx = 1;
                    while (rs.next()) {
                        long id = rs.getLong("id");
                        float distance = rs.getFloat("distance");
                        System.out.printf("  #%d: id=%d, distance=%.6f\n", idx, id, distance);
                        idx++;
                    }
                }
            }

            // 4. 查看存储策略
            String storageSql =
                "SELECT attname, attstorage " +
                "FROM pg_attribute " +
                "WHERE attrelid = 'embeddings'::regclass AND attname = 'embedding'";
            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery(storageSql)) {
                if (rs.next()) {
                    String attname = rs.getString("attname");
                    String attstorage = rs.getString("attstorage");
                    System.out.printf("向量列存储策略: attname=%s, attstorage=%s\n", attname, attstorage);
                }
            }

            // 5. 查看表大小
            String sizeSql = "SELECT pg_size_pretty(pg_total_relation_size('embeddings'))";
            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery(sizeSql)) {
                if (rs.next()) {
                    String totalSize = rs.getString(1);
                    System.out.printf("表总大小: %s\n", totalSize);
                }
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

多语言对比总结

下表对比了四种语言实现的核心差异,便于根据项目技术栈选择。

对比维度Node.js (pg)Go (pgx)Python (psycopg2)Java (JDBC)
驱动/库pg (纯 JavaScript)pgx/v5psycopg2-binarypostgresql JDBC
连接方式new Client() + 环境变量pgx.Connect()psycopg2.connect()DriverManager.getConnection()
参数化查询$1, $2 占位符$1, $2 占位符%s 占位符(psycopg2 使用 %s)? 占位符
向量类型转换手动拼接 [x,y] 字符串手动拼接手动拼接手动拼接
批量插入方式逐条循环 INSERT逐条循环 Exec逐条循环 executeaddBatch() + executeBatch()
事务控制默认自动提交,可手动管理默认自动提交设置 autocommit=True默认自动提交
错误处理try-catch / 回调if err != niltry-excepttry-catch
环境变量管理dotenvgodotenvpython-dotenvjava-dotenv
字符串拼接性能中等中等中等较好(StringBuilder)
适合场景快速原型、全栈高并发、微服务数据分析、机器学习企业级、Spring 生态
执行相似性搜索使用 <=> 运算符,参数化使用 <=> 运算符,参数化使用 <=> 运算符,参数化使用 <=> 运算符,参数化
存储策略查询查询 pg_attribute查询 pg_attribute查询 pg_attribute查询 pg_attribute
表大小查询pg_total_relation_sizepg_total_relation_sizepg_total_relation_sizepg_total_relation_size

所有语言的 Demo 均遵循以下共同逻辑:

  1. 连接 PostgreSQL 数据库,确保已启用 pgvector 扩展。
  2. 创建包含 vector(768) 列的表,并设置 PLAIN 存储策略(可选,根据前文最佳实践)。
  3. 批量生成并插入 1000 条随机向量。
  4. 使用余弦距离运算符 <=> 执行 Top-10 相似性搜索。
  5. 查询 pg_attribute 验证存储策略,查询 pg_total_relation_size 查看表空间占用。

各实现均可在对应环境中直接运行,所需依赖和启动命令已在上文提供。

官方文档

参考链接

总结

本文深入剖析了 pgvector 扩展中 vector 数据类型的底层存储机制,核心要点如下:

  1. 存储结构:vector 类型基于 PostgreSQL 的 varlena 可变长度头部构建,每个向量占用 4 × dim + 8 4 \times \text{dim} + 8 4×dim+8 字节磁盘空间,包含 4 字节头部、2 字节维度、2 字节保留字段和浮点数组。

  2. TOAST 机制:pgvector 0.4.0 起默认使用 EXTENDED 存储策略,当向量超过约 768 维时会被自动压缩或移入 TOAST 表。TOAST 数据与主表分离存储,对查询优化器的成本估算产生显著影响,可能导致并行查询规划不足。

  3. 索引维度限制:由于 TOAST 不能用于索引且 PostgreSQL 索引页固定为 8KB,vector 类型的索引维度被限制为 2000。高维场景可使用 halfvec(上限 4000 维)或 bit(上限 64000 维)类型。

  4. 最佳实践:对于维度不超过 2000 的向量,建议将存储策略设置为 PLAIN 并重写表,避免 TOAST 带来的性能损耗。结合分区表实现冷热分离,利用 COPY 或 pg_bulkload 进行批量导入。

基于Spring AI与pgvector构建企业级RAG知识库系统 RAG(检索增强生成)架构通过结合传统检索技术与大语言模型,有效解决了企业知识管理中的信息准确性与生成可控性问题。其核心原理是先通过向量数据库检索相关文档片段,再交由LLM生成最终回答,兼具数据利用率和结果可靠性。在技术实现层面,Spring AI框架凭借其与Spring生态的无缝集成、模块化设计等优势,成为企业级应用的首选;而pgvector作为PostgreSQL的向量扩展,既能复用现有数据库设施,又支持混合查询等高级特性。该技术组合特别适用于金融、保险等对数据一致性要求严格的领域,某保险公司的实际应用 阅读详情

相关推荐

PostgreSQL底层原理:MVCC、WAL与查询优化全解析

关系型数据库(RDBMS)的核心能力源于ACID事务保障与多版本并发控制(MVCC)机制,而PostgreSQL作为开源RDBMS的标杆,其可靠性与高性能并非来自黑盒封装,而是由WAL日志、共享缓冲区、事务快照、元组可见性检查等底层组件协同实现。理解MVCC如何通过xmin/xmax字段管理数据版本,掌握WAL写前日志如何确保崩溃恢复的一致性,结合EXPLAIN ANALYZE中的Buffers指标诊断I/O瓶颈,是构建高并发、低延迟数据库系统的工程基础。本文深入PostgreSQL进程模型、内存结构与物理

weixin_30294295的博客 431

pgvector 快速上手:在 PostgreSQL 里存向量、建索引、跑相似查询

如果你已经用 PostgreSQL 存业务数据,现在又想让模型产出的 embedding 也放进同一个库里、并能按"相似度"取最近邻,pgvector 就是为此而生的扩展。它给 Postgres 加了一个向量数据类型,再配上 HNSW、IVFFlat 两种索引,让你用普通 SQL 完成插入、过滤和最近邻查询,不需要单独维护一套向量数据库。 ## 项目定位:它是什么,适合谁 pgvector 是

gitblog_00082的博客 672

Nacos 2.3.2实战:5分钟搞定达梦数据库插件配置(含避坑指南)

本文详细介绍了在Nacos 2.3.2中配置达梦数据库数据源插件的完整实战流程。通过解析SPI插件机制,提供从环境准备、插件编译部署到核心配置的逐步指南,并重点分享了驱动加载、SQL兼容性等高频问题的避坑解决方案,助力开发者高效完成国产化数据库适配。

weixin_29200485的博客 502

PGVector 详解:PostgreSQL 世界里的向量能力插件

PGVector 是 PostgreSQL 生态中非常优秀的向量插件简单、稳定、低成本无法成为以向量为中心的系统核心当向量只是“辅助能力”,PGVector 是最佳选择;当向量成为“系统主角”,就该让 PostgreSQL 退回它最擅长的位置。

XiaoCai 1711

谷歌DreamFusion使用方法及实例详解

DreamFusion 是谷歌提出的基于文本生成3D模型的突破性技术,无需3D训练数据,通过2D扩散模型(如Imagen)与NeRF(神经辐射场)结合实现。以下为使用社区开源实现(如。)的详细步骤及实例。

主攻大数据 人工智能 物联网 安全 低空经济等方向。mtsc 、gtest特邀分享嘉宾 1043

Voltus功耗分析——读入Innovus数据/cpf/spef文件

检查的内容:物理库、时序库、网表、I/Os、tie-high&tie-low pins、电源/地pins。设计数据(LEF/DEF/netlist)、cpf文件、SPEF文件。方法二:定义每个 power/ground net's 电压。4.设计数据检查(design sanity check)方法一:读入.dat数据。方法二:读入def文件。方法一:读入CPF文件。

qq_44347276的博客 3991

揭秘pgvector向量表存储差异:为什么理论计算总与实际不符?

在PostgreSQL数据库中集成向量搜索功能时,开发者常常会遇到一个困惑:**为什么向量存储的实际磁盘占用总是与理论计算不符?** pgvector作为PostgreSQL生态中最受欢迎的开源向量相似性搜索扩展,其存储机制的设计细节直接影响着数据存储效率和查询性能。本文将深入剖析pgvector的存储原理,揭示理论计算与实际占用差异的底层原因,并提供优化存储的实用指南。 ## 一、pgvec

gitblog_00563的博客 417

EtherCAT同步模式实战:如何用TwinCAT配置DC-Synchronous模式(附时序图详解)

本文详细解析了如何在TwinCAT环境中配置EtherCAT的DC-Synchronous同步模式,实现纳秒级同步精度。通过硬件准备、参数配置和高级调优步骤,帮助工程师解决工业自动化中的同步挑战,提升运动控制性能。

gold的博客 1575

pgvector 向量数据库完全指南:PostgreSQL 生态的 AI 增强

pgvector是PostgreSQL的开源扩展,为关系型数据库添加向量相似度搜索能力。它完全集成在PostgreSQL内部,复用其存储引擎、事务机制和SQL接口,无需额外基础设施。pgvector支持两种主流索引算法:IVFFlat(适合批量导入)和HNSW(查询快、召回率高)。存储上利用PostgreSQL的TOAST机制,支持压缩和稀疏向量。作为扩展,pgvector添加了新的数据类型、操作符和函数,通过标准SQL即可进行向量操作。部署时可使用官方Docker镜像快速启动,适用于从开发到生产的环境。

心有猛虎细嗅蔷薇 5859

机器翻译的评价标准:BLEU、ROUGE等指标说明及代码实现

机器翻译领域常用的自动评价指标主要包括BLEU、ROUGE、METEOR和COMET四大类。BLEU基于n-gram精确率和短句惩罚,ROUGE关注召回率,METEOR结合精确率和召回率并考虑同义词,COMET则利用预训练模型计算语义相似度。这些指标各有特点:BLEU适用于机器翻译,ROUGE对长句友好,METEOR更接近人工评分,COMET无需参考译文。代码实现上,sacrebleu、rouge-score等库提供了便捷的计算方式。新兴趋势包括基于BERT等模型的语义评估和大语言模型评估器,但传统指标仍占

数据知道的博客 9421

使用 MATLAB/Simulink 构建一个 核电站汽轮机与发电机协调控制系统 的仿真模型

在核电机组中,汽轮机-发电机系统是将核能最终转化为电能的关键环节。与火电机组不同,核反应堆的热惯性大、功率调节慢,因此汽轮机的控制策略需与反应堆特性相匹配,实现安全、稳定、高效的能量转换。维持发电机输出功率/频率稳定(响应电网调度)调节汽轮机进汽压力(匹配一回路蒸汽供应)实现“堆-机协调控制”保障机组在启停、变负荷、甩负荷等工况下的安全性本教程将带你使用构建一个核电站汽轮机与发电机协调控制系统汽轮机调速系统(调门控制)建模发电机与电网接口建模(同步机)

xiaoheshang_123的博客 190

Windows系统编译pgvector完整指南:从零构建PostgreSQL向量搜索扩展

PostgreSQL向量搜索扩展pgvector为AI应用提供了强大的相似性搜索能力,但在Windows平台上的编译过程常常让开发者感到棘手。本文将为你提供一套完整的Windows编译解决方案,涵盖从环境准备到编译验证的全过程,帮助你在Windows系统上成功部署这个强大的向量搜索工具。 pgvector是一个开源的PostgreSQL扩展,支持单精度、半精度、二进制和稀疏向量存储,提供L2距离

gitblog_07156的博客 462

【AIZ智能体】Codex 全能AI助手从部署到使用实战操作详解

Codex 全能AI助手从部署到使用实战操作详解

congge 2990

PostgreSQL笔记4: 市场地位、生态全景与行业应用实践

技术能力实现方式适用场景全文检索tsvectortsquery+ GIN 索引内容搜索、日志分析半结构化数据JSONB类型 + GIN 索引灵活 Schema、配置存储向量检索pgvector扩展 + IVFFlat/HNSW 索引RAG、推荐系统、语义搜索混合查询上述能力的 SQL 组合复杂业务场景的统一数据访问表结构创建(若已存在则跳过)插入三条测试数据(包含 JSONB 和向量字段)全文检索查询(tsvectortsqueryJSONB 包含查询(@>运算符)

Wang的专栏 529

C#解析基恩士(KEYENCE)PLC数据:KV协议通信与实时监控实现

KV协议是基恩士PLC使用的一种专用通信协议,通常用于与上位机进行数据交换。KV协议通过RS-232C、RS-485、Ethernet等接口与上位机通信,支持多种数据类型的读写,包括但不限于位、字、双字、浮点数等。简洁易用:KV协议的通信格式简单,易于实现。支持多种数据类型:支持位数据、字数据、定长字符串等。高效实时性:能够提供较高的实时数据交换性能,适合实时监控应用。本文介绍了如何通过C#与基恩士KV系列PLC进行通信,实现数据的解析和实时监控功能。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1811

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

dbskill 更新机制完整拆解:6 步执行链路与 5 条安全边界

你刚装完 dbskill——dontbesilent 的商业诊断 Skills 工具箱,里面装着 34 个用于诊断商业模式、打磨内容、管理决策状态的 Skill。用了一阵子,对话末尾跳出一行「dbskill v3.0.3:xxx,回复 1,我现在帮你更新」。你回了个 1,Agent 没有反问「确定吗」,直接开干。它接下来做了哪几件事?会不会顺手把你 `~/.dbs/` 里的诊断存档覆盖掉?这篇 w

gitblog_01014的博客 896

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Project AIRI 记忆系统构建实录:基于 pgvector.rs 与 Drizzle ORM 的语义召回、遗忘曲线与情感权重设计

> 本文以 Project AIRI(moeru-ai/airi)2025-04-06 开发日志为蓝本,完整还原其「记忆更新」的第一层建造过程:从向量数据库选型、嵌入模型推荐、Drizzle 表结构与 HNSW 索引,到语义相似度 + 时间衰减的组合重排查询,再到面向人类记忆机制(遗忘曲线、情绪干扰、随机想起)的交互式实验平台,并对照仓库源码(telegram-bot 集成)给出可运行的实战细节。

gitblog_01034的博客 1085

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Immich 搜索体系深度解析:Postgres 向量检索、高级过滤器与 CLIP 模型选型实践

本文围绕 Immich 官方的 Searching 文档展开,系统讲解 Immich 的两大搜索体系:基于 Postgres 的元数据/高级过滤器搜索,以及由 VectorChord 扩展与 CLIP 模型驱动的自由文本语义搜索(俗称 Smart Search)。文中不仅完整保留了官方文档的过滤器类型、模型选型方法论与基准数据,还结合服务端与机器学习服务的源码实现,说明从一次查询到向量近邻排序的完

gitblog_01026的博客 322

Project AIRI 记忆系统开发日志:基于 pgvector.rs 与时间衰减重排的 RAG 实战

这篇技术文章脱胎于 Project AIRI 团队的开发日志(DevLog @ 2025.04.06),完整还原了 AIRI 记忆系统从零构建的全过程:Vector DB 选型与踩坑、嵌入模型基准对比、基于 Drizzle ORM 的向量检索查询、以及"相似度 + 时间衰减"的混合重排算法。读者读完后可以掌握一套可直接落地的自托管 RAG 记忆系统实现方案,并能对照仓库源码(`integratio

gitblog_06807的博客 172

Semantic Search Agent 验证报告深度解析:RAG 语义搜索智能体的测试体系与生产就绪评估

本文以 [VALIDATION_REPORT.md](https://link.gitcode.com/i/f189f4b3e9efb12f012ee6fe5a5ac49e) 为主线,深入解读一份基于 Pydantic AI 与 PostgreSQL/PGVector 构建的 RAG 语义搜索智能体(Semantic Search Agent)的完整验证报告。报告系统覆盖了需求合规性(8 大需求类

gitblog_00484的博客 330

从零理解Embedding向量:原理、实战与RAG应用全解析

在人工智能和自然语言处理领域,将非结构化数据转化为计算机可处理的形式是核心挑战。Embedding(向量嵌入)技术通过将文本、图像等高维数据映射到低维稠密向量空间,实现了语义信息的数学化表示。其技术原理基于深度学习模型,如Transformer架构,能够捕捉词汇和句子的上下文含义。这项技术的核心价值在于为语义计算提供了统一接口,使得计算机能够通过向量距离(如余弦相似度)量化文本间的语义相关性。在实际应用场景中,Embedding是构建语义搜索系统、推荐引擎和分类模型的基础组件。特别是在当前热门的RAG(检索

weixin_33693070的博客 379

如何解决pgvector在Windows平台的编译难题?开发者必备排错指南

pgvector作为PostgreSQL的向量搜索扩展,为数据库提供了高效的向量相似性搜索能力。然而在Windows环境下编译该扩展时,开发者常遇到各种阻碍。本文将系统梳理编译过程中的典型问题,提供从错误诊断到解决方案的完整路径,帮助开发者顺利完成编译工作。 ## 定位编译失败:从错误日志提取关键信息 当在Windows系统中编译pgvector时,常见的错误提示主要有两类,这些信息是问题诊断

gitblog_00586的博客 482

Multica:AI智能体协同编程平台的技术架构与实践

在AI编程领域,智能体协同技术正成为提升开发效率的关键突破点。通过将AI智能体视为一等公民,构建类人类团队的协作机制,可以有效解决传统AI编程工具存在的任务碎片化问题。其核心技术原理包括状态机引擎、向量检索系统和实时通信协议,这些技术组合使得AI智能体能够自主沟通、管理任务生命周期并积累可复用技能。从工程实践角度看,采用TypeScript+Go的全栈架构配合PostgreSQL的向量扩展,既保证了系统性能又实现了灵活的语义检索能力。这类技术特别适用于需要并行处理多个开发任务的前端迁移、测试覆盖提升等场景,

weixin_30411819的博客 663

MagicAnimate 完整指南:一张静态照片生成连贯人物动画的 6 步流程

MagicAnimate 是一个基于扩散模型(通过逐步去噪生成图像的生成式模型)的 AI 动画生成工具:输入一张静态人物照片和一段驱动视频,它就能让照片里的人物按照视频中的动作动起来,并保持时间一致性——帧与帧之间不闪烁、不跳变,人物长相和服装也保持稳定。项目出自 CVPR 2024 论文 *MagicAnimate: Temporally Consistent Human Image Anima

gitblog_01112的博客 1047

使用Rag 命中用户feedback提升triage agent 准确率

本文提出使用RAG(检索增强生成)技术提升多租户场景下智能分诊系统的准确性。针对租户业务差异大、数据安全要求高等约束,方案采用大语言模型(LLM)为核心,通过构建向量知识库存储历史标注数据,在新反馈到来时检索相似案例注入提示词,实现无需模型微调的"类记忆"效果。技术实现选用MaxKB框架和pgvector数据库,通过语义检索+上下文增强的方式,在测试中达到了100%的分类准确率。相比微调方案,RAG具有更新灵活、成本低、可溯源等优势,特别适合知识频繁变更的场景,为后续智能化升级奠定了基础

陈锐的技术笔记 1191

10分钟跑通ArtCraft:AI图像生成桌面应用安装指南

ArtCraft 是一款 Rust + Tauri 架构的 AI 图像生成桌面应用,内置 62 个生成模型并对接多家提供商,图像、视频、3D、音频创作全部在本地窗口完成。 [![ArtCraft AI图像生成桌面应用示例:雪山光径场景](https://raw.gitcode.com/GitHub_Trending/ar/artcraft/raw/dc6006f6251898137fc2249b

gitblog_01038的博客 169
上一篇: PostgreSQL笔记42: pgvector扩展安装与配置完全指南
下一篇: PostgreSQL笔记44:pgvector向量操作与查询实践
Wang's Blog
博客等级 码龄12年 7438粉丝 2422原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值