朴素贝叶斯假设各属性间相互独立,直接从已有样本中计算各种概率,以贝叶斯方程推导出预测样本的分类。
为了处理预测时样本的(类别,属性值)对未在训练样本出现,从而导致概率为0的情况,使用拉普拉斯修正(假设属性值与类别均匀分布)。
代码及注释如下:
一、离散值
1,朴素贝叶斯算法计算相关参数并返回,预测使用这些参数即可
# 手写拉普拉斯修正的朴素贝叶斯 import numpy as np import pandas as pd def naive_bayes(data): '''data:pandas.DataFrame''' # 列名 attrs=data.columns # 类别 labels=data[attrs[-1]].unique() # 类别数 N=labels.size # 样本总数 D=data.index.size # c类样本概率 pc=np.empty(shape=(N,1)) # c类中,第i个属性取值为xi的概率,这里计算了所有,而非只针对测试样本,保存后predict时直接从里面取值即可 p_xc=[] # 包含每个属性的可取值 features=[data[i].unique() for i in attrs[:-1]] for i in range(N): df=data[data[attrs[-1]]==labels[i]] Dc=df[attrs[0]].count() pc[i]=np.array([(Dc+1)/(D+N)]) p_c=[] for j in range(len(features)): values=features[j] Ni=values.size c_attr=[] for value in values: Dc_xi=df[df[attrs[j]]==value].index.size c_attr.append((Dc_xi+1)/(Dc+Ni)) p_c.append(c_attr) p_xc.append(p_c) return p_xc,pc,N,features,labels # 预测一个样本 def predict(x,p_xc,pc,num_class,features,labels): result=[] for i in range(num_class): res=1. c=p_xc[i] for j in range(len(c)): feature_j=c[j] for k in range(len(feature_j)): if x[j]==features[j][k]: res*=feature_j[k] result.append(pc[i][0]*res) max_c=0 max_index=-1 for i in range(len(result)): if result[i]>max_c: max_c=result[i] max_index=i return result,labels[max_index] # 预测多个样本 def predicts(x,p_xc,pc,num_class,features,labels): result=[] for data in x: _,clazz=predict(data,p_xc,pc,num_class,features,labels) result.append(clazz) return result
2,使用西瓜集2.0训练及测试
def createDataSet(): dataSet = [ # 1 ['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], # 2 ['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'], # 3 ['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], # 4 ['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', '好瓜'], # 5 ['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', '好瓜'], # 6 ['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '好瓜'], # 7 ['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', '好瓜'], # 8 ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', '好瓜'], # ---------------------------------------------------- # 9 ['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'], # 10 ['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', '坏瓜'], # 11 ['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', '坏瓜'], # 12 ['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', '坏瓜'], # 13 ['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', '坏瓜'], # 14 ['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', '坏瓜'], # 15 ['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', '坏瓜'], # 16 ['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', '坏瓜'], # 17 ['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', '坏瓜'] ] # 特征值列表 labels = ['色泽', '根蒂', '敲击', '纹理', '脐部', '触感','好坏'] dataset=pd.DataFrame(data=dataSet,columns=labels) return dataset
3,训练及预测
这里预测使用训练数据,可以看到精度却不咋样,个人认为这跟样本太小、使用了修正(修正在大样本下的影响较小)及属性并非相互独立有关
dataset=createDataSet() p_xc,pc,num_class,features,labels=naive_bayes(dataset) value=dataset[dataset.columns[:-1]].values result=predicts(value,p_xc,pc,num_class,features,labels) real=dataset[dataset.columns[-1]].values df=pd.DataFrame([[result[i]==real[i] for i in range(len(result))]]) # 精度 0.8235294117647058 df.iloc[0].sum()/df.iloc[0].count()
二、连续值
1,贝叶斯方法
def normal_distribution(mean,var,x): return np.power(np.e,-(x-mean)*(x-mean)/(2*var))/np.sqrt(2*np.pi*var) # 连续值处理,假设数据服从正态分布,如上函数所示 def naive_bayes_2(X_train,y_train): '''data:pandas.DataFrame''' labels=list(set(y_train)) # 类别数 num_class=len(labels) data=pd.DataFrame(X_train,columns=['l1','l2','l3','l4']) data['label']=y_train N=len(y_train) # 均值和方差 means=[] vals=[] # c类样本概率 pc=np.empty(shape=(num_class,1)) # 对每一类求均值和方差 for i in range(num_class): df=data[data['label']==labels[i]] l=df.index.size pc[i]=l/N mean=[] val=[] # 各属性的均值和方差 for col in df.columns[:-1]: mean.append(df[col].mean()) val.append(df[col].var()) means.append(mean) vals.append(val) return means,vals,pc,labels # 预测多个样本 def predict_2(x_test,means,vals,pc,labels): num_class=len(labels) results=[] for x in x_test: result=[] for i in range(num_class): res=1. res*=pc[i][0] j=0 for mean,val in zip(means[i],vals[i]): res*=normal_distribution(mean,val,x[j]) j+=1 result.append(res) results.append(labels[result.index(max(result))]) return results
2,使用sklearn中iris数据集
from sklearn.datasets import load_iris data = load_iris() x=data['data'] y=data['target'] cols=data['target_names'] from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test=train_test_split(x,y,test_size=0.2,random_state=10)
3,训练及测试
means,vals,pc,labels=naive_bayes_2(X_train,y_train) results=predict_2(X_test,means,vals,pc,labels) from sklearn.metrics import accuracy_score # 精度100% accuracy_score(results,y_test)
三、总结
例举了2个例子,离散值的样本少,使用了修正,精度不咋样,连续值的精度100%,取得不错的效果,也说明各个类别下的各个特征基本符合正态分布。