网络模型
定义一个两层网络
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
# 定义神经网络模型
class Net(nn.Module):
def __init__(self, init_x=0.0):
super().__init__()
self.fc1 = nn.Linear(1, 10)
self.fc2 = nn.Linear(10, 1)
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
return x
# 初始化模型
model = Net()
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
# 生成一些示例数据
x_train = torch.tensor([[1.0], [2.0], [3.0], [4.0]], dtype=torch.float32)
y_train = torch.tensor([[2.0], [4.0], [6.0], [8.0]], dtype=torch.float32)
# 训练模型
num_epochs = 1000
for epoch in range(num_epochs):
# 清零梯度
optimizer.zero_grad()
# 前向计算
outputs = model(x_train)
loss = criterion(outputs, y_train)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
if (epoch + 1) % 100 == 0:
print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')
# 保存模型
torch.save(model.state_dict(), 'model.pth')
# 加载模型
loaded_model = Net()
loaded_model.load_state_dict(torch.load('model.pth'))
loaded_model.eval() # 将模型设置为评估模式
# 输入新数据进行预测
new_input = torch.tensor([[5.0]], dtype=torch.float32)
with torch.no_grad():
prediction = loaded_model(new_input)
print(f"输入 {new_input.item()} 的预测结果: {prediction.item()}")
运行结果
训练好的参数值:
参数名: fc1.weight, 参数值: tensor([[ 0.5051],
[ 0.2675],
[ 0.4080],
[ 0.3069],
[ 0.9132],
[ 0.2250],
[-0.2428],
[ 0.4821],
[ 0.0998],
[ 0.6737]])
参数名: fc1.bias, 参数值: tensor([ 0.5201, -0.0252, 0.0504, 0.6593, -0.4250, 0.6001, 0.9645, -0.2310,
-0.2038, 0.2116])
参数名: fc2.weight, 参数值: tensor([[ 0.5492, 0.2550, 0.3046, 0.3183, 0.8147, 0.3062, -0.4165, 0.2969,
0.0482, 0.5535]])
参数名: fc2.bias, 参数值: tensor([0.0147])
fc1
层:fc1.weight
:这是输入层到隐藏层的权重矩阵,其形状为(10, 1)
,意味着输入层有 1 个神经元,隐藏层有 10 个神经元。矩阵中的每个元素代表从输入神经元到对应隐藏层神经元的连接权重。fc1.bias
:这是隐藏层每个神经元的偏置项,形状为(10,)
,也就是每个隐藏层神经元都有一个对应的偏置值。
fc2
层:fc2.weight
:这是隐藏层到输出层的权重矩阵,形状为(1, 10)
,表明隐藏层有 10 个神经元,输出层有 1 个神经元。矩阵中的每个元素代表从隐藏层神经元到输出层神经元的连接权重。fc2.bias
:这是输出层神经元的偏置项,形状为(1,)
,即输出层只有一个神经元,所以只有一个偏置值。
不同的优化器
激活函数解析
激活函数的作用
激活函数赋予神经网络非线性映射能力,使其能够更好地处理复杂的现实世界数据2。常见的激活函数包括ReLU、PReLU等。激活函数通常用于卷积层和全连接层,以增加模型的表达能力。
常见的激活函数
Sigmoid 函数
- 公式:
- 特点:输出范围在
(0, 1)
之间,能够把输入映射为概率值,常用于二分类问题。不过它存在梯度消失问题,当输入值非常大或者非常小时,梯度会趋近于 0。
import torch
import torch.nn.functional as F
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
sigmoid_output = torch.sigmoid(x)
print("Sigmoid 输出:", sigmoid_output)
Tanh 函数
- 公式:\(\tanh(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}\)
- 特点:输出范围在
(-1, 1)
之间,零中心化,相较于 Sigmoid 函数,梯度消失问题有所缓解,但仍然存在。
import torch
import torch.nn.functional as F
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
tanh_output = torch.tanh(x)
print("Tanh 输出:", tanh_output)
ReLU 函数
- 公式:\(ReLU(x)=\max(0, x)\)
- 特点:计算简单,能够有效缓解梯度消失问题,在深度学习中被广泛使用。不过它存在死亡 ReLU 问题,即某些神经元可能永远不会被激活。
import torch
import torch.nn.functional as F
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
relu_output = F.relu(x)
print("ReLU 输出:", relu_output)
Leaky ReLU 函数
- 公式:\(LeakyReLU(x)=\begin{cases}x, & x\geq0 \\ \alpha x, & x < 0\end{cases}\),其中 \(\alpha\) 是一个小的常数,例如 0.01。
- 特点:解决了死亡 ReLU 问题,当输入为负数时,也会有一个小的梯度。
import torch
import torch.nn.functional as F
x = torch.tensor([-2.0, -1.0, 0.0, 1.0, 2.0])
leaky_relu_output = F.leaky_relu(x, negative_slope=0.01)
print("Leaky ReLU 输出:", leaky_relu_output)
损失函数解析
# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)
该程序使用了MSELoss损失函数和SGD优化器
全部损失函数总类有
__all__ = [ "L1Loss", "NLLLoss", "NLLLoss2d", "PoissonNLLLoss", "GaussianNLLLoss", "KLDivLoss", "MSELoss", "BCELoss", "BCEWithLogitsLoss", "HingeEmbeddingLoss", "MultiLabelMarginLoss", "SmoothL1Loss", "HuberLoss", "SoftMarginLoss", "CrossEntropyLoss", "MultiLabelSoftMarginLoss", "CosineEmbeddingLoss", "MarginRankingLoss", "MultiMarginLoss", "TripletMarginLoss", "TripletMarginWithDistanceLoss", "CTCLoss", ]
L1Loss
:计算输入和目标之间的平均绝对误差(MAE),即loss = 1/n * sum(|input - target|)
。NLLLoss
:负对数似然损失,常用于分类任务,通常在模型输出经过log_softmax
变换后使用。NLLLoss2d
:二维的负对数似然损失,适用于图像等二维数据的分类任务。PoissonNLLLoss
:泊松负对数似然损失,适用于泊松分布的数据,常用于计数数据的回归。GaussianNLLLoss
:高斯负对数似然损失,假设数据服从高斯分布,用于回归任务。KLDivLoss
:Kullback-Leibler 散度损失,用于衡量两个概率分布之间的差异。MSELoss
:均方误差损失,计算输入和目标之间的平均平方误差,即loss = 1/n * sum((input - target) ** 2)
,常用于回归任务。BCELoss
:二元交叉熵损失,用于二分类任务,输入和目标都应该是概率值(在 0 到 1 之间)。BCEWithLogitsLoss
:将Sigmoid
函数和BCELoss
结合在一起,适用于输入是未经过激活函数的原始输出(logits)的情况。HingeEmbeddingLoss
:用于度量两个输入样本之间的相似性,常用于度量学习任务。MultiLabelMarginLoss
:多标签分类的边缘损失,适用于一个样本可能属于多个类别的情况。SmoothL1Loss
:平滑的 L1 损失,在 L1 损失的基础上进行了平滑处理,在某些情况下比 L1 和 L2 损失表现更好。HuberLoss
:也称为平滑 L1 损失,结合了 L1 和 L2 损失的优点,对离群点更鲁棒。SoftMarginLoss
:用于二分类的软边缘损失,允许一些样本在边缘内。CrossEntropyLoss
:交叉熵损失,通常是log_softmax
和NLLLoss
的组合,常用于多分类任务。MultiLabelSoftMarginLoss
:多标签软边缘损失,适用于多标签分类问题,每个标签都有一个独立的分类器。CosineEmbeddingLoss
:基于余弦相似度的嵌入损失,用于度量两个输入样本之间的余弦相似度,常用于度量学习。MarginRankingLoss
:边缘排序损失,用于比较两个输入样本的得分,常用于排序任务。MultiMarginLoss
:多边缘损失,用于多分类任务,基于每个类别的边缘来计算损失。TripletMarginLoss
:三元组边缘损失,常用于度量学习,通过比较三元组(锚点、正样本、负样本)之间的距离来学习嵌入。TripletMarginWithDistanceLoss
:结合了距离度量的三元组边缘损失,在TripletMarginLoss
的基础上增加了距离度量的计算。CTCLoss
:连接主义时间分类损失,常用于处理序列到序列的问题,如语音识别和手写文字识别等,不需要对齐输入和输出序列。
可视化模型
Graphviz
安装时候选择添加path到环境变量
输入
dot -version
显示下面说明安装成功
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchviz import make_dot
# 定义神经网络模型
class Net(nn.Module):
def __init__(self, init_x=0.0):
super().__init__()
self.fc1 = nn.Linear(1, 10)
self.fc2 = nn.Linear(10, 1)
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
return x
# 初始化模型
model = Net()
# 生成一个示例输入
x = torch.randn(1, 1)
# 前向传播
y = model(x)
# 绘制计算图
dot = make_dot(y, params=dict(model.named_parameters()))
dot.render('net_model_structure', format='png', cleanup=True)
Tensorboard
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.tensorboard import SummaryWriter
# 定义神经网络模型
class Net(nn.Module):
def __init__(self, init_x=0.0):
super().__init__()
self.fc1 = nn.Linear(1, 10)
self.fc2 = nn.Linear(10, 1)
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
return x
# 初始化模型
model = Net()
# 初始化 SummaryWriter
writer = SummaryWriter('file/net_model')
# 生成一个示例输入
x = torch.randn(1, 1)
# 将模型结构写入 TensorBoard
writer.add_graph(model, x)
# 关闭 writer
writer.close()
进入file文件夹
tensorboard --logdir="./net_model"