深度学习笔记

发布时间 2023-04-12 16:09:03作者: Tiork

从零训练一个神经网络 2023-04-12

1.读取训练数据

# 读取数据
# 这一步类似预处理,将图片裁剪成64*64大小
data_dir = "./data"
# 字典语法 dict = {a:b}
# Scale已经被删除,用Resize代替
data_transform = {x: transforms.Compose([transforms.Resize([64,64]),
                                         transforms.ToTensor()]) for x in ['train', 'valid']}

# 这一步相当于读取数据
# ImageFolder返回的对象是一个包含数据集所有图像及对应标签构成的二维元组容器,支持索引和迭代,可作为torch.utils.data.DataLoader的输入。
image_datasets = {x: datasets.ImageFolder(root=os.path.join(data_dir, x),
                                          transform=data_transform[x]) for x in ['train', 'valid']}

# 读取完数据后,对数据进行装载
dataloader = {x: torch.utils.data.DataLoader(dataset = image_datasets[x],
                                             batch_size=16,
                                             shuffle=True) for x in ['train', 'valid']}

2.搭建模型

# 搭建模型
class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.Conv = torch.nn.Sequential(
            torch.nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2),

            torch.nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(kernel_size=2, stride=2)
        )

        self.Classes = torch.nn.Sequential(
            torch.nn.Linear(16*16*256, 512),
            torch.nn.ReLU(),
            torch.nn.Dropout(p=0.5),
            torch.nn.Linear(512,7)
        )

    def forward(self, inputs):
        # 经过卷积层
        x = self.Conv(inputs)
        # 展平成一维数组
        x = x.view(-1, 16 * 16 * 256)
        # 经过全连接层
        x = self.Classes(x)
        return x

# 将模型实例化
model = MyModel()

3.定义超参数

# 定义损失和优化:交叉熵损失(适合多分类)和自适应优化(首选的优化函数)函数
loss_f = torch.nn.CrossEntropyLoss()

# 定义学习率
lr = 0.0005

# 定义优化器
optimizer = torch.optim.Adam(model.parameters(), lr=lr)

# 使用GPU训练
Use_gpu = torch.cuda.is_available()
if Use_gpu:
    # 將模型放在cuda上
    model = model.cuda()

# 训练轮次
epoch_n = 10

4.训练

# 每执行一个epoch输出一次
for epoch in range(epoch_n):
    print("epoch {}/{}".format(epoch+1,epoch_n))
    print("-"*10)
    # 判断是训练还是测试
    for phase in ['train','valid']:
        if phase=='train':
            print("training...")
            model.train(True)
        else:
            print("validating...")
            model.train(False)

        # running_loss && running_accuracy
        running_loss = 0.0
        running_accuracy = 0.0
        # 输出标号 和对应图片,下标从1开始
        # enumerate
        for batch, data  in enumerate(dataloader[phase], 1):
            X,Y = data
            # 将数据放在GPU训练
            X, Y = Variable(X).cuda(), Variable(Y).cuda()
            # predict
            y_pred = model(X)
            # pred,概率较大值对应的索引值,可看做预测结果,1表示行
            _, pred = torch.max(y_pred.data, 1)
            # -------反向传播---------------------------------------------------------------
            # 梯度归零
            optimizer.zero_grad()
            # 计算损失
            loss = loss_f(y_pred, Y)
            # 训练 需要反向传播及梯度更新
            if phase == 'train':
                # 反向传播出现问题
                loss.backward()
                optimizer.step()

            # 计算loss值
            running_loss += loss.data.item()
            # 计算accuracy
            running_accuracy += torch.sum(pred == Y.data)
            # 训练时,每500个batch输出一次,训练loss和acc
            if batch % 500 == 0 and phase == 'train':
                print('batch{},trainLoss:{:.4f},trainAcc:{:.4f}'.format(batch, running_loss / batch,
                                                                        100 * running_accuracy / (4 * batch)))

        # 输出每个epoch的loss和acc
        epoch_loss = running_loss * 4 / len(image_datasets[phase])
        epoch_acc = 100 * running_accuracy / len(image_datasets[phase])
        print('{} Loss:{:.4f} Acc:{:.4f}%'.format(phase, epoch_loss, epoch_acc))

5.保存模型

# 保存模型和参数
torch.save(model, 'classificationModel02.pth')

dropout层的用法 2023-04-12

在PyTorch中,我们只需要在全连接层后添加 Dropout 层并指定丢弃概率。在训练模型时, Dropout
层将以指定的丢弃概率随机丢弃上⼀层的输出元素;在测试模型时(即 model.eval()
后), Dropout 层并不发挥作⽤。
如:

net = nn.Sequential(
  d2l.FlattenLayer(),
  nn.Linear(num_inputs, num_hiddens1),
  nn.ReLU(),
  nn.Dropout(drop_prob1),
  nn.Linear(num_hiddens1, num_hiddens2),
  nn.ReLU(),
  nn.Dropout(drop_prob2),
  nn.Linear(num_hiddens2, 10)
)

model.train() , model.eval() , torch.no_grad() 2023-04-12

model.train()的作用是启用 Batch Normalization 和 Dropout
model.eval()的作用是不启用 Batch Normalization 和 Dropout
而with torch.no_grad()则主要是用于停止autograd模块的工作,以起到加速和节省显存的作用。它的作用是将该with语句包裹起来的部分停止梯度的更新,从而节省了GPU算力和显存,但是并不会影响dropout和BN层的行为。

如果不在意显存大小和计算时间的话,仅仅使用model.eval()已足够得到正确的validation/test的结果;而with torch.no_grad()则是更进一步加速和节省gpu空间(因为不用计算和存储梯度),从而可以更快计算,也可以跑更大的batch来测试。

load model时遇到的一个问题 2023-04-12

load model时必须导入这个Model类,否者无法导入

import 文件时遇到的一个问题 2023-04-12

import一个文件,这个文件里面的代码会被执行

imageFolder下存放数据的格式

- data
  - train
    - class1
    - class2
    - ...
  - valid
    - class1
    - class2
    ...
  - test
    - class1
    - class2
    ...