OneR算法介绍。
OneR的全称为:One Rule,顾名思义也就是一条规则的意思。也就是说我们最终仅仅根据训练集中的一个特征就能够实现对数据的分类。如果只是使用一条规则的话,很显然这个分类的准确度不会很高,但是在某些特定的数据集中这个简单的算法也能够得到比较好的表现。
为了明白这个算法的工作原理,首先举一个比较简单的例子:就拿人的身高和眼睛大小以及肤色的数据对人进行分类是男是女。其中的编号不属于特征范畴,只是为了后续介绍数据使用。
编号 | 身高 | 眼睛大小 | 肤色 | 性别 |
---|---|---|---|---|
1 | 180 | 正常 | 偏白 | 男 |
2 | 175 | 较大 | 偏黑 | 男 |
3 | 170 | 正常 | 偏黑 | 男 |
4 | 170 | 较大 | 偏黑 | 女 |
5 | 165 | 正常 | 偏白 | 女 |
6 | 160 | 较大 | 偏白 | 女 |
上述表格中随机写了一系列数据,我们来根据这些数据介绍一下OneR算法。
既然OneR算法是根据一个规则,也就是某一个特征来进行分类的,那么如何找到这个规则就比较重要了。
就拿上述的例子来看,我们有:身高、眼睛大小、肤色这三种特征,身高是属于连续值的范畴,眼睛大小是属于离散数据为:正常、较大,肤色也是离散数据:偏白、偏黑。
我们首先使用简单的方法将其转换为离散值,将大于(或等于)身高平均值的记为1,小于身高平均值的记为0,那么现在的数据就是这样。
编号 | 身高 | 眼睛大小 | 肤色 | 性别 |
---|---|---|---|---|
1 | 1 | 正常 | 偏白 | 男 |
2 | 1 | 较大 | 偏黑 | 男 |
3 | 1 | 正常 | 偏黑 | 男 |
4 | 1 | 较大 | 偏黑 | 女 |
5 | 0 | 正常 | 偏白 | 女 |
6 | 0 | 较大 | 偏白 | 女 |
接下来我们就需要根据这些数据找到一个可以用来分类的特征规则。这个特征究竟是根据身高、眼睛大小、肤色呢?
其实根据我们的生活常识,知道在这三种特征中身高的可靠性比较高的。但是如何根据计算找到身高这个特征呢?
如何找到用来分类的规则(特征)。
其实简单的想一下就知道了,当然是使用这个特征之后我们的划分结果的正确率是最高的。我们需要进行一些简单的计算,要确保使用这个特征进行分类得到的准确率最高。因为只有准确率最高我们才能够得到比较正确的分类结果,所以我们的任务就可以转换为找到一个特征,根据这个特征进行分类的时候能够得到最高的正确率。
但是要明白,某个特征可能会有多个特征值,所以计算特征的准确率的话,需要包含其中的所有特征值可能性。也就是说这个特征的准确率是根据所有的特征值计算出来的。而对于某个特征对应的特征值来看,当然是选择分类最多的那个作为本次特征值的分类结果,然后再根据本次特征值的分类结果计算出错误个数。最后汇总到一起计算出该特征的准确率。
下面对上述样本集中的身高、眼睛大小、肤色分别计算准确率。
身高。
我们可以看到身高这个特征共有两种特征值:0和1。
如果身高特征值为1的话,那么符合特征值的数据编号为:{1,2,3,4},对应的分类为:{男,男,男,女}。很明显如果身高这个特征值为1的话,在本次样本集中男生占了3/4,女生占了1/4。那么我们就选择分类结果最多的那个作为身高特征值为1的划分结果,也就是性别为男。所以现在可以简单的认为如果身高特征值为1的话,我们就简单的认为性别为男。但是很显然这个结论是有错误的,在本次样本集中这个结论的错误个数为1,因为在身高特征值为1的情况下有1样本的性别为女,与我们的结论不符。
如果身高特征值为0的话。那么符合特征值的数据编号为:{5,6},对应的分类为:{女,女}。这个计算结果比较明显了。如果身高特征值为0的话,女生占了2/2。那么我们就选择性别为女作为身高特征值为0的分类结果。也就是说如果身高的特征值为0的话,我们就认为性别为女。这个结论可能有错误,但是在本次的数据集中是全部正确的。
在计算完身高的全部特征值之后就可以计算准确率了,我们在身高特征值为0的情况下,我们将其划分为女,这个准确率为百分之百。在身高特征值为1的情况下我们将其划分为男,有一个数据是错误的。所以如果按照身高进行划分的话,得到的准确率为:5/6=0.833。
眼睛大小。
眼睛大小这个特征共有两种特征值:正常、较大。
如果眼睛大小是正常的话,符合特征值的数据编号为:{1,3,5},对应的分类结果为:{男,男,女}。同样的男生占了2/3,女生占了1/3,那么就简单的认为如果眼睛大小为正常的话,就认为性别为男性。同样的在本次数据集中,这个结论的错误个数为1,因为当前特征值分类结果中有一个是女性。
如果眼睛大小是较大的话,符合特征值的数据编号为:{2,4,6},对应的分类结果为:{女,女,男}。同样的女生占了2/3,男生占了1/3,那么就简单的认为如果眼睛大小为较大的话,就认为性别为女性。同样的在本次数据集中,这个结论的错误个数为1。
有关眼睛大小的所有特征值计算完成之后就可以计算准确率了。我们在眼睛大小为正常的情况下,将其划分为男,这个结论在样本集中有一个数据是错误的;同样的在眼睛大小为较大的情况下,将其划分为女,这个结论在样本集中有一个数据是错误的。所以如果按照眼睛大小进行划分的话,准确率为:4/6=0.667。
肤色。
肤色特征共有两个特征值:偏黑、偏白。
如果肤色偏黑的话,符合特征值的数据编号为:{2,3,4},对应的分类结果为:{男,男,女}。男生占了2/3,女生1/3。所以如果肤色偏黑的话,就简单认为是男生,这个结论的错误个数为1。
如果肤色偏白的话,符合特征值的数据编号为:{1,5,6},对应的分类结果为:{男,女,女}。男生占了1/3,女生占了2/3,。所以如果肤色偏白的话,就简单的认为是女生,这个结论的错误个数为1。
所以肤色的准确率为:4/6=0.667。
现在得到了各个特征的准确率。身高的划分准确率为:0.833;肤色和眼睛大小的准确率都为:0.667。所以我们使用身高作为划分特征。
也就是如果有新的数据,我们只看身高这一项数据就将其进行分类。但是分类器的准确率的话是需要使用测试数据进行计算的,我们计算出来的0.833只是根据训练样本集的准确率,只是为了找出用来划分的规则。
使用Python实现OneR算法。
下面使用Python实现OneR算法,我们首先回顾一下上面的计算流程。首先我们找到每个特征的所有特征值,然后根据每个特征值的分类结果对这个特征值进行了简单分类(比如肤色偏黑的情况下,我们就认为是男性),接下来找出每个特征值的分类错误个数(比如说肤色偏黑我们认为是男性,但是在样本集中存在一个女性偏黑,所以错误个数为1),最后计算出这个特征的准确率,然后找到准确率最高的作为划分规则。
1.创建数据集。
def createDataSetForPerson():
"""
使用身高、眼睛大小、肤色作为数据集
:return:
"""
dataSet = [
# 1
[1, '正常', '偏白', '男'],
# 2
[1, '较大', '偏黑', '男'],
# 3
[1, '正常', '偏黑', '男'],
# 4
[1, '较大', '偏黑', '女'],
# 5
[0, '正常', '偏白', '女'],
# 6
[0, '较大', '偏白', '女'],
]
# 特征值列表
labels = ['身高', '眼睛大小', '肤色']
dataSet = np.array(dataSet)
# 得到分类结果
classify = dataSet[:, -1]
# 去除分类之后的数据集
dataSet = dataSet[:, :-1]
return dataSet, classify, labels
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
查看一下该函数的返回结果:
2.对某个特征的特征值进行分类和计算错误个数。
def train_feature_value(dataSet, classify, feature_index, value):
"""
根据传入的样本集和分类集,计算某个特征等于目标特征值时最可能属于哪一个分类,计算出错误个数
:param dataSet: 数据集
:param classify: 数据集对应的分类结果
:param feature_index: 特征下标
:param value: 特征值
:return:
"""
# 用来保存每个分类的个数
class_counts = defaultdict(int)
# 遍历所有的样本数据和对应的标签
for sample, label in zip(dataSet, classify):
# 如果当前样本的指定特征等于目标特征值的话
if sample[feature_index] == value:
# 对应的特征值个数加一
class_counts[label] += 1
# 根据分类的值排序,从大到小
sorted_class_sounts = sorted(class_counts.items(), key=operator.itemgetter(1), reverse=True)
# 找到对多的分类,也就是我们的目标分类
most_class = sorted_class_sounts[0][0]
# 在分类结果中找到分类不等于目标分类的
incorrect_predictions = [class_count for class_value, class_count in class_counts.items()
if class_value != most_class]
# 计算出错误个数
error = sum(incorrect_predictions)
return most_class, error
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
使用该函数的返回结果:
第一个参数就是数据集,第二个参数是对应的分类结果,第三个参数是进行计算的特征下标,0对应的就是身高,第四个参数对应的就是当前特征的特征值,也就是说身高为1的情况。(这里用str类型的1是因为numpy中的就为str)
输出结果:
也就是说如果身高为1的情况下,我们会将其划分为男性,但是会有1个错误,这个与我们上面的计算结果一致。
当我们更换为其他的数据之后,也会得到与上面计算相同的结果:
3.计算某个特征划分的正确率(在这里计算的最小的错误率)。
我们在计算完某个特征的全部特征值之后,就需要根据对应的错误个数计算出这个特征划分的正确率。我们在这里计算的是错误率,所以如果正确率最高的话,错误率应该就是最低的了。
def train_on_feature(dataSet, classify, feature_index):
"""
计算某一个特征的总错误率
:param dataSet: 样本集
:param classify: 样本集对应的分类结果
:param feature_index: 特征下标
:return:
"""
# 得到该特征对应的所有可能性
current_feature_full = set(dataSet[:, feature_index])
# 用来存放当前特征的所有特征值的预测分类
predictors = {}
# 用来存放所有特征的错误个数
errors = []
# 遍历所有的特征可能性
for current_feature in current_feature_full:
# 计算当前特征值的预测分类和错误个数
most_class, error = train_feature_value(dataSet, classify, feature_index, current_feature)
# 将最可能划分的结果加入到预测结果中
predictors[current_feature] = most_class
errors.append(error)
# 计算出当前特征值分类的错误率
error_chance = sum(errors)/float(len(dataSet))
return predictors, error_chance
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
编写一个测试代码,使用上述的函数对身高计算正确率:
输出结果:
能够看到,该函数有两个返回值,第一个就是对应特征值的预测分类,例如:{1,男,0:女}就是如果身高的特征值为1的话,我们就认为是男的,如果身高的特征值为0的话,我们就认为是女的。第二个返回值就是这个特征的错误率,使用1减去错误率之后就是正确率得到0.833,与上面的计算结果一致。
4.找到最好的划分特征,创建model。
下面就需要计算所有的特征准确率,然后找到准确率最高的哪一个作为OneR算法的划分规则。
def createModel(dataSet, classify):
"""
实现OneR算法,找到特征错误率最低的特征作为划分结果,建立模型
:param dataSet: 样本集
:param classify: 样本集对应的分类
:return:
"""
# 用来存放所有的预测特征
all_predictors = {}
# 用来存放所有预测特征的错误率
errors = {}
# 遍历所有的特征
for feature_index in range(dataSet.shape[1]):
# 使用当前特征进行预测
predictors, total_error = train_on_feature(dataSet, classify, feature_index)
# 存放当前的特征预测
all_predictors[feature_index] = predictors
# 存放当前的特征预测错误率
errors[feature_index] = total_error
# 找出错误率最低的一个,用来划分特征
best_feature, best_error = sorted(errors.items(), key=operator.itemgetter(1))[0]
# 建立模型
model = {'variable': best_feature, 'predictor': all_predictors[best_feature]}
return model
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
测试代码:
输出结果:
能够看到这个model的输出内容,其中variable就是用来进行划分的那一个特征规则下标,其中predictor就是使用这个特征的话每个特征值的分类结果。比如图中的输出variable是0,就是身高的下标,所以predictor的输出内容就是当身高特征值为0的情况下我们认为是女,当身高特征值为1的情况下我们认为是男。
到现在为止,我们已经使用代码实现了OneR算法,并且建立了模型。也正如oneR算法那样描述的一样,仅仅根据身高这一项特征进行分类。
5.使用model进行预测。
创建完model之后,我们就可以使用model对数据进行分类预测了。
def classifyOneR(inX, model):
"""
使用OneR进行预测分类
:param inX: 要进行预测的数据
:param model: 建立的模型
:return:
"""
# 拿到模型中用来分类的特征下标
variable = model['variable']
# 拿到特征对应的划分集
predictor = model['predictor']
# 进行分类
classify = predictor[inX[variable]]
return classify
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
我们对一个新的数据进行预测:
能够看到输出结果为:男。
总结。
OneR算法是很简单的一个分类方法,能够快速的建立模型进行分类预测。主要的核心思想就是在数据集的所有特征中,找到最重要的那个特征进行分类。当然这个最重要的特征就是根据数据集中的每个特征划分的准确性进行查找。
由于OneR算法会找到最主要的那个特征,从而忽略了其他的特征,并不能完全的利用好数据的全部特征。所以其准确率相对就比较低了,但是在某些情况下,往往会存在某个最重要的特征条件。比如就是例子中的情况,在人类中对男女的身高体重差异还是比明显的,其他特征在这两种特征下就显得稍微不足。但是仅仅根据身高体重来划分也不会太准确,如果能够利用好其他的特征,找到其中的关系的话分类结果会准确很多。
除了文中使用的数据集之外,本人还使用了西瓜书中的西瓜数据和sklearn自带的Iris职务分类数据集进行测试,代码地址:OneR实现代码。