此为2022年第一学期Python与机器学习大作业所使用模型,在此将个人的作业成果分享给大家!源码可见文章底部!!
模型介绍
对于混凝土强度的预测我在这里选择了全连接神经网络进行预测。此模型的核心思想在于构建一个神经网络,也就是添加多层然后每一层都设置多个的神经元。这些神经元会接收上一层神经元的信号,并利用自身的一些机制与权重处理信号并传给下一神经元。因此我们最主要的就是要确定权重的大小,于是就利用利用梯度下降的方法让最后的结果均逼近于真实以确定权重。在模型中会利用到一些激活函数来消弱线性性质。这里利用的是全连接神经网络,也就是说下一层的每一个节点均会于上一层的全部节点相连。
我们使用的全连接神经网络拥有四层,第一层为输入层拥有8个节点,第二层和第三层均为隐层各拥有16个节点,第四层为输出层有1个节点。其中间的激活函数为RELU函数,其结构图如下,
对于我们本次设计的模型,其程序的编写我们令其进行了实例化,所以整个模型均为一个类,此类中包含了 数据预处理
,模型训练
,模型预测
,作图
,这四个模块。具体代码可见代码文件。下图为程序的整体结构图。
-
其中数据处理模块是用于划分数据集以及对数据进行标准化处理的。
-
而模型训练则是调用Pytorch来训练神经网络,其最后会返回一个模型对象。训练模块会在定义对象时自动调用,所以对象一定义模型就训练完成了。
-
而模型预测集则是利用训练完成的模型对象对一些测试集或者其他数据进行预测,同时其会自动计算R2值与均方差。
-
而作图模块是用于绘制迭代图以及准确图所使用的。
编程详解
-
导入必要库.
import torch import numpy as np import torch.nn as nn from torch.autograd import Variable import pandas as pd from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score from tqdm import tqdm from sklearn.preprocessing import scale as sc from sklearn.preprocessing import MinMaxScaler from matplotlib import pyplot as plt
-
定义类以及初始化数据
class Concrete_predict_withANN: def __init__(self, path='concrete_data_prepared.csv', label='strength', scale=0, batch_size=128, hidden_dim=8, lr=0.01, iteration_num=100, opt=0): self.path = path self.label = label self.batch_size = batch_size self.scale = scale self.hidden_dim = hidden_dim self.lr = lr #学习率 self.iteration_num = iteration_num #迭代次数 self.opt = opt #优化器选择 默认为adam #若为1则选择sgd #若为2则选择nadam #对象定义的同时进行训练 self.model, self.loss_list, self.dataset = self.con_tr() #返回训练好的模型,以及损失记录,以及测试集(用于测试)
-
数据预处理模块
#数据预处理 def pre_data(self): # 注意此处类型转化为float,不然后面求导会报错 train = pd.read_csv(self.path, dtype=np.float32) # 获取x,y y = train.loc[:, train.columns == self.label].values x = train.loc[:, train.columns != self.label].values #如果scale为0,则不数据标准化 if self.scale == 0: x_data = x #如果scale为1,则用minmax标准化 elif self.scale == 1: transfer = MinMaxScaler(feature_range=(0, 1)) # 处理后的范围 x_data = transfer.fit_transform(x) # 对data进行处理 elif self.scale == 2: x_data = sc(X=x, with_mean=True, with_std=True, copy=True) x_train, x_test, y_train, y_test = train_test_split(x_data, y, test_size=0.2, random_state=2019) #转化为torch形式 x_train = torch.from_numpy(x_train) y_train = torch.from_numpy(y_train) x_test = torch.from_numpy(x_test) y_test = torch.from_numpy(y_test) train = torch.utils.data.TensorDataset(x_train, y_train) test = torch.utils.data.TensorDataset(x_test, y_test) ''' DataLoader用于随机播放和批量处理数据。 它可用于与多处理工作程序并行加载数据 在dataset基础上多了batch_size, shuffle等操作 ''' train_loader = torch.utils.data.DataLoader(train, batch_size=self.batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader(test, batch_size=self.batch_size, shuffle=True) # 返回loader return train_loader, x.shape[1], {"train":[x_train,y_train], "test":[x_test,y_test]} #此函数为一个整体的函数用于训练
-
模型训练模块
def con_tr(self): train_loader, features, dataset = self.pre_data() # ANN全连接层神经网络 class ANNModel(nn.Module): def __init__(self, input_dim, hidden_dim, output_dim): super(ANNModel, self).__init__() # 定义层 self.fc1 = nn.Linear(input_dim, hidden_dim) self.relu1 = nn.ReLU() # nn.Linear为线性关系,加上激活函数转为非线性 self.fc2 = nn.Linear(hidden_dim, hidden_dim) self.relu2 = nn.ReLU() self.fc3 = nn.Linear(hidden_dim, output_dim) def forward(self, x): out = self.fc1(x) out = self.relu1(out) out = self.fc2(out) out = self.relu2(out) out = self.fc3(out) return out input_dim = features # 输入层神经元个数 hidden_dim = self.hidden_dim # hidden layer 神经元个数 output_dim = 1 # 输出层神经元个数 # 定义神经网络 model = ANNModel(input_dim, hidden_dim, output_dim) # 用均方差作为损失函数 MSELoss = nn.MSELoss() # 用于记录每次epcoh的损失值 loss_list = [] # 学习率 learning_rate = self.lr # 优化器选择 if self.opt == 0: optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) elif self.opt == 1: optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) elif self.opt == 2: optimizer = torch.optim.ASGD(model.parameters(), lr=learning_rate) iteration_num = self.iteration_num # 迭代次数 # tqdm用于显示进度条 for epoch in tqdm(range(iteration_num)): for fea, strength in train_loader: # 将其转化为变量 train = Variable(fea) # 标签为强度值 labels = Variable(strength) optimizer.zero_grad() outputs = model(train) loss = MSELoss(outputs, labels) loss.backward() optimizer.step() # 记录每次迭代的损失值 loss_list.append(loss.item()) #返回回模型与其训练过程的损失 return model, loss_list, dataset
-
预测模块
#测试函数 def predict(self, data=0, evaluate_result=True): if data == 0: data = self.dataset['test'] elif data == 1: data = self.dataset['train'] X_test = Variable(data[0]) y_test = Variable(data[1]) y_pred = self.model(X_test) MSELoss = nn.MSELoss() R2 = r2_score(y_pred.detach().numpy(), y_test.numpy()) loss = MSELoss(y_pred, y_test) if evaluate_result: print("MSE均方差: ", loss) print("R2值: ", R2) return y_pred, R2, loss
-
绘图模块
def draw(self, type): plt.rcParams['font.sans-serif'] = ['FangSong'] #迭代图 if type == 'iter': plt.xlabel('迭代次数', fontsize=15) plt.ylabel('损失值', fontsize=15) plt.title("迭代次数与损失值关系图", fontsize=15, loc='center', color='black') plt.plot([i for i in range(self.iteration_num)], self.loss_list, label="LOSS") elif type == 'accuracy': y_test_pred, R2_test, loss_test = self.predict(evaluate_result=False) y_train_pred, R2_train, loss_train = self.predict(data=1, evaluate_result=False) #添加绘制overall的 y_pred = torch.cat((y_train_pred ,y_test_pred)) y = torch.cat((self.dataset['train'][1],self.dataset['test'][1])) R2_overall = r2_score(y_pred.detach().numpy(), y.numpy()) plt.axis([0, 80, 0, 80]) # 设置轴的范围 plt.scatter(y_test_pred.detach().numpy(), self.dataset['test'][1].numpy(), s=25, c='green', marker='*', label='测试') plt.scatter(y_train_pred.detach().numpy(), self.dataset['train'][1].numpy(), s=25, c='red', marker='+', label='训练') plt.legend(loc="upper left", fontsize=10) #画直线 plt.plot([0,80],[0,80], c='blueviolet', linestyle='dashed') plt.xlabel('预测值', fontsize=15) plt.ylabel('真实值', fontsize=15) plt.title("预测值与真实值比较图", fontsize=15, loc='center', color='black') plt.text(70, 5, 'R2_overall={:.2f}\nR2_test={:.2f}\nR2_train={:.2f}\nMSE_test={:.2f}\nMSE_train={:.2f}'. format(R2_overall,R2_test,R2_train,loss_test,loss_train), ha='center', fontsize=10, c='black') plt.show() plt.close()
-
定义对象进行测试
test = Concrete_predict_withANN(iteration_num=1000, lr=0.01, scale=2, hidden_dim=300) test.draw('iter') test.draw('accuracy')
模型运行效果
参数的设置
对于神经网络的参数设置有很多种选择,例如隐含层层数、隐含层神经元个数、激活函数、优化器、迭代次数、损失函数、学习率等等,除了神经网络的一些参数设置外,还可以设置数据预处理即数据标准化的方法入minmax法、zscore法等等
在此模型中,下面为固定的值,
模型参数 | 值/类型 |
---|---|
隐含层层数 | 2 |
激活函数 | relu函数 |
损失函数 | MSE均方差 |
以下为可选值,
模型参数 | 值/类型 |
---|---|
标准化方法 | 无/minmax/zscore |
神经元个数 | 默认为8 |
优化器 | Adagrad/Adam/Nadam |
迭代次数 | 默认为1000 |
学习率 | 默认为0.01 |
模型可视化结果
以下为迭代次数为1000次、神经元为16个、优化器为Adam、学习率为0.01、标准化方法为Zscore的可视化结果.
-
预测值与真实值的比较图
从图中可以知道,测试集的R2为0.90,训练集的R2为0.95,其均方差对于测试集为29.17,训练集为11.87。那么可知此模型的预测效果是比较好的,从图中也可以看出无论是测试集还是训练集其均十分接近于紫色的虚线。
-
迭代次数与损失值关系图
这是迭代1000次的损失值变化图,可以找到此变化的波动并不算大,一开始时其损失值高达1400,但是在后面的更新参数后越来越小,最终接近于0。在接近迭代100次时,损失值开始趋于缓慢减小。
优化讨论
-
标准化方法
为了检验标准化方法哪种更好,我将神经元个数固定为
8
,优化器选择Adam
,迭代次数设置为1000
,学习率为0.01
来进行比较,以下为比较结果对比图:可以明显地知道,minmax标准化方法在这里并不适用,相反它会使得模型的效果更差。而zscore方法所得到的R2值均0.8以上,也可以从图中看出其拟合效果非常好,而且其损失值的变化也十分地圆滑。所以在这里zscore的拟合效果是很好的。
-
神经元个数
为了检验神经元个数的好坏,这里除神经元个数外其他参数均设置为默认值。神经元的个数分别设置为4、8、16。其对比图如下所示,
可知4个神经元的预测效果是十分地差的,而16个神经的效果则会优于8个的。其R2值为0.92于0.88,说明可以考虑使用更多地神经元。
-
优化器
对于优化器,我选择了adagrad\adam\asgd进行对比,
由图可以清楚地知道ASGD优化器不适合本次的数据,而对比Adam与Adagrad,Adam将会更优。
还有对于其他的可变参数如学习率和迭代次数等,可以根据不断测试来定出最佳值。
源代码文件:https://download.csdn.net/download/weixin_51735061/85816263