一、实验内容1、pytorch 基础练习首先根据教程创建好 Colab 环境每次使用时都要先挂载 Google Drive从而访问 Google Drive 中的文件。from google.colab import drive drive.mount(/content/drive/)1定义数据一般定义数据使用 torch.Tensortensor的意思是张量是数字各种形式的总称import torch # 可以是一个数 x torch.tensor(666) print(x)import torch # 可以是一维数组向量 x torch.tensor([1, 2, 3, 4, 5, 6]) print(x)import torch # 可以是二维数组矩阵 x torch.ones(2, 3) print(x)import torch # 可以是任意维度的数组张量 x torch.ones(2,3,4) print(x)Tensor支持各种各样类型的数据包括:torch.float32torch.float64 torch.float16 torch.uint8torch.int8torch.int16torch.int32torch.int64 。创建Tensor有多种方法包括:oneszeroseyearangelinspacerandrandn normaluniformrandperm下面主要通过代码展示。import torch # 创建一个空张量 x torch.empty(5,3) print(x)import torch #创建一个随机初始化的张量 x torch.rand(5,3) print(x)import torch #创建一个全0的张量里面的数据类型为long x torch.zeros(5,3,dtypetorch.long) print(x)import torch #基于现有的tensor创建一个新的tensor #从而可以利用原有的tensor的的typedevicesize之类的属性信息 x torch.zeros(5,3,dtypetorch.long) y x.new_ones(5,3) print(y) #利用原来的tensor的大小但是重新定义了dtype z torch.randn_like(x,dtypetorch.float) print(z)2定义操作计算操作基本运算加减乘除求幂求余布尔运算大于小于最大最小线性运算矩阵乘法求模求行列式基本运算abs /sqrt /div /exp /fmod /pow 及一些三角函数 cos /sin /asin /atan2 /cosh及 ceil /round /floor /trunc 等布尔运算gt /lt /ge /le /eq /netopk, sort, max /min线性运算trace, diag, mm /bmmtdot/crossinversesvd 等下面为具体操作案例import torch #创建一个2*4的tensor m torch.Tensor([[2,5,3,7], [4,2,1,9]]) print(m.size(0),m.size(1),m.size(),sep - )size()用于获取张量的形状。m.size(0)表示第 0 维的大小m.size()返回完整的形状信息import torch m torch.tensor([[2,5,3,7], [4,2,1,9]]) # 获取张量中元素的总数量 print(m.numel()) # 获取第 0 行、第 2 列的元素 print(m[0][2]) # 获取第 1 行的全部元素 print(m[1, :]) # 获取第 0 列的全部元素 print(m[:, 0])import torch #Create tensor of numbers from 1 to 5 #注意这里结果是1到4没有5 v torch.arange(1,5) print(v)import torch v torch.arange(1,5,,dtypetorch.float32) m torch.Tensor([[2,5,3,7], [4,2,1,9]]) # Scalar product # m 和 v 的类型一样才可以使用 否则会报错 m v# Calculated by 1*2 2*5 3*3 4*7 m[[0],:] v# Add a random tensor of size 2*4 to m m torch.rand(2,4)#转置由2*4变为4*2 print(m.t()) #使用transpose也可以达到相同效果 print(m.transpose(0,1))import torch #returns a 1D tensor of steps equally spaced points between start3 end8 and steps20 #返回一个一维向量从3开始8结束共20个 torch.linspace(3,8,20)from matplotlib import pyplot as plt # matlabplotlib只能显示numpy类型的数据下面展示了转换数据类型然后显示 # 注意randn是生成均值为0,方差为1的随机数 # 下面是生成1000个随机数并按照100个bin统计直方图 plt.hist(torch.randn(1000).numpy(),100)# 当数据非常多的时候正态分布会体现的非常明显 plt.hist(torch.randn(10**6).numpy(),100)import torch # 创建两个 1*4 的tensor a torch.Tensor([[1,2,3,4]]) b torch.Tensor([[5,6,7,8]]) # 在0方向拼接即在Y方向上拼接会得到 2*4 的矩阵 print(torch.cat((a,b),0)) # 在1方向拼接即在X方向上拼接会得到 1*8 的矩阵 print(torch.cat((a,b),1))torch.cat()用于沿指定维度拼接多个张量。并且参与拼接的张量除拼接维度外其余维度的大小必须一致。2、螺旋数据分类1运行代码下载绘图函数到本地!wget https://raw.githubusercontent.com/Atcold/NYU-DLSP21/refs/heads/master/res/plot_lib.py2引入基本的库然后初始化重要参数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 # 神经网络里隐层单元的数量3初始化 X 和 YX 可以理解为特征矩阵Y可以理解为样本标签。结合代码可以看到X的为一个 NxC 行 D 列的矩阵。C 类样本每类样本是 N个所以是 N*C 行。每个样本的特征维度是2所以是 2列。在 python 中调用 zeros 类似的函数第一个参数是 y方向的即矩阵的行第二个参数是 x方向的即矩阵的列大家得注意下不要搞反了。下面结合代码看看 3000个样本的特征是如何初始化的。X torch.zeros(N * C, D).to(device) Y torch.zeros(N * C, dtypetorch.long).to(device) for c in range(C): index 0 t torch.linspace(0, 1, N) # 在[01]间均匀的取10000个数赋给t # 下面的代码不用理解太多总之是根据公式计算出三类样本可以构成螺旋形 # torch.randn(N) 是得到 N 个均值为0方差为 1 的一组随机数注意要和 rand 区分开 inner_var torch.linspace( (2*math.pi/C)*c, (2*math.pi/C)*(2c), 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())# visualise the data plot_data(X,Y)4构建线性模型分类learning_rate 1e-3 lambda_l2 1e-5 # nn包用来创建线性模型 # 每一个线性模型都包含weight和bias model nn.Sequential( nn.Linear(D, H), nn.Linear(H, C) ) #把模型放到GPU上 model.to(device) # nn包含多种不同的损失函数这里使用的是交叉(cross entropy loss)损失函数 criterion torch.nn.CrossEntropyLoss() #这里使用optim包进行随机梯度下降(stochastic gradient descent)优化 optimizer torch.optim.SGD(model.parameters(), lrlearning_rate, weight_decaylambda_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(waitTrue) # 反向传播前把梯度置0 optimizer.zero_grad() # 反向传播优化 loss.backward() # 更新全部参数 optimizer.step()使用 print(y_pred.shape) 可以看到模型的预测结果为[3000, 3]的矩阵。每个样本的预测结果为3个保存在 y_pred 的一行里。值最大的一个即为预测该样本属于的类别score, predicted torch.max(y_pred, 1) 是沿着第二个方向即X方向提取最大值。最大的那个值存在 score 中所在的位置即第几列的最大保存在 predicted 中。交叉熵损失函数用于衡量模型输出与真实类别之间的差异。随机梯度下降SGD优化器则根据反向传播计算得到的梯度更新模型参数。由于 PyTorch 在每次反向传播时会把梯度累加到上一次的梯度上不会自动清零。所以在每次反向传播前都要把梯度置0即optimizer.zero_grad()这样可以保证每次更新都只用当前批次的梯度。下面代码把第10行的情况输出供解释说明。print(y_pred.shape) print(y_pred[10,:]) print(score[10]) print(predicted[10])# Plot trained model print(model) plot_model(X,Y,model)上面使用 print(model) 把模型输出可以看到有两层第一层输入为 2因为特征维度为主2输出为 100第二层输入为 100 上一层的输出输出为 3类别数该模型包含两层线性变换但由于两层之间没有加入非线性激活函数因此两个线性变换的组合仍然等价于一个线性变换。而且从上面图示可以看出线性模型的准确率最高只能达到 50% 左右对于这样复杂的一个数据分布线性模型难以实现准确分类。5构建两层神经网络分类learning_rate 1e-3 lambda_l2 1e-5 # 这里可以看到和上面模型不同的是在两层之间加入了一个 ReLU 激活函数 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(), lrlearning_rate, weight_decaylambda_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(waitTrue) # zero the gradients before running the backward pass. optimizer.zero_grad() # Backward pass to compute the gradient loss.backward() # Update params optimizer.step()# Plot trained modelprint(model) plot_model(X, Y, model)加入 ReLU 激活函数以后分类的准确率得到了显著提高。说明面对具有复杂非线性结构的数据仅使用线性变换往往难以获得理想的分类效果。通过在隐藏层中加入非线性激活函数模型的表达能力得到增强更容易学习螺旋形数据对应的复杂决策边界。二、问题总结与体会思考题1、AlexNet有哪些特点为什么可以比 LeNet 取得更好的性能特点网络结构更深、规模更大相比 LeNetAlexNet 拥有更多的卷积层和全连接层能够提取更复杂的图像特征。使用 ReLU 激活函数相较于 Sigmoid 等传统激活函数ReLU 能够缓解梯度消失问题加快训练速度。使用 Dropout在训练过程中随机屏蔽部分神经元降低神经元之间的过度依赖缓解过拟合。使用重叠最大池化池化窗口大于步长有助于提取显著特征。利用 GPU 加速训练能够处理更大规模的网络和数据。使用数据增强通过随机裁剪、翻转等方式扩充训练数据提高模型的泛化能力。AlexNet 比 LeNet 性能更好的主要原因是它具有更强的特征提取能力采用了 ReLU、Dropout 和数据增强等技术并借助 GPU 提高了训练效率因此能够更有效地处理复杂的大规模图像分类任务。2、激活函数有哪些作⽤激活函数的主要作用是为神经网络引入非线性能力。如果网络中只有线性变换那么无论叠加多少层整体仍然可以表示为一个线性变换难以学习复杂的数据关系。激活函数不仅影响网络的表达能力也会影响梯度传播和训练效率。3、梯度消失现象是什么梯度消失是指在神经网络反向传播过程中梯度随着层数增加而逐渐变小甚至接近于 0的现象。在反向传播中梯度需要经过多层求导结果的连乘。如果激活函数的导数较小多个较小的数相乘后就可能使梯度非常微弱导致靠近输入端的网络层参数更新缓慢难以有效学习特征。4、神经网络是更宽好还是更深好神经网络的宽度指每层神经元的数量深度指网络的层数。更宽和更深各有优势不能简单地认为其中一种一定更好。更宽的网络单层可以容纳更多神经元增强特征表示能力但参数量和计算开销可能增加。更深的网络能够逐层组合特征从简单特征逐步学习更抽象、复杂的特征通常具有较强的特征表达能力但训练难度也可能更高。网络过深或过宽都可能增加计算成本和过拟合风险。因此应根据任务复杂度、训练数据量、计算资源和模型效果选择合适的深度与宽度而不是一味增加层数或神经元数量。5、为什么要使⽤Softmax?Softmax 常用于多分类神经网络的输出层主要作用是将模型输出的原始得分Logits转换为一个概率分布。转换为概率分布每个类别的输出都在 0 到 1 之间且所有类别的概率之和为 1。便于进行多分类预测通常选择概率最大的类别作为预测结果。配合交叉熵损失函数训练模型衡量预测结果与真实标签之间的差异引导模型调整参数。6、SGD 和 Adam 哪个更有效SGD 和 Adam 都是常见的梯度优化算法各有优缺点没有一种算法在所有任务中都一定更有效。比较方面SGDAdam基本原理根据梯度更新参数结合梯度的一阶矩和二阶矩估计自适应调整学习率学习率通常需要仔细调整每个参数具有自适应的更新步长收敛速度可能较慢对学习率较敏感通常前期收敛较快计算与存储开销相对较小需要额外存储梯度的统计量泛化表现在部分任务中可能获得更好的泛化效果训练初期通常较方便但最终泛化效果依赖任务和设置如果希望训练过程更方便、较快地取得初步结果Adam 通常是不错的选择。如果追求特定任务上的泛化性能并且能够调整学习率和训练策略SGD 也可能取得更好的结果。收获和体会通过本次实验我学习了 PyTorch 中张量的创建、索引、拼接、转置和矩阵乘法等基本操作也熟悉了神经网络从数据构建、模型定义、损失计算到反向传播训练的完整流程对神经网络的实际运行过程有了更具体的体会。在螺旋数据分类实验中线性模型的准确率只有 50% 左右而加入 ReLU 激活函数后分类效果显著提升。通过对比可以发现线性模型虽然结构简单但由于层与层之间没有非线性激活函数最终仍然等价于一个线性变换因此面对螺旋形这种复杂数据分布时准确率只能停留在较低水平。而在加入 ReLU 激活函数后模型的分类准确率明显提高决策边界也能够更好地适应数据的非线性结构。这让我深刻体会到激活函数在神经网络中的关键作用它不仅增强了模型的表达能力也使神经网络能够拟合更加复杂的函数关系。此外通过思考题我对 AlexNet、激活函数、梯度消失、网络宽度与深度、Softmax 以及 SGD 与 Adam 的区别有了更系统的认识。总的来说这次实验让我对神经网络的结构设计、优化方法及训练细节都有了更清晰的认识为后续更深入的深度学习课程与项目打下了坚实基础。