赞
踩
原文链接:https://xiets.blog.csdn.net/article/details/131711757
版权声明:原创文章禁止转载
专栏目录:PyTorch 专栏(总目录)
PyTorch 相关网站:
线性回归 和 非线性回归 的输出都是连续的。逻辑回归 与 线性回归/非线性回归 区别在于 逻辑回归 的输出是 二元离散 的,即输出特征只有两种结果。因此逻辑回归通常用于二元分类问题,即把一堆样本分为两类。为了方便数学表达和计算,一般把逻辑回归分类的两类输出结果分别记做整数 0 和 1。
逻辑回归的输出是两种类别,记作 0 和 1,可以用普通网络层组合一个激活函数,让每个样本输出两个 0.0 ~ 1.0 之间的值分别表示两种类别的概率(概率之和为 1),概率大的就作为样本的预测类别。
sigmoid()
表示 S型函数,公式为 sigmoid(x) = 1 / (1 + e^-x)
,函数值域为 (0, 1)
,即将一组变量映射到 0.0 ~ 1.0 之间的一个数值(一组变量对应的输出的和等于 1)。sigmoid()
函数常用于二分类问题。
分类问题的损失函数一般使用 CrossEntropyLoss(input, target)
交叉熵损失函数,函数的 input 参数一般是二维矩阵,每一行表示预测样本对应的各类别的概率值,target 参数是样本的真实类别值(形状必须是 0D 或 1D)。
随机生成一堆不同类别的坐标点作为数据样本,每个样本有两个输入特征(X/Y坐标点)和 两个输出类别标签(0 或 1),使用 PyTorch 实现 逻辑回归 示例:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn, optim
class LogisticRegression(nn.Module):
"""
逻辑回归神经网络模型
"""
def __init__(self, *, in_features: int, out_features: int):
super().__init__()
# 只使用一层线型模型
self.linear = nn.Linear(in_features, out_features)
def forward(self, inputs: torch.Tensor) -> torch.Tensor:
"""
前向传播 (预测输出)
"""
# 经过线型模型前向传播
x = self.linear(inputs)
# 线型模型的输出应用激活函数, 输出张量的形状为 (inputs.shape[0], out_features), 是一个二维矩阵,
# 每一行表示一个样本的输出, 每个样本输出 out_features 个值在 (0.0, 1.0) 之间的参数, 分别表示 out_features 个标签类别对应的概率。
outputs = torch.sigmoid(x)
return outputs
def main():
#
# 生成样本
#
# 样本总数的一半
half_samples = 100
# 每个样本有 2 个输入特征, cluster 的形状为 (100, 2)
cluster = torch.ones((half_samples, 2))
# 生成具有指定均值和标准差的一组数据 (批量样本的输入特征), data0 的形状为 (100, 2), 表示 100 个 二维坐标点 (100 个样本的输入特征)
data0 = torch.normal(-4 * cluster, 3)
# data0 样本批数据对应的输出标签类别为 0, label0 的形状为 (100, 1), 表示 100 个样本的输出标签类别
label0 = torch.zeros((half_samples, 1))
# 同样的方法生成第二批样本, 该批样本是输出标签类别为 1
data1 = torch.normal(4 * cluster, 3)
label1 = torch.ones((half_samples, 1))
# 合并两批样本, 合并后 inputs 的形状为 (200, 2), targets 的形状为 (200, 1),
# 表示有 200 个样本, 每个样本有 2 个输入特征, 1 个输出特征(0/1标签类别)
inputs = torch.cat((data0, data1), dim=0).type(torch.FloatTensor)
targets = torch.cat((label0, label1), dim=0).type(torch.LongTensor)
#
# 绘制样本
#
# 把样本数据绘制为散点图
x = inputs.data.numpy()[:, 0] # 第 1 个输入特征作为 X 轴, 形状为 (200,)
y = inputs.data.numpy()[:, 1] # 第 2 个输入特征作为 Y 轴, 形状为 (200,)
labels = targets.data.numpy()[:, 0] # 输出标签类别, 形状为 (200,)
# 绘制散点图, 在 (x, y) 坐标处绘制圆点, 样本坐标对应的标签类别用颜色来体现。
# s=16 表示圆点的大小的 2 次方 (s = point_size ** 2)
# c=labels 表示圆点颜色值, 这里把标签类别(0/1)传给 c (也可以使用固定的 RGB 值)
# cmap="bwr" 颜色值映射, 颜色值 c 这里传的是标签类别值, cmap 负责把 c (标签类别值) 映射为具体的 RGB 颜色值, 这里 0映射为蓝色, 1映射为红色
plt.scatter(x, y, s=16, c=labels, cmap="bwr")
# cmap 可取值参考: https://matplotlib.org/2.0.2/examples/color/colormaps_reference.html
#
# 创建/训练网络模型
#
# 创建 神经网络模型, 样本输入特征数为 2, 输出特征数为 2 (因为样本有 2 个输出标签类别, 每个标签类别输出为一个概率, 所以输出特征数为 2)
model = LogisticRegression(in_features=2, out_features=2)
# 创建 优化器, 使用随机梯度下降法, 学习率为 0.02
optimizer = optim.SGD(model.parameters(), lr=0.03)
# 创建 损失函数, 分类问题一般使用 交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 总的迭代次数
epochs = 100
# CrossEntropyLoss 交叉熵函数的 目标张量 只支持 0D 或 1D, 转换后的形状为 (200,)
targets = targets.reshape(-1)
# 训练模型
for epoch in range(epochs):
# 1. 前向传播 (预测输出)
outputs = model(inputs)
# 2. 计算损失值, outputs 的形状为 (200, 2), targets 形状为 (200,)
loss = criterion(outputs, targets)
# 3. 梯度清零 (清空 model 参数的梯度值, 即 grad 属性, 不清空会累积)
optimizer.zero_grad()
# 4.反向传播 (计算梯度, 计算 model 参数的梯度值)
loss.backward()
# 5. 更新模型参数 (根据 model 参数的梯度值 更新 参数值)
optimizer.step()
#
# 输出准确度
#
# 每隔 10 次 或 最后一次 输出准确度
if (epoch % 10 == 0) or (epoch == epochs - 1):
# outputs 的形状为 (200, 2), 沿 dim=1 轴计算最大值 (计算每一行的最大值),
# 返回一个元祖 tuple(max_values_tensor, max_values_indexes_tensor), 元祖元素形状为 (200,)
# max_values_tensor 和 max_values_indexes_tensor 的每一个元素表示原矩阵每一行的最大值的 值 和 所在行的列索引
max_tensors = torch.max(outputs, dim=1)
# output_labels 张量表示原矩阵每一行的最大值的所在行的列索引, 形状为 (200,)
# 如果第 0 列比较大, 则值为 0, 即这一行样本预测输出的标签类别为0
# 如果第 1 列比较大, 则值为 1, 即这一行样本预测输出的标签类别为1
output_labels = max_tensors[1]
# 和真实输出标签对比, 计算出预测准确的样本数量。
# 下面 == 两边的值均为相同形状的 ndarray, == 的计算结果为 bool 类型的矩阵, 结果为 True 表示预测准确, False 表示预测错误,
# 用于数值计算时 True == 1, False == 0, bool矩阵的元素值累加和即为 True 的数量。
accurate_count = np.sum(output_labels.data.numpy() == targets.data.numpy())
# 计算准确率
accuracy = accurate_count / (2 * half_samples)
print(f"Epoch[{epoch:02d}/{epochs - 1}]: loss={loss}, accuracy={accuracy}")
plt.show()
if __name__ == "__main__":
main()
训练输出:
Epoch[00/99]: loss=0.9946394562721252, accuracy=0.05
Epoch[10/99]: loss=0.9072323441505432, accuracy=0.075
Epoch[20/99]: loss=0.7913260459899902, accuracy=0.19
Epoch[30/99]: loss=0.6698043346405029, accuracy=0.725
Epoch[40/99]: loss=0.5794425010681152, accuracy=0.935
Epoch[50/99]: loss=0.519798755645752, accuracy=0.95
Epoch[60/99]: loss=0.48007088899612427, accuracy=0.965
Epoch[70/99]: loss=0.4532660245895386, accuracy=0.965
Epoch[80/99]: loss=0.43472540378570557, accuracy=0.965
Epoch[90/99]: loss=0.421417236328125, accuracy=0.965
Epoch[99/99]: loss=0.41236305236816406, accuracy=0.965
样本数据集可视化:
逻辑回归是二元分类,属于多元分类的一种特殊情况。多元分类与二元分类类似,区别在于使用 softmax
函数替代 sigmoid
函数作为激活函数。如果分类的类别数为 n,则 softmax
函数接收 n 个输入,然后输出 n 个概率(概率之和为 1),概率最大的类别就是预测的类别。
多元分类问题的损失函数一般也是使用 CrossEntropyLoss(input, target)
交叉熵损失函数。
随机生成三组不同类别的坐标点作为数据样本,每个样本有两个输入特征(X/Y坐标点)和 三个输出类别标签(0、1、2),使用 PyTorch 实现 多元分类 示例:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Tuple
import matplotlib.pyplot as plt
import numpy as np
import torch
import torch.nn.functional as F
from torch import nn, optim
class ClassifierNet(nn.Module):
"""
多元分类神经网络模型
"""
def __init__(self, *, in_features: int, hidden_features: int, out_features: int):
super().__init__()
# 隐含层
self.hidden = nn.Linear(in_features, hidden_features)
# 输出层
self.out = nn.Linear(hidden_features, out_features)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
前向传播 (预测输出)
"""
# 输入层样本特征 输入 隐含层, 隐含层输出应用 ReLU 激活函数 (增加拟合非线性模型的能力)
x = F.relu(self.hidden(x))
# 输出层输出应用 softmax 激活函数 (把各类别输出值映射为对应的概率)
x = F.softmax(self.out(x))
return x
def generate_samples() -> Tuple[np.ndarray, np.ndarray]:
"""
生成样本, 样本有 2 个输入特征, 输出 3 种可可能的类别 (分别记作: 0, 1, 2)
"""
# 一种类别的数量
cluster_size = 100
# 每个样本有 2 个输入特征, cluster 的形状为 (cluster_size, 2)
cluster = torch.ones((cluster_size, 2))
# 生成具有指定均值和标准差的一组数据 (批量样本的输入特征), data0 的形状为 (cluster_size, 2), 表示 cluster_size 个 二维坐标点 (cluster_size 个样本的输入特征)
data0 = torch.normal(-4 * cluster, 3)
# data0 样本批数据对应的输出标签类别为 0, label0 的形状为 (cluster_size, 1), 表示 cluster_size 个样本的输出标签类别
label0 = torch.zeros((cluster_size, 1))
# 同样的方法生成第二批样本, 该批样本是输出标签类别为 1
data1 = torch.normal(4 * cluster, 3)
label1 = torch.ones((cluster_size, 1))
# 同样的方法生成第三批样本, 该批样本是输出标签类别为 2
data2 = torch.normal(12 * cluster, 3)
label2 = label1 * 2
# 合并三批样本, 合并后 inputs 的形状为 (3*cluster_size, 2), outputs 的形状为 (3*cluster_size, 1),
# 表示有 3*cluster_size 个样本, 每个样本有 2 个输入特征, 1 个输出特征(0/1标签类别)
inputs_2d = torch.cat((data0, data1, data2), dim=0).type(torch.FloatTensor)
targets_2d = torch.cat((label0, label1, label2), dim=0).type(torch.LongTensor)
return inputs_2d, targets_2d
def main():
# 生成样本
inputs, targets = generate_samples()
# 绘制样本: 把样本数据绘制为散点图
x_1d = inputs.data.numpy()[:, 0] # 第 1 个输入特征作为 X 轴
y_1d = inputs.data.numpy()[:, 1] # 第 2 个输入特征作为 Y 轴
labels_1d = targets.data.numpy()[:, 0] # 输出标签类别
# 绘制散点图, 在 (x, y) 坐标处绘制圆点, 样本坐标对应的标签类别用颜色来体现。
plt.scatter(x_1d, y_1d, s=20, c=labels_1d, cmap="tab10")
# 创建 网络模型, out_features 表示输出类别可能的数量
model = ClassifierNet(in_features=inputs.shape[1], hidden_features=20, out_features=3)
# 创建 优化器
optimizer = optim.SGD(model.parameters(), lr=0.02)
# 创建 损失函数, 分类问题一般使用 交叉熵损失函数
criterion = nn.CrossEntropyLoss()
# 总的迭代次数
epochs = 1000
# CrossEntropyLoss 交叉熵函数的 目标张量 只支持 0D 或 1D, 转换后的形状为 1D
targets = targets.reshape(-1)
# 训练模型
for epoch in range(epochs):
# 1. 前向传播 (预测输出)
outputs = model(inputs)
# 2. 计算损失值, outputs 的形状为 (samples_count, 3), targets 形状为 (samples_count,)
loss = criterion(outputs, targets)
# 3. 梯度清零 (清空 model 参数的梯度值, 即 grad 属性, 不清空会累积)
optimizer.zero_grad()
# 4.反向传播 (计算梯度, 计算 model 参数的梯度值)
loss.backward()
# 5. 更新模型参数 (根据 model 参数的梯度值 更新 参数值)
optimizer.step()
# 输出准确度: 每隔一定次数 或 最后一次 输出准确度
if (epoch % 100 == 0) or (epoch == epochs - 1):
# outputs 的形状为 (samples_count, 3), 沿 dim=1 轴计算最大值 (计算每一行的最大值),
# 返回一个元祖 tuple(max_values_tensor, max_values_indexes_tensor), 元祖元素形状为 (samples_count,)
# max_values_tensor 和 max_values_indexes_tensor 的每一个元素表示原矩阵每一行的最大值的 值 和 所在行的列索引
max_tensors = torch.max(outputs, dim=1)
# output_labels 张量表示原矩阵每一行的最大值的所在行的列索引, 形状为 (samples_count,)
# 如果第 0 列比较大, 则值为 0, 即这一行样本预测输出的标签类别为0
# 如果第 1 列比较大, 则值为 1, 即这一行样本预测输出的标签类别为1
# 如果第 2 列比较大, 则值为 2, 即这一行样本预测输出的标签类别为2
output_labels = max_tensors[1]
# 和真实输出标签对比, 计算出预测准确的样本数量
accurate_count = np.sum(output_labels.data.numpy() == targets.data.numpy())
# 计算准确率
accuracy = accurate_count / output_labels.shape[0]
print(f"Epoch[{epoch:03d}/{epochs - 1}]: loss={loss}, accuracy={accuracy:.3f}")
plt.show()
if __name__ == "__main__":
main()
训练输出:
Epoch[000/999]: loss=1.335349678993225, accuracy=0.240
Epoch[100/999]: loss=0.860129177570343, accuracy=0.653
Epoch[200/999]: loss=0.7869724631309509, accuracy=0.730
Epoch[300/999]: loss=0.7562829852104187, accuracy=0.797
Epoch[400/999]: loss=0.7306234240531921, accuracy=0.840
Epoch[500/999]: loss=0.7079392075538635, accuracy=0.903
Epoch[600/999]: loss=0.6910192370414734, accuracy=0.923
Epoch[700/999]: loss=0.6785814166069031, accuracy=0.920
Epoch[800/999]: loss=0.668964684009552, accuracy=0.930
Epoch[900/999]: loss=0.6612473130226135, accuracy=0.933
Epoch[999/999]: loss=0.6548553109169006, accuracy=0.943
样本数据集可视化:
Python 机器学习库 scikit-learn
的 sklearn.datasets 模块中内置了一些可用于分类和回归算法练习的数据集。用其中的鸢尾花(Iris)数据来做 PyTorch 神经网络模型分类问题的示例,即根据鸢尾花的 萼片长度、萼片宽度 和 花瓣长度、花瓣宽度 四个特征预测品种类别。
鸢尾花数据集:
PyTorch 实现鸢尾花分类示例:
import torch
import torch.nn.functional as F
from sklearn import datasets
from sklearn import preprocessing
from torch import nn
from torch import optim
class IrisClassifierNet(nn.Module):
"""
神经网络模型
"""
def __init__(self, *, in_features: int, hidden_features: int, out_features: int, dtype: torch.dtype):
super().__init__()
# 隐含层
self.hidden = nn.Linear(in_features=in_features, out_features=hidden_features, dtype=dtype)
# 输出层
self.out = nn.Linear(in_features=hidden_features, out_features=out_features, dtype=dtype)
def forward(self, inputs: torch.Tensor) -> torch.Tensor:
"""
前向传播 (预测输出)
"""
x = self.hidden(inputs) # 数据传输到隐含层
outputs = self.out(F.relu(x)) # 应用 ReLU 激活函数, 增加非线性拟合能力, 然后传输到输出层
return F.softmax(outputs, dim=1) # 把输出应用 softmax 激活函数 (把各类别输出值映射为对应的概率)
def main():
# 加载鸢尾花数据集, 获取样本的 输入 和 输出
iris_dataset = datasets.load_iris()
x_2d, y_1d = iris_dataset.data, iris_dataset.target
print(x_2d.shape) # (150, 4) 一共150个样本, 每个样本4个特征
print(y_1d.shape) # (150,) 样本对应的输出类别, 元素值 0、1 或 2
# 对输入特种数据做标准化处理(缩放为 单位方差, 0均值 的标准数据)
scaler = preprocessing.StandardScaler()
x_2d = scaler.fit_transform(x_2d)
# 样本数据转换为 Tensor
inputs = torch.tensor(x_2d, dtype=torch.float32)
# 交叉熵损失函数的第二个参数(即输出的目标类别标签targets)的类型必须是 int64 类型, 且形状必须为 0D 或 1D
targets = torch.tensor(y_1d, dtype=torch.int64)
print(inputs.dtype, inputs.shape) # torch.float32 torch.Size([150, 4])
print(targets.dtype, targets.shape) # torch.int64 torch.Size([150])
# 创建 网络模型, 4个输入特征, 3个输出类别 (模型的 dtype 必须和输入输出数据的 dtype 相同)
model = IrisClassifierNet(in_features=4, hidden_features=3, out_features=3, dtype=torch.float32)
# 创建 优化器
optimizer = optim.SGD(model.parameters(), lr=0.03)
# 创建 损失函数
criterion = nn.CrossEntropyLoss()
epochs = 2000
for epoch in range(epochs):
# 1. 前向传播, 预测输出
outputs = model(inputs)
# 2. 计算损失值
loss = criterion(outputs, targets)
# 3. 梯度清零
optimizer.zero_grad()
# 4. 误差反向传播, 计算梯度并累加
loss.backward()
# 5. 更新模型参数
optimizer.step()
# 每隔一定次数输出准确率
if (epoch % 200 == 0) or (epoch == epochs - 1):
# 预测的输出标签类别 (每行取概率最大的索引为标签类别值)
output_labels = torch.max(outputs, dim=1)[1]
# 预测准确的数量 (相同位置的类别值相等则为True, 不能则为False, True是1, False是0, 全部结果相加就是正确预测的数量)
accurate_count = torch.sum(output_labels == targets)
# 计算准确率
accuracy = accurate_count / output_labels.shape[0]
print(f"Epoch[{epoch:04d}/{epochs - 1}]: loss={loss}, accuracy={accuracy:.3f}")
if __name__ == "__main__":
main()
结果输出:
(150, 4)
(150,)
torch.float32 torch.Size([150, 4])
torch.int64 torch.Size([150])
Epoch[0000/1999]: loss=1.0580378770828247, accuracy=0.580
Epoch[0200/1999]: loss=0.9028461575508118, accuracy=0.667
Epoch[0400/1999]: loss=0.8457337617874146, accuracy=0.667
Epoch[0600/1999]: loss=0.8079221844673157, accuracy=0.667
Epoch[0800/1999]: loss=0.7771845459938049, accuracy=0.820
Epoch[1000/1999]: loss=0.7471984028816223, accuracy=0.853
Epoch[1200/1999]: loss=0.719592273235321, accuracy=0.893
Epoch[1400/1999]: loss=0.6952674388885498, accuracy=0.920
Epoch[1600/1999]: loss=0.6743661165237427, accuracy=0.947
Epoch[1800/1999]: loss=0.6568384170532227, accuracy=0.967
Epoch[1999/1999]: loss=0.6427242159843445, accuracy=0.980
经过 2000 次训练,最终准确率已达到 98%。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。