朴素贝叶斯应用之在手写数字识别的实践
文章目录
- 引言
- 朴素贝叶斯
- 朴素贝叶斯法的学习与分类
- 朴素贝叶斯法的参数估计
- 极大似然估计
- 贝叶斯估计
- 实战朴素贝叶斯
- 图片预处理
- 图片数据化
- 模型训练
- 模型预测
- 其他说明
- Reference
引言
我们都见过或者用过的一个东西就是输入法的手写键盘,如下面的动图所示,那么输入法是如何识别出我们手写的字迹是什么字的呢?这是一个对人而言非常简单(前提是你写的字体不过于潦草),但是对于程序而言,可能就没有那么简单了,这次我就从一个更简单的角度来试一下,如何去识别手写的数字。 1
朴素贝叶斯
我们在前面的文章中提到了贝叶斯定理,公式表示如下
P(y∣x)=P(x,y)p(x)=P(x∣y)P(y)P(x)=P(x∣y)P(y)∑y∈YP(x∣y)P(y)P(y|x)= \frac{P(x,y)}{p(x)} = \frac {P(x|y)P(y)} {P(x)} = \frac {P(x|y)P(y)} {\sum_{y \in Y} P(x|y)P(y)}P(y∣x)=p(x)P(x,y)=P(x)P(x∣y)P(y)=∑y∈YP(x∣y)P(y)P(x∣y)P(y)
并从中得出了这样的结论:
贝叶斯定理可以精确的说明在已知新证据 xxx 的情况下,我们应该改变多少关于 yyy 的信念,这个等式中,P(y)P(y)P(y) 是新证据 xxx 出现之前我对于 yyy 的先验信念。 P(x∣y)P(x|y)P(x∣y) 是在 yyy 确定的前提下,得到证据 xxx 的可能性。 P(y∣x)P(y|x)P(y∣x) 是在考虑新证据后我对于 yyy 的后验信念。
而朴素贝叶斯(Naive Bayes)法是基于贝叶斯定理与特征条件独立假设的分类方法。对于给定的训练数据集,首先基于特征条件独立假设学习输入/输出的联合概率分布,然后基于学习到模型,对于新的输入 xxx ,利用贝叶斯定理求出后验概率最大的输出 yyy ,朴素贝叶斯法实现简单,学习和预测的效率也非常高,是一种很常用的分类方法。
朴素贝叶斯法的学习与分类
朴素贝叶斯法如何用于分类工作呢?这里用更加数学的语言,可以表述如下:
首先设输入空间 $\mathcal{X} \subseteq R^n $ 为 n 维向量的集合,输出空间为类标记集合 Y={c1,c2,⋯,ck}\mathcal{Y} = \{c_1, c_2, \cdots , c_k\}Y={c1,c2,⋯,ck},其中输入为特征向量 x∈Xx \in \mathcal{X}x∈X,输出为类标记(class label) y∈Yy \in \mathcal{Y}y∈Y .则 $ P(X, Y) $ 是 XXX 和 YYY 的联合概率分布.
其次训练数据集表示为:
T={(x1,y1),(x2,y2),⋯,(xN,yN)}T = \{(x_1, y_1), (x_2, y_2), \cdots, (x_N, y_N) \} T={(x1,y1),(x2,y2),⋯,(xN,yN)}
由 $P(X, Y) $ 独立同分布产生.
最后由朴素贝叶斯法通过上述训练数据集学习到联合概率分布 $P(X, Y) $ ,由条件概率公式可以知道只需要学习到如下的先验概率分布:
P(Y=ck),k=1,2,⋯,KP(Y = c_k), k=1,2,\cdots, K P(Y=ck),k=1,2,⋯,K
以及条件概率分布:
P(X=x∣Y=ck)=P(X(1)=x(1),⋯,X(n)=x(n)∣Y=ck)P(X = x \mid Y = c_k) = P(X^{(1)} = x^{(1)}, \cdots, X^{(n)} = x^{(n)} \mid Y = c_k) P(X=x∣Y=ck)=P(X(1)=x(1),⋯,X(n)=x(n)∣Y=ck)
二者相乘即可得到联合概率分布。
但是,事实上,从我们学到的条件概率分布的知识可以知道,$P(X = x \mid Y = c_k) $ 是一个具有指数级数量的参数,一旦训练集稍微具有规模,那么这个概率分布都是无法估计的。而机器学习中,训练集的规模化显然是不可避免的。
那么现在我们明显已经知道通过学习上述两个概率分布,可以得到我们想要的联合概率分布,从而得到训练模型,然而其中条件概率分布计算的不可行性却让我们止步不前。
在这样一个尴尬的时候,朴素贝叶斯法站出来为我们解围了,它提出了一个非常强的假设,就是假设条件概率分布是特征条件独立的,因此朴素贝叶斯,也就朴素在这里:他的假设太强,强到改变了理论上计算的规则。
具体的,条件独立性假设是:
P(X=x∣Y=ck)=P(X(1)=x(1),⋯,X(n)=x(n)∣Y=ck)=∏j=1nP(X(j)=x(j)∣Y=ck)(1)P(X = x \mid Y = c_k) = P(X^{(1)} = x^{(1)}, \cdots, X^{(n)} = x^{(n)} \mid Y = c_k) \\ = \prod^{n}_{j=1}P(X^{(j)} = x^{(j)} \mid Y=c_k) \tag{1} P(X=x∣Y=ck)=P(X(1)=x(1),⋯,X(n)=x(n)∣Y=ck)=j=1∏nP(X(j)=x(j)∣Y=ck)(1)
这个假设等于是说用于分类的特征在类别确定的条件下都是条件独立的,这一假设使得条件概率的计算变得异常简单,但是显然也牺牲了一定的准确率,因为一般情况下特征并不都是独立的而是有关联的。
通过上述学习到模型,就可以计算后验概率分布 P(Y=ck∣X=x)P(Y = c_k \mid X = x)P(Y=ck∣X=x),将后验概率最大的类别作为要预测的样本 xxx 的输出。后验概率的计算依据贝叶斯定理进行:
P(Y=ck∣X=x)=P(X=x∣Y=ck)P(Y=ck)∑kP(X=x∣Y=ck)P(Y=ck)(2)P(Y = c_k \mid X = x) = \frac{P(X=x\mid Y=c_k) P(Y=c_k)}{\sum_k P(X=x\mid Y=c_k) P(Y=c_k)} \tag{2} P(Y=ck∣X=x)=∑kP(X=x∣Y=ck)P(Y=ck)P(X=x∣Y=ck)P(Y=ck)(2)
将 2 式代入 1 式,得到:
P(Y=ck∣X=x)=P(Y=ck)∏jP(X(j)=x(j)∣Y=ck)∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)P(Y = c_k \mid X = x) = \frac{P(Y=c_k) \prod_{j}P(X^{(j)} = x^{(j)} \mid Y=c_k)}{\sum_k P(Y=c_k) \prod_{j}P(X^{(j)} = x^{(j)} \mid Y=c_k)} P(Y=ck∣X=x)=∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)P(Y=ck)∏jP(X(j)=x(j)∣Y=ck)
所以,贝叶斯分类器可以表示为:
y=f(x)=argmaxckP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)y=f(x)= \arg \max_{c_k} \frac{P(Y=c_k) \prod_{j}P(X^{(j)} = x^{(j)} \mid Y=c_k)}{\sum_k P(Y=c_k) \prod_{j}P(X^{(j)} = x^{(j)} \mid Y=c_k)} y=f(x)=argckmax∑kP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)P(Y=ck)∏jP(X(j)=x(j)∣Y=ck)
上式因为分母对所有 ckc_kck 都是相同的,所以:
y=argmaxckP(Y=ck)∏jP(X(j)=x(j)∣Y=ck)y = \arg \max_{c_k} P(Y=c_k) \prod_{j}P(X^{(j)} = x^{(j)} \mid Y=c_k) y=argckmaxP(Y=ck)j∏P(X(j)=x(j)∣Y=ck)
这就是朴素贝叶斯分类器的最简形式,从这个式子可以看出来,朴素贝叶斯分类器本质上是根据先验概率及条件概率求最大化的后验概率,从而推断类别。
朴素贝叶斯法的参数估计
极大似然估计
在上面所说的朴素贝叶斯法中,训练学习意味着估计先验概率 P(Y=ck)P(Y=c_k)P(Y=ck) 和条件概率 P(X(j)=x(j)∣Y=ck)P(X^{(j)} = x^{(j)} \mid Y=c_k)P(X(j)=x(j)∣Y=ck),这可以应用极大似然估计来估计相应的概率,其中先验概率的极大似然估计是:
P(Y=ck)=∑i=1NI(yi=ck)N,k=1,2,⋯,KP(Y=c_k) = \frac{\sum^{N}_{i=1} I(y_i=c_k)}{N} ,k = 1,2,\cdots,K P(Y=ck)=N∑i=1NI(yi=ck),k=1,2,⋯,K
设第 j 个特征 xjx^{j}xj 可能的取值的集合为 ,则条件概率的极大似然估计为:
P(X(j)=ajl∣Y=ck)=∑i=1NI(xi(j)=ajl,yi=ck)∑i=1NI(yi=ck)(3)P(X^{(j)} = a_{jl} \mid Y=c_k) = \frac{\sum^{N}_{i=1} I (x_i^{(j)} = a_{jl}, y_i = c_k)}{\sum^{N}_{i=1}I (y_i=c_k)} \tag{3} P(X(j)=ajl∣Y=ck)=∑i=1NI(yi=ck)∑i=1NI(xi(j)=ajl,yi=ck)(3)
贝叶斯估计
因为用极大似然估计可能会出现所要估计的概率值为 0 的情况,这就会导致后续的计算出现错误,使得分类出现偏差,解决这个问题的方法就是使用贝叶斯估计,即是平滑处理估计结果,条件概率的贝叶斯估计是:
P(X(j)=ajl∣Y=ck)=∑i=1NI(xi(j)=ajl,yi=ck)+λ∑i=1NI(yi=ck)+Sjλ(3)P(X^{(j)} = a_{jl} \mid Y=c_k) = \frac{\sum^{N}_{i=1} I (x_i^{(j)} = a_{jl}, y_i = c_k) + \lambda}{\sum^{N}_{i=1}I (y_i=c_k) + S_j\lambda} \tag{3} P(X(j)=ajl∣Y=ck)=∑i=1NI(yi=ck)+Sjλ∑i=1NI(xi(j)=ajl,yi=ck)+λ(3)
其中 λ≥0\lambda \geq 0λ≥0,等价于给随机变量的各个取值的频数加上一个正数 λ>0\lambda > 0λ>0 常取 λ=1\lambda = 1λ=1,称为拉普拉斯平滑。
实战朴素贝叶斯
有了上面的基础,那么处理手写数字识别上面就变得很简单了,整个处理的步骤分为:
- 图片预处理
- 图片数据化
- 模型训练
- 模型测试
图片预处理
这里我们取现成的 mnist 数据集 2 ,首先数据集下载下来的是特殊格式的压缩包,其中训练集的图片包和标签包格式如下:
可以看出在 train-images.idx3-ubyte 中,第一个数为 32 位的整数(魔数,图片类型的数),第二个数为32位的整数(图片的个数),第三和第四个也是 32 位的整数(分别代表图片的行数和列数),接下来的都是一个字节的无符号数(即像素,值域为0~255),因此,我们只需要依次获取魔数和图片的个数,然后获取图片的长和宽,最后逐个按照图片大小的像素读取就可以得到一张张的图片内容了。标签数据集及测试数据集的的数据读取都是一样的原理。
读取训练图片集并将图片存储成图片,读取标签集的代码实现代码如下:
# -*- coding: utf-8 -*-from PIL import Image
import structdef read_image(filename):f = open(filename, 'rb')index = 0buf = 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('train/' + str(i) + '.png')def read_label(filename, saveFilename):f = open(filename, 'rb')index = 0buf = f.read()f.close()# 开始读取 魔数及标签数目magic, labels = struct.unpack_from('>II', buf, index)index += struct.calcsize('>II')labelArr = [0] * labelsfor 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 labelArrif __name__ == '__main__':read_image('train-images.idx3-ubyte')read_label('train-labels.idx1-ubyte', 'train/label.txt')
读取到图片之后,存储的结果如下:
图片数据化
为了将图片变成更易为计算机接受的形式,这里需要将图片二值化,即是只包含0和1的图片表现形式,即是下面这样的矩阵,其方法是超过一定像素值的点标记为 1,否则为 0:
可以隐约看出一个 5 的形状。
这里为了方便继续处理图片特征,将这个 28 *28 的矩阵进行 reshape 操作,将一幅图展开为行向量。因此整个训练集()60000张图片)就变成了一个大小为 60000×784 的矩阵,之后尽量进行矩阵操作。
同时为了方便标记,将每行向量表示的数字写在最后一列,因此整个矩阵的大小为 60000×785。
并为了后续数据操作的方便,将这个矩阵存在本地的CSV文件中,代码及结果如下:
N = 28
def get_train_set():f = open('data.csv', 'wb')category = MR.read_label('train-labels.idx1-ubyte', 'train/label.txt')file_names = os.listdir(r"./train/", )train_picture = np.zeros([len(file_names)-1, N ** 2 + 1])# 遍历文件,转为向量存储for file in range(len(file_names)-1):img_num = io.imread('./train/%d.png' % (file))rows, cols = img_num.shapefor i in range(rows):for j in range(cols):if img_num[i, j] < 100:img_num[i, j] = 0else:img_num[i, j] = 1train_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()time_e = time.time()print('process data train cost ', time_e - time_0, ' seconds', '\n')return train_picture
结果为,红色框内即是该行向量的标签:
处理测试数据也是一样的原理。
模型训练
从第一部分我们可以了解到,要求后验概率的本质是求在类别为 j 的条件下,样本 x 的第 i 个特征出现的条件概率的,将所有特征的概率与该类别的先验概率作连乘即得到后验概率。
因此,重点是计算类别的先验概率和在类别为 j 的条件下,样本 x 的第 i 个特征出现的条件概率,这也就是我们要训练的模型,代码如下:
def Train():conditional_probability = np.zeros((class_num, feature_len, 2)) # 条件概率# 计算先验概率及条件概率for i in range(len(labels)):img = data_map[i, :]label = labels[i]for j in range(feature_len):conditional_probability[label][j][img[j]] += 1# 将概率归到[1.1001]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像素点对应的条件概率probalility_0 = (float(pix_0)/float(pix_0+pix_1))*1000 + 1probalility_1 = (float(pix_1)/float(pix_0+pix_1))*1000 + 1conditional_probability[i][j][0] = probalility_0conditional_probability[i][j][1] = probalility_1return conditional_probability
其中部分说明如下:
由于 Python 浮点数精度的原因,784个浮点数联乘后结果变为 Inf,而 Python 中 int 可以无限相乘的,因此可以利用python int 数据类型的特性对先验概率与条件概率进行一些改造。
先验概率: 由于先验概率分母都是 N,因此不用除于 N,直接用分子即可。
条件概率: 条件概率公式如上说明济代码所示,我们得到概率后再乘以1000000 (最小的可能性为1/784,同时需要尽量保存概率精度,这里保存到白万分之一,因此乘以1000000),将概率映射到[0,1000000]中,但是为防止出现概率值为0的情况,人为的加上1,使概率映射到[1,1000001]中。3
模型预测
模型预测的方法就是根据上面训练出来的朴素贝叶斯模型,对任一个新的样本 x ,分别计算它是类别 j 的条件下的后验概率,取最大后验概率的类别即可。
代码如下:
# 计算概率
def calculate_probability(img, label):probability = int(prior_probability[label])for i in range(len(img)):probability *= int(conditional_probability[label][i][img[i]])return probabilitydef Predict(testset, test_labels):predict = []accuracy = []right = 0rows, cols = testset.shapefor row in range(rows):# 图像二值化img = testset[row, :]max_label = 0max_probability = calculate_probability(img, 0)for j in range(1, 10):probability = calculate_probability(img, j)if max_probability < probability:max_label = jmax_probability = probabilitypredict.append(max_label)if max_label == test_labels[row]:right += 1if (row+1) % 500 == 0:accuracy.append(float(right)/(row+1))return float(right)/len(test_labels), np.array(predict), accuracy
整个代码运行的结果如下:
准确率曲线如下:
其他说明
- 本次实验将图像展开、对单个像素独立判断,损失了图像的空间信息,而这种空间信息正是我们人眼识别图像的关键。所以最终的准确率受此影响。
- 测试数据的准确率为84.47%,还是非常不错的(共有10个分类,理论上随机猜测的准确率只有10%)。能使准确率达到84.15%的原因主要是:(1) MNIST数据集被预先处理过。通过上面的示例图片可以看出,MNIST中的图片较为纯净,没有噪声干扰,非常清晰。所以实验中可以直接进行二值化。(2) MNIST数据集中,图片中的数字总是在中心位置,大小合适,比较饱满。这一点保留了部分的空间信息。
- 本例中的二值化过程是直接将超过一定像素阀值值置1,如果采用更合理的阈值(比如取当前图片最大像素阀值为 100 )进行二值化操作,进行训练、测试过程,最终的准确率可能会提升。 4
以上就是这次朴素贝叶斯法的理论及实战啦。
下次是 K 近邻算法在手写识别上的应用。
Reference
李航《统计学习方法》第四章 ↩︎
THE MNIST DATABASE ↩︎
李航《统计学习方on实现朴素贝叶斯分类器(MNIST数据集)](https://blog.csdn.net/wds2006sdo/article/details/51967839) ↩︎
用朴素贝叶斯法对MNIST数据集分类 ↩︎
朴素贝叶斯应用之在手写数字识别的实践相关推荐
- 飞桨图像分类入门——多种网络模型在手写数字识别的应用
什么是图像分类 图像分类是基于深度学习的cv分类任务. 核心是从给定的分类集合中给图像分配一个标签的任务. 实际上我们的任务是分析一个输入图像并返回一个图像分类的标签. 标签总是来自预定义的可能类别集 ...
- 机器学习笔记——从手写数字识别开始
文章目录 前言 关于这篇博客(预计八月下旬全部完成) 关于项目实现 监督学习 ANN全连接神经网络的实现 1.总述 2.初始化 3.传播及损失 4.反向传播 决策树以及随机森林的实现 1.总述 2.单 ...
- RNN的手写数字识别
RNN和LSTM的原理可以看这篇文章 以下是RNN在手写数字识别上的简单应用 import tensorflow as tf from tensorflow.examples.tutorials.mn ...
- MindSpore手写数字识别初体验,深度学习也没那么神秘嘛
摘要:想了解深度学习却又无从下手,不如从手写数字识别模型训练开始吧! 深度学习作为机器学习分支之一,应用日益广泛.语音识别.自动机器翻译.即时视觉翻译.刷脸支付.人脸考勤--不知不觉,深度学习已经渗入 ...
- 现在论文用手写还是用计算机写,毕业论文计算机手写数字识别技术完整版.docx...
HEN system office room [HEN16H-HENS2AHENS8Q8-HENH1688] HEN system office room [HEN16H-HENS2AHENS8Q8- ...
- 完整代码及解析!!手写数字识别系统(手写数字测试识别 + pytoch实现 + 完整代码及解析)
基于深度学习的手写数字识别系统 一.实验目的 1.任选实验环境及深度学习框架,实现手写数字识别系统: 2.掌握所采用的深度血迹框架构建方式. 二.实验理论基础 1.MNIST数据集 MNI ...
- 03_深度学习实现手写数字识别(python)
本次项目采用了多种模型进行测试,并尝试策略来提升模型的泛化能力,最终取得了99.67%的准确率,并采用pyqt5来制作可视化GUI界面进行呈现.具体代码已经开源. 代码详情见附录 1简介 早在1998 ...
- MNIST手写数字识别 —— ResNet-经典卷积神经网络
了解ResNet18的网络结构:掌握模型的保存和加载方法:掌握批量测试图片的方法. 结合图像分类任务,使用典型的图像分类网络ResNet18,实现手写数字识别. ResNet作为经典的图像分类网络有其 ...
- 基于Paddle的计算机视觉入门教程——第7讲 实战:手写数字识别
B站教程地址 https://www.bilibili.com/video/BV18b4y1J7a6/ 任务介绍 手写数字识别是计算机视觉的一个经典项目,因为手写数字的随机性,使用传统的计算机视觉技术 ...
最新文章
- java与jquery的选择器区别_JQuery选择器
- 【Linux】【Services】【SaaS】Docker+kubernetes(11. 构建复杂的高可用网络)
- io.circe_如何使用Circe(Un)在Akka HTTP中封送JSON
- Ubuntu16.04下安装Sublime Sublime Text3
- 前端综合学习笔记---异步、ES6/7、Module、Promise同步 vs 异步
- 基于单片机智能电子密码锁设计(毕业设计资料)
- OC 5028B欧创芯原装,开关降压型大功率恒流驱动芯片
- 从Docker镜像创建Singularity镜像(SIF文件)
- 2022-2028全球与中国紫外线点固化系统市场现状及未来发展趋势
- Python脚本实现WIFI网络的扫描、连接和断开
- 宝莱坞机器人 西瓜_《宝莱坞机器人之恋》电影完整版免费在线观看_2010西瓜影音 - 辛集电影院...
- 计算机毕业设计java+ssm水果商城管理系统(源码+系统+mysql数据库+Lw文档)
- java类图_java UML类图的使用-UML基础-火龙果软件工程
- ERP编制物料清单 金蝶
- MYSQL基础之浅聊 变量
- Android Studio一直停留在MyApplication:syncing(解决方案)
- php pdo 支持mysql表类型_全新的PDO数据库操作类php版(仅适用Mysql)
- VTK笔记——点(point)和向量(vector)投影到平面(plane)
- Android studio使用.9图片报错
- 安霸ARM S2L板子烧写
热门文章
- 现在快手流量怎么样?如何增加流量?
- java练习题---前五章
- bash: ./deviceQuery.cpp: 权限不够;bash: ./deviceQuery: 没有那个文件或目录
- 手机远程连接window界面或ubuntu界面
- 企业上云业务系统上云是怎么回事儿?
- 91sp.vido.ws index.php_Vidows
- 项目启动报错No appropriate protocol (protocol is disabled or cipher suites are inappropriate) 解决办法
- 【Java】LeetCode 174. 地下城游戏 —— 困难
- MYSQL 基础篇 | 02-MYSQL基础应用
- 在线电子书阅读微信小程序 毕业设计(2)分类