分类问题和线性的问题有些区别,它的预测结果是离散的,比如判断一封邮件是否是垃圾邮件,它结果只可能是yes,no,今天带来的是基于概率来预测分类结果。而本篇文章使用的算法叫做朴素贝叶斯分类器,是基于朴素贝叶斯对于多特征参数的分类问题。
贝叶斯定理就不细说了,大学概率论一般都应该学过,引用下 WIKI:
1.引入内容:
简单地说,假设要求预测一个 未知邮件 是否是垃圾邮件,则我们需要一些已知的邮件,得到训练集来进行一些概率的统计,假定邮件里的文字特征是一些特定词汇 X (特征数量可能很多),那么结果是判断是否是垃圾邮件 Y,根据贝叶斯有:
但是如果有很多特征的时候(X种类很多),这个公式似乎就不好用了,因为特征之间可能存在概率关联,比如 可能是基于
的触发的概率下所触发的,
这个时候
的概率公式是:
这个时候基于贝叶斯定理提出了一个叫做 朴素贝叶斯 的理论,理论里假设特征之间的概率是相互独立的,即上面的 与
相互独立,所以这样求多特征的概率也就相互独立了,所以特征
的 概率:
所以所有特征的联合后验概率(预测结果为j,比如 Y = 1是垃圾邮件,Y = 0 是正常邮件):
那么对于一个 Y 是一个多结果的分类,需要取得这几个类别中最大的后验概率 (PS:因为只要比较概率大小,所以计算的时候计算分母中的先验概率 可以不计算的),即:
就能给出一封邮件是否是垃圾邮件的结果。
那么计算 就得需要知道
的概率,这个应该比较简单,简单的概率计算就能得到,我们定义下参数,
为特征类别,比如邮件中特定词的数量,发件人,发件时间;
为特征具体内容,如 特定词数量为1,发件人是admin,发件时间为晚上7点;
为结果,如 j = 1是垃圾邮件,j = 0 是正常邮件;
是训练集数量
其中方括号里的表示and,只有两者都ture才进入求和,
式子比较复杂,举个例子说明:
邮件里,发件时间在19:00为垃圾邮件的概率为:
特别地,如果要计算分母,需要用到概率公式:
最后的式子,分母求和的J相当于Y结果的数量。
2.实践
那么结合理论,就找一个数据集来进行一般分类预测,在具体实践前需要对数据集进行分类处理,即需要一个 训练集 一个 验证集, 本文采用的是 自助法,理论就不多说了,直接截取 周志华的《机器学习》里的描述:
数据集来源 Kaggle 的 www.kaggle.com/uciml/mushr… ,是一个关于蘑菇的数据集,里面含有23种大特征,然后区别了是否可食用,该例子就是通过特征来预测一个蘑菇是否是可食用。
2.1 Coding
2.1.1 取数据
import pandas as pd
# import matplotlib.pyplot as plt # 绘图库
import numpy.matlib
import numpy as np
import os
# data from https://www.kaggle.com/uciml/mushroom-classification
df = pd.read_csv("./../../LogisticRegression/20191018/input/mushrooms.csv")
print('size:', df.shape)
#拆解训练集 与 验证集
#训练集
train_df = []
#验证集
validation_df = []
m = len(df)
print('total:', m)
2.1.2 区别测试集和验证集
#使用自组法 获取训练集和验证集 (行数)
validation_list = np.random.choice(m, m) #取长度 0 ~ m-1 取m次
total_list = np.arange(m)
train_list = np.setdiff1d(total_list, validation_list)
print(train_list.shape)
其中 np.setdiff1d 方法是将 两个np.array 取差集作为训练集
2.1.3 格式化数据
#从df 中取出特征以及分类结果
df = df[['class', 'cap-shape', 'cap-color', 'bruises', 'odor', 'gill-attachment', 'gill-spacing']];
#取key做下标区别
key = ['cap_shape_', 'cap_color_', 'bruises_', 'odor_', 'gill-attachment_', 'gill-spacing_']
df = np.array(df)
#获取训练集
for i in range(len(train_list)):
train_df.append(df[train_list[i]])
train_df = np.array(train_df)
#获取验证集
for i in range(len(validation_list)):
validation_df.append(df[validation_list[i]])
validation_df = np.array(validation_df)
print(validation_df.shape)
字段class是表示是否是可使用数据里用的是 e(可食用)和p(有毒的)。
2.1.4 计算训练集里各个特征的概率
#计算各个参数的概率
#概率list
p = {}
total_p = {}
train_df = train_df.tolist()
validation_df = validation_df.tolist()
for i in range(len(train_df)):
for j in range(len(key)):
key1 = key[j] + str(train_df[i][j+1])
if key1 in p:
if train_df[i][0] in p[key1]:
p[key1][train_df[i][0]] = p[key1][train_df[i][0]] + 1
else :
p[key1][train_df[i][0]] = 1
else :
p[key1] = {train_df[i][0] : 1}
if train_df[i][0] in total_p:
total_p[train_df[i][0]] = total_p[train_df[i][0]] + 1
else :
total_p[train_df[i][0]] = 1
#加上拉普拉斯平滑
for i in p:
for j in p[i]:
p[i][j] = (p[i][j] + 1) / (total_p[j] + len(train_df))
for i in total_p:
total_p[i] = total_p[i] / m
print('train set p:', p)
print('total set p:', total_p)
就是上面的求各个特征的概率方法。接下来就是使用验证集合验证了。
2.1.5 验证结果
right_num = 0
error_num = 0
validation_num = len(validation_df)
print('start validat.....\n')
for i in range(validation_num):
data = validation_df[i]
#各个后验概率的字典
validation_p = {}
#p(x)的多元概率和
total_validation_p = 0
for j in total_p:
#分类器可能不止2种
_p_x_i = 1
#计算特征p(xi|y)的概率
for z in range(len(key)):
key1 = key[z] + str(data[z + 1])
if key1 not in p:
#加上拉普拉斯平滑
_p_x_i = _p_x_i * (1 / (len(train_df) + len(key)))
else :
if j not in p[key1]:
#加上拉普拉斯平滑
_p_x_i = _p_x_i * (1 / (len(train_df) + len(key)))
else :
_p_x_i = _p_x_i * p[key1][j]
_validation_p = total_p[j] * _p_x_i
validation_p[j] = _validation_p
total_validation_p = total_validation_p + _validation_p
_max = 0
_validation_result = ''
#这一步应该可以省略 用来计算分母p(x)的
for j in total_p:
validation_p[j] = validation_p[j] / total_validation_p
#判断最大后验概率
for j in validation_p:
if validation_p[j] > _max:
_max = validation_p[j]
_validation_result = j
print('validat data :', data)
print('out of test result :', _validation_result)
# data[0]是验证集合的class的位置
if data[0] == _validation_result:
print('result correct')
right_num = right_num + 1
else :
print('result fail')
print('validation_p is :', validation_p)
error_num = error_num + 1
print('run over...')
print('validation num:', validation_num)
print('validation right num', right_num)
print("validation right rate %s" %str(right_num / validation_num * 100))
print('validation error num', error_num)
print("validation error rate %s" %str(error_num / validation_num * 100))
2.1.6 查看结果
跑一下代码,查看最后输出结果:
到此,本人的机器学习的第三个算法记录就到这里结束了~
最后,cs229讲义牛逼!
以上。
代码git地址: 地址