【实习记录】pytorch学习持续更新

1-珍爱生命,远离tensorflow

今天学习pytorch的使用,参考Pytorch深度学习:60分钟快速入门 、PyTorch中关于backward、grad、autograd的计算原理的深度剖析PyTorch中的nn.Conv1d与nn.Conv2d等文章。

一、pytorch安装以及在pycharm中使用pytorch

首先确定自己的显卡型号,右键点击开始,打开设备管理器,看看你的显卡。根据显卡信息,打开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包含三个属性:

  1. data: 即存储的数据信息。
  2. .grad(): 保存了data的梯度,grad本身也是个Variable而非Tensor,与data形状一致, 每次在计算backward时都需要将前一时刻的梯度归零,否则梯度值会一直累加。
  3. grad_fn: 指示梯度函数是哪种类型,用于反向传播的梯度计算之用。如果变量是通过一个操作创建的(这里的操作指的是任意操作,比如y = x + 2,则y是通过一个操作创建的),它就会有grad_fn,如果是由用户创建(自己定义的,比如x = Variable(torch.ones(2, 2), requires_grad=True)),则它的grad_fn为None。volatile==True 就等价于 requires_grad==False。

三、梯度

如果我们需要计算某个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。

若定义输入 m = (x_{1},x_{2}) = (2,3) ,然后我们做的操作是 k = (x_{1}^{2}+3*x_{2},x_{2}^{2}+2*x_{1}),求解k的梯度?

解:

\frac{\partial k_{1}}{\partial x_{1}} = 2x_{1} ,\frac{\partial k_{1}}{\partial x_{2}} = 3\frac{\partial k_{2}}{\partial x_{1}} = 2\frac{\partial k_{2}}{\partial x_{2}} = 2x_{2}

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 中数据传递按以下顺序:

  1. 创建 datasets ,也就是所需要读取的数据集。
  2. 把 datasets 传入DataLoader。
  3. 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的输出特征图计算方式

(1)导入数据集

使用的是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

(2)定义神经网络模型

如果你的模型在别的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

  • 第一次卷积:使用6个大小为5 x 5的卷积核,故卷积核的规模为(5 x 5) x 6;卷积操作的stride参数默认值为1 x 1,32 - 5 + 1 = 28,并且使用ReLU对第一次卷积后的结果进行非线性处理,输出大小为28 x 28 x 6;
  • 第一次卷积后池化:kernel_size为2 x 2,输出大小变为14 x 14 x 6;
  • 第二次卷积:使用16个卷积核,故卷积核的规模为(5 x 5 x 6) x 16;使用ReLU对第二次卷积后的结果进行非线性处理,14 - 5 + 1 = 10,故输出大小为10 x 10 x 16;
  • 第二次卷积后池化:kernel_size同样为2 x 2,输出大小变为5 x 5 x 16;
  • 第一次全连接:将上一步得到的结果铺平成一维向量形式,5 x 5 x 16 = 400,即输入大小为400 x 1,W大小为120 x 400,输出大小为120 x 1;
  • 第二次全连接,W大小为84 x 120,输入大小为120 x 1,输出大小为84 x 1;
  • 第三次全连接:W大小为10 x 84,输入大小为84 x 1,输出大小为10 x 1,即分别预测为10类的概率值。

(3)定义损失函数、优化器

#使用nn.CrossEntropyLoss计算损失
#如果遇到维度问题可以使用torch.unsqueeze(target,dim=0),一维升二维
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

(4)训练网络

#训练网络
    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() 

(5)测试集上测试网络(10000个图像的测试结果+10个类的测试结果)

#测试集的图片
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 %

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习编程?其实不难,不过在学习编程之前你得先了解你的目的是什么?这个很重要,因为目的决定你的发展方向、决定你的发展速度。
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面设计类、前端与移动、开发与测试、营销推广类、数据运营类、运营维护类、游戏相关类等,根据不同的分类下面有细分了不同的岗位。
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生学习Java开发,但要结合自身的情况,先了解自己适不适合去学习Java,不要盲目的选择不适合自己的Java培训班进行学习。只要肯下功夫钻研,多看、多想、多练
Can’t connect to local MySQL server through socket \'/var/lib/mysql/mysql.sock问题 1.进入mysql路径
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 sqlplus / as sysdba 2.普通用户登录
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服务器有时候会断掉,所以写个shell脚本每五分钟去判断是否连接,于是就有下面的shell脚本。
BETWEEN 操作符选取介于两个值之间的数据范围内的值。这些值可以是数值、文本或者日期。
假如你已经使用过苹果开发者中心上架app,你肯定知道在苹果开发者中心的web界面,无法直接提交ipa文件,而是需要使用第三方工具,将ipa文件上传到构建版本,开...
下面的 SQL 语句指定了两个别名,一个是 name 列的别名,一个是 country 列的别名。**提示:**如果列名称包含空格,要求使用双引号或方括号:
在使用H5混合开发的app打包后,需要将ipa文件上传到appstore进行发布,就需要去苹果开发者中心进行发布。​
+----+--------------+---------------------------+-------+---------+
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 nu...
第一步:到appuploader官网下载辅助工具和iCloud驱动,使用前面创建的AppID登录。
如需删除表中的列,请使用下面的语法(请注意,某些数据库系统不允许这种在数据库表中删除列的方式):
前不久在制作win11pe,制作了一版,1.26GB,太大了,不满意,想再裁剪下,发现这次dism mount正常,commit或discard巨慢,以前都很快...
赛门铁克各个版本概览:https://knowledge.broadcom.com/external/article?legacyId=tech163829
实测Python 3.6.6用pip 21.3.1,再高就报错了,Python 3.10.7用pip 22.3.1是可以的
Broadcom Corporation (博通公司,股票代号AVGO)是全球领先的有线和无线通信半导体公司。其产品实现向家庭、 办公室和移动环境以及在这些环境...
发现个问题,server2016上安装了c4d这些版本,低版本的正常显示窗格,但红色圈出的高版本c4d打开后不显示窗格,
TAT:https://cloud.tencent.com/document/product/1340