基于全连接神经网络的minist数据集分类
基于全连接神经网络的minist数据集分类
MNIST数据集是一个广泛使用的手写数字数据集,包含60,000个训练样本和10,000个测试样本。每个样本是28x28像素的灰度图像,代表一个数字(0-9)。本教程将详细介绍如何使用全连接神经网络对MNIST数据集进行分类,包括数据预处理、模型构建、训练优化和结果评估等多个环节。
目的
- 理解全连接神经网络。
- 掌握数据预处理。
- 巩固神经网络的构建。
- 掌握训练和评估模型。
环境
PyCharm Community Edition 2023.2.1
内容与要求
- 数据集理解与预处理:
(1) 定义数据预处理步骤,包括将数据转换为张量并进行归一化。
(2) 下载和加载MNIST数据集,并将其分为训练集和测试集。
(3) 使用DataLoader加载数据,设置了批量大小和是否打乱数据。 - 构建全连接神经网络: 构建一个简单的全连接神经网络,包含三个线性层,并且使用ReLU激活函数。
- 网络训练与优化:
(1) 定义损失函数为交叉熵损失函数,优化器为Adam优化器,并设置了学习率。
(2) 进行了10个epoch的训练,在训练过程中,计算每个epoch的训练损失和验证损失。 - 模型评估: 在测试集上对模型进行评估,计算模型的准确率。
- 实验结果分析: 绘制了训练和验证损失的曲线,以便对模型的训练过程进行分析。
过程与分析
数据集理解与预处理
MNIST数据集是一个广泛使用的手写数字数据集,包含60,000个训练样本和10,000个测试样本。每个样本是28x28像素的灰度图像,代表一个数字(0-9)。MNIST数据集是一个经典的手写数字图像数据集,包含了大量的手写数字图片及其对应的标签。
其中使用了torchvision库中的datasets.MNIST类来处理MNIST数据集。train=True表示加载的是训练数据集,用于模型的训练;train=False表示加载的是测试数据集,用于评估模型的性能。download=True表示如果数据集在指定路径下不存在,则会自动从网络上下载该数据集。
通过定义transform对数据集进行预处理。transforms.Compose组合了两个变换操作,transforms.ToTensor()将数据转换为张量形式,以便模型能够处理;transforms.Normalize((0.5,), (0.5,))对数据进行归一化处理,将均值调整为0.5,标准差调整为0.5,这样可以使得数据在训练过程中更加稳定。
使用torch.utils.data.DataLoader类来加载数据。train_loader用于加载训练数据,设置batch_size=32表示每个批次包含32个样本,shuffle=True表示在每个epoch中会随机打乱数据的顺序,以增加数据的多样性。test_loader用于加载测试数据,同样设置batch_size=32,但shuffle=False表示不会打乱测试数据的顺序。
# 定义数据预处理步骤
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,))
])
构建4层全连接神经网络
# 构建4层全连接神经网络
class FourLayerNN(nn.Module):
def __init__(self):
super(FourLayerNN, self).__init__()
self.fc1 = nn.Linear(28 * 28, 256) # 输入层到第一个隐藏层
self.fc2 = nn.Linear(256, 128) # 第一个隐藏层到第二个隐藏层
self.fc3 = nn.Linear(128, 64) # 第二个隐藏层到第三个隐藏层
self.fc4 = nn.Linear(64, 10) # 第三层隐藏层到输出层
def forward(self, x):
x = x.view(-1, 28 * 28) # 展平图像
x = torch.relu(self.fc1(x)) # ReLU 激活
x = torch.relu(self.fc2(x)) # ReLU 激活
x = torch.relu(self.fc3(x)) # ReLU 激活
x = self.fc4(x) # 输出层
return x
# 实例化模型
model = FourLayerNN()
网络训练与优化
# 3. 网络训练与优化
criterion = nn.CrossEntropyLoss() # 损失函数
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9) # 优化器
num_epochs = 10
train_losses = []
valid_losses = []
train_accuracies = []
valid_accuracies = []
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
correct_train = 0
total_train = 0
for images, labels in train_loader:
images, labels = images.to(device), labels.to(device) # 移动到 GPU
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item() * images.size(0)
_, predicted = torch.max(outputs.data, 1)
total_train += labels.size(0)
correct_train += (predicted == labels).sum().item()
epoch_loss = running_loss / len(train_loader.dataset)
train_losses.append(epoch_loss)
train_accuracy = correct_train / total_train
train_accuracies.append(train_accuracy)
model.eval()
running_loss = 0.0
correct_valid = 0
total_valid = 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device) # 移动到 GPU
outputs = model(images)
loss = criterion(outputs, labels)
running_loss += loss.item() * images.size(0)
_, predicted = torch.max(outputs.data, 1)
total_valid += labels.size(0)
correct_valid += (predicted == labels).sum().item()
epoch_loss = running_loss / len(test_loader.dataset)
valid_losses.append(epoch_loss)
valid_accuracy = correct_valid / total_valid
valid_accuracies.append(valid_accuracy)
print(f'Epoch {epoch + 1}/{num_epochs}, '
f'Train Loss: {train_losses[-1]:.4f}, Train Accuracy: {train_accuracies[-1]:.4f}, '
f'Valid Loss: {valid_losses[-1]:.4f}, Valid Accuracy: {valid_accuracies[-1]:.4f}')
模型评估
# 模型评估
correct = 0
total = 0
with torch.no_grad():
for images, labels in test_loader:
images, labels = images.to(device), labels.to(device) # 移动到 GPU
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
accuracy = correct / total
print(f'Test Accuracy: {accuracy:.4f}')
实验结果分析
# 绘制训练和验证损失及准确率
plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
plt.plot(train_losses, label='Train Loss', marker='o')
plt.plot(valid_losses, label='Validation Loss', marker='o')
plt.title('Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(train_accuracies, label='Train Accuracy', marker='o')
plt.plot(valid_accuracies, label='Validation Accuracy', marker='o')
plt.title('Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
def moving_average(values, window_size):
weights = np.repeat(1.0, window_size) / window_size
return np.convolve(values, weights, 'valid')
总结
本实验主要构建并训练一个四层全连接神经网络,以识别MNIST数据集中的手写数字。通过使用归一化、激活函数和交叉熵损失函数,以及采用SGD优化器进行训练,实验成功实现了模型的训练过程。
模型通过训练损失、验证损失、训练准确率和验证准确率四个指标进行评估。在训练过程中,模型在训练集上的表现(训练损失和训练准确率)持续改善,显示出模型在学习过程中逐渐适应训练数据。同时,模型在验证集上的表现(验证损失和验证准确率)也被监测,以评估模型对未见数据的泛化能力。实验结果显示,模型在训练集和验证集上都取得了较高的准确率,表明模型具有良好的性能。
通过绘制损失和准确率随训练周期变化的曲线图,实验直观地展示了模型训练过程中性能的变化趋势。训练损失和验证损失的下降趋势表明模型在逐渐学习并减少预测误差。同时,训练准确率和验证准确率的提升表明模型的预测正确率在增加。