【问题标题】:Random Walk on Bipartite Graph with Gremlin使用 Gremlin 在二分图上随机游走
【发布时间】:2014-09-07 02:59:49
【问题描述】:

我想在 groovy 中使用 gremlin 在有向二部图上随机游走,根据给定的用户偏好(用户喜欢的项目)对项目进行排名。

该图具有以下基本结构:

[User1] ---'likes'---> [ItemA] [ItemB]

以下是我想出的查询:

def runRankQuery(def userVertex) {
    def m = [:]
    def c = 0
    while (c < 1000) {
        userVertex
            .out('likes')   // get all liked items of current or similar user
            .shuffle[0]     // select randomly one liked item
            .groupCount(m)  // update counts for selected item
            .in('likes')    // get all users who also liked item
            .shuffle[0]     // select randomly one user that liked item
            .loop(5){Math.random() < 0.5}   // follow liked edge of new user (feed new user in loop) 
                                            // OR abort query (restart from original user, outer loop)      
            .iterate()
        c++
    }
    m = m.sort {a, b -> b.value <=> a.value}
    println "intermediate result $m"
    m.keySet().removeAll(userVertex.out('likes').toList())
    // EDIT (makes no sense - remove): m.each{k,v -> m[k] = v / m.values().sum()}
    // EDIT (makes no sense - remove): m.sort {-it.value }
    return m.keySet() as List;
}

但是,此代码没有找到新项目(上面示例中的 [ItemB]),而是找到给定用户喜欢的项目(例如 [ItemA])。

  • 我需要改变什么来提供一个新用户(例如 [User2]),让循环步骤返回到“out('likes')”步骤以便继续步行?

  • 此代码运行后,是否可以将其视为“个性化 PageRank”的实现?


这里是运行示例的代码:

g = new TinkerGraph()

user1 = g.addVertex()
user1.name ='User1'
user2 = g.addVertex()
user2.name ='User2'
itemA = g.addVertex()
itemA.name ='ItemA'
itemB = g.addVertex()
itemB.name ='ItemB'

g.addEdge(user1, itemA, 'likes')
g.addEdge(user2, itemA, 'likes')
g.addEdge(user2, itemB, 'likes')

println runRankQuery(user1)

还有输出:

intermediate result [v[2]:1000]
[]
==>null
gremlin> g.v(2).name
==>ItemA
gremlin> 

【问题讨论】:

    标签: groovy gremlin bipartite graph-traversal random-walk


    【解决方案1】:

    我发现这是一个非常奇怪的问题。我发现了几个很难解释的非常奇怪的问题,最后,我不确定它们为什么会这样。对我来说很奇怪的两件大事是:

    1. 我不确定shuffle 步骤是否有问题。在您的情况下,它似乎没有正确随机化。我似乎无法在此案例之外重现问题,因此我不确定它是否与您的数据大小或其他原因有关。
    2. 我在使用Math.random() 跳出循环时遇到了奇怪的问题。

    无论如何,我想我已经在这里抓住了你代码的精髓,我所做的更改似乎可以满足你的需求:

    runRankQuery = { userVertex ->
        def m = [:]
        def c = 0
        def rand = new java.util.Random()
        while (c < 1000) {
            def max = rand.nextInt(10) + 1
            userVertex._().as('x')
                .out('likes')   
                .gather.transform{it[rand.nextInt(it.size())]}
                .groupCount(m) 
                .in('likes')    
                .gather.transform{it[rand.nextInt(it.size())]}
                .loop('x'){it.loops < max}  
                .iterate()
            c++
        }
        println "intermediate result $m"
        m.keySet().removeAll(userVertex.out('likes').toList())
        m.each{k,v -> m[k] = v / m.values().sum()}
        m.sort {-it.value }
        return m.keySet() as List;
    }
    

    我通过从收集的列表中随机选择一个顶点,将shuffle 替换为我自己品牌的“shuffle”。我还随机选择了一个max 循环而不是依赖Math.random()。当我现在运行它时,我想我得到了你正在寻找的结果:

    gremlin> runRankQuery(user1)                                       
    intermediate result [v[2]:1787, v[3]:326]
    ==>v[3]
    gremlin> runRankQuery(user1)
    intermediate result [v[2]:1848, v[3]:330]
    ==>v[3]
    gremlin> runRankQuery(user1)
    intermediate result [v[2]:1899, v[3]:339]
    ==>v[3]
    gremlin> runRankQuery(user1)
    intermediate result [v[2]:1852, v[3]:360]
    ==>v[3]
    

    您可能还可以让Math.random() 工作,因为它在我使用它的一些迭代中表现得可以预见。

    【讨论】:

    • 非常感谢斯蒂芬您的解决方案。它做我想做的事。但是,您的更改给我留下了很多其他问题:1) 为什么我们需要._()? 2) 那么shuffle 的问题是需要在某个地方跟踪的错误吗? 3) 为什么gather后面没有scatter
    • 4) 如果我将 loop 命令更改为 .loop('x'){println "gremlinLoopCount: ${it.loops} / $max"; it.loops &lt; max} 我看到循环计数总是以 3 (!) 开头,然后增加 +2。我什至看到像gremlinLoopCount: 3 / 0 这样的输出。这怎么可能? 5)所以如果我们从可变步行长度(Math.random() &lt; 0.5)更改为固定步数(it.loops &lt; max),该算法是否仍然可以被视为具有传送/重启的随机游走?我不这么认为。
    • 我只是使用_() 来标记要循环回的步骤,并使用x。我不认为你可以在顶点处使用as。我无法重现shuffle 问题,所以我不确定这是一个错误。如果您有更简单案例的重现步骤,则可以在 Pipes 中创建问题。我从管道中随机选择一项,所以我在transform 中展开了Listloop 是广度优先的,所以你不能指望println it.loop 按顺序打印任何东西。它可能看起来像两部分递增,但事实并非如此。
    • 另外请注意,从技术上讲,计数从“2”开始——我猜你可能认为它是“减一”。这是在 TinkerPop3 中修复的。从技术上讲,这意味着我们应该看到总是从 2 开始的输出,我不得不承认我目前没有看到 - 需要进行更多研究。
    • 我认为“0”是max 的有效值,因为 rand 应该给你一个介于 0 和 9 之间的值(10 是唯一的)。编辑我的答案以添加 1。不确定使用这种方法是否会破坏随机游走的经典概念。这取决于您:) 您可能会尝试再次使用rand var 以跳出循环。我只知道我似乎有问题,我没有进一步探索。也许我的问题与shuffle有关。
    猜你喜欢
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 2014-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 1970-01-01
    相关资源
    最近更新 更多