1 写在前面
1.本文为基于TensorFlow tf.estimator
接口的深度学习网络,而非TensorFlow 2.0中常用的Keras
接口;关于Keras
接口实现深度学习回归,请看这里:
https://blog.csdn.net/zhebushibiaoshifu/article/details/114016531。
2.本文代码以DNNRegressor
回归为例;而由于基于 tf.estimator
接口的深度学习回归与分类整体较为类似,因此二者具有触类旁通的效果。关于分类的一个具体实例,大家可以查看官网:
https://www.tensorflow.org/tutorials/estimator/premade。
3.本文第二部分为代码的分解介绍,第三部分为完整代码。
4.完整的.py格式代码可以在这里下载:
https://download.csdn.net/download/zhebushibiaoshifu/15447065,没有设置成需积分下载,所以大家直接点击下载即可。
5.相关版本信息:
Python版本:3.8.5
TensorFlow版本:2.4.1
编译器版本:Spyder 4.1.5
2 代码分解介绍
2.1 准备工作
首先需要引入相关的库与包。
代码语言:javascript复制import os
import openpyxl
import numpy as np
import pandas as pd
import tensorflow as tf
import scipy.stats as stats
import matplotlib.pyplot as plt
from sklearn import metrics
from sklearn.model_selection import train_test_split
其次,基于TensorFlow的代码往往会输出较多的日志信息,从而使得我们对代码执行情况的了解受到一定影响。代码输出的日志信息有四种,依据严重程度由低到高排序:INFO(通知)<WARNING(警告)<ERROR(错误)<FATAL(致命的);我们可以通过:
代码语言:javascript复制os.environ['TF_CPP_MIN_LOG_LEVEL']='3'
来对TensorFlow的输出日志信息加以约束。其中,“3”代表只输出FATAL信息。但要注意,这句代码需要放在 import tensorflow的前面:
代码语言:javascript复制import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='3'
import openpyxl
import numpy as np
import pandas as pd
import tensorflow as tf
import scipy.stats as stats
import matplotlib.pyplot as plt
from sklearn import metrics
from sklearn.model_selection import train_test_split
2.2 参数配置
深度学习代码一大特点即为具有较多的参数需要我们手动定义。为避免调参时上下翻找,我们可以将主要的参数集中在一起,方便我们后期调整。
其中,具体参数的含义在本文后续部分详细介绍。
代码语言:javascript复制# 将各类变量放在一个位置集中定义,十分有利于机器学习等变量较多的代码
MyModelPath="G:/CropYield/03_DL/02_DNNModle" # 确定每一次训练所得模型保存的位置
MyDataPath="G:/CropYield/03_DL/00_Data/AllDataAll.csv" # 确定输入数据的位置
MyResultSavePath="G:/CropYield/03_DL/03_OtherResult/EvalResult54.xlsx" # 确定模型精度结果(RMSE等)与模型参数保存的位置
TestSize=0.2 # 确定数据中测试集所占比例
RandomSeed=np.random.randint(low=24,high=25) # 确定划分训练集与测试集的随机数种子
OptMethod='Adam' # 确定模型所用的优化方法
LearningRate=0.01 # 确定学习率
DecayStep=200 # 确定学习率下降的步数
DecayRate=0.96 # 确定学习率下降比率
HiddenLayer=[64,128] # 确定隐藏层数量与每一层对应的神经元数量
ActFun='tf.nn.relu' # 确定激活函数
Dropout=0.3 # 确定Dropout的值
LossReduction='tf.compat.v1.ReductionV2.SUM_OVER_BATCH_SIZE' # 指定每个批次训练误差的减小方法
BatchNorm='False' # 确定是否使用Batch Normalizing
TrainBatchSize=110 # 确定训练数据一个Batch的大小
TrainStep=3000 # 确定训练数据的Step数量
EvalBatchSize=1 # 确定验证数据一个Batch的大小
PredictBatchSize=1 # 确定预测数据(即测试集)一个Batch的大小
2.3 原有模型删除
DNNRegressor
每执行一次,便会在指定路径中保存当前运行的模型。为保证下一次模型保存时不受上一次模型运行结果干扰,我们可以将模型文件夹内的全部文件删除。
# DeleteOldModel函数,删除上一次运行所保存的模型
def DeleteOldModel(ModelPath):
AllFileName=os.listdir(ModelPath) # 获取ModelPath路径下全部文件与文件夹
for i in AllFileName:
NewPath=os.path.join(ModelPath,i) # 分别将所获取的文件或文件夹名称与ModelPath路径组合
if os.path.isdir(NewPath): # 若组合后的新路径是一个文件夹
DeleteOldModel(NewPath) # 递归调用DeleteOldModel函数
else:
os.remove(NewPath) # 若不是一个新的文件夹,而是一个文件,那么就删除
# 调用DeleteOldModel函数,删除上一次运行所保存的模型
DeleteOldModel(MyModelPath)
需要注意,以上代码仅删除指定路径下的文件,文件夹不删除。大家如果需要将文件夹也同时删除,修改以上代码函数中的后面几句即可。
2.4 数据导入与数据划分
我的数据已经保存在了csv文件中,因此可以用pd.read_csv
直接读取。
其中,数据的每一列是一个特征,每一行是全部特征与因变量(就是下面的Yield)组合成的样本。
代码语言:javascript复制# LoadData函数,加载全部数据
def LoadData(DataPath):
MyData=pd.read_csv(DataPath,names=['EVI0610','EVI0626','EVI0712','EVI0728','EVI0813','EVI0829',
'EVI0914','EVI0930','EVI1016','Lrad06','Lrad07','Lrad08',
'Lrad09','Lrad10','Prec06','Prec07','Prec08','Prec09',
'Prec10','Pres06','Pres07','Pres08','Pres09','Pres10',
'SIF161','SIF177','SIF193','SIF209','SIF225','SIF241',
'SIF257','SIF273','SIF289','Shum06','Shum07','Shum08',
'Shum09','Shum10','SoilType','Srad06','Srad07','Srad08',
'Srad09','Srad10','Temp06','Temp07','Temp08','Temp09',
'Temp10','Wind06','Wind07','Wind08','Wind09','Wind10',
'Yield'],header=0) # 加载DataPath路径所指定的数据,names中的内容为各列的名称
return MyData
# 初始数据处理
AllXY=LoadData(MyDataPath) # 调用LoadData函数,获取数据
Label={"Yield":AllXY.pop("Yield")} # 将因变量从全部数据中提取出
AllX,AllY=AllXY,(pd.DataFrame(Label)) # 将自变量与因变量分离
# 划分数据训练集与测试集
TrainX,TestX,TrainY,TestY=train_test_split(AllX,
AllY,
test_size=TestSize, # 指定数据中测试集所占比例
random_state=RandomSeed # 指定划分训练集与测试集的随机数种子
)
2.5 Feature Columns定义
Feature Columns就是一个桥梁,联系你的初始数据与模型;其好比一个名单,模型拿着这个名单到你的数据(即2.4部分你导入的数据)中按列的名称一一搜索,若初始数据中的某列名称在Feature Columns里,那么模型就会把初始数据中这一列的数据全部拿到自己这里,进行训练。
因为我们是希望导入数据的全部特征,那么可以直接在全部数据的自变量中循环,将全部特征的名称导入Feature Columns。
在这里需要注意的是,只有连续数值变量才可以用tf.feature_column.numeric_column
处理;若是类别变量可以参考:
https://blog.csdn.net/zhebushibiaoshifu/article/details/115335441。
代码语言:javascript复制# estimator接口中的模型需要用“Feature columns”对象作为输入数据,只有这样模型才知道读取哪些数据
FeatureColumn=[] # 定义一个新的“Feature columns”对象
for key in AllX.keys():
FeatureColumn.append(tf.feature_column.numeric_column(key=key)) # 将全部因变量数据(需要均为连续变量)导入
2.6 模型优化方法构建与模型结构构建
模型优化方法即模型中的optimizer
,其可以在模型结构构建时输入;但有时优化方法较为复杂(例如引入了学习率下降),那么在构建模型时配置优化方法的话就会有些不方便。因此我们首先构建模型优化方法。
# 定义模型优化方法
# Optimizer=OptMethod # 优化方法选用OptMethod所指定的方法
Optimizer=lambda:tf.keras.optimizers.Adam(
learning_rate=tf.compat.v1.train.exponential_decay(learning_rate=LearningRate, # 初始学习率
global_step=tf.compat.v1.train.get_global_step(),
# 全局步数,用以计算已经衰减后的学习率
# get_global_step()函数自动获取当前的已经执行的步数
decay_steps=DecayStep, # 学习率下降完成的指定步数
decay_rate=DecayRate # 衰减率
) # 选用基于学习率指数下降的Adam方法,此举有助于降低过拟合风险
# 这一函数返回每次对应的学习率
)
以上代码中有两个Optimizer=
,第一个是直接输入优化方法的名称即可,名称包括:'Adagrad', 'Adam', 'Ftrl', 'RMSProp', SGD';默认为Adagrad。
第二个是在选择了优化方法的基础上,配置其他信息。例如第二个,其代表着学习率指数下降的Adam优化方法。其中,tf.compat.v1.train.exponential_decay
可视作一个计算每次训练学习率的函数,他返回的是每一次对应的学习率。可能这么说不太好理解,看这个公式:其返回值为 learning_rate *decay_rate ^ (global_step / decay_steps),是不是就明白啦。
我们选择第二个优化方法,因此把第一个注释掉。
随后,我们定义模型的结构。
代码语言:javascript复制# 基于DNNRegressor构建深度学习模型
DNNModel=tf.estimator.DNNRegressor(feature_columns=FeatureColumn, # 指定模型所用的“Feature columns”对象
hidden_units=HiddenLayer, # 指定隐藏层数量与每一层对应的神经元数量
optimizer=Optimizer, # 指定模型所用的优化方法
activation_fn=eval(ActFun), # 指定激活函数
dropout=Dropout, # 指定Dropout的值
label_dimension=1, # 输出数据的维度,即因变量的个数
model_dir=MyModelPath, # 指定每一次训练所得模型保存的位置
# loss_reduction=eval(LossReduction), # 指定每个批次训练误差的减小方法
batch_norm=eval(BatchNorm) # 指定是否使用Batch Normalizing
)
模型的构建,对照着代码上的注释,就比较好理解了;其中,我把loss_reduction
注释掉,是因为可能由于TensorFlow版本的问题,其总是报错,所以就用默认的值就好;而最后一个batch_norm
,决定了是否进行Batch Normalizing。Batch Normalizing可以保持深度神经网络在每一层保持相同分布,从而加快网络收敛与增强网络稳固性。
其它参数可以参考:
https://www.tensorflow.org/api_docs/python/tf/estimator/DNNRegressor;
https://www.tensorflow.org/api_docs/python/tf/nn
2.7 模型训练
训练模型这一部分,我认为反而比模型的构建可能还难理解一些。我们先看代码:
代码语言:javascript复制# 基于训练数据训练模型
DNNModel.train(input_fn=lambda:InputFun(TrainX,
TrainY,
True,
TrainBatchSize
), # 调用InputFun函数;InputFun函数返回“tf.data.Dataset”对象,这个对象才可以被
# train函数识别并带入模型;由于InputFun函数每次返回BatchSize大小的数据个数,
# 因此需要多次执行,前面需要加lambda
steps=TrainStep # 指定模型训练的步数
)
我们可以这么理解:在train
函数中,只有一个参数input_fn
;而这个参数的输入,又是一个新的函数——这个新的函数就是大名鼎鼎的input function
了。
他长这个样子:
代码语言:javascript复制# InputFun函数,训练数据与验证数据所用的Input函数
def InputFun(Features,Labels,Training,BatchSize):
Datasets=tf.data.Dataset.from_tensor_slices((dict(Features),Labels)) # 对数据加以加载
if Training:
Datasets=Datasets.shuffle(1000).repeat() # 对于训练数据,需要打乱(shuffle)、重复(repeat)
return Datasets.batch(BatchSize) # 将经过上述处理后的数据以每次BatchSize个输出
那我们首先就看input function
——也就是代码中的InputFun
函数。其实这个函数的用处很简单,用官网的话说,其就是用来输入模型支持的数据类型的——只有经过input function
处理后,数据才可以被DNNRegressor
识别。听上去这么厉害,它到底是如何操作的呢?
很简单,它只需要将初始的数据转换为特定的格式即可,这个格式是一个元组(tuple),这个元组有两个元素:
一就是features
,是一个字典。这个字典的每一个键是每一个特征的名称,就比如用植物特性对花的种类加以区分,那么花的“叶长”“叶片厚度”等等就是一个个特征的名称,也就是这里的一个个“键”;而这个字典的值,就是这个特征对应的全部样本的数值组成的数组。
二就是label
,是全部样本对应的label,也就是因变量。
不知道大家有没有理解,我们就举一个简单的例子。假如我们用两个地方的温度与降水预测这两个地方的作物产量:其温度分别为10 ℃、20 ℃,降水分别为15 mm,25 mm,作物产量分别为100千克每公顷,150千克每公顷——那么tuple由两个部分组成:
代码语言:javascript复制tuple=(features,label)
features={'温度':np.array([10,20]),'降水':np.array([15,25])}
label=np.array([100,150])
怎么样,是不是明白啦。
理解了之后,我们继续看InputFun
函数。首先,tf.data.Dataset.from_tensor_slices
用来将输入的数据加载并转换为Datase的形式;随后,如果是训练状态下,那么数据会进行打乱.shuffle(1000)
——相当于对数据加以洗牌,防止初始数据具有一定的趋势。例如如果我们做分类,其中初始数据的前80%都是第一类,后20%都是第二类,那么如果我们不打乱数据,会使得用前80%数据训练出来的结果都是第一类(即模型只认识第一类),在后20%进行测试时,所得结果也全都为第一类;所以要打乱。其中的1000是buffer_size
参数,这个数据必须要比你的数据样本个数大。至于.shuffle(1000)
这个函数的原理我一直没有搞明白,大家感兴趣的话可以了解一下:
https://www.tensorflow.org/api_docs/python/tf/data/Dataset#shuffle。
.repeat()
则是对数据集加以重复,之所以要重复,是因为我们需要对全部数据训练好几轮(即好几个Epoch),因此要对初始数据加以重复。
随后,用.batch()
函数输出BatchSize个数据,也就是一批数据;其中BatchSize就是每一批数据的个数。
这个就是InputFun
函数。再看train
函数函数:大家也看出来了,这个InputFun
函数是每次输出一批(BatchSize个)数据;而我们训练的时候,肯定是要一批一批不停输入数据的,因此这就解释了为什么InputFun
函数前有一个lambda
——因为InputFun
函数要把处理后的数据分多次传给train
。
2.8 模型验证与测试
理解了以上内容,接下来就好理解多了。我们需要进行验证与测试的操作——其实验证也就是利用了测试集数据,之所以我还进行了测试,是因为希望可以获取测试集预测结果,从而更直观地了解模型精度水平。
代码语言:javascript复制# InputFunPredict函数,测试数据所用的Input函数
def InputFunPredict(Features,BatchSize):
return tf.data.Dataset.from_tensor_slices(dict(Features)).batch(BatchSize) # 对数据加以加载,以每次BatchSize个输出
# 验证模型并保存验证结果
EvalResult=DNNModel.evaluate(input_fn=lambda:InputFun(TestX,
TestY,
False,
EvalBatchSize
)
)
# 打印验证结果
print('ev:{}'.format(EvalResult))
# 基于测试数据测试模型精度结果
PredictValues=DNNModel.predict(input_fn=lambda:InputFunPredict(TestX,
PredictBatchSize
)
)
其中,验证时.evaluate
所用的InputFun
函数其实和训练集所用的是一样的函数,只不过验证时不需要进行打乱.shuffle(1000)
和重复.repeat()
操作;而测试时.predict
的InputFun
函数则是新的,其只需要输入自变量、无需输入因变量。
2.9 精度评定、拟合图像绘制与模型参数与精度结果保存
精度评定与拟合图像就不用过多说啦~最终,我们最好将模型参数与精度衡量指标结果保存在Excel表格中,这样子方便之后的调参过程。这里就不再一一介绍啦,大家对照代码中的注释即可。
代码语言:javascript复制# AccuracyVerification函数,进行精度验证指标的计算与绘图
def AccuracyVerification(PredictLabels,TestLabels):
value=0
PredictValuesList=[]
for k in PredictLabels:
value=k.get('predictions')[0]
PredictValuesList.append(value)
TestLabels=TestLabels.values.tolist()
TestYList=sum(TestLabels,[])
# 以上为获取测试数据的因变量与模型预测所得的因变量
Pearsonr=stats.pearsonr(TestYList,PredictValuesList) # 计算皮尔逊相关系数
R2=metrics.r2_score(TestYList,PredictValuesList) # 计算R方
RMSE=metrics.mean_squared_error(TestYList,PredictValuesList)**0.5 # 计算RMSE
plt.cla()
plt.plot(TestYList,PredictValuesList,'r*')
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
# 以上为绘制拟合图像
print('Pearson correlation coefficient is {0}, and RMSE is {1}.'.format(Pearsonr[0],RMSE))
return (Pearsonr[0],R2,RMSE,PredictValuesList)
# WriteAccuracy函数,将模型所涉及的参数与最终精度结果保存
def WriteAccuracy(*WriteVar):
ExcelData=openpyxl.load_workbook(WriteVar[0])
SheetName=ExcelData.get_sheet_names() # 获取全部Sheet
WriteSheet=ExcelData.get_sheet_by_name(SheetName[0]) # 获取指定Sheet
WriteSheet=ExcelData.active # 激活指定Sheet
MaxRowNum=WriteSheet.max_row # 获取指定Sheet对应第一个空行
for i in range(len(WriteVar)-1):
exec("WriteSheet.cell(MaxRowNum 1,i 1).value=WriteVar[i 1]") # 用exec执行语句,写入信息
ExcelData.save(WriteVar[0]) # 保存文件
# 调用AccuracyVerification函数,进行精度验证指标的计算与绘图
AccuracyResult=AccuracyVerification(PredictValues,TestY)
PearsonR,R2,RMSE,PredictY=AccuracyResult[0],AccuracyResult[1],AccuracyResult[2],AccuracyResult[3]
# 调用WriteAccuracy函数,将模型所涉及的参数与最终精度结果保存
WriteAccuracy(MyResultSavePath,PearsonR,R2,RMSE,TestSize,RandomSeed,OptMethod,LearningRate,DecayStep,
DecayRate,','.join('%s' %i for i in HiddenLayer),ActFun,Dropout,LossReduction,
BatchNorm,TrainBatchSize,TrainStep,EvalBatchSize,PredictBatchSize)
至此,全部的代码分解介绍都结束啦~
3 详细代码
代码语言:javascript复制# -*- coding: utf-8 -*-
"""
Created on Tue Feb 23 16:13:21 2021
@author: Chutj
"""
# 加载必要的库、包等
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='3'
import openpyxl
import numpy as np
import pandas as pd
import tensorflow as tf
import scipy.stats as stats
import matplotlib.pyplot as plt
from sklearn import metrics
from sklearn.model_selection import train_test_split
# ===============*** 函数声明区域 ***===============
# DeleteOldModel函数,删除上一次运行所保存的模型
def DeleteOldModel(ModelPath):
AllFileName=os.listdir(ModelPath) # 获取ModelPath路径下全部文件与文件夹
for i in AllFileName:
NewPath=os.path.join(ModelPath,i) # 分别将所获取的文件或文件夹名称与ModelPath路径组合
if os.path.isdir(NewPath): # 若组合后的新路径是一个文件夹
DeleteOldModel(NewPath) # 递归调用DeleteOldModel函数
else:
os.remove(NewPath) # 若不是一个新的文件夹,而是一个文件,那么就删除
# LoadData函数,加载全部数据
def LoadData(DataPath):
MyData=pd.read_csv(DataPath,names=['EVI0610','EVI0626','EVI0712','EVI0728','EVI0813','EVI0829',
'EVI0914','EVI0930','EVI1016','Lrad06','Lrad07','Lrad08',
'Lrad09','Lrad10','Prec06','Prec07','Prec08','Prec09',
'Prec10','Pres06','Pres07','Pres08','Pres09','Pres10',
'SIF161','SIF177','SIF193','SIF209','SIF225','SIF241',
'SIF257','SIF273','SIF289','Shum06','Shum07','Shum08',
'Shum09','Shum10','SoilType','Srad06','Srad07','Srad08',
'Srad09','Srad10','Temp06','Temp07','Temp08','Temp09',
'Temp10','Wind06','Wind07','Wind08','Wind09','Wind10',
'Yield'],header=0) # 加载DataPath路径所指定的数据,names中的内容为各列的名称
return MyData
# InputFun函数,训练数据与验证数据所用的Input函数
def InputFun(Features,Labels,Training,BatchSize):
Datasets=tf.data.Dataset.from_tensor_slices((dict(Features),Labels)) # 对数据加以加载
if Training:
Datasets=Datasets.shuffle(1000).repeat() # 对于训练数据,需要打乱(shuffle)、重复(repeat)
return Datasets.batch(BatchSize) # 将经过上述处理后的数据以每次BatchSize个输出
# InputFunPredict函数,测试数据所用的Input函数
def InputFunPredict(Features,BatchSize):
return tf.data.Dataset.from_tensor_slices(dict(Features)).batch(BatchSize) # 对数据加以加载,以每次BatchSize个输出
# AccuracyVerification函数,进行精度验证指标的计算与绘图
def AccuracyVerification(PredictLabels,TestLabels):
value=0
PredictValuesList=[]
for k in PredictLabels:
value=k.get('predictions')[0]
PredictValuesList.append(value)
TestLabels=TestLabels.values.tolist()
TestYList=sum(TestLabels,[])
# 以上为获取测试数据的因变量与模型预测所得的因变量
Pearsonr=stats.pearsonr(TestYList,PredictValuesList) # 计算皮尔逊相关系数
R2=metrics.r2_score(TestYList,PredictValuesList) # 计算R方
RMSE=metrics.mean_squared_error(TestYList,PredictValuesList)**0.5 # 计算RMSE
plt.cla()
plt.plot(TestYList,PredictValuesList,'r*')
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
# 以上为绘制拟合图像
print('Pearson correlation coefficient is {0}, and RMSE is {1}.'.format(Pearsonr[0],RMSE))
return (Pearsonr[0],R2,RMSE,PredictValuesList)
# WriteAccuracy函数,将模型所涉及的参数与最终精度结果保存
def WriteAccuracy(*WriteVar):
ExcelData=openpyxl.load_workbook(WriteVar[0])
SheetName=ExcelData.get_sheet_names() # 获取全部Sheet
WriteSheet=ExcelData.get_sheet_by_name(SheetName[0]) # 获取指定Sheet
WriteSheet=ExcelData.active # 激活指定Sheet
MaxRowNum=WriteSheet.max_row # 获取指定Sheet对应第一个空行
for i in range(len(WriteVar)-1):
exec("WriteSheet.cell(MaxRowNum 1,i 1).value=WriteVar[i 1]") # 用exec执行语句,写入信息
ExcelData.save(WriteVar[0]) # 保存文件
# ===============*** 代码由此开始执行 ***===============
# --- 建议由这里开始看 ---
# 将各类变量放在一个位置集中定义,十分有利于机器学习等变量较多的代码
MyModelPath="G:/CropYield/03_DL/02_DNNModle" # 确定每一次训练所得模型保存的位置
MyDataPath="G:/CropYield/03_DL/00_Data/AllDataAll.csv" # 确定输入数据的位置
MyResultSavePath="G:/CropYield/03_DL/03_OtherResult/EvalResult54.xlsx" # 确定模型精度结果(RMSE等)与模型参数保存的位置
TestSize=0.2 # 确定数据中测试集所占比例
RandomSeed=np.random.randint(low=24,high=25) # 确定划分训练集与测试集的随机数种子
OptMethod='Adam' # 确定模型所用的优化方法
LearningRate=0.01 # 确定学习率
DecayStep=200 # 确定学习率下降的步数
DecayRate=0.96 # 确定学习率下降比率
HiddenLayer=[64,128] # 确定隐藏层数量与每一层对应的神经元数量
ActFun='tf.nn.relu' # 确定激活函数
Dropout=0.3 # 确定Dropout的值
LossReduction='tf.compat.v1.ReductionV2.SUM_OVER_BATCH_SIZE' # 指定每个批次训练误差的减小方法
BatchNorm='False' # 确定是否使用Batch Normalizing
TrainBatchSize=110 # 确定训练数据一个Batch的大小
TrainStep=3000 # 确定训练数据的Step数量
EvalBatchSize=1 # 确定验证数据一个Batch的大小
PredictBatchSize=1 # 确定预测数据(即测试集)一个Batch的大小
# 调用DeleteOldModel函数,删除上一次运行所保存的模型
DeleteOldModel(MyModelPath)
# 初始数据处理
AllXY=LoadData(MyDataPath) # 调用LoadData函数,获取数据
Label={"Yield":AllXY.pop("Yield")} # 将因变量从全部数据中提取出
AllX,AllY=AllXY,(pd.DataFrame(Label)) # 将自变量与因变量分离
# 划分数据训练集与测试集
TrainX,TestX,TrainY,TestY=train_test_split(AllX,
AllY,
test_size=TestSize, # 指定数据中测试集所占比例
random_state=RandomSeed # 指定划分训练集与测试集的随机数种子
)
# estimator接口中的模型需要用“Feature columns”对象作为输入数据,只有这样模型才知道读取哪些数据
FeatureColumn=[] # 定义一个新的“Feature columns”对象
for key in AllX.keys():
FeatureColumn.append(tf.feature_column.numeric_column(key=key)) # 将全部因变量数据(需要均为连续变量)导入
# 定义模型优化方法
# Optimizer=OptMethod # 优化方法选用OptMethod所指定的方法
Optimizer=lambda:tf.keras.optimizers.Adam(
learning_rate=tf.compat.v1.train.exponential_decay(learning_rate=LearningRate, # 初始学习率
global_step=tf.compat.v1.train.get_global_step(),
# 全局步数,用以计算已经衰减后的学习率
# get_global_step()函数自动获取当前的已经执行的步数
decay_steps=DecayStep, # 学习率下降完成的指定步数
decay_rate=DecayRate # 衰减率
) # 选用基于学习率指数下降的Adam方法,此举有助于降低过拟合风险
# 这一函数返回每次对应的学习率
)
# 基于DNNRegressor构建深度学习模型
DNNModel=tf.estimator.DNNRegressor(feature_columns=FeatureColumn, # 指定模型所用的“Feature columns”对象
hidden_units=HiddenLayer, # 指定隐藏层数量与每一层对应的神经元数量
optimizer=Optimizer, # 指定模型所用的优化方法
activation_fn=eval(ActFun), # 指定激活函数
dropout=Dropout, # 指定Dropout的值
label_dimension=1, # 输出数据的维度,即因变量的个数
model_dir=MyModelPath, # 指定每一次训练所得模型保存的位置
# loss_reduction=eval(LossReduction), # 指定每个批次训练误差的减小方法
batch_norm=eval(BatchNorm) # 指定是否使用Batch Normalizing
)
# tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO) # 将INFO级别的日志信息显示到屏幕
# 基于训练数据训练模型
DNNModel.train(input_fn=lambda:InputFun(TrainX,
TrainY,
True,
TrainBatchSize
), # 调用InputFun函数;InputFun函数返回“tf.data.Dataset”对象,这个对象才可以被
# train函数识别并带入模型;由于InputFun函数每次返回BatchSize大小的数据个数,
# 因此需要多次执行,前面需要加lambda
steps=TrainStep # 指定模型训练的步数
)
# 验证模型并保存验证结果
EvalResult=DNNModel.evaluate(input_fn=lambda:InputFun(TestX,
TestY,
False,
EvalBatchSize
)
)
# 打印验证结果
print('ev:{}'.format(EvalResult))
# 基于测试数据测试模型精度结果
PredictValues=DNNModel.predict(input_fn=lambda:InputFunPredict(TestX,
PredictBatchSize
)
)
# 调用AccuracyVerification函数,进行精度验证指标的计算与绘图
AccuracyResult=AccuracyVerification(PredictValues,TestY)
PearsonR,R2,RMSE,PredictY=AccuracyResult[0],AccuracyResult[1],AccuracyResult[2],AccuracyResult[3]
# 调用WriteAccuracy函数,将模型所涉及的参数与最终精度结果保存
WriteAccuracy(MyResultSavePath,PearsonR,R2,RMSE,TestSize,RandomSeed,OptMethod,LearningRate,DecayStep,
DecayRate,','.join('%s' %i for i in HiddenLayer),ActFun,Dropout,LossReduction,
BatchNorm,TrainBatchSize,TrainStep,EvalBatchSize,PredictBatchSize)