1、Pytorch - Conv2d 卷积
CLASS torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
假设 Conv2d 的输入 input 尺寸为 (N,Cin,Hin,Win),输出 output 尺寸为 (N,Cout,Hout,Wout),有:
其中,∗ 是 2D cross-correlation 操作. N - batch size, C - channels 数, H - Hight, W - Width.
- [1] - kernel_size
- [2] - stride - 步长
- [3] - padding - 每一维补零的数量
- [4] - dilation - 控制 kernel 点之间的空间距离(the spacing between the kernel points). 带孔卷积(atrous conv). 参考 dilated-convolution-animations.
- [5] - groups - 控制 inputs 和 outputs 间的关联性(分组). 其中,要求
in_channels
和out_channels
必须都可以被groups
整除.
demo
import torch
import torch.nn as nn
# With square kernels and equal stride
m = nn.Conv2d(16, 33, 3, stride=2)
# non-square kernels and unequal stride and with padding
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
# non-square kernels and unequal stride and with padding and dilation
m = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2), dilation=(3, 1))
input = torch.randn(20, 16, 50, 100)
output = m(input)
2、nn.BatchNorm2d
3、nn.ReLU
tf.nn.relu(features, name = None)
这个函数的作用是计算**函数 relu,即 max(features, 0)。即将矩阵中每行的非最大值置0。
import tensorflow as tf
a = tf.constant([-1.0, 2.0])
with tf.Session() as sess:
b = tf.nn.relu(a)
print sess.run(b)
ReLU函数表达式为:
其函数曲线图为:
由函数图像可知其导数为分段函数
x <= 0时,导数为0
x > 0时,导数为1
优点:
解决了gradient vanishing问题 (在正区间)
计算速度非常快,只需要判断输入是否大于0
收敛速度远快于sigmoid和tanh
缺点:
不是zero-centered
某些神经元可能永远不会被**
对应的tf函数为:tf.nn.relu()
4、nn.ModuleList
,nn.Sequential类似于Keras中的贯序模型,它是Module的子类,在构建数个网络层之后会自动调用forward()方法,从而有网络模型生成。而nn.ModuleList仅仅类似于pytho中的list类型,只是将一系列层装入列表,并没有实现forward()方法,因此也不会有网络模型产生的副作用。
需要注意的是,nn.ModuleList接受的必须是subModule类型,例如:
nn.ModuleList(
[nn.ModuleList([Conv(inp_dim + j * increase, oup_dim, 1, relu=False, bn=False) for j in range(5)]) for i in
range(nstack)])
其中,二次嵌套的list内部也必须额外使用一个nn.ModuleList修饰实例化,否则会无法识别类型而报错!
5、nn.Linear
最近主要看了一下pytorch nn中一些函数的具体形式
一般定义一个linear层的时候,写法为nn.Linear(in_features,out_features)
具体形式为:
y = wx+b
weight = Parameter(torch.Tensor(out_features, in_features))
bias = Parameter(torch.Tensor(out_features))
6、log_softmax
函数 Softmax(x) 也是一个 non-linearity, 但它的特殊之处在于它通常是网络中一次操作. 这是因为它接受了一个实数向量并返回一个概率分布.其定义如下. 定义 x 是一个实数的向量(正数或负数都无所谓, 没有限制). 然后, 第i个 Softmax(x) 的组成是
输出是一个概率分布: 每个元素都是非负的, 并且所有元素的总和都是1.
在softmax的结果上再做多一次log运算
虽然在数学上等价于log(softmax(x)),但做这两个
单独操作速度较慢,数值上也不稳定。这个函数
使用另一种公式来正确计算输出和梯度。
7、Tanh函数
sigmoid和tanh函数之间的关系式:
sigmoid函数和tanh函数导数区别
考虑相同的输入区间[0,1]
sigmoid函数导数输出范围为[0.20,0.25]
tanh函数导数曲线图为
其输出范围为[0.42,1]
优点:
对比sigmoid和tanh两者导数输出可知,tanh函数的导数比sigmoid函数导数值更大,即梯度变化更快,也就是在训练过程中收敛速度更快。
输出范围为-1到1之间,这样可以使得输出均值为0,这个性质可以提高BP训练的效率,具体原因参考文献 http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf
将线性函数转变为非线性函数
缺点:
gradient vanishing
幂运算相对来讲比较耗时
对应的tf函数:tf.nn.tanh()
8、存储和恢复模型并查看参数
在模型完成训练后,我们需要将训练好的模型保存为一个文件供测试使用,或者因为一些原因我们需要继续之前的状态训练之前保存的模型,那么如何在PyTorch中保存和恢复模型呢?
参考PyTorch官方的这份repo,我们知道有两种方法可以实现我们想要的效果
方法一(推荐):
第一种方法也是官方推荐的方法,只保存和恢复模型中的参数。
保存
1 |
torch.save(the_model.state_dict(), PATH) |
恢复
1 2 |
the_model = TheModelClass(*args, **kwargs) the_model.load_state_dict(torch.load(PATH)) |
使用这种方法,我们需要自己导入模型的结构信息。
方法二:
使用这种方法,将会保存模型的参数和结构信息。
保存
1 |
torch.save(the_model, PATH) |
恢复
1 |
the_model = torch.load(PATH) |
一个相对完整的例子
saving
1 2 3 4 5 6 |
torch.save({ 'epoch': epoch + 1, 'arch': args.arch, 'state_dict': model.state_dict(), 'best_prec1': best_prec1, }, 'checkpoint.tar' ) |
loading
1 2 3 4 5 6 7 8 9 |
if args.resume: if os.path.isfile(args.resume): print("=> loading checkpoint '{}'".format(args.resume)) checkpoint = torch.load(args.resume) args.start_epoch = checkpoint['epoch'] best_prec1 = checkpoint['best_prec1'] model.load_state_dict(checkpoint['state_dict']) print("=> loaded checkpoint '{}' (epoch {})" .format(args.evaluate, checkpoint['epoch'])) |
获取模型中某些层的参数
对于恢复的模型,如果我们想查看某些层的参数,可以:
1 2 3 4 5 6 7 8 9 10 |
# 定义一个网络 from collections import OrderedDict model = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ])) # 打印网络的结构 print(model) |
Out:
1 2 3 4 5 6 |
Sequential ( (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1)) (relu1): ReLU () (conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1)) (relu2): ReLU () ) |
如果我们想获取conv1的weight和bias:
1 2 3 4 5 |
params=model.state_dict() for k,v in params.items(): print(k) #打印网络中的变量名 print(params['conv1.weight']) #打印conv1的weight print(params['conv1.bias']) #打印conv1的bias |
9、Deque 模块使用详解
创建Deque序列:
1 2 3 |
|
Deque提供了类似list的操作方法:
1 2 3 4 5 6 7 |
|
输出结果:
1 2 3 |
|
两端都使用pop:
1 2 3 4 5 |
|
输出结果:
1 2 3 4 |
|
我们还可以限制deque的长度:
d = deque(maxlen=30)
当限制长度的deque增加超过限制数的项时, 另一边的项会自动删除:
1 2 3 4 5 6 7 8 |
|
添加list中各项到deque中:
1 2 3 4 |
|
输出结果:
1 |
|
10、torch.optim.Adam
torch.optim是一个实现了多种优化算法的包,大多数通用的方法都已支持,提供了丰富的接口调用,未来更多精炼的优化算法也将整合进来。
为了使用torch.optim,需先构造一个优化器对象Optimizer,用来保存当前的状态,并能够根据计算得到的梯度来更新参数。
要构建一个优化器optimizer,你必须给它一个可进行迭代优化的包含了所有参数(所有的参数必须是变量s)的列表。 然后,您可以指定程序优化特定的选项,例如学习速率,权重衰减等。
optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)
optimizer = optim.Adam([var1, var2], lr = 0.0001)
self.optimizer_D_B = torch.optim.Adam(self.netD_B.parameters(), lr=opt.lr, betas=(opt.beta1, 0.999))
Optimizer还支持指定每个参数选项。 只需传递一个可迭代的dict来替换先前可迭代的Variable。dict中的每一项都可以定义为一个单独的参数组,参数组用一个params键来包含属于它的参数列表。其他键应该与优化器接受的关键字参数相匹配,才能用作此组的优化选项。
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
如上,model.base.parameters()将使用1e-2的学习率,model.classifier.parameters()将使用1e-3的学习率。0.9的momentum作用于所有的parameters。
优化步骤:
所有的优化器Optimizer都实现了step()方法来对所有的参数进行更新,它有两种调用方法:
optimizer.step()
这是大多数优化器都支持的简化版本,使用如下的backward()方法来计算梯度的时候会调用它。
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
optimizer.step(closure)
一些优化算法,如共轭梯度和LBFGS需要重新评估目标函数多次,所以你必须传递一个closure以重新计算模型。 closure必须清除梯度,计算并返回损失。
for input, target in dataset:
def closure():
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
return loss
optimizer.step(closure)
Adam算法:
adam算法来源:Adam: A Method for Stochastic Optimization
Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。它的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。其公式如下:
其中,前两个公式分别是对梯度的一阶矩估计和二阶矩估计,可以看作是对期望E|gt|,E|gt^2|的估计;
公式3,4是对一阶二阶矩估计的校正,这样可以近似为对期望的无偏估计。可以看出,直接对梯度的矩估计对内存没有额外的要求,而且可以根据梯度进行动态调整。最后一项前面部分是对学习率n形成的一个动态约束,而且有明确的范围。
class torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
参数:
params(iterable):可用于迭代优化的参数或者定义参数组的dicts。
lr (float, optional) :学习率(默认: 1e-3)
betas (Tuple[float, float], optional):用于计算梯度的平均和平方的系数(默认: (0.9, 0.999))
eps (float, optional):为了提高数值稳定性而添加到分母的一个项(默认: 1e-8)
weight_decay (float, optional):权重衰减(如L2惩罚)(默认: 0)
step(closure=None)函数:执行单一的优化步骤
closure (callable, optional):用于重新评估模型并返回损失的一个闭包
torch.optim.adam源码:
import math
from .optimizer import Optimizer
class Adam(Optimizer):
def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,weight_decay=0):
defaults = dict(lr=lr, betas=betas, eps=eps,weight_decay=weight_decay)
super(Adam, self).__init__(params, defaults)
def step(self, closure=None):
loss = None
if closure is not None:
loss = closure()
for group in self.param_groups:
for p in group['params']:
if p.grad is None:
continue
grad = p.grad.data
state = self.state[p]
# State initialization
if len(state) == 0:
state['step'] = 0
# Exponential moving average of gradient values
state['exp_avg'] = grad.new().resize_as_(grad).zero_()
# Exponential moving average of squared gradient values
state['exp_avg_sq'] = grad.new().resize_as_(grad).zero_()
exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
beta1, beta2 = group['betas']
state['step'] += 1
if group['weight_decay'] != 0:
grad = grad.add(group['weight_decay'], p.data)
# Decay the first and second moment running average coefficient
exp_avg.mul_(beta1).add_(1 - beta1, grad)
exp_avg_sq.mul_(beta2).addcmul_(1 - beta2, grad, grad)
denom = exp_avg_sq.sqrt().add_(group['eps'])
bias_correction1 = 1 - beta1 ** state['step']
bias_correction2 = 1 - beta2 ** state['step']
step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1
p.data.addcdiv_(-step_size, exp_avg, denom)
return loss
Adam的特点有:
1、结合了Adagrad善于处理稀疏梯度和RMSprop善于处理非平稳目标的优点;
2、对内存需求较小;
3、为不同的参数计算不同的自适应学习率;
4、也适用于大多非凸优化-适用于大数据集和高维空间
或者看一下这个博客:https://www.cnblogs.com/dylancao/p/9878978.html
11、ScheduledOptim
学习率的重要性
目前深度学习使用的都是非常简单的一阶收敛算法,梯度下降法,不管有多少自适应的优化算法,本质上都是对梯度下降法的各种变形,所以初始学习率对深层网络的收敛起着决定性的作用,下面就是梯度下降法的公式
[Math Processing Error]w:=w−α∂∂wloss(w)
这里[Math Processing Error]α就是学习率,如果学习率太小,会导致网络loss下降非常慢,如果学习率太大,那么参数更新的幅度就非常大,就会导致网络收敛到局部最优点,或者loss直接开始增加,如下图所示。
学习率的选择策略在网络的训练过程中是不断在变化的,在刚开始的时候,参数比较随机,所以我们应该选择相对较大的学习率,这样loss下降更快;当训练一段时间之后,参数的更新就应该有更小的幅度,所以学习率一般会做衰减,衰减的方式也非常多,比如到一定的步数将学习率乘上0.1,也有指数衰减等。
这里我们关心的一个问题是初始学习率如何确定,当然有很多办法,一个比较笨的方法就是从0.0001开始尝试,然后用0.001,每个量级的学习率都去跑一下网络,然后观察一下loss的情况,选择一个相对合理的学习率,但是这种方法太耗时间了,能不能有一个更简单有效的办法呢?
一个简单的办法
Leslie N. Smith 在2015年的一篇论文“Cyclical Learning Rates for Training Neural Networks”中的3.3节描述了一个非常棒的方法来找初始学习率,同时推荐大家去看看这篇论文,有一些非常启发性的学习率设置想法。
这个方法在论文中是用来估计网络允许的最小学习率和最大学习率,我们也可以用来找我们的最优初始学习率,方法非常简单。首先我们设置一个非常小的初始学习率,比如1e-5,然后在每个batch之后都更新网络,同时增加学习率,统计每个batch计算出的loss。最后我们可以描绘出学习的变化曲线和loss的变化曲线,从中就能够发现最好的学习率。
下面就是随着迭代次数的增加,学习率不断增加的曲线,以及不同的学习率对应的loss的曲线。
从上面的图片可以看到,随着学习率由小不断变大的过程,网络的loss也会从一个相对大的位置变到一个较小的位置,同时又会增大,这也就对应于我们说的学习率太小,loss下降太慢,学习率太大,loss有可能反而增大的情况。从上面的图中我们就能够找到一个相对合理的初始学习率,0.1。
之所以上面的方法可以work,因为小的学习率对参数更新的影响相对于大的学习率来讲是非常小的,比如第一次迭代的时候学习率是1e-5,参数进行了更新,然后进入第二次迭代,学习率变成了5e-5,参数又进行了更新,那么这一次参数的更新可以看作是在最原始的参数上进行的,而之后的学习率更大,参数的更新幅度相对于前面来讲会更大,所以都可以看作是在原始的参数上进行更新的。正是因为这个原因,学习率设置要从小变到大,而如果学习率设置反过来,从大变到小,那么loss曲线就完全没有意义了。
实现
上面已经说明了算法的思想,说白了其实是非常简单的,就是不断地迭代,每次迭代学习率都不同,同时记录下来所有的loss,绘制成曲线就可以了。下面就是使用PyTorch实现的代码,因为在网络的迭代过程中学习率会不断地变化,而PyTorch的optim里面并没有把learning rate的接口暴露出来,导致显示修改学习率非常麻烦,所以我重新写了一个更加高层的包mxtorch,借鉴了gluon的一些优点,在定义层的时候暴露初始化方法,支持tensorboard,同时增加了大量的model zoo,包括inceptionresnetv2,resnext等等,提供预训练权重。
下面就是部分代码,这里使用的数据集是kaggle上的dog breed,使用预训练的resnet50,ScheduledOptim的源码如下:
-
class ScheduledOptim(object):
-
'''A wrapper class for learning rate scheduling'''
-
def __init__(self, optimizer):
-
self.optimizer = optimizer
-
self.lr = self.optimizer.param_groups[0]['lr']
-
self.current_steps = 0
-
def step(self):
-
"Step by the inner optimizer"
-
self.current_steps += 1
-
self.optimizer.step()
-
def zero_grad(self):
-
"Zero out the gradients by the inner optimizer"
-
self.optimizer.zero_grad()
-
def set_learning_rate(self, lr):
-
self.lr = lr
-
for param_group in self.optimizer.param_groups:
-
param_group['lr'] = lr
-
@property
-
def learning_rate(self):
-
return self.lr
-
def find_lr():
-
pass
-
def train_model():
-
pass
整体代码如下
-
criterion = torch.nn.CrossEntropyLoss()
-
net = model_zoo.resnet50(pretrained=True)
-
net.fc = nn.Linear(2048, 120)
-
with torch.cuda.device(0):
-
net = net.cuda()
-
basic_optim = torch.optim.SGD(net.parameters(), lr=1e-5)
-
optimizer = ScheduledOptim(basic_optim)
-
lr_mult = (1 / 1e-5) ** (1 / 100)
-
lr = []
-
losses = []
-
best_loss = 1e9
-
for data, label in train_data:
-
with torch.cuda.device(0):
-
data = Variable(data.cuda())
-
label = Variable(label.cuda())
-
# forward
-
out = net(data)
-
loss = criterion(out, label)
-
# backward
-
optimizer.zero_grad()
-
loss.backward()
-
optimizer.step()
-
lr.append(optimizer.learning_rate)
-
losses.append(loss.data[0])
-
optimizer.set_learning_rate(optimizer.learning_rate * lr_mult)
-
if loss.data[0] < best_loss:
-
best_loss = loss.data[0]
-
if loss.data[0] > 4 * best_loss or optimizer.learning_rate > 1.:
-
break
-
plt.figure()
-
plt.xticks(np.log([1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1]), (1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1))
-
plt.xlabel('learning rate')
-
plt.ylabel('loss')
-
plt.plot(np.log(lr), losses)
-
plt.show()
-
plt.figure()
-
plt.xlabel('num iterations')
-
plt.ylabel('learning rate')
-
plt.plot(lr)
12、nn.MSELoss
均方损失函数:
loss(xi,yi)=(xi−yi)2
loss(xi,yi)=(xi−yi)2
这里 loss, x, y 的维度是一样的,可以是向量或者矩阵,i 是下标。
很多的 loss 函数都有 size_average 和 reduce 两个布尔类型的参数。因为一般损失函数都是直接计算 batch 的数据,因此返回的 loss 结果都是维度为 (batch_size, ) 的向量。
(1)如果 reduce = False,那么 size_average 参数失效,直接返回向量形式的 loss
(2)如果 reduce = True,那么 loss 返回的是标量
a)如果 size_average = True,返回 loss.mean();
b)如果 size_average = False,返回 loss.sum();
1
2
注意:默认情况下, reduce = True,size_average = True
import torch
import numpy as np
1
2
1、返回向量
loss_fn = torch.nn.MSELoss(reduce=False, size_average=False)
1
a=np.array([[1,2],[3,4]])
b=np.array([[2,3],[4,5]])
1
2
input = torch.autograd.Variable(torch.from_numpy(a))
target = torch.autograd.Variable(torch.from_numpy(b))
1
2
这里将Variable类型统一为float()(tensor类型也是调用xxx.float())
loss = loss_fn(input.float(), target.float())
1
print(loss)
1
tensor([[ 1., 1.],
[ 1., 1.]])
1
2
2、返回平均值
a=np.array([[1,2],[3,4]])
b=np.array([[2,3],[4,4]])
1
2
loss_fn = torch.nn.MSELoss(reduce=True, size_average=True)
1
input = torch.autograd.Variable(torch.from_numpy(a))
target = torch.autograd.Variable(torch.from_numpy(b))
1
2
loss = loss_fn(input.float(), target.float())
1
print(loss)
1
tensor(0.7500)
13、pytorch在cpu上加载预先训练好的GPU模型以及GPU上加载CPU上训练的Model
有时候我们在CPU上训练的模型,因为一些原因,切换到GPU上,或者在GPU上训练的模型,因为条件限制,切换到CPU上。 GPU上训练模型时,将权重加载到CPU的最佳方式是什么?今天我们来讨论一下:
提取模型到指定的
从官方文档中我们可以看到如下方法
torch.load('tensors.pt')
# 把所有的张量加载到CPU中
torch.load('tensors.pt', map_location=lambda storage, loc: storage)
# 把所有的张量加载到GPU 1中
torch.load('tensors.pt', map_location=lambda storage, loc: storage.cuda(1))
# 把张量从GPU 1 移动到 GPU 0
torch.load('tensors.pt', map_location={'cuda:1':'cuda:0'})
在cpu上加载预先训练好的GPU模型,有一种强制所有GPU张量在CPU中的方式:
torch.load('my_file.pt', map_location=lambda storage, loc: storage)
上述代码只有在模型在一个GPU
上训练时才起作用。如果我在多个GPU
上训练我的模型,保存它,然后尝试在CPU
上加载,我得到这个错误:KeyError: 'unexpected key "module.conv1.weight" in state_dict'
如何解决?
您可能已经使用模型保存了模型nn.DataParallel
,该模型将模型存储在该模型中module
,而现在您正试图加载模型DataParallel
。您可以nn.DataParallel
在网络中暂时添加一个加载目的,也可以加载权重文件,创建一个没有module
前缀的新的有序字典,然后加载它。
参考:
# original saved file with DataParallel
state_dict = torch.load('myfile.pth.tar')
# create new OrderedDict that does not contain `module.`
from collections import OrderedDict
new_state_dict = OrderedDict()
for k, v in state_dict.items():
name = k[7:] # remove `module.`
new_state_dict[name] = v
# load params
model.load_state_dict(new_state_dict)
14、np.full
p.full()函数可以生成初始化为指定值的数组
>> np.full(4,np.pi)
array([ 3.14159265, 3.14159265, 3.14159265, 3.14159265])
>>> np.full((2,3),np.pi)
array([[ 3.14159265, 3.14159265, 3.14159265],
[ 3.14159265, 3.14159265, 3.14159265]])
15、np.concatenate
numpy.concatenate((a1, a2, ...), axis=0)
Join a sequence of arrays along an existing axis.(按轴axis连接array组成一个新的array)
The arrays must have the same shape, except in the dimension corresponding to axis
axis:default is 0
>>> a = np.array([[1, 2], [3, 4]])
>>> b = np.array([[5, 6]]) b是一个二维array
>>> np.concatenate((a, b), axis=0)
array([[1, 2],
[3, 4],
[5, 6]])
>>> np.concatenate((a, b.T), axis=1)
array([[1, 2, 5],
[3, 4, 6]])
>>> b = np.array([[5,6]]) 可以看出b是二维的不是一维的
>>> b.shape
(1, 2)
>>> b = np.array([5,6])
>>> b.shape
(2,)
更普通的例子
>>> a = np.array([[1, 2], [3, 4]]) a、b的shape为(2,2),连接第一维就变成(4,2),连接第二维就变成(2,4)
>>> b = np.array([[5, 6], [7, 8]])
>>> np.concatenate((a,b),axis=0)
array([[1, 2],
[3, 4],
[5, 6],
[7, 8]])
>>> np.concatenate((a,b),axis=1)
array([[1, 2, 5, 6],
[3, 4, 7, 8]])
>>> c = np.concatenate((a,b),axis=1)
>>> c
array([[1, 2, 5, 6],
[3, 4, 7, 8]])
>>> c.shape
(2, 4)
concatenate([a, b])
连接,连接后ndim不变,a和b可以有一维size不同,但size不同的维度必须是要连接的维度
例如,a.shape为(4,5,6,10),b.shape为(4,5,6,20)
np.concatenate([a,b], axis=3) # 返回张量的shape为(4,5,6,30)
有助于理解的例子。第一个例子是一维的,这一维全是数字,第二个例子是二维的,实际上可以看作将数字换成向量的一维的array。第一个例子axis=0把所有的数字
连接,第二个例子axis=0就可以把所有的向量连接。第二个例子中axis=1,这表明axis=0的个数不发生变化,只变化axis=1。axis=0不发生变化,那两个array对
应的axis=0的元素就可以进行连接。这两个array中的元素是一维向量,就对一维向量进行连接(其实这时候就相当于第一个例子中的连接了)。
若把axis=1中的数字换成一维向量就可以推广到3维的axis=1时的变化,若换到更高维可以推广到更高维的变化。
>>> a=np.array([1,2,3])
>>> b=np.array([11,22,33])
>>> c=np.array([44,55,66])
>>> np.concatenate((a,b,c),axis=0)
array([ 1, 2, 3, 11, 22, 33, 44, 55, 66])
>>> a=np.array([[1,2,3],[4,5,6]])
>>> b=np.array([[11,21,31],[7,8,9]])
>>> np.concatenate((a,b),axis=0)
array([[ 1, 2, 3],
[ 4, 5, 6],
[11, 21, 31],
[ 7, 8, 9]])
>>> np.concatenate((a,b),axis=1)
array([[ 1, 2, 3, 11, 21, 31],
[ 4, 5, 6, 7, 8, 9]])
16、np.exp
numpy.exp():返回e的幂次方,e是一个常数为2.71828
-
import numpy as np
-
a = 1
-
print np.exp(a)
-
a = 2
-
print np.exp(a)
结果:
2.71828182846
7.38905609893
np.exp(1) 为自身
np.exp(2) 为平方