zoukankan      html  css  js  c++  java
  • 手写朴素贝叶斯(naive_bayes)分类算法

    朴素贝叶斯假设各属性间相互独立,直接从已有样本中计算各种概率,以贝叶斯方程推导出预测样本的分类。

    为了处理预测时样本的(类别,属性值)对未在训练样本出现,从而导致概率为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%,取得不错的效果,也说明各个类别下的各个特征基本符合正态分布。

  • 相关阅读:
    UIView添加手势
    UIView常见属性设置汇总
    关于页面传值
    有关segue的简介
    alloc
    如何定义静态方法
    一座小城
    清明
    开通博客
    iOS学习之界面间传值
  • 原文地址:https://www.cnblogs.com/lunge-blog/p/11618970.html
Copyright © 2011-2022 走看看