深度学习 手写数字识别

一、首先需要配置对应的环境(anconda+GPU/CPU)

二、下载手写数字数据集

数据集在资源里

三、搭建2D卷积神经网络

2D卷积是由基本卷积层+激活函数+最大池化层构成。

  1. 基本概念

    • 卷积层:2D卷积神经网络的核心是卷积层,它通过在输入图像上滑动一个小窗口(即卷积核或滤波器)来提取特征。这个小窗口会在图像的高度和宽度两个方向上移动,因此被称为二维卷积。
    • 互相关运算:尽管名字中有“卷积”二字,但在深度学习中,2D卷积神经网络通常使用的是互相关运算,而不是真正的数学卷积运算。互相关运算与卷积运算类似,但卷积核不进行翻转。
    • 感受野:卷积层输出的每个元素都是输入图像中一个区域(即感受野)的特征表示。随着网络层次的加深,感受野会逐渐变大,从而能够捕捉到更大范围的特征。
  2. 工作原理

    • 单通道计算:对于单通道图像(如灰度图),卷积核直接在图像上滑动,并在每个位置进行内积运算,得到输出矩阵的一个元素。这个过程会在整个图像上重复进行,直到生成完整的输出矩阵。
    • 多通道计算:对于多通道图像(如RGB图像),需要使用多个卷积核分别对每个通道进行卷积运算,然后将所有通道的结果相加得到最终的输出。
    • 卷积核参数:卷积核的大小、数量以及是否使用填充(padding)和步长(stride)等参数都会影响卷积层的输出。
  3. 结构特点

    • 局部连接性:卷积层中的神经元只与输入图像中的局部区域相连,这种局部连接性有助于减少参数数量并提高计算效率。
    • 权值共享:同一个卷积核在所有位置上的权重是相同的,这种权值共享机制进一步减少了参数数量,并使得网络能够学习到更加泛化的特征。
    • 层级结构:2D卷积神经网络通常由多个卷积层、池化层和全连接层组成,形成一种层级结构。这种结构有助于逐层提取和抽象图像特征。
  4. 代码如下:

    • 其中通道数、池化层最好为偶数

        super(FalconNet, self).__init__()
        # 定义特征提取部分
        self.features = nn.Sequential(
            # 第一个 FalconConv2d 层,输入通道数为 1,输出通道数为 16
            FalconConv2d(1, 16, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),  # 激活函数
            nn.MaxPool2d(kernel_size=2, stride=2),  # 最大池化层
            # 第二个 FalconConv2d 层,输入通道数为 16,输出通道数为 32
            FalconConv2d(16, 32, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),  # 激活函数
            nn.MaxPool2d(kernel_size=2, stride=2),  # 最大池化层
            # 第三个 FalconConv2d 层,输入通道数为 32,输出通道数为 64
            FalconConv2d(32, 64, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),  # 激活函数
            nn.MaxPool2d(kernel_size=2, stride=2)  # 最大池化层
        )

 四、定义分类器

对输出特征进行分类

 self.classifier = nn.Sequential(
            nn.Linear(64 * 4 * 4, 128),  # 全连接层,输入维度为 64 * 4 * 4,输出维度为 128
            nn.ReLU(inplace=True),  # 激活函数
            nn.Linear(128, num_classes),  # 全连接层,输入维度为 128,输出维度为 num_classes
        )

五 、对数据集进行训练

criterion = nn.CrossEntropyLoss()
# 定义优化器,使用 Adam优化算法
optimizer = optim.Adam(model.parameters(), lr=0.0001)

# model.parameters():获取模型的所有可训练参数
# lr=0.0001:设置学习率为 0.0001
# 定义训练模型的函数
def train_model(model, dataloader, criterion, optimizer, num_epochs=10):
    
    for epoch in range(num_epochs):
        # 设置模型为训练模式
        model.train()
        # 初始化累积损失
        running_loss = 0.0
        # 遍历数据加载器中的每个批次
        for inputs, labels in dataloader:
            # 将输入和标签移动到指定设备(GPU 或 CPU)
            inputs, labels = inputs.to(device), labels.to(device)

            # 清零梯度,防止梯度累积
            optimizer.zero_grad()

            # 前向传播,计算模型输出
            outputs = model(inputs)

            # 计算损失
            loss = criterion(outputs, labels)

            # 反向传播,计算梯度
            loss.backward()

            # 更新模型参数
            optimizer.step()

            # 累积损失
            running_loss += loss.item() * inputs.size(0)

        epoch_loss = running_loss / len(dataloader.dataset)
        print(f'Epoch {epoch + 1}/{num_epochs}, Loss: {epoch_loss:.4f}')

train_model(model, dataloader, criterion, optimizer, num_epochs=5)
torch.save(model.state_dict(), 'model.pth')

六、对模型进行评估

def evaluate_model(model, dataloader, device='cpu'):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        # 遍历数据加载器中的每个批次
        for inputs, labels in dataloader:
            # 将输入和标签移动到指定设备
            inputs, labels = inputs.to(device), labels.to(device)
            # 前向传播,计算模型输出
            outputs = model(inputs)
            # 获取预测的类别标签
            _, predicted = torch.max(outputs, 1)
            # 将 one-hot 编码的标签转换回类别标签
            _, true_labels = torch.max(labels, 1)
            # 更新总样本数量
            total += true_labels.size(0)
            # 更新正确预测的数量
            correct += (predicted == true_labels).sum().item()
    # 计算准确率
    accuracy = correct / total
    # 打印模型的准确率
    print(f'Accuracy of the model: {accuracy:.4f}')
evaluate_model(model, dataloader, device=device)
# 测试数据文件夹的位置
test_images_folder = 'written_digits/test_no_label'
class CustomTransform:
    def __init__(self):
        # 定义一系列数据变换操作
        self.transform = transforms.Compose([
            transforms.ToTensor(),        # 将 numpy.ndarray 转换为 torch.Tensor
            transforms.Resize([32, 32]),  # 将输入图片 resize 成统一尺寸 32x32
            transforms.Normalize(mean=[0.485], std=[0.229])  # 对图像进行标准化处理
        ])
    def __call__(self, image):
        # 应用定义的变换操作
        image = self.transform(image)
        return image
# 创建自定义转换对象
transform = CustomTransform()
test_image_files = [f for f in os.listdir(test_images_folder) if f.endswith('.txt')]
model.eval()      
results = []
softmax = nn.Softmax(dim=1)
# 关闭梯度计算,因为评估阶段不需要计算梯度
with torch.no_grad():
    for image_file in test_image_files:
        # 构建图像文件路径
        image_path = os.path.join(test_images_folder, image_file)
        # 读取图像数据
        with open(image_path, 'r') as file:
            lines = file.readlines()
            image = np.array([[int(char) for char in line.strip()] for line in lines], dtype=np.float32)
           
        image = transform(image)
        
        image = image.to(device)
        output = model(image.unsqueeze(0))  # 添加批次维度 (1, 1, 32, 32)
       
        probabilities = softmax(output)
        
        _, predicted = torch.max(probabilities, 1)
       
        results.append([image_file.split('.tx')[0], predicted.item()])
        
results_df = pd.DataFrame(results, columns=['Test_txt_name', 'Digit'])

results_df.to_csv('predictions.csv', index=False)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值