今天学习pytorch的使用,参考Pytorch深度学习:60分钟快速入门 、PyTorch中关于backward、grad、autograd的计算原理的深度剖析,PyTorch中的nn.Conv1d与nn.Conv2d等文章。
首先确定自己的显卡型号,右键点击开始,打开设备管理器,看看你的显卡。根据显卡信息,打开https://pytorch.org/,往下滑根据你的情况查找安装命令。找到命令后,开始安装。
我是从cmd,用pip下载清华源的资源(可以直接在pycharm的terminal里下载,但是我的出了问题,解决不了,就从cmd下载了)。把你需要的全部下载。下载完输入python,import torch试试。
pip install torch –i https://pypi.tuna.tsinghua.edu.cn/simple
在pychram里使用,点开 file > settings
选择 python Interpreter,再点击+
在搜索框内搜索需要的 ,点击下方的Install package
然后就可以在pycharm里使用torch(或者你下载的东西)了。
Tensor:即张量。
Varibale:torch.autograd.Variable是Autograd的核心类,它封装了Tensor,并整合了反向传播的相关实现。Varibale包含三个属性:
如果我们需要计算某个Tensor的导数,那么我们需要设置其参数.requires_grad属性为True。当设置.requires_grad为True,程序将会追踪所有对于该张量的操作,当完成计算后通过调用.backward() ,自动计算所有的梯度, 所有梯度将会自动积累到.grad 属性。
import torch
from torch.autograd import Variable
x = Variable(torch.ones(2, 2), requires_grad=True)
print(x)
#结果
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
.backward()方法来自动计算所有的梯度,用于做反向传播
选出最佳模型的方式,其实就是利用梯度下降算法,选出损失函数最小的那个。在传统的机器学习当中,这个比较容易,直接算就行了。但是在深度学习当中,由于存在输入层,隐藏层,输出层,且隐藏层到底有多深,这些都是未知数,因此计算也会更加繁杂。
如果,在输出层输出的数据和我们设定的目标以及标准相差比较大,这个时候,就需要反向传播。利用反向传播,逐层求出目标函数对各神经元权值的偏导数,构成目标函数对权值向量的梯度,之所以算这个,是为了对权值的优化提供依据,等权值优化了之后,再转为正向传播……当输出的结果达到设定的标准时,算法结束深度学习——反向传播(Backpropagation)
torch.autograd.backward(
tensors,
grad_tensors=None,
retain_graph=None,
create_graph=False,
grad_variables=None)
参数说明:
1. tensors:用于计算梯度的tensor。
2. grad_tensors:在计算矩阵的梯度时会用到。grad_tensors其实也是一个tensor,它的shape一般需要和前面的tensor保持一致。引入参数grad_tensors可以解决欲求导的Tensor是非标量的情况(如果Tensor是 非标量,则需要指定一个gradient参数,它是形状匹配的张量)。
当前Variable(理解成函数Y)对leaf variable(理解成变量X=[x1,x2,x3])求偏导。
计算图可以通过链式法则求导。如果Variable是 非标量(non-scalar)的(即:Y中有不止一个y,即Y=[y1,y2,…]),且requires_grad=True。那么此函数需要指定gradient,它的形状应该和Variable的长度匹配(这个就很好理解了,gradient的长度体与Y的长度一直才能保存每一个yi的梯度值),里面保存了Variable的梯度。
backward()使用的各种情况:
def test_1():
x = Variable(torch.ones(2,2),requires_grad=True)
y = x * x * 3
out = y.mean()
out.backward()
print(x.grad)
#结果
tensor([[1.5000, 1.5000],
[1.5000, 1.5000]])
def test_2():
x = Variable(torch.ones(2, 2), requires_grad=True)
y=x+2
y.backward()
#结果
RuntimeError: grad can be implicitly created only for scalar outputs
test3:test_2的优化,通过y.sum()解决
def test_3():
x = Variable(torch.ones(2, 2), requires_grad=True)
y=x+2
y.sum()
y.sum().backward()
print(x.grad)
#结果
tensor([[1., 1.],
[1., 1.]])
指定一个gradient 参数,该参数是形状匹配的张量(上面提到的办法)
def test_4():
x = Variable(torch.ones(2, 2), requires_grad=True)
y=x+2
#放入一个和x一样大的张量,返回值的大小=x的大小
y.backward(torch.ones_like(x))
print(x.grad)
#结果
tensor([[1., 1.],
[1., 1.]])
3. retain_graph:每次 backward() 时,默认会把整个计算图free掉。一般情况下是每次迭代,只需一次 forward() 和一次 backward() ,但是也有例外。如果在当前backward()后,不执行forward() 而是执行另一个backward(),需要在当前backward()时,指定保留计算图,即backward(retain_graph)。retain_graph和create_graph两个参数作用相同。
4. grad_variables:此参数会丢弃,直接使用grad_tensors(不用管)
.backward()和.grad的关系:假设要求n对m求导,则n.backward(torch.Tensor),m.gard.data。
若定义输入 ,然后我们做的操作是 ,求解k的梯度?
解:
import torch from torch.autograd import Variable def test_1(): # (2) 若输入m=(x1,x2)=(2,3), 而k = (x1**2+3*x2,x2**2+2*x1),则 j = torch.zeros(2, 2) m = Variable(torch.Tensor([[2, 3]]), requires_grad=True) k = Variable(torch.zeros(1, 2)) k[0, 0] = m[0, 0] ** 2 + 3 * m[0, 1] k[0, 1] = m[0, 1] ** 2 + 2 * m[0, 0] # [1, 0] dk0/dm0, dk1/dm0 k.backward(torch.FloatTensor([[1, 0]]), retain_graph=True) # 需要两次反向求导 j[:, 0] = m.grad.data m.grad.data.zero_() # [0, 1] dk0/dm1, dk1/dm1 k.backward(torch.FloatTensor([[0, 1]])) j[:, 1] = m.grad.data print('jacobian matrix is:{}'.format(j)) #结果 jacobian matrix is:tensor([[4., 2.], [3., 6.]])
detach() :如果 x 为中间输出,x’ = x.detach 表示创建一个与 x 相同,但requires_grad==False 的variable。当反向传播到x'的时候,不继续回传求导了。detach_()不创建新变量,而是直接修改x。
使用torch.nn包来构建神经网络。一个nn.Module包含各个层和一个forward(input)方法,该方法返回output。
神经网络的典型训练过程如下:
1. 定义神经网络模型,它有一些可学习的参数(或者权重);
2. 在数据集上迭代;
3. 通过神经网络处理输入;
4. 计算损失(输出结果和正确值的差距大小)
5. 将梯度反向传播会网络的参数;
6. 更新网络的参数,主要使用 weight = weight - learning_rate * gradient 作为更新原则
torch.utils.data.DataLoader
将自定义的数据读取接口的输出或者PyTorch已有的数据读取接口的输入按照batch_size封装成Tensor,后续只需要再包装成Variable即可作为模型的输入。
在 pytorch 中数据传递按以下顺序:
- 创建 datasets ,也就是所需要读取的数据集。
- 把 datasets 传入DataLoader。
- DataLoader迭代产生训练数据提供给模型。
- dataset:数据读取接口
- batch_size:批训练数据量的大小,一般为2的N次方
- shuffle:是否打乱数据,一般在训练数据中会采用
- num_workers:这个参数必须大于等于0,为0时默认使用主线程读取数据,其他大于0的数表示通过多个进程来读取数据,可以加快数据读取速度,一般设置为2的N次方,且小于batch_size
参考:pytorch技巧 五: 自定义数据集 torch.utils.data.DataLoader 及Dataset的使用
Conv2d
class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
二维卷积,通常用于图像。卷积是为了进行特征提取,卷积的步骤为矩阵内內积乘法+将內积乘法的结果进行全加。
in_channels:输入图像通道数
out_channels:卷积产生的通道数
kernel_size:卷积核尺寸
stride:卷积步长
padding:填充操作,控制
padding_mode
的数目dilation:扩张操作,控制kernel点(卷积核点)的间距,默认值:1
groups:控制分组卷积,默认不分组,为1组
bias:为真,则在输出中添加一个可学习的偏差
参考:pytorch之torch.nn.Conv2d()函数详解
MaxPool2d
nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
池化层的作用为减少参数量。最大池化即选图像区域的最大值作为该区域池化后的值。
- kernel_size:最大池化的窗口大小
- stride:步长
- padding:填充
- dilation:扩张
- return_indices:布尔类型,返回最大值位置索引
- ceil_mode:布尔类型,为True,用向上取整的方法,计算输出形状;默认是向下取整
参考:torch.nn.MaxPool2d详解 、 池化层的作用总结:
ConvTranspose2d
nn.ConvTranspose2d(in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1)
一些相关的参数计算可以参考:Pytorch: conv2d、空洞卷积、maxpool2d、 ConvTranspose2d的输出特征图计算方式
使用的是CIFAR10数据集。请注意torch.utils.data.DataLoader的参数num_workers,如果num_workers 不为 0 ,也就是用多个线程处理数据的时候,需要放在if __name__ == '__main__'里才能正常运行,否则会报错。PyTorch入门学习:torch.utils.data.DataLoader
CIFAR10如果下载的很慢,可以在控制台中看它的下载地址,直接从浏览器下载cifar-10-python.tar.gz。下载完成后放在你的项目的data文件夹下就能使用。
CIFAR10介绍
- cifar-10数据集一共10类图片,每一类有6000张图片,加起来就是60000张图片,每张图片的尺寸是32x32,图片是彩色图
- 整个数据集被分为5个训练批次和1个测试批次,每一批10000张图片。测试批次包含10000张图片,是由每一类图片随机抽取出1000张组成的集合。剩下的50000张图片每一类的图片数量都是5000张,训练批次是由剩下的50000张图片打乱顺序,然后随机分成5份,所以可能某个训练批次中10个种类的图片数量不是对等的,会出现一个类的图片数量比另一类多的情况
- 每个data_batch中的数据包含4个字典键,分别是data、labels、batch_label和filenames。
转载:CIFAR-10 数据集可视化详细讲解(附代码)
main函数
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
#DataLoader迭代产生训练数据提供给模型
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
# 获得随机训练图像
dataiter = iter(trainloader) # iter用来生成迭代器,迭代器用于访问可迭代序列
images, labels = dataiter.next()
# 展示图片,用自定义的imshow函数,make_grid的作用是将若干幅图像拼成一幅图像
imshow(torchvision.utils.make_grid(images))
# 输出标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))
imshow()
# 图像展示
def imshow(img):
img = img / 2 + 0.5
npimg = img.numpy()
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
运行结果
frog dog deer plane
如果你的模型在别的py文件,注意文件名不要和类名一样,不然会报错。
#定义卷积神经网络
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 卷积 -->激活 -->池化操作
x = self.conv1(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, 2)
# 将前面操作输出的多维度的tensor展平成一维,然后输入分类器,-1
# 是自适应分配,指在不知道函数有多少列的情况下,根据原tensor数据自动分配列数
x = x.view(-1, 16 * 5 * 5)
# 调用分类器,分类器是一个简单的nn.Linear()
# 结构,在上边程序定义,其输入输出都是维度为一的值
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 s in size:
num_features *= s
return num_features
net = Net()
分析转载自:x = x.view(x.size()[0], -1) 的含义及理解
网络整体结构:[conv + relu + pooling] * 2 + FC * 3
从PyTorch中的nn.Conv1d与nn.Conv2d转载,需要的可以去原贴查看。
原始输入样本的大小:32 x 32 x 3
#使用nn.CrossEntropyLoss计算损失
#如果遇到维度问题可以使用torch.unsqueeze(target,dim=0),一维升二维
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
#训练网络
for epoch in range(2):
running_loss = 0.0
#enumerate在遍历中可以获得索引和元素值
for i,data in enumerate(trainloader,0):
inputs,labels = data
#包装成Variable,即可作为模型的输入
inputs,labels = Variable(inputs),Variable(labels)
#使用.zero_grad()把梯度归零,再用loss.backward()进行反向传播
optimizer.zero_grad()
#outputs是10类的概率值,大小为10*1
outputs = net(inputs)
#求loss
loss = criterion(outputs,labels)
loss = loss.float()
loss.backward()
#optimizer.step()用来更新权重
optimizer.step()
#输出loss
running_loss += loss.data
#每2000mini-batch打印一次
if i%2000 == 1999:
print('[%d, %5d] loss: %.3f' %
(epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
#训练结果
[1, 2000] loss: 2.196
[1, 4000] loss: 1.939
[1, 6000] loss: 1.729
[1, 8000] loss: 1.618
[1, 10000] loss: 1.552
[1, 12000] loss: 1.480
[2, 2000] loss: 1.443
[2, 4000] loss: 1.392
[2, 6000] loss: 1.369
[2, 8000] loss: 1.316
[2, 10000] loss: 1.316
[2, 12000] loss: 1.286
Finished Training
理解optimizer.zero_grad(), loss.backward(), optimizer.step()的作用及原理
#反向传播
print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)
net.zero_grad()
loss = loss.float()
loss.backward()
print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
#结果
conv1.bias.grad before backward
None
conv1.bias.grad after backward
tensor([-0.0430, 0.0959, -0.0853, -0.0467, -0.0118, 0.0588])
使用optimizer.step()执行参数更新。
import torch.optim as optim
# create your optimizer
optimizer = optim.SGD(net.parameters(), lr=0.01)
#in your trainning loop:
optimizer.zero_grad()
output = net(input)
loss = criter(output, target)
loss.backward()
#step通过梯度下降执行一步参数更新
optimizer.step()
#测试集的图片
dataiter = iter(testloader)
images,labels = dataiter.next()
#make_grid的作用是将若干幅图像拼成一幅图像
imshow(torchvision.utils.make_grid(images))
print('GroundTruth',' '.join('%5s' % classes[labels[j]] for j in range(4)))
#用于看10个类的预测结果
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
correct = 0
total = 0
for data in testloader:
images,labels = data
# outputs是10类的概率值,大小为10*1
outputs = net(Variable(images))
#使用max()函数对softmax函数的输出值进行操作,求出预测值索引
#dim是max函数索引的维度0/1,0是每列的最大值,1是每行的最大值
#返回值为两个tensor,第一个是每列/行的最大值,第二个是最大值的索引
#预测出最有可能的类
_, predicted = torch.max(outputs.data,1)
# print('Predicted:', ''.join('%5s' % classes[labels[j]] for j in range(4)))
total += labels.size(0) #第0维有几个数据
correct += (predicted == labels).sum() #预测正确的总数
#用于用于看10个类的预测结果
c = (predicted == labels).squeeze()
for i in range(4):
label = labels[i]
class_correct[label] += c[i]
class_total[label] += 1
#输出预测结果
print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))
#输出10个类的预测结果
for i in range(10):
print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))
输出
GroundTruth cat ship ship plane
测试结果
Accuracy of the network on the 10000 test images: 54 %
Accuracy of plane : 57 %
Accuracy of car : 62 %
Accuracy of bird : 28 %
Accuracy of cat : 54 %
Accuracy of deer : 47 %
Accuracy of dog : 31 %
Accuracy of frog : 63 %
Accuracy of horse : 61 %
Accuracy of ship : 60 %
Accuracy of truck : 77 %