import os
import numpy as np
from scipy.io import loadmat
from sklearn.model_selection import train_test_split
np.set_printoptions(threshold=np.inf)
# 数据准备
minist_path = r".\datasets\MNIST.mat"
lung_path = r".\datasets\lung.mat"
yale_path = r".\datasets\Yale.mat"
# 存储路径
PATH_MINIST = './data/minist/'
PATH_LUNG = './data/lung/'
PATH_YALE = './data/yale/'
# 加载数据
def create_data(path):
data = loadmat(path)
data_x = data["X"]
data_y = data["Y"][:, 0]
data_y -= 1
Data = np.array(data_x)
Label = np.array(data_y)
return Data, Label
def save_data(path, save_path):
X, y = create_data(path)
train_data, test_data, train_label, test_label = train_test_split(X, y, test_size=0.2, random_state=233)
if not os.path.exists(save_path):
os.makedirs(save_path)
np.savetxt(save_path + 'train_data.txt', train_data)
np.savetxt(save_path + 'train_label.txt', train_label)
np.savetxt(save_path + 'test_data.txt', test_data)
np.savetxt(save_path + 'test_label.txt', test_label)
if __name__ == '__main__':
save_data(yale_path, PATH_YALE)
save_data(lung_path, PATH_LUNG)
save_data(minist_path, PATH_MINIST)
print('完成')
P ( 类 别 ∣ 特 征 1 , 特 征 2 , . . . ) = P ( 特 征 1 , 特 征 2 , . . . ∣ 类 别 ) ∗ P ( 类 别 ) P ( 特 征 1 , 特 征 2... ) = P ( 特 征 1 ∣ 类 别 ) ∗ P ( 特 征 2 ∣ 类 别 ) ∗ . . . P ( 特 征 1 ) ∗ P ( 特 征 1 ) ∗ . . . P(类别|特征1,特征2,...) = \frac{P(特征1,特征2,...|类别) * P(类别)}{P(特征1,特征2...)}=\frac{P(特征1|类别)*P(特征2|类别)*...}{P(特征1)*P(特征1)*...} P(类别∣特征1,特征2,...)=P(特征1,特征2...)P(特征1,特征2,...∣类别)∗P(类别)=P(特征1)∗P(特征1)∗...P(特征1∣类别)∗P(特征2∣类别)∗...
for j in range(1+np.max(test_label)):
来做循环,而不是for j in range(labelnum)
,由于样本量太少,导致在测试集里面没有某一类的样本,循环次数少了一次,导致精确度一直只有0.12。import numpy as np
from data_handle import load_data, PATH_MINIST, PATH_LUNG, PATH_YALE
np.set_printoptions(threshold=np.inf)
# p(类别|特征) = p(特征|类别)*p(类别)/p(特征)
def normalize(data): ##将图片像素二值化
m, n = np.array(data).shape
h = (np.max(data) - np.min(data)) / 2
for i in range(m):
for j in range(n):
if data[i, j] > h:
data[i, j] = 1
else:
data[i, j] = 0
return data
def CalProb(train_data, train_label):
# p(y=j):p(类别) P(xk|y=j):p(特征|类别)
# 根据训练集 算条件概率P(xk|y=j) 和先验概率 P(y=j) 注意这两种概率可能会为0 后面无法计算 因此一定要进行Laplace平滑 参见李航P51
# num: 2400, dimsnum: 784
num, dimsnum = train_data.shape
# labelnum: 标签类别数
labelnum = len(set(train_label))
# pyj:1x10的零向量
pyj = np.zeros(labelnum)
# pyjk1:10x784的零向量
pyjk1 = np.zeros((labelnum, dimsnum))
# num: 2400
for i in range(num):
# 计算出每种标签的个数 ---> p(y=j):p(类别)
label = train_label[i]
# 需要laplace平滑 这里是真实个数
pyj[label] = pyj[label] + 1
# dimsnum: 784
for j in range(dimsnum):
# 因为会出现条件概率为0的情况 log无法计算 需要laplace平滑 ##算 Pj k = 1
# 此处计算的是所有label为1的数的个数
pyjk1[label][j] += train_data[i][j]
# print('pyj个数:', pyj)
# 条件概率 需要Laplace平滑 分母要加上xk的种类数 这里只能取0 1像素
# P y = j && xk = 1的概率 经验主义用频率去估计概率
# 此时pyj为10种类别各自的数目
# ni 为特征n的标签数
pyjk1 = (pyjk1.T + 1) / (pyj + 2)
# P y = j 的概率 先验概率 需要 Laplace平滑 分母要加上y的标签种类数
pyj = (pyj + 1) / (num + labelnum)
# pk1, #, pyjk1
return pyj, pyjk1
def CalTestProb_xk_yj(xk, pyjxk1): # 计算条件概率 P(xk|y=j)的概率的log
# xk=0时,概率为1 - pyjxk1 ,xk=1时,概率为pyjxk1
return xk * np.log(pyjxk1) + (1 - xk) * np.log(1 - pyjxk1)
# test 这块计算 应该可以优化
def test(test_data, test_label, pyjk1, pyj): # 测试
# num : 600 , dimsnum : 784
num, dimsnum = test_data.shape
labelnum = len(set(test_label))
acc = 0
for i in range(num):
testdata = test_data[i]
# 第i个样本属于j类的概率
p_yj_xi = np.log(pyj)
# 计算xi 属于 第j个类别的概率
for j in range(1+np.max(test_label)):
for k in range(dimsnum):
xk = testdata[k] # x^i的第j个像素 或者说是 维度
# print(pyjk1[j][k])
p_yj_xi[j] += CalTestProb_xk_yj(xk, pyjk1[j][k])
# p_yj_xi
# np.argmax : 取出a中元素最大值所对应的索引,此时最大值位6,其对应的位置索引值为4,(索引值默认从0开始)
p_y_xi = np.argmax(p_yj_xi)
acc += (p_y_xi == test_label[i])
# print('real is: ', test_label[i], ' predict is: ', p_y_xi)
print('Test accuracy is: ', acc / num)
def main(path):
train_data, test_data, train_label, test_label = load_data(path)
train_data = normalize(train_data)
test_data = normalize(test_data)
pyj, pyjk1 = CalProb(train_data, train_label)
test(test_data, test_label, pyjk1.T, pyj)
if __name__ == "__main__":
print('minist:')
main(PATH_MINIST)
print('---------')
print('lung')
main(PATH_LUNG)
print('---------')
print('yale')
main(PATH_YALE)
minist:
Test accuracy is: 0.8266666666666667
---------
lung
Test accuracy is: 0.926829268292683
---------
yale
Test accuracy is: 0.42424242424242425
P ( 特 征 j ∣ 类 别 m , 特 征 i ) = D ( 特 征 j , 特 征 i , 类 别 m ) + 1 D ( 类 别 m , 特 征 i ) + N j P(特征j|类别m,特征i) = \frac{ D(特征j,特征i,类别m) + 1}{D(类别m,特征i)+N_{j}} P(特征j∣类别m,特征i)=D(类别m,特征i)+NjD(特征j,特征i,类别m)+1
数据/方法 | minist | lung | yale |
---|---|---|---|
bayes | 0.826 | 0.926 | 0.424 |
神奇的拉普拉斯平滑(Laplacian Smoothing)及其在正则化上的应用~
谈谈自己对正则化的一些理解~
拉普拉斯修正的朴素贝叶斯分类器及AODE分类器
Python 机器学习_基于朴素贝叶斯分类的MNIST手写数字识别 - 本文的代码原型来源
详解朴素贝叶斯分类算法 - 用男生的特征来决定女生嫁不嫁,讲的生动有趣
贝叶斯分类器(一):朴素贝叶斯分类器与半朴素贝叶斯分类器 - 讲了很多贝叶斯相关原理