本文目录导读:

我将为您创建一个完整的PHP基于内容的推荐系统,这个系统会根据物品的特征和用户的偏好来计算相似度,从而推荐最相关的内容。
数据库结构设计
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 物品表(以电影为例)
CREATE TABLE items (
id INT PRIMARY KEY AUTO_INCREMENT,VARCHAR(200) NOT NULL,
description TEXT,
genres VARCHAR(200), -- 类型,用逗号分隔
director VARCHAR(100),
actors TEXT, -- 演员列表,用逗号分隔
release_year INT,
rating DECIMAL(3,1),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 用户行为表(记录用户对物品的评分或浏览)
CREATE TABLE user_behavior (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
item_id INT,
rating DECIMAL(2,1), -- 1-5分
behavior_type ENUM('view', 'rate', 'purchase', 'favorite') DEFAULT 'view',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (item_id) REFERENCES items(id)
);
-- 测试数据
INSERT INTO items (title, description, genres, director, actors, release_year, rating) VALUES
('Inception', 'A thief who steals corporate secrets through dream-sharing technology', 'Sci-Fi,Action,Thriller', 'Christopher Nolan', 'Leonardo DiCaprio,Joseph Gordon-Levitt,Ellen Page', 2010, 8.8),
('Interstellar', 'A team of explorers travel through a wormhole in space', 'Sci-Fi,Drama,Adventure', 'Christopher Nolan', 'Matthew McConaughey,Anne Hathaway', 2014, 8.6),
('The Dark Knight', 'Batman faces the Joker', 'Action,Crime,Drama', 'Christopher Nolan', 'Christian Bale,Heath Ledger', 2008, 9.0),
('Titanic', 'A love story on the ill-fated ship', 'Romance,Drama', 'James Cameron', 'Leonardo DiCaprio,Kate Winslet', 1997, 7.8),
('Avatar', 'A paraplegic marine dispatched to the moon Pandora', 'Action,Adventure,Sci-Fi', 'James Cameron', 'Sam Worthington,Zoe Saldana', 2009, 7.8);
内容特征提取与相似度计算类
<?php
class ContentBasedRecommender {
private $pdo;
private $userId;
private $items;
private $userProfile;
public function __construct($pdo, $userId = null) {
$this->pdo = $pdo;
$this->userId = $userId;
$this->loadItems();
}
// 加载所有物品
private function loadItems() {
$stmt = $this->pdo->query("SELECT * FROM items");
$this->items = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 加载用户画像(基于用户历史行为)
public function buildUserProfile() {
if (!$this->userId) {
return null;
}
// 获取用户所有行为记录
$stmt = $this->pdo->prepare("
SELECT i.*, ub.rating as user_rating
FROM user_behavior ub
JOIN items i ON ub.item_id = i.id
WHERE ub.user_id = ? AND ub.rating > 0
");
$stmt->execute([$this->userId]);
$ratedItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($ratedItems)) {
return null;
}
// 构建用户特征向量
$profile = [
'genres' => [],
'directors' => [],
'actors' => [],
'avg_rating' => 0,
'preferred_year' => 0
];
$totalRating = 0;
$totalYear = 0;
$count = 0;
foreach ($ratedItems as $item) {
$weight = $item['user_rating']; // 评分作为权重
// 处理类型
if (!empty($item['genres'])) {
$genres = explode(',', $item['genres']);
foreach ($genres as $genre) {
$genre = trim($genre);
$profile['genres'][$genre] = ($profile['genres'][$genre] ?? 0) + $weight;
}
}
// 处理导演
if (!empty($item['director'])) {
$director = $item['director'];
$profile['directors'][$director] = ($profile['directors'][$director] ?? 0) + $weight;
}
// 处理演员(取前3个)
if (!empty($item['actors'])) {
$actors = explode(',', $item['actors']);
$actors = array_slice($actors, 0, 3);
foreach ($actors as $actor) {
$actor = trim($actor);
$profile['actors'][$actor] = ($profile['actors'][$actor] ?? 0) + $weight;
}
}
$totalRating += $item['user_rating'];
$totalYear += $item['release_year'];
$count++;
}
// 计算平均值和权重
$profile['avg_rating'] = $totalRating / $count;
$profile['preferred_year'] = $totalYear / $count;
// 归一化
foreach ($profile['genres'] as &$val) {
$val = $val / $totalRating;
}
foreach ($profile['directors'] as &$val) {
$val = $val / $totalRating;
}
foreach ($profile['actors'] as &$val) {
$val = $val / $totalRating;
}
return $profile;
}
// 计算物品之间的余弦相似度
public function calculateCosineSimilarity($item1, $item2) {
$features1 = $this->extractFeatures($item1);
$features2 = $this->extractFeatures($item2);
// 合并所有特征
$allFeatures = array_unique(array_merge(
array_keys($features1['genres']),
array_keys($features2['genres'])
));
$dotProduct = 0;
$norm1 = 0;
$norm2 = 0;
foreach ($allFeatures as $feature) {
$v1 = $features1['genres'][$feature] ?? 0;
$v2 = $features2['genres'][$feature] ?? 0;
$dotProduct += $v1 * $v2;
$norm1 += $v1 * $v1;
$norm2 += $v2 * $v2;
}
// 如果两个物品都没有特征,返回0
if ($norm1 == 0 || $norm2 == 0) {
return 0;
}
return $dotProduct / (sqrt($norm1) * sqrt($norm2));
}
// 计算物品与用户画像的相似度
public function calculateItemUserSimilarity($item, $userProfile) {
if (!$userProfile) {
return 0;
}
$score = 0;
$weights = [
'genre_weight' => 0.4,
'director_weight' => 0.2,
'actor_weight' => 0.2,
'rating_weight' => 0.1,
'year_weight' => 0.1
];
// 类型相似度
$itemGenres = explode(',', $item['genres'] ?? '');
$genreScore = 0;
foreach ($itemGenres as $genre) {
$genre = trim($genre);
if (isset($userProfile['genres'][$genre])) {
$genreScore += $userProfile['genres'][$genre];
}
}
$score += $genreScore * $weights['genre_weight'];
// 导演相似度
$directorScore = 0;
if (isset($userProfile['directors'][$item['director']])) {
$directorScore = $userProfile['directors'][$item['director']];
}
$score += $directorScore * $weights['director_weight'];
// 演员相似度
$actorScore = 0;
if (!empty($item['actors'])) {
$actors = explode(',', $item['actors']);
foreach ($actors as $actor) {
$actor = trim($actor);
if (isset($userProfile['actors'][$actor])) {
$actorScore += $userProfile['actors'][$actor];
}
}
}
$score += $actorScore * $weights['actor_weight'];
// 评分相似度(与用户平均评分比较)
if ($item['rating'] > 0 && $userProfile['avg_rating'] > 0) {
$ratingDiff = abs($item['rating'] - $userProfile['avg_rating']);
$ratingScore = max(0, 1 - ($ratingDiff / 5));
$score += $ratingScore * $weights['rating_weight'];
}
// 年份相似度
if ($item['release_year'] > 0 && $userProfile['preferred_year'] > 0) {
$yearDiff = abs($item['release_year'] - $userProfile['preferred_year']);
$yearScore = max(0, 1 - ($yearDiff / 20));
$score += $yearScore * $weights['year_weight'];
}
return $score;
}
// 提取物品特征向量
private function extractFeatures($item) {
return [
'genres' => $this->createFeatureVector($item['genres'] ?? ''),
'directors' => $item['director'] ? [$item['director'] => 1] : [],
'actors' => $this->createFeatureVector($item['actors'] ?? '')
];
}
// 创建特征向量
private function createFeatureVector($csv) {
$vector = [];
if (!empty($csv)) {
$items = explode(',', $csv);
foreach ($items as $item) {
$item = trim($item);
if (!empty($item)) {
$vector[$item] = 1;
}
}
}
return $vector;
}
// 获取推荐结果
public function getRecommendations($topN = 10) {
$userProfile = $this->buildUserProfile();
if (!$userProfile) {
return $this->getPopularItems($topN);
}
// 获取用户已拥有或已评分的物品
$excludeIds = $this->getUserBehaviorItems();
$recommendations = [];
foreach ($this->items as $item) {
if (in_array($item['id'], $excludeIds)) {
continue;
}
$score = $this->calculateItemUserSimilarity($item, $userProfile);
$recommendations[] = [
'item' => $item,
'score' => $score
];
}
// 按分数排序
usort($recommendations, function($a, $b) {
return $b['score'] <=> $a['score'];
});
// 返回前N个
return array_slice($recommendations, 0, $topN);
}
// 获取物品相似推荐(类似"喜欢这个的人也喜欢")
public function getSimilarItems($itemId, $topN = 5) {
$targetItem = null;
foreach ($this->items as $item) {
if ($item['id'] == $itemId) {
$targetItem = $item;
break;
}
}
if (!$targetItem) {
return [];
}
$similarItems = [];
foreach ($this->items as $item) {
if ($item['id'] == $itemId) {
continue;
}
$similarity = $this->calculateCosineSimilarity($targetItem, $item);
$similarItems[] = [
'item' => $item,
'similarity' => $similarity
];
}
usort($similarItems, function($a, $b) {
return $b['similarity'] <=> $a['similarity'];
});
return array_slice($similarItems, 0, $topN);
}
// 获取用户已交互的物品
private function getUserBehaviorItems() {
if (!$this->userId) {
return [];
}
$stmt = $this->pdo->prepare("SELECT item_id FROM user_behavior WHERE user_id = ?");
$stmt->execute([$this->userId]);
return $stmt->fetchAll(PDO::FETCH_COLUMN);
}
// 获取热门物品(无用户画像时)
public function getPopularItems($topN = 10) {
$stmt = $this->pdo->query("
SELECT i.*, COUNT(ub.id) as view_count
FROM items i
LEFT JOIN user_behavior ub ON i.id = ub.item_id
GROUP BY i.id
ORDER BY i.rating DESC, view_count DESC
LIMIT $topN
");
$popularItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
return array_map(function($item) {
return ['item' => $item, 'score' => $item['rating']];
}, $popularItems);
}
// 获取物品的完整特征描述
public function getItemFeatures($itemId) {
foreach ($this->items as $item) {
if ($item['id'] == $itemId) {
return $this->extractFeatures($item);
}
}
return null;
}
// 获取解释推荐理由
public function getRecommendationExplanation($item, $userProfile) {
$reasons = [];
// 类型匹配
$itemGenres = explode(',', $item['genres']);
$matchedGenres = [];
foreach ($itemGenres as $genre) {
$genre = trim($genre);
if (isset($userProfile['genres'][$genre])) {
$matchedGenres[] = $genre;
}
}
if ($matchedGenres) {
$reasons[] = "你喜欢" . implode(',', $matchedGenres) . "类型";
}
// 导演匹配
if (isset($userProfile['directors'][$item['director']])) {
$reasons[] = "导演" . $item['director'] . "的作品";
}
// 演员匹配
$matchedActors = [];
$itemActors = explode(',', $item['actors']);
foreach ($itemActors as $actor) {
$actor = trim($actor);
if (isset($userProfile['actors'][$actor])) {
$matchedActors[] = $actor;
}
}
if ($matchedActors) {
$reasons[] = "有你喜欢的演员" . implode(',', $matchedActors);
}
// 评分推荐
if ($item['rating'] >= 7) {
$reasons[] = "评分高达" . $item['rating'] . "分";
}
return $reasons;
}
}
推荐引擎核心类
<?php
class RecommendationEngine {
private $pdo;
private $contentBased;
public function __construct($pdo) {
$this->pdo = $pdo;
$this->contentBased = new ContentBasedRecommender($pdo);
}
// 为指定用户生成推荐
public function recommendForUser($userId, $topN = 10) {
$recommender = new ContentBasedRecommender($this->pdo, $userId);
$recommendations = $recommender->getRecommendations($topN);
$results = [];
foreach ($recommendations as $rec) {
$item = $rec['item'];
$userProfile = $recommender->buildUserProfile();
$reasons = $recommender->getRecommendationExplanation($item, $userProfile);
$results[] = [
'item' => $item,
'score' => round($rec['score'], 3),
'reasons' => $reasons,
'reason_text' => implode(',', $reasons)
];
}
return $results;
}
// 批量为所有用户生成推荐
public function recommendForAllUsers($topN = 10) {
$stmt = $this->pdo->query("SELECT id FROM users");
$users = $stmt->fetchAll(PDO::FETCH_COLUMN);
$allRecommendations = [];
foreach ($users as $userId) {
$allRecommendations[$userId] = $this->recommendForUser($userId, $topN);
}
return $allRecommendations;
}
// 生成推荐报告
public function generateReport($userId = null) {
$recommender = new ContentBasedRecommender($this->pdo, $userId);
$report = [
'user_id' => $userId,
'items_count' => count($this->getAllItems()),
'users_count' => count($this->getAllUsers()),
'recommendations' => []
];
if ($userId) {
$report['recommendations'] = $this->recommendForUser($userId);
$report['user_profile'] = $recommender->buildUserProfile();
} else {
$report['recommendations'] = $this->recommendForAllUsers();
}
return $report;
}
private function getAllItems() {
$stmt = $this->pdo->query("SELECT COUNT(*) FROM items");
return $stmt->fetchColumn();
}
private function getAllUsers() {
$stmt = $this->pdo->query("SELECT COUNT(*) FROM users");
return $stmt->fetchColumn();
}
}
使用示例
<?php
// 数据库连接
$pdo = new PDO('mysql:host=localhost;dbname=recommendation', 'username', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 初始化推荐引擎
$engine = new RecommendationEngine($pdo);
// 为用户1生成推荐
$userId = 1;
$recommendations = $engine->recommendForUser($userId, 10);
echo "<h2>基于内容的推荐结果</h2>";
echo "<table border='1'>";
echo "<tr><th>标题</th><th>类型</th><th>评分</th><th>推荐理由</th><th>匹配度</th></tr>";
foreach ($recommendations as $rec) {
echo "<tr>";
echo "<td>" . $rec['item']['title'] . "</td>";
echo "<td>" . $rec['item']['genres'] . "</td>";
echo "<td>" . $rec['item']['rating'] . "</td>";
echo "<td>" . $rec['reason_text'] . "</td>";
echo "<td>" . $rec['score'] . "</td>";
echo "</tr>";
}
echo "</table>";
// 获取相似物品推荐
echo "<h2>相似物品推荐</h2>";
$itemId = 1; // Inception
$similarItems = $engine->contentBased->getSimilarItems($itemId);
foreach ($similarItems as $rec) {
echo $rec['item']['title'] . " - 相似度: " . round($rec['similarity'], 2) . "<br>";
}
优化建议
1 缓存优化
// 添加Redis缓存
class CachedContentBasedRecommender extends ContentBasedRecommender {
private $cache;
public function __construct($pdo, $userId = null) {
parent::__construct($pdo, $userId);
$this->cache = new Redis();
$this->cache->connect('127.0.0.1', 6379);
}
public function getRecommendations($topN = 10) {
$cacheKey = "recommendations:user:{$this->userId}:top:{$topN}";
if ($cached = $this->cache->get($cacheKey)) {
return unserialize($cached);
}
$recommendations = parent::getRecommendations($topN);
$this->cache->setex($cacheKey, 3600, serialize($recommendations));
return $recommendations;
}
}
2 特征权重调优
// 可以根据用户行为动态调整权重
public function adjustWeights($userBehavior) {
// 分析用户行为模式
$weights = [
'genre_weight' => 0.4,
'director_weight' => 0.2,
'actor_weight' => 0.2,
'rating_weight' => 0.1,
'year_weight' => 0.1
];
// 根据行为类型调整
if ($userBehavior['purchase_count'] > 0) {
$weights['genre_weight'] += 0.1;
}
return $weights;
}
的推荐系统会根据用户的评分行为构建用户画像,然后计算物品间的相似度来推荐最相关的内容,并提供推荐理由解释,您可以根据实际需求调整特征权重和相似度计算方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播