import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import feature_column
from tensorflow.keras import layers
from sklearn.model_selection import train_test_split
数据
URL = 'https://storage.googleapis.com/applied-dl/heart.csv'
dataframe = pd.read_csv(URL)
train, test = train_test_split(dataframe, test_size=0.2)
train, val = train_test_split(train, test_size=0.2)
使用tf.data
创建输入流水线
# 一种从 Pandas Dataframe 创建 tf.data 数据集的实用程序方法(utility method)
def df_to_dataset(dataframe, shuffle=True, batch_size=32):
dataframe = dataframe.copy()
labels = dataframe.pop('target')
ds = tf.data.Dataset.from_tensor_slices((dict(dataframe), labels))
if shuffle:
ds = ds.shuffle(buffer_size=len(dataframe))
ds = ds.batch(batch_size)
return ds
特征列
TensorFlow提供了多种特征列在feature_column
模块下,下面创建常用的几类特征列,应演示特征列如何转换dataframe中的列。
from tensorflow import feature_column
创建一个特征列,并转换一批次数据的一个实用程序方法:
# 使用该批次数据演示几种特征列
example_batch = next(iter(train_ds))[0]
def demo(feature_column):
feature_layer = layers.DenseFeatures(feature_column)
print(feature_layer(example_batch).numpy())
1. 分桶列
数值型离散化,例如:将年龄分成几个桶buckets
age_buckets = feature_column.bucketized_column(age,boundaries = [18,25,30,35,40,45,50,55,60,65])
demo(age_buckets)
2. 分类列
在此数据集中,thal用字符串表示(如:‘fixed’, ‘normal’, 或 ‘reversible’)。我们无法直接将字符串提供给模型,相反,我们必须首先将它们映射到数值。分类词汇列(categorical vocabulary columns)提供了一种用one-hot向量表示字符串的方法。词汇表可以用categorical_column_with_vocabulary_list
作为list传递,或者用categorical_column_with_vocabulary_file
从文件中加载
thal = feature_column.categorical_column_with_vocabulary_list('thal',['fixed', 'normal', 'reversible'])
thal_one_hot = feature_column.indicator_column(thal)
demo(thal_one_hot)
3.1 嵌入列
假设我们不是只有几个可能的字符串,而是每个类别有数千(或更多)值。由于多种原因,随着类别数量的增加,使用one-hot编码训练神经网络变得不可行。我们可以使用嵌入例来克服此限制。嵌入列(embedding column)将数据表示为一个低维度稠密向量,而非多维的one-hot向量,该低维密集向量可以包含任何数,而非0或1。嵌入的大小是必须调整的参数。
# 当分类列具有许多可能的值时,最好使用嵌入列
thal_embedding = feature_column.embedding_column(thal, dimension=8)
demo(thal_embedding)
3.2 经过哈希处理的特征列
表示具有大量数值的分类列的另一种方法是使用categorical_column_with_hash_bucket
。该特征列计算输入的一个哈希值,然后选择一个hash_bucket_size
分桶来编码字符串。使用此列时,您不需要提供词汇表,并且可以选择使hash_buckets的数量远远小于实际类别的数量以节省空间。
**注:**该技术的一个重要缺点是可能存在冲突,不同的字符串被映射到同一个范围。实际上,无论如何,经过哈希处理的特征列对某些数据集都是有效。
thal_hashed = feature_column.categorical_column_with_hash_bucket('thal',hash_bucket_size = 1000)
demo(feature_column.indicator_column(thal_hashed))
4. 组合的特征列
将多种特征组合到一个特征中,称为特征组合(feature crosses)
,它让模型能够为每种特征组合学习单独的权重。此外,我们将创建一个age和thal组合的新特征。请注意,crossed_column
不会构建所有可能组合的完整列表(可能非常大)。相反,它由hashed_column
支持,可以选择表的大小。
crossed_feature = feature_column.crossed_column([age_buckets,thal],hash_bucket_size=1000)
demo(feature_column,indicator_column(crossed_feature))
选择要使用的列,训练模型
feature_columns = []
# 数值列
for header in ['age','tresbps','chol','thalach','oldpeak','ca']:
feature_columns.append(feature_column.numeric_column(header))
# 分桶列
age_buckets = feature_column.bucketized_column(age, boundaries = [18,25,30,35,40,45,50,55,60,65])
feature_column.append(age_buckets)
# 分类列
thal =feature_column.categorical_column_with_vocabulary_list('thal',['fixed','normal','reversible'])
thal_one_hot = feature_column.indicator_column(thal)
feature_columns.append(thal_one_hot)
#嵌入列
thal_embedding = feature_column.embedding_column(thal,dimension =8)
feature_columns.append(thal_embedding)
# 组合列
crossed_feature = feature_column.crossed_column([age_buckets,thal],hash_bucket_size = 1000)
crossed_feature = feature_column.indicator_column(crossed_feature)
feature_columns.append(crossed_feature)
建立一个新的特征层
上面我们已经定义了特征列,需要使用密集特征(DenseFeatures)
层将特征列输入到Keras模型中。
feature_layer = tf.keras.layers.DenseFeatures(feature_columns)
训练数据集
batch_size = 32
train_ds = df_to_dataset(train, batch_size= batch_size)
val_ds = df_to_dataset(val, shuffle = False, batch_size=batch_size)
test_ds = df_to_dataset(test, shuffle = False, batch_size= batch_size)
创建、编译、训练模型
model = tf.keras.Sequential([
feature_layer,
layers.Dense(128, activation = 'relu'),
layers.Dense(128, activation = 'relu'),
layers.Dense(1, activation ='sigmoid')
])
model.compile(
optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'],
run_early=True)
model.fit(train_ds, validation_data= val_ds,epochs =5)