pytorch-深度学习笔记总结

这是跟着李岩老师的课程做的笔记,自己学习交流为主

如果有错误,请忽视,反正我也懒得改

主打的就是一个随缘

有问题可以私信

作者神经衰弱的厉害,所以得等身体条件较好的情况下会统一回复

初版,后续如果有精力会精修

卷积神经网络应该会单开一个文章

等有时间了吧

全篇一万八千字,看的话还是需要有点耐心的

数据操作

在PyTorch中,数据操作是非常基础也非常重要的部分。主要涉及到PyTorch的张量(Tensor)操作,它类似于NumPy的多维数组,但还可以在GPU上运行以加速计算。

基础概念

张量(Tensor): 多维数组,可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)等等。

形状(Shape): 张量的维度,如4x3的矩阵,形状就是(4, 3)。

创建张量

在PyTorch中,你可以使用多种方法创建张量,比如直接从Python的列表创建,或者使用特定的函数。

import torch



# 从Python列表创建张量

x = torch.tensor([1, 2, 3])

print(x)



# 创建一个5x3的未初始化的张量

x = torch.empty(5, 3)

print(x)



# 创建一个5x3的随机初始化的张量

x = torch.rand(5, 3)

print(x)



# 创建一个5x3的零张量,数据类型为long

x = torch.zeros(5, 3, dtype=torch.long)

print(x)

张量操作

PyTorch提供了丰富的张量操作,比如索引、切片、数学运算等。

# 索引

y = x[1, :]

print(y)



# 改变形状,使用view

y = x.view(15)

z = x.view(-1, 5)  # -1表示该维度由其他维度确定

print(y.shape, z.shape)



# 张量加法

y = torch.rand(5, 3)

print(x + y)



# 原地改变(In-place)

y.add_(x)

print(y)

使用GPU

为了在GPU上运行你的张量,你需要先将它们转移至GPU内存。



# 判断CUDA是否可用

if torch.cuda.is_available():

    device = torch.device("cuda")          # CUDA设备对象

    y = torch.ones_like(x, device=device)  # 直接在GPU上创建张量

    x = x.to(device)                       # 或者使用.to("cuda")来移动张量

    z = x + y

    print(z)

    print(z.to("cpu", torch.double))       # 移回CPU并转换为double类型

线性代数

线性代数是机器学习的重要基础,涉及向量、矩阵等概念和运算。在PyTorch中,你可以方便地执行这些线性代数运算。

向量和矩阵

在PyTorch中,向量和矩阵都可以用张量来表示。

向量: 一维张量

矩阵: 二维张量

矩阵运算

矩阵运算是线性代数中的核心,包括矩阵乘法、转置、逆等。

# 创建矩阵

x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float)

y = torch.tensor([[5, 6], [7, 8]], dtype=torch.float)



# 矩阵乘法

z = torch.mm(x, y)

print("矩阵乘法:\n", z)



# 转置

t = x.t()

print("转置:\n", t)



# 计算逆(仅对方阵有效)

inv_x = x.inverse()

print("逆矩阵:\n", inv_x)

特殊矩阵

特殊矩阵,如单位矩阵和对角矩阵,也是线性代数中的重要概念。

# 单位矩阵

I = torch.eye(3)

print("单位矩阵:\n", I)



# 对角矩阵

D = torch.diag(torch.tensor([1, 2, 3]))

print("对角矩阵:\n", D)

范数

范数是衡量向量大小的一种方法,在机器学习中经常用到。

# L2 范数

l2_norm = torch.norm(x)

print("L2 范数:", l2_norm)



# L1 范数

l1_norm = torch.norm(x, p=1)

print("L1 范数:", l1_norm)

广播机制

在进行矩阵运算时,PyTorch可以自动扩展一维数组以匹配更高维的数组,这称为广播。

# 广播示例

a = torch.arange(3).reshape((3, 1))

b = torch.arange(2).reshape((1, 2))

print("a:\n", a)

print("b:\n", b)

print("广播运算 a + b:\n", a + b)

矩阵分解

矩阵分解是线性代数中的一个重要话题,如特征分解(Eigen decomposition)和奇异值分解(SVD)。

# 特征分解

eigenvalues, eigenvectors = torch.eig(x, eigenvectors=True)

print("特征值:\n", eigenvalues)

print("特征向量:\n", eigenvectors)



# 奇异值分解

U, S, V = torch.svd(x)

print("奇异值分解:\n", "U:\n", U, "\nS:\n", S, "\nV:\n", V)

解线性方程组

使用PyTorch解线性方程组是一种常见的操作,例如使用torch.solve。



# 解线性方程组 Ax = b

A = torch.tensor([[3, 1], [1, 2]], dtype=torch.float)

b = torch.tensor([9, 8], dtype=torch.float).unsqueeze(1)  # 转为列向量

x, LU = torch.solve(b, A)

print("解 x:\n", x)

参数的用法

在PyTorch的线性代数运算中,很多函数都有额外的参数选项,用于调整运算的行为。

dtype: 指定张量的数据类型,例如torch.float、torch.int等。

device: 指定张量所在的设备,如torch.device("cuda")。

requires_grad: 指定是否需要自动计算该张量的梯度,对于后续的自动微分非常重要。

# 创建具有特定数据类型和梯度要求的张量

x = torch.tensor([[1, 2], [3, 4]], dtype=torch.float, requires_grad=True)

print(x)

自动微分

在PyTorch中,自动微分是通过torch.autograd模块实现的。这个模块为张量上所有操作提供了自动微分的能力。它是一个运行时定义的框架,意味着反向传播是根据代码如何运行来确定的,每次迭代都可以不同。

关键概念

张量(Tensor): PyTorch中的主要数据结构,自动微分是通过对这些张量进行操作来实现的。

梯度(Gradient): 微分或导数的另一种说法,在优化问题中,我们通常需要计算函数的梯度。

计算图(Computation Graph): 是一个动态图,用于记录从输入张量到输出张量的操作序列。

启用梯度追踪

要在PyTorch中启用自动微分,你需要设置张量的requires_grad属性为True。

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
进行运算和梯度计算

在定义了梯度跟踪的张量之后,所有的操作都会被自动追踪。

y = x * x * 3

z = y.mean()

print(z)

现在,z是x的一个函数。我们可以通过调用.backward()来自动计算z关于x的梯度。

# 计算梯度

z.backward()



# 查看x的梯度

print(x.grad)
停止追踪梯度

在某些情况下,你可能不想追踪梯度。这可以通过.detach()方法或者使用with torch.no_grad():来实现。

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)

y = x * x

z = y.detach()  # z不再追踪梯度



# 或者

with torch.no_grad():

    y = x * x
梯度累积

PyTorch默认会累积梯度,这在训练神经网络时非常有用。如果你需要多次反向传播,梯度会累加在.grad属性中。

注意事项

只有浮点数和复数类型的张量可以要求梯度。

使用.backward()时,如果张量是标量,则不需要为backward()指定任何参数;如果它有更多的元素,则需要指定一个gradient参数,它是与张量形状相同的张量。

自动微分的深入讲解

1. 创建需要梯度的张量

当你创建一个张量并设置requires_grad=True时,PyTorch会开始追踪在这个张量上的所有操作。

# 创建一个张量,设置requires_grad=True来跟踪它的梯度

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)

requires_grad: 如果设置为True,PyTorch会记录所有在张量上的操作以便于后续的梯度计算。

2. 定义一个张量函数

接下来,你可以定义一个张量的函数。PyTorch会自动计算这个函数的梯度。

# 定义一个张量函数

y = x * x * 3

这里,y是关于x的函数。在这个例子中,y = 3x^2。

3. 计算梯度

利用.backward()方法,你可以让PyTorch计算梯度。如果y是标量(即单个数字),你可以直接调用y.backward()来计算梯度。

# 计算y关于x的梯度

y.backward(torch.tensor([1.0, 1.0, 1.0]))

.backward(): 这个方法会计算y的梯度。这里传入的torch.tensor([1.0, 1.0, 1.0])是梯度的权重,在大多数情况下,如果y是标量,你可以省略这个参数。

4. 查看梯度

计算完梯度后,你可以通过.grad属性来查看每个元素的梯度。

# 输出梯度

print(x.grad)

x.grad: 存储了x的梯度。在这个例子中,梯度dy/dx将会被存储在x.grad中。

5. 停止追踪梯度

在某些情况下,你不希望继续追踪计算的梯度,可以使用.detach()或with torch.no_grad():。

# 使用.detach()停止追踪梯度

y = x.detach()



# 使用with torch.no_grad():临时停止追踪梯度

with torch.no_grad():

    y = x * x

.detach(): 创建一个新的张量,与原张量共享数据但不追踪梯度。

with torch.no_grad(): 这个代码块内的所有计算都不会追踪梯度。

注意事项

计算梯度是自动进行的,但是梯度是累加的。这意味着每次进行反向传播时,梯度都会加到.grad属性中。因此,在进行新的梯度计算前,通常需要将梯度清零。

当处理非标量输出时(例如输出是向量或矩阵),需要传递一个与输出相同形状的gradient参数给.backward()。

线性回归从零开始实现

线性回归的目标是找到一组权重和偏置,使得模型的输出尽可能接近目标值。在一个简单的线性回归模型中,输出是输入特征的加权和,加上一个偏置项(也称为截距)。

1. 数据准备

首先,我们需要准备数据。在这个例子中,我们将创建一个简单的合成数据集。

# 导入PyTorch

import torch

import numpy as np



# 真实的权重和偏置

true_w = torch.tensor([2, -3.4])

true_b = 4.2



# 创建随机数据集

features = torch.randn(1000, 2)

labels = torch.matmul(features, true_w) + true_b

labels += torch.randn(labels.shape) * 0.01  # 添加噪声

features: 输入特征,这里是一个1000行2列的张量。

labels: 标签,根据我们的线性模型和一些随机噪声生成。

2. 数据迭代

在训练模型时,我们需要批量获取数据。以下是一个简单的数据迭代器。

def data_iter(batch_size, features, labels):

    num_examples = len(features)

    indices = list(range(num_examples))

    np.random.shuffle(indices)  # 打乱顺序

    for i in range(0, num_examples, batch_size):

        batch_indices = torch.tensor(

            indices[i: min(i + batch_size, num_examples)]

        )

        yield features[batch_indices], labels[batch_indices]

batch_size = 10

batch_size: 批量大小。

yield: Python的生成器,用于每次返回一批数据。

3. 初始化模型参数

我们随机初始化模型的权重和偏置。

w = torch.randn(2, 1, requires_grad=True)

b = torch.zeros(1, requires_grad=True)

w: 权重,需要梯度。

b: 偏置,需要梯度。

4. 定义模型

接下来,定义线性回归模型。

def linreg(X, w, b):

    return torch.mm(X, w) + b

linreg: 线性回归模型函数。

5. 定义损失函数

我们将使用均方误差作为损失函数。

def squared_loss(y_hat, y):

    return (y_hat - y.view(y_hat.shape)) ** 2 / 2

squared_loss: 均方误差损失函数。

6. 定义优化算法

这里我们使用小批量随机梯度下降(mini-batch stochastic gradient descent)。

def sgd(params, lr, batch_size):

    for param in params:

        param.data -= lr * param.grad / batch_size

sgd: 优化函数。

lr: 学习率。

param.data: 更新参数,不记录梯度操作。

7. 训练模型

在每个epoch中,我们将遍历所有的数据,并对每个小批量执行一次梯度下降。

# 训练参数

lr = 0.03           # 学习率

num_epochs = 3      # 迭代周期数

net = linreg        # 我们的线性回归模型

loss = squared_loss # 损失函数



# 训练过程

for epoch in range(num_epochs):

    for X, y in data_iter(batch_size, features, labels):

        l = loss(net(X, w, b), y).sum()  # 计算小批量的损失

        l.backward()                     # 计算梯度

        sgd([w, b], lr, batch_size)      # 使用小批量随机梯度下降进行优化



        # 清零梯度

        w.grad.data.zero_()

        b.grad.data.zero_()

    train_l = loss(net(features, w, b), labels)

    print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

l.backward(): 计算梯度。

sgd([w, b], lr, batch_size): 对模型的参数进行优化。

w.grad.data.zero_(), b.grad.data.zero_(): 清零梯度,这是必须的步骤,因为PyTorch会累加梯度。

在这个训练过程中,模型的参数(w和b)通过梯度下降不断更新,以最小化训练数据集上的损失函数。每个epoch结束后,我们打印出当前的损失,观察训练过程中损失的变化。

线性回归的简洁实现

我们将使用PyTorch的nn模块来定义模型,torchvision和DataLoader来处理数据,以及optim模块来进行优化。

1. 生成数据集

这一步与从零开始的实现相同,我们生成相同的数据集。

import torch

from torch.utils.data import TensorDataset, DataLoader



# 真实的权重和偏置

true_w = torch.tensor([2, -3.4])

true_b = 4.2



# 创建数据集

features = torch.randn(1000, 2)

labels = torch.matmul(features, true_w) + true_b

labels += torch.randn(labels.shape) * 0.01



# 将特征和标签组合

dataset = TensorDataset(features, labels)

2. 读取数据

使用DataLoader来批量读取数据。

batch_size = 10

data_iter = DataLoader(dataset, batch_size, shuffle=True)

3. 定义模型

使用nn.Module来定义线性模型。

from torch import nn



net = nn.Sequential(nn.Linear(2, 1))

nn.Linear: 定义线性层,输入特征维数为2,输出特征维数为1。

4. 初始化模型参数

初始化权重和偏置。

net[0].weight.data.normal_(0, 0.01)

net[0].bias.data.fill_(0)

5. 定义损失函数

使用PyTorch内置的均方误差损失函数。

loss = nn.MSELoss()

6. 定义优化算法

使用PyTorch内置的优化器,如SGD。

from torch.optim import SGD



optimizer = SGD(net.parameters(), lr=0.03)

SGD: 随机梯度下降优化器。

net.parameters(): 自动提取模型中的参数。

7. 训练模型

训练过程中,我们将使用优化器来更新模型的权重和偏置。

num_epochs = 3

for epoch in range(num_epochs):

    for X, y in data_iter:

        output = net(X)

        l = loss(output, y.view(-1, 1))

        optimizer.zero_grad()  # 梯度清零

        l.backward()           # 计算梯度

        optimizer.step()       # 更新参数

    print(f'epoch {epoch + 1}, loss: {l:f}')

optimizer.zero_grad(): 在每次迭代前清零梯度。

l.backward(): 计算梯度。

optimizer.step(): 更新权重和偏置。

Softmax回归从零实现

1. 获取和处理数据

首先,我们需要准备用于分类的数据集。这里我们仍然使用合成数据进行演示。

import torch

from torch.utils.data import TensorDataset, DataLoader



# 创建一个简单的合成数据集

features = torch.randn(1000, 2) # 特征维度为2

labels = torch.randint(0, 3, (1000,)) # 标签有3个类别



# 将特征和标签组合

dataset = TensorDataset(features, labels)

batch_size = 10

data_iter = DataLoader(dataset, batch_size, shuffle=True)

features和labels分别是特征和标签。

DataLoader用于批量获取数据。

2. 初始化模型参数

Softmax回归的模型输出是一个长度为类别数的向量,每个元素代表该类的预测概率。

num_inputs = 2  # 输入特征数量

num_outputs = 3 # 输出类别数量



W = torch.randn(num_inputs, num_outputs, requires_grad=True)

b = torch.zeros(num_outputs, requires_grad=True)

W和b是模型的权重和偏置。

3. 定义softmax操作

Softmax函数将输出转换为概率。

def softmax(X):

    X_exp = torch.exp(X)

    partition = X_exp.sum(dim=1, keepdim=True)

    return X_exp / partition  # 这里应用了广播机制

torch.exp:计算指数。

sum:沿特定维度求和。

4. 定义模型

定义softmax回归模型。

def net(X):

    return softmax(torch.mm(X, W) + b)

torch.mm:矩阵乘法。

5. 定义损失函数

交叉熵损失函数适合用于分类问题。

def cross_entropy(y_hat, y):

    return - torch.log(y_hat[range(len(y_hat)), y])

torch.log:计算对数。

y_hat[range(len(y_hat)), y]:选择正确类别的预测概率。

6. 定义优化算法

使用小批量随机梯度下降进行优化。

def sgd(params, lr, batch_size):

    for param in params:

        param.data -= lr * param.grad / batch_size

param.data:参数更新。

7. 训练模型

进行模型的训练。

lr = 0.1

num_epochs = 5



for epoch in range(num_epochs):

    for X, y in data_iter:

        y_hat = net(X)

        l = cross_entropy(y_hat, y).sum()

        l.backward()

        sgd([W, b], lr, batch_size)



        W.grad.data.zero_()

        b.grad.data.zero_()

    print(f'epoch {epoch + 1}, loss: {l:f}')

每个epoch遍历一次数据集,使用cross_entropy计算损失,然后更新参数。

Softmax回归的关键算法和方法解释

1. Softmax函数

def softmax(X):

    X_exp = torch.exp(X)

    partition = X_exp.sum(dim=1, keepdim=True)

    return X_exp / partition

作用:将模型的输出转换为概率分布。这是通过对每个输出应用指数函数,然后标准化确保概率之和为1来实现的。

参数:

X_exp: 对输入X的每个元素应用指数函数。

partition: 计算每行的总和,用于标准化。

dim=1: 指定在哪个维度上求和。

keepdim=True: 保持输出的维度与输入相同。

2. 模型定义

def net(X):

    return softmax(torch.mm(X, W) + b)

作用:定义softmax回归模型。它首先计算输入X和权重W的矩阵乘法,然后加上偏置b,最后应用softmax函数。

参数:

torch.mm(X, W): 计算矩阵X和W的乘积。

b: 偏置向量。

3. 交叉熵损失函数

def cross_entropy(y_hat, y):

    return - torch.log(y_hat[range(len(y_hat)), y])

作用:计算预测概率分布y_hat和真实标签y之间的交叉熵损失。这在分类问题中是常用的损失函数。

参数:

y_hat: 模型的预测输出。

y: 真实的标签。

torch.log: 对y_hat中的元素应用对数函数,以计算交叉熵。

4. 优化算法(小批量随机梯度下降)

def sgd(params, lr, batch_size):

    for param in params:

        param.data -= lr * param.grad / batch_size

作用:对模型参数进行优化。通过梯度方向和学习率来更新每个参数。

参数:

params: 需要更新的模型参数(例如,W和b)。

lr: 学习率,控制更新的步长。

param.grad: 存储的梯度。

batch_size: 批量大小,用于梯度的标准化。

5. 训练循环

训练循环中,我们遍历数据集,计算每个小批量的损失,然后更新模型参数。

l.backward(): 计算损失l的梯度。

sgd([W, b], lr, batch_size): 使用小批量随机梯度下降来更新权重和偏置。

W.grad.data.zero_(), b.grad.data.zero_(): 在每次梯度计算后清零梯度,防止梯度累加。

Softmax回归的简洁实现

1. 读取数据

使用PyTorch提供的DataLoader来批量读取数据。

from torch.utils.data import DataLoader



batch_size = 10

data_iter = DataLoader(dataset, batch_size, shuffle=True)

DataLoader:方便的数据加载工具,可以自动进行批量处理和数据打乱。

dataset:包含特征和标签的数据集。

batch_size:每个小批量的大小。

shuffle:在每个epoch开始时,是否打乱数据。

2. 定义模型

使用PyTorch的nn.Module定义模型。

from torch import nn



net = nn.Sequential(nn.Linear(2, 3), nn.Softmax(dim=1))

nn.Linear:定义一个线性层,其参数包括输入和输出的特征维度。

nn.Softmax:应用softmax函数。

dim=1:指定在哪个维度上应用softmax。

3. 初始化模型参数

使用PyTorch的内置函数初始化模型参数。

net[0].weight.data.normal_(0, 0.01)

net[0].bias.data.fill_(0)

normal_:用正态分布随机初始化权重。

fill_:将偏置初始化为0。

4. 定义损失函数

使用PyTorch的内置交叉熵损失函数。

loss = nn.CrossEntropyLoss()

CrossEntropyLoss:计算交叉熵损失,结合了softmax操作和交叉熵损失计算,适合多分类问题。

5. 定义优化算法

使用PyTorch的优化器,如SGD。

from torch.optim import SGD



optimizer = SGD(net.parameters(), lr=0.1)

SGD:随机梯度下降优化器。

net.parameters():自动获取模型的参数。

lr:学习率。

6. 训练模型

组织训练循环。

num_epochs = 5

for epoch in range(num_epochs):

    for X, y in data_iter:

        output = net(X)

        l = loss(output, y)

        optimizer.zero_grad()

        l.backward()

        optimizer.step()

    print(f'epoch {epoch + 1}, loss: {l:f}')

optimizer.zero_grad():在每次梯度计算前清零。

l.backward():计算损失梯度。

optimizer.step():进行一步参数更新。

多层感知机从零开始实现

1. 获取和处理数据

这一步与前面的类似,我们继续使用合成数据。

import torch

from torch.utils.data import DataLoader, TensorDataset



# 创建数据集

features = torch.randn(1000, 2)  # 特征维度为2

labels = (features[:, 0] ** 2 + features[:, 1] ** 2 > 1).long()  # 二分类问题



# 将特征和标签组合

dataset = TensorDataset(features, labels)

batch_size = 10

data_iter = DataLoader(dataset, batch_size, shuffle=True)

features和labels分别是特征和标签。

DataLoader用于批量读取数据。

2. 定义模型参数

为隐藏层和输出层定义权重和偏置。

num_inputs, num_outputs, num_hiddens = 2, 2, 256



W1 = torch.randn(num_inputs, num_hiddens, requires_grad=True) * 0.01

b1 = torch.zeros(num_hiddens, requires_grad=True)



W2 = torch.randn(num_hiddens, num_outputs, requires_grad=True) * 0.01

b2 = torch.zeros(num_outputs, requires_grad=True)



params = [W1, b1, W2, b2]

num_hiddens: 隐藏层的节点数。

W1, b1, W2, b2: 分别是两层的权重和偏置。

3. 定义激活函数

ReLU是一种常用的激活函数。


def relu(X):

    return torch.max(X, torch.tensor(0.0))

torch.max: 计算两个张量的逐元素最大值。

4. 定义模型

定义多层感知机模型。

def net(X):

    X = X.reshape((-1, num_inputs))

    H = relu(torch.matmul(X, W1) + b1)

    return torch.matmul(H, W2) + b2

torch.matmul: 矩阵乘法。

relu: 应用ReLU激活函数。

5. 定义损失函数

使用交叉熵损失函数。

loss = torch.nn.CrossEntropyLoss()

CrossEntropyLoss: 适合多分类问题的损失函数。

6. 定义优化算法

实现小批量随机梯度下降。

def sgd(params, lr, batch_size):

    with torch.no_grad():

        for param in params:

            param -= lr * param.grad / batch_size

            param.grad.zero_()

with torch.no_grad(): 不追踪梯度。

param.grad.zero_(): 清零梯度。

7. 训练模型

进行模型的训练。

lr, num_epochs = 0.1, 10

for epoch in range(num_epochs):

    for X, y in data_iter:

        l = loss(net(X), y)

        l.backward()

        sgd(params, lr, batch_size)

    print(f'epoch {epoch + 1}, loss: {l:f}')

l = loss(net(X), y): 计算损失。

l.backward(): 计算梯度。

sgd(params, lr, batch_size): 更新参数。

多层感知机的简洁实现

1. 读取数据

与之前相同,我们继续使用DataLoader来批量读取数据。

from torch.utils.data import DataLoader, TensorDataset



batch_size = 10

data_iter = DataLoader(dataset, batch_size, shuffle=True)

DataLoader:自动进行批量处理和数据打乱。

2. 定义模型

使用PyTorch的nn模块来定义模型。

from torch import nn



net = nn.Sequential(

    nn.Linear(2, 256),

    nn.ReLU(),

    nn.Linear(256, 2)

)

nn.Linear:定义线性层,包括输入和输出的特征维度。

nn.ReLU:非线性激活函数,用于引入非线性。

Sequential:将多个模块按顺序组合成一个大的模块。

3. 初始化模型参数

PyTorch提供了方便的方法来初始化模型参数。

def init_weights(m):

    if type(m) == nn.Linear:

        nn.init.normal_(m.weight, std=0.01)

net.apply(init_weights)

init.normal_:用正态分布初始化权重。

net.apply:将init_weights函数应用于模型的每一个层。

4. 定义损失函数

使用PyTorch内置的交叉熵损失函数。


loss = nn.CrossEntropyLoss()

CrossEntropyLoss:自动进行softmax运算并计算交叉熵。

5. 定义优化算法

使用PyTorch的优化器,如SGD。


from torch.optim import SGD



optimizer = SGD(net.parameters(), lr=0.1)

SGD:随机梯度下降优化器。

net.parameters():自动提取模型中的参数。

lr:学习率。

6. 训练模型

组织训练循环。

num_epochs = 10

for epoch in range(num_epochs):

    for X, y in data_iter:

        output = net(X)

        l = loss(output, y)

        optimizer.zero_grad()

        l.backward()

        optimizer.step()

    print(f'epoch {epoch + 1}, loss: {l:f}')

optimizer.zero_grad():在每次梯度计算前清零。

l.backward():计算损失梯度。

optimizer.step():进行一步参数更新。

过拟合与权重衰减(L2正则化)

1. 过拟合的概念

过拟合:模型在训练数据上表现得很好,但在未见过的数据上表现不佳。

原因:模型可能过于复杂,学习了训练数据中的噪声和细节,而不是潜在的数据分布。

2. 权重衰减(L2正则化)

权重衰减:在损失函数中加入一个正则项,惩罚模型的权重大小。

L2正则化:正则项是权重的平方和的乘积。

3. 实现权重衰减

在PyTorch中,可以在优化器中设置权重衰减,来实现L2正则化。


optimizer = torch.optim.SGD(net.parameters(), lr=0.1, weight_decay=0.01)

weight_decay:权重衰减系数,对应于L2正则化中的λ。

SGD:随机梯度下降优化器。

4. 训练过程

在训练过程中,正则化项会自动加入到损失中。

for epoch in range(num_epochs):

    for X, y in data_iter:

        output = net(X)

        l = loss(output, y) + weight_decay * l2_penalty(net.parameters())

        optimizer.zero_grad()

        l.backward()

        optimizer.step()

l2_penalty:自定义的函数,计算权重的L2范数。

weight_decay * l2_penalty(net.parameters()):计算正则化项。

5. L2范数函数

计算模型参数的L2范数。

def l2_penalty(params):

    l2_norm = sum(p.pow(2.0).sum() for p in params)

    return l2_norm

p.pow(2.0).sum():计算每个参数的平方和。

注意点

权重衰减是防止过拟合的一种有效方法,特别是在数据量较少的情况下。

过大的权重衰减可能导致欠拟合,因此需要仔细选择衰减系数。

暂退法

1. 暂退法的概念

暂退法(Dropout):在训练过程中随机将一部分神经元的输出变为0,这样可以减少对特定特征的依赖,增强模型的泛化能力。

原理:每次前向传播时,随机选择一些节点忽略,这使得网络不能依赖于任何一个节点,从而防止过拟合。

2. 在PyTorch中使用Dropout

PyTorch提供了nn.Dropout模块,使得在神经网络中应用dropout变得简单。

dropout = nn.Dropout(p=0.2)

p:dropout的概率,即每个神经元被丢弃的概率。

3. 将Dropout添加到模型中

在模型的某些层后面添加dropout层。

net = nn.Sequential(

    nn.Linear(2, 256),

    nn.ReLU(),

    nn.Dropout(0.2),

    nn.Linear(256, 2)

)

在ReLU激活函数之后添加了一个dropout层。

4. 训练与测试时的不同行为

Dropout在训练时和测试时的行为不同,在测试时应关闭dropout。

net.train()  # 启用dropout

# 训练过程...

net.eval()   # 关闭dropout

# 测试过程...

net.train():开启训练模式,启用dropout。

net.eval():开启评估模式,关闭dropout。

5. 训练过程

训练时使用dropout,测试时关闭。


for epoch in range(num_epochs):

    net.train()

    for X, y in data_iter:

        output = net(X)

        l = loss(output, y)

        optimizer.zero_grad()

        l.backward()

        optimizer.step()



    net.eval()

    # 测试模型性能...

训练过程中,net.train()确保dropout被激活。

测试时,net.eval()确保dropout被关闭。

注意点

Dropout通常在较复杂的网络中使用,特别是在有足够多数据的情况下。

Dropout的概率p是一个超参数,需要根据具体问题调整。

数值稳定性与参数初始化

1. 数值稳定性

数值稳定性:在神经网络的训练过程中,保证数值计算的稳定性,防止例如梯度消失或爆炸等问题。

常见做法:使用ReLU激活函数来避免梯度消失,梯度裁剪来避免梯度爆炸。

2. 参数初始化

正确的参数初始化可以帮助加速训练过程,增加收敛的可能性。

均匀或正态分布初始化:常用于简单网络结构。

Xavier/Glorot初始化:根据前一层的节点数自动调整权重的尺度,适用于深度网络。

He初始化:专为ReLU激活函数设计,考虑到ReLU的非线性特性。

3. 在PyTorch中进行参数初始化

PyTorch提供了多种初始化方法。

def init_weights(m):

    if type(m) == nn.Linear:

        nn.init.xavier_uniform_(m.weight)



net.apply(init_weights)

init.xavier_uniform_:使用Xavier均匀初始化方法。

net.apply:将初始化函数应用于每个层。

4. 梯度裁剪

在训练过程中,梯度裁剪可以防止梯度爆炸。

torch.nn.utils.clip_grad_norm_(net.parameters(), max_norm=1)

clip_grad_norm_:裁剪梯度,确保梯度的范数不超过max_norm。

max_norm:梯度的最大范数。

5. 选择合适的激活函数

选择合适的激活函数也是保证数值稳定性的一种方式。

ReLU激活函数:避免梯度消失,特别是在深层网络中。

注意点

参数的初始化方式和激活函数的选择会显著影响模型的学习效果。

对于不同类型的网络和不同的问题,可能需要采用不同的初始化策略。

模型搭建

在PyTorch中,有两种主要的方式来搭建模型:使用Sequential类和定义自己的Module子类。

1. 使用Sequential类

Sequential类允许我们以顺序方式搭建模型。它适用于那些简单的前馈网络。

net = nn.Sequential(

    nn.Linear(20, 256),

    nn.ReLU(),

    nn.Linear(256, 10)

)

优点:快速、直观。

限制:不够灵活,难以构建具有复杂拓扑结构的网络。

2. 定义自己的Module子类

更灵活的方法是创建nn.Module的子类,并定义自己的前向传播。

class CustomNet(nn.Module):

    def __init__(self):

        super(CustomNet, self).__init__()

        self.layer1 = nn.Linear(20, 256)

        self.layer2 = nn.Linear(256, 10)



    def forward(self, x):

        x = torch.relu(self.layer1(x))

        return self.layer2(x)



net = CustomNet()

优点:非常灵活,可以自定义复杂的网络结构。

注意:需要手动定义每层以及如何在forward方法中连接它们。

参数管理

在PyTorch中,模型的参数存储在nn.Module的子类中,并可以通过parameters或named_parameters方法访问。

查看参数

可以查看模型的所有参数,或特定层的参数。

# 查看所有参数

for param in net.parameters():

    print(param)



# 查看特定层的参数

for name, param in net.named_parameters():

    if 'layer1' in name:

        print(name, param.size())

parameters():返回模型所有参数的迭代器。

named_parameters():返回每个参数的名称和参数本身。

初始化参数

可以对模型的参数进行自定义初始化。

for name, param in net.named_parameters():

    if 'weight' in name:

        nn.init.xavier_uniform_(param)

使用init模块中的函数对特定的参数进行初始化。

共享参数

在多个层之间共享参数。

shared = nn.Linear(256, 10)

net = nn.Sequential(

    nn.Linear(20, 256),

    nn.ReLU(),

    shared,

    nn.ReLU(),

    shared

)

shared层在网络中被多次使用,其参数在这些层之间共享。

自定义层

在PyTorch中,你可以通过扩展nn.Module来自定义层,从而创建符合特定需求的神经网络结构。

1. 定义一个自定义层

class CustomLayer(nn.Module):

    def __init__(self, size, activation_fn=None):

        super(CustomLayer, self).__init__()

        self.weight = nn.Parameter(torch.randn(size))

        self.activation_fn = activation_fn



    def forward(self, x):

        x = torch.matmul(x, self.weight)

        if self.activation_fn is not None:

            x = self.activation_fn(x)

        return x

nn.Parameter:用于定义参数,这些参数会自动被识别为模型的可训练参数。

activation_fn:可以为自定义层指定一个激活函数。

2. 使用自定义层

custom_layer = CustomLayer(size=(20, 10), activation_fn=torch.relu)

output = custom_layer(input_data)

CustomLayer(size=(20, 10), activation_fn=torch.relu):创建一个具有指定大小和激活函数的自定义层。

文件读写操作

在PyTorch中,可以使用torch.save和torch.load来保存和加载模型。

1. 保存模型

torch.save(net.state_dict(), 'model.pth')

net.state_dict():获取模型的状态(参数)。

'model.pth':文件路径。

2. 加载模型

net.load_state_dict(torch.load('model.pth'))

torch.load('model.pth'):从文件中加载状态字典。

使用GPU计算

使用GPU进行计算可以显著提高神经网络训练的速度。

1. 检查GPU可用性

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

torch.device("cuda"):指定使用GPU。

torch.cuda.is_available():检查CUDA(GPU)是否可用。

2. 将模型和数据移动到GPU

net.to(device)

input_data = input_data.to(device)

net.to(device):将模型移动到指定设备(GPU或CPU)。

input_data.to(device):将数据移动到指定设备。

调整参数的技巧

学习率(Learning Rate):通常是调整的第一个参数。如果训练不稳定,尝试降低学习率;如果训练速度太慢,可以尝试增加学习率。

批量大小(Batch Size):较大的批量大小可以提高训练稳定性,但会增加内存使用。需要根据GPU的内存容量来调整。

优化器(Optimizer):不同的优化器(如Adam, SGD)可能会影响模型的训练速度和最终性能。根据具体问题选择合适的优化器。

正则化(Regularization):如L2正则化或Dropout可以帮助减少过拟合。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值