MNIST多种方法Python实现(KNN、CNN、DNN)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文可用于机器学习作业,我们这里使用了三种方法进行判定,因为现在很多研究中经常会用MNIST的分类性能作为基准任务。

一、MNIST

MNIST是一个广泛使用的手写数字数据集,主要用于训练和测试机器学习模型。以下是对MNIST的简短介绍:

  1. 手写数字识别:MNIST包含60,000个训练样本和10,000个测试样本,全部为0-9的手写数字图像。

  2. 灰度图像:每个样本都是28x28像素的灰度图像,像素值范围为0-255。

  3. 简单任务:MNIST常被用作机器学习入门任务,因为它的数据集规模适中,且任务难度较低。

  4. 基准测试:MNIST常用于评估机器学习算法的性能,作为入门级的基准测试。

  5. 应用广泛:MNIST被广泛应用于深度学习、计算机视觉等领域,是机器学习领域的经典数据集之一。

二、方法

1.KNN

(1)KNN的结构如下图:
请添加图片描述
KNN,K近邻算法,找出待测样本距离最近的K个样本,根据计算距离,取最近距离的为它的标记。 具体的想要多加学习的朋友可以看一看算法详解,以及学习函数 neighbors.KNeighborsClassifier() 的参数

(2)代码如下:

import pandas as pd
from sklearn import neighbors
from sklearn.metrics import accuracy_score, confusion_matrix
import numpy as np
from torchvision import transforms
from torchvision.datasets import MNIST
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# 加载数据
train_data = pd.read_csv("./train.csv")        #这里这个train文件是使用Kaggle提供的csv文件,若是想要修改,改成和下面test—_data类似的格式,查一下就好了
train_dataset = train_data.iloc[:, 1:].values  # 提取特征
train_label = train_data.iloc[:, 0].values  # 提取标签

test_dataset = MNIST(root='./data', train=False, download=True, transform=transforms.ToTensor())
test_loader = DataLoader(test_dataset, batch_size=1, shuffle=False)

# 初始化KNN模型
knn = neighbors.KNeighborsClassifier(n_neighbors=5)

# 训练模型
knn.fit(train_dataset, train_label)

# 在训练集上进行预测
train_pred = knn.predict(train_dataset)

# 计算训练集上的准确率
train_accuracy = accuracy_score(train_label, train_pred)
print(f'Train accuracy: {train_accuracy:.4f}')

# 预测测试集结果
all_labels = []  # 用于存储真实标签
test_data = []
for data, label in test_loader:
    data = data.numpy().flatten()
    test_data.append(data)
    all_labels.extend(label.numpy())

# 将列表转换为NumPy数组
test_data = np.array(test_data) * 255
all_labels = np.array(all_labels).flatten()

# 确保标签是整型
all_labels = all_labels.astype(int)

# 将测试数据转换为DataFrame
test_data = pd.DataFrame(test_data)

# 预测测试集结果
all_predictions = knn.predict(test_data)

# 计算准确率
accuracy = accuracy_score(all_labels, all_predictions)
print(f'Test accuracy: {accuracy:.4f}')

# 将预测结果保存到CSV文件
a = pd.Series(all_predictions)
b = pd.Series(np.arange(1, len(all_predictions) + 1))
c = pd.DataFrame([a, b])
d = pd.DataFrame(c.T)
d.to_csv("./result/knn_mnist_torch.csv", index=False)

# 计算混淆矩阵
conf_matrix = confusion_matrix(all_labels, all_predictions)

# 归一化混淆矩阵以显示百分比
conf_matrix_norm = conf_matrix.astype('float') / conf_matrix.sum(axis=1)[:, np.newaxis]

# 绘制混淆矩阵
plt.figure(figsize=(10, 8))
plt.imshow(conf_matrix_norm, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('KNN Confusion Matrix')
#plt.colorbar()
tick_marks = np.arange(10)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)

# 在每个方格中添加百分比数值
thresh = conf_matrix_norm.max() / 2.
for i, j in np.ndindex(conf_matrix.shape):
    plt.text(j, i, f"{conf_matrix_norm[i, j]:.2%}",
             horizontalalignment="center",
             color="white" if conf_matrix_norm[i, j] > thresh else "black")

plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')

# 保存混淆矩阵为图片
plt.savefig("./result/KNN_confusion_matrix.png")  # 保存为PNG图片

# 显示混淆矩阵
plt.show()

2.CNN

(1)CNN模型的结构如下:
请添加图片描述
卷积神经网络的组成是卷积层池化层全连接层,可以调用函数。这里的MNIST可以作为一个简单的入门。

(2)代码如下:

import itertools
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
import pandas as pd
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import OneHotEncoder
from torchvision import transforms
from torchvision.datasets import MNIST

# 设置随机种子
np.random.seed(2)
torch.manual_seed(2)

# 加载数据
train = pd.read_csv("./train.csv")
Y_train = train["label"]
X_train = train.drop(labels=["label"], axis=1)
X_train = X_train.values.reshape(-1, 1, 28, 28) / 255.0

# 将标签转换为one-hot编码
encoder = OneHotEncoder(sparse_output=False)
Y_train = encoder.fit_transform(Y_train.values.reshape(-1, 1))

# 划分训练集和验证集
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=0.1, random_state=2)

# 创建TensorDataset和DataLoader
train_dataset = TensorDataset(torch.tensor(X_train, dtype=torch.float32), torch.tensor(Y_train, dtype=torch.float32))
val_dataset = TensorDataset(torch.tensor(X_val, dtype=torch.float32), torch.tensor(Y_val, dtype=torch.float32))
train_loader = DataLoader(train_dataset, batch_size=86, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=86, shuffle=False)

# 定义模型
class CNN1(nn.Module):
    def __init__(self):
        super(CNN1, self).__init__()
        self.cnn1 = nn.Sequential(
            nn.Conv2d(1, 32, kernel_size=5, padding='same'),
            nn.ReLU(),
            nn.Conv2d(32, 32, kernel_size=5, padding='same'),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            nn.Dropout(0.25),
            nn.Conv2d(32, 64, kernel_size=3, padding='same'),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, padding='same'),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            nn.Dropout(0.25),
            nn.Flatten(),
            nn.Linear(64*7*7, 256),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(256, 10)
        )

    def forward(self, x):
        out = self.cnn1(x)
        return out

# 实例化模型并移动到GPU
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = CNN1().to(device)

# 定义优化器和损失函数
optimizer = optim.RMSprop(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()

# 训练模型
epochs = 5
for epoch in range(epochs):
    model.train()
    for data, target in train_loader:
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, torch.argmax(target, dim=1))
        loss.backward()
        optimizer.step()

    # 验证模型
    model.eval()
    val_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in val_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            val_loss += criterion(output, torch.argmax(target, dim=1)).item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(torch.argmax(target, dim=1).view_as(pred)).sum().item()

    val_loss /= len(val_loader)
    print(f'Epoch {epoch+1}/{epochs}, Val Loss: {val_loss:.4f}, Val Acc: {correct/len(val_dataset):.4f}')

# 预测测试集结果
model.eval()
test_dataset = MNIST(root='./data', train=False, download=True, transform=transforms.ToTensor())
test_loader = DataLoader(test_dataset, batch_size=86, shuffle=False)
all_predictions = []
all_labels = []  # 用于存储真实标签

with torch.no_grad():
    for data, labels in test_loader:
        data = data.to(device).float()  # 确保测试数据也是float类型
        labels = labels.to(device)  # 将标签也移动到设备上
        output = model(data)
        all_predictions.extend(output.argmax(dim=1).cpu().numpy())  # 获取预测结果的类别
        all_labels.extend(labels.cpu().numpy())  # 获取真实标签

# 计算准确率
accuracy = accuracy_score(all_labels, all_predictions)
print(f'Test accuracy: {accuracy:.4f}')

# 确保all_predictions的长度与测试集样本数量相匹配
assert len(all_predictions) == len(test_dataset), "预测结果的数量与测试集样本数量不匹配"

# 计算混淆矩阵
conf_matrix = confusion_matrix(all_labels, all_predictions)

# 绘制混淆矩阵
plt.figure(figsize=(10, 8))
plt.imshow(conf_matrix, interpolation='nearest', cmap=plt.cm.Blues)
plt.title(' CNN Confusion Matrix')
#plt.colorbar()
tick_marks = np.arange(10)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)

# 将混淆矩阵的每个元素转换为百分比
conf_matrix_percent = conf_matrix.astype('float') / conf_matrix.sum(axis=1)[:, np.newaxis]
thresh = conf_matrix.max() / 2.

for i, j in itertools.product(range(conf_matrix.shape[0]), range(conf_matrix.shape[1])):
    plt.text(j, i, f"{conf_matrix_percent[i, j]:.2%}",
             horizontalalignment="center",
             color="white" if conf_matrix[i, j] > thresh else "black")

plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')

# 保存混淆矩阵为图片
plt.savefig("./result/CNN_confusion_matrix.png")  # 保存为PNG图片

# 显示混淆矩阵
plt.show()

==注:上面的只是结构示意图不是所构建网络的模型

3.DNN

(1)DNN模型的结构如下:
请添加图片描述
这里是只使用全连接层激活函数构建的神经网络,按理来说不能这样定义的,但是作为入门学习很有效果。

代码如下:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, confusion_matrix
from torchvision import transforms
from torchvision.datasets import MNIST
import matplotlib.pyplot as plt
import itertools

# 定义DNN模型
class DNN(nn.Module):
    def __init__(self):
        super(DNN, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
            nn.Linear(28 * 28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits

# 设置设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 加载数据集
train = pd.read_csv("./train.csv")
test = pd.read_csv("./test.csv")

Y_train = train["label"]
X_train = train.drop(labels=["label"], axis=1)
X_train = X_train.values.reshape(-1, 1, 28, 28) / 255.0
test = test.values.reshape(-1, 1, 28, 28) / 255.0

# 将标签转换为one-hot编码
encoder = OneHotEncoder(sparse_output=False)
Y_train = encoder.fit_transform(Y_train.values.reshape(-1, 1))

# 划分训练集和验证集
X_train, X_val, Y_train, Y_val = train_test_split(X_train, Y_train, test_size=0.1, random_state=2)

# 创建TensorDataset和DataLoader
train_dataset = TensorDataset(torch.tensor(X_train, dtype=torch.float32), torch.tensor(Y_train, dtype=torch.float32))
val_dataset = TensorDataset(torch.tensor(X_val, dtype=torch.float32), torch.tensor(Y_val, dtype=torch.float32))
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False)

# 初始化模型、损失函数和优化器
model = DNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 训练模型
epochs = 5
for epoch in range(epochs):
    model.train()
    for data, target in train_loader:
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, torch.argmax(target, dim=1))
        loss.backward()
        optimizer.step()

    # 验证模型
    model.eval()
    val_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in val_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            val_loss += criterion(output, torch.argmax(target, dim=1)).item()
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(torch.argmax(target, dim=1).view_as(pred)).sum().item()

    val_loss /= len(val_loader)
    print(f'Epoch {epoch+1}/{epochs}, Val Loss: {val_loss:.4f}, Val Acc: {correct/len(val_dataset):.4f}')

# 预测测试集结果
model.eval()
test_dataset = MNIST(root='./data', train=False, download=True, transform=transforms.ToTensor())
test_loader = DataLoader(test_dataset, batch_size=86, shuffle=False)
all_predictions = []
all_labels = []  # 用于存储真实标签

with torch.no_grad():
    for data, labels in test_loader:
        data = data.to(device).float()  # 确保测试数据也是float类型
        labels = labels.to(device)  # 将标签也移动到设备上
        output = model(data)
        all_predictions.extend(output.argmax(dim=1).cpu().numpy())  # 获取预测结果的类别
        all_labels.extend(labels.cpu().numpy())  # 获取真实标签

# 计算准确率
accuracy = accuracy_score(all_labels, all_predictions)
print(f'Test accuracy: {accuracy:.4f}')

# 确保all_predictions的长度与测试集样本数量相匹配
assert len(all_predictions) == len(test_dataset), "预测结果的数量与测试集样本数量不匹配"

# 计算混淆矩阵
conf_matrix = confusion_matrix(all_labels, all_predictions)
conf_matrix_percent = conf_matrix.astype('float') / conf_matrix.sum(axis=1)[:, np.newaxis] * 100

# 绘制混淆矩阵的热力图
plt.figure(figsize=(10, 8))
plt.imshow(conf_matrix_percent, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('DNN Confusion Matrix')
#plt.colorbar()
tick_marks = np.arange(10)
plt.xticks(tick_marks, tick_marks)
plt.yticks(tick_marks, tick_marks)

# 在热力图上标注百分比数值
thresh = conf_matrix_percent.max() / 2.
for i, j in itertools.product(range(conf_matrix_percent.shape[0]), range(conf_matrix_percent.shape[1])):
    plt.text(j, i, f"{conf_matrix_percent[i, j]:.1f}%",
             horizontalalignment="center",
             color="white" if conf_matrix_percent[i, j] > thresh else "black")

plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')

# 保存混淆矩阵为图片
plt.savefig("./result/DNN_confusion_matrix.png")  # 保存为PNG图片

# 显示混淆矩阵
plt.show()

在上述的代码中我加入了对测试集的预测结果给出混淆矩阵,测试结果可视化。


总结

在这里插入图片描述

这个总结之前写过的,直接挪过来了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值