tensorflow2建模--数据框dataframe转tensor进行建模

摘要

使用tensorflow2建立深度学习模型的第一步,往往是将我们加工处理好的pandas的dataframe转化为tensor。按照官方指南的介绍,最佳转化方法是将 pd.DataFrame 转换为 dict,
并对该字典进行切片。本文将结合一个小例子具体描述一下转化方法。

实验说明

我们使用最经典的iris数据集,训练一个多分类器。数据就不再详细描述了,大家应该都熟悉这个数据集。我使用的是tensorflow v2.2 的cpu版本,运行环境就是自己的本了,i7+8G内存。

数据准备

#加载需要的模块
import pandas as pd
import tensorflow as tf
import joblib
import matplotlib.pyplot as plt
import model
from importlib import reload #方便修改model文件后可以立即生效
reload(model)
from model import mymodel

导入数据,为了简便特征名称就记为’a1’,‘a2’,‘a3’,‘a4’吧,类别记为’class’。

df=pd.read_csv('iris.txt',header=None)
df.columns=['a1','a2','a3','a4','class']

我对iris做了一下处理,加了一个离散特征,虽然这个特征造的不自然,但是能更好说明转化方法,毕竟我们希望得到一个比较通用的转化方法,数据集应当同时包含连续数值型特征和离散类别型特征。

def preprocess(df,cate_cols): #cate_cols是我们要编码的特征名称
    #对样本类别进行编码
    categories = {}
    for c in cate_cols:
        categories[c] = df[c].unique().tolist()
        df[c] = pd.Categorical(df[c],
                  categories=categories[c]).codes
    #我们保存一下离散特征的编码,方便以后使用。
    joblib.dump(categories, 'cate_keys')
    
    #将a4特征进行分箱,造一个离散特征
    df['a1_bucket'] = pd.cut(df['a1'],
                             bins=[float('-Inf'),0,5.0,6.0,7.0,8.0,float('Inf')],
                             labels=[0,1,2,3,4,5])

#preprocess dataset
preprocess(df,cate_cols=['class'])

#target column
target=df.pop('class')

tensorflow2中对所有离散型特征都需要进行编码。

下面进行字典切片。这样在模型输入inputs中只需要指定列名就可以tensor的形式得到该列存储的所有数据,比较方便。

#transform dataframe to tensor
ds=tf.data.Dataset.from_tensor_slices((df.to_dict('list'),target.values))

将ds转成可以迭代的形式,以便进行mini-batch训练。

batch_size=32
epoch_num=100
ds=ds.shuffle(df.shape[0]).batch(batch_size).repeat(epoch_num)

模型训练

深度学习训练模型三板斧:
(1)定义模型结构
(2)定义损失函数
(3)定义优化器

模型结构

模型包含两个fully connected隐藏层和一个softmax输出层,我对离散型特征’a1_bucket’进行了向量嵌入(embedding),所以初始化时我们声明一个嵌入矩阵。

call方法就是定义模型的前向传播过程。在tensorflow中我们只需要将前向传播过程定义清楚,反向传播可以使用tf.GradientTape自动进行计算,非常方便。

call中我使用了tf.gather,从嵌入矩阵中查出对应编码的嵌入向量,所以将离散特征进行编码是十分有必要的。通过tf.concat将嵌入向量和其他维度的连续特征合并成一个长向量送入DNN中。

call的输入inputs就是我们之前得到的dataframe的字典切片了,从代码中可以看到字典切片可以很方便的进行tensor操作,非常灵活,能够胜任各种复杂的模型结构。后面我还会在其他文章中进一步说明。

class mymodel(tf.keras.Model):
    def __init__(self,hidden,embed):
        super().__init__()
        self.dense1=tf.keras.layers.Dense(hidden,activation=tf.nn.relu,name='hidden_layer')
        self.dense2 = tf.keras.layers.Dense(hidden, activation=tf.nn.relu, name='hidden_layer2')
        self.output1 = tf.keras.layers.Dense(3, activation=tf.nn.softmax, name='output_layer')
        self.embed=tf.Variable(tf.random.normal((6,embed)),name='a1_bucket_embed')

    def call(self,inputs):
        a1_buck_emb=tf.gather(self.embed,inputs['a1_bucket'])
        a1=tf.expand_dims(inputs['a1'],axis=1)
        a2 = tf.expand_dims(inputs['a2'], axis=1)
        a3 = tf.expand_dims(inputs['a3'], axis=1)
        a4 = tf.expand_dims(inputs['a4'], axis=1)
        i_cct=tf.concat([a1_buck_emb,a1,a2,a3,a4], axis=1)

        d1=self.dense1(i_cct)
        d2=self.dense2(d1)
        return self.output1(d2)

损失函数

对于多分类场景一般就是SparseCategoricalCrossentropy了。

#set loss func
loss=tf.losses.SparseCategoricalCrossentropy()

优化器

我一般使用adam,性能稳定。

#set optimizer
opt=tf.optimizers.Adam(learning_rate=0.01)

除了以上三板斧,我再定义一个metric,看一下模型分类的准确率。

#set evaluation metric
mtc=tf.metrics.SparseCategoricalAccuracy()

训练过程

训练过程就是常规套路了,这里有个坑就是loss()中的label最好定义成float32,不要使用整数型,不过本例中label是整型,训练好像也没有问题。

#record logloss of each step
logloss=[]

for ft,label in ds:
    print("Step %d ...."%i)
    with tf.GradientTape() as tape:
        cur_loss = loss(label, mymodel(ft))
        
    #compute gradient
    grad=tape.gradient(cur_loss,mymodel.trainable_variables)
    
    #update parameters
    opt.apply_gradients(zip(grad,mymodel.trainable_variables))

    mtc.update_state(label,mymodel(ft))
    logloss.append(cur_loss)

    i+=1

训练结果

print("Accuracy: %.4f"%mtc.result().numpy())

#plot logloss curve
img=plt.figure(figsize=(8,6))
plt.title("Iris Example")
plt.plot(range(len(logloss)),logloss)
plt.legend(['train logloss'])
img.show()

Accuracy: 0.9007

运行20个epoch的logloss曲线

总结

本实验描述了对dataframe建模的过程,通过进行字典切片将dataframe转为tensor,这种处理方式非常灵活,能够适用于复杂的模型结构。最终我们训练了一个包含两层隐藏层的DNN分类器。

后续工作

DNN将输入特征进行高阶组合,却忽视了输入特征的低阶组合。后面我将实现DeepFM,将特征的高阶和低阶组合同时考虑进来,看看分类效果会如何。DeepFM的模型结构相比于这个两层的DNN就更加复杂了,我们看下如何使用字典切片开发DeepFM。

欢迎大家一起记录学习新技术的点点滴滴,共同进步。转载请注明原文出处。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值