【问题标题】:How to recommend the next achievement [closed]如何推荐下一个成就[关闭]
【发布时间】:2010-11-08 01:58:52
【问题描述】:

短版:

我有一个与 StackOverflow 类似的设置。用户获得成就。我的成就比 SO 多得多,可以说大约 10k,每个用户都有 100 多个成就。现在,您将如何推荐(推荐)用户尝试的下一个成就?

长版:

这些对象在 django 中是这样建模的(仅显示重要部分):

class User(models.Model):
    alias = models.ForeignKey(Alias)

class Alias(models.Model):
    achievements = models.ManyToManyField('Achievement', through='Achiever')

class Achievement(models.Model):
    points = models.IntegerField()

class Achiever(models.Model):
    achievement = models.ForeignKey(Achievement)
    alias = models.ForeignKey(Alias)
    count = models.IntegerField(default=1)

我的算法只是找到与登录用户共享成就的所有其他用户,然后查看他们的所有成就并按出现次数排序:

def recommended(request) :
    user = request.user.get_profile()

    // The final response
    r = {}

    // Get all the achievements the user's aliases have received 
    // in a set so they aren't double counted
    achievements = set()
    for alias in user.alias_set.select_related('achievements').all() :
        achievements.update(alias.achievements.all())

    // Find all other aliases that have gotten at least one of the same
    // same achievements as the user
    otherAliases = set()
    for ach in achievements :
        otherAliases.update(ach.alias_set.all())

    // Find other achievements the other users have gotten in addition to
    // the shared ones.
    // And count the number of times each achievement appears
    for otherAlias in otherAliases :
        for otherAch in otherAlias.achievements.all() :
            r[otherAch] = r.get(otherAch, 0) + 1

    // Remove all the achievements that the user has already gotten
    for ach in achievements :
        r.pop(ach)

    // Sort by number of times the achievements have been received
    r = sorted(r.items(), lambda x, y: cmp(x[1], y[1]), reverse=True)

    // Put in the template for showing on the screen
    template_values = {}
    template_values['achievements'] = r

但它需要 FOREVER 才能运行,并且总是返回整个列表,这是不需要的。用户只需要前几项成就即可。

所以,欢迎我就其他算法和/或代码改进提出建议。我会在我的系统中为您提供推荐算法的成就:)

【问题讨论】:

  • 您能否在代码中添加一些 cmets 来解释每个 for 循环的作用?抱歉对 django 不太熟悉,所以无法判断您的算法是什么。
  • 我不明白您如何编码每个成就的条件以及用户已经满足的条件,这肯定与您的目标相关-您能解释一下吗?
  • 当然。添加 cmets。感谢您的帮助

标签: python django optimization achievements


【解决方案1】:

您可以推荐获得哪些成就的一种方法是查看有多少用户已经拥有这些成就并推荐那些受欢迎的成就。当他们达到这些目标时,您会在列表中列出并推荐稍微不那么受欢迎的目标。然而,这有一个天真的假设,即每个人都想追求受欢迎的成就。它可能会导致热门成就更受欢迎和更不受欢迎,嗯......令人欣慰的是,这不会占用太多资源,并且可能会运行得非常快。 (只需保留成就列表 + 实现次数)

另一种方法(尝试根据用户已有的成就猜测用户可能追求的成就)是使用一些机器学习算法。我认为k-nearest neighbor algorithm 在这里会表现得很好。选择一个阈值并仅输出高于此阈值的所有内容。现在,我不知道这是否会比您已经拥有的运行得更快,但是您应该在每次用户取得新成就时运行推荐引擎一次,存储前五个(假设)并输出它在需要推荐时返回给用户。

我希望这会有所帮助。 =)

【讨论】:

  • 第一个对我来说有点过于简单,因为有许多类型的用户获得不同的成就。关于“快速”(非暴力)kNN python库或代码的任何建议?我发现的只是一个未答复的 usenet 帖子coding.derkeiler.com/Archive/Python/comp.lang.python/2007-08/…
  • 如何通过拥有不同的一般用户组(并为每种类型保留一个列表)来改进第一种方法?就像在 MMORPG 中一样,他们的成就取决于这个人是探险家、权力玩家(power-leveler?)、社交人等等……你可以根据一些成就猜出他们属于哪个一般类别他们已经有了。不幸的是,除了最简单的情况外,kNN 不会很快。但是,我认为它会给出很好的建议,您可以使用它来帮助为第一种方法创建列表。抱歉,我还不知道 Python。
  • 这似乎类似于离线集群别名,然后显示他们最近集群的成就,然后按照接近的顺序沿着集群走。也许我会看看我是否可以编写代码以及它的速度。
  • 我猜你担心在生成推荐时会降低服务器速度。只是一个想法,如何在另一台机器上执行此任务(例如备份实时数据库的旧笔记本电脑)然后让它将这些数据上传到您的服务器数据库中?您可以设置一个 cron 使其每 24 小时运行一次。这样,您的 Web 服务器的性能相对不受影响,并且您的列表可以相对更新。祝你好运! =)
【解决方案2】:

我建议您将前三个步骤(成就、其他别名、计数)作为一个 SQL 语句执行。就像现在一样,您在 Python 中发出大量查询并汇总数千行,这是您应该委托给数据库的任务。例如代码

for otherAlias in otherAliases : #For every single other user
    for otherAch in otherAlias.achievements.all() : #execute a query
        r[otherAch] = r.get(otherAch, 0) + 1

执行数千个大型查询。

相反,您可以使用 SQL 来执行此操作,方法是根据 Alias id 不同和成就 id 相同来加入成就者本身。然后,您按成就 ID 分组并进行计数。

在下面的查询中,表“B”是其他用户的成就,“Achiever”是我们的成就。如果有任何其他用户分享了一项成就,他们会在“B”中为他们分享的每项成就出现一次。然后我们按 alias_id 对它们进行分组,并计算它们出现的次数,这样你就可以得到一个不错的 id,把表数出来。

非常粗略的代码(这里没有可用的 SQL)

SELECT B.Alias_id, COUNT(B.achievement_id) 
  FROM Achiever, Achiever as B 
  WHERE Achiever.achievement_id == B.achievement_id 
     AND Achiever.Alias_id == <insert current user alias here>;
  GROUP BY B.Alias_id

如果按照我的想法进行,您将获得一张其他用户别名的表格,以及他们与当前用户分享的成就数量。

接下来要做的是使用上面的 SQL 语句作为“内部选择”——称之为用户。您将其与当前用户的成就表和成就表连接起来。您可能希望忽略与当前用户相似的前 10 名用户以外的所有用户。

我现在没有时间编写一个好的查询,但请查看您的数据库的 JOIN 语句,该语句在指定的 10 个用户和当前用户之间连接成就 ID - 如果没有,则将该 ID 设置为 NULL不存在。过滤器只筛选出现 NULL 的行(未实现的成就)。

【讨论】:

  • 是的,如果我在 PHP 中使用 SQL 连接器进行编码,我就是这样做的。有没有在 django 对象模型中执行此操作的好方法,还是应该回退到 SQL?
  • 我会按照docs.djangoproject.com/en/dev/topics/db/sql 中的说明执行原始 SQL,然后将其封装在 User 或 Alias 中的方法中 - 此代码对 Python(或任何 Web 层)的性能至关重要因为它非常适合数据库。
猜你喜欢
  • 2014-01-21
  • 1970-01-01
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
  • 2011-01-25
  • 2010-10-05
  • 2010-09-25
相关资源
最近更新 更多