在Pytorch-learn1中,我已经介绍了pytorch的简单操作,接下来让我们来构建简单的神经网络。
首先还是库的导入。这次我们除了torch和numpy,还需要导入可视化库来观察模型的预测结果,和数据处理计算库。
#-------库的导入------
import torch
import torch.nn as nn
import numpy as np
import torch.optim as optim#优化器
#数据处理与计算
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
#数据可视化
import matplotlib.pyplot as plt
#阻止一些无意义的警告
import warnings
warnings.filterwarnings('ignore')
导入成功库之后,我们需要对数据进行训练集和测试集的划分。在这里我们用自定义的数据划分库。
def split_dataset(datax,datay,shuffle,testsize):#划分训练集和测试集
if shuffle:#时序模型一般不shuffle
random_num = [i for i in range(len(datax))]
np.random.shuffle(random_num)
datax = datax[random_num]
datay = datay[random_num]
split_num = int(len(datax)*testsize)
train_x = datax[:split_num,:]
train_y = datay[:split_num,:]
testx = datax[split_num:,:]
testy = datay[split_num:,:]
return train_x, train_y, testx, testy
之后就是我们的数据集,其中x1和x2作为feature(输入特征),y_data作为label(参照)
# -------测试数据集--------
x1 = np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],[9.779], [6.182], [7.59],
[2.167], [7.042], [10.791], [5.313], [7.997], [3.1],[3.75],[3.25]], dtype=np.float32)
x2 = np.array([[3.1], [4.1], [5.53], [6.712], [6.937], [4.1682],[9.4], [6.18], [7.77],
[2.11], [6.88], [11.65], [5.22], [7.57], [3.35],[3.79],[3.225]], dtype=np.float32)
y_data = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573], [3.366], [2.596], [2.53],
[1.221], [2.827], [3.465], [1.65], [2.904], [1.3],[1.59],[1.56]], dtype=np.float32)
有了数据集,就要对数据进行合并于归一化处理。
x_data=np.stack([x1,x2],axis=1).squeeze()
##-----------归一化处理-----------
# 创建 MinMaxScaler 对象
x_scaler,y_scaler = MinMaxScaler(),MinMaxScaler()
x_data = x_scaler.fit_transform(x_data)
y_data=y_scaler.fit_transform(y_data)#可以用scaler.inverse_transform(y_data)反归一化
#-------划分训练集和测试集,要求输入x的第一维为特征数量--------
train_x, train_y, testx,testy = split_dataset(x_data,y_data,False,0.7)
接下来我们要运用多个激活函数构造神经网络哦,每个神经元都包含了一个激活函数。理论上来讲,只要激活函数的数量够多够复杂,这个函数集能模拟任意一种函数曲线。Pytorch为我们提供了各种激活函数,他们的基本图像如下。
接下来就是构建我们的神经网络。这里为了展示一下神经网络的堆叠使用了三层隐藏层。
#----------构建简单的神经网络---------
class Simple_Linear(nn.Module):
def __init__(self):
super(Simple_Linear, self).__init__()#获取输入
# Simple_Linear.__init__(self) # 用父类名称继承父类的init方法
self.fc1 = nn.Linear(2, 16) # 输入层到第一个隐藏层
self.fc2 = nn.Linear(16, 32) # 第一个隐藏层到第二个隐藏层
self.fc3 = nn.Linear(32, 16) # 第二个隐藏层到第三个隐藏层
self.fc4 = nn.Linear(16, 1) # 第三个隐藏层到输出层
# 记录隐藏层的数量及其架构
self.hidden_layers = [
('fc1', self.fc1),
('fc2', self.fc2),
('fc3', self.fc3),
]
def forward(self,x):#前向传播层
x=torch.relu(self.fc1(x))
x = torch.sigmoid(self.fc2(x))
x = torch.relu(self.fc3(x))
x=self.fc4(x)
return x
def print_hidden_layers(self):#模型内置结构
print("隐藏层数量:", len(self.hidden_layers))
print("隐藏层架构:")
for name, layer in self.hidden_layers:
print(f"{name}: Input size = {layer.in_features}, Output size = {layer.out_features}")
def __str__(self):
return f'{self.hidden_layers}'
接下来,如果电脑有GPU,就将模型转到GPU上训练
#-------转到GPU上训练---------
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(f"Device: {torch.cuda.get_device_name()}")
model=Simple_Linear().to(device)
print(model.print_hidden_layers())#查看模型内置参数
然后,我们对Hyperparameters(超参数)进行设置。这里使用了广泛使用的Adam优化器,相较于传统的梯度下降法,它是RMSProp和Momentum的集合方法,表现更优。
#-------定义超参数-------
learning_rate=0.5e-2#学习率
num_epochs=200#训练次数
# 定义损失函数和优化器
criterion = nn.MSELoss() # 均方误差损失函数用于回归
#beta1的值越小对小梯度变化越敏感
optimizer=optim.Adam(model.parameters(),lr=learning_rate,betas=(0.5, 0.999))
接下来就是训练模型了。
#-----------训练模型------------
for epoch in range(num_epochs):
inputs=torch.from_numpy(train_x).to(device)#输入
labels=torch.from_numpy(train_y).to(device)#参照
# 前向传播
outputs=model(inputs)
loss=criterion(outputs,labels)
model.train()#不加会影响到如 Dropout 和 BatchNorm
# 后向传播与优化
optimizer.zero_grad()#归零梯度,如果不归零,梯度值会不断累加
loss.backward()#进行反向传播,计算损失函数 loss 对模型参数的梯度
#前一步计算得到的梯度以及预定义的学习率,调整模型参数以最小化损失函数
optimizer.step()
if (epoch + 1) % 10 == 0:
model.eval()
with torch.no_grad():
test_x=torch.from_numpy(testx).to(device)
test_y = torch.from_numpy(testy).to(device)
output = model(test_x)
test_loss = criterion(output, test_y)
print('Epoch [{}/{}], Loss: {:.3f}'.format(epoch + 1, num_epochs, test_loss.item()))
对于训练好的模型,我们用测试集验证他的性能。记住,对前面归一化后的数据我们要进行反归一化。
#-----获得预测结果-----
xx_test = torch.from_numpy(testx).float().to(device)
pred = model(xx_test)
predicted=pred.cpu().detach().numpy()
#------反归一化------
predicted=y_scaler.inverse_transform(predicted)
testy=y_scaler.inverse_transform(testy)
#计算RMSE和R
rmse = np.sqrt(mean_squared_error(predicted, testy))
pearson_corr = np.corrcoef(np.array(predicted).squeeze(), np.array(testy).squeeze())[0, 1]
最后可视化我们模型在测试集上的表现结果
#--------可视化--------
plt.plot(testy, 'ro', label='Original data')
plt.plot(predicted, label='Fitted line')
plt.text(0.80, 0.85 ,f'Corr: {pearson_corr:.2f}', transform=plt.gca().transAxes, fontsize=10,c='k',
verticalalignment='top')
plt.text(0.80, 0.79 ,f'RMSE: {rmse:.2f}', transform=plt.gca().transAxes, fontsize=10,c='k',
verticalalignment='top')
plt.legend()
plt.show()
以上是构建神经网络的全过程,在数据量少的情况下,通常不建议使用过于复杂的神经网络,在数据具有时间序列等周期特征或数据量庞大时,可能要考虑更为复杂的模型。