机器学习经典算法之EM

1、简介html

EM 的英文是 Expectation Maximization,因此 EM 算法也叫最大指望算法。git

咱们先看一个简单的场景:假设你炒了一份菜,想要把它平均分到两个碟子里,该怎么分?github

不多有人用称对菜进行称重,再计算一半的份量进行平分。大部分人的方法是先分一部分到碟子 A 中,而后再把剩余的分到碟子 B 中,再来观察碟子 A 和 B 里的菜是否同样多,哪一个多就匀一些到少的那个碟子里,而后再观察碟子 A 和 B 里的是否同样多……整个过程一直重复下去,直到分量不发生变化为止。算法

你能从这个例子中看到三个主要的步骤:初始化参数、观察预期、从新估计。首先是先给每一个碟子初始化一些菜量,而后再观察预期,这两个步骤实际上就是指望步骤(Expectation)。若是结果存在误差就须要从新估计参数,这个就是最大化步骤(Maximization)。这两个步骤加起来也就是 EM 算法的过程。数组

/*请尊重做者劳动成果,转载请标明原文连接:*/数据结构

/* https://www.cnblogs.com/jpcflyer/p/11143638.html * /app

2、EM 算法的工做原理框架

说到 EM 算法,咱们先来看一个概念“最大似然”,英文是 Maximum Likelihood,Likelihood 表明可能性,因此最大似然也就是最大可能性的意思。函数

什么是最大似然呢?举个例子,有一男一女两个同窗,如今要对他俩进行身高的比较,谁会更高呢?根据咱们的经验,相同年龄下男性的平均身高比女性的高一些,因此男同窗高的可能性会很大。这里运用的就是最大似然的概念。工具

最大似然估计是什么呢?它指的就是一件事情已经发生了,而后反推更有多是什么因素形成的。仍是用一男一女比较身高为例,假设有一我的比另外一我的高,反推他多是男性。最大似然估计是一种经过已知结果,估计参数的方法。

那么 EM 算法是什么?它和最大似然估计又有什么关系呢?EM 算法是一种求解最大似然估计的方法,经过观测样本,来找出样本的模型参数。

再回过来看下开头我给你举的分菜的这个例子,实际上最终咱们想要的是碟子 A 和碟子 B 中菜的分量,你能够把它们理解为想要求得的 模型参数 。而后咱们经过 EM 算法中的 E 步来进行观察,而后经过 M 步来进行调整 A 和 B 的参数,最后让碟子 A 和碟子 B 的参数再也不发生变化为止。

实际咱们遇到的问题,比分菜复杂。我再给你举个一个投掷硬币的例子,假设咱们有 A 和 B 两枚硬币,咱们作了 5 组实验,每组实验投掷 10 次,而后统计出现正面的次数,实验结果以下:

投掷硬币这个过程当中存在隐含的数据,即咱们事先并不知道每次投掷的硬币是 A 仍是 B。假设咱们知道这个隐含的数据,并将它完善,能够获得下面的结果:

咱们如今想要求得硬币 A 和 B 出现正面次数的几率,能够直接求得:

而实际状况是我不知道每次投掷的硬币是 A 仍是 B,那么如何求得硬币 A 和硬币 B 出现正面的几率呢?

这里就须要采用 EM 算法的思想。

1. 初始化参数。咱们假设硬币 A 和 B 的正面几率(随机指定)是θA=0.5 和θB=0.9。

2. 计算指望值。假设实验 1 投掷的是硬币 A,那么正面次数为 5 的几率为:

公式中的 C(10,5) 表明的是 10 个里面取 5 个的组合方式,也就是排列组合公式,0.5 的 5 次方乘以 0.5 的 5 次方表明的是其中一次为 5 次为正面,5 次为反面的几率,而后再乘以 C(10,5) 等于正面次数为 5 的几率。

假设实验 1 是投掷的硬币 B ,那么正面次数为 5 的几率为:

因此实验 1 更有可能投掷的是硬币 A。

而后咱们对实验 2~5 重复上面的计算过程,能够推理出来硬币顺序应该是{A,A,B,B,A}。

这个过程其实是经过假设的参数来估计未知参数,即“每次投掷是哪枚硬币”。

3. 经过猜想的结果{A, A, B, B, A}来完善初始化的参数θA 和θB。

而后一直重复第二步和第三步,直到参数再也不发生变化。

简单总结下上面的步骤,你能看出 EM 算法中的 E 步骤就是经过旧的参数来计算隐藏变量。而后在 M 步骤中,经过获得的隐藏变量的结果来从新估计参数。直到参数再也不发生变化,获得咱们想要的结果。

 

EM 聚类的工做原理

上面你能看到 EM 算法最直接的应用就是求参数估计。若是咱们把潜在类别当作隐藏变量,样本看作观察值,就能够把聚类问题转化为参数估计问题。这也就是 EM 聚类的原理。

相比于 K-Means 算法,EM 聚类更加灵活,好比下面这两种状况,K-Means 会获得下面的聚类结果。

由于 K-Means 是经过距离来区分样本之间的差异的,且每一个样本在计算的时候只能属于一个分类,称之为是硬聚类算法。而 EM 聚类在求解的过程当中,实际上每一个样本都有必定的几率和每一个聚类相关,叫作软聚类算法。

你能够把 EM 算法理解成为是一个框架,在这个框架中能够采用不一样的模型来用 EM 进行求解。经常使用的 EM 聚类有 GMM 高斯混合模型和 HMM 隐马尔科夫模型。GMM(高斯混合模型)聚类就是 EM 聚类的一种。好比上面这两个图,能够采用 GMM 来进行聚类。

和 K-Means 同样,咱们事先知道聚类的个数,可是不知道每一个样本分别属于哪一类。一般,咱们能够假设样本是符合高斯分布的(也就是正态分布)。每一个高斯分布都属于这个模型的组成部分(component),要分红 K 类就至关因而 K 个组成部分。这样咱们能够先初始化每一个组成部分的高斯分布的参数,而后再看来每一个样本是属于哪一个组成部分。这也就是 E 步骤。

再经过获得的这些隐含变量结果,反过来求每一个组成部分高斯分布的参数,即 M 步骤。反复 EM 步骤,直到每一个组成部分的高斯分布参数不变为止。

这样也就至关于将样本按照 GMM 模型进行了 EM 聚类。

 

3、 如何使用 EM 工具包

在 Python 中有第三方的 EM 算法工具包。因为 EM 算法是一个聚类框架,因此你须要明确你要用的具体算法,好比是采用 GMM 高斯混合模型,仍是 HMM 隐马尔科夫模型。

咱们主要讲解 GMM 的使用,在使用前你须要引入工具包:

1 from sklearn.mixture import GaussianMixture

咱们看下如何在 sklearn 中建立 GMM 聚类。

首先咱们使用 gmm = GaussianMixture(n_components=1, covariance_type=‘full’, max_iter=100) 来建立 GMM 聚类,其中有几个比较主要的参数(GMM 类的构造参数比较多,我筛选了一些主要的进行讲解),我分别来说解下:

1.n_components:即高斯混合模型的个数,也就是咱们要聚类的个数,默认值为 1。若是你不指定 n_components,最终的聚类结果都会为同一个值。

2.covariance_type:表明协方差类型。一个高斯混合模型的分布是由均值向量和协方差矩阵决定的,因此协方差的类型也表明了不一样的高斯混合模型的特征。协方差类型有 4 种取值:

covariance_type=full,表明彻底协方差,也就是元素都不为 0;

covariance_type=tied,表明相同的彻底协方差;

covariance_type=diag,表明对角协方差,也就是对角不为 0,其他为 0;

covariance_type=spherical,表明球面协方差,非对角为 0,对角彻底相同,呈现球面的特性。

3.max_iter:表明最大迭代次数,EM 算法是由 E 步和 M 步迭代求得最终的模型参数,这里能够指定最大迭代次数,默认值为 100。

建立完 GMM 聚类器以后,咱们就能够传入数据让它进行迭代拟合。

咱们使用 fit 函数,传入样本特征矩阵,模型会自动生成聚类器,而后使用 prediction=gmm.predict(data) 来对数据进行聚类,传入你想进行聚类的数据,能够获得聚类结果 prediction。

你能看出来拟合训练和预测能够传入相同的特征矩阵,这是由于聚类是无监督学习,你不须要事先指定聚类的结果,也没法基于先验的结果经验来进行学习。只要在训练过程当中传入特征值矩阵,机器就会按照特征值矩阵生成聚类器,而后就可使用这个聚类器进行聚类了。

 

4、 如何用 EM 算法对王者荣耀数据进行聚类

了解了 GMM 聚类工具以后,咱们看下如何对王者荣耀的英雄数据进行聚类。

首先咱们知道聚类的原理是“人以群分,物以类聚”。经过聚类算法把特征值相近的数据归为一类,不一样类之间的差别较大,这样就能够对原始数据进行降维。经过分红几个组(簇),来研究每一个组之间的特性。或者咱们也能够把组(簇)的数量适当提高,这样就能够找到能够互相替换的英雄,好比你的对手选择了你擅长的英雄以后,你能够选择另外一个英雄做为备选。

咱们先看下数据长什么样子:

这里咱们收集了 69 名英雄的 20 个特征属性,这些属性分别是最大生命、生命成长、初始生命、最大法力、法力成长、初始法力、最高物攻、物攻成长、初始物攻、最大物防、物防成长、初始物防、最大每 5 秒回血、每 5 秒回血成长、初始每 5 秒回血、最大每 5 秒回蓝、每 5 秒回蓝成长、初始每 5 秒回蓝、最大攻速和攻击范围等。

具体的数据集你能够在 GitHub 上下载: https://github.com/cystanford/EM_data

如今咱们须要对王者荣耀的英雄数据进行聚类,咱们先设定项目的执行流程:

首先咱们须要加载数据源;

在准备阶段,咱们须要对数据进行探索,包括采用数据可视化技术,让咱们对英雄属性以及这些属性之间的关系理解更加深入,而后对数据质量进行评估,是否进行数据清洗,最后进行特征选择方便后续的聚类算法;

聚类阶段:选择适合的聚类模型,这里咱们采用 GMM 高斯混合模型进行聚类,并输出聚类结果,对结果进行分析。

按照上面的步骤,咱们来编写下代码。完整的代码以下:

 1 # -*- coding: utf-8 -*-
 2 
 3 import pandas as pd
 4 
 5 import csv
 6 
 7 import matplotlib.pyplot as plt
 8 
 9 import seaborn as sns
10 
11 from sklearn.mixture import GaussianMixture
12 
13 from sklearn.preprocessing import StandardScaler
14 
15 # 数据加载,避免中文乱码问题
16 
17 data_ori = pd.read_csv('./heros7.csv', encoding = 'gb18030')
18 
19 features = [u'最大生命',u'生命成长',u'初始生命',u'最大法力', u'法力成长',u'初始法力',u'最高物攻',u'物攻成长',u'初始物攻',u'最大物防',u'物防成长',u'初始物防', u'最大每 5 秒回血', u'每 5 秒回血成长', u'初始每 5 秒回血', u'最大每 5 秒回蓝', u'每 5 秒回蓝成长', u'初始每 5 秒回蓝', u'最大攻速', u'攻击范围']
20 
21 data = data_ori[features]
22 
23 # 对英雄属性之间的关系进行可视化分析
24 
25 # 设置 plt 正确显示中文
26 
27 plt.rcParams['font.sans-serif']=['SimHei'] # 用来正常显示中文标签
28 
29 plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号
30 
31 # 用热力图呈现 features_mean 字段之间的相关性
32 
33 corr = data[features].corr()
34 
35 plt.figure(figsize=(14,14))
36 
37 # annot=True 显示每一个方格的数据
38 
39 sns.heatmap(corr, annot=True)
40 
41 plt.show()
42 
43 # 相关性大的属性保留一个,所以能够对属性进行降维
44 
45 features_remain = [u'最大生命', u'初始生命', u'最大法力', u'最高物攻', u'初始物攻', u'最大物防', u'初始物防', u'最大每 5 秒回血', u'最大每 5 秒回蓝', u'初始每 5 秒回蓝', u'最大攻速', u'攻击范围']
46 
47 data = data_ori[features_remain]
48 
49 data[u'最大攻速'] = data[u'最大攻速'].apply(lambda x: float(x.strip('%'))/100)
50 
51 data[u'攻击范围']=data[u'攻击范围'].map({'远程':1,'近战':0})
52 
53 # 采用 Z-Score 规范化数据,保证每一个特征维度的数据均值为 0,方差为 1
54 
55 ss = StandardScaler()
56 
57 data = ss.fit_transform(data)
58 
59 # 构造 GMM 聚类
60 
61 gmm = GaussianMixture(n_components=30, covariance_type='full')
62 
63 gmm.fit(data)
64 
65 # 训练数据
66 
67 prediction = gmm.predict(data)
68 
69 print(prediction)
70 
71 # 将分组结果输出到 CSV 文件中
72 
73 data_ori.insert(0, '分组', prediction)
74 
75 data_ori.to_csv('./hero_out.csv', index=False, sep=',')

运行结果以下:

1 [28 14  8  9  5  5 15  8  3 14 18 14  9  7 16 18 13  3  5  4 19 12  4 12
2 
3 12 12  4 17 24  2  7  2  2 24  2  2 24  6 20 22 22 24 24  2  2 22 14 20
4 
5 14 24 26 29 27 25 25 28 11  1 23  5 11  0 10 28 21 29 29 29 17]

同时你也能看到输出的聚类结果文件 hero_out.csv(它保存在你本地运行的文件夹里,程序会自动输出这个文件,你能够本身看下)。

我来简单讲解下程序的几个模块。

 

关于引用包

首先咱们会用 DataFrame 数据结构来保存读取的数据,最后的聚类结果会写入到 CSV 文件中,所以会用到 pandas 和 CSV 工具包。另外咱们须要对数据进行可视化,采用热力图展示属性之间的相关性,这里会用到 matplotlib.pyplot 和 seaborn 工具包。在数据规范化中咱们使用到了 Z-Score 规范化,用到了 StandardScaler 类,最后咱们还会用到 sklearn 中的 GaussianMixture 类进行聚类。

 

数据可视化的探索

你能看到咱们将 20 个英雄属性之间的关系用热力图呈现了出来,中间的数字表明两个属性之间的关系系数,最大值为 1,表明彻底正相关,关系系数越大表明相关性越大。从图中你能看出来“最大生命”“生命成长”和“初始生命”这三个属性的相关性大,咱们只须要保留一个属性便可。同理咱们也能够对其余相关性大的属性进行筛选,保留一个。你在代码中能够看到,我用 features_remain 数组保留了特征选择的属性,这样就将本来的 20 个属性降维到了 13 个属性。

 

关于数据规范化

咱们能看到“最大攻速”这个属性值是百分数,不适合作矩阵运算,所以咱们须要将百分数转化为小数。咱们也看到“攻击范围”这个字段的取值为远程或者近战,也不适合矩阵运算,咱们将取值作个映射,用 1 表明远程,0 表明近战。而后采用 Z-Score 规范化,对特征矩阵进行规范化。

 

在聚类阶段

咱们采用了 GMM 高斯混合模型,并将结果输出到 CSV 文件中。

这里我将输出的结果截取了一段(设置聚类个数为 30):

第一列表明的是分组(簇),咱们能看到张飞、程咬金分到了一组,牛魔、白起是一组,老夫子本身是一组,达摩、典韦是一组。聚类的特色是相同类别之间的属性值相近,不一样类别的属性值差别大。所以若是你擅长用典韦这个英雄,不防试试达摩这个英雄。一样你也能够在张飞和程咬金中进行切换。这样就算你的英雄被别人选中了,你依然能够有备选的英雄可使用。

相关文章
相关标签/搜索