机器学习高阶教程<6>推荐系统高阶修炼手册:混排、多任务与在线学习,解锁精准推荐新境界

哈喽,各位机器学习爱好者!

先问你一个扎心的问题:你有没有过这样的经历?打开购物APP,首页全是同类型的商品,看3秒就觉得腻;刷短视频,前两条还精准戳中兴趣,后面突然刷到完全不相关的内容,瞬间没了继续刷的欲望;甚至看资讯APP,推荐的文章要么全是"标题党",要么深度不够,越看越觉得"算法不懂我"。

其实,这些问题的核心,就是入门和进阶推荐系统没解决的"高阶难题":如何在精准匹配用户兴趣的同时,兼顾多样性?如何让推荐系统同时满足多个业务目标(比如既想让用户点击,又想让用户下单)?如何让系统实时跟上用户兴趣的变化?

今天这篇,咱们就用"接地气的比喻+具体的实现逻辑+真实的业务价值",把这三个高阶技术扒得明明白白。全程无晦涩公式堆砌,只有让你拍大腿的"原来如此",看到最后还有下一篇高阶内容的悬念预告,准备好开启修炼了吗?

一、混排:不止于"排",更是"精准搭配"的艺术

先从最贴近我们感知的"混排"说起。入门阶段的推荐系统,大多是"单品类排序"------比如给你推荐的全是衣服,或者全是零食。但真实的产品场景里,用户的需求是"多元且动态"的:比如你在购物APP上搜"运动跑鞋",可能同时也想看看运动袜、运动背包;刷视频时,既想看搞笑段子放松,也想偶尔看个知识科普充电。

混排,就是解决这个问题的"神器"。它的核心不是简单把不同品类的内容混在一起,而是像餐厅的"配菜师"一样,根据你的"口味(用户兴趣)""用餐场景(当前行为)""餐厅目标(业务指标)",把不同类型的"菜品(推荐内容)"搭配成一套让你满意、同时让餐厅盈利的"套餐"。

1. 为什么需要混排?------ 破解"精准陷阱"

很多人觉得,推荐系统越精准越好,但过度精准会陷入"精准陷阱":

  • 用户兴趣固化:一直给用户推同类型内容,用户会觉得枯燥,慢慢流失(比如你喜欢看科幻电影,天天推科幻片,一周后可能就想换个口味);

  • 业务目标单一:只追求点击率,可能导致用户"光点不买",或者只看免费内容,不产生付费转化;

  • 内容覆盖不足:新上线的内容、小众品类的内容,很难被用户发现,形成"马太效应"(热门内容越热门,冷门内容越冷门)。

而混排,就是通过"品类多样化""内容层级化"的搭配,既保留精准推荐的核心优势,又解决这些痛点。比如购物APP的首页,会同时给你推"你最近浏览的跑鞋""搭配跑鞋的袜子""热门运动装备榜单",既让你觉得"算法懂我",又让你有新的发现欲。

2. 混排的核心逻辑:如何平衡"多样性"与"精准度"?

混排的本质,是"多目标下的资源分配问题"------把有限的推荐位,分给不同类型、不同价值的内容,实现"用户体验"和"业务目标"的双赢。这里有两个核心步骤,用"配菜"的比喻就能看懂:

  1. 第一步:确定"菜品池"------ 筛选候选内容。先通过基础推荐模型(比如深度学习模型),筛选出用户可能感兴趣的不同品类内容,形成一个"候选池"(比如候选池里有跑鞋、袜子、背包、运动手环四类内容);

  2. 第二步:制定"搭配规则"------ 动态分配推荐位。这是混排的核心,常见的规则有三种:

    1. 规则式混排:比如固定比例"3:2:1",3个核心品类(跑鞋)、2个搭配品类(袜子、背包)、1个探索品类(运动手环),适合业务初期,简单易落地;

    2. 模型式混排:用专门的混排模型(比如多目标排序模型),预测每类内容在"点击率、转化率、用户停留时长"等多个指标上的得分,然后动态分配推荐位,适合业务成熟后,追求更精准的平衡;

    3. 强化学习混排:让系统通过"试错"学习最优搭配策略(比如这次推2个探索品类用户停留变久,下次就适当增加比例),适合用户兴趣变化快的场景(比如短视频、资讯)。

这里要注意一个关键:混排不是"平均分配",而是"动态调整"。比如用户刚搜完"跑鞋",就多给核心品类推荐位;用户连续刷了5条同类型内容,就立刻增加探索品类的比例------这就是混排的"智慧"。

3. 混排的真实价值:从"用户停留"到"业务增长"

很多产品做混排后,都看到了明显的效果:比如某购物APP做混排后,用户首页停留时长提升了25%,跨品类购买率提升了30%;某短视频APP通过混排,用户日均刷视频次数提升了18%,留存率提升了12%。

核心原因就是:混排满足了用户的"潜在需求"和"探索欲",让用户觉得"这个APP总能给我惊喜",从而愿意花更多时间,产生更多消费------这就是混排从"用户体验"到"业务增长"的转化逻辑。

二、多任务学习:让推荐系统"举一反三"的修炼秘诀

聊完混排,咱们再看第二个高阶技术------多任务学习。先问你一个问题:如果让推荐系统同时完成两个目标,比如"预测用户是否点击"和"预测用户是否下单",你会怎么做?

入门阶段的做法是:分别训练两个独立的模型,一个预测点击率(CTR),一个预测转化率(CVR),然后再把两个模型的结果结合起来。但这种做法有个很大的问题:两个模型是"各自为战"的,没有利用到"点击"和"下单"之间的关联信息(比如喜欢点击运动装备的用户,下单运动背包的概率也更高),而且训练成本高、推理速度慢。

而多任务学习,就是让推荐系统像"学霸"一样,能够"举一反三"------通过一次训练,同时掌握多个相关任务的能力,既提升模型效果,又降低成本。

1. 多任务学习的核心:共享"知识",差异化"任务"

多任务学习的本质,是"共享特征提取层,差异化任务输出层"。用"老师教学生"的比喻来理解:

比如老师要教学生"语文阅读"和"语文写作"两个任务。这两个任务都需要"理解文字含义""把握文章逻辑"这些基础能力------这就是"共享知识"。老师不会分别教两次基础能力,而是先教一次基础能力,再针对"阅读"和"写作"的不同要求,教差异化的技巧(比如阅读要学会找重点,写作要学会组织结构)。

推荐系统的多任务学习也是一样:

  • 共享层:用深度学习模型(比如DNN)提取用户和内容的通用特征(比如用户的年龄、兴趣标签,内容的品类、关键词),这些特征是多个任务都需要的;

  • 任务层:针对不同的业务目标(CTR预测、CVR预测、停留时长预测),分别设计独立的输出层,用不同的损失函数训练;

  • 优势:共享层的特征可以相互促进(比如CVR任务的特征能提升CTR任务的预测精度),而且只需要训练一个模型,降低了训练和推理成本。

2. 常见的多任务学习架构:从"简单共享"到"动态调整"

多任务学习的架构有很多,从易到难主要有三种,适合不同的业务场景:

  1. Hard Parameter Sharing(硬共享):最基础的架构,所有任务共享同一个特征提取层,只保留任务专属的输出层。优点是简单易实现,适合任务相关性很高的场景(比如CTR和CVR预测);缺点是如果任务相关性低(比如预测点击和预测用户性别),会出现"任务干扰"(一个任务的训练会影响另一个任务的效果);

  2. Soft Parameter Sharing(软共享):每个任务有自己的特征提取层,但让不同任务的特征提取层参数"相似"(比如通过正则化约束)。优点是适合任务相关性较低的场景,避免了"任务干扰";缺点是训练复杂度比硬共享高;

  3. MMoE(Mixture of Experts):最灵活的架构,把特征提取层分成多个"专家网络",每个专家网络负责处理不同类型的特征,然后用一个"门控网络"根据用户和内容的特点,动态选择哪些专家网络参与不同任务的训练。优点是能自动适配不同任务的相关性,避免"任务干扰",效果最好;缺点是实现复杂,适合业务场景复杂、任务较多的大型推荐系统(比如淘宝、抖音)。

3. 多任务学习的落地价值:一次训练,多效提升

多任务学习在实际业务中应用非常广泛,比如某电商平台用多任务学习同时优化CTR和CVR,最终CTR提升了15%,CVR提升了20%;某资讯APP用多任务学习同时优化"点击""停留时长""分享"三个任务,用户日均使用时长提升了22%。

核心原因就是:多任务学习充分利用了任务之间的关联信息,让模型更全面地理解用户需求,同时降低了系统的训练和运维成本------这就是"举一反三"的力量。

三、在线学习系统:让推荐系统"实时进化"的核心引擎

最后,咱们聊第三个高阶技术------在线学习系统。不知道你有没有发现,很多推荐系统存在"滞后性":比如你今天突然对"露营装备"感兴趣,刷了很多相关内容,但推荐系统要等到第二天甚至第三天,才会给你推大量露营装备。这种"滞后性"很容易让用户失去兴趣,因为用户的兴趣可能是"即时且短暂"的(比如周末想露营,周内兴趣就消失了)。

而在线学习系统,就是让推荐系统具备"实时进化"的能力------能够实时捕捉用户的最新行为,实时更新模型参数,让推荐结果紧跟用户兴趣的变化。

1. 在线学习vs离线学习:从"定期更新"到"实时迭代"

要理解在线学习,先对比一下我们之前聊的"离线学习":

对比维度 离线学习 在线学习
数据使用 使用历史数据(比如过去一周的数据)批量训练 使用用户实时行为数据(比如用户刚完成的点击、浏览)增量训练
模型更新频率 定期更新(比如每天一次、每周一次) 实时更新(比如每秒钟更新一次、每处理一个用户行为就更新一次)
响应速度 慢,存在滞后性 快,能实时跟上用户兴趣变化
适用场景 用户兴趣稳定的场景(比如图书推荐) 用户兴趣变化快的场景(比如短视频、资讯、直播)

简单来说,离线学习是"批量生产"模型,在线学习是"个性化实时定制"模型------后者更适合当下快节奏、个性化的产品场景。

2. 在线学习的核心挑战:如何在"实时"和"稳定"之间平衡?

在线学习听起来很完美,但落地时面临一个核心挑战:实时性和稳定性的平衡。如果模型每收到一个用户行为就更新一次,很容易被"噪声数据"(比如用户误点击)影响,导致模型参数波动过大,推荐结果不稳定(比如刚给用户推了露营装备,下一秒因为误点击了一次美食视频,就立刻全推美食)。

为了解决这个问题,在线学习系统有三个核心设计:

  1. 增量训练算法:不用重新训练整个模型,只需要用新的实时数据对模型参数进行"微调"。常见的算法有SGD(随机梯度下降)、FTRL(Follow The Regularized Leader)等,这些算法能快速更新参数,同时通过正则化约束参数波动;

  2. 数据过滤和采样:对实时用户行为数据进行预处理,过滤掉明显的噪声数据(比如点击时长小于0.5秒的误点击),同时对高频行为(比如同一用户反复点击同一内容)进行采样,避免数据分布失衡;

  3. 模型监控和回滚:实时监控模型的关键指标(比如点击率、用户停留时长),如果指标突然下降,说明模型可能被噪声数据影响,立刻触发模型回滚,恢复到之前的稳定版本。

实际业务中,在线学习系统的经典架构是"实时数据处理框架 + 在线模型服务":

  • 实时数据处理:用Flink、Spark Streaming等框架,实时采集用户的行为数据(点击、浏览、下单、收藏等),进行数据清洗、特征提取;

  • 增量训练:将处理后的实时特征和标签,输入到在线模型中(比如FTRL模型、在线DNN模型),进行增量训练,更新模型参数;

  • 模型服务:将更新后的模型参数实时部署到推荐服务中,用户每次请求推荐时,都用最新的模型进行预测,返回最贴合用户当前兴趣的推荐结果。

比如某短视频APP的在线学习系统,能在用户完成一次点击后,1秒内更新模型参数,下一次给用户推荐的内容就会立刻贴合最新兴趣------这就是为什么你刷短视频时,会觉得"越刷越懂我"。

四、项目实战:推荐系统高阶功能演示

python 复制代码
"""
推荐系统高阶技术可视化平台 - 单文件版本
包含所有HTML、CSS和JS代码
"""

from flask import Flask, render_template_string, jsonify, request
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import random
from collections import defaultdict
import json
from typing import Dict, List, Tuple, Any

app = Flask(__name__)

# ==================== 推荐系统核心类 ====================

class UserProfile:
    """用户画像类"""
    def __init__(self, user_id: int, name: str = None):
        self.user_id = user_id
        self.name = name or f"用户{user_id}"
        self.base_interests = self._generate_base_interests()
        self.real_time_interests = self.base_interests.copy()
        self.history_behavior = []
        self.recommendation_history = []
        self.created_at = datetime.now()

    def _generate_base_interests(self) -> Dict[str, float]:
        """生成用户基础兴趣"""
        categories = ['科技', '娱乐', '体育', '财经', '时尚', '教育', '健康', '美食']
        interests = {}
        # 每个用户随机选择3-5个兴趣类别
        selected_categories = random.sample(categories, random.randint(3, 5))
        for category in selected_categories:
            interests[category] = random.uniform(0.4, 0.95)
        return interests

    def update_realtime_interests(self, action: Dict[str, Any]):
        """更新实时兴趣"""
        category = action['category']
        action_type = action['type']

        if action_type == 'click':
            weight = 0.1
        elif action_type == 'purchase':
            weight = 0.3
        elif action_type == 'watch_long':
            weight = 0.2
        else:
            weight = 0.05

        if category in self.real_time_interests:
            self.real_time_interests[category] = min(1.0, self.real_time_interests[category] + weight)
        else:
            self.real_time_interests[category] = weight

        # 兴趣衰减(非交互类别)
        for cat in list(self.real_time_interests.keys()):
            if cat != category:
                self.real_time_interests[cat] = max(0.1, self.real_time_interests[cat] - 0.02)

        self.history_behavior.append({
            'timestamp': datetime.now().strftime("%H:%M:%S"),
            'action': action,
            'category': category
        })

        # 只保留最近50条行为
        if len(self.history_behavior) > 50:
            self.history_behavior = self.history_behavior[-50:]

    def get_interest_score(self, category: str) -> float:
        """获取用户对某类别的兴趣分数"""
        return self.real_time_interests.get(category, 0.0)

    def to_dict(self):
        """转换为字典格式"""
        return {
            'user_id': self.user_id,
            'name': self.name,
            'base_interests': self.base_interests,
            'real_time_interests': self.real_time_interests,
            'history_count': len(self.history_behavior),
            'recommendation_count': len(self.recommendation_history)
        }

class ContentPool:
    """内容池"""
    def __init__(self):
        self.contents = self._generate_contents()
        self.categories = ['科技', '娱乐', '体育', '财经', '时尚', '教育', '健康', '美食']

    def _generate_contents(self) -> List[Dict]:
        """生成模拟内容"""
        categories = ['科技', '娱乐', '体育', '财经', '时尚', '教育', '健康', '美食']
        contents = []

        content_id = 0
        for category in categories:
            # 每个类别生成12-15个内容
            for i in range(random.randint(12, 15)):
                contents.append({
                    'id': content_id,
                    'title': f"{category}内容_{i}",
                    'category': category,
                    'ctr': random.uniform(0.01, 0.2),  # 预估点击率
                    'cvr': random.uniform(0.001, 0.05),  # 预估转化率
                    'quality_score': random.uniform(0.5, 1.0),
                    'freshness': random.randint(1, 30),  # 内容新鲜度(天数)
                    'description': self._generate_description(category, i),
                    'image_color': self._generate_color()
                })
                content_id += 1
        return contents

    def _generate_description(self, category: str, index: int) -> str:
        """生成内容描述"""
        descriptions = {
            '科技': ['最新科技动态', '前沿技术分析', '数码产品评测', 'AI发展趋势'],
            '娱乐': ['明星八卦新闻', '电影电视剧推荐', '综艺节目看点', '娱乐热点追踪'],
            '体育': ['体育赛事报道', '运动员专访', '比赛分析预测', '健身锻炼技巧'],
            '财经': ['股市行情分析', '投资理财建议', '经济政策解读', '商业趋势洞察'],
            '时尚': ['潮流穿搭指南', '美妆护肤技巧', '时尚品牌推荐', '设计风格解析'],
            '教育': ['学习方法分享', '考试备考策略', '教育资源推荐', '教育政策解读'],
            '健康': ['健康养生知识', '疾病预防指南', '运动健身建议', '心理健康贴士'],
            '美食': ['美食制作教程', '餐厅探店推荐', '营养搭配建议', '饮食文化分享']
        }
        return f"{random.choice(descriptions[category])} - {category}领域优质内容"

    def _generate_color(self) -> str:
        """生成随机颜色用于卡片展示"""
        colors = [
            '#4CAF50', '#2196F3', '#FF9800', '#9C27B0',
            '#00BCD4', '#FF5722', '#673AB7', '#3F51B5'
        ]
        return random.choice(colors)

    def get_contents_by_category(self, category: str, limit: int = 20) -> List[Dict]:
        """按类别获取内容"""
        filtered = [c for c in self.contents if c['category'] == category]
        return sorted(filtered, key=lambda x: x['ctr'], reverse=True)[:limit]

    def get_content_by_id(self, content_id: int) -> Dict:
        """根据ID获取内容"""
        for content in self.contents:
            if content['id'] == content_id:
                return content
        return None

class MixedRanking:
    """混排算法"""
    def __init__(self):
        pass

    def rule_based_mix(self, user_profile: UserProfile, contents_by_category: Dict[str, List[Dict]]) -> List[Dict]:
        """基于规则的混排"""
        # 规则: 70%用户兴趣内容 + 20%相关品类 + 10%探索内容
        result = []

        # 1. 主要兴趣内容 (70%)
        main_interests = sorted(user_profile.real_time_interests.items(),
                              key=lambda x: x[1], reverse=True)[:2]
        for category, score in main_interests:
            if category in contents_by_category:
                result.extend(contents_by_category[category][:3])

        # 2. 相关品类 (20%)
        all_categories = list(contents_by_category.keys())
        related_categories = [c for c in all_categories if c not in dict(main_interests)]
        if related_categories:
            category = random.choice(related_categories)
            result.extend(contents_by_category[category][:2])

        # 3. 探索内容 (10%)
        if len(result) < 8:  # 保证至少8条内容
            explore_category = random.choice(all_categories)
            explore_contents = [c for c in contents_by_category.get(explore_category, [])
                              if c not in result]
            result.extend(explore_contents[:1])

        # 打乱顺序增加多样性
        random.shuffle(result)
        return result[:10]  # 返回前10条

    def model_based_mix(self, user_profile: UserProfile, contents_by_category: Dict[str, List[Dict]]) -> List[Dict]:
        """基于模型的混排"""
        all_contents = []
        for category, contents in contents_by_category.items():
            for content in contents:
                # 计算综合得分 (多目标)
                interest_score = user_profile.get_interest_score(category)
                diversity_penalty = self._calculate_diversity_penalty(content, all_contents)

                # 多目标加权得分
                score = (
                    content['ctr'] * 0.4 +           # 点击率权重
                    content['cvr'] * 0.3 +           # 转化率权重
                    interest_score * 0.2 +           # 用户兴趣权重
                    (1 / content['freshness']) * 0.05 +  # 新鲜度权重
                    diversity_penalty * 0.05          # 多样性权重
                )

                all_contents.append({
                    'content': content,
                    'score': score,
                    'category': category
                })

        # 按得分排序并确保类别多样性
        sorted_contents = sorted(all_contents, key=lambda x: x['score'], reverse=True)

        # 类别多样性控制
        final_result = []
        category_count = defaultdict(int)

        for item in sorted_contents:
            if len(final_result) >= 10:
                break

            category = item['category']
            if category_count[category] < 3:  # 每个类别最多3条
                final_result.append(item['content'])
                category_count[category] += 1

        return final_result

    def _calculate_diversity_penalty(self, content: Dict, selected_contents: List) -> float:
        """计算多样性惩罚"""
        if not selected_contents:
            return 1.0

        same_category_count = sum(1 for item in selected_contents
                                if item['content']['category'] == content['category'])

        # 相同类别越多,惩罚越大
        return max(0, 1.0 - (same_category_count * 0.2))

class MultiTaskModel:
    """多任务学习模型"""
    def __init__(self):
        self.shared_layer_weights = np.random.randn(10, 8)  # 共享层权重
        self.ctr_task_weights = np.random.randn(8, 1)      # CTR任务权重
        self.cvr_task_weights = np.random.randn(8, 1)      # CVR任务权重
        self.update_count = 0

    def predict(self, user_features: np.ndarray, content_features: np.ndarray) -> Tuple[float, float]:
        """预测CTR和CVR"""
        # 特征拼接
        combined_features = np.concatenate([user_features, content_features])

        # 共享层
        shared_output = np.tanh(np.dot(combined_features, self.shared_layer_weights))

        # 任务特定层
        ctr_prediction = sigmoid(np.dot(shared_output, self.ctr_task_weights))
        cvr_prediction = sigmoid(np.dot(shared_output, self.cvr_task_weights))

        return float(ctr_prediction), float(cvr_prediction)

    def update_with_feedback(self, user_features: np.ndarray,
                            content_features: np.ndarray,
                            ctr_label: float,
                            cvr_label: float,
                            learning_rate: float = 0.01):
        """根据反馈更新模型(简化版)"""
        self.update_count += 1

        # 在实际中,这里应该实现梯度下降更新
        # 这里简化为随机调整
        if random.random() > 0.7:  # 30%的概率调整
            adjustment = np.random.randn(*self.shared_layer_weights.shape) * 0.1
            self.shared_layer_weights += adjustment

    def get_model_info(self):
        """获取模型信息"""
        return {
            'update_count': self.update_count,
            'shared_layer_shape': self.shared_layer_weights.shape,
            'ctr_layer_shape': self.ctr_task_weights.shape,
            'cvr_layer_shape': self.cvr_task_weights.shape
        }

class OnlineLearningSystem:
    """在线学习系统"""
    def __init__(self):
        self.user_models = {}  # 用户ID -> 模型
        self.global_model = MultiTaskModel()
        self.recent_feedback = []
        self.update_interval = 5  # 5秒更新一次
        self.last_update = datetime.now()
        self.total_updates = 0
        self.is_running = True

    def process_feedback(self, user_id: int, action: Dict[str, Any]):
        """处理用户反馈"""
        feedback = {
            'user_id': user_id,
            'action': action,
            'timestamp': datetime.now().strftime("%H:%M:%S")
        }
        self.recent_feedback.append(feedback)

        # 检查是否需要更新模型
        self._check_model_update()

    def _check_model_update(self):
        """检查并执行模型更新"""
        current_time = datetime.now()
        if (current_time - self.last_update).seconds >= self.update_interval:
            if self.recent_feedback:
                self._update_models()
                self.recent_feedback = []
                self.last_update = current_time

    def _update_models(self):
        """更新模型(简化实现)"""
        self.total_updates += 1

        for feedback in self.recent_feedback:
            # 生成模拟特征
            user_features = np.random.randn(5)
            content_features = np.random.randn(5)

            # 根据action类型生成标签
            action_type = feedback['action']['type']
            ctr_label = 1.0 if action_type in ['click', 'watch_long'] else 0.0
            cvr_label = 1.0 if action_type == 'purchase' else 0.0

            # 更新全局模型
            self.global_model.update_with_feedback(
                user_features, content_features, ctr_label, cvr_label
            )

    def get_system_info(self):
        """获取系统信息"""
        return {
            'total_updates': self.total_updates,
            'recent_feedback_count': len(self.recent_feedback),
            'last_update': self.last_update.strftime("%H:%M:%S"),
            'update_interval': self.update_interval,
            'user_model_count': len(self.user_models)
        }

class RecommendationSystem:
    """推荐系统主类"""
    def __init__(self):
        self.users = {}
        self.content_pool = ContentPool()
        self.mixed_ranking = MixedRanking()
        self.online_learning = OnlineLearningSystem()
        self.multi_task_model = MultiTaskModel()
        self.system_metrics = {
            'total_recommendations': 0,
            'total_interactions': 0,
            'average_diversity': 0.0,
            'performance_history': []
        }

    def get_or_create_user(self, user_id: int, name: str = None) -> UserProfile:
        """获取或创建用户"""
        if user_id not in self.users:
            self.users[user_id] = UserProfile(user_id, name)
            self.online_learning.user_models[user_id] = self.users[user_id]
        return self.users[user_id]

    def generate_recommendations(self, user_id: int, strategy: str = 'model_based') -> List[Dict]:
        """生成推荐列表"""
        user = self.get_or_create_user(user_id)

        # 1. 获取候选内容
        categories = list(user.real_time_interests.keys())
        if len(categories) < 3:
            categories.extend(['科技', '娱乐', '体育'])  # 默认类别

        contents_by_category = {}
        for category in categories:
            contents = self.content_pool.get_contents_by_category(category, 10)
            if contents:
                contents_by_category[category] = contents

        # 2. 应用混排策略
        if strategy == 'rule_based':
            recommendations = self.mixed_ranking.rule_based_mix(user, contents_by_category)
        else:
            recommendations = self.mixed_ranking.model_based_mix(user, contents_by_category)

        # 3. 使用多任务模型重新排序(可选)
        reranked_recommendations = self._rerank_with_multi_task(user, recommendations)

        # 记录推荐历史
        for content in reranked_recommendations:
            user.recommendation_history.append({
                'timestamp': datetime.now().strftime("%H:%M:%S"),
                'content_id': content['id'],
                'title': content['title'],
                'category': content['category']
            })

        # 更新系统指标
        self.system_metrics['total_recommendations'] += 1

        return reranked_recommendations

    def _rerank_with_multi_task(self, user: UserProfile, recommendations: List[Dict]) -> List[Dict]:
        """使用多任务模型重新排序"""
        reranked = []
        for content in recommendations:
            # 生成特征(简化)
            user_features = np.array([user.get_interest_score(content['category'])] * 5)
            content_features = np.array([
                content['ctr'],
                content['cvr'],
                content['quality_score'],
                1 / content['freshness'],
                random.random()
            ])

            # 预测
            ctr_pred, cvr_pred = self.multi_task_model.predict(user_features, content_features)

            # 综合得分
            final_score = ctr_pred * 0.7 + cvr_pred * 0.3
            reranked.append((content, final_score, ctr_pred, cvr_pred))

        # 按综合得分排序
        reranked.sort(key=lambda x: x[1], reverse=True)

        # 添加预测分数到内容
        result = []
        for content, final_score, ctr_pred, cvr_pred in reranked:
            content_with_score = content.copy()
            content_with_score['final_score'] = round(final_score, 3)
            content_with_score['ctr_pred'] = round(ctr_pred, 3)
            content_with_score['cvr_pred'] = round(cvr_pred, 3)
            result.append(content_with_score)

        return result[:10]

    def process_user_action(self, user_id: int, content_id: int, action_type: str):
        """处理用户行为"""
        user = self.get_or_create_user(user_id)
        content = self.content_pool.get_content_by_id(content_id)

        if not content:
            return False

        action = {
            'content_id': content_id,
            'title': content['title'],
            'category': content['category'],
            'type': action_type
        }

        # 更新用户兴趣
        user.update_realtime_interests(action)

        # 在线学习处理反馈
        self.online_learning.process_feedback(user_id, action)

        # 更新系统指标
        self.system_metrics['total_interactions'] += 1

        return True

    def get_system_stats(self):
        """获取系统统计信息"""
        # 计算平均多样性
        diversity_scores = []
        for user_id, user in self.users.items():
            if user.recommendation_history:
                categories = [rec['category'] for rec in user.recommendation_history[-20:]]
                if categories:
                    category_counts = {}
                    for category in categories:
                        category_counts[category] = category_counts.get(category, 0) + 1

                    total = len(categories)
                    hhi = sum((count/total)**2 for count in category_counts.values())
                    diversity_scores.append(1 - hhi)

        avg_diversity = sum(diversity_scores) / len(diversity_scores) if diversity_scores else 0

        return {
            'total_users': len(self.users),
            'total_recommendations': self.system_metrics['total_recommendations'],
            'total_interactions': self.system_metrics['total_interactions'],
            'average_diversity': round(avg_diversity, 3),
            'online_learning_updates': self.online_learning.total_updates
        }

    def get_user_info(self, user_id: int):
        """获取用户详细信息"""
        if user_id not in self.users:
            return None

        user = self.users[user_id]
        return {
            'user_info': user.to_dict(),
            'recent_behavior': user.history_behavior[-10:],
            'recent_recommendations': user.recommendation_history[-10:]
        }

def sigmoid(x):
    """Sigmoid函数"""
    return 1 / (1 + np.exp(-x))

# ==================== Flask应用初始化 ====================

recommendation_system = RecommendationSystem()

# 创建一些初始用户
for i in range(1, 4):
    names = ['张三', '李四', '王五', '赵六', '钱七']
    recommendation_system.get_or_create_user(i, names[i-1])

# ==================== HTML模板 ====================

HTML_TEMPLATE = '''
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>推荐系统高阶技术可视化平台</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        /* 基础样式 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: #333;
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1800px;
            margin: 0 auto;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            overflow: hidden;
        }

        /* 头部样式 */
        .header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 20px 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 2rem;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 15px;
        }

        .subtitle {
            font-size: 1.1rem;
            opacity: 0.9;
            font-weight: 300;
        }

        /* 主内容布局 */
        .main-content {
            display: grid;
            grid-template-columns: 1fr;
            gap: 20px;
            padding: 20px;
            min-height: 800px;
        }

        @media (min-width: 1200px) {
            .main-content {
                grid-template-columns: 300px 1fr 300px;
            }
        }

        /* 卡片通用样式 */
        .card {
            background: white;
            border-radius: 15px;
            padding: 15px;
            margin-bottom: 20px;
            box-shadow: 0 5px 20px rgba(0, 0, 0, 0.08);
            border: 1px solid rgba(0, 0, 0, 0.05);
            transition: transform 0.3s ease, box-shadow 0.3s ease;
        }

        .card:hover {
            transform: translateY(-3px);
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
        }

        .card h2 {
            color: #4a5568;
            font-size: 1.2rem;
            margin-bottom: 15px;
            display: flex;
            align-items: center;
            gap: 10px;
            border-bottom: 2px solid #e2e8f0;
            padding-bottom: 8px;
        }

        /* 用户管理样式 */
        .user-list {
            margin-bottom: 15px;
        }

        .user-item {
            padding: 10px 12px;
            background: #f7fafc;
            border-radius: 8px;
            margin-bottom: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            border: 2px solid transparent;
        }

        .user-item:hover {
            background: #edf2f7;
            transform: translateX(3px);
        }

        .user-item.active {
            border-color: #4299e1;
            background: #ebf8ff;
        }

        .user-name {
            font-weight: 600;
            color: #2d3748;
            margin-bottom: 3px;
        }

        .user-meta {
            font-size: 0.8rem;
            color: #718096;
            display: flex;
            justify-content: space-between;
        }

        /* 按钮样式 */
        .btn {
            padding: 10px 15px;
            border: none;
            border-radius: 8px;
            font-size: 0.9rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            display: inline-flex;
            align-items: center;
            gap: 5px;
            justify-content: center;
            width: 100%;
            margin-bottom: 10px;
        }

        .btn-primary {
            background: linear-gradient(135deg, #4299e1 0%, #3182ce 100%);
            color: white;
        }

        .btn-primary:hover {
            background: linear-gradient(135deg, #3182ce 0%, #2c5282 100%);
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(66, 153, 225, 0.4);
        }

        /* 推荐内容样式 */
        .recommendations-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
            gap: 15px;
            margin-top: 15px;
        }

        .recommendation-item {
            background: white;
            border-radius: 10px;
            overflow: hidden;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
            transition: all 0.3s ease;
            border: 1px solid #e2e8f0;
        }

        .recommendation-item:hover {
            transform: translateY(-3px);
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
        }

        .content-header {
            height: 50px;
            display: flex;
            align-items: center;
            padding: 0 12px;
            color: white;
            font-weight: 600;
            font-size: 1rem;
        }

        .content-body {
            padding: 15px;
        }

        .content-title {
            font-size: 1rem;
            font-weight: 600;
            color: #2d3748;
            margin-bottom: 8px;
            line-height: 1.3;
        }

        .content-description {
            font-size: 0.85rem;
            color: #718096;
            margin-bottom: 12px;
            line-height: 1.4;
        }

        .content-meta {
            display: flex;
            justify-content: space-between;
            font-size: 0.8rem;
            color: #4a5568;
            margin-bottom: 12px;
        }

        .content-category {
            background: #edf2f7;
            padding: 2px 8px;
            border-radius: 12px;
            font-weight: 600;
        }

        .content-stats {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 8px;
            margin-bottom: 12px;
            text-align: center;
        }

        .stat-item {
            background: #f7fafc;
            padding: 6px;
            border-radius: 6px;
        }

        .stat-label {
            font-size: 0.7rem;
            color: #718096;
            margin-bottom: 2px;
        }

        .stat-value {
            font-weight: 700;
            color: #2d3748;
            font-size: 0.9rem;
        }

        .content-actions {
            display: flex;
            gap: 8px;
        }

        .action-btn {
            flex: 1;
            padding: 6px 8px;
            border: none;
            border-radius: 6px;
            font-size: 0.8rem;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 3px;
        }

        .action-click {
            background: #4299e1;
            color: white;
        }

        .action-click:hover {
            background: #3182ce;
        }

        .action-purchase {
            background: #48bb78;
            color: white;
        }

        .action-purchase:hover {
            background: #38a169;
        }

        .action-watch {
            background: #ed8936;
            color: white;
        }

        .action-watch:hover {
            background: #dd6b20;
        }

        /* 图表容器 */
        .chart-container {
            height: 200px;
            margin-bottom: 15px;
            position: relative;
        }

        /* 技术标签页 */
        .tech-tabs {
            display: flex;
            gap: 8px;
            margin-bottom: 15px;
            border-bottom: 2px solid #e2e8f0;
            padding-bottom: 8px;
        }

        .tech-tab {
            padding: 6px 12px;
            background: #edf2f7;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 600;
            color: #4a5568;
            transition: all 0.3s ease;
            font-size: 0.85rem;
        }

        .tech-tab:hover {
            background: #e2e8f0;
        }

        .tech-tab.active {
            background: #4299e1;
            color: white;
        }

        /* 统计网格 */
        .stats-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
        }

        .stat-box {
            background: #f7fafc;
            padding: 12px;
            border-radius: 8px;
            text-align: center;
        }

        .stat-value {
            font-size: 1.5rem;
            font-weight: 700;
            color: #4299e1;
            margin-bottom: 3px;
        }

        .stat-label {
            font-size: 0.85rem;
            color: #718096;
        }

        /* 行为列表 */
        .behavior-list {
            max-height: 250px;
            overflow-y: auto;
        }

        .behavior-item {
            padding: 10px;
            background: #f7fafc;
            border-radius: 6px;
            margin-bottom: 8px;
            border-left: 4px solid #4299e1;
            font-size: 0.85rem;
        }

        .behavior-time {
            font-size: 0.75rem;
            color: #718096;
            margin-bottom: 3px;
        }

        .behavior-content {
            font-weight: 600;
            color: #2d3748;
            margin-bottom: 3px;
        }

        .behavior-type {
            display: inline-block;
            padding: 2px 6px;
            border-radius: 10px;
            font-size: 0.7rem;
            font-weight: 600;
            margin-right: 5px;
        }

        .type-click { background: #bee3f8; color: #2c5282; }
        .type-purchase { background: #c6f6d5; color: #276749; }
        .type-watch { background: #fed7d7; color: #9b2c2c; }
        .type-skip { background: #e2e8f0; color: #4a5568; }

        /* 策略选择器 */
        .strategy-selector {
            display: flex;
            align-items: center;
            gap: 10px;
            flex-wrap: wrap;
            margin-bottom: 10px;
        }

        .strategy-btn {
            padding: 6px 12px;
            background: #edf2f7;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-weight: 600;
            color: #4a5568;
            transition: all 0.3s ease;
            font-size: 0.85rem;
        }

        .strategy-btn:hover {
            background: #e2e8f0;
        }

        .strategy-btn.active {
            background: #4299e1;
            color: white;
        }

        .diversity-score {
            background: #48bb78;
            color: white;
            padding: 4px 10px;
            border-radius: 12px;
            font-weight: 600;
            font-size: 0.85rem;
        }

        /* 兴趣列表 */
        .interest-list {
            margin-top: 15px;
        }

        .interest-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px;
            background: #f7fafc;
            border-radius: 6px;
            margin-bottom: 6px;
            font-size: 0.85rem;
        }

        .interest-category {
            font-weight: 600;
            color: #2d3748;
        }

        .interest-values {
            display: flex;
            gap: 12px;
        }

        .interest-value {
            text-align: center;
        }

        .interest-label {
            font-size: 0.7rem;
            color: #718096;
        }

        /* 表单样式 */
        .form-group {
            margin-bottom: 12px;
        }

        .form-group label {
            display: block;
            margin-bottom: 3px;
            color: #4a5568;
            font-weight: 600;
            font-size: 0.9rem;
        }

        .form-group input,
        .form-group select {
            width: 100%;
            padding: 8px;
            border: 2px solid #e2e8f0;
            border-radius: 6px;
            font-size: 0.9rem;
            transition: border-color 0.3s ease;
        }

        .form-group input:focus,
        .form-group select:focus {
            outline: none;
            border-color: #4299e1;
        }

        /* 底部样式 */
        .footer {
            background: #2d3748;
            color: white;
            padding: 15px 30px;
            text-align: center;
            border-top: 1px solid #4a5568;
            font-size: 0.9rem;
        }

        .footer p {
            margin-bottom: 3px;
        }

        .update-time {
            font-size: 0.8rem;
            opacity: 0.7;
        }

        /* 响应式设计 */
        @media (max-width: 768px) {
            .container {
                border-radius: 10px;
                padding: 10px;
            }
            
            .header {
                padding: 15px;
            }
            
            .header h1 {
                font-size: 1.5rem;
            }
            
            .main-content {
                padding: 10px;
            }
            
            .recommendations-grid {
                grid-template-columns: 1fr;
            }
            
            .strategy-selector {
                flex-direction: column;
                align-items: flex-start;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <!-- 头部 -->
        <header class="header">
            <h1><i class="fas fa-robot"></i> 推荐系统高阶技术可视化平台</h1>
            <p class="subtitle">混排 · 多任务学习 · 在线学习</p>
        </header>

        <div class="main-content">
            <!-- 左侧面板 -->
            <div class="left-panel">
                <!-- 用户管理 -->
                <div class="card user-management">
                    <h2><i class="fas fa-users"></i> 用户管理</h2>
                    <div class="user-list" id="userList">
                        <!-- 用户列表将通过JS动态加载 -->
                    </div>
                    <div class="user-actions">
                        <button class="btn btn-primary" onclick="simulateActions()">
                            <i class="fas fa-play"></i> 模拟用户行为
                        </button>
                        <div class="form-group">
                            <label>模拟次数:</label>
                            <input type="number" id="actionCount" value="3" min="1" max="10">
                        </div>
                    </div>
                </div>

                <!-- 系统统计 -->
                <div class="card system-stats">
                    <h2><i class="fas fa-chart-bar"></i> 系统统计</h2>
                    <div class="stats-grid" id="systemStats">
                        <!-- 统计信息将通过JS动态加载 -->
                    </div>
                </div>

                <!-- 技术说明 -->
                <div class="card tech-info">
                    <h2><i class="fas fa-info-circle"></i> 技术说明</h2>
                    <div class="tech-tabs">
                        <button class="tech-tab active" onclick="showTechTab('mixed')">混排技术</button>
                        <button class="tech-tab" onclick="showTechTab('multi')">多任务学习</button>
                        <button class="tech-tab" onclick="showTechTab('online')">在线学习</button>
                    </div>
                    <div class="tech-content" id="mixedTech">
                        <h3>混排技术 (Mixed Ranking)</h3>
                        <p>通过智能搭配不同品类内容,平衡精准推荐和内容多样性。</p>
                        <ul>
                            <li><strong>基于规则混排</strong>: 固定比例分配推荐位</li>
                            <li><strong>基于模型混排</strong>: 多目标优化动态分配</li>
                            <li><strong>解决精准陷阱</strong>: 避免用户兴趣固化</li>
                        </ul>
                    </div>
                    <div class="tech-content" id="multiTech" style="display: none;">
                        <h3>多任务学习 (Multi-Task Learning)</h3>
                        <p>同时优化多个业务目标,让模型更全面理解用户需求。</p>
                        <ul>
                            <li><strong>共享特征层</strong>: 提取通用用户和内容特征</li>
                            <li><strong>任务特定层</strong>: 针对不同目标分别优化</li>
                            <li><strong>协同优化</strong>: CTR、CVR、停留时长等多目标</li>
                        </ul>
                    </div>
                    <div class="tech-content" id="onlineTech" style="display: none;">
                        <h3>在线学习 (Online Learning)</h3>
                        <p>实时捕捉用户行为,动态更新模型参数。</p>
                        <ul>
                            <li><strong>增量训练</strong>: 使用实时数据微调模型</li>
                            <li><strong>快速响应</strong>: 秒级更新推荐结果</li>
                            <li><strong>动态适应</strong>: 紧跟用户兴趣变化</li>
                        </ul>
                    </div>
                </div>
            </div>

            <!-- 主内容区 -->
            <div class="main-panel">
                <!-- 推荐结果 -->
                <div class="card recommendations">
                    <div class="card-header">
                        <h2><i class="fas fa-th-list"></i> 推荐结果</h2>
                        <div class="strategy-selector">
                            <span>推荐策略:</span>
                            <button class="strategy-btn active" onclick="changeStrategy('model_based')">模型混排</button>
                            <button class="strategy-btn" onclick="changeStrategy('rule_based')">规则混排</button>
                            <span class="diversity-score" id="diversityScore">多样性: --</span>
                        </div>
                    </div>
                    <div class="recommendations-grid" id="recommendationsGrid">
                        <!-- 推荐内容将通过JS动态加载 -->
                    </div>
                </div>

                <!-- 用户兴趣分析 -->
                <div class="card user-interests">
                    <h2><i class="fas fa-chart-line"></i> 用户兴趣分析</h2>
                    <div class="chart-container">
                        <canvas id="interestChart"></canvas>
                    </div>
                    <div class="interest-list" id="interestList">
                        <!-- 兴趣列表将通过JS动态加载 -->
                    </div>
                </div>

                <!-- 在线学习状态 -->
                <div class="card online-learning">
                    <h2><i class="fas fa-sync-alt"></i> 在线学习状态</h2>
                    <div class="online-stats" id="onlineStats">
                        <!-- 在线学习状态将通过JS动态加载 -->
                    </div>
                    <div class="update-log" id="updateLog">
                        <h3>最近更新记录</h3>
                        <div class="log-list" id="logList">
                            <!-- 更新日志将通过JS动态加载 -->
                        </div>
                    </div>
                </div>
            </div>

            <!-- 右侧面板 -->
            <div class="right-panel">
                <!-- 用户行为记录 -->
                <div class="card behavior-log">
                    <h2><i class="fas fa-history"></i> 用户行为记录</h2>
                    <div class="behavior-list" id="behaviorList">
                        <!-- 行为记录将通过JS动态加载 -->
                    </div>
                </div>

                <!-- 内容分类统计 -->
                <div class="card category-stats">
                    <h2><i class="fas fa-tags"></i> 内容分类统计</h2>
                    <div class="category-chart">
                        <canvas id="categoryChart"></canvas>
                    </div>
                    <div class="category-list" id="categoryList">
                        <!-- 分类统计将通过JS动态加载 -->
                    </div>
                </div>

                <!-- 多任务模型信息 -->
                <div class="card model-info">
                    <h2><i class="fas fa-network-wired"></i> 多任务模型</h2>
                    <div class="model-details" id="modelDetails">
                        <!-- 模型信息将通过JS动态加载 -->
                    </div>
                </div>
            </div>
        </div>

        <!-- 底部信息 -->
        <footer class="footer">
            <p>推荐系统高阶技术演示平台 | 混排 + 多任务学习 + 在线学习</p>
            <p class="update-time" id="updateTime">最后更新: --</p>
        </footer>
    </div>

    <script>
        // 全局变量
        let currentUserId = 1;
        let currentStrategy = 'model_based';
        let interestChart = null;
        let categoryChart = null;

        // DOM加载完成后初始化
        document.addEventListener('DOMContentLoaded', function() {
            loadUsers();
            loadSystemStats();
            loadUserData(currentUserId);
            loadContentCategories();
            loadOnlineLearningStats();
            
            // 设置自动刷新
            setInterval(updateDashboard, 5000);
            
            // 初始化技术标签页
            showTechTab('mixed');
        });

        // 加载用户列表
        async function loadUsers() {
            try {
                const response = await fetch('/api/users');
                const data = await response.json();
                
                const userList = document.getElementById('userList');
                userList.innerHTML = '';
                
                data.users.forEach(user => {
                    const userItem = document.createElement('div');
                    userItem.className = `user-item ${user.id === currentUserId ? 'active' : ''}`;
                    userItem.innerHTML = `
                        <div class="user-name">${user.name}</div>
                        <div class="user-meta">
                            <span>兴趣: ${user.interest_count}</span>
                            <span>行为: ${user.behavior_count}</span>
                        </div>
                    `;
                    
                    userItem.onclick = () => {
                        currentUserId = user.id;
                        loadUserData(user.id);
                        document.querySelectorAll('.user-item').forEach(item => {
                            item.classList.remove('active');
                        });
                        userItem.classList.add('active');
                    };
                    
                    userList.appendChild(userItem);
                });
            } catch (error) {
                console.error('加载用户失败:', error);
            }
        }

        // 加载用户数据
        async function loadUserData(userId) {
            try {
                const response = await fetch(`/api/user/${userId}`);
                const data = await response.json();
                
                // 加载推荐
                loadRecommendations();
                
                // 加载用户兴趣
                loadUserInterests(userId);
                
                // 加载行为记录
                loadBehaviorLog(data.recent_behavior);
            } catch (error) {
                console.error('加载用户数据失败:', error);
            }
        }

        // 加载推荐结果
        async function loadRecommendations() {
            try {
                const response = await fetch(`/api/recommend/${currentUserId}?strategy=${currentStrategy}`);
                const data = await response.json();
                
                const grid = document.getElementById('recommendationsGrid');
                grid.innerHTML = '';
                
                // 更新多样性分数
                document.getElementById('diversityScore').textContent = `多样性: ${data.diversity_score}`;
                
                data.recommendations.forEach(content => {
                    const item = document.createElement('div');
                    item.className = 'recommendation-item';
                    
                    // 为不同类别设置不同颜色
                    const categoryColors = {
                        '科技': '#2196F3',
                        '娱乐': '#FF9800',
                        '体育': '#4CAF50',
                        '财经': '#9C27B0',
                        '时尚': '#E91E63',
                        '教育': '#00BCD4',
                        '健康': '#8BC34A',
                        '美食': '#FF5722'
                    };
                    
                    const bgColor = categoryColors[content.category] || '#607D8B';
                    
                    item.innerHTML = `
                        <div class="content-header" style="background: ${bgColor}">
                            ${content.category}
                        </div>
                        <div class="content-body">
                            <div class="content-title">${content.title}</div>
                            <div class="content-description">${content.description}</div>
                            <div class="content-meta">
                                <span class="content-category">${content.category}</span>
                                <span>新鲜度: ${content.freshness}天</span>
                            </div>
                            <div class="content-stats">
                                <div class="stat-item">
                                    <div class="stat-label">CTR预测</div>
                                    <div class="stat-value">${content.ctr_pred}</div>
                                </div>
                                <div class="stat-item">
                                    <div class="stat-label">CVR预测</div>
                                    <div class="stat-value">${content.cvr_pred}</div>
                                </div>
                                <div class="stat-item">
                                    <div class="stat-label">综合得分</div>
                                    <div class="stat-value">${content.final_score}</div>
                                </div>
                            </div>
                            <div class="content-actions">
                                <button class="action-btn action-click" onclick="processAction(${content.id}, 'click')">
                                    <i class="fas fa-mouse-pointer"></i> 点击
                                </button>
                                <button class="action-btn action-watch" onclick="processAction(${content.id}, 'watch_long')">
                                    <i class="fas fa-eye"></i> 观看
                                </button>
                                <button class="action-btn action-purchase" onclick="processAction(${content.id}, 'purchase')">
                                    <i class="fas fa-shopping-cart"></i> 购买
                                </button>
                            </div>
                        </div>
                    `;
                    
                    grid.appendChild(item);
                });
            } catch (error) {
                console.error('加载推荐失败:', error);
            }
        }

        // 处理用户行为
        async function processAction(contentId, actionType) {
            try {
                const response = await fetch('/api/action', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        user_id: currentUserId,
                        content_id: contentId,
                        action_type: actionType
                    })
                });
                
                const data = await response.json();
                
                if (data.success) {
                    // 重新加载数据
                    loadUserData(currentUserId);
                    loadSystemStats();
                    loadOnlineLearningStats();
                    
                    // 显示成功消息
                    showNotification(`用户行为已处理: ${getActionName(actionType)}`, 'success');
                }
            } catch (error) {
                console.error('处理行为失败:', error);
                showNotification('处理失败,请重试', 'error');
            }
        }

        // 获取行为名称
        function getActionName(actionType) {
            const names = {
                'click': '点击',
                'watch_long': '观看',
                'purchase': '购买',
                'skip': '跳过'
            };
            return names[actionType] || actionType;
        }

        // 加载系统统计
        async function loadSystemStats() {
            try {
                const response = await fetch('/api/system/stats');
                const data = await response.json();
                
                const statsGrid = document.getElementById('systemStats');
                statsGrid.innerHTML = `
                    <div class="stat-box">
                        <div class="stat-value">${data.system_stats.total_users}</div>
                        <div class="stat-label">总用户数</div>
                    </div>
                    <div class="stat-box">
                        <div class="stat-value">${data.system_stats.total_recommendations}</div>
                        <div class="stat-label">推荐次数</div>
                    </div>
                    <div class="stat-box">
                        <div class="stat-value">${data.system_stats.total_interactions}</div>
                        <div class="stat-label">交互次数</div>
                    </div>
                    <div class="stat-box">
                        <div class="stat-value">${data.system_stats.average_diversity}</div>
                        <div class="stat-label">平均多样性</div>
                    </div>
                `;
                
                // 更新模型信息
                const modelDetails = document.getElementById('modelDetails');
                modelDetails.innerHTML = `
                    <div class="stat-box">
                        <div class="stat-value">${data.multi_task_model.update_count}</div>
                        <div class="stat-label">更新次数</div>
                    </div>
                    <div class="stat-box">
                        <div class="stat-value">${data.multi_task_model.shared_layer_shape[0]}×${data.multi_task_model.shared_layer_shape[1]}</div>
                        <div class="stat-label">共享层尺寸</div>
                    </div>
                `;
            } catch (error) {
                console.error('加载系统统计失败:', error);
            }
        }

        // 加载用户兴趣
        async function loadUserInterests(userId) {
            try {
                const response = await fetch(`/api/user/${userId}/interests`);
                const data = await response.json();
                
                // 更新兴趣列表
                const interestList = document.getElementById('interestList');
                interestList.innerHTML = '';
                
                data.interests.forEach(interest => {
                    const item = document.createElement('div');
                    item.className = 'interest-item';
                    
                    // 计算变化指示器
                    let changeIndicator = '';
                    if (interest.change > 0.1) {
                        changeIndicator = '<i class="fas fa-arrow-up" style="color: #48bb78;"></i>';
                    } else if (interest.change < -0.1) {
                        changeIndicator = '<i class="fas fa-arrow-down" style="color: #f56565;"></i>';
                    }
                    
                    item.innerHTML = `
                        <div class="interest-category">${interest.category}</div>
                        <div class="interest-values">
                            <div class="interest-value">
                                <div class="interest-label">基础</div>
                                <div>${interest.base_interest}</div>
                            </div>
                            <div class="interest-value">
                                <div class="interest-label">当前</div>
                                <div>${interest.current_interest} ${changeIndicator}</div>
                            </div>
                        </div>
                    `;
                    
                    interestList.appendChild(item);
                });
                
                // 更新图表
                updateInterestChart(data.interests);
            } catch (error) {
                console.error('加载用户兴趣失败:', error);
            }
        }

        // 更新兴趣图表
        function updateInterestChart(interests) {
            const ctx = document.getElementById('interestChart').getContext('2d');
            
            const categories = interests.map(i => i.category);
            const baseInterests = interests.map(i => i.base_interest);
            const currentInterests = interests.map(i => i.current_interest);
            
            if (interestChart) {
                interestChart.destroy();
            }
            
            interestChart = new Chart(ctx, {
                type: 'bar',
                data: {
                    labels: categories,
                    datasets: [
                        {
                            label: '基础兴趣',
                            data: baseInterests,
                            backgroundColor: 'rgba(54, 162, 235, 0.6)',
                            borderColor: 'rgba(54, 162, 235, 1)',
                            borderWidth: 1
                        },
                        {
                            label: '当前兴趣',
                            data: currentInterests,
                            backgroundColor: 'rgba(75, 192, 192, 0.6)',
                            borderColor: 'rgba(75, 192, 192, 1)',
                            borderWidth: 1
                        }
                    ]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            max: 1,
                            title: {
                                display: true,
                                text: '兴趣度'
                            }
                        }
                    },
                    plugins: {
                        legend: {
                            position: 'top',
                        },
                        tooltip: {
                            mode: 'index',
                            intersect: false
                        }
                    }
                }
            });
        }

        // 加载行为记录
        function loadBehaviorLog(behaviors) {
            const behaviorList = document.getElementById('behaviorList');
            behaviorList.innerHTML = '';
            
            if (!behaviors || behaviors.length === 0) {
                behaviorList.innerHTML = '<p style="text-align: center; color: #718096;">暂无行为记录</p>';
                return;
            }
            
            behaviors.reverse().forEach(behavior => {
                const item = document.createElement('div');
                item.className = 'behavior-item';
                
                const actionType = behavior.action.type;
                const typeClass = `type-${actionType}`;
                
                item.innerHTML = `
                    <div class="behavior-time">${behavior.timestamp}</div>
                    <div class="behavior-content">${behavior.action.title}</div>
                    <div>
                        <span class="behavior-type ${typeClass}">${getActionName(actionType)}</span>
                        <span style="color: #718096; font-size: 0.9rem;">${behavior.category}</span>
                    </div>
                `;
                
                behaviorList.appendChild(item);
            });
        }

        // 加载内容分类统计
        async function loadContentCategories() {
            try {
                const response = await fetch('/api/content/categories');
                const data = await response.json();
                
                // 更新分类列表
                const categoryList = document.getElementById('categoryList');
                categoryList.innerHTML = '';
                
                Object.entries(data.categories).forEach(([category, stats]) => {
                    const item = document.createElement('div');
                    item.className = 'interest-item';
                    
                    item.innerHTML = `
                        <div class="interest-category">${category}</div>
                        <div class="interest-values">
                            <div class="interest-value">
                                <div class="interest-label">内容数</div>
                                <div>${stats.count}</div>
                            </div>
                            <div class="interest-value">
                                <div class="interest-label">平均CTR</div>
                                <div>${stats.avg_ctr}</div>
                            </div>
                            <div class="interest-value">
                                <div class="interest-label">平均CVR</div>
                                <div>${stats.avg_cvr}</div>
                            </div>
                        </div>
                    `;
                    
                    categoryList.appendChild(item);
                });
                
                // 更新分类图表
                updateCategoryChart(data.categories);
            } catch (error) {
                console.error('加载分类统计失败:', error);
            }
        }

        // 更新分类图表
        function updateCategoryChart(categories) {
            const ctx = document.getElementById('categoryChart').getContext('2d');
            
            const categoryNames = Object.keys(categories);
            const contentCounts = categoryNames.map(name => categories[name].count);
            
            // 生成颜色数组
            const backgroundColors = [
                '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0',
                '#9966FF', '#FF9F40', '#8AC926', '#1982C4'
            ];
            
            if (categoryChart) {
                categoryChart.destroy();
            }
            
            categoryChart = new Chart(ctx, {
                type: 'doughnut',
                data: {
                    labels: categoryNames,
                    datasets: [{
                        data: contentCounts,
                        backgroundColor: backgroundColors,
                        borderWidth: 1
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            position: 'right',
                            labels: {
                                boxWidth: 12,
                                padding: 15
                            }
                        }
                    }
                }
            });
        }

        // 加载在线学习状态
        async function loadOnlineLearningStats() {
            try {
                const response = await fetch('/api/system/stats');
                const data = await response.json();
                
                const onlineStats = document.getElementById('onlineStats');
                onlineStats.innerHTML = `
                    <div class="stats-grid">
                        <div class="stat-box">
                            <div class="stat-value">${data.online_learning.total_updates}</div>
                            <div class="stat-label">总更新次数</div>
                        </div>
                        <div class="stat-box">
                            <div class="stat-value">${data.online_learning.recent_feedback_count}</div>
                            <div class="stat-label">待处理反馈</div>
                        </div>
                        <div class="stat-box">
                            <div class="stat-value">${data.online_learning.update_interval}s</div>
                            <div class="stat-label">更新间隔</div>
                        </div>
                        <div class="stat-box">
                            <div class="stat-value">${data.online_learning.user_model_count}</div>
                            <div class="stat-label">用户模型数</div>
                        </div>
                    </div>
                `;
                
                // 更新日志
                const logList = document.getElementById('logList');
                const now = new Date();
                const timeStr = now.getHours().toString().padStart(2, '0') + ':' + 
                               now.getMinutes().toString().padStart(2, '0') + ':' + 
                               now.getSeconds().toString().padStart(2, '0');
                
                const logItem = document.createElement('div');
                logItem.className = 'behavior-item';
                logItem.innerHTML = `
                    <div class="behavior-time">${timeStr}</div>
                    <div class="behavior-content">系统状态检查 - 在线学习运行正常</div>
                    <div>
                        <span class="behavior-type type-click">更新</span>
                        <span style="color: #718096; font-size: 0.9rem;">模型检查完成</span>
                    </div>
                `;
                
                logList.insertBefore(logItem, logList.firstChild);
                
                // 保持日志数量不超过10条
                if (logList.children.length > 10) {
                    logList.removeChild(logList.lastChild);
                }
                
                // 更新最后更新时间
                document.getElementById('updateTime').textContent = `最后更新: ${timeStr}`;
            } catch (error) {
                console.error('加载在线学习状态失败:', error);
            }
        }

        // 更改推荐策略
        function changeStrategy(strategy) {
            currentStrategy = strategy;
            
            // 更新按钮状态
            document.querySelectorAll('.strategy-btn').forEach(btn => {
                btn.classList.remove('active');
            });
            event.target.classList.add('active');
            
            // 重新加载推荐
            loadRecommendations();
        }

        // 显示技术标签页
        function showTechTab(tabName) {
            // 隐藏所有内容
            document.querySelectorAll('.tech-content').forEach(content => {
                content.style.display = 'none';
            });
            
            // 更新标签按钮
            document.querySelectorAll('.tech-tab').forEach(tab => {
                tab.classList.remove('active');
            });
            event.target.classList.add('active');
            
            // 显示对应内容
            document.getElementById(tabName + 'Tech').style.display = 'block';
        }

        // 模拟用户行为
        async function simulateActions() {
            const actionCount = document.getElementById('actionCount').value;
            
            try {
                const response = await fetch('/api/demo/simulate', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        user_id: currentUserId,
                        action_count: parseInt(actionCount)
                    })
                });
                
                const data = await response.json();
                
                if (data.success) {
                    // 重新加载数据
                    loadUserData(currentUserId);
                    loadSystemStats();
                    loadOnlineLearningStats();
                    
                    // 显示模拟结果
                    showNotification(`成功模拟${data.actions.length}个用户行为`, 'success');
                    
                    // 添加到日志
                    const logList = document.getElementById('logList');
                    data.actions.forEach(action => {
                        const logItem = document.createElement('div');
                        logItem.className = 'behavior-item';
                        logItem.innerHTML = `
                            <div class="behavior-time">${action.timestamp}</div>
                            <div class="behavior-content">${action.content_title}</div>
                            <div>
                                <span class="behavior-type type-${action.action_type}">${getActionName(action.action_type)}</span>
                                <span style="color: #718096; font-size: 0.9rem;">${action.category}</span>
                            </div>
                        `;
                        logList.insertBefore(logItem, logList.firstChild);
                    });
                }
            } catch (error) {
                console.error('模拟行为失败:', error);
                showNotification('模拟失败,请重试', 'error');
            }
        }

        // 更新仪表板
        function updateDashboard() {
            loadSystemStats();
            loadOnlineLearningStats();
            loadUserInterests(currentUserId);
        }

        // 显示通知
        function showNotification(message, type = 'info') {
            // 创建通知元素
            const notification = document.createElement('div');
            notification.className = `notification notification-${type}`;
            notification.innerHTML = `
                <i class="fas fa-${type === 'success' ? 'check-circle' : 'exclamation-circle'}"></i>
                <span>${message}</span>
            `;
            
            // 添加到页面
            document.body.appendChild(notification);
            
            // 添加样式
            notification.style.cssText = `
                position: fixed;
                top: 20px;
                right: 20px;
                padding: 12px 16px;
                background: ${type === 'success' ? '#48bb78' : '#f56565'};
                color: white;
                border-radius: 8px;
                box-shadow: 0 5px 15px rgba(0,0,0,0.2);
                display: flex;
                align-items: center;
                gap: 8px;
                z-index: 1000;
                animation: slideIn 0.3s ease;
                font-size: 0.9rem;
            `;
            
            // 添加动画
            const style = document.createElement('style');
            style.textContent = `
                @keyframes slideIn {
                    from { transform: translateX(100%); opacity: 0; }
                    to { transform: translateX(0); opacity: 1; }
                }
                @keyframes slideOut {
                    from { transform: translateX(0); opacity: 1; }
                    to { transform: translateX(100%); opacity: 0; }
                }
            `;
            document.head.appendChild(style);
            
            // 3秒后移除
            setTimeout(() => {
                notification.style.animation = 'slideOut 0.3s ease';
                setTimeout(() => {
                    if (notification.parentNode) {
                        notification.parentNode.removeChild(notification);
                    }
                }, 300);
            }, 3000);
        }
    </script>
</body>
</html>
'''

# ==================== Flask路由 ====================

@app.route('/')
def index():
    """主页面"""
    return render_template_string(HTML_TEMPLATE)

@app.route('/api/users')
def get_users():
    """获取所有用户列表"""
    users = []
    for user_id, user in recommendation_system.users.items():
        users.append({
            'id': user_id,
            'name': user.name,
            'interest_count': len(user.real_time_interests),
            'behavior_count': len(user.history_behavior)
        })
    return jsonify({'users': users})

@app.route('/api/user/<int:user_id>')
def get_user(user_id):
    """获取用户详细信息"""
    user_info = recommendation_system.get_user_info(user_id)
    if user_info:
        return jsonify(user_info)
    return jsonify({'error': '用户不存在'}), 404

@app.route('/api/recommend/<int:user_id>')
def get_recommendations(user_id):
    """获取用户推荐"""
    strategy = request.args.get('strategy', 'model_based')
    recommendations = recommendation_system.generate_recommendations(user_id, strategy)

    # 计算推荐列表的多样性
    categories = [r['category'] for r in recommendations]
    category_counts = {}
    for category in categories:
        category_counts[category] = category_counts.get(category, 0) + 1

    total = len(categories)
    hhi = sum((count/total)**2 for count in category_counts.values())
    diversity = 1 - hhi

    return jsonify({
        'recommendations': recommendations,
        'diversity_score': round(diversity, 3),
        'strategy': strategy,
        'count': len(recommendations)
    })

@app.route('/api/action', methods=['POST'])
def process_action():
    """处理用户行为"""
    data = request.json
    user_id = data.get('user_id')
    content_id = data.get('content_id')
    action_type = data.get('action_type')

    if not all([user_id, content_id, action_type]):
        return jsonify({'error': '参数不完整'}), 400

    success = recommendation_system.process_user_action(user_id, content_id, action_type)

    if success:
        return jsonify({'success': True, 'message': '行为已处理'})
    else:
        return jsonify({'error': '处理失败'}), 400

@app.route('/api/system/stats')
def get_system_stats():
    """获取系统统计信息"""
    stats = recommendation_system.get_system_stats()
    online_info = recommendation_system.online_learning.get_system_info()
    model_info = recommendation_system.multi_task_model.get_model_info()

    return jsonify({
        'system_stats': stats,
        'online_learning': online_info,
        'multi_task_model': model_info
    })

@app.route('/api/content/categories')
def get_content_categories():
    """获取内容分类统计"""
    categories = recommendation_system.content_pool.categories
    stats = {}

    for category in categories:
        contents = recommendation_system.content_pool.get_contents_by_category(category, 100)
        avg_ctr = sum(c['ctr'] for c in contents) / len(contents) if contents else 0
        avg_cvr = sum(c['cvr'] for c in contents) / len(contents) if contents else 0

        stats[category] = {
            'count': len(contents),
            'avg_ctr': round(avg_ctr, 3),
            'avg_cvr': round(avg_cvr, 4)
        }

    return jsonify({'categories': stats})

@app.route('/api/user/<int:user_id>/interests')
def get_user_interests(user_id):
    """获取用户兴趣变化"""
    if user_id not in recommendation_system.users:
        return jsonify({'error': '用户不存在'}), 404

    user = recommendation_system.users[user_id]

    # 基础兴趣
    base_interests = user.base_interests

    # 当前实时兴趣
    current_interests = user.real_time_interests

    # 所有兴趣类别
    all_categories = list(set(list(base_interests.keys()) + list(current_interests.keys())))

    interests_data = []
    for category in all_categories:
        interests_data.append({
            'category': category,
            'base_interest': round(base_interests.get(category, 0), 3),
            'current_interest': round(current_interests.get(category, 0), 3),
            'change': round(current_interests.get(category, 0) - base_interests.get(category, 0), 3)
        })

    # 按当前兴趣排序
    interests_data.sort(key=lambda x: x['current_interest'], reverse=True)

    return jsonify({'interests': interests_data})

@app.route('/api/demo/simulate', methods=['POST'])
def simulate_actions():
    """模拟用户行为"""
    data = request.json
    user_id = data.get('user_id', 1)
    action_count = data.get('action_count', 5)

    user = recommendation_system.get_or_create_user(user_id)

    actions = []
    for i in range(action_count):
        # 生成推荐
        recommendations = recommendation_system.generate_recommendations(user_id)

        if recommendations:
            # 随机选择一条内容
            selected_content = random.choice(recommendations[:5])

            # 随机选择行为类型
            action_types = ['click', 'watch_long', 'purchase', 'skip']
            weights = [0.5, 0.3, 0.1, 0.1]
            action_type = random.choices(action_types, weights=weights)[0]

            # 处理行为
            recommendation_system.process_user_action(
                user_id, selected_content['id'], action_type
            )

            actions.append({
                'content_title': selected_content['title'],
                'category': selected_content['category'],
                'action_type': action_type,
                'timestamp': datetime.now().strftime("%H:%M:%S")
            })

    return jsonify({
        'success': True,
        'message': f'成功模拟{len(actions)}个行为',
        'actions': actions
    })

if __name__ == '__main__':
    print("="*70)
    print("推荐系统高阶技术可视化平台")
    print("访问地址: http://127.0.0.1:5000")
    print("="*70)
    app.run(debug=True, threaded=True)

这段代码是一个完整的推荐系统高阶技术可视化平台 ,集成了三个核心推荐系统技术:混排(Mixed Ranking)、多任务学习(Multi-Task Learning)、在线学习(Online Learning)

1.技术栈

  • 后端:Flask框架 + Python

  • 前端:HTML5 + CSS3 + JavaScript + Chart.js

  • 数据结构:Python类 + 随机数据生成

  • 部署:单文件Flask应用

2. 核心功能模块

推荐系统核心类分析

  1. UserProfile(用户画像类)
python 复制代码
功能:
- 生成用户基础兴趣(随机3-5个类别)
- 实时更新用户兴趣(基于用户行为)
- 兴趣衰减机制(非交互类别兴趣下降)
- 行为历史记录(最多50条)

技术特点:
- 模拟真实用户兴趣变化
- 支持兴趣的动态调整
- 记录完整行为轨迹
  1. ContentPool(内容池类)
python 复制代码
功能:
- 生成8个类别的模拟内容(科技、娱乐等)
- 每个内容包含:标题、类别、CTR、CVR、质量分、新鲜度
- 为每个类别生成独特的描述和颜色

技术特点:
- 模拟真实内容多样性
- 包含多维度内容特征
- 支持按类别检索
  1. MixedRanking(混排算法类)
python 复制代码
两种混排策略:
1. 规则混排:
   - 70%用户兴趣内容 + 20%相关品类 + 10%探索内容
   - 固定比例,简单直观

2. 模型混排:
   - 多目标优化:CTR(40%) + CVR(30%) + 用户兴趣(20%) + 新鲜度(5%) + 多样性(5%)
   - 动态权重分配
   - 多样性惩罚机制(避免同类内容过多)

技术特点:
- 平衡精准推荐与内容多样性
- 解决"精准陷阱"问题
- 支持多目标优化
  1. MultiTaskModel(多任务学习模型)
python 复制代码
神经网络架构:
- 共享层:10×8的全连接层(提取通用特征)
- 任务特定层:
  - CTR预测层:8×1
  - CVR预测层:8×1

技术特点:
- 共享特征提取,降低计算成本
- 同时优化CTR和CVR两个目标
- 模型参数可实时更新
  1. OnlineLearningSystem(在线学习系统)
python 复制代码
功能:
- 实时处理用户反馈
- 每5秒更新一次模型参数
- 增量训练(使用随机梯度下降简化版)

技术特点:
- 秒级响应时间
- 实时适应用户兴趣变化
- 处理噪声数据的鲁棒性
  1. RecommendationSystem(推荐系统主类)
python 复制代码
整合所有组件:
1. 用户管理:创建/获取用户画像
2. 推荐生成:混排策略 → 多任务重排
3. 行为处理:更新兴趣 + 在线学习
4. 系统统计:多样性计算、性能指标

工作流程:
用户请求 → 候选内容筛选 → 混排 → 多任务重排 → 返回推荐

可视化功能分析

  1. 前端界面布局
python 复制代码
三栏响应式设计:
- 左侧面板:用户管理 + 系统统计 + 技术说明
- 中间面板:推荐结果 + 用户兴趣图表 + 在线学习状态
- 右侧面板:行为记录 + 分类统计 + 模型信息
  1. 核心可视化组件

a) 推荐结果展示

彩色卡片设计(不同类别不同颜色)

显示内容:标题、描述、类别、新鲜度

预测指标:CTR预测、CVR预测、综合得分

交互按钮:点击、观看、购买

b) 用户兴趣分析

柱状图对比:基础兴趣 vs 当前兴趣

兴趣变化指示器(箭头图标)

兴趣数值列表(带变化值)

c) 系统统计仪表盘

实时指标:用户数、推荐次数、交互次数、多样性

在线学习状态:更新次数、待处理反馈、更新间隔

模型信息:更新次数、网络结构

d) 数据图表

用户兴趣柱状图(Chart.js)

内容分类环形图(Chart.js)

  1. 交互功能
python 复制代码
1. 用户切换:点击用户卡片切换当前用户
2. 策略切换:模型混排 vs 规则混排
3. 行为模拟:一键模拟多个用户行为
4. 实时交互:点击推荐内容的不同行为按钮
5. 技术说明:标签页切换查看不同技术原理

API接口设计

python 复制代码
1. GET /api/users                # 获取用户列表
2. GET /api/user/<user_id>       # 获取用户详细信息
3. GET /api/recommend/<user_id>  # 获取推荐内容
4. POST /api/action             # 处理用户行为
5. GET /api/system/stats        # 获取系统统计
6. GET /api/content/categories  # 获取内容分类统计
7. GET /api/user/<id>/interests # 获取用户兴趣
8. POST /api/demo/simulate      # 模拟用户行为

运行效果

运行后:

  1. 访问 http://127.0.0.1:5000

  2. 看到完整的可视化界面

  3. 可以选择用户、切换策略

  4. 可以点击推荐内容进行交互

  5. 观察用户兴趣和系统状态的变化

五、总结:高阶推荐系统的核心逻辑------从"精准"到"智能"

今天我们聊了推荐系统高阶的三个核心技术:混排、多任务学习、在线学习。其实这三个技术的核心逻辑,都是从入门阶段的"追求精准",升级到高阶的"追求智能"------

  • 混排:让推荐从"单品类精准"升级到"多品类智能搭配",兼顾用户体验和业务目标;

  • 多任务学习:让模型从"单一目标优化"升级到"多目标协同优化",更全面理解用户需求;

  • 在线学习:让系统从"定期更新"升级到"实时进化",紧跟用户兴趣的动态变化。

这三个技术不是孤立的,在实际业务中通常会结合使用:比如用多任务学习训练混排模型,再通过在线学习系统实时更新模型参数,最终实现"实时、精准、多样化"的推荐效果。

看到这里,你是不是对高阶推荐系统有了清晰的认知?其实这些技术看似复杂,但核心都是"以用户为中心",解决实际业务中的痛点。接下来的下一篇高阶内容,我们会聊"推荐系统的工程化实践"------比如如何应对高并发推荐请求?如何设计推荐系统的缓存策略?如何做推荐效果的AB测试?这些都是把高阶技术落地的关键。

如果今天的内容对你有帮助,欢迎在评论区分享你的学习心得,或者提出你想了解的高阶问题。咱们下一篇,继续解锁机器学习高阶技能!

相关推荐
AI营销资讯站2 小时前
AI营销内容生产新范式,原圈科技多智能体平台赋能全球化出海新机遇
人工智能
断剑zou天涯2 小时前
【算法笔记】AC自动机
java·笔记·算法
pingao1413782 小时前
气象监测新纪元:多功能自动站如何赋能智慧城市
人工智能·智慧城市
崇山峻岭之间2 小时前
Matlab学习记录06
前端·学习·matlab
独自归家的兔2 小时前
基于GUI-PLUS 搭配 Java Robot 实现智能桌面操控
java·开发语言·人工智能
西西学代码2 小时前
Flutter---常用打印图标
前端·python·flutter
IT猿手2 小时前
基于粒子群算法与动态窗口混合的无人机三维动态避障路径规划研究,MATLAB代码
算法·matlab·无人机·多目标优化算法·多目标算法
企微自动化2 小时前
企业微信外部群自动化系统的异常处理机制设计
开发语言·python
民乐团扒谱机2 小时前
【微实验】仿AU音频编辑器开发实践:从零构建音频可视化工具
算法·c#·仿真·audio·fft·频谱