实验2:深度学习基础

2024年秋季《软件工程原理与实践》实验报告

一、实验内容

1.1 测试——运行基本的Python代码

基本数据类型

数字:整数和浮点数的工作方式与其他语言的一样:

布尔值: Python 实现了布尔逻辑的所有常用运算符,但使用英文单词而不是符号(&&||等):

字符串: Python 对字符串有很好的支持:

1.2 PyTorch 基础练习实验步骤

1. 什么是 PyTorch?

PyTorch是一个强大的Python库,专为深度学习和科学计算而设计。它提供了以下两大核心功能:

  1. GPU加速的张量计算:利用GPU进行高效的数值计算,极大地加速了模型训练和数据处理。
  2. 基于自动求导的深度神经网络构建:支持灵活构建和训练深度学习模型,使得研究者能够更轻松地进行实验和创新。
2. 定义数据

实验代码与结果
在这里插入图片描述
在这里插入图片描述

创建张量的多种方式
张量是PyTorch的基本数据结构,可以看作是一个多维数组。以下是不同维度张量的创建方法及其应用:

  • 标量张量

    x = torch.tensor(666)
    print(x)  # 输出: tensor(666)
    
    • 思考: 这个简单的标量张量演示了PyTorch张量的基本构造。从这个简单的构造中可以看到,PyTorch以直观且一致的方式处理数值,便于在更复杂的模型中快速应用。
  • 一维张量(向量)

    x = torch.tensor([1, 2, 3, 4, 5, 6])
    print(x)  # 输出: tensor([1, 2, 3, 4, 5, 6])
    
    • 思考: 向量在许多机器学习算法中非常常见,作为特征和样本的表示形式,它们的运算直接影响模型的表现。这让我思考,如何有效地利用向量化操作提升计算效率,尤其在处理大型数据集时,向量的操作无疑能显著提高性能。
  • 二维张量(矩阵)

    x = torch.ones(2, 3)
    print(x)  # 输出: tensor([[1., 1., 1.], [1., 1., 1.]])
    
    • 思考: 矩阵是神经网络中数据处理的基础,尤其在执行线性变换时,理解矩阵的性质至关重要。通过构建不同的矩阵,我们能够模拟神经元之间的权重连接,进而反思网络结构设计的重要性。
  • 高维张量

    x = torch.ones(2, 3, 4)
    print(x)  # 输出: 形状为 (2, 3, 4) 的张量
    
    • 思考: 高维张量在处理图像、视频等复杂数据时尤为重要。它们的灵活性让我意识到,随着数据维度的增加,所需的计算资源和算法复杂性也会相应提高,这促使我思考如何在高维空间中有效提取特征和信息。

创建不同类型的张量

# 创建空张量
x = torch.empty(5, 3)
print(x)  # 输出: 具有未定义值的张量

# 随机初始化张量
x = torch.rand(5, 3)
print(x)  # 输出: 随机数填充的张量

# 创建全0张量
x = torch.zeros(5, 3, dtype=torch.long)
print(x)  # 输出: 全为0的长整型张量
  • 思考: 使用不同的创建方法可以为不同的任务提供便利。例如,torch.empty虽然提供未定义值,但它可以用于节省内存,而torch.rand则适用于初始化神经网络权重。
3. 定义操作

实验代码与结果
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

基本运算
张量运算是PyTorch的核心。以下是一些常见的操作示例:

  • 矩阵与向量乘法

    m = torch.Tensor([[2, 5, 3, 7], [4, 2, 1, 9]])
    v = torch.arange(1, 5).float()
    result = m @ v
    print(result)  # 输出: tensor([49., 47.])
    
    • 思考: 矩阵乘法是深度学习中最基本的操作之一。尤其是在反向传播算法中,矩阵乘法的有效实现对梯度计算至关重要。
  • 矩阵转置与拼接

    print(m.t())  # 输出: 转置后的矩阵
    a = torch.Tensor([[1, 2, 3, 4]])
    b = torch.Tensor([[5, 6, 7, 8]])
    print(torch.cat((a, b), 0))  # 在Y方向拼接
    print(torch.cat((a, b), 1))  # 在X方向拼接
    

其他操作示例

# 创建并展示等间隔的数
linspace = torch.linspace(3, 8, 20)
print(linspace)

# 使用matplotlib绘制直方图
from matplotlib import pyplot as plt
plt.hist(torch.randn(1000).numpy(), 100)
plt.show()
  • 思考: torch.linspace在生成均匀分布的数值时非常有用,尤其是在模型训练中需要设定学习率或其他超参数时。通过数据可视化工具,如直方图,能够直观地理解数据分布,如实验中可以清楚看到当数据非常非常多的时候,正态分布会体现的非常明显。

1.3 螺旋数据分类实验步骤

实验代码与结果:

  1. 初始化重要参数
    在这里插入图片描述
    在这里插入图片描述

  2. 构建线性模型分类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 构建两层神经网络分类
    在这里插入图片描述
    在这里插入图片描述
实验目的

本实验旨在通过螺旋数据集分类,探索线性模型与神经网络模型的性能差异,深刻理解激活函数的作用。

1. 下载绘图函数

首先,需要下载绘图函数以便可视化数据和模型。

!wget https://raw.githubusercontent.com/Atcold/pytorch-Deep-Learning/master/res/plot_lib.py
2. 引入库与初始化参数
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   # 隐层单元数量
  • 解读: 初始化随机种子确保实验的可复现性。特征维度和类别数的选择直接影响后续模型的复杂性,使用GPU加速则能大幅提高计算效率。
3. 初始化样本数据
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)
    inner_var = torch.linspace((2 * math.pi / C) * c, (2 * math.pi / C) * (2 + c), N) + torch.randn(N) * 0.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)
  • 解读: 数据生成的方式使得样本呈现出螺旋形状。每个类别都有其独特的分布,这种结构使得简单的线性模型难以适应。通过可视化,我们能直观感受到数据的复杂性和分类的挑战。
4. 构建线性模型
learning_rate = 1e-3
lambda_l2 = 1e-5

model = nn.Sequential(
    nn.Linear(D, H),
    nn.Linear(H, C)
)
model.to(device)

criterion = torch.nn.CrossEntropyLoss()
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)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
  • 解读: 在这个线性模型中,准确率仅达50%。这表明线性模型对于螺旋数据的复杂结构缺乏足够的表达能力。损失函数与准确率的监控过程有助于理解模型的学习动态。

    在这里插入图片描述

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)

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()
  • 解读: 加入ReLU激活函数后,模型的准确率跃升至94.9%。这充分展示了非线性激活函数在捕捉复杂模式中的重要性。通过层间引入激活函数,模型可以更好地拟合数据分布,实现更高的分类效果。
    在这里插入图片描述
6. 可视化模型
print(model)
plot_model(X, Y, model)

二、问题总结与体会

2.1 实验中遇到的问题及解决方法

  1. 矩阵乘法类型不匹配
    在进行矩阵乘法时,发现torch.arange(1, 5)创建的张量为LongTensor类型,而参与计算的矩阵mFloatTensor类型。此类型不匹配导致无法进行有效的矩阵运算。为解决此问题,将张量v转换为浮点数类型是必要的,通过调用v = torch.arange(1, 5).float(),确保所有参与计算的张量具有相同的类型。
    在这里插入图片描述

  2. 绘图函数缺少依赖文件
    下载的绘图函数plot_lib.py未包含名为ziegler.png的图像文件,导致在绘图时出现错误。为解决此问题,需要修改plot_lib.py文件的第40行代码,使其正确读取图片:zieger = plt.imread('ziegler.png')。此外,还需将ziegler.png图片放置在与plot_lib.py同一文件夹内,以便绘图函数能够正确加载并使用该图像。

    在这里插入图片描述

    在这里插入图片描述

    2.2 问题总结与解答

    1. AlexNet有哪些特点?为什么可以⽐LeNet取得更好的性能?
      AlexNet引入了多个卷积层和ReLU激活函数,使用了较大的卷积核,并通过最大池化减少特征维度。此外,它采用了数据增强和Dropout技术来防止过拟合。这些设计使其能够有效捕捉特征并提高性能,相较于LeNet在处理复杂数据时具有更高的表达能力。

    2. 激活函数有哪些作⽤?
      激活函数引入非线性,使得神经网络能够学习和表达复杂的特征。常见的激活函数如ReLU、Sigmoid和Tanh,分别具有不同的优势,如ReLU在深度网络中缓解了梯度消失的问题。

    3. 梯度消失现象是什么?
      梯度消失是指在反向传播过程中,随着层数增加,梯度逐渐趋近于零,导致权重无法有效更新。这通常发生在使用Sigmoid或Tanh等饱和激活函数时。

    4. 神经网络是更宽好还是更深好?
      这取决于具体任务和数据。更深的网络可以捕捉更复杂的特征,而更宽的网络能够在每层中提取更多信息。现代研究表明,深度和宽度的结合往往能取得更好的效果。

    5. 为什么要使用Softmax?
      Softmax函数将模型输出的原始分数转换为概率分布,使得每个类别的预测值在0到1之间,并且所有预测值之和为1。这使得它适合于多类分类问题,便于进行模型评估和选择。

    6. SGD 和 Adam 哪个更有效?
      Adam优化器通常被认为比SGD更有效,因为它结合了动量和自适应学习率,有助于更快收敛,并且在处理稀疏数据时表现良好。然而,SGD在某些情况下也能提供更好的泛化能力,尤其是在大规模数据集上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值