机器学习笔记(一)贝叶斯分类器(上)

机器学习笔记(一)贝叶斯分类器(上)

本文将介绍朴素贝叶斯分类器的基本原理,并将其应用到图像分类任务中。(数据集为字母大小写灰度图,大小为16×16,每种字母1016张,总共26×2×1016张图片,数据集下载链接:github仓库)

原理概述

贝叶斯公式描述了在已知P(X)发生的条件下,导致其发生的原因是 C i C_i Ci的概率,称为条件 X X X C i C_i Ci的后验概率, P ( C i ∣ X ) P(C_i |X) P(CiX) 即表示 X X X属于 C i C_i Ci类的概率。假设有 m m m个类,则分别求出在条件 X X X下, C i ( i = 1 , 2 , . . . . . . , m ) C_i (i=1,2,......,m) Ci(i=1,2,......,m) 的后验概率,最大后验概率对应的类别即为X所属的类别。
P ( C i │ X ) = P ( C i ) P ( X ∣ C i ) ( P ( X ) P(C_i│X) = \frac{P(C_i)P(X|C_i)}{(P(X)} P(CiX)=(P(X)P(Ci)P(XCi)
在分类任务中,每个数据样本用一个 n n n维特征向量表示: X = x 1 , x 2 , … … , x n X = {x_1, x_2,……, x_n} X=x1,x2,,xn。假设有 m m m个类: C 1 , C 2 , . . . . . . , C m C_1, C_2,......, C_m C1,C2,......,Cm,分离器依次假设 X X X属于 C i ( i = 1 , 2 , . . . . . . , m ) C_i (i=1,2,......,m) Ci(i=1,2,......,m),并求得各后验概率并找出最大后验概率对应的类。对于 P ( X ) , P ( C i ) P(X),P(C_i) P(X),P(Ci)一类直接分析数据得到的概率称为先验概率。如果 C i C_i Ci类的先验概率未知,一般设 C i C_i Ci的先验概率是相等的,即如果有 m m m个类, P ( C i ) = 1 m P(C_i) = \frac1m P(Ci)=m1,或者如果样本有三类 C 1 , C 2 , C 3 C_1, C_2, C_3 C1,C2,C3三类的训练样本数为 1 : 2 : 3 1:2:3 123,可以设置 P ( C i ) , i = 1 , 2 , 3 P(C_i),i=1,2,3 P(Ci)i=123 的值为 1 6 , 1 3 , 1 2 \frac16,\frac13,\frac12 613121 P ( X ) P(X) P(X)为归一化因子,对于所有类来说是一致的。 P ( X ∣ C i ) P(X|C_i) P(XCi)称为给的 C i C_i Ci X X X的似然度。如果训练样本各类数目均相等,则可将问题简化为求最大似然度的问题。

实现过程及完整代码

这里是数据预处理过程,包括将数据分割为训练数据和测试数据,删除非图片文件(.db文件),为图片打标签等

import os
import shutil
import zipfile
if not os.path.exists('work/recognise'):
    os.makedirs('work/recognise')
#os.chdir('../work')
extracting = zipfile.ZipFile('/home/aistudio/data/data32588/letters.zip')
extracting.extractall('work/')


folders = os.listdir('work/letters')
#文件夹重命名为字母
for folder in folders:
    i = int(folder[-3:])
    if i<=36:
        os.rename('work/letters/%s'%folder,'work/letters/%s'%chr(54+i))
    else:
        os.rename('work/letters/%s'%folder,'work/letters/%s'%chr(60+i))
print(os.listdir('work/letters'))

#图片重命名为数字
for path in os.listdir('work/letters'):
    path = 'work/letters/%s'%path
    count = 0
    for f in os.listdir(path):
        file_path = '%s%s'%(path,'/')
        if f[-3:]=='png':
            os.rename('%s%s'%(file_path,f),'%s%s%s'%(file_path,str(count),'.png'))
            count+=1
        else:
            os.remove('%s%s'%(file_path,f))
            print(f)
#重命名为数字


for folder in os.listdir('work/letters'):
    os.makedirs('work/recognise/%s' % folder)
    for i in range(916,1016):
        shutil.move('work/letters/%s/%s.png' % (folder,str(i)),'work/recognise/%s/%s.png' % (folder,str(i-916)))

以下为图像分类的实现过程

from PIL import Image,ImageOps
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import os
from sklearn.decomposition import PCA

大津法求阈值
大津法利用求最大类间方差,按照图像的灰度特性,将图片划分为前景和背景。最大类间方差对应的分割点即为阈值。
假设阈值为 T ( T = 0 , 1 , 2 , . . . . . . , 255 ) T(T=0,1,2,......,255) T(T=0,1,2,......255),图像大小为 M × N M×N M×N(这里为16×16), N 0 N_0 N0为小于 T T T的像素个数, μ 0 μ_0 μ0为小于T的像素值的均值, N 1 N_1 N1为大于 T T T的像素个数, μ 1 μ_1 μ1为大于 T T T的像素值的均值。
ω 0 = N 0 M × N ω_0 = \frac{N_0}{M×N} ω0=M×NN0 , ω 1 = N 1 M × N ω_1 = \frac{N_1}{M×N} ω1=M×NN1 ,其中 N 0 + N 1 = M × N N_0+ N_1=M×N N0+N1=M×N
μ = μ 0 × ω 0 + μ 1 × ω 1 μ = μ_0×ω_0 + μ_1×ω_1 μ=μ0×ω0+μ1×ω1 ,
g = ω 0 × ( μ 0 − μ ) 2 + ω 1 × ( μ 1 − μ ) 2 g = ω_0×(μ_0-μ)^2+ ω_1×(μ_1-μ)^2 g=ω0×(μ0μ)2+ω1×(μ1μ)2
遍历0-255,求出 g g g最大时对应的 T T T即为最佳阈值。

def myOtsu(img_array):
    height = 16
    width = 16
    size = height*width
    T = 0
    g_0 = 0
    for i in range(size+1):
        back = np.where(img_array>=i)[0]
        front = np.where(img_array<i)[0]
        front_cnt = len(front)
        back_cnt = size-front_cnt
        w_f = front_cnt/size
        w_b = back_cnt/size
        if len(img_array[front]) == 0:
            u_f = 0
        else:
            u_f = img_array[front].mean()
        if len(img_array[back]) == 0:
            u_b = 0
        else:
            u_b = img_array[back].mean()
        g = w_f*w_b*np.square(u_f-u_b)
        if g>g_0:
            g_0 = g
            T = i
    front = np.where(img_array<T)[0]
    back = np.where(img_array>=T)[0]
    img_array[front] = 255
    img_array[back] = 0
    return img_array,T

train_data()test_data()负责加载数据,将图片展开为256维的向量,使用大津法二值化图像,前景为0,后景为1。最后返回一个字典,键为字母(即标签),值为一个二维数组,行向量为一张图片的特征向量。

def train_data():
    data = {}
    for folder in os.listdir('work/letters'):
        data[folder]=[]
        file_list = os.listdir('work/letters/%s'%folder)
        for i in range(900):
            im = Image.open("work/letters/%s/%s.png" % (folder,str(i))).convert('L')
            im = im.resize((16, 16), Image.ANTIALIAS)               
            im = np.array(im).reshape(256).astype(np.float32)
            im = myOtsu(im)[0]/255
            data[folder].append(im)
    return data

def test_data():
    data={}
    for label in os.listdir('work/recognise'):
        imgs = []
        for i in range(100):
            im = Image.open("work/recognise/%s/%s.png" % (label,str(i))).convert('L')
            im = im.resize((16, 16), Image.ANTIALIAS)               
            im = np.array(im).reshape(256).astype(np.float32)
            im = myOtsu(im)[0]/255
            imgs.append(im)
        data[label]=imgs
    return data

计算分量的概率
这里需要利用拉普拉斯平滑,以防止某个分量概率为0导致其联合分布概率为0的情况。具体做法使假设该训练图片中该分量为0、1的图片至少有一张,最后求概率时除以(训练图片数+2)即可。

def cal_prob(data):
    prob = {}
    for label in data:
        cnt = np.ones(16*16)
        imgs = data[label]
        for img in imgs:
            ones = np.where(img==1)[0]
            cnt[ones]+=1
        prob[label] = cnt/(len(imgs)+2)
    return prob

分类过程中,假设在 C i C_i Ci条件下, C i C_i Ci类图片第k个分量为1的概率为 p k 1 p_{k1} pk1 p k 1 + p k 0 = 1 p_{k1}+p_{k0}=1 pk1+pk0=1。对于测试图片的特征向量 X = { x 1 , x 2 , … , x n } X = \{x_1, x_2,…, x_n\} X={x1,x2,,xn} P ( x k = a ) = p k a P(x_k=a)=p_{ka} P(xk=a)=pka。其中, a = 1 , 0 , k = 0 , 1 , … 255 a=1,0,k=0,1,…255 a=1,0,k=0,1,255。通过遍历每一种图片的概率模型,可求得测试图片的特征向量 X = { x 1 , x 2 , … , x n } X = \{x_1, x_2,…, x_n\} X={x1,x2,,xn}在给定 C i C_i Ci类下的似然度:
P ( X ∣ C i ) = ∏ k = 0 255 P ( x k = a ) P(X|C_i) = ∏_{k=0}^{255}P(x_k=a) P(XCi)=k=0255P(xk=a)
遍历各个类的概率模型,求出该测试图片的最大似然度对应的类即为分类结果。

class bayes_classifier:
    def __init__(self,data):
        self.__probs = cal_prob(data)
    def classfy(self,img):
        results = {}
        for label in self.__probs:
            res = 1
            for i in range(len(img)):
                if img[i] == 1:
                    res=res*self.__probs[label][i]
                else:
                    res=res*(1-self.__probs[label][i])
            results[label] = res
        res_label = max(results, key=results.get)
        return res_label
classifier = bayes_classifier(train_data())
test = test_data()
correct_cnt ={}
for label in test:
    correct=0
    for i in test[label]:
        res_label = classifier.classfy(i)
        if res_label == label:
            correct+=1
    correct_cnt[label]=correct
    print('%s:%s' % (label,str(correct/100)))
print('correct_prob:%s'% str(sum(correct_cnt.values())/(26*2*100)))

测试数据中每种字母100张图片,共26×2×100张测试图片。以下为输出结果,分别为每个字母的识别准确率和整体的识别准确率:(最高为 K : 91%,最低为 I : 29%,整体为 66.03846153846153%)

H:0.65
z:0.58
h:0.7
G:0.65
c:0.56
Q:0.52
n:0.77
D:0.69
P:0.82
V:0.52
f:0.72
Y:0.73
w:0.71
y:0.68
U:0.57
q:0.78
u:0.66
m:0.78
o:0.52
J:0.47
F:0.68
Z:0.56
M:0.71
A:0.75
R:0.74
S:0.52
K:0.91
C:0.68
a:0.7
r:0.68
L:0.74
l:0.72
v:0.47
T:0.72
g:0.69
j:0.78
I:0.29
k:0.79
N:0.84
d:0.8
E:0.77
X:0.59
B:0.77
e:0.7
x:0.58
p:0.66
t:0.68
W:0.41
b:0.73
i:0.71
O:0.43
s:0.46
correct_prob:0.6603846153846153

你可能感兴趣的:(机器学习)