pytorch面试总结

一. pytorch多卡训练的原理

  1. 思想
    (1)将模型加载到一个指定的主GPU上,然后将模型浅拷贝到其它的从GPU上;
    (2)将总的batch数据等分到不同的GPU上(坑:需要先将数据加载到主GPU上);
    (3)每个GPU根据自己分配到的数据进行forward计算得到loss,并通过backward得到权重梯度;
    (4)主GPU将所有从GPU得到的梯度进行合并并用于更新模型的参数。
  2. 实现
    (1)模型方面
device_ids = [0, 1, 2, 3]
model = Model(input_size, output_size)
model = nn.DataParallel(model, device_ids=device_ids) #单卡没有这行代码
model = model.cuda(device_ids[1]) #指定哪块卡为主GPU,默认是0卡

(2)数据方面
像上面那样如果显示的指定1卡为主GPU的话,如果加载数据时这样写,就会报错:

for data in data_loader:
	input_var = Variable(data.cuda()) #默认指定用0卡先加载数据就会报错
	output = model(input_var)

修改方案:必须和模型的指定的主GPU的id一致

for data in data_loader:
	input_var = Variable(data.cuda(device_ids[1])) #默认指定用0卡先加载数据就会报错
	output = model(input_var)

二. pytorch中gather和scatter_

  1. gather(聚合操作)
    (1)函数原型:torch.gather(input, dim, index, out=None);
    (2)函数功能:对于out指定位置上的值,去寻找input里面对应的索引位置,根据是index;
    (3)三维数组的通项公式
out[i][j][k] = input[index[i][j][k]][j][k]  # if dim = 0
out[i][j][k] = input[i][index[i][j][k]][k]  # if dim = 1
out[i][j][k] = input[i][j][index[i][j][k]]  # if dim = 2

(4)例子

Example:
 
>>> t = torch.Tensor([[1,2],[3,4]])
>>> torch.gather(t, 1, torch.LongTensor([[0,0],[1,0]]))
 1  1
 4  3
[torch.FloatTensor of size 2x2]

具体过程就是这里的input = [[1,2],[3,4]], index = [[0,0],[1,0]], dim = 1, 则

out[0][0] = input[0][ index[0][0] ] = input[0][0] = 1
out[0][1] = input[0][ index[0][1] ] = input[0][0] = 1
out[1][0] = input[1][ index[1][0] ] = input[1][1] = 4
out[1][1] = input[1][ index[1][1] ] = input[1][0] = 3
  1. scatter_(分散操作)
    (1)函数原型:Tensor.scatter_(dim, index, src)
    (2)函数功能:src(或者说input)指定位置上的值,去分配给output对应索引位置,根据是index;
    (3)三维数组的通项公式:
self[index[i][j][k]][j][k] = src[i][j][k]  # if dim == 0
self[i][index[i][j][k]][k] = src[i][j][k]  # if dim == 1
self[i][j][index[i][j][k]] = src[i][j][k]  # if dim == 2

(4)例子

Example:
 
>>> x = torch.rand(2, 5)
>>> x
 
 0.4319  0.6500  0.4080  0.8760  0.2355
 0.2609  0.4711  0.8486  0.8573  0.1029
[torch.FloatTensor of size 2x5]
 
>>> torch.zeros(3, 5).scatter_(0, torch.LongTensor([[0, 1, 2, 0, 0], [2, 0, 0, 1, 2]]), x)
 
 0.4319  0.4711  0.8486  0.8760  0.2355
 0.0000  0.6500  0.0000  0.8573  0.0000
 0.2609  0.0000  0.4080  0.0000  0.1029
[torch.FloatTensor of size 3x5]

此例中,src就是x,index就是[[0, 1, 2, 0, 0], [2, 0, 0, 1, 2]], dim=0。我们把src写在左边,把self写在右边,这样好理解一些,但要注意是把src的值赋给self,所以用箭头指过去

0.4319 = Src[0][0] ----->self[ index[0][0] ][0] ----> self[0][0]
0.6500 = Src[0][1] ----->self[ index[0][1] ][1] ----> self[1][1]
0.4080 = Src[0][2] ----->self[ index[0][2] ][2] ----> self[2][2]
0.8760 = Src[0][3] ----->self[ index[0][3] ][3] ----> self[0][3]
0.2355 = Src[0][4] ----->self[ index[0][4] ][4] ----> self[0][4]

0.2609 = Src[1][0] ----->self[ index[1][0] ][0] ----> self[2][0]
0.4711 = Src[1][1] ----->self[ index[1][1] ][1] ----> self[0][1]
0.8486 = Src[1][2] ----->self[ index[1][2] ][2] ----> self[0][2]
0.8573 = Src[1][3] ----->self[ index[1][3] ][3] ----> self[1][3]
0.1029 = Src[1][4] ----->self[ index[1][4] ][4] ----> self[2][4]

则我们把src也就是 x的每个值都成功的分配了出去,然后我们再把self对应位置填好,剩下的未得到分配的位置,就填0补充

三. pytorch中torch.Tensor()和torch.tensor()的相同点和区别

  1. 相同点
    Tensor和tensor都能用于生成新的张量
>>> a=torch.Tensor([1,2])
>>> a
tensor([1., 2.])
>>> a=torch.tensor([1,2])
>>> a
tensor([1, 2])
  1. 不同点
    torch.Tensor()是python类,是torch.FloatTensor()的别名,使用torch.Tensor()会调用Tensor类的构造函数,生成float类型的张量;而torch.tensor()仅仅是python的函数,函数原型是torch.tensor(data, dtype=None, device=None, requires_grad=False),其中data可以是scalar,list,tuple,numpy array等等。然后torch.tensor会从data中的数据部分进行拷贝(而不是引用
    根据原始数据类型生成相应的 torch.LongTensor、torch.FloatTensor和torch.DoubleTensor。比如:
>>> a=torch.tensor([1,2])
>>> a.type()
'torch.LongTensor'

>>> a=torch.tensor([1.,2.])
>>> a.type()
'torch.FloatTensor'

>>> a=np.zeros(2,dtype=np.float64)
>>> a=torch.tensor(a)
>>> a.type()
'torch.DoubleTensor'
  1. 易错点
>>> a=torch.tensor(1)
>>> a
tensor(1)
>>> a.type()
'torch.LongTensor'
>>> a=torch.Tensor(1)
>>> a
tensor([0.])
>>> a.type()
'torch.FloatTensor'
>>> a=torch.Tensor([1])
>>> a
tensor([1.])
>>> a.type()
'torch.FloatTensor'

解释:torch.tensor(1)把1当做一个Long型的value传入;torch.Tensor(1)把1当做size传入;torch.Tensor([1])把1当做Float类型的value传入。

四. pytorch中Variable的理解

torch.autograd.Variable是Autograd的核心类,它封装了Tensor,并整合了反向传播的相关实现。Variable包含了三个属性:(1)data:存储了Tensor本体的数据;(2)grad:保存了data的梯度,其本身也是个Variable,shape与data相同;(3)grad_fn:指向Function对象,用于反向传播的梯度计算。但是在pytorch0.4之后,将Variable与Tensor整合到了一起,声明torch.tensor也包含这三个属性。

五. pytorch中backward()的理解

参考这篇博客。在面试回答时,通过一个简单的例子向面试官说明每一行代码在计算图中都干了些什么事;然后在说明当输入的标量变成向量时,pytorch是如何处理的。

六. tensorflow中variable和get_variable的区别

(1)variable是用来创建变量的,当两个变量的名字在同一作用域内相同时,tensorflow会自动将第二个定义的variable的名字加上"_1",则会生成新的name,如果使用在name_scope内,则会在name的前面加上name_scope名字的前缀;
(2)get_variable的功能是用来进行变量共享的,当变量不存在时,则会自动创建该变量。如果存在时,需要设置reuse=True来获取该变量,实现变量共享。需要注意的是,get_variable在variable_scope内使用才会给name加上前缀,在name_scope中使用并不会。

七. tensorflow中节点和边代表的什么

(1)节点代表着多种功能,比如说输入,变量初始化,运算,控制,输出等;
(2)边代表输入与输出之间的关系,即数据流动的方向。

八. pytorch中train和eval有什么不同

(1). model.train()——训练时候启用
启用 BatchNormalization 和 Dropout,将BatchNormalization和Dropout置为True
(2). model.eval()——验证和测试时候启用
不启用 BatchNormalization 和 Dropout,将BatchNormalization和Dropout置为False

train模式会计算梯度,eval模式不会计算梯度。

九、caffe的im2col

参考 caffe在实现卷积的时候,将kernel展开成行向量(每一行表示一个输出channel,output channel为几,就有几个行向量),将input tensor展开成列向量(im2col操作),但是是根据kernel的shape展开,(kernel能滑动多少次,就有多少列),不同的input channel被分成了很多矩阵块,但是都是“纵向放置”,然后将kernel和input tensor做矩阵乘法,得到output_channels行,output_h * output_w列的矩阵,每一行都可以展开成一张输出特征图(由于图像数据是连续存储的,只需要按行按列排满即可)。

PS:不同框架的访存机制不一样,所以会有行列相反这样的区别。python和c++的数据存储是行优先,matlab是列优先。所以,在caffe框架下,im2col是将一个小窗的值展开为一行,而在matlab中则展开为列。所以说,行列的问题没有本质区别,目的都是为了在计算时读取连续的内存。

十. tensorflow底层实现卷积的方式

参考 pytorch,tensorflow,caffe底层实现卷积的核心都是im2col, 这里以tensorflow为例。

tf.nn.conv2d()函数的定义为:
conv2d(input,filter,strides,padding,use_cudnn_on_gpu=True,data_format="NHWC",dilations=[1,1,1,1],name=None)

给定 4-D input 和 filter tensors计算2-D卷积,其中input tensor 的 shape是: [B, H, W, C],filter / kernel tensor 的 shape是: [filter_height, filter_width, in_channels, out_channels]

卷积op执行方式:

  1. 将filter展开为一个 shape 为[filter_height * filter_width * in_channels, out_channels] 大小的2-D 矩阵。
  2. 从 input tensor按照每个filter位置上提取图像patches来构成一个虚拟的shape大小为[batch, out_height, out_width,filter_height * filter_width * in_channels]的tensor 。(相当于在input每个卷积核的位置上(包含了同一位值对应的不同channel)提取patches)
    ps:把输入图像要经行卷积操作的这一区域展成列向量的操作通常称为im2col
  3. 对每个patch, 右乘以 filter matrix.得到[batch, out_height, out_width,out_channels]大小的输出。(Input maps的第一列×kernels的第一行,得到输出的第一个值,Input maps的第i列×kernels的第j行,得到输出特征图第i列第 j行的值)
    pytorch面试总结_第1张图片

十一. Pytorch和tensorflow区别,底层分别是怎么实现动态图和静态图的?

【参考】

tensorflow实现反向传播:

import tensorflow as tf
weight = tf.Variable(tf.random_uniform([1], -1.0, 1.0))
biases = tf.Variable(tf.zeros([1]))
y = weight * x_data + biases

loss = tf.reduce_mean(tf.square(y - y_data)) # 计算loss
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.05)

train_step = optimizer.minimize(loss)

init = tf.initialize_all_variables()
sess.run(init)

sess.run(train_step, feed_dict={x_data:x, y_data:y})

pytorch实现反向传播:loss.backward()

你可能感兴趣的:(笔记)