本文目录导读:

在PHP项目中实现基于向量的相似度检索,主要有以下几种方法:
使用 PHP 扩展 + 外部向量数据库
Milvus + PHP SDK
// 安装:composer require milvus-io/milvus-sdk-php
use Milvus\Client;
use Milvus\Collection;
use Milvus\Index\IndexType;
// 连接 Milvus
$client = new Client('localhost', 19530);
// 插入向量数据
$collection = $client->createCollection([
'collection_name' => 'documents',
'dimension' => 128, // 向量维度
'metric_type' => MetricType::IP, // 内积相似度
]);
// 搜索相似向量
$results = $collection->search([
'vector' => [0.1, 0.2, ..., 0.8], // 查询向量
'top_k' => 10,
'metric_type' => MetricType::COSINE, // 余弦相似度
]);
Pinecone(云服务)
// 安装:composer require pinecone-io/pinecone-sdk
use Pinecone\Client;
$client = new Client([
'apiKey' => 'your-api-key',
'environment' => 'us-west1-gcp'
]);
// 插入向量
$upsertResponse = $client->index('documents')->upsert([
'vectors' => [
[
'id' => 'doc1',
'values' => [0.1, 0.2, 0.3, 0.4],
'metadata' => ['title' => '文档1']
]
]
]);
// 查询相似向量
$queryResponse = $client->index('documents')->query([
'vector' => [0.1, 0.2, 0.3, 0.4],
'topK' => 5,
'includeMetadata' => true
]);
纯 PHP 实现(小规模数据)
余弦相似度计算
class VectorSimilarity {
/**
* 计算余弦相似度
*/
public static function cosineSimilarity(array $vectorA, array $vectorB): float {
$dotProduct = 0;
$normA = 0;
$normB = 0;
for ($i = 0; $i < count($vectorA); $i++) {
$dotProduct += $vectorA[$i] * $vectorB[$i];
$normA += $vectorA[$i] * $vectorA[$i];
$normB += $vectorB[$i] * $vectorB[$i];
}
if ($normA == 0 || $normB == 0) {
return 0;
}
return $dotProduct / (sqrt($normA) * sqrt($normB));
}
/**
* 计算欧几里得距离
*/
public static function euclideanDistance(array $vectorA, array $vectorB): float {
$sum = 0;
for ($i = 0; $i < count($vectorA); $i++) {
$diff = $vectorA[$i] - $vectorB[$i];
$sum += $diff * $diff;
}
return sqrt($sum);
}
/**
* 计算点积相似度
*/
public static function dotProduct(array $vectorA, array $vectorB): float {
$dotProduct = 0;
for ($i = 0; $i < count($vectorA); $i++) {
$dotProduct += $vectorA[$i] * $vectorB[$i];
}
return $dotProduct;
}
}
// 使用示例
$vector1 = [0.1, 0.2, 0.3, 0.4];
$vector2 = [0.15, 0.25, 0.35, 0.45];
$similarity = VectorSimilarity::cosineSimilarity($vector1, $vector2);
echo "余弦相似度: " . $similarity;
向量索引实现(小规模)
class VectorIndex {
private array $vectors = [];
private array $metadata = [];
/**
* 添加向量到索引
*/
public function add(string $id, array $vector, array $meta = []): void {
$this->vectors[$id] = $vector;
$this->metadata[$id] = $meta;
}
/**
* 搜索最相似的向量
*/
public function search(array $queryVector, int $topK = 10): array {
$scores = [];
foreach ($this->vectors as $id => $vector) {
$score = VectorSimilarity::cosineSimilarity($queryVector, $vector);
$scores[$id] = $score;
}
// 排序获取 topK
arsort($scores);
$results = [];
$count = 0;
foreach ($scores as $id => $score) {
if ($count >= $topK) break;
$results[] = [
'id' => $id,
'score' => $score,
'metadata' => $this->metadata[$id] ?? []
];
$count++;
}
return $results;
}
/**
* 批量添加向量
*/
public function batchAdd(array $vectors): void {
foreach ($vectors as $item) {
$this->add($item['id'], $item['vector'], $item['metadata'] ?? []);
}
}
}
优化的大规模方案
使用 SQLite + FTS5 + 向量扩展
// 需要安装 sqlite-vector 扩展
class SQLiteVectorSearch {
private PDO $pdo;
public function __construct(string $dbPath) {
$this->pdo = new PDO("sqlite:$dbPath");
$this->initDatabase();
}
private function initDatabase(): void {
$this->pdo->exec("
CREATE VIRTUAL TABLE IF NOT EXISTS vectors USING vec0(
id INTEGER PRIMARY KEY,
embedding float[128]
)
");
}
public function insert(int $id, array $vector): void {
$stmt = $this->pdo->prepare(
"INSERT INTO vectors (id, embedding) VALUES (?, ?)"
);
$stmt->execute([$id, json_encode($vector)]);
}
public function search(array $queryVector, int $limit = 10): array {
$stmt = $this->pdo->prepare("
SELECT
id,
distance
FROM vectors
WHERE embedding MATCH ?
ORDER BY distance
LIMIT ?
");
$stmt->execute([json_encode($queryVector), $limit]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
完整的项目实现示例
class DocumentSearchEngine {
private VectorIndex $index;
private array $documents;
public function __construct() {
$this->index = new VectorIndex();
$this->documents = [];
}
/**
* 添加文档
*/
public function addDocument(string $id, string $content): void {
$vector = $this->textToVector($content);
$this->documents[$id] = $content;
$this->index->add($id, $vector, [
'content' => $content,
'length' => strlen($content)
]);
}
/**
* 将文本转换为向量(简化示例)
* 实际应用中应使用如 OpenAI Embedding API 或本地模型
*/
private function textToVector(string $text): array {
// 这是一个简化的示例,实际应该使用词嵌入模型
$words = str_word_count($text, 1);
$vector = array_fill(0, 128, 0);
foreach ($words as $word) {
$hash = crc32($word);
$index = abs($hash) % 128;
$vector[$index] += 1;
}
// 归一化
$norm = sqrt(array_sum(array_map(function($v) {
return $v * $v;
}, $vector)));
if ($norm > 0) {
$vector = array_map(function($v) use ($norm) {
return $v / $norm;
}, $vector);
}
return $vector;
}
/**
* 搜索相似文档
*/
public function search(string $query, int $limit = 5): array {
$queryVector = $this->textToVector($query);
$results = $this->index->search($queryVector, $limit);
// 格式化结果
$formatted = [];
foreach ($results as $result) {
$formatted[] = [
'id' => $result['id'],
'content' => $this->documents[$result['id']],
'score' => $result['score'],
'relevance' => round($result['score'] * 100, 2) . '%'
];
}
return $formatted;
}
}
// 使用示例
$engine = new DocumentSearchEngine();
// 添加文档
$engine->addDocument('1', 'PHP是一种流行的服务器端脚本语言');
$engine->addDocument('2', 'JavaScript用于前端网页开发');
$engine->addDocument('3', 'Python在数据科学领域广泛应用');
// 搜索
$results = $engine->search('编程语言');
foreach ($results as $result) {
echo "文档: {$result['content']}\n";
echo "相似度: {$result['relevance']}\n\n";
}
性能优化建议
class OptimizedVectorSearch {
/**
* 使用近似最近邻算法(ANN)
*/
public function approximateSearch(array $query, int $k = 10): array {
// 使用空间分割或聚类算法
// KD-Tree, R-Tree, Product Quantization
}
/**
* 分片索引
*/
public function shardSearch(array $query, int $shards = 4): array {
$results = [];
// 并行搜索多个分片
$this->parallelSearch($query, $shards, $results);
return $this->mergeResults($results);
}
/**
* 向量量化压缩
*/
private function quantizeVector(array $vector, int $bits = 8): array {
// 将浮点数向量量化为低精度整数
$quantized = [];
foreach ($vector as $value) {
$quantized[] = (int)($value * (pow(2, $bits) - 1));
}
return $quantized;
}
}
选择建议
- 数据量 < 1万:使用纯PHP实现
- 数据量 1万-100万:使用SQLite向量扩展
- 数据量 > 100万:使用Milvus、Pinecone等专业向量数据库
- 需要实时更新:选择支持动态更新的方案
- 高并发场景:使用异步架构和缓存优化
这些方法可以根据你的具体需求进行调整,需要注意,实际的文本向量化通常需要使用预训练模型(如BERT、Word2Vec等),这里为了示例使用了简化的哈希方法。