1.什么是 PyTorch ?
PyTorch是一个python库,它主要提供了两个高级功能:
- GPU加速的张量计算
- 构建在反向自动求导系统上的深度神经网络
一般定义数据使用torch.Tensor , tensor的意思是张量,是数字各种形式的总称
import torch
x = torch.tensor(666) #可以是一个数
y = torch.tensor([1,2,3,4,5]) #也可以是一个一维数组
z = torch.ones(2,3) #也可以是二维数组
a = torch.ones(2,2,4) #也可以是任意维度的数组
b = torch.empty(2,3) #创建一个空张量
c = torch.rand(2,3) #创建一个随机初始化的张量
d = torch.zeros(2,3,dtype=torch.long) #创建一个全0的张量,里面的数据类型为long
e = y.new_ones(2,3) #基于原有的张量,从而可以利用原有的tensor的dtype,device,size之类的属性信息
f = torch.randn_like(a,dtype=torch.float) #利用原来的tensor的大小,但是重新定义了dtype
print(x)
print(y)
print(z)
print(a)
print(b)
print(c)
print(d)
print(e)
print(f)
2. 定义操作
凡是用Tensor进行各种运算的,都是Function
最终,还是需要用Tensor来进行计算的,计算无非是
- 基本运算,加减乘除,求幂求余
- 布尔运算,大于小于,最大最小
- 线性运算,矩阵乘法,求模,求行列式
import torch
x = torch.Tensor([[2,3,4,5],[3,4,5,6]])
print(x.size(0),x.size(1),x.size(),sep='--') #打印出x的行数,列数,以及行列数
print(x.numel()) #打印出张量共多少数据量
print(x[0][2]) #打印出第0行第二列的数据
print(x[:,1]) #打印出第一列
print(x[1,:]) #打印出第一行
y = torch.arange(1,5,dtype=torch.float) #生成一个左闭右开连续数的张量
print(y)
x @ y #使用@计算两个张量的标量积,需要数据都是float类型的
x[0,:] @ y
x + torch.rand(2,4) #在张量x的基础上加一个2*4的随机张量
print(x.t() )#转置,由 2x4 变为 4x2
torch.linspace(3,8,20) #返回起始=3、结束=8和步长=20之间等距点的步长1D张量
import torch
from matplotlib import pyplot as plt
plt.hist(torch.randn(1000).numpy(),100) #生成1000个随机数并按照100个bin统计直方图,注意randn是生成均值为0,方差为1的随机数
3.螺旋数据分类
样本的初始化
import random
import torch
from torch import nn, optim
import math
from IPython import display
from plot_lib import plot_data, plot_model, set_default
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device: ', device)
seed = 12345
random.seed(seed)
torch.manual_seed(seed)
N = 1000 # 每类样本的数量
D = 2 # 每个样本的特征维度
C = 3 # 样本的类别
H = 100 # 神经网络里隐层单元的数量
X = torch.zeros(N * C, D).to(device)
Y = torch.zeros(N * C, dtype=torch.long).to(device)
for c in range(C):
index = 0
t = torch.linspace(0, 1, N) # 在[0,1]间均匀的取1000个数,赋给t
inner_var = torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2+c), N) + torch.randn(N) * 0.2
# 每个样本的(x,y)坐标都保存在 X 里
# Y 里存储的是样本的类别,分别为 [0, 1, 2]
for ix in range(N * c, N * (c + 1)):
X[ix] = t[index] * torch.FloatTensor((math.sin(inner_var[index]), math.cos(inner_var[index])))
Y[ix] = c
index += 1
print("Shapes:")
print("X:", X.size())
print("Y:", Y.size())
plot_data(X, Y)
模型
- 第一层输入为 2(因为特征维度为主2),输出为 100;
- 第二层输入为 100 (上一层的输出),输出为 3(类别数)
- 使用交叉熵损失函数,优化函数是SGD
learning_rate = 1e-3
lambda_l2 = 1e-5
model = nn.Sequential(
nn.Linear(D, H),
nn.Linear(H, C)
)
model.to(device) # 把模型放到GPU上
# nn 包含多种不同的损失函数,这里使用的是交叉熵(cross entropy loss)损失函数
criterion = torch.nn.CrossEntropyLoss()
# 这里使用 optim 包进行随机梯度下降(stochastic gradient descent)优化
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=lambda_l2)
for t in range(1000):
# 把数据输入模型,得到预测结果
y_pred = model(X)
# 计算损失和准确率
loss = criterion(y_pred, Y)
score, predicted = torch.max(y_pred, 1)
acc = (Y == predicted).sum().float() / len(Y)
print('[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f' % (t, loss.item(), acc))
display.clear_output(wait=True)
# 反向传播前把梯度置 0
optimizer.zero_grad()
# 反向传播优化
loss.backward()
# 更新全部参数
optimizer.step()
print(y_pred.shape)
print(y_pred[10, :])
print(score[10])
print(predicted[10])
print(model)
plot_model(X, Y, model)
在模型两层全连接层之间加入relu激活函数防止过拟合,分类效果更好,且优化函数换为Adam
learning_rate = 1e-3
lambda_l2 = 1e-5
model = nn.Sequential(
nn.Linear(D, H),
nn.ReLU(),
nn.Linear(H, C)
)
model.to(device)
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay=lambda_l2) # built-in L2
for t in range(1000):
y_pred = model(X)
loss = criterion(y_pred, Y)
score, predicted = torch.max(y_pred, 1)
acc = ((Y == predicted).sum().float() / len(Y))
print("[EPOCH]: %i, [LOSS]: %.6f, [ACCURACY]: %.3f" % (t, loss.item(), acc))
display.clear_output(wait=True)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(model)
plot_model(X, Y, model)
大家可以看到,在两层神经网络里加入 ReLU 激活函数以后,分类的准确率得到了显著提高。
问题总结
1、AlexNet有哪些特点?为什么可以比LeNet取得更好的性能?
AlexNet包含8层变换,有5层卷积和2层全连接隐藏层,以及1个全连接输出层
AlexNet第一层中的卷积核形状是11×11。第二层中的卷积核形状减小到5×5,之后全采用 3×3。所有的池化层窗口大小为3×3、步幅为2的最大池化。
AlexNet将sigmoid激活函数改成了ReLU激活函数,使计算更简单,网络更容易训练
AlexNet通过dropOut来控制全连接层的模型复杂度。
AlexNet引入了大量的图像增强,如翻转、裁剪和颜色变化,从而进一步扩大数据集来缓解过拟合。
基于上述进步更新,使得AlexNet要比LeNet性能更好。
2、激活函数有哪些作用?
激活函数是用来加入非线性因素的,提高神经网络对模型的表达能力,解决线性模型所不能解决的问题这使得神经网络拥有能拟合所有非线性函数的本领。
3、梯度消失现象是什么?
梯度消失就是指在网络反向传播过程中由于链式求导法则不断的累积,如果每一层的梯度都小于1,由于累乘效应,出现了某些参数的梯度非常小的现象。在使用这些梯度更新梯度的时候参数值基本没有发生变化,因此就出现了网络训练停滞、模型无法继续优化的问题。
4、神经网络是更宽好还是更深好?
神经网络一般来说不是越深越好,也不是越宽越好。但是目前针对神经网络宽度的研究远远比不上深度。1.提升同样效果需要增加的宽度远远超过需要增加的深度。2.层的优势在于可以在不同的抽象层次上学习特征,随着层数的增加,每个神经元相对于前一层的感受野变得越来越大,因此深层可以提供全局语义和抽象细节的信息,这是宽层很难做到的。
5、为什么要使用Softmax?
Softmax 函数可以将输出映射到概率空间,适用于分类问题,Softmax 函数的输出可以被解释为各个类别的概率,Softmax 函数在多分类问题中表现良好。
6、SGD 和 Adam 哪个更有效?
SGD的优点:由于不是在全部训练数据上的损失函数,而是在每轮迭代中,随机优化某一条训练数据上的损失函数,这样每一轮参数的更新速度大大加快。
SGD的缺点:准确度下降。由于即使在目标函数为强凸函数的情况下,SGD仍旧无法做到线性收敛。可能会收敛到局部最优,由于单个样本并不能代表全体样本的趋势。不易于并行实现。
数据的分布好计算,更适合SGD,而对于Adam更适合计算复杂分布的数据。