本文目录导读:

在PHP项目中实现跨模态检索(Cross-Modal Retrieval)通常涉及文本、图像、音频等不同模态数据之间的相互搜索,以下是几种可行的实现方案:
基于向量嵌入的检索方法(推荐)
技术架构
文本/图像 → 嵌入模型 → 向量数据库 → 相似度检索
实现步骤
1 安装依赖
composer require openai-php/client composer require elasticsearch/elasticsearch # 或其他向量数据库
2 生成向量嵌入
<?php
// 使用OpenAI的CLIP模型(通过API调用)
class EmbeddingGenerator {
private $client;
public function __construct() {
$this->client = OpenAI::client(getenv('OPENAI_API_KEY'));
}
// 生成文本嵌入
public function generateTextEmbedding(string $text): array {
$response = $this->client->embeddings()->create([
'model' => 'text-embedding-ada-002',
'input' => $text,
]);
return $response->embeddings[0]->embedding;
}
// 生成图像嵌入(需要base64编码的图像数据)
public function generateImageEmbedding(string $imagePath): array {
$imageData = base64_encode(file_get_contents($imagePath));
// 使用CLIP模型或其他多模态模型
$response = $this->client->embeddings()->create([
'model' => 'clip-vit-base-patch32',
'input' => "data:image/jpeg;base64,{$imageData}",
]);
return $response->embedding;
}
}
3 向量数据库存储
<?php
// 使用Elasticsearch作为向量数据库
class VectorStorage {
private $client;
public function __construct() {
$this->client = Elasticsearch\ClientBuilder::create()
->setHosts(['localhost:9200'])
->build();
}
// 创建索引并存储向量
public function storeVector(string $id, array $vector, string $type, array $metadata = []) {
$params = [
'index' => 'multimodal_items',
'id' => $id,
'body' => [
'vector' => $vector,
'content_type' => $type, // 'text', 'image', 'audio'
'metadata' => $metadata,
'timestamp' => time()
]
];
return $this->client->index($params);
}
// 相似度检索
public function searchSimilar(array $queryVector, int $topK = 10) {
$params = [
'index' => 'multimodal_items',
'body' => [
'size' => $topK,
'query' => [
'script_score' => [
'query' => ['match_all' => new \stdClass()],
'script' => [
'source' => "cosineSimilarity(params.query_vector, 'vector') + 1.0",
'params' => ['query_vector' => $queryVector]
]
]
]
]
];
return $this->client->search($params);
}
}
4 完整的检索服务
<?php
class CrossModalRetrievalService {
private $embeddingGenerator;
private $vectorStorage;
public function __construct() {
$this->embeddingGenerator = new EmbeddingGenerator();
$this->vectorStorage = new VectorStorage();
}
// 文本搜索图像
public function textToImage(string $query, int $topK = 10) {
$queryVector = $this->embeddingGenerator->generateTextEmbedding($query);
$results = $this->vectorStorage->searchSimilar($queryVector, $topK);
return $this->formatResults($results, 'image');
}
// 图像搜索文本
public function imageToText(string $imagePath, int $topK = 10) {
$queryVector = $this->embeddingGenerator->generateImageEmbedding($imagePath);
$results = $this->vectorStorage->searchSimilar($queryVector, $topK);
return $this->formatResults($results, 'text');
}
private function formatResults(array $results, string $targetType) {
$formatted = [];
foreach ($results['hits']['hits'] as $hit) {
if ($hit['_source']['content_type'] !== $targetType) {
continue;
}
$formatted[] = [
'id' => $hit['_id'],
'score' => $hit['_score'],
'metadata' => $hit['_source']['metadata'],
'content_type' => $hit['_source']['content_type']
];
}
return $formatted;
}
}
使用现成的多模态检索服务
1 使用Pinecone向量数据库
<?php
use Pinecone\Client;
class PineconeRetrieval {
private $client;
public function __construct() {
$this->client = new Client([
'apiKey' => getenv('PINECONE_API_KEY'),
'environment' => 'us-west1-gcp'
]);
}
public function search(array $queryVector, int $topK = 10) {
$response = $this->client->query([
'vector' => $queryVector,
'topK' => $topK,
'includeMetadata' => true
]);
return $response['matches'];
}
}
2 使用Milvus
<?php
// 使用Milvus PHP SDK
use Milvus\MilvusClient;
class MilvusRetrieval {
private $client;
public function __construct() {
$this->client = new MilvusClient([
'host' => 'localhost',
'port' => 19530
]);
}
// 创建集合
public function createCollection(string $collectionName, int $dimension = 512) {
$this->client->createCollection([
'collection_name' => $collectionName,
'fields' => [
[
'name' => 'embedding',
'type' => DataType::FLOAT_VECTOR,
'params' => ['dim' => $dimension]
],
[
'name' => 'metadata',
'type' => DataType::JSON
]
],
'metric_type' => 'IP', // 内积相似度
]);
}
}
基于特征提取的轻量级方案
1 使用图像哈希和文本特征
<?php
class LightweightCrossModal {
// 图像特征提取(颜色直方图)
public function extractImageFeatures(string $imagePath): array {
$image = imagecreatefromjpeg($imagePath);
$width = imagesx($image);
$height = imagesy($image);
$features = [];
for ($x = 0; $x < $width; $x += 10) {
for ($y = 0; $y < $height; $y += 10) {
$rgb = imagecolorat($image, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$features[] = ($r + $g + $b) / 3;
}
}
imagedestroy($image);
return $features;
}
// 文本特征提取(TF-IDF)
public function extractTextFeatures(string $text): array {
$words = str_word_count($text, 1);
$wordCount = array_count_values($words);
$totalWords = count($words);
$features = [];
foreach ($wordCount as $word => $count) {
$features[$word] = $count / $totalWords;
}
return $features;
}
// 归一化特征
public function normalizeFeatures(array $features): array {
$max = max($features);
$min = min($features);
if ($max == $min) return $features;
return array_map(function($val) use ($min, $max) {
return ($val - $min) / ($max - $min);
}, $features);
}
}
数据预处理和索引构建
1 批量数据处理
<?php
class DataIndexer {
private $retrievalService;
public function __construct() {
$this->retrievalService = new CrossModalRetrievalService();
}
// 批量索引文本数据
public function indexTextData(array $textItems) {
foreach ($textItems as $item) {
$embedding = $this->retrievalService->embeddingGenerator
->generateTextEmbedding($item['content']);
$this->retrievalService->vectorStorage
->storeVector($item['id'], $embedding, 'text', $item['metadata']);
}
}
// 批量索引图像数据
public function indexImageData(array $imageItems) {
foreach ($imageItems as $item) {
$embedding = $this->retrievalService->embeddingGenerator
->generateImageEmbedding($item['path']);
$this->retrievalService->vectorStorage
->storeVector($item['id'], $embedding, 'image', [
'path' => $item['path'],
'caption' => $item['caption'] ?? ''
]);
}
}
}
API接口实现
1 创建REST API
<?php
// routes/api.php
Route::post('/api/cross-modal/text-to-image', function(Request $request) {
$service = new CrossModalRetrievalService();
$results = $service->textToImage(
$request->input('query'),
$request->input('top_k', 10)
);
return response()->json([
'success' => true,
'results' => $results
]);
});
Route::post('/api/cross-modal/image-to-text', function(Request $request) {
$service = new CrossModalRetrievalService();
$imageFile = $request->file('image');
$imagePath = $imageFile->store('temp');
$results = $service->imageToText(
storage_path('app/' . $imagePath),
$request->input('top_k', 10)
);
return response()->json([
'success' => true,
'results' => $results
]);
});
优化建议
1 性能优化
// 使用缓存
class CachedRetrievalService extends CrossModalRetrievalService {
private $cache;
public function __construct() {
parent::__construct();
$this->cache = new RedisCache();
}
public function textToImage(string $query, int $topK = 10) {
$cacheKey = "cross_modal:{$query}:{$topK}";
if ($cached = $this->cache->get($cacheKey)) {
return $cached;
}
$results = parent::textToImage($query, $topK);
$this->cache->set($cacheKey, $results, 3600); // 1小时缓存
return $results;
}
}
2 批量检索优化
// 使用异步处理
$promises = [];
foreach ($queries as $query) {
$promises[] = async(function() use ($query) {
return $service->textToImage($query);
});
}
$results = await_all($promises);
推荐方案:
- 生产环境:使用OpenAI/CLIP + Pinecone/Elasticsearch向量数据库
- 轻量级场景:使用传统特征提取 + MySQL + 近似最近邻搜索
- 快速原型:使用现成的多模态检索API服务
需要根据具体的数据规模、检索精度要求和预算来选择合适的方案,对于大型项目,建议使用专门的向量数据库和预训练的多模态模型。