Dempster在1967年的文献《多值映射导致的上下文概率》中提出上、下概率的概念,并在一系列关于上下概率的文献中进行了拓展和应用,其后又在文献《贝叶斯推理的一般化》中进一步探讨了不满足可加性的概率问题以及统计推理的一般化问题。
Shafer在Dempster研究的基础上提出了证据理论,把Dempster合成规则推广到更为一般的情况,并与1976年出版《证据的数学理论》,这一著作的出版标志着证据理论真正的诞生,为了纪念两位学者对证据理论所做的贡献,人们把证据理论称为Dempster-Shafer证据理论(D-S证据理论)。
自从证据理论诞生以来,在将近四十年的发展中,很多学者对证据理论进行了较为广泛的研究,也取得了丰硕的理论研究结果,D-S证据理论的应用性得到了广泛的证实。
证据理论自1976年美国学者G.Shafer发表了著作《证据的数学理论》以来,在理论上取得了很大的发展,在应用上也取得了丰富的成果。
证据理论在多分类器融合、不确定性推理、专家意见综合、多准则决策、模式识别、综合诊断等领域中都得到了较好的应用。
证据理论基于人们对客观世界的认识,根据人们掌握的证据和知识,对不确定性事件给出不确定性度量。这样做使得不确定性度量更切近人们的习惯,易于使用。
证据理论对论证合成给出了系统的合成公式,使多个证据合成后得到的基本可信数依然满足证据基本可信数的性质。
Dempster合成公式具有结合律与交换律,使其有利于实现计算和选择合成效果好的、信息质量高的信息源。
目前传感器采集到的数据都是带有噪声的,比如:
LiDAR采集的点云的内部噪声:
点云外部噪声:
Radar采集中的噪声:
相机采集的图片中的噪声:
一个典型的问题是,现在自动驾驶车在行驶过程中,LiDAR检测到了某个位置有个障碍物,Radar没有检测到,图像没有检测到,那么在这多种传感器的数据源下,我们需要进行一个决策,该位置有没有障碍物,最好可以给出这种决策的不确定性区间是多少。
暂无
前三种概率满足可加性,即:
根据可加性可知,如果我们相信一个命题为真的程度为S,那么我们必须以 1-S 的程度去相信该命题的反。但是在很多情况下,这是不合理的,例如:”地球以外存在着生命“这一命题,其反命题是:”地球以内不存在生命“。
在实现世界的大部分时候,证据与证据之间都是存在着交叠与冲突的,并不是完美的.i.i.d.关系。
Daldey的不可能性定理指出,在两个基本假设下,不存在一种集结个体概率估计的数学公式满足概率定理。这两个假设如下:
也就是说,如果群体概率估计是个体概率估计的函数,则群体概率估计不满足概率定律,这意味着基于概率的集结模型在Daldey不可能性定理的条件下都将失效。
在证据理论中,证据指的是人们分析命题求其基本可信数所依据的事物的属性与客观环境(实证据),还包括人们的经验、知识和对该问题所做的观察和研究。
人们通过对证据的分析得出命题的基本可信数分配函数:m(A)。
笔者注:
D-S理论中的证据概念,是从概率论中随机变量的概念中推广而来的,也可以理解为机器学特征工程中的特征向量。
对一个判决问题,设人们所能认识到的可能结果用集合Θ表示,那么人们所关心的任一命题都对应于Θ的一个子集。Θ被称为识别框架。
识别框架依赖于人们的认知水平,框架中的元素由人们”已经知道“或”想知道“的知识决定。
笔者注:
识别框架是一个更泛化的理论概念,在经典概率理论中被称为样本空间。它代表了具体问题可能的预测结果集合。
设Θ为识别框架,A为识别框架下的一个命题,如果集函数 m:2Θ → [0,1] 满足:
则称 m 为框架Θ上的基本可信度分配。m(A) 被称为 A 的基本可信数。
基本可信数反映了对 A 本身(而不去管它的任何真子集与前因后果)的信度大小。
命题本质上就是一组证据的集合。
笔者注:
命题表达了我们对待认识的目标对象(识别框架)的一种潜在推测,每一个命题都是识别框架的一个子集,对应一个对现实问题的抽象表征。
基本可信度分配是概率论中完备性的一个泛化推广。基本可信数累加和为1,代表着所有命题共同组合在一起,构成了完整的识别框架。
需要注意的是,和随机事件一样,命题本身是一个集合的概念(离散情况下),所以命题可以有子命题,对命题的可信度分配,同样也有子集的概念。
设Θ为识别框架,集函数 m:2Θ → [0,1] 为框架Θ上的基本可信度分配,则称由:
所定义的函数 Bel:2Θ → [0,1] 为Θ上的信度函数。信度函数表达了对每个命题的信度(考虑前因后果)。
笔者注:
可以看到,信度函数是一系列可信度分配的累计合成结果,这和概率论中随机变量是单个离散随机事件(离散概率)的累计的概念是一致的。
设Θ为识别框架,集函数 Bel:2Θ → [0,1] 是信度函数,当且仅当它满足:
,,n为任意自然数
则有下式:
上式为半可加性应满足的条件,满足可加性则一定满足半可加性。
前面说过,贝叶斯信度函数是一种特殊的信度函数,它的所有焦元都是单元素的,所以贝叶斯信度函数是满足完全可加性的。
一批证据对一个命题提供支持的话,那么它也应该对该命题的推论提供同样的支持(互信息原理)。所以, 对一个命题的信度应该等于证据对它的所有前提本身提供的支持度之和。
如果 m(A) > 0,则称 A 为信度函数 Bel 的焦元
本质上说,焦元就是信度函数非零的证据集合,或者说焦元就是非零信度分配命题。
几种特殊的信度函数如下:
称 m(A) 为绝对信度函数,其中 A* ∈ Θ。
对一个信度函数而言,如果 m(Θ) = 1,我们将其成为空信度函数。
如果一个信度函数的焦元都为单元素集,我们将其成为 Bayesian信度函数,Bayesian信度函数即为概率函数。
笔者注:
贝叶斯理论最核心的假设就是元素(证据)之间是.i.i.d.独立同分布假设,这个假设在实际问题中很多时候是不成立的。 证据合成理论最大的理论贡献就是在可信度分配的新理论框架下,将证据之间的冲突性问题纳入了考虑和计算范围。
设函数 Q:2Θ → [0,1],且满足下式:
则 Q 被称为众信度函数。众信度函数 Q(A) 反映了包含A的集合的所有基本可信数之和。
笔者注:
信度函数 Bel 是从一个结论的前提这个角度来描述信度的,它计算的结论的前提(也就是证据)的累计合并结果;而相比不同的是,众信度函数 Q 是从一个前提的结论这个角度来描述信度的。
设 Bel:2Θ → [0,1] 为Θ上的信度函数,Q 是它的众信度函数,那么,
该定理说明,Bel 与 Q 可以互相定义,众信度函数从另一个侧面反映了信度。
Dempster合成法则是证据理论的核心,是一个反映证据的联合 作用的法则,该法则可对不同证据源的证据进行合成。
设函数 pl:2Θ → [0,1],且满足下式:
我们称 pl 为似真度函数(plausibility function)。pl(A) 称为 A 的似真度,表示我们不怀疑 A 的程度。pl(A) 包含了所有与 A 相容的那些集合(命题)的基本可信度。pl(A) 是比 Bel(A) 更宽松的一种估计。
设 Bel1 和 Bel2 是同一识别框架Θ上的两个信度函数,m1 和 m2 分别是其对应的基本信度分配,焦元分别为 A1,A2,….,Ak 和 B1,B2,…,Bl,设:
则由下式定义的函数 m:2Θ → [0,1] 是基本信度分配:
并称此为两个信度函数合成的Dempster法则。
由此得到的合成后的基本信度分配m被称为 m1 和 m2 的直和。记为。所对应的信度函数也被称为 Bel1 和 Bel2 的直和,记为。
(1 - K)-1 称为归一化因子,它的引入实际上是为了避免证据组合时将非零的信度赋给空集,把空集所丢弃的信度分配按比例地补到非空集上,K表示证据间的冲突程度,其值越大说明证据之间的冲突越大。
当 K=1 时,则 m1 和 m2 完全冲突,直和或不存在。
设 Bel1,Bel2,…,Beln 是同一识别框架Θ上的信度函数,m1,m2,….,mn 是对应的基本信度分配,如果存在且脚本可信度分配为m,且,则有:
,其中
上式即多个信度函数合成的Dempster法则。
上面公式可以这么理解,命题的合成结果A,等于所有参与合并的命题中,相交于 A 的那些命题的的mass函数值的乘积的和,再除以一个归一化系数 1-K。归一化系数 1-K 中的 K 的含义是证据之间的冲突(the conflict between the evidences, called conflict probability)。
对于归一化系数K来说:
笔者注:
从信度函数合成的Dempster法则公式可以看出,Dempster合成公式是在贝叶斯概率分解公式的基础上,增加了对证据间冲突性的考虑,对彼此存在冲突的证据合成起到归一化的作用。
当组合多个证据时,可通过该公式将证据两两组合。但是该合成法则强调多种证据的协调性,抛弃所有冲突的证据。用上式合成高度冲突的证据时,合成的结果常有悖常理,在极限情况下则无法组合证据。
交换律:,证据合成并不依赖于合成的顺序。
结合律:,证据合成次序不影响合成结果。
同一性:存在唯一的幺元m0,对所有的基本信度分配m,有,其中m0是空基本信度分配,即 m0(Θ) = 1,m0(其他) = 0,该性质在现实世界中的意义是某些专家不表态(弃权)时不影响最终结果。
极化性:,其中表示一种”大于“或”放大“,表示意见相同的地位专家合成的效果是”支持的假设更支持,否定的假设更否定“,向两级增幅地发展。
证据聚焦性:当两个证据都均以较高的信度支持识别框架中的某一命题时,合成后该命题将具有更高的信度。
[Bel(A),pl(A)]:表示命题A的信任区间,
例如(0.25,0.85),表示A为真有0.25的信任度,A为假有0.15的信任度,A不确定度为0.6。
一般来说,对任何一个证据处理人员来讲,他所想到的各种有用的观念、判断,是非常多的,某一框架只能包含其中很少的一部分,所以用单个识别框架来处理问题是很不够的。这种情况下证据处理人员为了进行某种特殊的似真推理,就有必要求助于不同的观念,侧重于不同的特性相应地转换他所使用的识别框架。
粗化与细化就是为适应这个要求而采用的两种转换方法。值得说明的是,框架的转化不是随意的,尽管转化前后两者要强调不同的特性、不同的侧面,而且在其关注的方向上可以具有不同程度的判决,但是两者决不能使用相互矛盾的、互不相容的概念。
细化与粗化就是两种相容的变换,而收缩与扩张就不是相容变换。
设Θ是识别框架,δ是Θ的一些子集所构成的集合,满足:
则δ被称为Θ的一个划分。
δ1和δ2都是Θ的划分,对都存在使,则称δ1是δ2的加细(细分),δ2是δ1的加粗(粗化)。记为,意味着δ2的每一个元素都是δ1的一些元素的并。
对于Θ的任意两个划分δ1和δ2,可以定义另一个
仍是Θ的一个划分,且有:
若有另一个划分δ3,满足:
则有:
由以上定理可知,我们所定义的划分既是δ1的加细,又是δ2的加细,而且在既是δ1的加细,又是δ2的加细划分中,又是最粗的,所以称是δ1和δ2最粗的公共加细。读者朋友通过画一个简单的韦恩图,基于集合论的知识就很容易理解这个性质了。
在Θ的所有划分中,最粗的划分是{Θ},即只包含一个元素即Θ本身的一个集合;最细的划分是{{θ} θ∈Θ},即Θ的单点子集的集合。
对框架的细分来讲,是否存在一个极细化的精细框架(ultimate refinement),Shafer理论和贝叶斯理论有着不同的看法:
所以在Shafer理论中,有如下的两个框架相容的概念:
定理1:
设Θ1和Θ2为两个识别框架,若这两框架具有一个公共的精细化框架,则称框架Θ1和Θ2是相容的。
显然,根据这个定理,在两个框架中,当其中一个是另一个的精细时,这两个框架必相容。
定理2:
如果在一个框架Θ上加上一个新假设,则Θ的元素必定要减少,设Θ1是由Θ加上一个新假设而得到的识别框架,则Θ1作为可能事件或结果的一个集合,将是可能结果Θ的一个子集。
此时,我们称Θ1是Θ的一个收缩,Θ是Θ1的一个扩张。
笔者注:
识别框架的收缩与扩张,其本质就是概率论中假设空间放缩的一个推广,在贝叶斯理论中,通过引入先验知识,可以显著降低潜在的假设搜索空间,从而让概率评估结果更加符合人们对目标对象的事实依据。
Relevant Link:
https://www.hindawi.com/journals/js/2016/1903792/
这一章节,我们通过几个具体的例子,来具体看下,D-S证据融合理论与算法是如何对现实世界的问题进行建模与分析的。
我们先来假设这样一个情景,有一个传感器探测一个很远处的一道光,这道光只能发出 {red,yellow,green} 这三种中的一种光。
在这里,{red,yellow,green} 分别代表了不同的证据。
传感器对所探测的光做出分析,形成了一些观察,从不同的传感器得到的证据统计,这道光可能是下列一些单元素假设:
以及这些假设相应的可能性(也就是说基本分配函数Mass)。
Hypothesis
Mass
Belief
Plausibility
Null
0
0
0
Red
0.35
0.35
0.56
Yellow
0.25
0.25
0.45
Green
0.15
0.15
0.34
Red or Yellow
0.06
0.66
0.85
Red or Green
0.05
0.55
0.75
Yellow or Green
0.04
0.44
0.65
Any
0.1
1.0
1.0
首先,我们可以定义识别框架:
Θ = {Red,Yellow,Green} = {Null,Red,Yellow,Green,Red or Yellow,Red or Green,Yellow or Green,Any}
DS证据理论针对识别框架中的每一个假设都分配了概率,我们称为基本概率分配(BPA, Basic Probability Assignment)或者是基本置信分配(BBA, Basic Belief Assignment)。这个分配函数我们称为mass函数。
因此我们有集函数 m:2Θ → [Red,Yellow,Green]。
这里要注意几点关于mass函数的性质:
在我们上面的例子中,Column 2 即为mass函数针对各个假设的值,根据该列我们可以得到下面的定义。
接下来我们根据mass函数来计算每一个命题(命题是单元素假设的集合)信度函数(Belief function)以及似真度函数(Plausibility function)。
所谓命题A的信度函数,是指所有真属于A的假设的概率分配累加和,即:∑m(B),B∈A。
以上面的例子来讲,
接下来看似真度函数,对于命题A,它的似真度函数为所有与A相交不为空的命题B的mass值的和。
以上面的例子来讲, 如果A命题为:Red,那么 pl(A) = m(Red) + m(Red or Yellow) + m(Red or Green) + m(Any) = 0.35 + 0.06 + 0.05 + 0.1 = 0.56
一宗谋杀案有三个犯罪嫌疑人:U = {Peter, Paul, Mary}。两个目击证人(W1,W2)分别指证犯罪嫌疑人,得到两个mass函数 m1 和 m2。
该问题抽象为识别框架为:Θ = {Peter,Paul,Mary},基本概率分配函数为:m{Peter},m{Paul},m{Mary}。
根据上述公式,为了求得合成规则 m12,我们先求归一化系数 1-K 值,注意,对于该例子来说,罪犯只能有一个,所以 Peter、Paul、Mary彼此交集都为空:
然后再求合成之后的每个假设的mass函数值
Peter的组合mass函数值:
Paul的组合mass函数值:
Mary的组合mass函数值:
由此,我们得到了如上表所示的组合函数 m12。
根据得到的Dempster合成的mass函数,我们同样能计算对于组合mass函数对于各个命题的信度函数以及似然函数。
但是这一结果却有悖于我们的常识,因为在两个目击证人指证的证据中,Paul是凶手的概率都不大,但是最终的结果却直接指向了Paul。该例子就是“Zadeh悖论”。
更极端的情况如果W1中,m{peter)=1,W2中m{Mary}=1,则归一化因子K=0,D-S组合规则无法进行。
若修改“Zadeh悖论”中的部分数据,即允许目击者指认多个犯罪嫌疑人,如下表:
我们来重新计算一下新的组合mass函数。
还是先计算归一化系数 1-K,
也可以反过来,用相交为空的公式来算:
计算Peter的组合mass函数:
计算Paul的组合mass函数:
计算Mary的组合mass函数:
计算 {Peter, Paul, Mary} 的组合mass函数:
根据这次的结果,我们同样可以计算组合函数对每个命题的信度函数值以及似然函数值:
以自动驾驶障碍物检测数据融合为例。
首先确定假设空间,对于自动驾驶场景来说,某一个时刻,对一个具体空间的情况,会有一些命题假设:
第二步确定基本分配概率,即确定每个证据源对于命题空间中每种命题假设发生的概率分布。
比如LiDAR作为证据源之一,在某个时刻对某个位置各个假设的分配概率为:
分别用可信数来表示如下:
而图像感知器作为另一个证据源之一,在某个时刻对某个位置各个假设的分配概率为:
分别用可信数来表示如下:
m(A)image = 0.1,m(B)image = 0.8,m(C)image = 0.1,m(C)image = 0
LiDAR和Image证据,对各个命题假设的置信度分配矩阵如下:
第三步是计算归一化常数(1 - K)(K是冲突因子,表示两个集合的冲突性)。
1 - K = 0.51
第三步计算每个命题假设的联合mass:
第四步计算每个命题假设的信度函数(Bel)和似真概率(pl):
命题假设
mL
mI
m{L,I}
bel
pl
A
0.6
0.1
0.275
0.275
0.295
B
0.3
0.8
0.706
0.706
0.726
C
0.1
0.1
0.020
1
1
D
0
0
0
0
0
由于证据理论中的证据模糊主要来自于各子集的模糊度。根据信息论的观点,子集中元素的个数越多,子集的模糊度越大。换句话说,证据并不是越多越好,抛开证据间的相关性和证据之间彼此存在的冲突性不说,过多的证据还会稀释对最终合成结果的置信度。
Relevant Link:
https://blog.csdn.net/zfcjhdq/article/details/51566875
https://www.cnblogs.com/mmgf/p/8025261.html
https://blog.csdn.net/wsyhawl/article/details/78261554
https://www.ixueshu.com/document/889d1a6efed6edd8933d7c8c81103cda318947a18e7f9386.html
https://blog.csdn.net/wsyhawl/article/details/78261554
https://blog.csdn.net/zfcjhdq/article/details/51566875
https://www.cnblogs.com/mmgf/p/8025261.html
https://blog.csdn.net/zfcjhdq/article/details/51566875
https://blog.csdn.net/am45337908/article/details/48832947
https://zhuanlan.zhihu.com/p/99517591
http://dy.163.com/v2/article/detail/ELQG2DUV05311NDJ.html
在前面的章节中,我们讨论了基本的证据融合方法以及对命题假设的似真度估计等问题。
这个章节开始,我们继续深入现实世界的复杂场景中,来讨论一下我们如何基于证据融合进行未来决策。
在决策分析中,系统的未来状态在一般情况下难以精确地给出。一般专家根据其掌握的知识与经验对系统状态进行分析,给出系统未来状态的主观估计。
当利用专家群体对系统未来状态进行估计时,就需要对专家群体的预测意见进行合成。
设决策集为:
D = {d1,d2,….,dm}
在不同的系统状态下,选择不同的决策,所获得的报酬(收益)是不同的。第 i 个系统状态,第 j 个决策方案对应的报酬函数为:
r(di,xj),(i = 1,….,m;j = 1,….,n)
在已知上述条件下,如何进行决策,是基于证据理论进行决策要解决的问题。
其过程可以表示如下:
利用似真度函数计算公式:
求出单点似真度:
令:
则 p(x1),…,p(xn) 即可作为各种状态出现的主观概率。
选择与期望报酬最大值对应的决策方案为最佳方案,即:
使用这种决策方案在应用时,应该注意它的假设前提:
它认为似真度越大,其主观概率就越大。但是一般情况下,点函数 pl({xi}) 不能表示基本可信数 m 或信度函数 Bel 所包含的所有信息,点函数 pl({xi}) 越大,m({xi}) 与 Bel({xi}) 不一定越大。
该方法的思想是先求出各焦元的报酬函数,然后用基本可信数作为主观概率,求出各决策方案的期望报酬作为决策依据。
其过程可以表示如下,令:
则有:
选择与期望报酬最大值对应的决策方案为最佳方案。
使用这种决策方案在应用时,应该注意它的假设前提:
该方法计算对应于焦元的报酬函数,该计算公式是对焦元所包含的状态因素的报酬数值求简单平均值。这也就意味着该方法认为每个焦元内包含的各状态因素其出现的概率是相同的,而一般情况下,各状态出现的概率是不相等的。
上一个小节末我们说到,由于上述两个假设的存在,使上述两种决策方法存在着局限性。要科学地解决该问题,就是要解决如何在已知各焦元的基本可信度分配的条件下,求解系统各状态的发生概率或基本可信数。
因此有学者提出了,“基于焦元分析求解各状态的基本可信数的决策方法”,对群体专家意见合成得到的基本可信数的焦元进行分析,可分为两种情况来处理,
系统各状态的基本可信数作为分析计算各状态发生概率的依据。此时 m(Θ) 可能不等于零。m(Θ) 表示对证据的怀疑。
当对系统各状态的基本可信数充分信任时,可用下式定义各状态发生概率:
当对系统各状态的基本可信数有怀疑时,则可将 m(Θ) 均分给各个状态,即可用下式定义各状态发生概率:
用逐步求精的方法对基本可信数的焦元进行分析求解构成焦元的状态因素的基本可信数。
首先,对由单个状态因素构成的焦元确定该单个状态因素的基本可信数就是与该焦元对应的基本可信数。
其次,对焦元中含有已知基本可信数的单个状态因素的焦元,可将已知基本可信数的单个状态因素及其基本可信数中提出,这样可以缩小等待进一步分配的焦元所含构成状态因素的数量,并可能缩小未知基本可信数的单个状态因素的数量。
最好,对还不能确定基本可信数的单个状态因素,就要引入新的信息进行判断与求解。
例如,已知:
则可首先确定:
其次可确定:
与
接下来,对 m({x3}) 和 m({x4}) 就要引入新的信息来确定,可向专家咨询:“已知 m({x3, x4}) = 0.2,m({x3}), m({x4})应为多少?”,专家可根据自己的知识,做出进一步的判断。将专家的意见进行合成后,就可得到 m({x3}) 和 m({x4}) 的数值。
当求解出系统各状态的基本可信数 m({xi}) 后,就可用第一种情况下的方法求解各状态发生概率。
求解出各系统状态的发生概率后,计算各个决策方案的期望报酬,选择出最优决策方案,其计算公式如下:
可进一步把证据理论的合成结果转化为规则,在合成结果中发生概率最大的某种状态,就是将要发生的结果。
经过转化后,历史样本就可以看做决策表,可返回历史样本对各个合成规则的不确定性进行分析与评价。
"""
Shows different use cases of the library.
"""
from __future__ import print_function
from pyds import MassFunction
from itertools import product
print('=== creating mass functions ===')
m1 = MassFunction({'ab':0.6, 'bc':0.3, 'a':0.1, 'ad':0.0}) # using a dictionary
print('m_1 =', m1)
m2 = MassFunction([({'a', 'b', 'c'}, 0.2), ({'a', 'c'}, 0.5), ({'c'}, 0.3)]) # using a list of tuples
print('m_2 =', m2)
m3 = MassFunction()
m3['bc'] = 0.8
m3[{}] = 0.2
print('m_3 =', m3, ('(unnormalized mass function)'))
#print('\n=== belief, plausibility, and commonality ===')
#print('bel_1({a, b}) =', m1.bel({'a', 'b'}))
#print('pl_1({a, b}) =', m1.pl({'a', 'b'}))
#print('q_1({a, b}) =', m1.q({'a', 'b'}))
#print('bel_1 =', m1.bel()) # entire belief function
#print('bel_3 =', m3.bel())
#print('m_3 from bel_3 =', MassFunction.from_bel(m3.bel())) # construct a mass function from a belief function
#print('\n=== frame of discernment, focal sets, and core ===')
#print('frame of discernment of m_1 =', m1.frame())
#print('focal sets of m_1 =', m1.focal())
#print('core of m_1 =', m1.core())
#print('combined core of m_1 and m_3 =', m1.core(m3))
#print('\n=== Dempster\'s combination rule, unnormalized conjunctive combination (exact and approximate) ===')
#print('Dempster\'s combination rule for m_1 and m_2 =', m1 & m2)
#print('Dempster\'s combination rule for m_1 and m_2 (Monte-Carlo, importance sampling) =', m1.combine_conjunctive(m2, sample_count=, importance_sampling=True))
#print('Dempster\'s combination rule for m_1, m_2, and m_3 =', m1.combine_conjunctive(m2, m3))
#print('unnormalized conjunctive combination of m_1 and m_2 =', m1.combine_conjunctive(m2, normalization=False))
#print('unnormalized conjunctive combination of m_1 and m_2 (Monte-Carlo) =', m1.combine_conjunctive(m2, normalization=False, sample_count=))
#print('unnormalized conjunctive combination of m_1, m_2, and m_3 =', m1.combine_conjunctive([m2, m3], normalization=False))
#print('\n=== normalized and unnormalized conditioning ===')
#print('normalized conditioning of m_1 with {a, b} =', m1.condition({'a', 'b'}))
#print('unnormalized conditioning of m_1 with {b, c} =', m1.condition({'b', 'c'}, normalization=False))
#print('\n=== disjunctive combination rule (exact and approximate) ===')
#print('disjunctive combination of m_1 and m_2 =', m1 | m2)
#print('disjunctive combination of m_1 and m_2 (Monte-Carlo) =', m1.combine_disjunctive(m2, sample_count=))
#print('disjunctive combination of m_1, m_2, and m_3 =', m1.combine_disjunctive([m2, m3]))
#print('\n=== weight of conflict ===')
#print('weight of conflict between m_1 and m_2 =', m1.conflict(m2))
#print('weight of conflict between m_1 and m_2 (Monte-Carlo) =', m1.conflict(m2, sample_count=))
#print('weight of conflict between m_1, m_2, and m_3 =', m1.conflict([m2, m3]))
#print('\n=== pignistic transformation ===')
#print('pignistic transformation of m_1 =', m1.pignistic())
#print('pignistic transformation of m_2 =', m2.pignistic())
#print('pignistic transformation of m_3 =', m3.pignistic())
print('\n=== local conflict uncertainty measure ===')
print('local conflict of m_1 =', m1.local_conflict())
print('entropy of the pignistic transformation of m_3 =', m3.pignistic().local_conflict())
#print('\n=== sampling ===')
#print('random samples drawn from m_1 =', m1.sample(, quantization=False))
#print('sample frequencies of m_1 =', m1.sample(, quantization=False, as_dict=True))
#print('quantization of m_1 =', m1.sample(, as_dict=True))
#print('\n=== map: vacuous extension and projection ===')
#extended = m1.map(lambda h: product(h, {, }))
#print('vacuous extension of m_1 to {1, 2} =', extended)
#projected = extended.map(lambda h: (t[] for t in h))
#print('project m_1 back to its original frame =', projected)
#print('\n=== construct belief from data ===')
#hist = {'a':, 'b':, 'c':}
#print('histogram:', hist)
#print('maximum likelihood:', MassFunction.from_samples(hist, 'bayesian', s=))
#print('Laplace smoothing:', MassFunction.from_samples(hist, 'bayesian', s=))
#print('IDM:', MassFunction.from_samples(hist, 'idm'))
#print('MaxBel:', MassFunction.from_samples(hist, 'maxbel'))
#print('MCD:', MassFunction.from_samples(hist, 'mcd'))
这个代码示例涵盖了D-S证据理论主要的定理公式,读者朋友可以通过打开注释以及debug单步调试,帮助我们更好地从代码层面理解抽象的数学公式。
Relevant Link:
https://github.com/reineking/pyds
# -*- coding: utf- -*-
"""
This script implement algorithm from
THESIS "Automatic Detection of Click Fraud in Online Advertisements" AGARWAL
Calculate the belief of fraud
http://repositories.tdl.org/ttu-ir/bitstream/handle/2346/46429/AGARWAL-THESIS.pdf
Import library using pyds (https://github.com/reineking/pyds)
Require: Python3, numpy, scipy, redis (https://github.com/andymccurdy/redis-py)
Author: TrucLK
"""
from pyds import MassFunction
from itertools import product
import redis
import sys
import pprint
import time
################################################
class Config(object):
"""
Redis for store click history
"""
redis_host = '127.0.0.1'
redis_port =
redis_db =
# Time for redis key expire
time\_to\_expire =
# Visitor length of checking
visit\_length =
# weight is an empirically derived value that signifies the strength of the evidence in supporting the user is fraud
# caution changing it will change the maximum value of result.
IDWeight = 0.5
UAWeight = 0.4
IPWeight = 0.4
class EcLogger(object):
"""
This is object relate to log
"""
def \_\_init\_\_(self):
self.ro = redis.Redis(host=Config.redis\_host, port=Config.redis\_port, db=Config.redis\_db)
def record(self, hit):
"""
Add click to history for checking
"""
clickid = self.ro.incr('ec:clicknum')
self.ro.zadd('click:ip:' + hit.ip, clickid, int(hit.time))
self.ro.expire('click:ip:' + hit.ip, Config.time\_to\_expire)
self.ro.zadd('click:cookie:' + hit.cookie, clickid, int(hit.time))
self.ro.expire('click:cookie:' + hit.cookie, Config.time\_to\_expire)
self.ro.zadd('click:config:' + hit.pubid + ':' + hit.config, clickid, int(hit.time))
self.ro.expire('click:config:' + hit.pubid + ':' + hit.config, Config.time\_to\_expire)
pass
def getClickNumFromIp(self, hit):
"""
Get click time from this IP address in this session
"""
count =
count = self.ro.zcount('click:ip:' + hit.ip, int(hit.time) - Config.visit\_length, int(hit.time))
if (count == ):
count =
return count
def getClickNumFromCookie(self, hit):
"""
Get click time from this anonymous id in this session
"""
count = self.ro.zcount('click:cookie:' + hit.cookie, int(hit.time) - Config.visit\_length, int(hit.time))
if (count == ):
count =
return count
def getClickNumFromConfig(self, hit):
"""
Get cliock time from this user agent in this session
"""
count = self.ro.zcount('click:config:' + hit.pubid + ':' + hit.config, int(hit.time) - Config.visit\_length,
int(hit.time))
if (count == ):
count =
return count
class Hit(object):
"""
It's a simple container.
"""
def \_\_init\_\_(self, \*\*kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
super(Hit, self).\_\_init\_\_()
class ClickProcessingUnit(object):
"""
Mass functions for Click Fraud Detection
"""
def \_\_init\_\_(self, hit, ecLogger):
self.hit = hit
self.ecLogger = ecLogger
class ClickProcessingUnitIp(ClickProcessingUnit):
"""
Evidence number of clicks on the ad by ip address
Create mass function for ip address checking
"""
def process(self):
numberclick = self.ecLogger.getClickNumFromIp(self.hit)
coefficient\_value = Config.IPWeight
a = coefficient\_value \* ( - / numberclick)
b =
ab = - a
massfunction = MassFunction({'a': a, 'b': b, 'ab': ab})
return massfunction;
class ClickProcessingUnitCookie(ClickProcessingUnit):
"""
Evidence number of clicks on the ad by user ID
Create mass function for id cookie checking
"""
def process(self):
numberclick = self.ecLogger.getClickNumFromCookie(self.hit)
coefficient\_value = Config.IDWeight
a = coefficient\_value \* ( - / numberclick)
b =
ab = - a
massfunction = MassFunction({'a': a, 'b': b, 'ab': ab})
return massfunction;
class ClickProcessingUnitConfig(ClickProcessingUnit):
"""
Evidence number of clicks on the ad by user agent
Create mass function for user agent checking
"""
def process(self):
numberclick = self.ecLogger.getClickNumFromConfig(self.hit)
coefficient\_value = Config.UAWeight
a = coefficient\_value \* ( - / numberclick)
b =
ab = - a
massfunction = MassFunction({'a': a, 'b': b, 'ab': ab})
return massfunction
class ClickProcessing(object):
"""
Main click processing function
"""
def \_\_init\_\_(self, hit, ecLogger):
self.hit = hit
self.ecLogger = ecLogger
def process(self):
processingList = self.getListOfProcessing()
m = None
# Loop for list of processing class
for processing in processingList:
# Init first mass function
if not m:
m = processing.process()
else:
# Dempster's rule of combination create a new mass function
m = m & processing.process()
return m
def getListOfProcessing(self):
"""
Config list of processing
"""
dict1 = \[\]
dict1.append(ClickProcessingUnitIp(self.hit, self.ecLogger))
dict1.append(ClickProcessingUnitCookie(self.hit, self.ecLogger))
dict1.append(ClickProcessingUnitConfig(self.hit, self.ecLogger))
return dict1
if __name__ == '__main__':
"""
Add click from command line argument
"""
try:
hit = hit = Hit(
ip=sys.argv[],
time=sys.argv[],
config=sys.argv[],
cookie=sys.argv[],
pubid=sys.argv[],
)
ecLogger = EcLogger()
processing = ClickProcessing(hit, ecLogger)
ecLogger.record(hit)
m = processing.process()
print(m.bel('a'))
sys.exit()
except Exception:
print(0.01)
sys.exit()
读者朋友在阅读这段代码的时候,要重点理解信度分配函数部分,
对信度函数的分配,涉及到我们如何对待解决问题进行抽象建模的过程,可以将其简单理解为一种特征工程。
Relevant Link:
https://github.com/afterlastangel/simple_ads_clickfraud_detection/blob/master/clickprocess.py
https://ttu-ir.tdl.org/bitstream/handle/2346/46429/AGARWAL-THESIS.pdf
# -*- coding: utf- -*-
import os
import time
import numpy as np
import scipy.io as sio
import sklearn
def test_real(path):
real1=[]
for i in range(grid_num):
for j in range(grid_test_sample):
real1.append(i+)
real=np.array(real1)
return real
def read_data(path):
pathtrain = os.path.join(path,'train.mat')
traindataset = sio.loadmat(pathtrain).get('data') # 训练数据
pathtest = os.path.join(path,'test.mat')
testdataset = sio.loadmat(pathtest).get('X\_test') # 测试数据
trainlabelset1 = \[\]
for grid in range(grid\_num):
for sample in range(grid\_train\_sample):
local\_tmp = grid +
trainlabelset1.append(local\_tmp)
trainlabelset = np.array(trainlabelset1) # 训练数据集对应坐标
testlabelset = test\_real(path) # 测试数据集对应坐标
return traindataset, trainlabelset, testdataset, testlabelset
def knn_classifier(train_x, train_y):
from sklearn.neighbors import KNeighborsClassifier
model = KNeighborsClassifier()
model.fit(train_x, train_y)
return model
def logistic_regression_classifier(train_x, train_y):
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(penalty='l2')
model.fit(train_x, train_y)
return model
def random_forest_classifier(train_x, train_y):
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier(n_estimators=)
model.fit(train_x, train_y)
return model
def decision_tree_classifier(train_x, train_y):
from sklearn import tree
model = tree.DecisionTreeClassifier()
model.fit(train_x, train_y)
return model
def gradient_boosting_classifier(train_x, train_y):
from sklearn.ensemble import GradientBoostingClassifier
model = GradientBoostingClassifier(n_estimators=)
model.fit(train_x, train_y)
return model
def svm_classifier(train_x, train_y):
from sklearn.svm import SVC
model = SVC(kernel='rbf', probability=True)
model.fit(train_x, train_y)
return model
def svm_cross_validation(train_x, train_y):
from sklearn.grid_search import GridSearchCV
from sklearn.svm import SVC
model = SVC(kernel='rbf', probability=True)
param_grid = {'C': [1e-, 1e-, 1e-, , , , ], 'gamma': [0.001, 0.0001]}
grid_search = GridSearchCV(model, param_grid, n_jobs = , verbose=)
grid_search.fit(train_x, train_y)
best_parameters = grid_search.best_estimator_.get_params()
for para, val in best_parameters.items():
print para, val
model = SVC(kernel='rbf', C=best_parameters['C'], gamma=best_parameters['gamma'], probability=True)
model.fit(train_x, train_y)
return model
def func_rmse(predict_label,real):
pathlocal=os.path.join(path,'dist1.mat')
local=sio.loadmat(pathlocal).get('dist')#56个格点坐标
tmp = predict_label-np.ones(np.shape(predict_label))
predict_2d = local[map(int,tmp)]
tmp_real = real-np.ones(np.shape(predict_label))
real_2d = local[map(int,tmp_real)]
norm_sqrt = (np.linalg.norm(predict_2d-real_2d,axis=-))**
rmse = np.sqrt(sum(norm_sqrt)/np.shape(predict_label))
return rmse
def get_probability(data_chuang):
"""
对窗数据来说,维度为:len_chuang*算法个数
计算每种算法下格点序号出现次数及概率
"""
mygrid = range(,)
cishu_total=[]
for sf in range(len_sf):
cishu=[]
for item in mygrid:
cishu_sf = data_chuang[:,sf].tolist().count(item)
cishu.append(cishu_sf)
cishu = np.array(cishu)
cishu_total.append(cishu)
probio_total = [item/float(len_chuang) for item in cishu_total]
probio_total = np.array(probio_total).transpose()
return probio_total
if __name__ == '__main__':
path = os.path.abspath('.')
thresh = 0.5
model_save_file = None
model_save = {}
grid_num =
grid_train_sample =
grid_test_sample =
test\_classifiers = \['KNN', 'LR', 'RF', 'DT', 'SVM'\]
classifiers = {
'KNN': knn\_classifier,
'LR': logistic\_regression\_classifier,
'RF': random\_forest\_classifier,
'DT': decision\_tree\_classifier,
'SVM': svm\_classifier,
'SVMCV': svm\_cross\_validation,
'GBDT': gradient\_boosting\_classifier
}
print 'reading training and testing data...'
train\_x, train\_y, test\_x, test\_y = read\_data(path)
# print "test\_x: ", test\_x
# print "test\_y: ", test\_y
num\_train, num\_feat = train\_x.shape
num\_test, num\_feat = test\_x.shape
is\_binary\_class = (len(np.unique(train\_y)) == )
print '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* Data Info \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*'
print '#training data: %d, #testing\_data: %d, dimension: %d' % (num\_train, num\_test, num\_feat)
predict\_total2 = \[\]
rmse\_total = \[\]
for classifier in test\_classifiers:
print '\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\* %s \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*' % classifier
start\_time = time.time()
model = classifiers\[classifier\](train\_x, train\_y)
print 'training took %fs!' % (time.time() - start\_time)
predict = model.predict(test\_x)
if model\_save\_file != None:
model\_save\[classifier\] = model
if is\_binary\_class:
tmp = func\_rmse(predict, test\_y)
rmse\_total.append(tmp)
accuracy = sklearn.metrics.accuracy\_score(test\_y, predict)
print 'accuracy: %.2f%%' % ( \* accuracy)
predict\_total2.append(predict)
predict\_total1 = np.array(predict\_total2)
predict\_total = predict\_total1.transpose()
print "predict\_total: ", predict\_total
"""
对已经被机器学习算法进行预测之后的数据矩阵predict\_total(维度为:测试样本数\*算法数)
滑窗对数据集进行处理,窗长度为len\_chuang,每次下滑一格
对窗长度内的数据进行概率计算并DS融合
"""
len\_chuang = # 窗数据长度
pre\_ds = \[\]
pre\_dsjq = \[\]
pre\_max = \[\]
pre\_test = \[\]
for start in range(, - len\_chuang + ): # 滑动窗口
data\_chuang = predict\_total\[start:len\_chuang+start, :\] # 取出窗长度的数据
len\_sf = len(data\_chuang\[\]) # 多算法个数
probio\_total = get\_probability(data\_chuang) # 窗数据矩阵中每种算法对应所有格点出现概率
"""
.普通ds
等同于求多种算法概率平均之后,概率最大的格点为所预测格点
"""
init\_mean = np.mean(probio\_total,) # 每个格点下多种算法提供的平均概率
init\_ds = init\_mean
# 这部分并没有起到实际作用
for i in range(len\_sf-):
init\_ds = np.multiply(init\_ds,init\_mean)
k = sum(init\_ds)
bel = init\_ds/float(k)
pre\_grid = np.argmax(bel)+
pre\_ds.append(pre\_grid)
"""
.加强ds
求得多种算法的源可信度,得到估计
"""
m\_between = np.zeros(\[len\_sf,len\_sf\])
for i in range(len\_sf):
for j in range(len\_sf):
m\_between\[i, j\]=sum(np.multiply(probio\_total\[:,i\],probio\_total\[:,j\]))
# m\_between两个证据的内积=两个证据中,每个出现事件的概率乘积\*(出现事件的交集/并集)
# 对单个事件而不是集合事件而言,等同于对应事件的概率乘积之和
d = np.zeros(\[len\_sf,len\_sf\])
sim = np.zeros(\[len\_sf,len\_sf\])
for i in range(len\_sf):
for j in range(len\_sf):
d\[i,j\]=np.sqrt(0.5\*(m\_between\[i,i\]+m\_between\[j,j\]-\*m\_between\[i,j\]))
# d为两个证据间的距离,距离越小表示两个证据提出的意见越一致
sim\[i,j\]=-d\[i,j\]
# sim为两个证据之间的相似度,越大代表两个证据之间的一致性越强
sup = np.zeros(len\_sf)
for i in range(len\_sf):
sup\[i\]=sum(sim\[i,:\])-sim\[i,i\]
# sup为对每个证据的支持度,为两个证据之间的相似度之和减去该证据自己对自己的支持度
# 证据对自己的支持度为1
crd = np.zeros(len\_sf)
for i in range(len\_sf):
crd\[i\]=float(sup\[i\])/sum(sup)
# crd为证据的可信度
# 即为归一化的支持度,其他证据对该证据支持度越高,则可信度越高
A = np.zeros(grid\_num)
for i in range(grid\_num):
A\[i\] = sum(np.multiply(probio\_total\[i,:\],crd))
# 将可信度作为源权重,估计所有情况下数据出现的概率
AA = A
# 这部分并没有起到实际作用
# 对于所有元素均为0-1的概率值,进行元素对于相乘之后并不改变元素的大小排序
for i in range(len\_sf-):
init\_ds = np.multiply(AA,A)
# 分子为与某事件有交集的事件概率之乘积
k = sum(init\_ds)
# 分母K=∑(所有有交集的事件的概率乘积)
# 或者为1-∑(所有不相交的时间概率乘积)
# 对全部都是单事件而不是集体事件而言,有交集的事件即为事件其本身
# K表示了证据的冲突程度,冲突越大,越接近0,一致性越大,越接近1
bel = init\_ds/float(k)
pre\_grid = np.argmax(bel)+
pre\_dsjq.append(pre\_grid)
# 下面两行代码是为了验证多次融合A矩阵并不能起到结果
testgrid = np.where(A == np.max(A))\[\]\[\]
pre\_test.append(testgrid+)
"""
.使用窗数据中出现次数最多的作为正确结果
"""
data\_chuang\_list = data\_chuang.flatten().tolist()
matrix\_cishu = max(data\_chuang\_list,key=data\_chuang\_list.count)
pre\_max.append(matrix\_cishu)
truth\_chuang = test\_y\[len\_chuang-:\]
rmse\_ds = func\_rmse(pre\_ds, truth\_chuang)
rmse\_max = func\_rmse(pre\_max, truth\_chuang)
rmse\_dsjq = func\_rmse(pre\_dsjq, truth\_chuang)
rmse\_test = func\_rmse(pre\_test, truth\_chuang)
print "pre\_ds: ", pre\_ds
print "rmse\_ds: ", rmse\_ds
accuracy = sklearn.metrics.accuracy\_score(test\_y\[len\_chuang-:\], pre\_ds)
print 'accuracy: %.2f%%' % ( \* accuracy)
基于D-S证据融合算法,对包括KNN、LR、RF、DT、SVM在内的5种算法的概率预测结果(每一个算法的预测结果相当于一个命题假设),进行融合,将融合得到的概率结果作为最终的概率预测结果。
可以看到,融合后的概率预测结果,总体上优于单个算法的预测结果,仅略逊于LR算法,同时规避了LR的过拟合问题。
Relevant Link:
https://github.com/Keats13/dempster_shafer_multi_rss
Relevant Link:
https://github.com/aus10powell/fuzzy_dempster_shafer
Relevant Link:
https://github.com/Moldoteck/Dempster-Shafer
Relevant Link:
https://github.com/bpietropaoli/THEGAME-Python
https://github.com/amineremache/dempster-shafer/blob/master/dempster_shafer.py
https://github.com/redaneqrouz/dempster-Shafer/blob/master/dams.py
https://github.com/leokan92/dempster-shafer/blob/master/Dempstershafercomb.py
https://github.com/Zhiming-Huang/Dempster-shafer-combination-rules/blob/master/DS.py
笔者在进行相关理论学习和编码开发的过程中,隐约中感觉到D-S理论和Bayesian理论之间存在比较明显的区别,似乎D-S是从贝叶斯理论的基础上发展而来的。但是又不能完全理解其中的脉络,这里权且放一些粗浅的理论,留待将来解决。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章