神经网络基础NN

1. 神经网络组成

1.1 神经网络的构成

神经网络是一种模仿生物神经网络结构和功能的计算模型,由多个神经元串联构成。每个神经元执行的操作可以简化为加权和加上一个激活函数(引入非线性)。神经网络主要由以下几部分构成:

  • 输入层:接收外部输入数据。
  • 输出层:产生网络的最终输出,通常是加权和的结果。
  • 隐藏层:包含加权和与激活函数,用于处理中间信息。

神经网络的特点包括:

  • 同一层内的神经元之间没有直接连接。
  • 在全连接神经网络中,第N层的每个神经元与第N-1层的所有神经元相连。
  • 第N-1层神经元的输出是第N层所有神经元的输入。
  • 每个连接都有一个权重值(w系数和b系数)。

在编程接口中,常用的API包括Linear(线性层)和FC(全连接层)。

import torch.nn as nn

# 定义线性层
linear_layer = nn.Linear(in_features, out_features)

# 定义全连接层
fc_layer = nn.Linear(in_features, out_features)

1.2 激活函数

激活函数的主要目的是向神经网络中添加非线性因素,使其能够拟合更复杂的场景,比如曲线和曲面。常见的激活函数有:

  • Sigmoid:适用于二分类问题的输出层,输出范围在[0,1]之间,可以解释为概率值。但由于梯度较小,容易导致梯度消失问题。
y = torch.sigmoid(x)
  • Tanh:通常在隐藏层中使用,输出范围[-1,1],关于0对称。相比Sigmoid,Tanh的导数更大,更新速度更快,迭代次数更少,但在x远离0点时,梯度可能为0,导致梯度消失。
y = torch.tanh(x)
  • ReLU:最常用于隐藏层,计算简单且能有效减少计算量。其输出为0时,可以防止过拟合;但如果大部分神经元输出为0,则可能导致“死亡”现象,此时可考虑使用Leaky ReLU等优化版本。
y = torch.relu(x)
  • Softmax:适用于多分类问题的输出层,将输出层的加权和转换为概率值,概率值之和为1,通常配合交叉熵损失函数使用。
y = torch.softmax(scores, dim=0)

选择激活函数时,对于隐藏层优先选择ReLU激活函数,如果效果不佳可尝试其他变体。对于输出层,根据任务类型选择相应的激活函数。

1.3 参数初始化

良好的参数初始化有助于加速训练过程并提高模型性能。权重的初始化方法包括均匀分布、标准正态分布、Kaiming(HE)初始化和Xavier(Glorot)初始化。偏置通常初始化为全0或全1,也可以设置为固定值。

import torch.nn.init as init

# 均匀分布
init.uniform_(linear_layer.weight)

# 标准正态分布
init.normal_(linear_layer.weight, mean=0, std=1)

# Kaiming (HE) 初始化
init.kaiming_uniform_(linear_layer.weight)
init.kaiming_normal_(linear_layer.weight)

# Xavier (Glorot) 初始化
init.xavier_uniform_(linear_layer.weight)
init.xavier_normal_(linear_layer.weight)

# 偏置初始化
init.zeros_(linear_layer.bias)
init.ones_(linear_layer.bias)

# 固定值初始化
init.constant_(linear_layer.weight, value)

1.4 神经网络搭建及参数计算

构建神经网络通常涉及创建一个继承自nn.Module的类,并实现__init__forward方法。__init__方法用于定义网络结构,包括全连接层和参数初始化;forward方法定义了前向传播过程,将所有层串联起来。可以通过torchsummary库来查看模型结构和计算模型参数量。

import torch.nn as nn
from torchsummary import summary

class MyNet(nn.Module):
    def __init__(self):
        super(MyNet, self).__init__()
        self.fc1 = nn.Linear(in_features, hidden_features)
        self.fc2 = nn.Linear(hidden_features, out_features)
        # 初始化参数
        init.kaiming_normal_(self.fc1.weight)
        init.zeros_(self.fc1.bias)
    
    def forward(self, x):
        x = torch.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# 创建模型实例
model = MyNet()

# 查看模型结构和参数量
summary(model, input_size=(1, in_features))

神经网络的优点在于精度高、性能优越,但也有黑箱性质、训练时间长、网络结构复杂等缺点。

2. 损失函数

损失函数用于衡量模型参数的质量,通过比较网络输出与真实输出的差异来评估模型性能。常见的损失函数包括:

  • 多分类:使用CrossEntropyLoss,结合了softmax和损失计算。
loss_fn = nn.CrossEntropyLoss()
my_loss = loss_fn(predictions, targets).detach().numpy()
  • 二分类:使用BCELoss(二分类交叉熵损失)。
loss_fn = nn.BCELoss()
my_loss = loss_fn(predictions, targets).detach().numpy()
  • 回归:使用L1Loss(MAE)、MSELoss(MSE)或SmoothL1Loss,这些损失函数分别针对不同的回归问题设计。
# MAE损失函数
loss_fn = nn.L1Loss()
my_loss = loss_fn(predictions, targets).detach().numpy()

# MSE损失函数
loss_fn = nn.MSELoss()
my_loss = loss_fn(predictions, targets).detach().numpy()

# SmoothL1损失函数
loss_fn = nn.SmoothL1Loss()
my_loss = loss_fn(predictions, targets).detach().numpy()

3. 网络优化方法

优化方法是训练神经网络的关键,主要包括:

  • 前向传播:数据从输入层逐层传递至输出层。
  • 反向传播:通过损失函数从后向前计算梯度,并更新参数。
  • 梯度下降:寻找损失函数最小化的方法。常用优化算法包括Momentum、AdaGrad、RMSprop和Adam等,它们通过不同的策略改善梯度下降过程。
import torch.optim as optim

# SGD优化器
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# AdaGrad优化器
optimizer = optim.Adagrad(model.parameters(), lr=0.01)

# RMSprop优化器
optimizer = optim.RMSprop(model.parameters(), lr=0.01, alpha=0.9)

# Adam优化器
optimizer = optim.Adam(model.parameters(), lr=0.01, betas=(0.9, 0.999))

此外,学习率衰减方法如等间隔衰减、指定间隔衰减和按指数衰减等,可以帮助模型在训练过程中更好地收敛。

from torch.optim.lr_scheduler import StepLR, MultiStepLR, ExponentialLR

# 等间隔学习率衰减
scheduler = StepLR(optimizer, step_size=50, gamma=0.1)

# 指定间隔学习率衰减
scheduler = MultiStepLR(optimizer, milestones=[50, 125, 160], gamma=0.1)

# 按指数学习率衰减
scheduler = ExponentialLR(optimizer, gamma=0.95)

4. 正则化方法

正则化技术主要用于缓解过拟合问题,常见的方法包括:

  • Dropout:随机失活一定比例的神经元,减少模型复杂度。
dropout = nn.Dropout(p=0.4)
y = dropout(y)
  • Batch Normalization:对数据进行标准化,然后通过缩放和平移重构数据,有助于加速训练过程。
bn = nn.BatchNorm1d(num_features)
y = bn(y)

5. 案例—手机价格分类

通过构建数据集、定义模型、训练模型和评估模型四个步骤完成手机价格分类任务。优化思路包括调整优化方法、学习率、数据预处理、网络深度、训练轮次和批处理大小等,以提升模型性能。

5.1 流程

  • 构建数据集
def create_dataset():
    # 创建数据集
    pass
  • 构建模型
class MobilePriceClassifier(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(MobilePriceClassifier, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.fc2 = nn.Linear(hidden_size, output_size)
        self.dropout = nn.Dropout(p=0.4)
        self.bn = nn.BatchNorm1d(hidden_size)
    
    def forward(self, x):
        x = self.fc1(x)
        x = self.bn(x)
        x = torch.relu(x)
        x = self.dropout(x)
        x = self.fc2(x)
        return x
  • 训练模型
def train_model(model, dataloader, criterion, optimizer, num_epochs=25):
    for epoch in range(num_epochs):
        for inputs, labels in dataloader:
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
  • 评估模型
def evaluate_model(model, dataloader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for inputs, labels in dataloader:
            outputs = model(inputs)
            _, predicted = torch.max(outputs.data, 1)
            total += labels.size(0)
            correct += (predicted == labels).sum().item()
    accuracy = correct / total
    return accuracy

5.2 优化思路

  1. 优化方法:将优化方法由SGD调整为Adam。
  2. 学习率:将学习率由1e-3调整为1e-4。
  3. 数据预处理:对数据进行标准化。
  4. 网络深度:增加网络深度,即增加网络参数量。
  5. 训练轮次:调整训练轮次。
  6. 批处理大小:设置batch_size为2的次方,如4、8、16、32、64、128等。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐