如何用PHP项目实现基于内容的推荐?

wen java案例 1

本文目录导读:

如何用PHP项目实现基于内容的推荐?

  1. 完整PHP实现示例
  2. 优化建议

我会为您介绍如何在PHP项目中实现基于内容的推荐系统,并提供一个完整的实现示例。 推荐的核心原理 的推荐通过分析项目特征用户历史偏好的相似度来推荐内容,核心步骤:

  1. 提取项目特征(如标签、关键词、分类等)
  2. 构建用户偏好模型(基于用户历史行为)
  3. 计算相似度
  4. 生成推荐列表

完整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;
    }
}

优化建议

  1. 缓存机制:使用Redis缓存用户偏好向量和推荐结果
  2. 增量更新:用户新行为发生后只更新受影响的部分
  3. 混合推荐:结合协同过滤提高多样性
  4. 特征工程:使用更复杂的特征表示(如Word2Vec)
  5. 性能优化:对于大量项目使用向量数据库(如Milvus)

这个实现提供了完整的基于内容推荐系统基础,您可以根据实际需求调整相似度计算方式和特征权重。

抱歉,评论功能暂时关闭!