机器学习笔记(一)贝叶斯分类器(上)
本文将介绍朴素贝叶斯分类器的基本原理,并将其应用到图像分类任务中。(数据集为字母大小写灰度图,大小为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(Ci∣X) 即表示
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(Ci│X)=(P(X)P(Ci)P(X∣Ci)
在分类任务中,每个数据样本用一个
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
1:2:3,可以设置
P
(
C
i
)
,
i
=
1
,
2
,
3
P(C_i),i=1,2,3
P(Ci),i=1,2,3 的值为
1
6
,
1
3
,
1
2
\frac16,\frac13,\frac12
61,31,21。
P
(
X
)
P(X)
P(X)为归一化因子,对于所有类来说是一致的。
P
(
X
∣
C
i
)
P(X|C_i)
P(X∣Ci)称为给的
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(X∣Ci)=∏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