# 用GPU存储张量
import torch
import torch.nn as nn
from matplotlib import pyplot as plt
# 默认的张量存储在CPU上
ts1 = torch.rand(3, 4)
print(ts1)
# 把张量移动到GPU上
ts2 = ts1.to('cuda:0')
print(ts2)
# 以上操作可以把数据集搬到GPU上,但是神经网络模型也要搬到GPU上才可正常运行
# 搭建一个神经网络的类
class DNN(torch.nn.Module):
def __init__(self):
super(DNN, self).__init__()
# 根据神经网络的类创建一个网络,并把这个网络搬到GPU上
model = DNN().to('cuda:0')
# 神经网络分为几步:划分数据集、训练网络、测试网络、使用网络
# 划分数据集
# 数据集里每个样本必须包含输入与输出,将数据集按一定的比例划分为训练集与测试集,分别用于训练网络与测试网络
# 将输入层称为第0层
# 因为Python列表、NumPy数组以及PyTorch张量都是从索引[0]开始
# 再加之输入层没有内部参数(权重w和偏置b)
# 训练网络
# 神经网络的训练过程,就是经过很多次前向传播与反向传播的轮回,最终不断调整其内部参数(权重w和偏置b),以拟合任意复杂函数的过程。
# 内部参数称为参数;外部参数称为超参数
# 外部参数:网络的层数、每个隐藏层的节点数、每个节点的激活函数类型、学习率、轮回次数、每次轮回的样本数
# 前向传播
# 反向传播
# batch_size
# epochs
# 测试网络
# 使用网络
# DNN的实现
# 制作数据集
# 这里生成10000个样本,设定3个输入特征与3个输出特征,其中
# 每个输入特征相互独立,均服从均匀分布
# 当(x1+x2+x3)<1时,Y1为1,否则Y1为0
# 当1<(x1+x2+x3)<2,Y2为1,否则Y2为0
# 当(x1+x2+x3)>2,Y3为1,否则Y3为0
# .float()将布尔型张量转化为浮点型张量
# torch.rand(*sizes)
# 生成一个张量,其元素从[0,1)的均匀分布中随机抽取
# 生成的张量的每个元素都是在0(包含)到1(不包含)之间的随机数
x1 = torch.rand(10000, 1) # 输入特征1
x2 = torch.rand(10000, 1) # 输入特征2
x3 = torch.rand(10000, 1) # 输入特征3
Y1 = ((x1 + x2 + x3) < 1).float() # 输出特征1
Y2 = ((1 < (x1 + x2 + x3)) & ((x1 + x2 + x3) < 2)).float() # 输出特征2
Y3 = ((x1 + x2 + x3) > 2).float() # 输出特征3
Data = torch.cat([x1, x2, x3, Y1, Y2, Y3], axis=1) # 整合数据集
Data = Data.to('cuda:0') # 把数据集搬到GPU上
print(Data.shape)
# 划分训练集和测试集 训练集和测试集7:3
# 训练集的样本数量
train_size = int(len(Data) * 0.7)
# 测试集的样本数量
test_size = len(Data) - train_size
# 打乱样本的顺序
Data = Data[torch.randperm(Data.size(0)), :]
# 训练集样本
train_data = Data[:train_size, :]
# 测试集样本
test_data = Data[train_size:, :]
print(train_data.shape)
print(test_data.shape)
# 搭建神经网络
# 1.要继承父类 nn.Module
# 2.要有__init__方法和forward方法
# 3.不需要反向传播方法
class DNN(nn.Module):
def __init__(self):
"搭建神经网络的各层"
super(DNN, self).__init__()
self.net = nn.Sequential(
# 第一层:全连接层 输入3输出5
nn.Linear(3, 5),
nn.ReLU(),
# 第二层:全连接层 输入5输出5
nn.Linear(5, 5),
nn.ReLU(),
# 第三层:全连接层 输入5输出5
nn.Linear(5, 5),
nn.ReLU(),
# 第四层:全连接层 输入5输出3
nn.Linear(5, 3)
)
def forward(self, x):
'前向传播'
y = self.net(x)
return y
# 创建子类的实例,并搬到GPU上
model = DNN().to('cuda:0')
print(model)
# 网络的内部参数
# 神经网络的内部参数是权重与偏置
# 内部参数在神经网络训练之前会被赋予随机数,随着训练的进行,内部参数会逐渐迭代至最佳值
# 可以查看内部参数
for name, param in model.named_parameters():
print(f"参数:{name}\n形状:{param.shape}\n数值:{param}")
# 网络的外部参数
# 外部参数即超参数
# 搭建网络时的超参数:网络的层数、各隐藏层节点数、各节点激活函数、内部参数的初始值
# 训练网络的超参数:如损失函数、学习率、优化算法、batch_size、epochs
# 激活函数
# 损失函数
loss_fn = nn.MSELoss()
# 学习率与优化算法
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
# 训练网络
epochs = 1000
losses = [] # 记录损失函数变化的列表
# 给训练集划分输入与输出
# 前3列为输入特征
X = train_data[:, :3]
# 后3列为输出特征
Y = train_data[:, -3:]
for epoch in range(epochs):
# 一次前向传播
Pred = model(X)
# 计算损失函数
loss = loss_fn(Pred, Y)
# 记录损失函数的变化
losses.append(loss.item()) # .item()方法可将Pytorch张量退化为普通元素
# 清理上一轮滞留的梯度
optimizer.zero_grad()
# 一次反向传播
loss.backward()
# 优化内部参数
optimizer.step()
Fig = plt.figure()
plt.plot(range(epochs), losses)
plt.ylabel('loss')
plt.xlabel('epoch')
plt.show()
# 测试网络
# 测试时,只需要让测试集进行1次前向传播即可,不需要计算梯度
# 局部关闭梯度的操作,with torch.no_grad():
# 给测试集划分为输入与输出
# 前3列为输入特征
X = test_data[:, :3]
# 后3列为输出特征
Y = test_data[:, -3:]
# 局部关闭梯度计算功能
with torch.no_grad():
# 一次前向传播
Pred = model(X)
# 对于每一行,找到这一行中最大的那个元素设置为1
Pred[:, torch.argmax(Pred, dim=1)] = 1
# 不是1的元素设置为0
Pred[Pred != 1] = 0
# 预测正确的样本
correct = torch.sum((Pred == Y).all(1)) # all(1)按行扫描
# 全部的样本数量
total = Y.size(0) # size(0)是按列扫描
# 打印预测的准确率
print(f"测试集精准度:{100 * correct / total}%") # *100是按百分制计算准确率
# 保存与导入网络
# (1)保存网络
# torch.save(模型名,'文件名.pth')
torch.save(model, 'DNNmodel.pth') # 保存网络
# (2)导入网络
# 新网络=torch.load('文件名.pth')
new_model = torch.load('DNNmodel.pth') # 把模型赋给新网络
# (3)用新模型测试网络
# 给测试集划分为输入与输出
# 前3列为输入特征
X = test_data[:, :3]
# 后3列为输出特征
Y = test_data[:, -3:]
# 局部关闭梯度计算功能
with torch.no_grad():
# 一次前向传播
Pred = new_model(X)
# 对于每一行,找到这一行中最大的那个元素设置为1
Pred[:, torch.argmax(Pred, dim=1)] = 1
# 不是1的元素设置为0
Pred[Pred != 1] = 0
# 预测正确的样本 # all(1)按行扫描
correct = torch.sum((Pred == Y).all(1))
# 全部的样本数量 # size(0)是按列扫描
total = Y.size(0)
# 打印预测的准确率 # *100是按百分制计算准确率
print(f"测试集精准度:{100 * correct / total}%")