先来一段西瓜书里面的介绍:
在“无监督学习”中,训练样本的标记信息是未知的,目标是通过对无标记训练样本的学习来揭示数据的内在性质及规律,为进一步的数据分析提供基础,此类学习任务中研究最多、应用最广的是“聚类”(clustering)
总结一下关键词:标记信息未知、学习、内在性质及规律、聚类。
对,还有一个:无监督学习
无监督算法大多都可以用上面的关键词来描述,就拿今天的聚类算法来说,我们的目的是对一批未知标记的数据通过某种方式进行聚类,使其能够有效的分成若干个类别,每一个类别里面的数据特征都类似,不同类别的数据差异性较大。
举个简单的例子:在中国的乡村有这样一个现象,一个村子的姓氏大多相同,不同村子有不同的姓氏。那如果现在把王家村、李家村、赵家村的所有人都聚集在一起,前提是不知道他们是哪个村子的,如何对他们进行聚类?
上面的姓氏、性别、年龄、价值和属性等都是村民的内在性质(人物特征),这样的内在性质有很多,也就决定了聚类的标准并不唯一。
ok,想必大家已经明白了什么是聚类,通过上面的例子我们总结一下
聚类:将数据集中的样本划分为若干个不相交的子集,每个子集内部的样本之间具有相同的性质,不同子集之间差异性较大。
通常情况下,我们会将子集称之为“簇”(cluster)
聚类的本质是将具有相似特征的样本划分在一个簇里面,根据聚类算法的不同,聚类的实现过程也不尽相同。
例如,聚类算法中k-means是基于均值的聚类,DBSCAN是基于密度的聚类,AGNES是基于层次的聚类,可以针对不同的样本集使用不同算法进行聚类。
聚类性能的评估比较麻烦,主要有两个原因:
针对上面的问题,可以大致分为两种,一种是存在已经确定的标记类别数据(类似于分类数据集),一种是完全没有标记的类别数据。
先来看有标记类别数据的评估:
当前的样本数据有标记类别数据C1和预测后的标记类别数据C2,但是无法直观的通过C1、C2去计算聚类错误率。
这个时候,可以通过条件熵去分析,条件熵在决策树中详细介绍过:《大话机器学习算法》决策树—看这一篇就够了
通过条件熵的,可以认识到两个指标,分别是齐次性和完整性。
其中齐次性表示一个聚类元素只由一种类别的元素组成,完整性则表示给定的已标记的类别 ,全部分配到一个聚类里。
通过这两个指标可以评估带有类别标记样本的聚类性能
再来看没有标记的类别数据的评估:
大多应用于聚类算法的数据都是无标记的,因为既然数据都有标记了,直接用分类算法不香吗?
对对对,所以无标记的数据聚类后如何评估?
有一个指标叫做轮廓系数,它可以在不需要已标记数据集的前提下,对聚类算法的性能进行评估。
轮廓算法由以下两个指标构成:
则针对这个样本,其轮廓系数s的值为:
s = b − a m a x ( a , b ) s = \frac{b-a}{max(a,b)} s=max(a,b)b−a
针对一个数据集,其轮过系数s 为其所有样本的轮廓系数的平均值。轮廓系数的数值介于[-1,1]之间,-1表示完全错误的聚类,1表示完美的聚类,0表示聚类重叠。
EM的英文全称是:Expectation Maximization,所以EM算法也叫最大期望算法。
学习EM之前,希望你已经理解了什么是极大似然估计,不了解的点这个:《两日算法系列》之第二篇:贝叶斯分类
先说一下极大似然估计:已知某个随机样本满足某种概率分布,且某个参数能使这个样本出现的概率最大,我们把这个参数作为估计的真实值叫做最大似然估计。也就是求解出现样本结果的最佳参数θ。
所以极大似然估计需要面临的概率分布只能有一个,但是要是不止一个呢?
看个例子:
假设现在有两枚硬币A和B,随机抛掷后正面朝上概率分别为P_A,P_B。
为了估计这两个概率,需要每次取一枚硬币,连掷10下并记录下结果,结果如下:
实验 |
投掷的硬币 |
正面次数 |
---|---|---|
1 | A | 5 |
2 | B | 7 |
3 | B | 8 |
4 | B | 9 |
5 | A | 4 |
ok,根据以上分布结果,可以轻松算出:
P A = 5 + 4 10 + 10 = 0.45 P_A = \frac{5+4}{10+10} = 0.45 PA=10+105+4=0.45
P B = 7 + 8 + 9 10 + 10 + 10 = 0.8 P_B = \frac{7+8+9}{10+10+10} = 0.8 PB=10+10+107+8+9=0.8
似乎很简单,但是你要知道每次我们都知道了是抛出哪个硬币。换句话说,我们已经提前知道了每次选择硬币抛出的样本分布
如果,我们不知道呢?那会是什么样的?
上面的例子变成了这种:
实验 |
投掷的硬币 |
正面次数 |
---|---|---|
1 | Z{A,B} | 5 |
2 | Z{A,B} | 7 |
3 | Z{A,B} | 8 |
4 | Z{A,B} | 9 |
5 | Z{A,B} | 4 |
Z{A,B}表示每次选择A、B两个硬币中一个,但是我们现在已经不知道是哪一个了,目标还是估计A、B两个硬币正面朝上的概率
理一下逻辑:
这样来看,这个问题就有趣了,我们并不知道是鸡生蛋还是蛋生鸡,怎么判断是先有鸡还是先有蛋?
考虑这个问题不妨再来看个例子:
大厨炒好了一锅菜,需要把锅里的菜平均分配
到两个碟子里,大厨应该怎么办呢?
如果是浸淫已久的大厨,可能随便小手一抖就平均到两个碟子里了,但是大多数都是伪大厨,手上功夫还不到位,他们应该怎么做?
伪大厨可能需要多试几次才行,比如先随便分在两个碟子里,然后把菜多碟子的菜往另一个碟子匀匀,然后再匀匀,再匀匀…一直到两个碟子中菜的量大致一样。
ok,那我们这个问题呢?要不也试试伪大厨的方法?
首先,我们模仿伪大厨的方法,随便设置A、B硬币正面朝上的概率P_A=0.5、P_B=0.9
如果第一枚硬币是A硬币,那么它正面朝上的概率是:
0. 5 5 ∗ 0. 5 5 = 0.000976 0.5^5 * 0.5^5 = 0.000976 0.55∗0.55=0.000976
如果第一枚硬币是B硬币,那么它正面朝上的概率是:
0. 9 5 ∗ 0. 1 5 = 0.0000059 0.9^5 * 0.1^5 = 0.0000059 0.95∗0.15=0.0000059
同样的计算方法,其他几枚硬币的概率分别是:
次数 |
正面次数 |
若是硬币A的概率 |
若是硬币B的概率 |
---|---|---|---|
1 | 5 | 0.000976 | 0.0000059 |
2 | 7 | 0.000976 | 0.000478 |
3 | 8 | 0.000976 | 0.0043 |
4 | 9 | 0.000976 | 0.0387 |
5 | 4 | 0.000976 | 0.00000065 |
按照最大似然法则,每一次取概率最大的作为我们的结果,则硬币的顺序依次是:AABBA
这个也就是上面我们假设的Z值,那么根据这个Z值我们可以轻松的算出:
P A = 5 + 7 + 4 10 + 10 + 10 = 0.533333 P_A = \frac{5+7+4}{10+10+10} = 0.533333 PA=10+10+105+7+4=0.533333
P B = 8 + 9 10 + 10 = 0.85 P_B = \frac{8+9}{10+10} = 0.85 PB=10+108+9=0.85
算出的P_A、P_B的值和我们假设的不一致,这时候说明伪大厨还没有把菜分均匀
看来伪大厨还需要再分一次,这个时候A、B正面朝上的值由第一次的:
P A = 0.5 , P B = 0.9 P_A=0.5 , P_B=0.9 PA=0.5,PB=0.9
更新成现在的:
P A = 0.5333 , P B = 0.85 P_A=0.5333 , P_B=0.85 PA=0.5333,PB=0.85
重复上面的步骤,最终A、B正面朝上的概率不再发生变化,且会逐渐逼近一个值,这就是EM算法的工作原理。
虽然说我们一直在模仿大厨的操作,但是我们也想要超越他成为更厉害的大厨。
在上面的过程中,我们发现直接每一次选择最大概率的结果作为硬币的选择有点过于绝对,因为从计算结果来看另一枚硬币也是有概率的,只是概率偏小一点。这样的话,我们在第一轮中可以这样计算:
如果第一枚硬币是A硬币,那么它正面朝上的概率是:
0. 5 5 ∗ 0. 5 5 0. 5 5 ∗ 0. 5 5 + 0. 9 5 ∗ 0. 1 5 = 0.994 \frac{0.5^5 * 0.5^5}{0.5^5 * 0.5^5+0.9^5 * 0.1^5} = 0.994 0.55∗0.55+0.95∗0.150.55∗0.55=0.994
如果第一枚硬币是B硬币,那么它正面朝上的概率是:
0. 9 5 ∗ 0. 1 5 0. 9 5 ∗ 0. 1 5 + 0. 5 5 ∗ 0. 5 5 = 0.006 \frac{0.9^5 * 0.1^5}{0.9^5 * 0.1^5+0.5^5 * 0.5^5} = 0.006 0.95∗0.15+0.55∗0.550.95∗0.15=0.006
同理可以求出第2轮到底5轮的概率值
次数 |
正面次数 |
若是硬币A的概率 |
若是硬币B的概率 |
---|---|---|---|
1 | 5 | 0.994 | 0.006 |
2 | 7 | 0.671 | 0.329 |
3 | 8 | 0.185 | 0.815 |
4 | 9 | 0.025 | 0.975 |
5 | 4 | 0.999 | 0.001 |
此时,我们可以根据最大似然估计求出的概率,分别算出AB正反面的期望值:
例如:第一轮中,0.994的概率为A,抛10次,正面朝上的概率为0.994*5=9.94,同理反正为0.06
同理可以求出第2轮到第5轮的期望值:
次数 |
正面次数 |
若是硬币A的期望 |
若是硬币B的期望 |
---|---|---|---|
1 | 5 | 正:4.97;反:4.97 | 正:0.03;反:0.03 |
2 | 7 | 正:4.697;反:2.013 | 正:2.303;反:0.987 |
3 | 8 | 正:1.48;反:0.37 | 正:6.52;反:1.63 |
4 | 9 | 正:0.225;反:0.025 | 正:8.775;反:0.975 |
5 | 4 | 正:3.996;反:5.994 | 正:0.004;反:0.006 |
正:15.368 ;反:13.372 | 正:17.632 ;反:3.628 |
此时根据期望值我们可以轻松的算出:
P A 正 = 15.368 15.368 + 13.372 = 0.5347 P_{A正} = \frac{15.368}{15.368+13.372} = 0.5347 PA正=15.368+13.37215.368=0.5347
P B 正 = 17.632 17.632 + + 3.628 = 0.829 P_{B正} = \frac{17.632}{17.632++3.628} = 0.829 PB正=17.632++3.62817.632=0.829
可以看出,相比上一种方法,这种方法的收敛会更快些,更加逼近我们的目标值。
这个过程就是EM算法的实现过程
上面的投掷硬币属于A硬币还是B硬币我们称之为隐含参数,A硬币和B硬币的分布参数我们称之为模型参数。
EM 算法解决这个的思路是使用启发式的迭代方法,既然我们无法直接求出模型分布参数,那么我们可以先猜想隐含参数(EM算法的E步),接着基于观察数据和猜测的隐含参数一起来极大化似然估计,求解我们的模型参数(EM算法的M步)。
由于我们之前的隐含参数是猜测的,所以此时得到的模型参数一般还不是我们想要的结果。我们基于当前得到的模型参数,继续猜测隐含参数(EM算法的E步),然后继续极大化似然估计,求解我们的模型参数(EM算法的M步)。
以此类推,不断的迭代下去,直到模型分布参数基本无变化,算法收敛,找到合适的模型参数。
画了一个图,感受一下:
EM算法在聚类的时候也是要先估计一个隐状态,这个隐状态也就是我们的样本标签。
有了样本标签之后,就可以将原来的无监督学习转换为监督学习,然后通过极大似然估计法求解出模型最优参数。
需要解释一点的是,在整个过程中,隐状态的估计需要用到EM算法。
k-means算法是通过距离来聚类的,因为距离是确定的,所以就导致每个样本只能归为一类,这叫做硬聚类。
而EM算法在聚类的过程中,每个样本都有一定的概率和每个聚类有关,这叫做软聚类。
通常,我们可以假设样本符合高斯分布,因为每个高斯分布都属于这个模型的组成部分,要分成K个簇就相当于是K个组成部分。
这样我们可以先初始化每个组成部分的高斯分布的参数,然后再来看每个样本是属于哪个组成部分。这就是E步骤。
再通过得到的这些隐含变量结果,反过来求每个组成部分高斯分布的参数,即 M 步骤。
反复EM步骤,直到每个组成部分的高斯分布参数不变为止,这样也就相当于将样本按照高斯模型进行了EM聚类。
EM 算法相当于一个框架,我们可以采用不同的模型来进行聚类,比如 GMM(高斯混合模型)、 HMM(隐马尔科夫模型)来进行聚类。
如何创建高斯聚类呢,我们需要先了解一下高斯聚类的参数
在sklearn 中,高斯聚类可以这样创建:
# 创建高斯聚类模型
gmm = GaussianMixture(n_components=1, covariance_type='full', max_iter=100)
解释一下主要的参数:
其中协方差类型covariance_type又四种取值,分别是:
需要注意的是,聚类的个数往往是由业务决定的,比如对用户收入进行聚类,可以分为:高收入群体、中收入群体、低收入群体,根据用户价值进行聚类,可以分为:高价值用户、中价值用户、低价值用户、无价值用户等等
当然如果你无法确定聚类的个数,可以通过设置不同的聚类个数进而选择具有最优效果的模型
本次实战我们的数据是王者荣耀的英雄属性数据,通过对69个英雄的22个属性数据,其中包括英雄的最大生命、生命成长、最大发力、最高物攻等等,通过每个英雄之间的特征,对69个英雄进行“人以群分,物以类聚”。感兴趣的同学可以尝试一下最终的结果能否应用于实际游戏中。
ok,先来看看我们本次数据的整体描述
再来看看各个英雄属性的整体情况
一共22个英雄属性(不包括姓名),其中次要定位存在空值,且空值较多
再来看看数值型数据的整体分布情况
数据分布没有什么异常,但是应该需要考虑进行标准化,这个后面再说
最大攻速字段应该是数值型的,我们需要对其进行处理
另外,次要定位属性缺失值太多,而且没有有效的填充方法,直接删掉它
# 最大攻速为百分比需要替换成数值
df_data['最大攻速'] = df_data['最大攻速'].apply(lambda str: str.replace('%', ''))
# 次要定位数据无法填充,且已存在主要定位,直接删除该字段
df_data.drop('次要定位', axis=1, inplace=True)
一共只有69数据,但是却有22个属性,是否存在属性重复的情况呢?
我们知道在建模过程中,重复的属性对最终结果不会产生影响
所以我们可以通过关联度分析,看一下数据之间的关联度情况,这种方式在前面的实战种很多次都用到过。
可以看到,用红框标出的,都是属性之间相互关联度特别大的,对于这些我们只需要保留其中的一种属性即可
通过筛选,我们最终确定了13个英雄属性
再来看英雄属性:攻击范围和主要定位,是离散型特征,直接对其进行特征量化
"""通过标签编码实现特征量化"""
for feature in ['攻击范围', '主要定位']:
le = preprocessing.LabelEncoder()
le.fit(df_data[feature])
df_data[feature] = le.transform(df_data[feature])
最后就是数据的规范化,直接通过Z-Score进行数据规范
"""采用Z-Score规范化数据,保证每个特征维度的数据均值为0,方差为1"""
stas = StandardScaler()
df_data = stas.fit_transform(df_data)
选用我们前面提到的GMM进行建模
# 构造GMM聚类
gmm = GaussianMixture(n_components=30, covariance_type='full')
gmm.fit(df_data)
# 训练数据
prediction = gmm.predict(df_data)
最终的模型聚类结果是这样的:
上面的图中放了前20的英雄,组号相同的英雄表示属性相近,感兴趣的同学不妨在游戏中试试?
另外,聚类算法属于无监督的学习方式,我们并没有实际的结果可以进行对比来区别模型的准确率。
但是在文章的前面也提到了几种评估方式,这里我们试着用轮廓系数进行模型的评分
最后得分0.246,也不是很高,说明聚类的效果不是特别好,应该还是英雄属性的原因,例如,通过主要定位就可以对英雄就行聚类,或者通过攻击范围进行聚类,但是这两个属性和其他属性的结合,有时候并非是最好的,对游戏理解比较深刻的同学可以考虑一下优化方法。
EM算法实在是没想到会写这么多,有点出乎意料。
文章中公示的推导我也都一笔带过了,相信在网上都能找到相关的资料,也就不费力去敲公式了,留给大家了。
这篇主要是入门,了解原理,以及如何在实战中用起来。
写文章花了很多时间,导致有很多内容都没有补充,包括思维导图,也只是粗粗写了个大纲,没有去细分。
先贴出来了,后面整理好了统一放在《大话系列》上
其实还是挺感谢DataWhale 组织的这次学习
昨晚也和群里的朋友交流了下,有时候一个人学习真的会突然的放弃,组队学习这种方式就很不错
四月份快结束了,大家加油呀!