从0到1,AI我来了- (2)解读程序-从AI手写数字识别开始

拆解上篇文章的代码,每行通过注释解释,有不清晰的,告诉我,我更新上去。

#torch是一个开源的机器学习库,主要用于深度学习应用,它提供了广泛的工具和功能,
#支持张量操作、自动微分以及各种神经网络构建模块
import torch  

# PyTorch 库中的一个模块,主要用于构建和训练神经网络。它提供了许多用于构建深度学习模型的工具,
#包括层、损失函数、优化器等。用户可以利用这个模块方便地定义和训练神经网络模型,进行前向传播和反向
#传播等操作。具体来说,"torch.nn" 包括线性层、卷积层、循环神经网络层、激活函数等多种神经网络组件
import torch.nn as nn  

# PyTorch 库中的一个模块,提供了一组优化算法,用于训练神经网络,常见的优化器包括随机梯度
#下降(SGD)、Adam、RMSprop 等
import torch.optim as optim  

# PyTorch 中的一个模块,主要用于数据加载和处理
from torch.utils.data import DataLoader  

# PyTorch 的 torchvision 库中导入 datasets 和 transforms 模块。
#datasets 模块提供了一些公共数据集的接口,例如 MNIST、CIFAR-10 等,方便加载和使用。
#transforms 模块则提供了一些数据预处理和增强的功能,可以对图像进行转换
#例如调整大小、归一化、翻转等
from torchvision import datasets, transforms  

##SummaryWriter 一个用于记录训练过程中的各种信息(如损失、准确率、图像等)到 TensorBoard
#的工具,帮助你可视化模型的训练进度和性能
from torch.utils.tensorboard import SummaryWriter  


#Matplotlib 是一个用于绘制图表和数据可视化的强大工具
#pyplot 模块则提供了类似于 MATLAB 的绘图接口,使得绘制各种图形变得简单和直观
import matplotlib.pyplot as plt  
 
# 数据加载与预处理  
# transforms.Compose 用于构建图像转换操作序列的函数
transform = transforms.Compose([  
     #将图像转换为张量(下面附录了解何为张量),并归一化至[0, 1]
    transforms.ToTensor(), 
     #标准化图像数据,将每个通道的像素值按照指定的均值和标准差进行归一化操作
     #这里参数(0.5,)表示一个元组,且只有一个元素,表示只有一个通道均值为0.5,标准差为0.5
    transforms.Normalize((0.5,), (0.5,))  
])  
# 加载 MNIST 数据集,并应用指定的变换(transform)
# datasets.MNIST 是PyTorch 中的一个数据集类,用于加载 MNIST 数据集
# root 参数:指定数据存储位置,这里表示存储在当前目录下的 data 文件夹中
# train 参数:如果设置为 True,则加载训练集;如果设置为 False,则加载测试集
# download 参数:如果设置为 True,则如果数据集不存在,会自动下载;如果设置为 False,则不会下载(跑成功过,就可以改为False)
# train_dataset 变量为下载、变换后的训练集数据
train_dataset = datasets.MNIST(root='./data', train=True, download=False, transform=transform)  
#test_dataset 变量为下载、变换后的测试集数据
test_dataset = datasets.MNIST(root='./data', train=False, download=False, transform=transform)  
print("download success")

#DataLoader 批量加载训练集数据,每个批次包含的样本数64,Shuffle 表示每次训练,随机打乱
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)  

#DataLoader 批量加载测试集数据,每个批次样本数64,思考测试数据集,为什么不随机打乱?
#训练时,随机打乱,有助于更好训练模型,测试时打乱是为了验证模型结果,打不打乱不影响模型
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)  
 
# 模型定义  
#"SimpleCNN" 是一个自定义的模型类名,而 "nn.Module" 是所有神经网络模块的基类。
# 在这个类中,通常会定义神经网络的结构(如卷积层、激活函数、池化层等)以及前向传播的方法。
# 通过继承 "nn.Module",可以利用 PyTorch 提供的神经网络功能,如参数管理、向前传播和反向传播的计算等
class SimpleCNN(nn.Module):  
    #是 Python 中类的构造函数(initializer)。创建一个类的实例时,__init__ 方法会被自动调用。
    #它通常用于初始化类的属性,将参数传递给类的实例,并设置初始状态。
    #在这个方法中,self 参数代表实例本身,它允许你访问类的属性和方法。
    #通过在 __init__ 方法中定义参数,可以在创建对象时传递特定的值来初始化对象的状态。
    def __init__(self):  
        #继承
        super(SimpleCNN, self).__init__()  
        # 段代码用PyTorch的torch.nn模块定义了一个卷积层,具体如下:
        #self.conv1 是 Conv2d 类的一个实例,表示一个2D卷积层。
        #参数解释如下:1 表示输入通道的数量(例如,灰度图像只有1个通道)。
        #32 表示该层将生成输出通道的数量(或滤波器的数量)。
        #kernel_size=3 设置卷积核的大小为3x3。
        #stride=1 表示步为1
        #padding=1 表示图像素填充1格空
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1) 

        #这里32 是因为self.conv1的输出为32,这里的输入层为刚conv1的输出层
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)  
        
        #nn.MaxPool2d类创建一个二维最大池化层。具体来说:
        #nn.MaxPool2d(2, 2) 表示创建一个大小为2x2的池化窗口,步幅也是2。
        #也就是说,它会对输入图像的每个2x2区域取最大值,并在每次滑动时跳过两个像素。
        #这种池化操作通常用于减少特征图的空间尺寸,从而降低计算量和参数数量,同时保留重要的特征信息
        self.pool = nn.MaxPool2d(2, 2)  
        #定义一个全连接层(fully connected layer),通常在构建神经网络时使用。
        #self.fc1 是该层的名称,可以用来在类的其他方法中引用。
        #nn.Linear 是 PyTorch 中的一个函数,用于创建一个全连接层。
        #64 * 7 * 7 是该层的输入特征维度,表示输入数据的大小。在这里,输入是一个形状为 (64, 7, 7) 的张量,
        #其中 64 是通道数(或特征图数量),7 和 7 是特征图的高度和宽度。
        #即数据第一维是特征图的数量,第二维和第三维是特征图的尺寸。
        #128 是该层的输出特征维度,表示输出将会有 128 个神经元。
        self.fc1 = nn.Linear(64 * 7 * 7, 128)  
        
        #再做一次全连接,输出为10个特征
        self.fc2 = nn.Linear(128, 10)  



    #这段代码是一个神经网络模型的前向传播(forward)方法,通常出现在深度学习框架PyTorch的模型定义中。下面是代码的逐行解释
    def forward(self, x):  
        #这行代码首先将输入x传递给卷积层self.conv1,该层对输入执行卷积操作。然后通过nn.ReLU()应用ReLU激活函数,最后将结果输入到池化层self.pool,通常用于减少特征图的尺寸。
        x = self.pool(nn.ReLU()(self.conv1(x)))  
        #类似于第一行,这里将经过卷积层self.conv2和ReLU激活函数的输出再次传递给池化层self.pool,进一步处理特征图
        x = self.pool(nn.ReLU()(self.conv2(x)))  
        #这行代码将池化后的张量x重新调整形状。-1表示自动计算该维度的大小,64 * 7 * 7是特征图展平后的维度,通常用于连接到全连接层之前
        x = x.view(-1, 64 * 7 * 7)  
        #最后一行返回模型的输出,通常是类别概率、回归值或者其他预测结果。
        x = nn.ReLU()(self.fc1(x))  
        #在这一行,隐藏层的输出x被传递到第二个全连接层self.fc2,通常作为输出层,用于生成模型的最终预测。
        x = self.fc2(x)  
        #最后一行返回模型的输出,通常是类别概率、回归值或者其他预测结果。
        return x  
 
# 训练设置  
#实例化神经网络模型
model = SimpleCNN()  
#定义一个损失函数,这个函数是多分类问题的一种损失函数,它结合了‌Softmax函数和交叉熵损失的操作
criterion = nn.CrossEntropyLoss()  
#PyTorch 库中的 Adam 优化器来优化一个模型
#optimizer:这是我们定义的变量,用于存储优化器的实例。
#optim.Adam:这是调用了 Adam 优化算法,Adam 是一种常用的优化算法,它结合了动量和自适应学习率的优点。
#model.parameters():用于获取模型中所有可学习参数的列表,这些参数将在优化过程中进行更新。
#lr=0.001:学习率的设置,表示每一次更新参数时,步长的大小为 0.001。学习率控制模型学习的速度,值过大会导致训练不稳定,值过小则可能导致训练速度过慢。
optimizer = optim.Adam(model.parameters(), lr=0.001)  
#分10批训练
num_epochs = 10  
 
# TensorBoard 初始化  
writer = SummaryWriter()  
 
# 记录损失和准确率  
loss_values = []  
accuracy_values = []  
 
# 训练循环  
for epoch in range(num_epochs):  
    running_loss = 0.0  
    correct = 0  
    total = 0  
    
    for images, labels in train_loader:  
        #用于在每次优化步骤之前清零模型参数的梯度。在使用反向传播计算梯度时,会在每次迭代中累加梯度。
        #如果不清零,梯度将会不断累积,从而导致更新不准确。
        optimizer.zero_grad()  
        #输入的图像数据 images 传递给模型 model,以生成对应的输出 outputs
        outputs = model(images)  
        #这句代码是在计算模型输出和真实标签之间的损失值。
        #outputs 是模型的预测结果,labels 是实际的类别标签,
        #criterion 通常是一个损失函数(如交叉熵损失或均方误差)。
        #损失值用于衡量模型的预测与真实标签之间的差距,损失越小,模型的预测性能越好
        loss = criterion(outputs, labels)  
        #计算损失函数关于模型参数的梯度,每个可训练参数的梯度,并将这些梯度存储在相应参数的 .grad 属性中
        loss.backward()  
        #作用是使用已计算出的梯度来更新模型的参数,使模型向更低的损失值方向优化
        optimizer.step()  
 
        #统计总的loss 
        running_loss += loss.item()  
        #找到最概率最大的预测值
        #_, predicted:下划线 _ 表示我们不关心第一个返回值(即最大值)
        #predicted 则会存储每个样本中得分最高类别的索引
        _, predicted = torch.max(outputs.data, 1)  
        #计算总数
        total += labels.size(0)  
        #计算预测值与实际值正确的次数
        correct += (predicted == labels).sum().item()  
    #计算平均差异(损失)
    avg_loss = running_loss / len(train_loader)  
    #计算争取率
    accuracy = 100 * correct / total  
 
    # 记录到 TensorBoard  画图,见下图,y轴为平均损失,x 轴为批次好
    writer.add_scalar('Loss/train', avg_loss, epoch)  
    writer.add_scalar('Accuracy/train', accuracy, epoch)  
 
    # 记录到列表  
    loss_values.append(avg_loss)  
    accuracy_values.append(accuracy)  
    
    #打印每个批次,损失情况,准确率情况
    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.4f}, Accuracy: {accuracy:.2f}%')  
 
writer.close()  
 
# 测试模型  
model.eval()  
test_loss = 0.0  
correct = 0  
total = 0  
i=0

#用于在代码块内部禁用梯度计算。这意味着在这个代码块内,模型的参数不会计算梯度
with torch.no_grad():  
    #开始测试,每行代码含义,参考训练代码中的描述
    for images, labels in test_loader:  
        outputs = model(images)  
        loss = criterion(outputs, labels)  
        test_loss += loss.item()  
         
        _, predicted = torch.max(outputs.data, 1)  
        total += labels.size(0)  
        correct += (predicted == labels).sum().item() 
        
        if (predicted[i] != labels[i] ):
            print("我猜是",predicted[i],"实际是",labels[i])
            plt.imshow(images[i].reshape(28,28),cmap='Greys', interpolation='nearest')
            plt.show()
        
        
 
test_loss /= len(test_loader)  
test_accuracy = 100 * correct / total  
print(f'Test Loss: {test_loss:.4f}, Test Accuracy: {test_accuracy:.2f}%')  
 
# 使用 Matplotlib 绘制损失和准确率  
plt.figure(figsize=(12, 4))  
 
# 绘制损失图  
plt.subplot(1, 2, 1)  
plt.plot(range(1, num_epochs + 1), loss_values, label='Loss')  
plt.title('Training Loss')  
plt.xlabel('Epochs')  
plt.ylabel('Loss')  
plt.legend()  
 
# 绘制准确率图  
plt.subplot(1, 2, 2)  
plt.plot(range(1, num_epochs + 1), accuracy_values, label='Accuracy', color='orange')  
plt.title('Training Accuracy')  
plt.xlabel('Epochs')  
plt.ylabel('Accuracy (%)')  
plt.legend()  
 
plt.tight_layout()  
plt.show()



    

附录:

这篇学点什么
一、理解什么是张量/矩阵/向量/标量?

   1、什么是标量【Scalar】一个单独的数字,比如 50,3.1425926..  【零维张量,无方向】
   2、什么是向量【Vector】多个标量的集合【一维张量】,如[年龄、性别、身高], 描述一个人 [50,1,178]
   3、什么是矩阵【Matrix可以】理解为多维的向量【二维张量】,比如一群人,2*3矩阵 [[50,1,178],[20,0,160]]
   4、什么是张量【Tensor】可以理解为多维的数组,多维矩阵男人、女人维度2*2*3 [[[50,1,178],[20,1,180]],[[50,0,178],[20,0,160]]]

二、神经网络的实现大致步骤:

有样本,有标注的数据,经过模型训练后,如何进行对测试样本进行预测。整个过程大致是:

①  下载数据样本数据,并进行预处理,比如图片进行transform为张量,样本数据可以分为训练数据与测试数据,训练数据用于训练模型,测试数据为了验证模型。

②设计神经网络,根据样本图片,抽象为N个通道(比如R/G/B 三个通道的)张量。

  • 选择网络类型:根据任务选择合适的网络结构(如全连接网络、卷积神经网络(CNN)、递归神经网络(RNN)等)。
  • 确定层数和每层神经元数:根据任务复杂性决定深度和宽度。
  • 选择激活函数:常用的有ReLU、Sigmoid、Tanh等。
  • 设计输出层:根据问题选择输出层的激活函数(如Softmax用于分类,线性用于回归)。

 ③ 训练配置

  • 选择损失函数:根据任务类型选择适当的损失函数(如交叉熵、均方误差)。
  • 选择优化算法:如SGD、Adam等。
  • 设置超参数:如学习率、批次大小、训练轮次等。

④ 模型评估

  • 评估标准:选择合适的评估指标(如准确率、召回率、F1-score等)。
  • 在验证集和测试集上评估:确定模型的实际性能。

下篇,再尝试找个官方体系化的教程,细化下整个案例的过程中,隐藏的AI知识或概念。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值