1. 问题描述 (2 points)
利用概率密度表进行概率密度计算,并利用Bayes公式对字符进行识别。
2. 问题的本质和分析 (10 points)
基于贝叶斯方法的手写字符识别是一种统计学习方法,它的本质是通过概率统计来模拟和推断手写字符的识别过程。这种方法利用贝叶斯定理,将观测到的手写字符数据与不同字符的概率模型相结合,以确定哪个字符最有可能与给定的手写输入匹配。
公式:
3. 解决问题的思路,方法,思路的可性行和预期结果表现(避免截图和word打入公式)(10 points)
数据收集和预处理:收集手写字符图像数据,清理和标准化图像。
特征提取:从图像中提取有用的特征,如像素值或轮廓信息。
建立模型:使用贝叶斯方法建立字符类别的概率模型,包括特征分布和先验概率。
训练模型:使用训练数据估计模型参数。
分类:对新的手写字符图像,计算每个字符类别的后验概率,选择概率最高的类别作为识别结果。
评估性能:使用测试数据集来评估模型的准确性。
改进和部署:根据性能评估结果改进模型,然后部署到实际应用中。
4. 解决问题中遇到的难点 (3 points)
数据质量不佳:手写字符数据可能受到噪音、模糊或变形的影响,导致识别难度增加。
特征提取:选择合适的特征和提取方法对于识别性能至关重要。不同字符可能需要不同的特征表示,而这通常需要领域知识。
数据不平衡:某些字符类别可能具有较少的样本,导致模型训练不均匀,影响性能。
参数调整:调整贝叶斯方法的参数,如平滑参数或深度学习模型的超参数,需要耗费时间和经验。
解释性:有时需要解释模型的决策过程,特别是在一些领域,如司法或医疗应用中。
5.与算法对应的关键代码的实现进行文字解释和注释,
from sklearn import datasets
import numpy as np
import gzip
def LoadMnistImages(filename):
with gzip.open(filename, 'rb') as f:
data = np.frombuffer(f.read(), np.uint8, offset=16)
data = data.reshape(-1, 28 * 28) / 255.0
return data
def LoadMnistLabels(filename):
with gzip.open(filename, 'rb') as f:
data = np.frombuffer(f.read(), np.uint8, offset=8)
return data
train_x = LoadMnistImages('train.gz')
train_y = LoadMnistLabels('train_label.gz')
test_x = LoadMnistImages('test.gz')
test_y = LoadMnistLabels('test_label.gz')
pz = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pxz = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
grouped_image = [[], [], [], [], [], [], [], [], [], []]
for y in range(10):
num = 0
for idx, i in enumerate(train_y):
if i == y:
num = num + 1
grouped_image[y].append(train_x[idx])
pz[y] = num / len(test_y)
accuracies = []
correct = 0
for idx, test in enumerate(test_x):
for idx2, group in enumerate(grouped_image):
for i in range(784):
for g in group:
if abs(g[i] - test[i]) < 5: # 阈值
pxz[idx2] = pxz[idx2] + 1
pxz[idx2] = pxz[idx2] / len(group)
max = 0
for i in range(10):
if pxz[i] + pz[i] > pxz[max] + pz[max]:
max = i
if max == test_y[idx]:
correct = correct + 1
accuracy = correct / len(test_x)
print('accuracy: ' + str(accuracy))
5. 对结果进行分析,提出改进想法的动机,依据、算法设计和预期结果(10 points)
这个代码存在如下问题:比较图片的像素值时,认为只有像素值完全相同才时相同,这样会使准确率有所下降,如下:
if g[i] == test[i]:
pxz[idx2] = pxz[idx2] + 1
为了解决这个问题,我设定了阈值比较的方法:
if abs(g[i] - test[i]) < 5: # 阈值
pxz[idx2] = pxz[idx2] + 1
通过for循环,找到一个最佳的阈值为5,使得准确率最高。
6.撰写格式
2.6.1 一致性的建议
整个项目的开发中,函数和对象的命名保持一致性,尤其是一个模块或者一个函数中的一致性更为重要。命名应遵循驼峰命名规则。
2.6.2 代码逻辑块
2.6.2.1 缩进
众所周知,python是通过缩进来进行代码布局的,使用pycharm可以配置几个空格来代表一个tab,从而来布局代码的缩进。
2.6.2.2 不混用Tab键和空格
Python 里有一句话叫“以用空格为荣,以用Tab键为耻”。但全用空格时确实很麻烦。因此,这里不限定用 Tab 键还是空格。但记住:不可混用!
print("Hello world") #Tab换行
2.6.2.3 规范行的最大长度
如果代码的某一行很长,我们需要换行折叠观看代码,这会影响代码的阅读。因此,对顺序排放的大块代码,推荐长度在72个字节以内,另外推荐使用反斜杠续行。
if (first_result >=0.5 and second_result >=0.5) or \ #反斜杠续行 (second_result >=0.5 and last_result>=0.5) :
last_result = max(first_result,second_result,last_result)
2.6.2.4 用代码块体现程序逻辑
通常,我们用两行分割顶层函数和类的定义,
用一行分割类成员方法的定义。
在一个函数内使用空格时请注意谨慎使用于一个逻辑段。
class A:
a = 42
b = list(a + i for i in range(10))
2.6.3 注释规范
注释是指对一部分代码的文字解释。注释内容必须跟代码保持一致,当你想修改代码时,建议优先修改注释。
注释必须是一个完整的句子。如果注释只有一句话,建议省略句末的句号。
注释块由一个或多个完整句子构成,每个句子应该以句号结尾。
注释阅读统一用英文或中文。使用统一注释格式有助于良好的习惯和团队的进步。
x = 1 # 给x赋值为1
2.6.3.1 注释块
注释块通常应用于跟随着一些(或者全部)代码并和这些代码有着相同的缩进层次。注释块中每行以’#'和一个空格开始(除非他是注释内的缩进文本)。
注释块内的段落以仅含单个’#'的行分割。
注释块上下方最好有一空行包围(或上方两行下方一行,对一个新函数定义段的注释)。
# print(a)
# print(b)
#
# print(c)
2.6.3.2 行内注释
行内注释应该至少用两个空格和语句分开,他们应该以’#'和单个空格开始。
Dict = dict(zip(map(str,range(10)), range(10))) #创建长度为10的字典
图1-45行内注释
如果语意易理解,那么行内注释是不必要的,事实上是应该被去掉的,不要这样写:
x = x + 1 #increment x
图1-46 注释错误示例
2.6.4 命名规范
命名规范在编写代码中起到很重要的作用,使用命名规范可以更加直观地了解代码所代表的含义,让代码具有有可读性、易写性与明义性。骆驼式命名法(Camel-Case)一词来自 Perl 语言中普遍使用的大小写混合格式,又称驼峰式命名法,是电脑程式编写时的一套命名规则(惯例),并无绝对与强制,为的是增加识别和可读性。驼峰式命名法是指混合使用大小写字母来构成变量和函数的名字。
驼峰命名法(CamelCase)
驼峰法(即帕斯卡命名法)单词首字母均大写。
class MyNaiveBayesClassifier:
匈牙利命名法(HN-case)
变量名可依次由属性、类型、描述组成。
iUserName #i为int类型缩写,UserName是描述。
蛇形命名法
函数通常全由小写字母和下划线组成,小写单词间用下划线连接
def use_naivebayes_as_classifier():
2.6.4.1 命名风格
命名风格应该遵循驼峰命名规则。
class MyNaiveBayesClassifier:
此外,还需要注意:
1)单下划线作为前导,如:_single_begin,这是弱的内部使用标识,例如我们在M文件中定义如下变量:
_a = 1
但是在使用"from M import *"的时候变量不会被导入;
print(_a) # NameError: name '_a' is not defined
2)单下划线作为结尾的,如:single_end_,这一般用于避免跟 python 关键词冲突;
class_ = "This is not a class, just a variable."
3)双下划线前导,如:__double_begin,类私有名;
class Test:
def __init__(self):
self.__private_var = 10
4) 双下划线前导+结尾,如:double_begin_and_end,特殊对象或属性,存在于用户控制的命名空间中,如:int,__import__等。有时可以被用户定义,用于触发某个特殊行为,如运算符重载。
class Test:
def __str__(self):
return "This is a Test class."
2.6.4.2 应避免的对象命名
为了避免文字显示的差异,永远不要用:
1)小写字母“l”(小写的"L");
2)大写字母“O”;
3)大写字母“I”(读音 eye);
作为单字符的变量名,因为不利于跟数字“0”和”1“很好的区分开来。
当要用小写字母“l”时,请用大写字母“L”代替。
变量名必须是一个有效的标识符;
变量名不用使用Python中的保留字;
慎用小写字面l和大写字母O;
应选用有意义的词作为变量;
尽量小写, 如有多个单词,用下划线隔开即采用蛇形命名法(snake_case)命名。
i = 0
if i > 0:
number = 0
school_name = "Tsinhua"
常量采用全大写,如多个单词,用下划线隔开。
PAI = 3.14
MAX_CONNECTION = 100
CONNECTION_TIMEOUT = 500
2.6.4.3 模块命名
模块尽量使用小写命名,首字母保持小写,尽量不要用下划线(除非多个单词,且数量不多的情况)(加入下划线可改善可读性)。因为模块名被映射到文件名,有些文件系统大写不敏感并且截短长名字,模块名被选为相当短是重要的—这在 Unix 上不是问题,但当代码传到 Mac 或 Windows 上就可能是个问题了。
import decoder # 正确的模块名
import html_parser # 正确的模块名
import Decoder # 不推荐的模块名
2.6.4.4 类命名
类名总是使用首字母大写式驼峰,例如:ClassName(),命名单词串的约定。
Class ClassName(): #定义一个类
2.6.4.5 全局变量命名
这个的约定跟用于函数的约定差不多。那些模块,应该在那些不想被导入的全局变量(还有内部函数和类)前加一个下划线。例如:GLOBAL_VAR
global GLOBAL_VAR #定义一个全局变量GLOBAL_VAR
2.6.4.6 函数命名
函数命名函数名应该为小写、动宾短语,可能用下划线风格单词以增加可读性。如:open_file()表示打开一个文件,
def open_file(): #定义一个打开文件的函数
如果需要表明能快速打开一个文件,命名还可以为
def fast_open_file(): #定义一个快速打开文件的函数
class MyNaiveBayesClassifier: # 驼峰命名法定义一个类
'''定义一个NaiveBayesClassifier的类
#一句话总结这个类的功能和作,文档字符串需要用三引号包括
Attributes:
class_probs: 类别先验概率(Numpy)
feature_probs: 特征条件概率(Numpy)
'''
def __init__(self):
self.class_probs = None # 初始化参数
self.feature_probs = None
def fit(self, X_train, y_train):
'''定义fit函数计算朴素贝叶斯的训练过程
Attributes:
X_train:训练使用的数据(Numpy)
y_train:训练使用的类别(Numpy)
'''
num_samples, num_features = X_train.shape #样本数量,特征数量
self.classes = np.unique(y_train) #类别数量
num_classes = len(self.classes)
self.class_probs = np.zeros(num_classes) # 计算类别先验概率P(z)
for i, c in enumerate(self.classes):
self.class_probs[i] = np.sum(y_train == c) / num_samples
# 计算特征条件概率P(x_1,x_2,…,x_784|z)
self.feature_probs = np.zeros((num_classes, num_features))
for i, c in enumerate(self.classes):
X_c = X_train[y_train == c]
self.feature_probs[i] = (np.sum(X_c, axis=0) + 1) / #反斜杠实现换行
(np.sum(X_c) + num_features)
def predict(self, X_test):
'''定义predict函数计算类别先验概率P(z|X)
Attributes:
X_test:测试使用的数据(Numpy)
return: 预测结果(Numpy)
'''
num_samples, _ = X_test.shape
num_classes, num_features = self.feature_probs.shape
y_pred = np.zeros(num_samples) # 初始化预测结果数组
for i in range(num_samples):
posteriors = np.zeros(num_classes)
for j in range(num_classes): # 计算后验概率
posteriors[j] = np.log(self.class_probs[j]) + /
np.sum(np.log(self.feature_probs[j]) * X_test[i])
y_pred[i] = np.argmax(posteriors)
return y_pred
按上述格式撰写报告,且满足撰写格式
最新发布