本文目录导读:

我会为您介绍如何在PHP项目中实现基于内容的推荐系统,并提供一个完整的实现示例。 推荐的核心原理 的推荐通过分析项目特征和用户历史偏好的相似度来推荐内容,核心步骤:
- 提取项目特征(如标签、关键词、分类等)
- 构建用户偏好模型(基于用户历史行为)
- 计算相似度
- 生成推荐列表
完整PHP实现示例
数据模型定义
<?php
// Item.php - 项目模型
class Item {
public $id;
public $title;
public $features; // 特征向量,如 ['action'=>0.8, 'comedy'=>0.2]
public $tags; // 标签数组
public function __construct($id, $title, $features = [], $tags = []) {
$this->id = $id;
$this->title = $title;
$this->features = $features;
$this->tags = $tags;
}
}
// UserPreference.php - 用户偏好模型
class UserPreference {
public $userId;
public $profileVector; // 用户偏好向量
public $likedItems; // 喜欢的项目ID数组
public function __construct($userId, $profileVector = [], $likedItems = []) {
$this->userId = $userId;
$this->profileVector = $profileVector;
$this->likedItems = $likedItems;
}
}
核心推荐引擎
<?php
// ContentBasedRecommender.php
class ContentBasedRecommender {
private $items = [];
private $users = [];
/**
* 添加项目到推荐系统
*/
public function addItem(Item $item) {
$this->items[$item->id] = $item;
}
/**
* 添加用户偏好
*/
public function addUser(UserPreference $user) {
$this->users[$user->userId] = $user;
}
/**
* 计算两个特征向量的余弦相似度
*/
private function cosineSimilarity($vectorA, $vectorB) {
$dotProduct = 0;
$normA = 0;
$normB = 0;
// 获取所有特征键
$allKeys = array_unique(array_merge(array_keys($vectorA), array_keys($vectorB)));
foreach ($allKeys as $key) {
$valA = $vectorA[$key] ?? 0;
$valB = $vectorB[$key] ?? 0;
$dotProduct += $valA * $valB;
$normA += $valA * $valA;
$normB += $valB * $valB;
}
$normA = sqrt($normA);
$normB = sqrt($normB);
if ($normA == 0 || $normB == 0) {
return 0;
}
return $dotProduct / ($normA * $normB);
}
/**
* 基于标签计算相似度
*/
private function tagSimilarity($tagsA, $tagsB) {
if (empty($tagsA) || empty($tagsB)) {
return 0;
}
$intersection = array_intersect($tagsA, $tagsB);
$union = array_unique(array_merge($tagsA, $tagsB));
return count($intersection) / count($union);
}
/**
* 计算综合相似度
*/
private function calculateSimilarity(Item $itemA, Item $itemB, $featureWeight = 0.7, $tagWeight = 0.3) {
$featureSim = $this->cosineSimilarity($itemA->features, $itemB->features);
$tagSim = $this->tagSimilarity($itemA->tags, $itemB->tags);
return ($featureWeight * $featureSim) + ($tagWeight * $tagSim);
}
/**
* 构建用户偏好向量(基于用户喜欢的项目)
*/
public function buildUserProfile($userId) {
$user = $this->users[$userId] ?? null;
if (!$user) {
return [];
}
$profileVector = [];
$tagCount = [];
foreach ($user->likedItems as $itemId) {
if (isset($this->items[$itemId])) {
$item = $this->items[$itemId];
// 聚合特征向量
foreach ($item->features as $key => $value) {
if (!isset($profileVector[$key])) {
$profileVector[$key] = 0;
}
$profileVector[$key] += $value;
}
// 统计标签
foreach ($item->tags as $tag) {
if (!isset($tagCount[$tag])) {
$tagCount[$tag] = 0;
}
$tagCount[$tag]++;
}
}
}
// 归一化特征向量
$totalLiked = count($user->likedItems);
if ($totalLiked > 0) {
foreach ($profileVector as $key => $value) {
$profileVector[$key] = $value / $totalLiked;
}
}
$user->profileVector = $profileVector;
$user->tagCount = $tagCount;
return $profileVector;
}
/**
* 为用户生成推荐
*/
public function recommend($userId, $topN = 10) {
$user = $this->users[$userId] ?? null;
if (!$user) {
return [];
}
// 构建用户偏好
$this->buildUserProfile($userId);
$scores = [];
foreach ($this->items as $itemId => $item) {
// 跳过用户已经喜欢的项目
if (in_array($itemId, $user->likedItems)) {
continue;
}
// 计算项目与用户偏好的相似度
$featureSim = $this->cosineSimilarity($user->profileVector, $item->features);
// 计算标签匹配度(基于用户喜欢的标签频率)
$tagMatch = 0;
if (!empty($user->tagCount) && !empty($item->tags)) {
$matchCount = 0;
foreach ($item->tags as $tag) {
if (isset($user->tagCount[$tag])) {
$matchCount += $user->tagCount[$tag];
}
}
$maxPossible = max(array_values($user->tagCount)) * count($item->tags);
$tagMatch = $maxPossible > 0 ? $matchCount / $maxPossible : 0;
}
// 综合评分
$scores[$itemId] = (0.6 * $featureSim) + (0.4 * $tagMatch);
}
// 按分数降序排序
arsort($scores);
// 返回TopN推荐
return array_slice($scores, 0, $topN, true);
}
/**
* 获取项目相似项目(用于"猜你喜欢")
*/
public function getSimilarItems($itemId, $topN = 5) {
if (!isset($this->items[$itemId])) {
return [];
}
$targetItem = $this->items[$itemId];
$scores = [];
foreach ($this->items as $id => $item) {
if ($id == $itemId) continue;
$similarity = $this->calculateSimilarity($targetItem, $item);
$scores[$id] = $similarity;
}
arsort($scores);
return array_slice($scores, 0, $topN, true);
}
}
使用示例
<?php
// index.php - 使用示例
require_once 'ContentBasedRecommender.php';
// 初始化推荐系统
$recommender = new ContentBasedRecommender();
// 1. 添加项目(例如电影)
$recommender->addItem(new Item(1, '黑客帝国',
['action'=>0.9, 'sci-fi'=>0.9, 'drama'=>0.3],
['动作', '科幻', '经典']
));
$recommender->addItem(new Item(2, '星际穿越',
['action'=>0.4, 'sci-fi'=>0.9, 'drama'=>0.8],
['科幻', '剧情', '太空']
));
$recommender->addItem(new Item(3, '肖申克的救赎',
['action'=>0.1, 'drama'=>0.9, 'thriller'=>0.5],
['剧情', '经典', '励志']
));
$recommender->addItem(new Item(4, '盗梦空间',
['action'=>0.8, 'sci-fi'=>0.8, 'thriller'=>0.7],
['动作', '科幻', '悬疑']
));
// 2. 设置用户偏好
$userPref = new UserPreference(1, [], [1, 4]); // 用户喜欢电影1和4
$recommender->addUser($userPref);
// 3. 生成推荐
$recommendations = $recommender->recommend(1, 3);
echo "为用户推荐的电影:\n";
foreach ($recommendations as $itemId => $score) {
echo " - {$itemId}: 相似度 " . round($score, 3) . "\n";
}
// 4. 获取相似项目
$similarItems = $recommender->getSimilarItems(1, 3);
echo "\n与《黑客帝国》相似的电影:\n";
foreach ($similarItems as $itemId => $score) {
echo " - {$itemId}: 相似度 " . round($score, 3) . "\n";
}
MySQL数据库集成版本
<?php
// DatabaseRecommender.php - 数据库集成版本
class DatabaseRecommender {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
/**
* 从数据库获取所有项目特征
*/
public function getAllItems() {
$stmt = $this->db->query("SELECT * FROM items");
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 获取用户历史行为
*/
public function getUserHistory($userId) {
$stmt = $this->db->prepare("
SELECT item_id, rating
FROM user_ratings
WHERE user_id = ?
");
$stmt->execute([$userId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 生成并存储推荐结果
*/
public function generateAndStoreRecommendations($userId, $topN = 10) {
// 获取用户历史
$history = $this->getUserHistory($userId);
$likedItems = array_column($history, 'item_id');
// 获取所有项目
$allItems = $this->getAllItems();
// 计算推荐分数
$scores = [];
foreach ($allItems as $item) {
if (in_array($item['id'], $likedItems)) continue;
// 这里实现具体的推荐逻辑
$score = $this->calculateUserItemScore($userId, $item['id']);
$scores[$item['id']] = $score;
}
arsort($scores);
$recommendations = array_slice($scores, 0, $topN, true);
// 存储推荐结果
$this->storeRecommendations($userId, $recommendations);
return $recommendations;
}
private function storeRecommendations($userId, $recommendations) {
// 先清除旧的推荐
$stmt = $this->db->prepare("DELETE FROM recommendations WHERE user_id = ?");
$stmt->execute([$userId]);
// 插入新推荐
$stmt = $this->db->prepare("
INSERT INTO recommendations (user_id, item_id, score, created_at)
VALUES (?, ?, ?, NOW())
");
foreach ($recommendations as $itemId => $score) {
$stmt->execute([$userId, $itemId, $score]);
}
}
}
特征提取辅助类
<?php
// FeatureExtractor.php - 特征提取工具
class FeatureExtractor {
/**
* 从文本中提取关键词特征
*/
public static function extractTextFeatures($text) {
// 简单的TF-IDF实现
$words = str_word_count(strtolower($text), 1);
$wordCount = array_count_values($words);
// 去除停用词
$stopWords = ['the', 'a', 'an', 'in', 'on', 'at', 'for', 'of', 'to'];
foreach ($stopWords as $word) {
unset($wordCount[$word]);
}
// 归一化
$total = array_sum($wordCount);
if ($total > 0) {
foreach ($wordCount as $word => $count) {
$wordCount[$word] = $count / $total;
}
}
return $wordCount;
}
/**
* 从分类标签构建特征向量
*/
public static function extractTagsFeatures($tags, $allCategories) {
$features = [];
foreach ($allCategories as $category) {
$features[$category] = in_array($category, $tags) ? 1.0 : 0.0;
}
return $features;
}
}
优化建议
- 缓存机制:使用Redis缓存用户偏好向量和推荐结果
- 增量更新:用户新行为发生后只更新受影响的部分
- 混合推荐:结合协同过滤提高多样性
- 特征工程:使用更复杂的特征表示(如Word2Vec)
- 性能优化:对于大量项目使用向量数据库(如Milvus)
这个实现提供了完整的基于内容推荐系统基础,您可以根据实际需求调整相似度计算方式和特征权重。