0%

pytorch

基础知识

魔术方法: P23

调试: P27

1
2
import ipdb
ipdb.set_trace()

带下划线_的函数会修改Tensor本身,比如x.add_(y)和x.add(y)的区别

Tensor

Numpy与Tensor共享内存
b = a.numpy() # Tensor -> Numpy

a = t.from_numpy(a)# Numpy -> Tensor
x = x.cuda()
tensor的操作:torch.function,tensor.function.
普通索引共享内存
高级索引不共享内存
线性代数函数 P70
自动广播原则: unsqueese(view),expand(expand_as)
tensor=Tensor+Storage
持久化和加载: t.save(a,’a.pth’) b=t.load(‘a.pth’) P77
%timeit -n 10


Variable和autograd

from torch.autograd import Variable
三个属性
data: 对应Tensor
grad: 梯度,和data大小一样,也是Variable
grad_fn: 指向Function对象,用于构建计算图。用户创建对应叶子节点,grad_fn=None.记录的是它什么操作的输出。
Variable的构造函数的关键字参数:requires_grad(bool):是否需要求导; volatile(bool):True表示之上的计算图都不会求导

1
2
3
4
5
6
x = Variable(t.ones(2,2),requires_grad = True)
y = x.sum()
y.grad_fn
y.backward()
x.grad
x.grad.data.zero_() # 反向传播清零

variable.backward(grad_variable=None, retain_graph=None, create_graph=None)
假设用户输入的数据是真实的不需要求导的。
数值在前向传导过程成会保存成buffer,计算梯度之后自动清空。多次反向求导可以使用关键字参数retain_graph=True
retain_graph=True 实现多次反向传播????

反向传播过程中非叶子节点的导数在计算完之后就会清空,y=x*w,z=y.sum() 其中y.grad会清空。其对应的方法有两种,P92,t.autograd.grad(z,y)和hook
扩展Autograd Function:P95 自己实现前向和反向


nn.Module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def \_\_init\_\_():
super(Net,self).__init__()
\# 有参数的层的定义
def forward(self,x):

net = Net()
print(net)
list(net.parameters())
for name,paramenters in net.named_parameters():
print(name,':',parameters.size())

out = net(input)
net.zero_grad()
out.backwad(Variable(t.ones(1,10)))
> ???

nn.Sequential()
nn.ModuleList()
nn.ParameterList()
在优化器中为各层分别设置学习率
nn.functional对应nn.Module
参数初始化:
nn.Conv2d: nSamplesnChannelsHeight*Widh
单样本: input.unsqueeze(0)
model.train()
model.eval()
前向或者后向注册钩子函数,P125,可以查看中间层。
获取网络的模块属性:getattr(module)P128
保存模型:t.save(net.state_dict(), ‘net.pth’)
加载模型:net2=Net() net2.load_state_dict(t.load(‘net.pth’))
多个GPU并行操作


损失函数

1
2
3
4
5
output = net(input)
target = Variable(t.arrange(0,10))
criterion = nn.MSELoss()
loss = criterion(output, target)
loss
1
2
3
4
net.zero_grad()
print(net.con1.bias.grad)
loss.backward()
print(net.conv1.bias.grad)

优化器

1
2
3
learning_rate=0.01
for f in net.parameters():
  f.data.sub_(f.grad.data*leaning_data)
1
2
3
4
5
6
7
8
9
10
11
12
import torch.optim as optim
# 优化器
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 训练过程中 梯度清零
optimizer.zero_grad() # 等效于 net.zero_grad()
# 损失函数
output=net(input)
loss = criterion(output, target)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()

CIFAR-10分类

  1. 数据预处理:transform,trainset,trainloader,testset,testloader
  2. 定义网络:Net(nn.Module),super(Net,self).init(),forward()
  3. 定义损失函数和优化器
  4. 训练网络
    1. 输入数据
    2. 梯度清零
    3. 前向传播+反向传播
    4. 更新参数

数据处理

自定义的数据集需要继承Dataset类,并实现两个Python魔术方法
__getitem:返回一个样本。obj[index]=obj.getitem(index)
__len
:返回样本数量。
transform=T.Compose()
trans=T.Lambda(lambda img:img.rotate(randonm()*360))
ImageFolder(root,transform,target_transform,loader)P139
self.class_to_idx了解label和文件夹名的映射关系
DataLoader()定义shuffle等P142
取样:P146
工具包:torchvision P147

  • models:训练好模型
  • dataset:数据集加载
  • transforms:数据预处理操作,主要针对Tensor和PIL Image对象的操作

make_grid和save_img
可视化工具 Tensorboard和visdom
tensor_board和TensorboardX
visdom:env pane

%env LS_COLORS=None
!tree —charset ascii data/dogcat
Tensor—numpy:np.array(Tensor) torch.Tensor(np.darray)
PIL.image—numpy:np.asarray(PIL.image) image.fromarray(numpy.ndarray)
PIL.image—Tensor:trans = transforms.Compose([transforms.ToTensor()]) tens = trans(img) ToPILImage()

GPU

P158
with t.cuda.device(1):
t.cuda.set_device(1)
export CUDA_VISIVLE_DEVICES=1
b = t.load(‘a.pth’)
c = t.load(‘a.pth’,map_location=lamdba storage, loc: storage)
d = t.load(‘a.pth’,map_location={‘cuda:1’:’cuda:0’})
Module和Optimizer: state_dict

Dog.vs.Cat

checkpoints/ 中间模型
data/

  • __init__.py
  • dataset.py
    • def init(self,root,transform=None, train=True, test=False):
    • def getitem(self,index):
    • def len(self):
  • get_data.sh
    models/
  • __init__.py
  • AlexNet.py
  • BasicModule.py
  • ResNet34.py
    utils/
  • __init__.py
  • visualize.py
    config.py
    main.py
    requirements.txt
    README.md

main.py

  • def train(**kwargs):
  • def test(**kwargs):
  • def val(model,dataloader):
  • def help():
1
2
3
if __name__=='main':
import fire
fire.Fire()

pytorch 中文文档

https://pytorch-cn.readthedocs.io/zh/latest/package_references/functional/

PyTorch实战指南 第六章 Dog.VS.Cat

这是根据深度学习框架:PyTorch入门与实践这本书的第六章写的代码,是关于猫狗识别的,在这个过程中,一边看,一边写,刚开始是运行作者已经写好的代码,后来自己在jupyter上进行复制的复现,发现import无法导入ipynb文件,在使用了Ipynb_importer.py之后可以实现同一文件内导入ipynb模块,如果是在其他文件中进行导入,会有点费事,以下会记录Ipynb_importer.py的用法。因为费事,自己开始开始使用pycharm+jupyter的方式,直接自己根据作者提供的源码进行编写,在编写的过程中接受作者的思想。用pycharm的不方便的地方是无法直接运行测试,所以采取的是对自己不熟悉的模块或者方法,用jupyter进行测试,而直接编写则是pycharm。但是感觉pycharm还是没有那么好用,可能是自己用的少。我是按照data、model、util、main+config、requirement的顺序编写的。在编写函数的过程中,因为刚开始不理解各个模块是怎么组织起来的,所以都是从简单的开始,所以函数的位置和作者的不一样,其中对于model.save和model.load、vis.plot和vis.log的封装让我感觉很有意思,刚开始是编写的时候只能直接打上问号,因为不懂这么编写的意义,但在编写主函数main的时候才感觉到了这种编写的好处,基本把模型训练和对模型、结果的处理完全分离开,避免了耦合性很强的后果。


Ipynb_importer.py

我通过几次测试发现,import Ipynbimporter 只需要放在你的当前要运行的文件中即可,然后在其他文件下的init.py 中导入所有的当前文件夹中的Module,就像这样
/first/second/models/
——-_init
.py
——————- None
——-BasicModule.ipynb
——-AlexNet.ipynb
—————from models.BasicModule import BasicModule

/first/main.py
import Ipynb_importer
from models import AlexNet

之所以在AlexNet中写models.BasicModule是因为直接导入BasicModule会报错,我根据dict的输出发现有问题,这一点和官网介绍的有一点区别,我没有实现官网说明的跨文件夹导入。因为如果改文件夹导入的话,models.BasicModule要接着换成相应的名字,与我预想的不一致,我预想的是不管在哪里导入,已经导入的应该不受影响才对。


ipynb-py.sh

之后发现了这个神器,可以把ipynb转化成.py,还是挺好用的,转化之后也没问题。


同时,借助这次实验,自己对python的掌握也更深了一点。


不过对于网络的构成还是有一些问题,那就是网络为什么这么写,这应该属于理论的东西。还需要进一步加强。


这次实验一共用了三天才完全搞懂,可以说其中涉及到的函数的用法基本都明白了。
本意是记录自己,不过如果有任何问题,欢迎交流。


PyTorch实战指南 第七章 DCGAN

这一次实现的也比较慢,用了小三天才做完,现在记录一下其中学到的几个东西。

__file__:

用来获取模块所在路径 可能是一个相对路径,可能是一个绝对路径,
如果当前文件包含在sys.path里面,那么,__file__返回一个相对路径!
也可以认为获取模块的名字
最后的落脚点一定是XX/XX.py
类没有这个属性

1
2
3
4
5
6
7
8
9
In [1]: import numpy
In [3]: numpy.__file__
Out[3]: 'F:\\Programs\\Anaconda3\\lib\\site-packages\\numpy\\__init__.py'
In [6]: numpy.random.__file__
Out[6]: 'F:\\Programs\\Anaconda3\\lib\\site-packages\\numpy\\random\\__init__.py'

$ python test.py ##print(__file__)
test.py

__name__:

__name__就是标识模块的名字的一个系统变量。这里分两种情况:假如当前模块是主模块(也就是调用其他模块的模块),那么此模块名字就是__main__,通过if判断这样就可以执行“__mian__:”后面的主函数内容;假如此模块是被import的,则此模块名字为文件名字(不加后面的.py),通过if判断这样就会跳过“__main__:”后面的内容。
这个模块可以是文件夹的名字,可以是类的名字,可以是__mian__”,XX的形式。
可以用于获取当前文件的文件名
通过上面方式,python就可以分清楚哪些是主函数,进入主函数执行;并且可以调用其他模块的各个函数等等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# test.py
## print(__file__)
## print(__name__)
# test2.py
## import test
## print(__file__)
## print(__name__)
H:\GitHub\pytorch_learn\Chapter7\test.py
test
test2.py
__main__

## from test import ccc
## print(ccc.__name__)
## print(__file__)
## print(__name__)
ccc
test2.py
__main__

type():

返回对象的类型
如果是module,则返回module
如果是类的实例,则返回类的名称,这个名称以XXX.XXX的形式返回,从import的第一个开始算起。
常用于判断数据类型,在pytorch中,用于返回模型名称,这个用法很巧妙,相当于返回了子类的类型名字
我觉得没有理解作者是怎么用的。在父类里的type(self) 返回的是子类的类名

1
2
3
4
5
6
7
8
9
10
11
12
In [1]: import numpy

In [2]: type(numpy)
Out[2]: module

In [3]: a = numpy.array(1)

In [4]: type(a)
Out[4]: numpy.ndarray

In [5]: type(numpy.array)
Out[5]: builtin_function_or_method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class A:
pass

class B(A):
pass

isinstance(A(), A) # returns True
type(A()) == A # returns True
isinstance(B(), A) # returns True
type(B()) == A # returns False

class A(object):
def __init__(self):
print(type(self))
pass

class B(A):
def __init__(self):
super(B,self).__init__()
print(type(self))
pass

import test
test.B()

<class 'test.B'>
<class 'test.B'>


__class__:

和type类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A(object):
def __init__(self):
print(type(self))
print(self.__class__)
pass

class B(A):
def __init__(self):
super(B,self).__init__()
print(type(self))
print(self.__class__)
pass

<class 'test.B'>
<class 'test.B'>
<class 'test.B'>
<class 'test.B'>

获取config源码

打印参数,方便输入参数
inspect.getsource

1
2
3
from inspect import getsource
source = getsource(opt.__class__)
print(source)