尽量少使用显式循环
用向量化(并行运算) 即将元素作为向量进行计算
numpy.dot(x,y)
numpy.exp(v)
等等 numpy有很多向量化函数
即为点乘
平时计算的时候也可以直接用向量化计算,速度快很多神经网络——堆叠的线性分类器
一个网元就是一个线性分类器激活函数的选择 ReLU>sigmod
ReLU最常用,sigmod基本不用tensor = 张量
在pytorch中,matrix即为张量tensor,list,numpy array, tensor list的 转换方法
list = [[1,2,3],[3,4,5]] tensor = torch.Tensor(list) #List->Tensor narray = numpy.array(list) #List->narray tensor = torch.from_numpy(narray) #narray->tensor narray = tensor.numpy() #Tensor->narray 注意list narray tensor之间的转换方法
将tensor list转变为tensor: 用torch.stack而不是torch.Tensor tensor_list = [tensor0,tensor1,tensor2..]=>tensor tensor = torch.stack(tensor_list) 注意tensor中的格式必须相同,如果想要和list一样合并变长tensor必须用cat)
Tensor数学运算
tensor.sub(b) #tensor-b fin = torch.add(tensor1,tensor2) #fin = tensor1+tensor2 (mul div同add) torch.sum() #批矩阵相乘函数 torch.bmm(A,B)
对numpy array求math function,不要用math.function,直接用numpy.function如
np.exp(nparray)
如果对tensor求math函数的报错
only one element tensors can be converted to Python scalars
的话,就将tensor转化为numpy array再用numpy库的数学函数求torch.Varibale和torch.Tensor用法基本一致,可以替换使用
Variable
包装了一个Tensor
,并且保存着梯度和创建这个Variable
function的引用
本质上Variable和Tensor没有什么区别,
不过Variable会放在一个计算图里面,
可以进行前向传播和反向传播以及求导显卡信息
nvidia-smisum(condition)
计算满足condition的总数
eg:sum(predict == label)
preidict 是一个list
label是一个list
他们形状相同
则统计两个列表中对应元素相同的个数batch
每一次从训练集/验证集/预测集 里面取数(从dataloader里取),都是以一个batch为一组来取
按batch进行训练的模型,在使用的时候也是按batch进行预测,输出的也是按batch的输出值释放CUDA显存
torch.cuda.empty_cache()
保存和加载模型
#直接保存模型和参数 torch.save(model_object, 'resnet.pth') #直接加载模型和参数 model = torch.load('resnet.pth') #但要注意! 加载的时候,得先运行网络的定义!(即要先有网络结构,它才会把加载的参数填入网络)
#加载预训练模型 https://blog.csdn.net/lscelory/article/details/81482586
测试的时候爆显存有可能是忘记设置no_grad, 示例代码如下:
with torch.no_grad(): for ii,(inputs,filelist) in tqdm(enumerate(test_loader), desc='predict'): if opt.use_gpu: inputs = inputs.cuda() if len(inputs.shape) < 4: inputs = inputs.unsqueeze(1) else: if len(inputs.shape) < 4: inputs = torch.transpose(inputs, 1, 2) inputs = inputs.unsqueeze(1)
先设置不使用梯度,然后将测试时候的batchsize设置成训练时候的二分之一或者三分之一就不会爆了。
可能原因是测试的时候真的需要更大的显存。指定使用哪块GPU 0123
os.environ["CUDA_VISIBLE_DEVICES"] = 3
保存和加载模型 ,获得模型权重参数
# 保存和加载整个模型 torch.save(model, 'model.pkl') model = torch.load('model.pkl') # 仅保存和加载模型参数(推荐使用) torch.save(model.state_dict(), 'params.pkl') # 注意是先在里面torch.load文件 在加载到model(网络实例)的权重字典里 model.load_state_dict(torch.load('params.pkl'))
得到模型权重:
model.state_dict
查看trainset打的标签
trainset.class_to_idx
nn.Linear层
就相当于一个y = w·x + b
对于带有梯度的tensor(由于variable和tensor合并了, 在cuda且求了grad),要对其操作则要
tensor.cpu().detach() 再进行运算操作
detach
可以去掉去梯度部分Pytorch 训练和测试时记得加 model.train 和 model.eval 设置模型模式
如果用到了BN和dropout,用PyTorch进行训练和测试时一定注意要把实例化的model指定train/eval,eval()时,框架会自动把BN和DropOut固定住,不会取平均,而是用训练好的值,不然的话,一旦test的batch_size过小,很容易就会被BN层导致生成图片颜色失真极大。
这两个方法是针对在网络train和eval时采用不同方式的情况,比如Batch Normalization和DropoutClass Inpaint_Network() ...... Model = Inpaint_Nerwoek() #train: Model.train(mode=True) ..... #test: Model.eval()
glob模块 (非常有用的文件读取操作模块)
glob是python自己带的一个文件操作相关模块,用它可以查找符合自己目的的文件,类似于Windows下的文件搜索,支持通配符操作,_,?,[]这三个通配符,_代表0个或多个字符,?代表一个字符,[]匹配指定范围内的字符,如[0-9]匹配数字。两个主要方法如下。
①glob方法:
glob模块的主要方法就是glob,该方法返回所有匹配的文件路径列表(list);该方法需要一个参数用来指定匹配的路径字符串(字符串可以为绝对路径也可以为相对路径),其返回的文件名只包括当前目录里的文件名,不包括子文件夹里的文件。
比如:glob.glob(r’c:*.txt’) #我这里就是获得C盘下的所有txt文件 glob.glob(r’E:\pic**.jpg’) #获得指定目录下的所有jpg文件 #使用相对路径: glob.glob(r’../*.py’)
②iglob方法
获取一个**迭代器**( iterator )对象,使用它可以逐个获取匹配的文件路径名。与glob.glob()的区别是:glob.glob同时获取所有的匹配路径,而 glob.iglob一次只获取一个匹配路径。
这样就可以不一次性读完所有文件,节约内存
下面是一个简单的例子:f = glob.iglob(r'../*.py') print f >> <generator object iglob at 0x00B9FF80> for py in f: print py
常用help看用法..torch里的函数用法繁多
向量运算的一个注意点:要先去掉梯度!
涉及梯度计算的Tensor(以前是被封装成Variable,但后来合并了),需要用.data
来取得其tensor
eg:loss.data
outputs.data
损失函数返回值,模型输出,都是含有梯度的向量,需要用.data
再参与运算!注意单元素tensor!
Use tensor.item() to convert a 0-dim tensor to a Python number
比如每个batch的loss返回值、sum(tensor1==tensor2)等都是单元素tensor
tensor和普通数字不能随意运算,不然会出错
要注意,tensor运算得到的结果也是tensor,就算是sum(tensor1==tensor2)得到相等元素数量,得到的也是tensor(x) (x为相等元素数量,一个常数),依然需要sum(temsor1==temsor2).item()才能参与普通数字运算
否则会出莫名其妙的错误!:比如t1 = torch.Tensor([2,3,4,5]) t2 = torch.Tensor([2,3,7,6]) sum(t1==t2) >> tensor(2,dtype=torch.uint8) sum(t1==t2)/10 >> tensor(0,dtype=torch.uint8) torch.sum(t1==t2) >> tensor(2) torch.sum(t1==t2)/10 >> tensor(0) #这样才正确 sum(t1==t2).item()/10 >> 0.2 torch.sum(t1==t2).item()/10 >> 0.2 #tensor除(/)一个常数是整除!
数据集
最好使用三个数据集:训练集、验证集、测试集
sklearn.model_selection.train_test_split
函数可用于方便的划分数据集dropout 和 dropout2d的区别
torch.nn.Dropout对所有元素中每个元素按照概率0.5更改为零
而torch.nn.Dropout2d是对每个通道按照概率0.5置为0(即一个通道全为0)对于网络
class model(nn.Module) ... net = model() model.features net
注意
a = a.cuda()
要赋值回去torch的交叉熵的输入第一个位置的输入应该是在每个label下的概率, 而不是对应的label
否则会报错dimension out of range (expected to be in range of [-1, 0], but got 1)返回对象的属性值
vars()
用法vars(object)
可用于很多封装对象如dataloader等等交叉熵损失函数中的weight参数 (权重)
weight = torch.FloatTensor([0.13859937, 0.5821059, 0.63871904, 2.30220396, 7.1588294, 0]).cuda()
迁移学习修改网络应该直接修改层,修改线性层的out_features会遇到问题
#应该直接修改最后一层网络 vgg.classifier[6] = nn.Linear(4096,2)
注意初始化网络结构和传入参数到网络里进行计算的区别
初始化网络实体的时候是传入init()里的参数,相当于初始化网络部件
传入参数是传入forward()里的传输,这个传入的就需要计算了
先初始化网络再传入参数进行计算输入损失函数的真实标签项,必须为long类型 不能是tensor float!tensor int!
具体如此生成:
list = [1,2,3] tensor = torch.LongTensor(list)
cuda runtime error: device-side assert triggered at xxx
这个问题一般来自模型输出的label数量和标签类别种类数量不同。要洗一遍数据集的标签自定义层
可以自己定义层,只需要继承自
nn.Module
并实现forward()
函数问题:需要实现backwards函数吗?它会自动求导吗?:
pytorch可以自动求导,但如果实现的层不可导,就需要自己实现梯度的反向传递(比如存在if条件,孤立点,拐点,就需要自定义求导式)也就是所谓的 “Extending torch.autograd”. 官网虽然给了例子
以下举例,自己建立了一个计算Gram Matrix 格拉姆矩阵的层(由于是可导的,所以不需要自己实现)
继承的时候别忘了
super().__init__()
notes_0915
2018-9-15
赏