本文目录导读:

在PHP中实现余弦相似度主要涉及文本向量化和数学计算两个核心步骤,以下是完整的实现方案:
基础余弦相似度函数
<?php
/**
* 计算两个向量的余弦相似度
* @param array $vectorA 向量A
* @param array $vectorB 向量B
* @return float 余弦相似度 (-1到1)
*/
function cosineSimilarity(array $vectorA, array $vectorB): float {
// 确保两个向量长度相同
if (count($vectorA) !== count($vectorB)) {
throw new InvalidArgumentException('向量维度必须相同');
}
$dotProduct = 0;
$normA = 0;
$normB = 0;
foreach ($vectorA as $i => $valueA) {
$valueB = $vectorB[$i];
$dotProduct += $valueA * $valueB;
$normA += $valueA * $valueA;
$normB += $valueB * $valueB;
}
$normA = sqrt($normA);
$normB = sqrt($normB);
// 防止除以0
if ($normA == 0 || $normB == 0) {
return 0.0;
}
return $dotProduct / ($normA * $normB);
}
文本向量化实现
1 简单词频向量化
<?php
/**
* 将文本转为词频向量
* @param string $text 输入文本
* @param array $vocabulary 词汇表(可选)
* @return array 词频向量
*/
function textToVector(string $text, array $vocabulary = []): array {
// 分词(简单实现,可根据需求改进)
$words = preg_split('/\s+/', mb_strtolower($text));
// 去除标点符号
$words = array_map(function($word) {
return preg_replace('/[^\w\s]/u', '', $word);
}, $words);
// 过滤空词
$words = array_filter($words);
// 统计词频
$wordFreq = array_count_values($words);
// 如果没有提供词汇表,使用文本中的词构建
if (empty($vocabulary)) {
$vocabulary = array_keys($wordFreq);
}
// 构建向量
$vector = [];
foreach ($vocabulary as $word) {
$vector[] = $wordFreq[$word] ?? 0;
}
return $vector;
}
/**
* 计算两段文本的余弦相似度
*/
function textCosineSimilarity(string $textA, string $textB): float {
// 构建联合词汇表
$wordsA = array_keys(array_count_values(
array_filter(preg_split('/\s+/', mb_strtolower($textA)))
));
$wordsB = array_keys(array_count_values(
array_filter(preg_split('/\s+/', mb_strtolower($textB)))
));
$vocabulary = array_unique(array_merge($wordsA, $wordsB));
// 向量化
$vectorA = textToVector($textA, $vocabulary);
$vectorB = textToVector($textB, $vocabulary);
return cosineSimilarity($vectorA, $vectorB);
}
2 TF-IDF向量化(更精确)
<?php
class TFIDFSimilarity {
private array $documents;
private array $vocabulary = [];
private array $idfValues = [];
public function __construct(array $documents) {
$this->documents = $documents;
$this->buildVocabulary();
$this->calculateIDF();
}
private function buildVocabulary(): void {
$allWords = [];
foreach ($this->documents as $doc) {
$words = $this->tokenize($doc);
$allWords = array_merge($allWords, $words);
}
$this->vocabulary = array_unique($allWords);
}
private function tokenize(string $text): array {
$text = mb_strtolower($text);
$words = preg_split('/\s+/', $text);
return array_filter($words, function($word) {
return !empty(trim($word));
});
}
private function calculateIDF(): void {
$docCount = count($this->documents);
foreach ($this->vocabulary as $word) {
$docWithWord = 0;
foreach ($this->documents as $doc) {
$words = $this->tokenize($doc);
if (in_array($word, $words)) {
$docWithWord++;
}
}
// IDF = log(文档总数 / 包含该词的文档数)
$this->idfValues[$word] = log(($docCount + 1) / ($docWithWord + 1)) + 1;
}
}
public function textToTfidfVector(string $text): array {
$words = $this->tokenize($text);
$wordCount = count($words);
$vector = [];
foreach ($this->vocabulary as $word) {
$tf = empty($words) ? 0 : array_count_values($words)[$word] ?? 0;
$tf = $tf / $wordCount; // 归一化词频
$idf = $this->idfValues[$word] ?? 0;
$vector[] = $tf * $idf;
}
return $vector;
}
public function calculateSimilarity(string $textA, string $textB): float {
$vectorA = $this->textToTfidfVector($textA);
$vectorB = $this->textToTfidfVector($textB);
return cosineSimilarity($vectorA, $vectorB);
}
}
完整使用示例
<?php
// 示例1:简单文本相似度比较
$text1 = "机器学习和深度学习是人工智能的核心技术";
$text2 = "深度学习在自然语言处理中有广泛应用";
$similarity = textCosineSimilarity($text1, $text2);
echo "文本相似度: " . round($similarity, 4) . PHP_EOL;
// 示例2:使用TF-IDF
$documents = [
"人工智能可以模拟人类智能",
"机器学习是人工智能的重要分支",
"深度学习让机器学习更加强大",
"自然语言处理需要深度学习技术",
"Python是机器学习常用的编程语言"
];
$tfidf = new TFIDFSimilarity($documents);
$query1 = "人工智能机器学习";
$query2 = "深度学习自然语言";
$similarity = $tfidf->calculateSimilarity($query1, $query2);
echo "TF-IDF相似度: " . round($similarity, 4) . PHP_EOL;
// 示例3:文档搜索
function searchSimilarDocuments(string $query, array $documents, TFIDFSimilarity $tfidf): array {
$results = [];
foreach ($documents as $index => $doc) {
$similarity = $tfidf->calculateSimilarity($query, $doc);
$results[] = [
'index' => $index,
'document' => $doc,
'similarity' => $similarity
];
}
usort($results, function($a, $b) {
return $b['similarity'] <=> $a['similarity'];
});
return $results;
}
$query = "人工智能技术";
$results = searchSimilarDocuments($query, $documents, $tfidf);
echo "搜索\"$query\"的结果:" . PHP_EOL;
foreach (array_slice($results, 0, 3) as $result) {
echo sprintf("相似度 %.4f: %s\n",
$result['similarity'],
$result['document']
);
}
性能优化建议
<?php
// 1. 使用缓存存储向量
class CachedVectorSimilarity {
private array $vectorCache = [];
private TFIDFSimilarity $tfidf;
public function __construct(TFIDFSimilarity $tfidf) {
$this->tfidf = $tfidf;
}
public function getVector(string $text): array {
$key = md5($text);
if (!isset($this->vectorCache[$key])) {
$this->vectorCache[$key] = $this->tfidf->textToTfidfVector($text);
}
return $this->vectorCache[$key];
}
}
// 2. 使用SplFixedArray提高性能
function optimizedCosineSimilarity(array $vectorA, array $vectorB): float {
$a = SplFixedArray::fromArray($vectorA);
$b = SplFixedArray::fromArray($vectorB);
$dotProduct = 0;
$normA = 0;
$normB = 0;
for ($i = 0; $i < $a->getSize(); $i++) {
$dotProduct += $a[$i] * $b[$i];
$normA += $a[$i] * $a[$i];
$normB += $b[$i] * $b[$i];
}
return $dotProduct / (sqrt($normA) * sqrt($normB));
}
高级实现:使用外部库
# 使用composer安装数学库 composer require markrogoyski/math-php
<?php
use MathPHP\LinearAlgebra\Vector;
function cosineSimilarityWithLibrary(array $vectorA, array $vectorB): float {
$v1 = new Vector($vectorA);
$v2 = new Vector($vectorB);
return $v1->dotProduct($v2) / ($v1->l2Norm() * $v2->l2Norm());
}
注意事项
- 中文分词:中文文本需要先分词,建议使用
jieba-php或scws等分词库 - 停用词处理:过滤常见的无意义词(的、了、是等)
- 向量维度:词汇表大小直接影响性能和内存使用
- 精度问题:使用
float类型时注意精度损失
这个实现提供了从基础到高级的多种方案,可以根据具体需求选择合适的实现方式。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播