pairwise voting以及trueskill

pairwise voting以及trueskill

一、pairwise voting

1.1、定义及应用

pairwise voting是一种用于集体决策的方法,也被称为两两对比投票

  • 参与者被要求对给定的样本进行两两对比,然后选择他们认为更优的样本,或者选择两个样本的相对次序==》样本对生成
  • 然后,根据所有参与者的选择,对每个选项进行计分,最终得出一个总分来比较选项之间的相对优劣==》更新样本能力值

通过使用pairwise voting,可以避免在复杂多元的选择中出现投票过于分散的情况,而是通过逐一比较,逐步确定最佳选项。

这种方法在众多决策情境中都有应用,包括团队合作、项目选择、政治选举等。

有一些工具和库可以实现pairwise voting:

  • Tallyfy:是一个在线工具,提供了一个直观的界面,让用户可以轻松地进行两两对比并进行投票。它还提供了结果计算和报告生成的功能。
  • Decision Lab:是一个决策分析和决策支持工具,提供了一个用户友好的界面,可以创建投票任务并邀请参与者进行投票。还提供结果分析和可视化的功能。
  • Rankade:是一个用于比较和排名的平台,允许用户创建投票任务,并进行两两对比。它使用TrueSkill算法来计算最终的排名。
  • Python库 - voteit:voteit是一个Python库,专门用于实现各种投票方法,包括pairwise voting。它提供了一系列用于创建投票任务和计算结果的函数和类。
  • Rankit:Rankit是一个R包,用于进行排名和优选分析。它提供了许多排名方法,包括pairwise voting。Rankit还支持结果可视化和统计分析,以及进行不同偏好和权重的尝试。
  • 也可以尝试自己开发投票界面

1.2、pairwise voting核心模块

1.2.1、备选样本对生成

任务描述:

N N N个样本进行pairwise voting,保证每个样本至少参与 C C C次比较(可以略微超过 C C C次)

为每个参与者分配 P P P道题目(每个题目都包含两个样本,两两比较

思路:

  • 每个样本赋予初始次数 C C C,次数不为0的样本为可选样本;
  • 从可选样本中选择第1个样本,从剩余可选的样本中选择第2个样本,组成1个样本对;
    • 更新每个样本的可选次数
  • 可选样本较少时,需要特殊处理(部分样本被选择次数可能超过 C C C次);
  • 为每个参与者分配 P P P道题目(题库)
1.2.1、投票任务创建

任务描述:

前端页面展示 + 后台数据生成及结果存储

  • 开始页面:供参与者填写信息;投票页面:供参与者比较两个样本并投票;
  • 参与者填写信息后开始pairwise voting,后台从题库中拉取 P P P道题目;参与者投票之后记录样本两两比较的结果

思路:

  • 初始页面:供参与者填写信息,之后开始pairwise voting(后台记录参与者的信息并从题库中拉取 P P P道题目);
  • 投票页面:参与者比较两个样本并投票(后台记录参与者的信息、题目中的样本ID,样本的比较结果);
    在这里插入图片描述

二、trueskill算法

2.1、定义及应用

trueskill算法是一种用于评估和排名参与者技能水平的算法,基于概率论和统计学原理,通过比较参与者之间的比赛结果,推断和调整每个参与者的技能等级。最初用于计算和更新玩家在Microsoft Xbox Live游戏平台上的技能等级,也可以在体育竞技、推荐系统和排名系统等领域应用。

trueskill算法使用正态分布来表示参与者的能力水平,其中每个参与者都有一个技能值和一个对应的标准差。

  • 技能值表示参与者的真实技能,标准差表示技能估计的不确定性或波动范围;
  • 当参与者进行比赛或对决时,trueskill算法会根据比赛结果来更新每个参与者的技能值和标准差
    • 胜者会增加他们的技能值,而失败者会减少他们的技能值;
    • 算法还会根据比赛结果的不确定性来调整参与者的标准差。

trueskill算法具体细节可阅读该博文:现有的评分和排名算法_评分四等分法csdn-CSDN博客

2.2、trueskill库

在python中,可以使用trueskill库来实现trueskill算法的功能。

  • 要使用trueskill库,首先需要安装:pip install trueskill

  • 使用时在代码中导入trueskill库:import trueskill

  • 导入trueskill库后,可以创建参与者的技能对象,并使用算法来进行比赛结果的更新和技能等级的调整。

import trueskill

# 创建参与者的技能对象
player1 = trueskill.Rating()
player2 = trueskill.Rating()

# 模拟比赛结果
results = [(player1, ), (player2, )]  # player1获胜,player2失败

# 更新技能等级
new_ratings = trueskill.rate(results)

# 输出更新后的技能等级
print(new_ratings)

三、示例

3.1、生成样本对

示例描述:

300个样本进行pairwise voting,保证每个样本至少参与30次比较(可以超过30次)

为每个参与者分配100道题目(每个题目都包含两个样本,两两比较

编程思路:

  • 每个样本赋予初始次数30,次数不为0的样本为可选样本;
  • 从可选样本中选择第1个样本,从剩余可选的样本中选择第2个样本,组成1个样本对;
  • 可选样本较少时,需要特殊处理(部分样本被选择次数可能超过30次)
import random
import pprint
import pandas as pd
import numpy as np


def get_pariwise_voting_options(sample_list, pairwise_num, respondent_num):
    # 所有样本的副本
    copy = sample_list[:]
    # 每个样本剩余可以被选择的次数
    sample_selected_list = [pairwise_num] * (len(sample_list))
    # 记录完成选择的样本(暂时没有使用,考虑删除)
    sample_selected_index_list = []
    # 记录样本对
    result_list = []
    # 记录打乱顺序的样本对
    result_shuffled_list = []
    # 记录分组后的样本对
    result_grouped_list = []

    # TODO:循环停止条件可靠性待确认
    while max(sample_selected_list) > 0:
        # 随机选择一个样本
        first_sample = random.choice(sample_list)
        # 删除第一个样本(直接循环到可选次数为0)
        sample_list.remove(first_sample)
        sample_selected_index_list.append(first_sample)
        # 保存一份副本(再选出第2个样本后即时删除,避免重复选择)
        sample_list_copy = sample_list[:]

        # 循环到可选次数为0
        while sample_selected_list[first_sample] > 0:
            # 记录样本对
            pairwise = [first_sample]
            # 从剩下的样本中选择第二个样本,更新选择次数
            # 若第2个可选择(不重复)的样本均已经选择
            if len(sample_list_copy) == 0:
                # 若此时仅剩1个未完成选择的样本(此时没有可选择的第2个样本)
                if len(sample_list) == 0:
                    # 从原始样本副本中删除第一个样本,从剩下的样本中随机选择第2个样本
                    copy.remove(first_sample)
                    second_sample = random.choice(copy)
                # 从未完成选择的样本中选择第2个样本
                else:
                    second_sample = random.choice(sample_list)
                pairwise.append(second_sample)
                # 更新可选次数
                sample_selected_list[first_sample] -= 1
                sample_selected_list[second_sample] -= 1
                result_list.append(pairwise)
            else:
                second_sample = random.choice(sample_list_copy)
                if sample_selected_list[second_sample] > 0:
                    pairwise.append(second_sample)
                    # 更新可选次数
                    sample_selected_list[first_sample] -= 1
                    sample_selected_list[second_sample] -= 1

                    result_list.append(pairwise)
                # 避免重复选择,从副本中删除已经选择的样本
                sample_list_copy.remove(second_sample)
    print(result_list)

    # 获取打乱顺序的样本对
    result_shuffled_index_list = list(range(len(result_list)))
    random.shuffle(result_shuffled_index_list)
    for result_shuffled_index in result_shuffled_index_list:
        result_shuffled_list.append(result_list[result_shuffled_index])
    print(result_shuffled_list)

    file_name = 'demo.txt'
    file_obj = open(file_name, 'a')

    # 样本对分组,每组respondent_num个
    for i in range(0, len(result_shuffled_list), respondent_num):
        if len(result_shuffled_list[i:i + respondent_num]) <= respondent_num:
            break
        for count in range(respondent_num):
            # 保存组号、第1个样本序号、第2个样本序号
            file_obj.write(str(i // respondent_num + 1) + '\t')
            file_obj.write(str(result_shuffled_list[i + count][0]) + '\t')
            file_obj.write(str(result_shuffled_list[i + count][1]) + '\t\n')
        result_grouped_list.append(result_shuffled_list[i:i + respondent_num])
    file_obj.close()

    return result_grouped_list


if __name__ == '__main__':
    # 样本数
    sample_num = 300
    # 每个样本的出现次数
    pairwise_num = 30
    # 每个受访者接收的题目数
    respondent_num = 100

    # 从0开始到300
    sample_list = list(range(sample_num + 1))

    result_grouped_list = get_pariwise_voting_options(sample_list, pairwise_num, respondent_num)
    # pprint.pprint(result_grouped_list)

样本对记录如下:

样本对组号样本1ID样本2ID
124910
1206281
4570153

3.2、更新样本能力值

示例描述:

每个样本初始能力值为25,根据pairwise voting(两两比较)的结果更新各个样本的能力值

pairwise voting的结果表格(safety.csv)如下:

样本对组号样本1ID样本2IDwinlosegenderage
12491024910male25
1206281281206male25
457015315370male25
import numpy as np
import pandas as pd
import trueskill
from trueskill import Rating, rate_1vs1

if __name__ == '__main__':
    path = r"C:\Users\20458\Desktop\\"
    file_name = r"safety.csv"
    data = pd.read_csv(path + file_name)

    # 获取所有图片的序号(用于比较的对象)
    merged_arr = np.concatenate((data['win'].values, data['lose'].values))
    pic_list = list(set(merged_arr))

    # 为每个图片创建对象
    rating_list = [0] * (max(pic_list) + 1)
    for i in pic_list:
        rating_list[i] = trueskill.Rating()

    # 遍历表格中的每行(图片两两比较的结果),更新图片的“能力值”,初始值为25
    for i in data.index.values:
        pic_win = data.loc[i, 'win']
        pic_lose = data.loc[i, 'lose']
        try:
            new_win, new_lose = rate_1vs1(rating_list[pic_win], rating_list[pic_lose])
            rating_list[pic_win] = new_win
            rating_list[pic_lose] = new_lose
        except IndexError:
            print(i + 2, data.loc[i, 'No.'], pic_win, pic_lose)

    # 保存结果
    file_name = file_name.split('.')[0] + '.txt'
    file_obj = open(file_name, 'a')
    for pic_index, rating in enumerate(rating_list):
        if isinstance(rating, Rating):
            print(pic_index, rating.mu)
            file_obj.write(str(pic_index) + '\t')
            file_obj.write(str(rating.mu) + '\t\n')
    file_obj.close()

各个样本的最终能力值记录如下:

样本ID样本能力值
028.218
125.572
30021.683

四、相关链接

trueskill算法相关:现有的评分和排名算法_评分四等分法csdn-CSDN博客

trueskill库api:https://github.com/sublee/trueskill

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值