4、基于内容的推荐配对:用户画像构建、物品特征提取、基于内容的匹配算法

好,咱们进入第四讲。前面几章我们聊了协同过滤,那种「物以类聚,人以群分」的思路。但现实中有个问题——新用户怎么办?新物品怎么办?这就是所谓的冷启动问题。

我个人习惯的做法是,当数据稀疏到没法做协同过滤时,就果断切换到基于内容的方法。说白了,就是让机器去理解「用户喜欢什么」和「物品有什么」,然后做匹配。

4.1 用户画像构建:给用户贴标签

用户画像,听起来高大上,其实核心就一件事:把用户的行为和属性,转成结构化的特征向量

我在项目中遇到过这样一个场景:一个婚恋交友平台,用户注册时填了一大堆信息——年龄、职业、兴趣爱好、星座、收入范围……但这些是原始数据,不能直接用来做匹配。你得加工。

4.1.1 显式特征与隐式特征

用户画像分两类特征:

  • 显式特征:用户主动填写的。比如「我喜欢运动」、「我月收入2万+」。这些直接可用,但可能不真实(你懂的,有人会夸大)。
  • 隐式特征:从行为中挖掘的。比如用户点击了哪些类型的照片、在哪个页面停留时间长、给哪些人点了赞。这些更真实,但需要算法去提取。
我的经验:显式特征做初筛,隐式特征做精排。两者结合,效果远好于只用一种。

4.1.2 画像构建的常用方法

嗯,这里要注意,构建画像不是简单地把字段拼起来。你需要做特征工程:

  1. 离散特征编码:比如性别、职业,用 one-hot 或 embedding。
  2. 连续特征归一化:年龄、收入,缩放到 [0,1] 区间,不然收入这个特征会主导一切。
  3. 文本特征提取:用户写的自我介绍,用 TF-IDF 或 Word2Vec 转成向量。
  4. 行为特征聚合:过去7天点击了哪些类别,用时间衰减加权。
# 一个简单的用户画像构建示例(伪代码)
def build_user_profile(user_id):
    # 显式特征
    explicit = {
        'age': normalize(user.age, 18, 60),
        'income': normalize(user.income, 0, 100000),
        'gender_onehot': one_hot(user.gender),
        'job_embedding': job_to_vec(user.job)
    }
    # 隐式特征(从行为日志中提取)
    implicit = {
        'click_category_dist': get_click_distribution(user_id, days=30),
        'avg_stay_time': get_avg_stay_time(user_id),
        'like_ratio': get_like_ratio(user_id)
    }
    return merge_features(explicit, implicit)

4.2 物品特征提取:让机器看懂「东西」

用户画像搞定了,接下来是物品。在配对场景里,物品可以是人(相亲对象)、商品(推荐商品)、文章(内容推荐)。

你想想看,一个物品有哪些特征?

  • 结构化属性:价格、品牌、类别、尺寸……这些直接拿过来用就行。
  • 非结构化内容:图片、文本、视频。这些需要深度学习模型来提取特征。

4.2.1 文本特征提取

我记得有一次做简历匹配系统,需要提取职位描述和候选人简历的文本特征。当时用了两种方法:

方法 适用场景 优点 缺点
TF-IDF 短文本、关键词匹配 简单、可解释性强 无法捕捉语义
BERT Embedding 长文本、语义匹配 理解上下文 计算量大
Word2Vec + 平均池化 中等长度文本 效率与效果平衡 忽略词序
核心原则:物品特征提取的粒度,决定了匹配的上限。你提取的是「颜色=红色」,那只能匹配喜欢红色的人。如果你提取的是「风格=热情奔放」,那就能匹配更深层的偏好。

4.2.2 图像特征提取

如果物品是图片(比如头像、商品图),我会用预训练的 CNN 模型(ResNet、EfficientNet)提取特征向量。这些向量通常有 512 或 2048 维,直接作为物品的「视觉指纹」。

# 使用预训练模型提取图像特征
from torchvision import models, transforms
import torch

model = models.resnet18(pretrained=True)
model = torch.nn.Sequential(*list(model.children())[:-1])  # 去掉分类层

def extract_image_features(img_tensor):
    with torch.no_grad():
        features = model(img_tensor)
    return features.flatten().numpy()  # 输出512维向量

4.3 基于内容的匹配算法

好,现在用户画像有了,物品特征也有了。怎么匹配?

说白了,就是计算用户画像向量和物品特征向量之间的相似度。我常用的方法有这么几种:

4.3.1 余弦相似度

最经典的方法。把用户和物品都表示成向量,计算夹角余弦值。值越接近1,说明越匹配。

def cosine_similarity(user_vec, item_vec):
    dot = sum(u * i for u, i in zip(user_vec, item_vec))
    norm_u = sum(u**2 for u in user_vec) ** 0.5
    norm_i = sum(i**2 for i in item_vec) ** 0.5
    return dot / (norm_u * norm_i + 1e-8)  # 加小常数防除零
我曾经踩过的坑:直接用原始特征算余弦相似度,结果年龄这个维度权重太大,导致推荐结果全是同龄人。后来我做了特征标准化,每个维度的贡献才均衡。

4.3.2 加权匹配

不是所有特征都同等重要。比如在婚恋场景,「价值观」的权重应该远大于「身高」。所以我会给每个特征维度加一个权重:

def weighted_match(user_profile, item_features, weights):
    score = 0
    for key in user_profile:
        sim = cosine_similarity(user_profile[key], item_features[key])
        score += weights.get(key, 1.0) * sim
    return score / sum(weights.values())

4.3.3 基于决策树的匹配规则

有时候,简单的规则比复杂模型更有效。我习惯先用决策树做一轮硬过滤:

  • 年龄差 < 5岁 → 保留
  • 收入差距 < 50% → 保留
  • 至少有一个共同爱好 → 保留
  • 否则 → 直接过滤掉

然后再对通过过滤的候选集做精细的相似度排序。这样既保证了效率,又保证了质量。

4.4 知识体系总览

下面这张图,是我自己总结的基于内容推荐配对的完整流程。你看一眼,就能把整个章节串起来:

基于内容的推荐配对流程 用户画像构建 显式特征(年龄/职业) 隐式特征(行为/偏好) 特征工程与归一化 物品特征提取 结构化属性 文本特征(TF-IDF/BERT) 图像特征(CNN) 匹配算法 余弦相似度计算 加权匹配(特征重要性) 决策树硬过滤 + 精细排序 相似度阈值控制 推荐配对结果 反馈更新画像
我的建议:实际项目中,别一上来就上深度学习。先用 TF-IDF + 余弦相似度跑通基线,看看效果。如果效果不够,再逐步升级到 BERT 或图神经网络。这样迭代成本最低。

4.5 避坑指南

最后,分享几个我亲身踩过的坑:

  • 特征维度爆炸:one-hot 编码几百个职业,结果向量稀疏得没法算相似度。后来改用 embedding,维度降到 32 维,效果反而更好。
  • 冷启动不是万能药:基于内容的方法虽然能解决冷启动,但容易陷入「信息茧房」——推荐来推荐去都是同类内容。需要配合探索策略(比如随机曝光一些新类别)。
  • 用户画像要动态更新:人的偏好会变。我见过一个系统,用户画像三个月不更新,推荐结果越来越差。后来加了实时流处理,每 6 小时更新一次画像,点击率提升了 23%。

嗯,这一章的内容就到这儿。基于内容的推荐配对,核心就是「理解用户、理解物品、做匹配」。方法不复杂,但细节决定成败。

专注资料整理