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)
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
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补充
>>> a=torch.Tensor([1,2])
>>> a
tensor([1., 2.])
>>> a=torch.tensor([1,2])
>>> a
tensor([1, 2])
>>> 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'
>>> 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传入。
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是如何处理的。
(1)variable是用来创建变量的,当两个变量的名字在同一作用域内相同时,tensorflow会自动将第二个定义的variable的名字加上"_1",则会生成新的name,如果使用在name_scope内,则会在name的前面加上name_scope名字的前缀;
(2)get_variable的功能是用来进行变量共享的,当变量不存在时,则会自动创建该变量。如果存在时,需要设置reuse=True来获取该变量,实现变量共享。需要注意的是,get_variable在variable_scope内使用才会给name加上前缀,在name_scope中使用并不会。
(1)节点代表着多种功能,比如说输入,变量初始化,运算,控制,输出等;
(2)边代表输入与输出之间的关系,即数据流动的方向。
(1). model.train()——训练时候启用
启用 BatchNormalization 和 Dropout,将BatchNormalization和Dropout置为True
(2). model.eval()——验证和测试时候启用
不启用 BatchNormalization 和 Dropout,将BatchNormalization和Dropout置为False
train模式会计算梯度,eval模式不会计算梯度。
参考 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中则展开为列。所以说,行列的问题没有本质区别,目的都是为了在计算时读取连续的内存。
参考 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执行方式:
【参考】
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()