深度学习的3个主要步骤如下:
- 定义一个函数集F。
- 定义一个评价函数:其输入是F中的某个函数f(实际上是决定了函数表达式的参数),输出是f的好坏。
- 从F中找到一个最好的函数。
神经网络其实就是一个函数集,我们通过定义神经网络的结构,决定了函数的一般形式。神经网络的参数则决定了 函数的表达式,这些参数是计算机根据一定的规则学习的。
定义函数集
在上一节中,我们已经定义了一个简易的神经网络,其代码如下:
#神经网络
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import torch.utils.data as Data
#神经网络需要定义两个函数
#分别是构造函数,前向传播
#自定义的神经网络需要继承nn.Module
class Net(nn.Module):
#构造函数
def __init__(self):
super(Net, self).__init__()
#卷积层三个参数:in_channel, out_channels, 5*5 kernal
self.con1 = nn.Conv2d(3, 10, 5)
self.con2 = nn.Conv2d(10, 10, 5)
#全连接层两个参数:in_channels, out_channels
self.fc1 = nn.Linear(10 * 5 * 5, 10)
self.fc2 = nn.Linear(10, 10)
self.fc3 = nn.Linear(10, 10)
#前向传播
def forward(self, input):
#卷积 --> 激活函数(Relu) --> 池化
x = self.con1(input)
x = F.relu(x)
x = F.max_pool2d(x, (2, 2))
#重复上述过程
x = self.con2(x)
x = F.relu(x)
x = F.max_pool2d(x, (2, 2))
#展平
x = x.view(-1, self.num_flat_features(x))
#全连接层
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
#展平
def num_flat_features(self, x):
size = x.size()[1:]
num_features = 1
for i in size:
num_features = num_features * i
return num_features
定义评价函数
通常使用损失函数(Loss Function)来定义一个函数的好坏。损失函数的值越大,则该函数越不好。
常见的损失函数有交叉熵(常用于分类问题)、均方根误差(常用于回归问题)等。
在torch.nn中定义了常见的损失函数。
#交叉熵
criterion = nn.CrossEntropyLoss()
寻找最优函数
寻找最优函数本质是一个无约束优化问题,目标是求损失函数的最小值。
m
i
n
(
L
)
L
=
∑
i
=
1
N
l
i
min(L) \\ L = \sum_{i=1}^{N}l_i
min(L)L=i=1∑Nli
其中
l
i
l_i
li的自变量是F中的某个函数,实际上就是决定了函数具体表达形式的参数,如权重w和表差b。我们的目标就是求使得L最小的参数w和b。
最朴素的优化方法是梯度下降,常见的方法还有SGD,SGDM,Adam,Adagrad等,常见的优化函数定义在torch.optim中。他们的核心都是求导。torch为我们提供了自动求导功能,就是其特点之一。
自动求导的步骤如下:
- 设置优化器种类:如Adam等。
- 梯度清零:清楚缓存中的梯度。
- 前向传播:计算输出和损失函数。
- 向后传播:本质是链式法则
- 参数更新
具体训练的代码如下:
#开始训练
#初始化神经网络中的参数
net = Net()
#定义损失函数种类:交叉熵
criterion = nn.CrossEntropyLoss()
#定义优化器种类:Adam
optimizer = optim.Adam(net.parameters(), lr = 0.001)
#定义变换:转化成Tensor。必须转化成张量形式才能求导。
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]
)
#用于训练的训练集和测试集
transet = torchvision.datasets.CIFAR10(root = './data', train = True, download=True, transform = transform)
testset = torchvision.datasets.CIFAR10(root = './data', train=False, download=True, transform = transform)
trainLoader = Data.DataLoader(dataset = transet, batch_size = 40, shuffle = True)
testLoader = Data.DataLoader(dataset = testset, batch_size = 40, shuffle = False)
num_peochs = 1
#开始训练:num_peochs是训练周期数
for epoch in range(num_peochs):
correct = 0
total = 0
run_loss = 0.0
for i, data in enumerate(trainLoader):
input, label = data
input, label = input, label
#梯度清零
optimizer.zero_grad()
#前向传播:计算输出
outputs = net(input)
#计算损失函数
lossValue = criterion(outputs, label)
#反向传播
lossValue.backward()
#参数更新
optimizer.step()
run_loss += lossValue.item()
num = 20
if i % num == num - 1:
print('[%d, %5d] loss : %.3f' % (epoch + 1, i + 1, run_loss / num))
run_loss = 0
#训练集准确率
_, pred = outputs.max(1)
correct += (pred == label).sum().item()
total += label.size()[0]
print("训练集准确率:", correct / total)
#打印训练结束标识符
print("finished training!")
完整代码
#神经网络
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import torch.utils.data as Data
#神经网络需要定义两个函数
#分别是构造函数,前向传播
#自定义的神经网络需要继承nn.Module
class Net(nn.Module):
#构造函数
def __init__(self):
super(Net, self).__init__()
#卷积层三个参数:in_channel, out_channels, 5*5 kernal
self.con1 = nn.Conv2d(3, 10, 5)
self.con2 = nn.Conv2d(10, 10, 5)
#全连接层两个参数:in_channels, out_channels
self.fc1 = nn.Linear(10 * 5 * 5, 10)
self.fc2 = nn.Linear(10, 10)
self.fc3 = nn.Linear(10, 10)
#前向传播
def forward(self, input):
#卷积 --> 激活函数(Relu) --> 池化
x = self.con1(input)
x = F.relu(x)
x = F.max_pool2d(x, (2, 2))
#重复上述过程
x = self.con2(x)
x = F.relu(x)
x = F.max_pool2d(x, (2, 2))
#展平
x = x.view(-1, self.num_flat_features(x))
#全连接层
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
#展平
def num_flat_features(self, x):
size = x.size()[1:]
num_features = 1
for i in size:
num_features = num_features * i
return num_features
#开始训练
#初始化神经网络中的参数
net = Net()
#定义损失函数种类:交叉熵
criterion = nn.CrossEntropyLoss()
#定义优化器种类:Adam
optimizer = optim.Adam(net.parameters(), lr = 0.001)
#定义变换:转化成Tensor。必须转化成张量形式才能求导。
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))]
)
#用于训练的训练集和测试集
transet = torchvision.datasets.CIFAR10(root = './data', train = True, download=True, transform = transform)
testset = torchvision.datasets.CIFAR10(root = './data', train=False, download=True, transform = transform)
trainLoader = Data.DataLoader(dataset = transet, batch_size = 40, shuffle = True)
testLoader = Data.DataLoader(dataset = testset, batch_size = 40, shuffle = False)
num_peochs = 1
#开始训练:num_peochs是训练周期数
for epoch in range(num_peochs):
correct = 0
total = 0
run_loss = 0.0
for i, data in enumerate(trainLoader):
input, label = data
input, label = input, label
#梯度清零
optimizer.zero_grad()
#前向传播:计算输出
outputs = net(input)
#计算损失函数
lossValue = criterion(outputs, label)
#反向传播
lossValue.backward()
#参数更新
optimizer.step()
run_loss += lossValue.item()
num = 20
if i % num == num - 1:
print('[%d, %5d] loss : %.3f' % (epoch + 1, i + 1, run_loss / num))
run_loss = 0
#训练集准确率
_, pred = outputs.max(1)
correct += (pred == label).sum().item()
total += label.size()[0]
print("训练集准确率:", correct / total)
#打印训练结束标识符
print("finished training!")