一、学习目标
1、学习使用google Colab编写代码
2、学习pytorch深度学习的基础代码
3、⽤神经⽹络实现简单数据分类
二、学习内容
列出实验的关键步骤、代码解析、截图。
2.1 pytorch 基础练习
定义数据使用torch.Tensor , tensor的意思是张量,是数字各种形式的总称
Tensor支持各种各样类型的数据,包括:torch.float32, torch.float64, torch.float16, torch.uint8, torch.int8, torch.int16, torch.int32, torch.int64 。
创建Tensor有多种方法,
使用matplotlib作图
2.2 螺旋数据分类
1.导入需要用到的画图脚本plot_lib.py
!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
显示导入成功:
在content目录下可见:
2.引入基本的库,然后初始化重要参数
tips:plot_lib.py文件额外引用了一张图片,直接引入该文件会报错,因为接下来所需要用的方法与该图片无关,选择注释。
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
# 因为colab是支持GPU的,torch 将在 GPU 上运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('device: ', device)
# 初始化随机数种子。神经网络的参数都是随机初始化的,
# 不同的初始化参数往往会导致不同的结果,当得到比较好的结果时我们通常希望这个结果是可以复现的,
# 因此,在pytorch中,通过设置随机数种子也可以达到这个目的
seed = 12345
random.seed(seed)
torch.manual_seed(seed)
N = 1000 # 每类样本的数量
D = 2 # 每个样本的特征维度
C = 3 # 样本的类别
H = 100 # 神经网络里隐层单元的数量
随机种子是为了确保实验的可重复性,当下一次运行时设置seed=12345随机生成的数据还是一致的。
结果:
3.初始化 X 和 Y。 X 可以理解为特征矩阵,Y可以理解为样本标签。 结合代码可以看到,X的为一个 NxC 行, D 列的矩阵。C 类样本,每类样本是 N个,所以是 N*C 行。每个样本的特征维度是2,所以是 2列。
在 python 中,调用 zeros 类似的函数,第一个参数是 y方向的,即矩阵的行;第二个参数是 x方向的,即矩阵的列,不要搞反了。
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]间均匀的取10000个数,赋给t
# 下面的代码不用理解太多,总之是根据公式计算出三类样本(可以构成螺旋形)
# torch.randn(N) 是得到 N 个均值为0,方差为 1 的一组随机数,注意要和 rand 区分开
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())
结果:
4.构建线性模型分类
learning_rate = 1e-3
lambda_l2 = 1e-5
# nn 包用来创建线性模型
# 每一个线性模型都包含 weight 和 bias
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()
准确率仅有0.504,可见对于这样复杂的一个数据分布,线性模型难以实现准确分类
5.添加激活函数
ReLU(Rectified Linear Unit)函数
-
公式:
R e L U ( x ) = m a x ( 0 , x ) ReLU(x)=max(0,x) ReLU(x)=max(0,x)
特点:当输入大于0时输出输入值,否则输出0。它解决了梯度消失问题,计算效率高,是目前最常用的激活函数之一。
仅有此处代码不同,其余一样
# 这里可以看到,和上面模型不同的是,在两层之间加入了一个 ReLU 激活函数
model = nn.Sequential(
nn.Linear(D, H),
nn.ReLU(),
nn.Linear(H, C)
)
准确率也上来了,达到0.955
三、程序运行结果
列出程序的最终运行结果及截图。
1.根据特征矩阵X和样本标签Y作图
# visualise the data
plot_data(X, Y)
2.使用线性模型分类结果:
3.使用两层神经网络分类(加入激活函数)
四、问题总结与体会
AlexNet的特点及为何优于LeNet:
- 特点:
- 比LeNet 网络更深、更宽。
- 使用了 ReLU 和 Dropout,AlexNet 在训练时更稳定,不易过拟合。
- 使用了数据增强技术,通过对训练图像进行随机变换来增加数据多样性。
- 采用了 GPU 加速训练,显著提高了训练速度。
- 为何优于LeNet:
- AlexNet 的网络结构更深,有更多的卷积层和全连接层,能够学习更复杂的特征。
- 由于使用了 ReLU 和 Dropout,AlexNet 在训练时更稳定,且不易过拟合。
- 数据增强和 GPU 加速使得 AlexNet 能够处理更大的数据集和更快地训练。
激活函数的作用:
- 引入非线性,使得神经网络能够学习复杂的函数映射。
- 有助于缓解梯度消失问题。
梯度消失现象:
- 参数梯度无限趋近于0,导致网络中的权重更新非常缓慢或几乎不更新,从而使得网络难以训练。。
神经网络的宽度与深度:
- 宽度:指的是每层的神经元数量,增加宽度可以提供更多的参数来学习数据的特征,但同时也会增加计算复杂度和过拟合的风险。
- 深度:指的是增加网络的层数,增加深度可以增加网络的学习能力,使其能够学习更复杂的特征层次,但可能会导致梯度消失或梯度爆炸问题。
- 一般来说,深度的贡献是指数增加,而宽度的贡献是线性增加。
为什么要使用Softmax:
- Softmax 函数可以将神经网络输出的 logits 转换为概率分布,使得每个类别的预测概率都在 0 到 1 之间,且所有类别的概率之和为 1。
SGD与Adam的有效性:
- SGD(随机梯度下降):是最基础的优化算法,它在每次迭代中使用一个样本或一个批次的样本来估计梯度。SGD 简单且在很多情况下都很有效,但可能需要更多的迭代次数来收敛。
- Adam(自适应矩估计):是一种更高级的优化算法,它结合了动量方法和自适应学习率的优点。Adam 能够自动调整每个参数的学习率,通常收敛速度更快,对超参数的选择更不敏感。
- 两者的有效性取决于具体任务和数据。在某些情况下,SGD 可能更有效,因为它更简单,而 Adam 可能在其他情况下表现更好,因为它提供了更快的收敛速度和更好的稳定性。通常,Adam 在实践中更受欢迎,因为它通常能够更快地收敛,并且对超参数的调整要求较低。
代码问题1:在引入plot_lib文件时报错
解决:先稳住,看报错信息,根据报错信息去原文件修改。
代码问题2:print(y_pred[10, :])输出的概率为负数
解决:查阅资料得知y_pred
是模型的原始输出,也就是logits。这些值可以是正的、负的或零。