选了个模式识别的课,好家伙老师上来就给任务写个手写数字识别的应用程序,还要有界面,语言不限,C、C++、Python、Java、Matlab都可以。Emmm……其实这个问题不算太大,毕竟就个人而言写程序这种实践类的事总是比学习概率论之类的理论分析稍微好受一些的QAQ……好了废话不多说,下面正式开始吧:
编程及运行环境: VS Code + Win10(Ubuntu也是可以跑的)
使用软件包: PIL + PyQt5 + numpy + pandas + matplotlib
无论对数学多头疼,不把理论上的东西整明白还是没法写程序的。
首先明确一下需要用到的定义(不急,咱用专业的数学语言 人话 自己的理解 来慢慢展开),有基础可酌情跳至第二节:
条件概率 P ( A ∣ B ) P(A|B) P(A∣B) ,就是指事件 B B B发生的情况下,事件 A A A发生的概率。公式可以直接在网上找到: P ( A ∣ B ) = P ( A ⋂ B ) P ( B ) P(A|B)=\frac {P(A \bigcap B)} {P(B)} P(A∣B)=P(B)P(A⋂B)
从文氏图上的效果来看, P ( A ∣ B ) P(A|B) P(A∣B) 就是将原本的考量范围缩小到了事件 B B B必然发生的范围内( P ( A ) P(A) P(A) 则是“原始”的考量范围下的推算结果)。
相对地,有 P ( A ∣ B ) P(A|B) P(A∣B) ,自然也就会有 P ( B ∣ A ) P(B|A) P(B∣A) 。很明显 P ( B ∣ A ) = P ( A ⋂ B ) P ( A ) P(B|A)=\frac {P(A \bigcap B)} {P(A)} P(B∣A)=P(A)P(A⋂B)
全概率公式,就是在条件概率的理论基础上,将原本的“原始”考量范围下的 P ( A ) P(A) P(A) ,转化为各种条件下事件 A A A发生概率的总和 : P ( A ) = ∑ i = 1 n P ( A ⋂ B i ) = ∑ i = 1 n P ( A ∣ B i ) P ( B i ) P(A)=\sum_{i=1}^nP(A \bigcap B_i)=\sum_{i=1}^nP(A |B_i)P(B_i) P(A)=i=1∑nP(A⋂Bi)=i=1∑nP(A∣Bi)P(Bi) 如下图所示,整个大圆为样本空间,中间阴影面积为事件 A A A。大圆的每一个切分代表每一个 B i B_i Bi 。
因为对于条件概率 P ( A ∣ B ) P(A|B) P(A∣B) 和 P ( B ∣ A ) P(B|A) P(B∣A) 来说, P ( A ⋂ B ) P(A \bigcap B) P(A⋂B) 是一样的,所以将 P ( A ∣ B ) P(A|B) P(A∣B) 和 P ( B ∣ A ) P(B|A) P(B∣A) 的条件概率公式联立,就可以得到贝叶斯公式: P ( A ∣ B ) = P ( A ⋂ B ) P ( B ) = P ( B ∣ A ) P ( A ) P ( B ) P(A|B)=\frac {P(A \bigcap B)} {P(B)}=\frac {P(B|A)P(A)} {P(B)} P(A∣B)=P(B)P(A⋂B)=P(B)P(B∣A)P(A) 对上式做些小改动,得到: P ( B i ∣ A ) = P ( A ∣ B i ) P ( B i ) P ( A ) P(B_i|A)=\frac {P(A|B_i)P(B_i)} {P(A)} P(Bi∣A)=P(A)P(A∣Bi)P(Bi) 如果再将 P ( A ) P(A) P(A) 用全概率公式表示,即可得到贝叶斯公式的另一形态: P ( B i ∣ A ) = P ( A ∣ B i ) P ( B i ) ∑ j = 1 n P ( A ∣ B j ) P ( B j ) P(B_i|A)=\frac {P(A|B_i)P(B_i)} {\sum_{j=1}^nP(A|B_j)P(B_j)} P(Bi∣A)=∑j=1nP(A∣Bj)P(Bj)P(A∣Bi)P(Bi) 其中, P ( B i ) P(B_i) P(Bi)被称为先验概率, P ( B i ∣ A ) P(B_i|A) P(Bi∣A) 是事件 A A A发生的条件下事件 B i B_i Bi发生的概率,也被称作后验概率。如果将事件 B i B_i Bi视作事件 A A A发生的原因之一( B 1 B_1 B1到 B n B_n Bn都是),那么上式所表达的含义就可以理解为:当事件 A A A发生时,事件 B i B_i Bi是其原因的概率。这也是贝叶斯方法实现手写数字识别的基本公式,即当得到一张图片(事件A)时,求取图片上的数字是 X(0~9分别对应事件 B 1 B_1 B1 ~ B 10 B_{10} B10)的概率,最后算得谁的概率最大,就将这张图片判断为谁的手写数字。
明确了贝叶斯公式应用于手写数字识别的理论基础,接下来要做的自然就是对纯理论的可行性进行优化,使之更加贴合实际,以便能顺利用于解决实际问题。朴素贝叶斯就是一种相对简单的优化方法,适合新手入门(所以博主选的也是朴素贝叶斯)。
在正式介绍朴素贝叶斯方法之前再给没接触过相关领域的人扫个盲:关于手写数字识别是一种监督模式识别,需要用到的模型训练集/测试集,里面是包含图片和标签两个东西的,一张图片对应一个标签。所谓标签,就是用来明确告知 训练对象/测试对象 其对应图片的数字的。所以,在对模型进行训练的时候,实际上是要同时输入图片和标签的(测试的时候,标签则是用来检验识别结果,用于计算模型识别的准确率的)。那么,正式开始吧:
首先确定求取概率对象。目标是计算出所有的 P ( B i ∣ A ) P(B_i|A) P(Bi∣A) ,然后找出最大值。那么,根据前面提到的公式可以知道,我们需要通过训练数据集计算出所有的先验概率 P ( B i ) P(B_i) P(Bi) 和条件概率 P ( A ∣ B i ) P(A|B_i) P(A∣Bi) 。
如果用 X X X表示图片集合, Y Y Y表示标签集合,则训练数据集就可以表示为 T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . ( x n , y n ) } T=\lbrace (x_1,y_1),(x_2,y_2),...(x_n,y_n) \rbrace T={(x1,y1),(x2,y2),...(xn,yn)} 其中 x 1 , x 2 , . . . , x n ∈ X x_1,x_2,...,x_n \in X x1,x2,...,xn∈X, y 1 , y 2 , . . . y n ∈ Y y_1,y_2,...y_n \in Y y1,y2,...yn∈Y,对每一个 x i ∈ X x_i \in X xi∈X,都有 x i = { x i 1 , x i 2 , . . . , x i n } x_i= \lbrace x_i^1,x_i^2,...,x_i^n \rbrace xi={xi1,xi2,...,xin}(意为第i个样本的第n个特征)。很明显对于训练数据集, P ( X , Y ) P(X,Y) P(X,Y)是独立同分布的,所以有 P ( X ∣ Y ) = P ( X , Y ) P ( Y ) P(X|Y)=\frac {P(X,Y)}{P(Y)} P(X∣Y)=P(Y)P(X,Y) 。 P ( Y = c k ) , k = 0 , 1 , . . . , 9 P(Y=c_k),k=0,1,...,9 P(Y=ck),k=0,1,...,9 P ( X = x ∣ Y = c k ) = P ( X 1 = x 1 , X 2 = x 2 , . . . , X n = x n ∣ Y = c k ) P(X=x|Y=c_k)=P(X^1=x^1,X^2=x^2,...,X^n=x^n|Y=c_k) P(X=x∣Y=ck)=P(X1=x1,X2=x2,...,Xn=xn∣Y=ck) 上面两式即是要求的先验概率与条件概率。其中,先验概率 P ( Y ) P(Y) P(Y) 好解决,可以自行给出理论上的概率(10%),也可以结合训练集的实际情况,通过统计各类数字的占比得出。但是条件概率 P ( X ∣ Y ) P(X|Y) P(X∣Y) 却由于训练集的规模化而变得无法估计。
如果假设 P ( X ∣ Y ) P(X|Y) P(X∣Y) 的条件概率分布是特征条件独立的话,就可以将其表示为: P ( X = x ∣ Y = c k ) = ∏ j = 1 n P ( X j = x j ∣ Y = c k ) P(X=x|Y=c_k)=\prod_{j=1}^nP(X^j=x^j|Y=c_k) P(X=x∣Y=ck)=j=1∏nP(Xj=xj∣Y=ck)
PS:如果看到这里有点懵的话,博主在这里简单地说明一下:对于任意输入的数字,取 28 × 28 28\times28 28×28的图片,如果将所有的像素点都视作其特征,那么就会有 28 × 28 = 784 28\times28=784 28×28=784个特征,而每一个特征都对应一个像素点的取值(像素值)。在朴素贝叶斯的假设条件下,这张图片是 “ 1 1 1”的概率 就是每一个特征 是 “ 1 1 1” 的特征的概率 的乘算。
这样就以牺牲一定准确性的代价(实际上特征的出现并非是独立的而是有一定关联的),极大地简化了 P ( X ∣ Y ) P(X|Y) P(X∣Y) 的求取过程,这就是朴素贝叶斯的“朴素”之处(不跟你玩什么花里胡哨的,怎么算简单就怎么来)。
如此,后验概率 P ( Y ∣ X ) P(Y|X) P(Y∣X) 的算式就可以写成: P ( Y = c k ∣ X = x ) = P ( Y = c k ) ∏ j P ( X j = x j ∣ Y = c k ) ∑ k P ( Y = c k ) ∏ j P ( X j = x j ∣ Y = c k ) P(Y=c_k|X=x)=\frac{P(Y=c_k)\prod_jP(X^j=x^j|Y=c_k)}{\sum_kP(Y=c_k)\prod_jP(X^j=x^j|Y=c_k)} P(Y=ck∣X=x)=∑kP(Y=ck)∏jP(Xj=xj∣Y=ck)P(Y=ck)∏jP(Xj=xj∣Y=ck) 再把公分母去掉,即可得到朴素贝叶斯分类器的最简形式: y = f ( x ) = argmax c k P ( Y = c k ) ∏ j P ( X j = x j ∣ Y = c k ) y=f(x)={\underset {c_k}{\operatorname {argmax} }}\,P(Y=c_k)\prod_jP(X^j=x^j|Y=c_k) y=f(x)=ckargmaxP(Y=ck)j∏P(Xj=xj∣Y=ck) 就差最后一步了!最后利用极大似然估计来估计相应的 先验概率 P ( Y ) P(Y) P(Y) 和条件概率 P ( X ∣ Y ) P(X|Y) P(X∣Y) : P ( Y = c k ) = ∑ i I ( y i = c k ) N , k = 0 , 1 , . . . , 9 P(Y = c_k)=\frac{\sum_i{I(y_i=c_k)}}{N},k=0,1,...,9 P(Y=ck)=N∑iI(yi=ck),k=0,1,...,9 P ( X j = a j l ∣ Y = c k ) = ∑ i I ( x i j = a j l ∣ y i = c k ) ∑ i I ( y i = c k ) , l = 0 , 1 P(X^j=a_{jl}|Y=c_k)=\frac{\sum_i{I(x_i^j=a_{jl}|y_i=c_k)}}{\sum_i{I(y_i=c_k)}},l=0,1 P(Xj=ajl∣Y=ck)=∑iI(yi=ck)∑iI(xij=ajl∣yi=ck),l=0,1 其中, I I I 为指示函数, l l l 在上式中代表二值化后的像素点取值情况,而 j j j 则表示给定的图片 x i x_i xi 对应的第 j j j 个特征(像素点)。
PS:看懂了当然最好,不过看懵了也没关系,博主在这里继续将这些“意义不明”的公式翻译成人话。简单来说还是通过统计的方式,得出 0 、 1 、 2 、 . . . 、 9 0、1、2、...、9 0、1、2、...、9 这10个数字对应到 784 784 784 个特征,也就是像素点的概率分布。比如说对于第1个特征,有两种取值(后续直接将图片二值化,这样就只有0和1两种取值了),统计取0的次数中把图片归到0~9这十类的次数占比,就得到了对应的10个概率,同理统计取1的次数也能出来10个概率,第2个特征的每种取值也对应10个概率…统计完成,极大似然估计到此为止,之后对于任意输入的图片( 28 × 28 28 \times 28 28×28),784个特征值都是已知的,接下来就根据后验概率的简化算式,按着之前统计出来的概率分布进行乘算,最后取最大概率对应的数字作为识别到的数字。
首先是程序的运行流程设计。因为数据集的预处理被单独拉出来安排了,所以这里的流程图实际上是完成了数据预处理部分以后的,因此第一步就是直接从文件里读取数据了。读完数据以后就是训练和测试,完了以后就可以自己写数字检验识别效果啦(关键环节后面都会提及)。
首先从网站上下载MNIST数据集
可以看出在 train-images.idx3-ubyte 中,第一个数为 32 位的整数(魔数,图片类型的数),第二个数为32位的整数(图片的个数),第三和第四个也是 32 位的整数(分别代表图片的行数和列数),接下来的都是一个字节的无符号数(即像素,值域为0~255),因此,我们只需要依次获取魔数和图片的个数,然后获取图片的长和宽,最后逐个按照图片大小的像素读取就可以得到一张张的图片内容了。标签数据集及测试数据集的的数据读取都是一样的原理。这里分享一下 piao 来的python处理脚本:
from PIL import Image
import struct
import numpy as np
import os
from PIL import Image
def read_image(filename):
f = open(filename, 'rb')
index = 0
buf = f.read()
f.close()
# 开始读取 魔数、图片数目、图片行数、列数
magic, images, rows, columns = struct.unpack_from('>IIII', buf, index)
index += struct.calcsize('>IIII')
for i in range(images):
# 逐个读取图片,每个图片字节数为 行数X列数
image = Image.new('L', (columns, rows))
for x in range(rows):
for y in range(columns):
# 读取并填充图片的像素值,每个像素值为一个字节
image.putpixel((y, x), int(struct.unpack_from('>B', buf, index)[0]))
index += struct.calcsize('>B')
print('save ' + str(i) + 'image')
image.save('这里设置图片存储路径' + str(i) + '.png')
def read_label(filename, saveFilename):
f = open(filename, 'rb')
index = 0
buf = f.read()
f.close()
# 开始读取 魔数及标签数目
magic, labels = struct.unpack_from('>II', buf, index)
index += struct.calcsize('>II')
labelArr = [0] * labels
for x in range(labels):
# 一个标签一个字节
labelArr[x] = int(struct.unpack_from('>B', buf, index)[0])
index += struct.calcsize('>B')
save = open(saveFilename, 'w')
save.write(','.join([str(x) for x in labelArr]))
save.write('\n')
save.close()
print('save labels success')
return labelArr
N = 28
def get_train_set():
f = open('这里设置data.csv文件的路径', 'wb')
category = read_label('这里设置train-labels.idx1-ubyte文件的路径(gz包解压出来就是)', '这里设置label.txt文件的路径,和图片要在同一目录下')
file_names = os.listdir(r"图片与label.txt的所在目录", )
train_picture = np.zeros([len(file_names)-1, N ** 2 + 1])
# 遍历文件,转为向量存储
for file in range(len(file_names)-1):
train_im = Image.open('图片存储路径 + %d.png' % (file))
img_num = np.array(train_im)
rows, cols = img_num.shape
for i in range(rows):
for j in range(cols):
if img_num[i, j] < 100:
img_num[i, j] = 0
else:
img_num[i, j] = 1
train_picture[file, 0:N ** 2] = img_num.reshape(N ** 2)
train_picture[file, N ** 2] = category[file]
print("完成处理第%d张图片" % (file+1))
np.savetxt(f,train_picture,fmt='%d',delimiter=',', newline='\n', header='', footer='')
f.close()
return train_picture
def get_test_set():
f = open('test.csv') #和上面一样设置路径
category = read_label('t10k-labels.idx1-ubyte', 'label.txt') #设置路径
file_names = os.listdir(r"", ) #设置路径
test_picture = np.zeros([len(file_names)-1, N ** 2 + 1])
# 遍历文件,转为向量存储
for file in range(len(file_names)-1):
train_im = Image.open('%d.png' % (file)) #设置路径
img_num = np.array(train_im)
rows, cols = img_num.shape
for i in range(rows):
for j in range(cols):
if img_num[i, j] < 100:
img_num[i, j] = 0
else:
img_num[i, j] = 1
test_picture[file, 0:N ** 2] = img_num.reshape(N ** 2)
test_picture[file, N ** 2] = category[file]
print("完成处理第%d张图片" % (file+1))
np.savetxt(f,test_picture,fmt='%d',delimiter=',', newline='\n', header='', footer='')
f.close()
return test_picture
if __name__ == '__main__':
#训练集图像和标签解压
read_image('train-images.idx3-ubyte')
read_label('train-labels.idx1-ubyte', 'label.txt')
#遍历文件,转为向量存储(图像矩阵+标签整合)
get_train_set()
#这俩由于保存图片操作没有拉出接口需要分开运行,不能一起跑。当然也可以自己修改程序,增加形参
#我是懒得改了
#测试集图像和标签解压
#read_image('t10k-images.idx3-ubyte')
#read_label('t10k-labels.idx1-ubyte', 'label.txt')
#遍历文件,转为向量存储(图像矩阵+标签整合)
#get_test_set()
程序跑完,就可以得到60000张图片和1个标签txt文本(电脑性能偏弱的谨慎打开,不然可能会卡成翔,虽然总共才十几兆但是架不住它文件数量多)以及一个 60000 × 785 60000\times785 60000×785的Excel表格(将每张图片二值化以后转化为了行向量,最后给每张图片都打上标签,这个csv文件同样请谨慎打开):
Excel表格中,最后一列非零值就是各行向量的标签了。
测试集的处理也是类似的。
def Train(trainset, train_labels):
global prior_probability,conditional_probability
prior_probability = np.zeros(class_num) #先验概率
conditional_probability = np.zeros((class_num,feature_len,2)) #条件概率
#计算先验概率及条件概率
for i in range(len(train_labels)):
img = trainset[i] #图像二值化,但由于之前已经做好了图像数据预处理,这里直接抓取对应的行向量
label = train_labels[i]
prior_probability[label] += 1
for j in range(feature_len):
conditional_probability[label][j][img[j]] += 1
for i in range(class_num):
for j in range(feature_len):
#经过二值化后像素点只有 0 和 1 两种取值
pix_0 = conditional_probability[i][j][0]
pix_1 = conditional_probability[i][j][1]
#计算对应像素点取0、1的条件概率
probability_0 = (float(pix_0) / float(pix_0 + pix_1)) * 1000000 + 1 #float型保留到小数点后六位,这里放大一百万倍尽量使小数位计算数据不丢失,再加1防止出现概率为0的情况
probability_1 = (float(pix_1) / float(pix_0 + pix_1)) * 1000000 + 1
conditional_probability[i][j][0] = probability_0
conditional_probability[i][j][1] = probability_1
return prior_probability, conditional_probability
#计算概率
def caculate_probability(img, label):
probability = int(prior_probability[label])
for i in range(len(img)):
probability *= int(conditional_probability[label][i][img[i]])
return probability
def Test_Predict(testset, test_labels):
pre_right = np.zeros(10) # 每一类预测正确的数量
act_numbers = np.zeros(10) # 每一类的样本实际数量(P)
predict = [] # 预测结果
accuracy = [] # 预测准确率
# 统计测试数据中各类数字的数量
for i in test_labels:
act_numbers[i] += 1
for i in range(len(testset)):
max_label = 0
max_probability = caculate_probability(testset[i], 0)
for j in range(1, 10):
probability = caculate_probability(testset[i], j)
if max_probability < probability:
max_label = j
max_probability = probability
predict.append(max_label)
if max_label == test_labels[i]:
pre_right[test_labels[i]] += 1
if (i+1) % 500 == 0:
accuracy.append(float(pre_right.sum())/(i+1))
print(pre_right)
print('-------------------------------------------------------------------')
print(act_numbers)
print('-------------------------------------------------------------------')
print(pre_right/act_numbers)
plt.figure()
plt.plot(np.arange(1,21),accuracy,marker="o",markersize=8)
plt.xlabel("x -- 1:500")
plt.title("Predict Accuracy Rate")
plt.show()
return accuracy, np.array(predict)
以下是博主利用PyQt5设计的程序界面:
以下是测试结果(博主没有加入计时模块,暂时没有这方面的需求,如有需要可自行添加):
事实上博主一直都觉得这种总体准确率指标最多给一个大概的直观感受,实际上还不如分别统计各类的准确率指标有价值……
这三个数组分别对应每一类预测正确数、每一类实际样本数、每一类的识别准确率。这里对分类器性能的展示更加直观一些:
def pretreatment(ima, threshold):
ima = ima.convert('L') #转化为灰度图像
im = np.array(ima) #转化为二维数组
for i in range(im.shape[0]): #转化为二值矩阵
for j in range(im.shape[1]):
if im[i, j] > threshold:
im[i, j] = 1
else:
im[i, j] = 0
return im
前面提及贝叶斯公式: P ( B i ∣ A ) = P ( A ∣ B i ) P ( B i ) ∑ j = 1 n P ( A ∣ B j ) P ( B j ) P(B_i|A)=\frac {P(A|B_i)P(B_i)} {\sum_{j=1}^nP(A|B_j)P(B_j)} P(Bi∣A)=∑j=1nP(A∣Bj)P(Bj)P(A∣Bi)P(Bi) 对于上式,实现手写数字识别的思路就是:忽略公有分母简化计算、求先验概率 P ( B i ) P(B_i) P(Bi) 和 条件概率 P ( A ∣ B i ) P(A|B_i) P(A∣Bi) 。而正态分布和朴素贝叶斯的区别就在于它们对条件概率的求法不同。朴素贝叶斯是假设特征条件独立,然后直接拿特征的统计概率进行乘算,从本质上来说,训练就是制表,预测就是查表;正态分布则是假设 P ( A ∣ B i ) P(A|B_i) P(A∣Bi) 的概率密度函数服从多元正态分布,这样就得到了真正意义上的判别函数(参数待定),然后根据训练集数据估计出各个参数后装载到判别函数中,完成分类器的设计。
先简化一下判别函数( x x x为 n 维的输入样本, w i w_i wi为 x x x 的类别): g i ( x ) = P ( x ∣ w i ) P ( w i ) g_i(x)=P(x|w_i)P(w_i) gi(x)=P(x∣wi)P(wi) 假设 P ( x ∣ w i ) P(x|w_i) P(x∣wi)~ N ( μ i , Σ i ) , i = 1 , . . . , c N(\mu_i,\Sigma_i),i=1,...,c N(μi,Σi),i=1,...,c ,则 g i ( x ) = P ( w i ) ( 2 π ) n 2 ∣ Σ i ∣ 1 2 e x p { − 1 2 ( x − μ i ) T Σ i − 1 ( x − μ i ) } g_i(x)=\frac{P(w_i)}{(2\pi)^\frac{n}{2}|\Sigma_i|^\frac{1}{2}}exp\lbrace -\frac{1}{2}(x-\mu_i)^T\Sigma_i^{-1}(x-\mu_i) \rbrace gi(x)=(2π)2n∣Σi∣21P(wi)exp{−21(x−μi)TΣi−1(x−μi)} 取对数得到 g i ( x ) = − 1 2 ( x − μ i ) T Σ i − 1 ( x − μ i ) − n 2 l n 2 π − 1 2 l n ∣ Σ i ∣ + l n P ( w i ) g_i(x)=-\frac{1}{2}(x-\mu_i)^T\Sigma_i^{-1}(x-\mu_i)-\frac{n}{2}ln2\pi-\frac{1}{2}ln|\Sigma_i|+lnP(w_i) gi(x)=−21(x−μi)TΣi−1(x−μi)−2nln2π−21ln∣Σi∣+lnP(wi) 上式中, μ i = E [ X i ] \mu_i=E[X_i] μi=E[Xi] 为第 i i i 类样本集 X i X_i Xi 的 n 维均值向量 ; Σ i = E [ ( X i − μ i ) ( X i − μ i ) T ] \Sigma_i=E[(X_i-\mu_i)(X_i-\mu_i)^T] Σi=E[(Xi−μi)(Xi−μi)T] 为第 i i i 类样本集 X i X_i Xi 的 n x n 维协方差矩阵; Σ i − 1 \Sigma_i^{-1} Σi−1 是 Σ i \Sigma_i Σi 的逆矩阵; ∣ Σ i ∣ |\Sigma_i| ∣Σi∣ 是 Σ i \Sigma_i Σi 的行列式。
对 Σ i \Sigma_i Σi 的情况进行分类讨论:
1. Σ i = σ 2 I \Sigma_i=\sigma^2I Σi=σ2I,即各类的协方差矩阵都相等( I I I 为单位矩阵),类内各特征间相互独立,且方差 σ 2 \sigma^2 σ2 相等,则 Σ i − 1 = 1 σ 2 I , ∣ Σ ∣ = σ 2 n \Sigma_i^{-1}=\frac{1}{\sigma^2}I,|\Sigma|=\sigma^{2n} Σi−1=σ21I,∣Σ∣=σ2n
代入 g i ( x ) g_i(x) gi(x),忽略掉与类别 i i i 无关的项,得 g i ( x ) = − ( x − μ i ) T ( x − μ i ) 2 σ 2 + l n P ( w i ) g_i(x)=-\frac{(x-\mu_i)^T(x-\mu_i)}{2\sigma^2}+lnP(w_i) gi(x)=−2σ2(x−μi)T(x−μi)+lnP(wi) 如果各类先验概率 P ( w i ) P(w_i) P(wi) 是相等的,那么上式的后一项就可以忽略,决策函数就能简化为 g i ( x ) = − ∣ ∣ x − μ i ∣ ∣ 2 2 σ 2 g_i(x)=-\frac{||x-\mu_i||^2}{2\sigma^2} gi(x)=−2σ2∣∣x−μi∣∣2 由于上式的分子对应的几何意义为 x x x 到第 i i i 类的均值向量 μ i \mu_i μi 的欧氏距离的平方,所以此时的决策规则变为 argmin i ∣ ∣ x − μ i ∣ ∣ 2 {\underset {i}{\operatorname {argmin} }}\,||x-\mu_i||^2 iargmin∣∣x−μi∣∣2 如果各类先验概率 P ( w i ) P(w_i) P(wi) 不全相等,则可将 ( x − μ i ) T ( x − μ i ) (x-\mu_i)^T(x-\mu_i) (x−μi)T(x−μi) 展开后去除与类别 i i i 无关的项 x T x x^Tx xTx,得到 g i ( x ) = − 1 2 σ 2 ( − 2 μ i T x + μ i T μ i ) + l n P ( w i ) = ( 1 σ 2 μ i ) T x + [ − 1 2 σ 2 μ i T μ i + l n P ( w i ) ] g_i(x)=-\frac{1}{2\sigma^2}(-2\mu_i^Tx+\mu_i^T\mu_i)+lnP(w_i)=(\frac{1}{\sigma^2}\mu_i)^Tx+[-\frac{1}{2\sigma^2}\mu_i^T\mu_i+lnP(w_i)] gi(x)=−2σ21(−2μiTx+μiTμi)+lnP(wi)=(σ21μi)Tx+[−2σ21μiTμi+lnP(wi)] 此时的决策规则为 argmax i g i ( x ) {\underset {i}{\operatorname {argmax} }}\,g_i(x) iargmaxgi(x)
PS:对于欧氏距离取 a r g m i n argmin argmin 的情况,考虑到前面有一个 “ − - −” 号,其实和 a r g m a x argmax argmax 的情况也没差,嫌麻烦统一记为 a r g m a x argmax argmax 也是可以的。对于下面的情况同样适用。
2. Σ i = Σ \Sigma_i=\Sigma Σi=Σ,即各类的协方差矩阵都相等, Σ 1 = Σ 2 = . . . = Σ n = Σ \Sigma_1=\Sigma_2=...=\Sigma_n=\Sigma Σ1=Σ2=...=Σn=Σ,此时 Σ \Sigma Σ 与 i i i 无关,则 g i ( x ) = − 1 2 ( x − μ i ) T Σ − 1 ( x − μ i ) + l n P ( w i ) g_i(x)=-\frac{1}{2}(x-\mu_i)^T\Sigma^{-1}(x-\mu_i)+lnP(w_i) gi(x)=−21(x−μi)TΣ−1(x−μi)+lnP(wi) 如果各类先验概率 P ( w i ) P(w_i) P(wi) 相等,同理可将决策函数简化为 g i ( x ) = − 1 2 ( x − μ i ) T Σ − 1 ( x − μ i ) = − γ 2 2 g_i(x)=-\frac{1}{2}(x-\mu_i)^T\Sigma^{-1}(x-\mu_i)=-\frac{\gamma^2}{2} gi(x)=−21(x−μi)TΣ−1(x−μi)=−2γ2 和之前的欧氏距离类似,这里的 γ 2 \gamma^2 γ2 是马氏距离的平方,决策规则同样可以转化为取最小值。
如果各类先验概率 P ( w i ) P(w_i) P(wi) 不全相等,则将 ( x − μ i ) T Σ − 1 ( x − μ i ) (x-\mu_i)^T\Sigma^{-1}(x-\mu_i) (x−μi)TΣ−1(x−μi) 展开后去除与类别 i i i 无关的项 x T Σ − 1 x x^T\Sigma^{-1}x xTΣ−1x,得到线性形式的判别函数 g i ( x ) = ( Σ − 1 μ i ) T x + [ − 1 2 μ i T Σ − 1 μ i + l n P ( w i ) ] g_i(x)=(\Sigma^{-1}\mu_i)^Tx+[-\frac{1}{2}\mu_i^T\Sigma^{-1}\mu_i+lnP(w_i)] gi(x)=(Σ−1μi)Tx+[−21μiTΣ−1μi+lnP(wi)] 3.一般情况,即各类的协方差矩阵不相等,此时情况相对复杂一些,因为只有第二项与 i i i 无关可以忽略。 g i ( x ) g_i(x) gi(x) 此时为 x x x 的二次型,这里不做讨论(不会真有老师要考查这种情况吧,不会吧不会吧)。
拿到简化的判别函数以后,再用最大似然估计方法确定相关参数:
直接上公式: μ i ^ = 1 N ∑ j = 1 N x j \hat{\mu_i}=\frac{1}{N}\sum_{j=1}^Nx_j μi^=N1j=1∑Nxj Σ i ^ = 1 N ∑ j = 1 N ( x j − μ i ^ ) ( x j − μ i ^ ) T \hat{\Sigma_i}=\frac{1}{N}\sum_{j=1}^N(x_j-\hat{\mu_i})(x_j-\hat{\mu_i})^T Σi^=N1j=1∑N(xj−μi^)(xj−μi^)T 上式中, N N N 为第 i i i 类的样本总数, x j x_j xj 为第 i i i 类的第 j j j 个样本。
小结一下,正态分布概率模型下贝叶斯手写数字分类器的实现步骤: