层次分析法AHP-Analytic Hierarchy Process操作流程及代码实现
“养鱼这种事情,请各位渣男渣女自重…”—猴可夫斯基
前言
坚持做一个”技术“搬运工,总结互相学习成长。项目中遇到一个给出评估分的需求,一开始会想到分数用类似评分卡模型(logit),或者分类模型用于给评定级别。但是这两种都属于监督学习范畴,我们遇到的情况是压根也没有监督值,甚至需要我们去创造出一个监督值,于是在美丽的PMO小姐姐的建议下,我们决定使用AHP层次分析法来创造一个分数,并在最终结果中加以修改调整成预期结果。
层次分析法AHP(Analytic Hierarchy Process)的选择
AHP层次分析法是一种定性、定量相结合的系统化、层次化的分析方法。与传统的统计学模型不同,它其实是基于专家经验主观的一种权重决策的方法。
举个例子,加入我们想要决定一个人的违约概率,我们可以选用评分卡模型,跑Logit。或者分类模型去预测和描述一种关系或规则。然而,这种监督学习的机器学习方法,需要我们有足够大量的历史真实数据进行训练。我们在现实工作中,连一个Y值都没有,甚至是需要创造一个Y值。而当业务实质具有强经验导向时,我们不妨采用AHP层次分析法,对专家给予个模块指标的评分作为我们计算权重的依据,并进行相关测试和校验。
AHP的优点:
- 不依赖历史客观数据。
- 强化专家经验,以最简便的代价得到相对比较符合规则的指标权重或评分。
AHP的缺点:
- 依赖专家主观经验,是一种模糊的笼统的计算方法,当询问的专家经验不足时,会造成偏差,却又无法验证。
- 当指标过多时,会在实际操作中引起专家的方案导致收集到的评价有偏。
- 过于关注矩阵一致性检验,而不是合理性的探究。
操作流程梳理及控制
- 构建层次体系,分为:总目标,目标层,方案层(指标)。
- 收集清洗相关指标,并制作相关调查问卷或访谈计划。
- 选择调查访谈的target人群,一般为专家或经验丰富的相关经验接触者。
- 针对指标让target人群进行评价。
- 制作矩阵,并计算验证,求取矩阵特征值。
- 得到最终结论,并加以修正。
计算逻辑梳理
step1: 构建层次结构模型
作为一名资深的鱼塘养殖户,张翠花小姐精心从她所养的鱼塘中挑选了以上四位候选者,王聪聪、刘冬冬、吴祖祖和李二狗。但是她很纠结,这三位备胎到底选择哪一位转正。于是翠花女士与其闺蜜进行了一次头脑风暴茶话会,罗列出了以下几个备选指标,作为她考量选哪位备胎转正的量化依据。但是,由于翠华女士养鱼多年,在各大指标中已经迷失了自我,无法得到令自己信服的选择。
于是乎,翠花女士像30位资深鱼塘养殖户发送了针对以上指标调查问卷,希望他们给出大家心中最好的评判标准。
step2: 构造互反矩阵
翠花女士按照层次结构模型,从上到下逐层构造判断矩阵。每一层元素都以相邻上一层次各元素为准则,按1-9标度方法两两比较构造判断矩阵。换而言之,让养鱼专家们对这一坨指标进行两两打分。
打分标准如下:
根据养鱼专家们的经验,参考上述评分标准,我们得到了以下矩阵:
step3: 一致性检验
所谓一致性检验,简单讲就是,两两指标的协调性,我们认为a比不重要,b比c重要,那么一致性便是a比c也要重要。定义与公式如下:
当然,我们可以通过求出最大特征根及其对应的特征向量,并将特征向量进行归一化。
将判断矩阵(正反阵)A的所有列向量归一化,后将每个行向量求和,并进行归一化处理得列向量W(权向量/特征向量)。
最大特征根为:
进行一致性检验:
CI(一致性指标)=(最大特征根-维度)/(维度-1)
其值与一致性程度成反比。
RI(随机一致性指标,由维度判断)
初始化RI值,用于一致性检验:
RI_list = [0, 0, 0.58, 0.90, 1.12, 1.24, 1.32, 1.41, 1.45]
计算后的CI值,如果<0.1,则视为通过一致性检验,否则需要调整矩阵参数。
代码实现
python计算逻辑,首先指标比较少的情况,不需要调用tensorflow。此外,如果有多层指标情况出现,需要多层次迭代计算权重与最大特征值。
import numpy as np
import pandas as pd
class AHP:
# 传入的np.ndarray是的判断矩阵
def __init__(self, dataframe, result=None):
# 哪种方法
self.re = result
# 传入矩阵
self.df = dataframe
if self.re == 'v1':
# 排序形成的字典 # 结果展示
self.m = self.createdict()
self.array = self.matrix1()
# 记录矩阵大小
self.n = self.matrix1().shape[0]
elif self.re =='v2':
self.array = self.matrix2()
# 记录矩阵大小
self.n = len(self.df.index)
# 初始化RI值,用于一致性检验
RI_list = [0, 0, 0.58, 0.90, 1.12, 1.24, 1.32, 1.41, 1.45]
self.RI = RI_list[self.n - 1]
# 转换矩阵用于计算
def matrix1(self):
name = []
for key in self.m:
name.append(key)
values = []
for item in name:
values.append(self.m[item])
mylist = []
for i in range(len(values)):
alist = []
for j in range(len(values)):
temp = values[i]/values[j]
alist.append(temp)
mylist.append(alist)
array = np.array(mylist)
return array
# 生成输入条件
def createdict(self):
my_dict = {}
print('标度1:同样重要;标度3:稍微重要;标度5:明显重要;标度7:强烈重要;标度9:极端重要;标度2、4、6、8:临界重要性中值')
for item in data.columns.to_list()[1:]:
print('===============================')
print('请输入'+str(item)+'的排序重要性!')
my_dict[item] = float(input())
print('===============================')
print('请输入想要的计算结果:评估分or评估等级')
result = str(input())
return my_dict
# 转换矩阵用于计算
def matrix2(self):
n = len(self.df.index)
alist = []
for i in range(n):
temp = list(data.T[i])[1:]
alist.append(temp)
return alist
# 获取最大特征值和对应的特征向量
def get_eig(self):
# numpy.linalg.eig() 计算矩阵特征值与特征向量
eig_val, eig_vector = np.linalg.eig(self.array)
# 获取最大特征值
max_val = np.max(eig_val)
max_val = round(max_val.real, 6)
# 通过位置来确定最大特征值对应的特征向量
index = np.argmax(eig_val)
max_vector = eig_vector[:, index]
max_vector = max_vector.real.round(6)
# 添加最大特征值属性
global m
maxval = max_val
# 计算权重向量W
weight_vector = max_vector / sum(max_vector)
weight_vector = weight_vector.round(6)
# 打印结果
print("最大的特征值: " + str(max_val))
print("对应的特征向量为: " + str(max_vector))
print("归一化后得到权重向量: " + str(weight_vector))
return weight_vector, maxval
# 测试一致性
def changjiajun(self):
# 计算CI值
if self.re == 'v1' or self.re =='v2':
maxval = self.get_eig()[1]
CI = (maxval - self.n) / (self.n - 1)
CI = round(CI, 6)
# 打印结果
print("判断矩阵的CI值为" + str(CI))
print("判断矩阵的RI值为" + str(self.RI))
# 分类讨论
if self.n == 2:
print("仅包含两个子因素,不存在一致性问题")
else:
# 计算CR值
CR = CI / self.RI
CR = round(CR, 6)
# CR < 0.10才能通过检验
if CR < 0.10:
print("判断矩阵的CR值为" + str(CR) + ",通过一致性检验")
vector = self.get_eig()[0]
return True
else:
print("判断矩阵的CR值为" + str(CR) + ",未通过一致性检验")
return False
else:
print('请输入正确的版本v1、v2!')
"""
input
"""
if __name__=='__main__':
data = pd.read_excel('养鱼专业户.xlsx')
# V1的方法为对所有中间指标排序 V2是直接导入无反矩阵(两两比较的值)@常老师,她清楚
AHP(data,result='v1').changjiajun()
注意!
这份代码是之前项目使用过的正路版本,v1参数是当专家乱写经验指标评分或者是种无法通过一致性检验时使用。v2参数便是两两标度的原始版本。
结果展示
翠花的结果:
不难看出,在闺蜜的眼中,爱我、人品、健康的权重很高。而家境和资产是嘴不重要的指标。
因此,根据四个备胎的具体指标进行权重加总,翠花应当选择李二狗同学。
…
…
然而,最终翠花选择了王聪聪。
课后思考,这印证了AHP归于强调矩阵一致性,而忽略了合理性探究。基于专家经验的结果,并不能指导最终目标的选择。