🤾
MLStudy
  • README
  • Linux
    • command
    • Basic_commands
    • Advanced_commands
    • Linuxvirtual_machine_operate
    • virtual machine
  • Python
    • Deploy_Issue
    • Model_Analysis
    • Model_concept
    • Model_Grammar
    • Python_print_format
    • Model_ComFuc
    • Deep_learning_know
  • Theoretical knowledge
    • MLE_MAP_Bayesian
    • TV_denoise
  • Research
    • Writing
    • How to read
  • To_be_classified
    • tqdm
    • fast_visit_github
    • windows_issue
    • Zotero
    • data_struct
    • dataset
      • chapter_1
      • chapter_2
      • chapter_3
      • chapter_4
      • chapter_5
      • chapter_6
      • chapter_7
Powered by GitBook
On this page
  • 扩维 降维 squeeze和unsuqeeze
  • TORCH.spilt()
  • glob.glob()之返回路径的正反斜杆问题
  • 拼接1 torch.cat()
  • 拼接2 torch.stack
  • tf.expand_dims
  • torch.flatten()
  • torch.view()
  • 图像处理
  • 张量存储为图片
  • 随机数Rand
  • torch.rand
  • torch.randn
  • torch.randperm
  • torch.normal()
  • randn和normal的区别
  • torch.Tensor.uniform_()
  • torch.rand和torch.Tensor.uniform_
  • torch 检查
  • 判断tensor中是否存在NAN, inf
  • Numpy函数
  • numpy.tile()
  • torch的model.train()和eval()
  • with torch.no_grad()
  • tf.placeholder
  • 关于SELF理解
  1. Python

Model_ComFuc

扩维 降维 squeeze和unsuqeeze

squeeze(input, dim=None, out=None) 降维

函数功能:去除size为1的维度,包括行和列。当维度大于等于2时,squeeze()无作用。

input (Tensor) – 输入张量 dim (int, optional) – 如果给定,则input只会在给定维度挤压,维度的索引(从0开始) out (Tensor, optional) – 输出张量

当给定dim时,那么挤压操作只在给定维度上。注意这里dim指定的维度,其size必须为1,

其中squeeze(0)代表若第一维度值为1则去除第一维度,squeeze(1)代表若第二维度值为1则去除第二维度;-1,去除最后维度值为1的维度 当不给定dim时,将输入张量形状中的1 去除并返回。

例如:如果输入是形如(A×1×B×1×C×1×D)(A×1×B×1×C×1×D),squeeze()那么输出形状就为: (A×B×C×D)(A×B×C×D) 例如:输入形状为: (A×1×B)(A×1×B), squeeze(input, 0) 将会保持张量不变(因为A≠1),只有用 squeeze(input, 1),形状会变成 (A×B)(A×B)。

注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。

unsqueeze(input, dim, out=None) 增维

功能:增加大小为1的维度,也就是返回一个新的张量,对输入的指定位置插入维度 1且必须指明维度

x = torch.unsqueeze(x, 3) # 在第3个维度上扩展 注意: 返回张量与输入张量共享内存,所以改变其中一个的内容会改变另一个。

如果dim为负,则将会被转化dim+input.dim()+1


原文链接:https://blog.csdn.net/lj2048/article/details/114263912

举例:

# 如我们有一组数据3,7,2,5,6,8。我们想用一个变量来表示它,那么可以把它们放在一个数组里,相当于把这六个元素(小球)投到了一个箱子里
a= torch.tensor([3,7,2,5,6,8])
print(a.size()) # torch.Size([6])

# 我们希望这组小球里,把3,7单独放在一个箱子里,2,5放在一个箱子,6,8放在一个箱子:
a = torch.tensor([[3,7],[2,5],[6,8]])
print(a.size()) # torch.Size([3, 2])
 
# 图省略,比如最外层的箱子起名为a,里面一层的3个箱子起名a\[0\],a\[1\]和a\[2\],这三个箱子里分别有2的小球.分别对其命名 
# unsqueeze(i)表示用一个箱子把第i层的箱子都包起来;
a= a.unsqueeze(0)
print(a.size()) # torch.Size([1, 3, 2])

#  而squeeze(i)表示把第i层的箱子去掉(第i层只有一个箱子时才能用这个函数)
a = a.squeeze(0)
print(a.size()) # torch.Size([3, 2])

TORCH.spilt()

torch.split(tensor, split_size_or_sections, dim=0)

torch.split()作用将tensor分成块结构。

参数:

  • tesnor:input,待分输入

  • split_size_or_sections:需要切分的大小(int or list )

  • dim:切分维度

  • output:切分后块结构 <class 'tuple'>

  • 当split_size_or_sections为int时,tenor结构和split_size_or_sections,正好匹配,那么ouput就是大小相同的块结构。如果按照split_size_or_sections结构,tensor不够了,那么就把剩下的那部分做一个块处理。

  • 当split_size_or_sections 为list时,那么tensor结构会一共切分成len(list)这么多的小块,每个小块中的大小按照list中的大小决定,其中list中的数字总和应等于该维度的大小,否则会报错(注意这里与split_size_or_sections为int时的情况不同)。

split_size_or_sections为int型时

import torch
 
x = torch.rand(4,8,6)
y = torch.split(x,2,dim=0) #按照4这个维度去分,每大块包含2个小块
for i in y :
    print(i.size())
 
output:
torch.Size([2, 8, 6])
torch.Size([2, 8, 6])
 
y = torch.split(x,3,dim=0)#按照4这个维度去分,每大块包含3个小块
for i in y:
    print(i.size())
 
output:
torch.Size([3, 8, 6])
torch.Size([1, 8, 6])

split_size_or_sections为list型时。

import torch
 
x = torch.rand(4,8,6)
y = torch.split(x,[2,3,3],dim=1)
for i in y:
    print(i.size())
 
output:
torch.Size([4, 2, 6])
torch.Size([4, 3, 6])
torch.Size([4, 3, 6])
 
 
y = torch.split(x,[2,1,3],dim=1) #2+1+3 等于6 != 8 ,报错
for i in y:
    print(i.size())
 
output:
split_with_sizes expects split_sizes to sum exactly to 8 (input tensor's size at dimension 1), but got split_sizes=[2, 1, 3]

glob.glob()之返回路径的正反斜杆问题

import glob

li = glob.glob("E:\\datasets\\DAVIS\\ImageSets\\*\\*")
for item in li:
    print(item)
# li = glob.glob("E:\datasets\DAVIS\ImageSets\*\*")   正确方式1
# E:\datasets\DAVIS\ImageSets\480p\tmp_val.txt
# E:\datasets\DAVIS\ImageSets\480p\train.txt

# li = glob.glob("E:\\datasets\\DAVIS\\ImageSets\\*\\*") 正确方式2
# E:\datasets\DAVIS\ImageSets\480p\trainval.txt
# E:\datasets\DAVIS\ImageSets\480p\val.txt


# li = glob.glob("E:/datasets/DAVIS/ImageSets/*/*")
# E:/datasets/DAVIS/ImageSets\480p\trainval.txt
# E:/datasets/DAVIS/ImageSets\480p\val.txt

# li = glob.glob(r"E:\\datasets\\DAVIS\\ImageSets\\*\\*")
# E:\\datasets\\DAVIS\\ImageSets\480p\trainval.txt
# E:\\datasets\\DAVIS\\ImageSets\480p\val.txt

拼接1 torch.cat()

1. 字面理解:torch.cat是将两个张量(tensor)拼接在一起,cat是concatenate的意思,即拼接,联系在一起。

2. 例子理解

>>> import torch
>>> A=torch.ones(2,3) #2x3的张量(矩阵)                                     
>>> A
tensor([[ 1.,  1.,  1.],
        [ 1.,  1.,  1.]])
>>> B=2*torch.ones(4,3)#4x3的张量(矩阵)                                    
>>> B
tensor([[ 2.,  2.,  2.],
        [ 2.,  2.,  2.],
        [ 2.,  2.,  2.],
        [ 2.,  2.,  2.]])
>>> C=torch.cat((A,B),0)#按维数0(行)拼接
>>> C
tensor([[ 1.,  1.,  1.],
         [ 1.,  1.,  1.],
         [ 2.,  2.,  2.],
         [ 2.,  2.,  2.],
         [ 2.,  2.,  2.],
         [ 2.,  2.,  2.]])
>>> C.size()
torch.Size([6, 3])
>>> D=2*torch.ones(2,4) #2x4的张量(矩阵)
>>> C=torch.cat((A,D),1)#按维数1(列)拼接
>>> C
tensor([[ 1.,  1.,  1.,  2.,  2.,  2.,  2.],
        [ 1.,  1.,  1.,  2.,  2.,  2.,  2.]])
>>> C.size()
torch.Size([2, 7])

上面给出了两个张量A和B,分别是2行3列,4行3列。即他们都是2维张量。因为只有两维,这样在用torch.cat拼接的时候就有两种拼接方式:按行拼接和按列拼接。即所谓的维数0和维数1.

C=torch.cat((A,B),0)就表示按维数0(行)拼接A和B,也就是竖着拼接,A上B下。此时需要注意:列数必须一致,即维数1数值要相同,这里都是3列,方能列对齐。拼接后的C的第0维是两个维数0数值和,即2+4=6.

C=torch.cat((A,B),1)就表示按维数1(列)拼接A和B,也就是横着拼接,A左B右。此时需要注意:行数必须一致,即维数0数值要相同,这里都是2行,方能行对齐。拼接后的C的第1维是两个维数1数值和,即3+4=7.

从2维例子可以看出,使用torch.cat((A,B),dim)时,除拼接维数dim数值可不同外其余维数数值需相同,方能对齐。

3.实例

在深度学习处理图像时,常用的有3通道的RGB彩色图像及单通道的灰度图。张量size为cxhxw,即通道数x图像高度x图像宽度。在用torch.cat拼接两张图像时一般要求图像大小一致而通道数可不一致,即h和w同,c可不同。当然实际有3种拼接方式,另两种好像不常见。比如经典网络结构:U-Net

                                ![](https://img-blog.csdnimg.cn/20181209091439150.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5NzA5NTM1,size_16,color_FFFFFF,t_70)

里面用到4次torch.cat,其中copy and crop操作就是通过torch.cat来实现的。可以看到通过上采样(up-conv 2x2)将原始图像h和w变为原来2倍,再和左边直接copy过来的同样h,w的图像拼接。这样做,可以有效利用原始结构信息。

4.总结

使用torch.cat((A,B),dim)时,除拼接维数dim数值可不同外其余维数数值需相同,方能对齐。

拼接2 torch.stack

  • 先用图示讲讲 torch.cat() 是在原先维度上进行拼接

我们用图+代码来举例

import torch
x1 = torch.randn(1, 3)
x2 = torch.randn(1, 3)
# 在 0 维(纵向)进行拼接
torch.cat((x1, x2), 0)	# size [2, 3]
# 在 1 维(横向)进行拼接
torch.cat((x1, x2), 1)	# size [1, 6]

注意:对于cat拼接张量,==维度数量 (不是维度上的量)必须相同==;==进行拼接的维度的尺寸可以不同,但是其它维度的尺寸必须相同==。【简而言之,堆积木,对上了就可以拼】

  • torch.stack() 新增一个维度

我们继续用图+代码来举例

import torch
x1 = torch.randn(3, 4)
x2 = torch.randn(3, 4)
# 在 0 维插入一个维度,进行前后组合
torch.stack((x1, x2), 0)	
# size [2, 3,4]
# 在 1 维插入一个维度
torch.stack((x1, x2), 1)
# size [3, 2,4]
# 在 2 维插入一个维度
torch.stack((x1, x2), 2)
# size [3, 4,2]

补充: 拼接多个向量,例如:torch.stack((x1, x2, x3, x4), 2),再上述的方法中接入需要拼接的向量就可以了

tf.expand_dims

TensorFlow中,想要维度增加一维,可以使用tf.expand_dims(input, dim, name=None)函数。当然,我们常用tf.reshape(input, shape=[])也可以达到相同效果,但是有些时候在构建图的过程中,placeholder没有被feed具体的值,这时就会包下面的错误:TypeError: Expected binary or unicode string, got 1 在这种情况下,我们就可以考虑使用expand_dims来将维度加1。比如我自己代码中遇到的情况,在对图像维度降到二维做特定操作后,要还原成四维[batch, height, width, channels],前后各增加一维。如果用reshape,则因为上述原因报错

one_img2 = tf.reshape(one_img, shape=[1, one_img.get_shape()[0].value, one_img.get_shape()[1].value, 1])

用下面的方法可以实现:

one_img = tf.expand_dims(one_img, 0)
one_img = tf.expand_dims(one_img, -1) #-1表示最后一维

在最后,给出官方的例子和说明

# 't' is a tensor of shape [2]
shape(expand_dims(t, 0)) ==> [1, 2]
shape(expand_dims(t, 1)) ==> [2, 1]
shape(expand_dims(t, -1)) ==> [2, 1]

# 't2' is a tensor of shape [2, 3, 5]
shape(expand_dims(t2, 0)) ==> [1, 2, 3, 5]
shape(expand_dims(t2, 2)) ==> [2, 3, 1, 5]
shape(expand_dims(t2, 3)) ==> [2, 3, 5, 1]

举例:

import tensorflow as tf

a = tf.constant([[1, 2], [3, 4], [5, 6]], dtype=tf.float32)
a0 = tf.expand_dims(a, 0)
a1 = tf.expand_dims(a, 1)
a2 = tf.expand_dims(a, 2)
with tf.Session() as sess:
    print(sess.run(a))
    print('------------')
    print(sess.run(a0))
    print('------------')
    print(sess.run(a1))
    print('------------')
    print(sess.run(a2))

结果:

[[1. 2.]
 [3. 4.]
 [5. 6.]]
------------
[[[1. 2.]
  [3. 4.]
  [5. 6.]]]
------------
[[[1. 2.]]

 [[3. 4.]]

 [[5. 6.]]]
------------
[[[1.]
  [2.]]

 [[3.]
  [4.]]

 [[5.]
  [6.]]]

torch.flatten()

1)flatten(x,1)是按照x的第1个维度拼接(按照列来拼接,横向拼接); 2)flatten(x,0)是按照x的第0个维度拼接(按照行来拼接,纵向拼接); 3)有时候会遇到flatten里面有两个维度参数,flatten(x, start_dim, end_dimension),此时flatten函数执行的功能是将从start_dim到end_dim之间的所有维度值乘起来,其他的维度保持不变。例如x是一个size为[4,5,6]的tensor, flatten(x, 0, 1)的结果是一个size为[20,6]的tensor。

flatten的中文含义为“扁平化”,具体怎么理解呢?我们可以尝试这么理解,假设你的数据为1维数据,那么这个数据天然就已经扁平化了,如果是2维数据,那么扁平化就是将2维数据变为1维数据,如果是3维数据,那么就要根据你自己所选择的“扁平化程度”来进行操作,假设需要全部扁平化,那么就直接将3维数据变为1维数据,如果只需要部分扁平化,那么有一维的数据不会进行扁平操作,具体看下面的案例分析。

import numpy as np 
import torch

x = np.arange(27)
x = np.reshape(x, (3,3,3))
x = torch.from_numpy(x)
print('before flatten', x)
x = torch.flatten(x)  # 默认扁平化程度为最高
print('after flatten', x)

# 部分扁平化
x = np.arange(27)
x = np.reshape(x, (3, 3, 3))
x = torch.from_numpy(x)
print('before flatten', x)
print(x.shape)
x = torch.flatten(x, start_dim=1, end_dim=2)
print('after flatten', x)

torch.view()

view()的原理很简单,其实就是把原先tensor中的数据进行排列,排成一行,然后根据所给的view()中的参数从一行中按顺序选择组成最终的tensor。

view()可以有多个参数,这取决于你想要得到的是几维的tensor,一般设置两个参数,也是神经网络中常用的(一般在全连接之前),代表二维。

view(h,w),h代表行(想要变为几行),w代表的是列(想要变为几列)#这里所说并不严谨,只是为了更好理解,

view()的参数
作用

h

取值代表行数,当不知道要变为几行,但知道要变为几列时可取-1

w

取值代表列数,当不知道要变为几列,但知道要变为几行时可取-1

注意:元素个数要能整除行和列

下面看几个例子就理解了。 1、把原先tensor中的数据进行排列,排成一行,然后根据所给的view()中的参数从一行中按顺序选择组成最终的tensor

import torch
a=torch.Tensor([[[1,2,3],[4,5,6],[7,8,9]]])
b=torch.Tensor([1,2,3,4,5,6,7,8,9])

#结果:
torch.Size([1, 3, 3])
tensor([[[1., 2., 3.],
         [4., 5., 6.],
         [7., 8., 9.]]])
torch.Size([9])
tensor([1., 2., 3., 4., 5., 6., 7., 8., 9.])

#a是[1,3,3]的tensor向量:
b是[9]的tensor向量

a1 = a.view(3,-1)
b1 = b.view(3,-1)

#a1和b1的结果:
torch.Size([3, 3])
tensor([[1., 2., 3.],
        [4., 5., 6.],
        [7., 8., 9.]])
torch.Size([3, 3])
tensor([[1., 2., 3.],
        [4., 5., 6.],
        [7., 8., 9.]])
结果一样

2、当知道要变成的tensor的行时:

import torch
a=torch.Tensor([[[1,2,3],[4,5,6]]])
b=a.view(3,-1)
#a的结果:2行3列
tensor([[[1., 2., 3.],
         [4., 5., 6.]]])

#b的结果:变为了3行2列
tensor([[1., 2.],
        [3., 4.],
        [5., 6.]])

2、当知道要变成的tensor的列时:

import torch
a=torch.Tensor([[[1,2,3],[4,5,6]]])
b=a.view(-1,1)

#a的结果:2行三列
tensor([[[1., 2., 3.],
         [4., 5., 6.]]])
#b的结果:变为了6行1列

tensor([[1.],
        [2.],
        [3.],
        [4.],
        [5.],
        [6.]])

图像处理

张量存储为图片

三维张量转图片

import torchvision
import torchvision.transforms as transforms
import cv2 as cv
import matplotlib.pyplot as plt
import os
import torch

# 读取图片
img = cv.imread('./1.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
print(img.shape)        # (405, 405, 3)
# 图片转张量
img_tensor = transforms.ToTensor()(img)  # tensor数据格式是torch(C,H,W)
print(img_tensor.size())        # torch.Size([3, 405, 405])

## 方法1
# 用torchvision.utils.save_image()直接把张量保存成图片
input_tensor_1 = img_tensor.clone().detach().to(torch.device('cpu'))
# torchvision.utils.save_image(input_tensor_1, "out_x.jpg")
## 方法2: 转换为数组再保存
input_tensor_2 = img_tensor.cpu().numpy().transpose(1, 2, 0)
path = os.path.join('./', '_test_' + '.png')
plt.imsave(path, input_tensor_2)

四维张量转图片(卷积神经网络的输出,多了一个batch_size维度)

for (x, _) in zip(data_loader):
    x, z = x.to(device), z.to(device)       # x.shape = torch.Size([1, 3, 256, 256])
    k = x.permute(0, 2, 3, 1)
    k = k.cpu().detach().numpy()

    path = os.path.join('./result/', str(epoch+1) + '_epoch' + '.jpg')
    plt.imsave(path, (k[0] + 1) / 2)

随机数Rand

torch.rand

torch.rand(*sizes, out=None) → Tensor

返回一个张量,包含了从区间[0,1)的均匀分布中抽取的一组随机数,形状由可变参数sizes 定义。

print(torch.rand(3))
print(torch.rand(3,2))

输出为:

>>tensor([0.3445, 0.9567, 0.7707])
tensor([[0.7644, 0.5776],
        [0.4379, 0.5460],
        [0.1860, 0.2946]])

torch.randn

torch.randn(*sizes, out=None) → Tensor

返回一个张量,包含了服从标准正态分布(均值为0,方差为 1,即高斯白噪声)中抽取一组随机数,形状由可变参数sizes定义。

print(torch.randn(3))
print(torch.randn(3,2))
>>tensor([ 0.7186, -1.4286,  0.6510])
tensor([[ 1.1820, -0.4163],
        [-1.2188,  1.3141],
        [ 0.9691,  0.2682]])

torch.randperm

torch.randperm(n, out=None) → LongTensor

给定参数n,返回一个从[0, n -1) 的随机整数排列。

参数:n (int) – 上边界(不包含)

print(torch.randperm(10))
>>tensor([2, 4, 8, 5, 0, 9, 6, 1, 7, 3])

可以回想一下range或者arrange。他们俩是返回有序的数列并且可以设定步长,官方建议是使用函数 torch.arange()。

介绍完上边的rand*函数再介绍一个torch.normal()

torch.normal()

torch.normal(means, std, out=None)

返回一个张量,包含从给定参数means,std的离散正态分布中抽取随机数。

均值means是一个张量,包含每个输出元素相关的正态分布的均值。

std是一个张量,包含每个输出元素相关的正态分布的标准差。均值和标准差的形状不须匹配,但每个张量的元素个数须相同。

参数:

  • means (Tensor) – 均值

  • std (Tensor) – 标准差

  • out (Tensor) – 可选的输出张量

randn和normal的区别

虽然randn和normal都可以生成服从正态分布的随机数,但是normal可以自己设定均值和标准差。就这点区别。

torch.Tensor.uniform_()

A.uniform_(-10,20)将会把A里面的每个值都从[-10, 20]里面重新随机取一次,即在[-10, 20]的随机均匀分布里面取值并重新赋值

是uniform_(),不是uniform()!以下划线结尾的是inplace方法。 什么是inplace方法:将会**==改变调用者本身值的方法==**。如a, b = torch.rand(2,2), torch.rand(2,2),如果a.add(b),那么a值不变;如果a.add_(b),a值就会变为a+b

比如:

a = torch.Tensor(2,3).uniform_(5,6)
b = torch.zeros(2,3).uniform_(5,6)
c = torch.ones(2,3).uniform_(5,6)
print(a,b,c,sep="\n")

三种都是,生成一个2×3的张量,将其中的值改为5-6之间的浮点数。

>>
tensor([[5.5388, 5.6403, 5.2553],
        [5.0379, 5.8440, 5.0334]])
tensor([[5.0001, 5.2357, 5.4277],
        [5.9316, 5.4877, 5.3986]])
tensor([[5.4156, 5.6199, 5.4743],
        [5.4273, 5.4571, 5.6763]])

torch.rand和torch.Tensor.uniform_

这样看到话,两个都能取0-1之间的均匀分布,但是问题在于rand取不到1,uniform_可以取到1。

torch 检查

判断tensor中是否存在NAN, inf

在用torch搭建深度学习模型时,很容易在数据中存在inf与nan的情况,对应的数据类型分别时torch.inf与torch.nan。 大多数情况下,产生nan数据的原因基本上是出现了分母为0的情况,所以需要检查张量计算过程中是否有除法运算,比如softmax就有除法。

torch.isnan(tensor).any() # 有一个True(非NAN)则都为 True
torch.isnan(tensor).all() # 有一个 NAN 则为 False

# 与之类似,用tensor.isinf()方法也可以查看有没有无穷数在张量里。
torch.isinf(tensor).any()
torch.isinf(tensor).all()

Numpy函数

numpy.tile()

numpy.tile()是个什么函数呢,说白了,就是把数组沿各个方向复制

比如 a = np.array([0,1,2]), np.tile(a,(2,1))就是把a先沿x轴(就这样称呼吧)复制1倍,即没有复制,仍然是 [0,1,2]。 再把结果沿y方向复制2倍,即最终得到

array([[0,1,2], [0,1,2]])

>>> import numpy as np
>>> b = np.array([[1, 2], [3, 4]])  # (2,2)
>>> np.tile(b,2) #沿X轴复制2倍
array([[1, 2, 1, 2],
       [3, 4, 3, 4]])
>>> np.tile(b,(2,1)) #沿X轴复制1倍(相当于没有复制),再沿Y轴复制2倍
array([[1, 2],
       [3, 4],
       [1, 2],
       [3, 4]])

>>> a=np.array([0,1,2])
>>> np.tile(a,2)
array([0, 1, 2, 0, 1, 2])
>>> np.tile(a,(2,2))
array([[0, 1, 2, 0, 1, 2],
       [0, 1, 2, 0, 1, 2]])
>>> np.tile(a,(2,1,2))
array([[[0, 1, 2, 0, 1, 2]],

       [[0, 1, 2, 0, 1, 2]]])
>>>

原文链接:https://blog.csdn.net/qq_18433441/article/details/54897250

torch的model.train()和eval()

  1. 两种模式

pytorch可以给我们提供两种方式来切换训练和评估(推断)的模式,分别是:model.train() 和 model.eval()。

一般用法是:在训练开始之前写上 model.trian() ,在测试时写上 model.eval() 。

  1. 功能

  • ==model.train()==

在使用 pytorch 构建神经网络,添加一句model.train()作用是 启用 batch normalization 和 dropout 。

如果模型中有BN层(Batch Normalization)和 Dropout ,需要在 训练时 添加 model.train()。

model.train() 是保证 BN 层能够用到 每一批数据 的均值和方差。

对于 Dropout,model.train() 是 随机取一部分 网络连接来训练更新参数。

  • ==model.eval()==

model.eval()的作用是 不启用 Batch Normalization 和 Dropout。

如果模型中有 BN 层(Batch Normalization)和 Dropout,在 测试时 添加 model.eval()。

model.eval() 是保证 BN 层能够用 全部训练数据 的均值和方差,即测试过程中要保证 BN 层的均值和方差不变。

对于 Dropout,model.eval() 是利用到了 所有 网络连接,即不进行随机舍弃神经元。


  • 为什么测试时要用 model.eval() ?

训练完 train 样本后,生成的模型 model 要用来测试样本了。在 model(test) 之前,需要加上model.eval(),否则的话,有输入数据,即使不训练,它也会改变权值。这是 model 中含有 BN 层和 Dropout 所带来的的性质。

eval() 时,pytorch 会自动把 BN 和 DropOut 固定住,不会取平均,而是用训练好的值。 不然的话,一旦 test 的 batch_size 过小,很容易就会被 BN 层导致生成图片颜色失真极大。 eval() 在非训练的时候是需要加的,没有这句代码,一些网络层的值会发生变动,不会固定,你神经网络每一次生成的结果也是不固定的,生成质量可能好也可能不好。

也就是说,测试过程中使用model.eval(),这时神经网络会 沿用 batch normalization 的值,而并 不使用 dropout。

  • 总结与对比

如果模型中有 BN 层(Batch Normalization)和 Dropout,需要在训练时添加 model.train(),在测试时添加 model.eval()。

其中 model.train() 是保证 BN 层用每一批数据的均值和方差,而 model.eval() 是保证 BN 用全部训练数据的均值和方差;

而对于 Dropout,model.train() 是随机取一部分网络连接来训练更新参数,而 model.eval() 是利用到了所有网络连接。


参考链接

with torch.no_grad()

1.关于with

with是python中上下文管理器,简单理解,当要进行固定的进入,返回操作时,可以将对应需要的操作,放在with所需要的语句中。比如文件的写入(需要打开关闭文件)等。

以下为一个文件写入使用with的例子。

with open (filename,'w') as sh:    
    sh.write("#!/bin/bash\n")
    sh.write("#$ -N "+'IC'+altas+str(patientNumber)+altas+'\n')
    sh.write("#$ -o "+pathSh+altas+'log.log\n') 
    sh.write("#$ -e "+pathSh+altas+'err.log\n') 
    sh.write('source ~/.bashrc\n')          
    sh.write('. "/home/kjsun/anaconda3/etc/profile.d/conda.sh"\n')
    sh.write('conda activate python27\n')
    sh.write('echo "to python"\n')
    sh.write('echo "finish"\n')
    sh.close()

with后部分,可以将with后的语句运行,将其返回结果给到as后的变量(sh),之后的代码块对close进行操作。

2.关于with torch.no_grad():

在使用pytorch时,并不是所有的操作都需要进行计算图的生成(计算过程的构建,以便梯度反向传播等操作)。而对于tensor的计算操作,默认是要进行计算图的构建的,在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建。

以下分别为使用和不使用的情况:

(1)使用with torch.no_grad():

with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))        
print(outputs)

运行结果:

Accuracy of the network on the 10000 test images: 55 %
tensor([[-2.9141, -3.8210,  2.1426,  3.0883,  2.6363,  2.6878,  2.8766,  0.3396,
         -4.7505, -3.8502],
        [-1.4012, -4.5747,  1.8557,  3.8178,  1.1430,  3.9522, -0.4563,  1.2740,
         -3.7763, -3.3633],
        [ 1.3090,  0.1812,  0.4852,  0.1315,  0.5297, -0.3215, -2.0045,  1.0426,
         -3.2699, -0.5084],
        [-0.5357, -1.9851, -0.2835, -0.3110,  2.6453,  0.7452, -1.4148,  5.6919,
         -6.3235, -1.6220]]

此时的outputs没有 属性。

(2)不使用with torch.no_grad():

而对应的不使用的情况

for data in testloader:
    images, labels = data
    outputs = net(images)
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))
print(outputs)

结果如下:

Accuracy of the network on the 10000 test images: 55 %
tensor([[-2.9141, -3.8210,  2.1426,  3.0883,  2.6363,  2.6878,  2.8766,  0.3396,
         -4.7505, -3.8502],
        [-1.4012, -4.5747,  1.8557,  3.8178,  1.1430,  3.9522, -0.4563,  1.2740,
         -3.7763, -3.3633],
        [ 1.3090,  0.1812,  0.4852,  0.1315,  0.5297, -0.3215, -2.0045,  1.0426,
         -3.2699, -0.5084],
        [-0.5357, -1.9851, -0.2835, -0.3110,  2.6453,  0.7452, -1.4148,  5.6919,
         -6.3235, -1.6220]], grad_fn=<AddmmBackward>)

可以看到,此时有grad_fn=<AddmmBackward>属性,表示,计算的结果在一计算图当中,可以进行梯度反传等操作。但是,两者计算的结果实际上是没有区别的。

简单的说:

不用with时的代码:
try:
do A(something before work)
work
finally:
do B(something after work)

用with时:
with (A, B):
work

效果等同.

tf.placeholder

函数形式:

tf.placeholder(
    dtype, # dtype:数据类型。常用的是tf.float32,tf.float64等数值类型
    shape=None, # shape:数据形状。默认是None,就是一维值,也可以是多维(比如\[2,3\], \[None, 3\]表示列是3,行不定)
    name=None # name:名称
)
  • 为什么要用placeholder?

    Tensorflow的设计理念称之为计算流图,在编写程序时,首先构筑整个系统的graph,代码并不会直接生效,这一点和python的其他数值计算库(如Numpy等)不同,graph为静态的,类似于docker中的镜像。然后,在实际的运行时,启动一个session,程序才会真正的运行。这样做的好处就是:避免反复地切换底层程序实际运行的上下文,tensorflow帮你优化整个系统的代码。我们知道,很多python程序的底层为C语言或者其他语言,执行一行脚本,就要切换一次,是有成本的,tensorflow通过计算流图的方式,帮你优化整个session需要执行的代码,还是很有优势的。

    所以placeholder()函数是在神经网络构建graph的时候在模型中的占位,此时并没有把要输入的数据传入模型,它只会分配必要的内存。等建立session,在会话中,运行模型的时候通过feed_dict()函数向占位符喂入数据。


  • 代码示例:

import tensorflow as tf
import numpy as np

input1 = tf.placeholder(tf.float32)
input2 = tf.placeholder(tf.float32)

output = tf.multiply(input1, input2)

with tf.Session() as sess:
    print(sess.run(output, feed_dict = {input1:[3.], input2: [4.]}))
import tensorflow as tf
import numpy as np

x = tf.placeholder(tf.float32, shape=(1024, 1024))
y = tf.matmul(x, x)

with tf.Session() as sess:
    #print(sess.run(y))  # ERROR:此处x还没有赋值
    rand_array = np.random.rand(1024, 1024)
    print(sess.run(y, feed_dict={x: rand_array})) 

关于SELF理解

在使用tensorFlow2搭建神经网络模型的时候,除了Sequential的方法,还有就是自己写模型class,然后初始化。自己写模型类的优势就是可以自定义层与层之间的连接关系,自定义数据流x的流向。

这是为鸢尾花数据做的一个简单基础的神经网络模型代码:

class IrisModel(Model): #继承TF的Model类
    def __init__(self): #在这里定义网络结构块
        super(IrisModel, self).__init__()
        self.d1 = Dense(3, activation='softmax', kernel_regularizer=tf.keras.regularizers.l2()) #定义网络结构块

    def call(self, x): #在这里实现前向传播
        y = self.d1(x) #调用网络结构块,实现前向传播
        return y

model = IrisModel() #类实例化

我在第一次学习用类来写这个代码的时候,有几个地方觉得很奇怪,不习惯。

1、为什么要定义 def __init__()

2、为什么要有 super(MyModel ,self).__init__()

3、为什么到处都有一个self? 又不能省略,又感觉很累赘,它到底意义何在?

原文:博主Chou_pijiang,原文链接:https://blog.csdn.net/zyh19980527/article/details/107206483

总结下来,有这样几点:

1、self参数

self指的是实例Instance本身,在Python类中规定,函数的第一个参数是实例对象本身,并且约定俗成,把其名字写为self,也就是说,类中的方法的第一个参数一定要是self,而且不能省略。 我觉得关于self有三点是很重要的: self指的是实例本身,而不是类 self可以用this替代,但是不要这么去写 类的方法中的self不可以省略

2、__ init__ ()方法

在python中创建类后,通常会创建一个 __ init__ ()方法,这个方法会在创建类的实例的时候自动执行。 __ init__ ()方法必须包含一个self参数,而且要是第一个参数

__ init__ ()方法在实例化的时候就已经自动执行了,但是如果不是 __ init__ ()方法,那肯定就只有调用才执行。如果 __ init__ ()方法中还需要传入另一个参数name,但是我们在创建Bob的实例的时候没有传入name,那么程序就会报错, 说我们少了一个__ init__ ()方法的参数,因为__ init__ ()方法是会在创建实例的过程中自动执行的,这个时候发现没有name参数,肯定就报错了。

那么什么需要在__ init__ ()方法中定义?就是当我们认为一些属性、操作是在创建实例的时候就有的时候,就应该把这个量定义在__ init__ ()方法中。我们写神经网络的代码的时候,一些网络结构的设置,也最好放在__ init__ ()方法中。

3、super(MyModel, self).__init__()

简单理解就是子类把父类的__init__()放到自己的__init__()当中,这样子类就有了父类的__init__()的那些东西。

Net类继承nn.Module,super(Net, self).__init__()就是对继承自父类nn.Module的属性进行初始化。而且是用nn.Module的初始化方法来初始化继承的属性。

子类继承了父类的所有属性和方法,父类属性自然会用父类方法来进行初始化。 当然,如果初始化的逻辑与父类的不同,不使用父类的方法,自己重新初始化也是可以的。

写神经网络模型的时候,父类Modle中还有一些属性我们不用再自己手写了,直接将其初始化就好,另外Model也是有继承的父类network.Network,这里也有一些我们不知道、不知如何自己写的属性和方法。所以最好都加上 super(MyModel,self).__init__()这样的父类初始化命令行。

class Module(tracking.AutoTrackable):
  def __init__(self, name=None):
    if name is None:  #模型名称
      name = camel_to_snake(type(self).__name__)
    else:
      if not valid_identifier(name):
        raise ValueError(
            "%r is not a valid module name. Module names must be valid Python "
            "identifiers (e.g. a valid class name)." % name)

    self._name = name
    if tf2.enabled():
      with ops.name_scope_v2(name) as scope_name:  #TF命名空间
        self._name_scope = ops.name_scope_v2(scope_name)
    else:
      with ops.name_scope(name, skip_on_eager=False) as scope_name:
        self._scope_name = scope_name
PreviousPython_print_formatNextDeep_learning_know

Last updated 2 years ago

本文转自 ,如有侵权,请联系删除。

本文转自 ,如有侵权,请联系删除。

本文转自 ,如有侵权,请联系删除。

在这里插入图片描述
在这里插入图片描述
pic
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文转自 ,如有侵权,请联系删除。

本文转自 ,如有侵权,请联系删除。

本文转自 ,如有侵权,请联系删除。

参数:sizes (int…) – 整数序列,定义了输出形状;out (, optinal) - 结果张量

好文:

本文转自 ,如有侵权,请联系删除。

本文转自 ,如有侵权,请联系删除。

参考资料:

本文转自 ,如有侵权,请联系删除。

原文:博文:

参考TensorFLow官方API中Model的代码,我们来看看Model初始化__init__()中都定义了什么:

本文转自 ,如有侵权,请联系删除。

https://blog.csdn.net/kuan__/article/details/109209709
https://blog.csdn.net/qq_42518956/article/details/103882579
https://blog.csdn.net/qq_39709535/article/details/80803003
https://blog.csdn.net/weixin_42516475/article/details/119084904
https://blog.csdn.net/duanlianvip/article/details/96448393
(14条消息) torch.flatten()函数_torch.flatten(x,1)_行者无疆哇的博客-CSDN博客
https://blog.csdn.net/everyxing1007/article/details/126881242
Tensor
PyTorch中train()方法的作用是什么
【pytorch】model.train()和model.evel()的用法
pytorch中net.eval() 和net.train()的使用
Pytorch学习笔记11----model.train()与model.eval()的用法、Dropout原理、relu,sigmiod,tanh激活函数、nn.Linear浅析、输出整个tensor的方法
Pytorch:model.train()和model.eval()用法和区别,以及model.eval()和torch.no_grad()的区别
https://blog.csdn.net/weixin_44211968/article/details/123774649
https://blog.csdn.net/weixin_44134757/article/details/105775027
https://www.jianshu.com/p/ec261a65e3c9
https://blog.csdn.net/kdongyi/article/details/82343712
self参数 - __ init__ ()方法 super(Net, self).__init__()是什么
https://github.com/tensorflow/tensorflow/blob/v2.5.0/tensorflow/python/module/module.py#L286-L317
https://blog.csdn.net/eylier/article/details/118931908
在这里插入图片描述
在这里插入图片描述