• Tensorflow2.x——FeutureColumns特征列详解


    @

    特征列feature_column

    特征列 通常用于对结构化数据实施特征工程时候使用,图像或者文本数据一般不会用到特征列。可以将特征列视为原始数据和 Estimator 之间的媒介。特征列非常丰富,使您可以将各种原始数据转换为 Estimators 可用的格式,从而可以轻松进行实验。

    特征列就是对原始数据的特征进行处理,处理成Estimators 可用的格式

    一,特征列用法概述

    • 将类别特征转换为one-hot编码特征
    • 将连续特征构建分桶特征
    • 对多个特征生成交叉特征等等。

    要创建特征列,请调用 tf.feature_column 模块的函数。该模块中常用的九个函数如下图所示,所有九个函数都会返回一个 Categorical-Column 或一个Dense-Column 对象,但却不会返回 bucketized_column,后者继承自这两个类。
    在这里插入图片描述
    注意:所有的Catogorical Column类型最终都要通过indicator_column转换成Dense Column类型才能传入模型!

    • numeric_column 数值列,不做任何处理,直接进行格式转换,最常用。

    • bucketized_column 分桶列,由数值列生成,将一个连续的数值列变成分段的数值特征,one-hot编码。

    • categorical_column_with_identity 分类标识列,one-hot编码,相当于分桶列每个桶为1个整数的情况。

    • categorical_column_with_vocabulary_list 分类词汇列,one-hot编码,由list指定词典。

    • categorical_column_with_vocabulary_file 分类词汇列,由文件file指定词典。

    • categorical_column_with_hash_bucket 哈希列,整数或词典较大时采用。

    • indicator_column 指标列,由Categorical Column生成,one-hot编码

    • embedding_column 嵌入列,由Categorical Column生成,嵌入矢量分布参数需要学习。嵌入矢量维数建议取类别数量的 4 次方根。

    • crossed_column 交叉列,可以由除categorical_column_with_hash_bucket的任意分类列构成。

    二、数值列 Numeric column

    age = tf.feature_column.numeric_column("age")
    age
    
    NumericColumn(key='age', # 列名,也就是特征名
    			shape=(1,),  # 数据形状
    			default_value=None, # 缺省值的默认值
    			dtype=tf.float32, # 数据格式
    			normalizer_fn=None) # 标准化函数normalizer_fn,可以对每个每行数据进行处理。
    

    数值列,(如果没有定义normalizer_fn函数)不做任何处理,直接进行格式转换,最常用。

    # 使用normalizer_fn函数将每个数据都+2
    age = tf.feature_column.numeric_column("age", normalizer_fn=lambda x:x+2)
    

    三、分箱列 Bucketized column

    分箱是指把一个连续的数字范围分成几段,以表示房屋建造年份的原始数据为例。我们并非以标量数值列表示年份,而是将年份分成下列四个分桶:

    在这里插入图片描述模型将按以下方式表示这些 bucket:

    日期范围 表示为…
    < 1960 年 [1, 0, 0, 0]
    >= 1960 年但 < 1980 年 [0, 1, 0, 0]
    >= 1980 年但 < 2000 年 [0, 0, 1, 0]
    >= 2000 年 [0, 0, 0, 1]

    为什么要将数字(一个完全有效的模型输入)拆分为分类值?首先,该分类将单个输入数字分成了一个四元素矢量。因此模型现在可以学习四个单独的权重而不是一个。四个权重能够创建一个更强大的模型。更重要的是,借助 bucket,模型能够清楚地区分不同年份类别,因为仅设置了一个元素 (1),其他三个元素则被清除 (0)。例如,当我们仅将单个数字(年份)用作输入时,线性模型只能学习线性关系,而使用 bucket 后,模型可以学习更复杂的关系。

    以下代码演示了如何创建 bucketized feature:

    # 首先,将原始输入转换为一个numeric column
    numeric_feature_column = tf.feature_column.numeric_column("Year")
    
    # 然后,按照边界[1960,1980,2000]将numeric column进行bucket
    bucketized_feature_column = tf.feature_column.bucketized_column(
        source_column = numeric_feature_column,
        boundaries = [1960, 1980, 2000])
    
    

    注意,指定一个三元素边界矢量可创建一个四元素 bucket 矢量。

    四、分类识别列Categorical identity column

    语法格式:

    categorical_column_with_identity(
        key,
        num_buckets,
        default_value=None
    )
    
    

    相当于把经过标签数字化的列再转换成one-hot编码
    在这里插入图片描述

    import tensorflow as tf
    
    pets = {'pets': [2,3,0,1]}  #猫0,狗1,兔子2,猪3
    
    column = tf.feature_column.categorical_column_with_identity(
        key='pets',
        num_buckets=4)
    
    indicator = tf.feature_column.indicator_column(column)
    tensor = tf.feature_column.input_layer(pets, [indicator])
    
    with tf.Session() as session:
            print(session.run([tensor]))
    
    

    输出结果

    [array([[0., 0., 1., 0.], #兔子
           [0., 0., 0., 1.], #猪
           [1., 0., 0., 0.], #猫
           [0., 1., 0., 0.]], dtype=float32)] #狗
    
    

    五、分类词汇列Categorical vocabulary column

    在上面的示例图中我们看到,必须手工在excel里面把cat、dog、rabbit、pig转为0123才行,能不能更快一些?
    tf.feature_column.categorical_column_with_vocabulary_list这个方法就是将一个单词列表生成为分类词汇特征列的。

    语法格式

    categorical_column_with_vocabulary_list(
        key,
        vocabulary_list,
        dtype=None,
        default_value=-1,
        num_oov_buckets=0
    )
    
    

    num_ovv_buckets,超出词汇表词汇箱子标签数目Out-Of-Vocabulary,如果数据里面的某个单词没有对应的箱子,比如出现了老鼠mouse,那么老鼠的类别就会在箱子总数~(num_ovv_buckets+ 箱子总数)之间随机选择。
    比如num_ovv=3,那么老鼠mouse会被标记为4~7中的某个数字,可能是5,也可能是4或6。num_ovv不可以是负数。

    测试代码

    import tensorflow as tf
    
    pets = {'pets': ['rabbit','pig','dog','mouse','cat']}  
    
    column = tf.feature_column.categorical_column_with_vocabulary_list(
        key='pets',
        vocabulary_list=['cat','dog','rabbit','pig'], 
        dtype=tf.string, 
        default_value=-1,
        num_oov_buckets=3)
    
    indicator = tf.feature_column.indicator_column(column)
    tensor = tf.feature_column.input_layer(pets, [indicator])
    
    with tf.Session() as session:
        session.run(tf.global_variables_initializer())
        session.run(tf.tables_initializer())
        print(session.run([tensor]))
    
    

    输出结果如下,注意到独热list 有7个元素,这是由于【猫狗兔子猪4个+num_oov_buckets】得到的。

    [array([[0., 0., 1., 0., 0., 0., 0.], #'rabbit'
           [0., 0., 0., 1., 0., 0., 0.], #'pig'
           [0., 1., 0., 0., 0., 0., 0.], #'dog'
           [0., 0., 0., 0., 0., 1., 0.], #mouse
           [1., 0., 0., 0., 0., 0., 0.]], dtype=float32)] #'cat'
    
    

    六、分类词汇列categorical_column_with_vocabulary_file

    单词有些时候会比较多,这时候我们可以直接从文件中读取文字列表:

    import os
    import tensorflow as tf
    
    pets = {'pets': ['rabbit','pig','dog','mouse','cat']}  
    
    dir_path = os.path.dirname(os.path.realpath(__file__))
    fc_path=os.path.join(dir_path,'pets_fc.txt')
    
    column=tf.feature_column.categorical_column_with_vocabulary_file(
            key="pets",
            vocabulary_file=fc_path,
            num_oov_buckets=0)
    
    indicator = tf.feature_column.indicator_column(column)
    tensor = tf.feature_column.input_layer(pets, [indicator])
    
    with tf.Session() as session:
        session.run(tf.global_variables_initializer())
        session.run(tf.tables_initializer())
        print(session.run([tensor]))
    
    

    其中pets_fc.txt每行一个单词如:

    cat
    dog
    rabbit
    pig
    
    

    运行,得到以下结果,这次我们oov使用了0,并没有增加元素数量,但是也导致了mouse变成了全部是0的列表

    [array([[0., 0., 1., 0.], #rabbit
           [0., 0., 0., 1.], #pig
           [0., 1., 0., 0.], #dog
           [0., 0., 0., 0.],#mosue
           [1., 0., 0., 0.]], dtype=float32)] #cat
    
    

    七、哈希列categorical_column_with_hash_bucket

    仍然是分箱,但是这一次我们更加关心“我希望有多少分类?”,也许我们有150个单词,但我们只希望分成100个分类,多下来50个的怎么处理?

    取余数!101除以100余1,我们就把第101种单词也标记为1,和我们的第1种单词变成了同一类,如此类推,第102种和2种同属第2类,第103种和3种同属第3类...

    我们把计算余数的操作写为%;那么第N个单词属于N%100类。

    feature_id = hash(raw_feature) % hash_buckets_size
    

    哈希列HashedColumn对于大数量的类别很有效(vocabulary的file模式也不错),尤其是语言文章处理,将文章分句切词之后,往往得到大数量的单词,每个单词作为一个类别,对于机器学习来说,更容易找到潜在的单词之间的语法关系。

    但哈希也会带来一些问题。如下图所示,我们把厨房用具kitchenware和运动商品sports都标记成了分类12。这看起来是错误的,不过很多时候tensorflow还是能够利用其他的特征列把它们区分开。所以,为了有效减少内存和计算时间,可以这么做。

    在这里插入图片描述
    语法格式

    categorical_column_with_hash_bucket(
        key,
        hash_bucket_size,
        dtype=tf.string
    )
    
    

    测试代码:

    import tensorflow as tf
    
    colors = {'colors': ['green','red','blue','yellow','pink','blue','red','indigo']}  
    
    column = tf.feature_column.categorical_column_with_hash_bucket(
            key='colors',
            hash_bucket_size=5,
        )
    
    indicator = tf.feature_column.indicator_column(column)
    tensor = tf.feature_column.input_layer(colors, [indicator])
    
    with tf.Session() as session:
        session.run(tf.global_variables_initializer())
        session.run(tf.tables_initializer())
        print(session.run([tensor]))
    
    

    运行得到如下的输出,我们注意到red和blue转化后都是一样的,yellow,indigo,pink也都一样,这很糟糕。

    [array([[0., 0., 0., 0., 1.],#green
           [1., 0., 0., 0., 0.],#red
           [1., 0., 0., 0., 0.],#blue
           [0., 1., 0., 0., 0.],#yellow
           [0., 1., 0., 0., 0.],#pink
           [1., 0., 0., 0., 0.],#blue
           [1., 0., 0., 0., 0.],#red
           [0., 1., 0., 0., 0.]], dtype=float32)]#indigo
    
    

    八、交叉列Crossed column

    交叉列可以把多个特征合并成为一个特征,比如把经度longitude、维度latitude两个特征合并为地理位置特征location。
    如下图,我们把Atlanda城市范围的地图横向分成100区间,竖向分成100区间,总共分割成为10000块小区域。(也许接下来我们需要从数据分析出哪里是富人区哪里是穷人区)

    在这里插入图片描述

    import tensorflow as tf
    
    featrues = {
            'longtitude': [19,61,30,9,45],
            'latitude': [45,40,72,81,24]
        }
    
    longtitude = tf.feature_column.numeric_column('longtitude')
    latitude = tf.feature_column.numeric_column('latitude')
    
    longtitude_b_c = tf.feature_column.bucketized_column(longtitude, [33,66])
    latitude_b_c  = tf.feature_column.bucketized_column(latitude,[33,66])
    
    column = tf.feature_column.crossed_column([longtitude_b_c, latitude_b_c], 12)
    
    indicator = tf.feature_column.indicator_column(column)
    tensor = tf.feature_column.input_layer(featrues, [indicator])
    
    with tf.Session() as session:
        session.run(tf.global_variables_initializer())
        session.run(tf.tables_initializer())
        print(session.run([tensor]))
    
    

    上面的代码中进行了分箱操作,分成~33,33~66,66~三箱,运行得到下面输出

    [array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
           [0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
           [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
           [0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]], dtype=float32)]
    
    

    九、指示列Indicator Columns和嵌入列Embeding Columns

    指标列和嵌入列从不直接处理特征,而是将分类列视为输入。

    指示列把特征转换成独热编码
    当我们遇到成千上万个类别的时候,独热列表就会变的特别长[0,1,0,0,0,....0,0,0]。嵌入列可以解决这个问题,它不再限定每个元素必须是0或1,而可以是任何数字,从而使用更少的元素数表现数据。

    如下图,我们最初的数据可能是4个单词比如dog、spoon、scissors、guitar,然后这些单词被分类特征列Categorical处理成为数字0、32、79、80,接下来我们可以使用指示列来处理成为独热的01列表(图中假设我们有81种单词分类),也可以按照嵌入Embeding列来处理成小数元素组成的3元素数列。

    在这里插入图片描述
    嵌入列中的小数只在train训练的时候自动计算生成,能够有效增加训练模型的效率和性能,同时又能便于机器学习从数据中发现潜在的新规律。

    为什么嵌入Embeding的都是[0.421,0.399,0.512]这样的3元素列表,而不是4元5元?实际上有下面的参考算法:

    在这里插入图片描述嵌入列表的维数等于类别总数开4次方,也就是3的4次方等于81种类。

    [3^4=81 ]

    嵌入列语法:

    embedding_column(
        categorical_column,
        dimension, # 维度,即每个列表元素数
        combiner='mean', # 组合器,默认meam,在语言文字处理中选sqrtn可能更好
        initializer=None, # 初始器
        ckpt_to_load_from=None, # 恢复文件
        tensor_name_in_ckpt=None,# 可以从check point中恢复
        max_norm=None,
        trainable=True
    )
    
    

    示例代码

    import tensorflow as tf
    
    features = {'pets': ['dog','cat','rabbit','pig','mouse']}  
    
    pets_f_c = tf.feature_column.categorical_column_with_vocabulary_list(
        'pets',
        ['cat','dog','rabbit','pig'], 
        dtype=tf.string, 
        default_value=-1)
    
    column = tf.feature_column.embedding_column(pets_f_c, 3)
    tensor = tf.feature_column.input_layer(features, [column])
    
    with tf.Session() as session:
        session.run(tf.global_variables_initializer())
        session.run(tf.tables_initializer())
    
        print(session.run([tensor]))
    
    

    运行得到输出,我们看到由于老鼠mouse没有对应的箱子,所以元素都为0

    [array([[ 0.15651548, -0.620424  ,  0.41636208],
           [-1.0857592 ,  0.03593585,  0.20340031],
           [-0.6021426 , -0.48347804, -0.7165713 ],
           [-0.36875582,  0.4034163 , -1.0998975 ],
           [ 0.        ,  0.        ,  0.        ]], dtype=float32)]
    
    

    十、特征列使用范例

    以下是一个使用特征列解决Titanic生存问题的完整范例。

    import datetime
    import numpy as np
    import pandas as pd
    from matplotlib import pyplot as plt
    import tensorflow as tf
    from tensorflow.keras import layers,models
    
    
    #打印日志
    def printlog(info):
        nowtime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        print("
    "+"=========="*8 + "%s"%nowtime)
        print(info+'...
    
    ')
    
    
        
    
    #================================================================================
    # 一,构建数据管道
    #================================================================================
    printlog("step1: prepare dataset...")
    
    
    dftrain_raw = pd.read_csv("./data/titanic/train.csv")
    dftest_raw = pd.read_csv("./data/titanic/test.csv")
    
    dfraw = pd.concat([dftrain_raw,dftest_raw])
    
    def prepare_dfdata(dfraw):
        dfdata = dfraw.copy()
        dfdata.columns = [x.lower() for x in dfdata.columns]
        dfdata = dfdata.rename(columns={'survived':'label'})
        dfdata = dfdata.drop(['passengerid','name'],axis = 1)
        for col,dtype in dict(dfdata.dtypes).items():
            # 判断是否包含缺失值
            if dfdata[col].hasnans:
                # 添加标识是否缺失列
                dfdata[col + '_nan'] = pd.isna(dfdata[col]).astype('int32')
                # 填充,如果是数字,那么就添加这一列的平均值,否则空着
                if dtype not in [np.object,np.str,np.unicode]:
                    dfdata[col].fillna(dfdata[col].mean(),inplace = True)
                else:
                    dfdata[col].fillna('',inplace = True)
        return(dfdata)
    
    dfdata = prepare_dfdata(dfraw)
    dftrain = dfdata.iloc[0:len(dftrain_raw),:]
    dftest = dfdata.iloc[len(dftrain_raw):,:]
    dfdata
    
    
    # 从 dataframe 导入数据 
    def df_to_dataset(df, shuffle=True, batch_size=32):
        dfdata = df.copy()
        if 'label' not in dfdata.columns:
            ds = tf.data.Dataset.from_tensor_slices(dfdata.to_dict(orient = 'list'))
        else: 
            labels = dfdata.pop('label')
            ds = tf.data.Dataset.from_tensor_slices((dfdata.to_dict(orient = 'list'), labels))  
        if shuffle:
            ds = ds.shuffle(buffer_size=len(dfdata))
        ds = ds.batch(batch_size)
        return ds
    
    ds_train = df_to_dataset(dftrain)
    ds_test = df_to_dataset(dftest)
    
    
    feature_columns = []
    
    # 数值列
    for col in ['age','fare','parch','sibsp'] + [
        c for c in dfdata.columns if c.endswith('_nan')]:
        feature_columns.append(tf.feature_column.numeric_column(col))
    feature_columns
    age = tf.feature_column.numeric_column('age')
    age_buckets = tf.feature_column.bucketized_column(age, 
                 boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])
    feature_columns.append(age_buckets)
    feature_columns
    
    #================================================================================
    # 二,定义特征列
    #================================================================================
    printlog("step2: make feature columns...")
    
    feature_columns = []
    
    # 数值列
    for col in ['age','fare','parch','sibsp'] + [
        c for c in dfdata.columns if c.endswith('_nan')]:
        feature_columns.append(tf.feature_column.numeric_column(col))
    
    # 分桶列  # 意思就是我们设置一些分界点,假如是3个分界点,那么这些分界点将数据分成3+1=4类,并且表示为one-hot编码形式
    age = tf.feature_column.numeric_column('age')
    age_buckets = tf.feature_column.bucketized_column(age, 
                 boundaries=[18, 25, 30, 35, 40, 45, 50, 55, 60, 65])
    feature_columns.append(age_buckets)
    
    # 类别列
    # 注意:所有的Catogorical Column类型最终都要通过indicator_column转换成Dense Column类型才能传入模型!!
    sex = tf.feature_column.indicator_column(
          tf.feature_column.categorical_column_with_vocabulary_list(
          key='sex',vocabulary_list=["male", "female"]))
    feature_columns.append(sex)
    
    pclass = tf.feature_column.indicator_column(
          tf.feature_column.categorical_column_with_vocabulary_list(
          key='pclass',vocabulary_list=[1,2,3]))
    feature_columns.append(pclass)
    
    ticket = tf.feature_column.indicator_column(
         tf.feature_column.categorical_column_with_hash_bucket('ticket',3))
    feature_columns.append(ticket)
    
    '''
    ********************************哈希列****************************
    当类别很多或者我们不知道有多少类的时候,我们不能一个一个的列出来,这时候就可以使用hash_bucket,
    第二个参数是我们想把这些数据分成多少类,这个类别数和真实的类别数不一定是一样的,我们自己设置划分为多少类即可
    '''
    embarked = tf.feature_column.indicator_column(
          tf.feature_column.categorical_column_with_vocabulary_list(
          key='embarked',vocabulary_list=['S','C','B']))
    feature_columns.append(embarked)
    
    # 嵌入列
    cabin = tf.feature_column.embedding_column(
        tf.feature_column.categorical_column_with_hash_bucket('cabin',32),2)
    feature_columns.append(cabin)
    
    # 交叉列
    pclass_cate = tf.feature_column.categorical_column_with_vocabulary_list(
              key='pclass',vocabulary_list=[1,2,3])
    
    crossed_feature = tf.feature_column.indicator_column(
        tf.feature_column.crossed_column([age_buckets, pclass_cate],hash_bucket_size=15))
    
    feature_columns.append(crossed_feature)
    
    
    #================================================================================
    # 三,定义模型
    #================================================================================
    printlog("step3: define model...")
    
    tf.keras.backend.clear_session()
    model = tf.keras.Sequential([
      layers.DenseFeatures(feature_columns), #将特征列放入到tf.keras.layers.DenseFeatures中!!!
      layers.Dense(64, activation='relu'),
      layers.Dense(64, activation='relu'),
      layers.Dense(1, activation='sigmoid')
    ])
    
    
    #================================================================================
    # 四,训练模型
    #================================================================================
    printlog("step4: train model...")
    
    model.compile(optimizer='adam',
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
    
    history = model.fit(ds_train,
              validation_data=ds_test,
              epochs=10)
    
    #================================================================================
    # 五,评估模型
    #================================================================================
    printlog("step5: eval model...")
    
    model.summary()
    
    
    %matplotlib inline
    %config InlineBackend.figure_format = 'svg'
    
    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,"accuracy")
    
    Model: "sequential"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #   
    =================================================================
    dense_features (DenseFeature multiple                  64        
    _________________________________________________________________
    dense (Dense)                multiple                  3008      
    _________________________________________________________________
    dense_1 (Dense)              multiple                  4160      
    _________________________________________________________________
    dense_2 (Dense)              multiple                  65        
    =================================================================
    Total params: 7,297
    Trainable params: 7,297
    Non-trainable params: 0
    _________________________________________________________________
    

    在这里插入图片描述

    打赏

    码字不易,如果对您有帮助,就打赏一下吧O(∩_∩)O

    参考链接:https://www.jianshu.com/p/fceb64c790f3

  • 相关阅读:
    vue 5 父子组件及组件间数据传递 学习代码片段
    微信小程序设置数组对象的值
    微信小程序样式拼接 类名三元运算 以及条件拼接
    超简易 消息通知滚动 pc移动适用
    js 批量监听-序号的闭包问题
    docker基础
    mysql
    mongodb
    django-restframework_认证
    SSM 整合
  • 原文地址:https://www.cnblogs.com/long5683/p/13178004.html
Copyright © 2020-2023  润新知