import numpy as np
import struct
import matplotlib.pyplot as plt
import math
from sklearn.decomposition import PCA
# 训练集文件
train_images_idx3_ubyte_file = 'MNIST_data/train-images.idx3-ubyte'
# 训练集标签文件
train_labels_idx1_ubyte_file = 'MNIST_data/train-labels.idx1-ubyte'
# 测试集文件
test_images_idx3_ubyte_file = 'MNIST_data/t10k-images.idx3-ubyte'
# 测试集标签文件
test_labels_idx1_ubyte_file = 'MNIST_data/t10k-labels.idx1-ubyte'
def decode_idx3_ubyte(idx3_ubyte_file):
"""
解析idx3文件的通用函数
:param idx3_ubyte_file: idx3文件路径
:return: 数据集
"""
# 读取二进制数据
bin_data = open(idx3_ubyte_file, 'rb').read()
# 解析文件头信息,依次为魔数、图片数量、每张图片高、每张图片宽
offset = 0
fmt_header = '>iiii' #因为数据结构中前4行的数据类型都是32位整型,所以采用i格式,但我们需要读取前4行数据,所以需要4个i。我们后面会看到标签集中,只使用2个ii。
magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, offset)
# print('魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols))
# 解析数据集
image_size = num_rows * num_cols
offset += struct.calcsize(fmt_header) #获得数据在缓存中的指针位置,从前面介绍的数据结构可以看出,读取了前4行之后,指针位置(即偏移位置offset)指向0016。
# print(offset)
fmt_image = '>' + str(image_size) + 'B' #图像数据像素值的类型为unsigned char型,对应的format格式为B。这里还有加上图像大小784,是为了读取784个B格式数据,如果没有则只会读取一个值(即一副图像中的一个像素值)
# print(fmt_image,offset,struct.calcsize(fmt_image))
images = np.empty((num_images, num_rows, num_cols))
#plt.figure()
for i in range(num_images):
# if (i + 1) % 10000 == 0:
# print('已解析 %d' % (i + 1) + '张')
# print(offset)
images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))
#print(images[i])
offset += struct.calcsize(fmt_image)
# plt.imshow(images[i],'gray')
# plt.pause(0.00001)
# plt.show()
#plt.show()
return images
def decode_idx1_ubyte(idx1_ubyte_file):
"""
解析idx1文件的通用函数
:param idx1_ubyte_file: idx1文件路径
:return: 数据集
"""
# 读取二进制数据
bin_data = open(idx1_ubyte_file, 'rb').read()
# 解析文件头信息,依次为魔数和标签数
offset = 0
fmt_header = '>ii'
magic_number, num_images = struct.unpack_from(fmt_header, bin_data, offset)
# print('魔数:%d, 图片数量: %d张' % (magic_number, num_images))
# 解析数据集
offset += struct.calcsize(fmt_header)
fmt_image = '>B'
labels = np.empty(num_images)
for i in range(num_images):
# if (i + 1) % 10000 == 0:
# print ('已解析 %d' % (i + 1) + '张')
labels[i] = struct.unpack_from(fmt_image, bin_data, offset)[0]
offset += struct.calcsize(fmt_image)
return labels
def load_train_images(idx_ubyte_file=train_images_idx3_ubyte_file):
"""
TRAINING SET IMAGE FILE (train-images-idx3-ubyte):
[offset] [type] [value] [description]
0000 32 bit integer 0x00000803(2051) magic number
0004 32 bit integer 60000 number of images
0008 32 bit integer 28 number of rows
0012 32 bit integer 28 number of columns
0016 unsigned byte ?? pixel
0017 unsigned byte ?? pixel
........
xxxx unsigned byte ?? pixel
Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).
:param idx_ubyte_file: idx文件路径
:return: n*row*col维np.array对象,n为图片数量
"""
return decode_idx3_ubyte(idx_ubyte_file)
def load_train_labels(idx_ubyte_file=train_labels_idx1_ubyte_file):
"""
TRAINING SET LABEL FILE (train-labels-idx1-ubyte):
[offset] [type] [value] [description]
0000 32 bit integer 0x00000801(2049) magic number (MSB first)
0004 32 bit integer 60000 number of items
0008 unsigned byte ?? label
0009 unsigned byte ?? label
........
xxxx unsigned byte ?? label
The labels values are 0 to 9.
:param idx_ubyte_file: idx文件路径
:return: n*1维np.array对象,n为图片数量
"""
return decode_idx1_ubyte(idx_ubyte_file)
def load_test_images(idx_ubyte_file=test_images_idx3_ubyte_file):
"""
TEST SET IMAGE FILE (t10k-images-idx3-ubyte):
[offset] [type] [value] [description]
0000 32 bit integer 0x00000803(2051) magic number
0004 32 bit integer 10000 number of images
0008 32 bit integer 28 number of rows
0012 32 bit integer 28 number of columns
0016 unsigned byte ?? pixel
0017 unsigned byte ?? pixel
........
xxxx unsigned byte ?? pixel
Pixels are organized row-wise. Pixel values are 0 to 255. 0 means background (white), 255 means foreground (black).
:param idx_ubyte_file: idx文件路径
:return: n*row*col维np.array对象,n为图片数量
"""
return decode_idx3_ubyte(idx_ubyte_file)
def load_test_labels(idx_ubyte_file=test_labels_idx1_ubyte_file):
"""
TEST SET LABEL FILE (t10k-labels-idx1-ubyte):
[offset] [type] [value] [description]
0000 32 bit integer 0x00000801(2049) magic number (MSB first)
0004 32 bit integer 10000 number of items
0008 unsigned byte ?? label
0009 unsigned byte ?? label
........
xxxx unsigned byte ?? label
The labels values are 0 to 9.
:param idx_ubyte_file: idx文件路径
:return: n*1维np.array对象,n为图片数量
"""
return decode_idx1_ubyte(idx_ubyte_file)
def find_3_8():
"""
用于在数据集中寻找标签为3和8的数据,用于二分类问题
:param: none
:return:train_X_3, train_X_8, train, train_label, test, test_label
"""
train_images = load_train_images().reshape(-1, 784)
train_labels = load_train_labels()
test_images = load_test_images().reshape(-1, 784)
test_labels = load_test_labels()
train_X_3 = []
train_X_8 = []
train = []
train_label = []
test = []
test_label = []
pca = PCA(n_components=8, whiten=True)
train_images = pca.fit_transform(train_images)
test_images = pca.transform(test_images)
for i in range(len(train_images)):
if train_labels[i] == 3 or train_labels[i] == 8:
train.append(train_images[i].reshape(8, 1))
train_label.append(train_labels[i])
if train_labels[i] == 3:
train_X_3.append(train_images[i].reshape(8, 1))
else:
train_X_8.append(train_images[i].reshape(8, 1))
for i in range(len(test_images)):
if test_labels[i] == 3 or test_labels[i] == 8:
test.append(test_images[i].reshape(8, 1))
test_label.append(test_labels[i])
return train_X_3, train_X_8, train, train_label, test, test_label
def calculate_mu_cor(imput):
"""
计算协方差和均值
:param imput: 784*1的特征向量
:return: 均值mu和方差cor
"""
cor = 0
mu = np.mean(imput, axis=0) # 计算每一列的均值
for i in range(len(imput)):
cor += (imput[i] - mu) @ (imput[i] - mu).T
cor = cor/len(imput)
return mu, cor
def LDF(mu_3, mu_8, cor, test_data):
"""
线性判别函数: 用两个类别的共同方差作为判别函数的方差
:param: 测试数据,方差及均值
:return: 返回测试概率
"""
cor_inv = np.linalg.inv(cor)
omega_3 = cor_inv @ mu_3
omega_8 = cor_inv @ mu_8
omega_0_3 = -0.5 * mu_3.T @ cor_inv @ mu_3 + math.log(0.5117)
omega_0_8 = -0.5 * mu_8.T @ cor_inv @ mu_8 + math.log(0.4883)
g3 = omega_3.T @ test_data + omega_0_3
g8 = omega_8.T @ test_data + omega_0_8
return g3, g8
def QDF(mu_3, mu_8, cor_3, cor_8, test_data):
"""
线性判别函数:用两个类别不同的方差作为输入
:param: 测试数据,方差及均值
:return: 返回测试概率
"""
cor_3_inv = np.linalg.inv(cor_3)
cor_8_inv = np.linalg.inv(cor_8)
Omega_3 = -0.5 * cor_3_inv
Omega_8 = -0.5 * cor_8_inv
omega_3 = cor_3_inv @ mu_3
omega_8 = cor_8_inv @ mu_8
omega_0_3 = -0.5 * mu_3.T @ cor_3_inv @ mu_3 + math.log(0.5117) - math.log(np.linalg.norm(cor_3, ord=2))
omega_0_8 = -0.5 * mu_8.T @ cor_8_inv @ mu_8 + math.log(0.4883) - math.log(np.linalg.norm(cor_8, ord=2))
g3 = test_data.T @ Omega_3 @ test_data + omega_3.T @ test_data + omega_0_3
g8 = test_data.T @ Omega_8 @ test_data + omega_8.T @ test_data + omega_0_8
return g3, g8
if __name__ == '__main__':
# 查看前十个数据及其标签以读取是否正确
train_X_3, train_X_8, train, train_label, test, test_label = find_3_8()
mu_3, cor_3 = calculate_mu_cor(train_X_3)
mu_8, cor_8 = calculate_mu_cor(train_X_8)
mu, cor = calculate_mu_cor(train)
ACC_LDF = 0
ACC_QDF = 0
for i in range(len(test_label)):
LDF_3, LDF_8 = LDF(mu_3, mu_8, cor, test[i])
QDF_3, QDF_8 = QDF(mu_3, mu_8, cor_3, cor_8, test[i])
if LDF_3 > LDF_8:
if test_label[i] == 3:
ACC_LDF += 1
else:
if test_label[i] == 8:
ACC_LDF += 1
if QDF_3 > QDF_8:
if test_label[i] == 3:
ACC_QDF += 1
else:
if test_label[i] == 8:
ACC_QDF += 1
print("ACC_LDF=", ACC_LDF/len(test_label), "\tACC_QDF:", ACC_QDF/len(test_label))