这里记录下“30天吃掉tensorflow2”这个github上的教学项目的学习笔记,上面的教程很好,而且还自带数据集等等,代码偶尔有些小瑕疵
附上地址https://github.com/lyhue1991/eat_tensorflow2_in_30_days
1-1,结构化数据建模流程范例
一,准备数据
dftrain_raw = pd.read_csv('./data/titanic/train.csv')
dftest_raw = pd.read_csv('./data/titanic/test.csv')
dftrain_raw.head(10)
print(dftrain_raw)
print(type(dftrain_raw))
这一段是通过pd.fead_csv读取csv文件,读取的内容以dataframe格式保存,运行结果如图
Dftrain_raw.headfa方法是返回数据的前10行,以此来判断数据有没有读错
接下来是画图部分,
ax = dftrain_raw['Survived'].value_counts().plot(kind = 'bar',
figsize = (12,8),fontsize=15,rot = 0)
ax.set_ylabel('Counts',fontsize = 15)
ax.set_xlabel('Survived',fontsize = 15)
plt.show()
首先看drtrain_raw[‘Survived’],这里是通过字段从dataframe中选择数据
这是print(dftrain_raw['Survived'].head(10))的结果,对比下之前的数据
Value_counts这个方法,是将数据元素出现的次数倒叙排序
print(dftrain_raw['Survived'].head(10).value_counts())
接下来是plot这个画图方法,参数比较多也比较复杂,常用的参数是这几个
来源:https://blog.csdn.net/jinlong_xu/article/details/70175107
Set_xlabel这些方法从名字上就能看出来,是给坐标轴打上名字的
这里放一个网站,该网站可在线测试代码,以便快速理解画图代码:
http://kakazai.cn/index.php/Kaka/Python/query/name/set_xlabel
最后运行结果是柱状图
ax = dftrain_raw['Age'].plot(kind = 'hist',bins = 20,color= 'purple',
figsize = (12,8),fontsize=15)
ax.set_ylabel('Frequency',fontsize = 15)
ax.set_xlabel('Age',fontsize = 15)
plt.show()
这段代码和之前的大同小异,一样是画图,不过图的类型不一样,结果如下
ax = dftrain_raw.query('Survived == 0')['Age'].plot(kind = 'density',
figsize = (12,8),fontsize=15)
dftrain_raw.query('Survived == 1')['Age'].plot(kind = 'density',
figsize = (12,8),fontsize=15)
ax.legend(['Survived==0','Survived==1'],fontsize = 12)
ax.set_ylabel('Density',fontsize = 15)
ax.set_xlabel('Age',fontsize = 15)
plt.show()
上面这部分代码是求年龄和生还的相关性
首先.query方法,可以理解为把满足数据条件的dataframe里的数据提出来,然后['Age']在前面的基础上指定需要的列的字段名,
如print(dftrain_raw.query('Survived == 0'))的结果如下
而print(dftrain_raw.query('Survived == 0')['Age'])的结果如下
可以看到,.plot方法就是用提取的数据画图,但数据至少要符合维度标准,如
dftrain_raw.query('Survived == 0').plot(kind = 'density',figsize = (12,8),fontsize=15)这样的语句就会报错
正常运行结果
.legend方法是自定义图例(背景),说白了就是这个东西
接下来是数据预处理
dfresult= pd.DataFrame()
这个语句其实就相当于初始化,dfresult里存的是一个空的dataframe
dfresult= pd.DataFrame()
print("dfresult")
print(dfresult)
运行结果:
get_dummies方法是利用pandas实现one hot encode的方法
实验代码
dfresult= pd.DataFrame()
print("dfdata['Pclass']")
print(dfdata['Pclass'])
#Pclass
dfPclass = pd.get_dummies(dfdata['Pclass'])
print("pd.get_dummies")
print(dfPclass)
运行结果
可以看出来,get_dummies是实现onehot-encoding的一种方法,同时保留dataframe的数据类型
这段语句是给列重新命名
实验代码:
dfPclass.columns = ['Pclass_' +str(x) for x in dfPclass.columns ]
print(dfPclass)
结果:
接下来是pd.concat方法,这个方法可以将数据根据不同的轴作简单的融合
pd.concat(objs, axis=0, join='outer', join_axes=None)
参数说明:objs: 融合内容,可以是series,dataframe或者是panel构成的序列lsit
axis: 需要合并链接的轴,0是行,1是列
join:连接的方式 inner,或者outer,即内连接或外连接
实验代码:
dfresult= pd.DataFrame()
#Pclass
dfPclass = pd.get_dummies(dfdata['Pclass'])
dfPclass.columns = ['Pclass_' +str(x) for x in dfPclass.columns ]
dfresult = pd.concat([dfresult,dfPclass],axis = 1)
print(dfresult)
#Sex
dfSex = pd.get_dummies(dfdata['Sex'])
dfresult = pd.concat([dfresult,dfSex],axis = 1)
print(dfresult)
结果:
可以看到两个dataframe融合在一起了
接下来是.fillna这个方法
.fillna函数是填补缺失值,查看原始数据会发现age一栏里确实存在确实值,在dataframe中会用NaN占位
接下来是.isna方法,这个方法会返回一个dataframe,其中会用1替换NaN的位置,用0替换非NAN的位置
实验代码:
dfresult['Age_null'] = pd.isna(dfdata['Age']).astype('int32')
print(dfresult['Age_null'])
结果:
.astype方法是返回新的对象,其中的数值转换成指定的格式,如float64转成int32。注意返回的是新的对象,原来对象的格式不会发生改变。
接下来是series.values方法,这个方法是将series里的值单独返回出来,series是pandas中的一维数组
实验代码
x_train = preprocessing(dftrain_raw)
y_train = dftrain_raw['Survived'].values
print(dftrain_raw['Survived'])
print(y_train)
结果:
在这里x_train需要进行预处理函数,而y_train不需要,是因为y_train是作为输入参数x_train的结果,在这个项目里结果就是生或死,不需要其他的参数,所以作为结果的y_train只需要一维就行了
接下来是.shape方法,这个方法可以得到dataframe的维度,如x_train就是712*15
实验代码
print("x_train.shape =", x_train.shape )
print("y_train.shape=",y_train.shape)
print("x_test.shape =", x_test.shape )
print("y_test.shape=",y_test.shape)
实验结果:
二,定义模型
定义模型部分,使用Keras接口有以下3种方式构建模型:使用Sequential按层顺序构建模型,使用函数式API构建任意结构模型,继承Model基类构建自定义模型。
教学代码里选择使用最简单的Sequential,按层顺序模型
tf.keras.backend.clear_session()
这个方法是清除之前存在的tf训练进程,不清除的话内存里还会有之前训练的数据,影响运行
model = models.Sequential()
这部分是调用keras自带的序列模型
model.add方法即向模型里添加层
layers.Dense(20,activation = 'relu',input_shape=(15,))
tf.layers.dense()层参数解释:https://blog.csdn.net/sazass/article/details/86935556
其中20是units,即输出空间的维数,input_shape是输入参数的维度。我的理解是类似于矩阵相乘,a*b,b*c的矩阵才能相乘,因此需要规范输入参数和输出的维度
Activation是激活函数,我对激活函数的理解是,激活函数是为了让输出的结果变化更明显,范围更广。
model.summary()是输出模型总结,可以看到各层的状况
三,训练模型
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['AUC'])
history = model.fit(x_train,y_train,
batch_size= 64,
epochs= 30,
validation_split=0.2)
model.compile方法来配置训练时的参数
参考博客:https://blog.csdn.net/yunfeather/article/details/106461754
其中adam是优化器的类型,即自适应矩估计,loss是损失函数,这里用的是binary_crossentropy,适用于二分类问题,categorical_crossentropy是分类交叉熵适,用于多分类问题
meteics是衡量训练模型好坏标准,这里使用的是AUC
评价指标介绍:https://blog.csdn.net/liming89/article/details/106854661
Model.fit方法,其中前两个参数x和y分别对应训练数据的输入和结果标签,batchsize指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降,使目标函数优化一步。Epochs是训练终止时的epoch值,训练将在达到该epoch值时停止,当没有设置initial_epoch时,它就是训练的总轮数,否则训练的总轮数为epochs - inital_epoch。validation_split是0~1之间的浮点数,用来指定训练集的一定比例数据作为验证集。验证集将不参与训练,并在每个epoch结束后测试的模型的指标,如损失函数、精确度等。注意,validation_split的划分在shuffle之前,因此如果你的数据本身是有序的,需要先手工打乱再指定validation_split,否则可能会出现验证集样本不均匀。
参考:https://blog.csdn.net/a1111h/article/details/82148497
四,评估模型
import matplotlib.pyplot as plt
def plot_metric(history, metric):
train_metrics = history.history[metric]
val_metrics = history.history['val_'+metric]
epochs = range(1, len(train_metrics) + 1)
plt.plot(epochs, train_metrics, 'bo--')
plt.plot(epochs, val_metrics, 'ro-')
plt.title('Training and validation '+ metric)
plt.xlabel("Epochs")
plt.ylabel(metric)
plt.legend(["train_"+metric, 'val_'+metric])
plt.show()
实验代码:
plot_metric(history,"loss")
train_metrics = history.history[metric]
print("history.history",history.history)
print("train_mertics",train_metrics)
结果:
可以看到,history里存放的就是训练过程中的数据,如loss等,注意plot_metric(history,"AUC")这段代码会报错,输出history.history后我找到了原因,在history里是auc而不是大写的AUC
Model.evaluate这个方法可以用测试集来评估之前训练的模型的准确度
实验代码:
print("evaluate")
model.evaluate(x = x_test,y = y_test)
结果:
五,使用模型
#预测概率
model.predict(x_test[0:10])
#model(tf.constant(x_test[0:10].values,dtype = tf.float32)) #等价写法
#预测类别
model.predict_classes(x_test[0:10])
实验代码:
print("predit",model.predict(x_test[0:10]))
print("predit_class",model.predict_classes(x_test[0:10]))
结果
上面分别是分类概率和结果,可以看出来默认的阈值是0.5
六,保存模型
Keras方式保存
# 保存模型结构及权重
model.save('./data/keras_model.h5')
del model #删除现有模型
# identical to the previous one
model = models.load_model('./data/keras_model.h5')
model.evaluate(x_test,y_test)
# 保存模型结构
json_str = model.to_json()
# 恢复模型结构
model_json = models.model_from_json(json_str)
#保存模型权重
model.save_weights('./data/keras_model_weight.h5')
# 恢复模型结构
model_json = models.model_from_json(json_str)
model_json.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['AUC']
)
# 加载权重
model_json.load_weights('./data/keras_model_weight.h5')
model_json.evaluate(x_test,y_test)
附json_str的内容:
json_model {"class_name": "Sequential", "config": {"name": "sequential", "layers": [{"class_name": "InputLayer", "config": {"batch_input_shape": [null, 15], "dtype": "float32", "sparse": false, "ragged": false, "name": "dense_input"}}, {"class_name": "Dense", "config": {"name": "dense", "trainable": true, "batch_input_shape": [null, 15], "dtype": "float32", "units": 20, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dense", "config": {"name": "dense_1", "trainable": true, "dtype": "float32", "units": 10, "activation": "relu", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}, {"class_name": "Dense", "config": {"name": "dense_2", "trainable": true, "dtype": "float32", "units": 1, "activation": "sigmoid", "use_bias": true, "kernel_initializer": {"class_name": "GlorotUniform", "config": {"seed": null}}, "bias_initializer": {"class_name": "Zeros", "config": {}}, "kernel_regularizer": null, "bias_regularizer": null, "activity_regularizer": null, "kernel_constraint": null, "bias_constraint": null}}]}, "keras_version": "2.4.0", "backend": "tensorflow"}
可以看出通过json保存模型,好处是可以在各个平台上通用,但是读取起来也更为繁琐,不仅需要json格式的模型,还需要再读取权重,并设置优化器和损失函数
TensorFlow原生方式保存
# 保存权重,该方式仅仅保存权重张量
model.save_weights('./data/tf_model_weights.ckpt',save_format = "tf")
# 保存模型结构与模型参数到文件,该方式保存的模型具有跨平台性便于部署
model.save('./data/tf_model_savedmodel', save_format="tf")
print('export saved model.')
model_loaded = tf.keras.models.load_model('./data/tf_model_savedmodel')
model_loaded.evaluate(x_test,y_test)
可以看出最好直接用model.save方法,这是最方便的