文章目录
0 写在前面
- 这篇博文主要针对一个深度学习代码进行学习,提高读代码的能力!学完这篇你就会发现,原来深度学习还可以很简单!
- 如果想要进行深入学习的话,可以看这一篇博文
https://blog.csdn.net/weixin_42521185/article/details/123544136?spm=1001.2014.3001.5501
1 完整代码
- 这段代码可以直接运行,数据集是自动加载的!
import numpy as np
import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim
batch_size = 64
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转为Tensor向量
transforms.Normalize((0.1307,), (0.3081,)) # 均值和标准差,都是前人已经算好的值
])
train_dataset = datasets.MNIST(root='../dataset/mnist/',
train=True,
download=True,
transform=transform)
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(test_dataset,
shuffle=False,
batch_size=batch_size)
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.l1 = torch.nn.Linear(784, 512)
self.l2 = torch.nn.Linear(512, 256)
self.l3 = torch.nn.Linear(256, 128)
self.l4 = torch.nn.Linear(128, 64)
self.l5 = torch.nn.Linear(64, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x)
model = Net()
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
# forward + backward + update
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d, %5d] loss:%.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_loss = 0.0
def test():
correct = 0
total = 0
with torch.no_grad():
for data in test_loader:
images, labels = data
outputs = model(images)
_, predicted = torch.max(outputs.data, dim=1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy on test set: %d %%' % (100 * correct / total))
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test()
2 处理数据部分
2.1 batch enumerate epoch
- 首先是batch_size,这是我们一次训练的输入数目。
- MNIST数据集(手写数字),有60000个样本的训练集和10000个样本的测试集。(都是已经划分好了,直接拿来用就可以)
- 一次性将60000个数据都喂给模型容易出现鞍点,但是每次训练1个样本虽然效果好但是开销太大,所以就提出了mini-batch的概念!
- mini-batch经常被简化说出batch,我们这里的batch_size就表示一次输入64个数据。这样60000个数据需要(60000/64)938次输入
- 训练一个batch,叫做enumerate;训练全部数据(938个enumerate)叫做一个epoch
batch_size = 64
2.2 transforms.Compose()函数
- 需要头文件
from torchvision import transforms
torchvision.transforms.Compose()
这个类作用:将图片变换操作(ToTensor+Normalize)串联起来(我理解为封装起来)
- ToTensor():数据集下载好之后,PIL数据读入进来是H*W*C 【通道数C在最后】,但是Pytorch处理数据的话,是C*H*W。这个函数的作用就是完成这个转换。
注意:Python图像库PIL是python的第三方图像处理课,但是由于其强大的功能与众多的使用者,目前已经被认为是python的官方图像处理库了。
transform.Normalize()
的作用:将数据从[0,1]变换为[-1,1],并且这里指定了平均值0.1307和标准差0.3081.【这些值都是前人算好的,我们直接使用就可以】
transform = transforms.Compose([
transforms.ToTensor(), # 将图像转为Tensor向量
transforms.Normalize((0.1307,), (0.3081,)) # 均值和标准差,都是前人已经算好的值
])
2.3 dataset和dataloader
- Dataset作用:下载数据,并且用刚刚定义的transform对数据进行变换,将数据包装起来,传给DataLoader。
- 我们再使用DataLoader这个类来更加快捷的对数据进行操作,例如确定batch_size大小,shuffle打乱数据,num_workers(选择数据处理时使用几个子进程,如果使用子进程,则需要将函数主体写进__main__函数中),也就是需要下方代码的第一行。
if __name__ == '__main__':
for epoch in range(10):
train(epoch)
test()
- 对train训练集合test测试集的操作类似,可以复制粘贴稍微修改一下。
train_dataset = datasets.MNIST(root='../dataset/mnist/',
train=True,
download=True,
transform=transform)
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(test_dataset,
shuffle=False,
batch_size=batch_size)
3 搭建模型部分
- 模型的名字Net是自己取的,后面括号里的是父类,必须继承父类
- 第二行和第三行是死代码,记住就可以,初始化必有
- 然后这边定义了5个线性层,self.后面的的l1,l2…等等都是自己对网络层取的名字。
- 需要注意括号内,上一层的输出,应该是下一层的输入【每两层之间的参数是不透明的,我们不用强求了解。】
- forward函数的x.view()是修改张量的形状,然后在第一层、第二层、第三层、第四层计算结束之后都要加一个relu()激活层,【因为两个线性之间如果没有激活层,那么就相当于一个线性层】
- 注意:第五层是直接输出的,不要激活函数
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.l1 = torch.nn.Linear(784, 512)
self.l2 = torch.nn.Linear(512, 256)
self.l3 = torch.nn.Linear(256, 128)
self.l4 = torch.nn.Linear(128, 64)
self.l5 = torch.nn.Linear(64, 10)
def forward(self, x):
x = x.view(-1, 784)
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x)
最后new出model
model = Net()
4 loss和优化部分
- 在这里计算损失用的是交叉熵,优化器用的是SGD,并且增加了冲量momentum
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
5 训练数据部分
-
每个epoch都要将running_loss损失值归零
-
在每个epoch里面,batch_size=64,for循环就是完成这个操作的。
-
并且enumerate可以将序号和data对应起来赋值给batch_idx和data
-
data里面封装的是输入的属性和一个目标值target。(模型就是需要预测出target的值)
-
optimizer.zero_grad()
梯度清零 -
下一步是使用模型model计算:
outputs = model(inputs)
-
然后计算loss值,用模型预测的outputs和target目标值去计算loss,刚刚定义的criterion中使用的是交叉熵CrossEntropyLoss,
loss = criterion(outputs, target)
-
接下来就是反向传播和优化器
loss.backward() optimizer.step()
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
inputs, target = data
optimizer.zero_grad()
# forward + backward + update
outputs = model(inputs)
loss = criterion(outputs, target)
loss.backward()
optimizer.step()
running_loss += loss.item()
if batch_idx % 300 == 299:
print('[%d, %5d] loss:%.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_loss = 0.0