作者: Khalid Salama
创建日期: 2020/12/31
最后修改: 2021/05/05
描述: 使用宽&深和深&交叉网络进行结构化数据分类。
本示例演示如何使用两种建模技术进行结构化数据分类:
请注意,此示例应在 TensorFlow 2.5 或更高版本下运行。
本示例使用来自 UCI 机器学习库的 Covertype 数据集。任务是根据制图变量预测森林覆盖类型。数据集包含 506,011 个实例,具有 12 个输入特征:10 个数值特征和 2 个分类特征。每个实例被分类为 7 个类别中的 1 个。
import os
# 只有 TensorFlow 后端支持字符串输入。
os.environ["KERAS_BACKEND"] = "tensorflow"
import math
import numpy as np
import pandas as pd
from tensorflow import data as tf_data
import keras
from keras import layers
首先,让我们从 UCI 机器学习库加载数据集到 Pandas DataFrame:
data_url = (
"https://archive.ics.uci.edu/ml/machine-learning-databases/covtype/covtype.data.gz"
)
raw_data = pd.read_csv(data_url, header=None)
print(f"数据集形状: {raw_data.shape}")
raw_data.head()
数据集形状: (581012, 55)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2596 | 51 | 3 | 258 | 0 | 510 | 221 | 232 | 148 | 6279 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 |
1 | 2590 | 56 | 2 | 212 | -6 | 390 | 220 | 235 | 151 | 6225 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 |
2 | 2804 | 139 | 9 | 268 | 65 | 3180 | 234 | 238 | 135 | 6121 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 |
3 | 2785 | 155 | 18 | 242 | 118 | 3090 | 238 | 238 | 122 | 6211 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 |
4 | 2595 | 45 | 2 | 153 | -1 | 391 | 220 | 234 | 150 | 6172 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 5 |
5 行 × 55 列
数据集中的两个类别特征是以二进制编码的。 我们将把这种数据集表示转换为典型表示,其中每个 类别特征表示为一个整数值。
soil_type_values = [f"soil_type_{idx+1}" for idx in range(40)]
wilderness_area_values = [f"area_type_{idx+1}" for idx in range(4)]
soil_type = raw_data.loc[:, 14:53].apply(
lambda x: soil_type_values[0::1][x.to_numpy().nonzero()[0][0]], axis=1
)
wilderness_area = raw_data.loc[:, 10:13].apply(
lambda x: wilderness_area_values[0::1][x.to_numpy().nonzero()[0][0]], axis=1
)
CSV_HEADER = [
"Elevation",
"Aspect",
"Slope",
"Horizontal_Distance_To_Hydrology",
"Vertical_Distance_To_Hydrology",
"Horizontal_Distance_To_Roadways",
"Hillshade_9am",
"Hillshade_Noon",
"Hillshade_3pm",
"Horizontal_Distance_To_Fire_Points",
"Wilderness_Area",
"Soil_Type",
"Cover_Type",
]
data = pd.concat(
[raw_data.loc[:, 0:9], wilderness_area, soil_type, raw_data.loc[:, 54]],
axis=1,
ignore_index=True,
)
data.columns = CSV_HEADER
# 将目标标签索引转换为从 0 到 6 的范围(总共有 7 个标签)。
data["Cover_Type"] = data["Cover_Type"] - 1
print(f"Dataset shape: {data.shape}")
data.head().T
数据集形状: (581012, 13)
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
Elevation | 2596 | 2590 | 2804 | 2785 | 2595 |
Aspect | 51 | 56 | 139 | 155 | 45 |
Slope | 3 | 2 | 9 | 18 | 2 |
Horizontal_Distance_To_Hydrology | 258 | 212 | 268 | 242 | 153 |
Vertical_Distance_To_Hydrology | 0 | -6 | 65 | 118 | -1 |
Horizontal_Distance_To_Roadways | 510 | 390 | 3180 | 3090 | 391 |
Hillshade_9am | 221 | 220 | 234 | 238 | 220 |
Hillshade_Noon | 232 | 235 | 238 | 238 | 234 |
Hillshade_3pm | 148 | 151 | 135 | 122 | 150 |
Horizontal_Distance_To_Fire_Points | 6279 | 6225 | 6121 | 6211 | 6172 |
Wilderness_Area | area_type_1 | area_type_1 | area_type_1 | area_type_1 | area_type_1 |
Soil_Type | soil_type_29 | soil_type_29 | soil_type_12 | soil_type_30 | soil_type_29 |
Cover_Type | 4 | 4 | 1 | 1 | 4 |
数据框的形状显示每个样本有 13 列 (12 个特征和 1 个目标标签)。
让我们将数据分成训练集(85%)和测试集(15%)。
train_splits = []
test_splits = []
for _, group_data in data.groupby("Cover_Type"):
random_selection = np.random.rand(len(group_data.index)) <= 0.85
train_splits.append(group_data[random_selection])
test_splits.append(group_data[~random_selection])
train_data = pd.concat(train_splits).sample(frac=1).reset_index(drop=True)
test_data = pd.concat(test_splits).sample(frac=1).reset_index(drop=True)
print(f"Train split size: {len(train_data.index)}")
print(f"Test split size: {len(test_data.index)}")
训练集大小: 493323
测试集大小: 87689
接下来,将训练和测试数据存储在单独的 CSV 文件中。
train_data_file = "train_data.csv"
test_data_file = "test_data.csv"
train_data.to_csv(train_data_file, index=False) # 将训练数据保存为CSV文件
test_data.to_csv(test_data_file, index=False) # 将测试数据保存为CSV文件
在这里,我们定义数据集的元数据,这将有助于读取和解析数据为输入特征,并根据它们的类型对输入特征进行编码。
TARGET_FEATURE_NAME = "Cover_Type"
TARGET_FEATURE_LABELS = ["0", "1", "2", "3", "4", "5", "6"]
NUMERIC_FEATURE_NAMES = [
"Aspect",
"Elevation",
"Hillshade_3pm",
"Hillshade_9am",
"Hillshade_Noon",
"Horizontal_Distance_To_Fire_Points",
"Horizontal_Distance_To_Hydrology",
"Horizontal_Distance_To_Roadways",
"Slope",
"Vertical_Distance_To_Hydrology",
]
CATEGORICAL_FEATURES_WITH_VOCABULARY = {
"Soil_Type": list(data["Soil_Type"].unique()),
"Wilderness_Area": list(data["Wilderness_Area"].unique()),
}
CATEGORICAL_FEATURE_NAMES = list(CATEGORICAL_FEATURES_WITH_VOCABULARY.keys())
FEATURE_NAMES = NUMERIC_FEATURE_NAMES + CATEGORICAL_FEATURE_NAMES
COLUMN_DEFAULTS = [
[0] if feature_name in NUMERIC_FEATURE_NAMES + [TARGET_FEATURE_NAME] else ["NA"]
for feature_name in CSV_HEADER
]
NUM_CLASSES = len(TARGET_FEATURE_LABELS)
接下来,让我们定义一个输入函数,读取和解析文件,然后将特征和标签转换为一个 tf.data.Dataset
以用于训练或评估。
def get_dataset_from_csv(csv_file_path, batch_size, shuffle=False):
dataset = tf_data.experimental.make_csv_dataset(
csv_file_path,
batch_size=batch_size,
column_names=CSV_HEADER,
column_defaults=COLUMN_DEFAULTS,
label_name=TARGET_FEATURE_NAME,
num_epochs=1,
header=True,
shuffle=shuffle,
)
return dataset.cache()
在这里,我们配置参数并实现给定模型进行训练和评估实验的过程。
learning_rate = 0.001
dropout_rate = 0.1
batch_size = 265
num_epochs = 50
hidden_units = [32, 32]
def run_experiment(model):
model.compile(
optimizer=keras.optimizers.Adam(learning_rate=learning_rate),
loss=keras.losses.SparseCategoricalCrossentropy(),
metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
train_dataset = get_dataset_from_csv(train_data_file, batch_size, shuffle=True)
test_dataset = get_dataset_from_csv(test_data_file, batch_size)
print("开始训练模型...")
history = model.fit(train_dataset, epochs=num_epochs)
print("模型训练完成")
_, accuracy = model.evaluate(test_dataset, verbose=0)
print(f"测试准确率: {round(accuracy * 100, 2)}%")
现在,将模型的输入定义为一个字典,其中键是特征名称,值是具有相应特征形状和数据类型的 keras.layers.Input
张量。
def create_model_inputs():
inputs = {}
for feature_name in FEATURE_NAMES:
if feature_name in NUMERIC_FEATURE_NAMES:
inputs[feature_name] = layers.Input(
name=feature_name, shape=(), dtype="float32"
)
else:
inputs[feature_name] = layers.Input(
name=feature_name, shape=(), dtype="string"
)
return inputs
我们创建输入特征的两种表示形式:稀疏和密集:
1. 在 稀疏 表示中,分类特征使用 CategoryEncoding
层进行独热编码。此表示形式对模型“记住”特定特征值以进行某些预测是有用的。
2. 在 密集 表示中,分类特征使用 Embedding
层进行低维嵌入编码。此表示形式帮助模型对未见过的特征组合进行良好的“泛化”。
def encode_inputs(inputs, use_embedding=False):
encoded_features = []
for feature_name in inputs:
if feature_name in CATEGORICAL_FEATURE_NAMES:
vocabulary = CATEGORICAL_FEATURES_WITH_VOCABULARY[feature_name]
# 创建查找表以将字符串值转换为整数索引。
# 因为我们不使用掩码令牌,也不期待任何超出词汇表
# (oov) 的令牌,所以我们将 mask_token 设置为 None, num_oov_indices 设置为 0。
lookup = layers.StringLookup(
vocabulary=vocabulary,
mask_token=None,
num_oov_indices=0,
output_mode="int" if use_embedding else "binary",
)
if use_embedding:
# 将字符串输入值转换为整数索引。
encoded_feature = lookup(inputs[feature_name])
embedding_dims = int(math.sqrt(len(vocabulary)))
# 创建具有指定维度的嵌入层。
embedding = layers.Embedding(
input_dim=len(vocabulary), output_dim=embedding_dims
)
# 将索引值转换为嵌入表示。
encoded_feature = embedding(encoded_feature)
else:
# 将字符串输入值转换为独热编码。
encoded_feature = lookup(
keras.ops.expand_dims(inputs[feature_name], -1)
)
else:
# 直接使用数值特征。
encoded_feature = keras.ops.expand_dims(inputs[feature_name], -1)
encoded_features.append(encoded_feature)
all_features = layers.concatenate(encoded_features)
return all_features
在第一项实验中,让我们创建一个多层前馈网络,其中分类特征经过独热编码。
def create_baseline_model():
inputs = create_model_inputs()
features = encode_inputs(inputs)
for units in hidden_units:
features = layers.Dense(units)(features)
features = layers.BatchNormalization()(features)
features = layers.ReLU()(features)
features = layers.Dropout(dropout_rate)(features)
outputs = layers.Dense(units=NUM_CLASSES, activation="softmax")(features)
model = keras.Model(inputs=inputs, outputs=outputs)
return model
baseline_model = create_baseline_model()
keras.utils.plot_model(baseline_model, show_shapes=True, rankdir="LR")
/Users/fchollet/Library/Python/3.10/lib/python/site-packages/numpy/core/numeric.py:2468: FutureWarning: 元素逐个比较失败;返回标量,但将来会执行逐个比较
return bool(asarray(a1 == a2).all())
让我们运行它:
run_experiment(baseline_model)
开始训练模型...
Epoch 1/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 6s 3ms/step - loss: 1.0713 - sparse_categorical_accuracy: 0.5634
Epoch 2/50
179/1862 ━━━━━━━━━━━━━━━━━━━━ 1s 848us/step - loss: 0.7473 - sparse_categorical_accuracy: 0.6840
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/contextlib.py:153: UserWarning: 您的输入数据耗尽;中断训练。确保您的数据集或生成器能够生成至少 `steps_per_epoch * epochs` 批次。您可能需要在构建数据集时使用 `.repeat()` 函数。
self.gen.throw(typ, value, traceback)
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 904us/step - loss: 0.7386 - sparse_categorical_accuracy: 0.6866
Epoch 3/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 909us/step - loss: 0.7135 - sparse_categorical_accuracy: 0.6958
Epoch 4/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 878us/step - loss: 0.6975 - sparse_categorical_accuracy: 0.7051
Epoch 5/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 941us/step - loss: 0.6876 - sparse_categorical_accuracy: 0.7089
Epoch 6/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 936us/step - loss: 0.6848 - sparse_categorical_accuracy: 0.7106
Epoch 7/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 934us/step - loss: 0.7165 - sparse_categorical_accuracy: 0.6969
Epoch 8/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 924us/step - loss: 0.6979 - sparse_categorical_accuracy: 0.7053
Epoch 9/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 967us/step - loss: 0.6913 - sparse_categorical_accuracy: 0.7088
Epoch 10/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 975us/step - loss: 0.6807 - sparse_categorical_accuracy: 0.7124
Epoch 11/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 987us/step - loss: 0.6829 - sparse_categorical_accuracy: 0.7110
Epoch 12/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 917us/step - loss: 0.6823 - sparse_categorical_accuracy: 0.7109
Epoch 13/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 879us/step - loss: 0.6658 - sparse_categorical_accuracy: 0.7175
Epoch 14/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 948us/step - loss: 0.6677 - sparse_categorical_accuracy: 0.7170
Epoch 15/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 866us/step - loss: 0.6695 - sparse_categorical_accuracy: 0.7130
Epoch 16/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 860us/step - loss: 0.6847 - sparse_categorical_accuracy: 0.7074
Epoch 17/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 853us/step - loss: 0.6660 - sparse_categorical_accuracy: 0.7174
Epoch 18/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 855us/step - loss: 0.6620 - sparse_categorical_accuracy: 0.7184
Epoch 19/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 900us/step - loss: 0.6642 - sparse_categorical_accuracy: 0.7163
Epoch 20/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 969us/step - loss: 0.6614 - sparse_categorical_accuracy: 0.7167
Epoch 21/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 988us/step - loss: 0.6560 - sparse_categorical_accuracy: 0.7199
Epoch 22/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 969us/step - loss: 0.6559 - sparse_categorical_accuracy: 0.7201
Epoch 23/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 868us/step - loss: 0.6514 - sparse_categorical_accuracy: 0.7217
Epoch 24/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 925us/step - loss: 0.6509 - sparse_categorical_accuracy: 0.7222
Epoch 25/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 879us/step - loss: 0.6464 - sparse_categorical_accuracy: 0.7233
Epoch 26/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 898us/step - loss: 0.6442 - sparse_categorical_accuracy: 0.7237
Epoch 27/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 842us/step - loss: 0.6476 - sparse_categorical_accuracy: 0.7210
Epoch 28/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 815us/step - loss: 0.6427 - sparse_categorical_accuracy: 0.7247
Epoch 29/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 837us/step - loss: 0.6414 - sparse_categorical_accuracy: 0.7244
Epoch 30/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 865us/step - loss: 0.6408 - sparse_categorical_accuracy: 0.7256
Epoch 31/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 845us/step - loss: 0.6378 - sparse_categorical_accuracy: 0.7269
Epoch 32/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 842us/step - loss: 0.6432 - sparse_categorical_accuracy: 0.7235
Epoch 33/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 905us/step - loss: 0.6482 - sparse_categorical_accuracy: 0.7226
Epoch 34/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - loss: 0.6586 - sparse_categorical_accuracy: 0.7191
Epoch 35/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 958us/step - loss: 0.6511 - sparse_categorical_accuracy: 0.7215
Epoch 36/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 910us/step - loss: 0.6571 - sparse_categorical_accuracy: 0.7217
Epoch 37/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 897us/step - loss: 0.6451 - sparse_categorical_accuracy: 0.7253
Epoch 38/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 846us/step - loss: 0.6455 - sparse_categorical_accuracy: 0.7254
Epoch 39/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 907us/step - loss: 0.6722 - sparse_categorical_accuracy: 0.7131
Epoch 40/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1000us/step - loss: 0.6393 - sparse_categorical_accuracy: 0.7282
Epoch 41/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 872us/step - loss: 0.6804 - sparse_categorical_accuracy: 0.7078
Epoch 42/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 884us/step - loss: 0.6657 - sparse_categorical_accuracy: 0.7135
Epoch 43/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 960us/step - loss: 0.6557 - sparse_categorical_accuracy: 0.7180
Epoch 44/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 870us/step - loss: 0.6671 - sparse_categorical_accuracy: 0.7115
Epoch 45/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 871us/step - loss: 0.6730 - sparse_categorical_accuracy: 0.7069
Epoch 46/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 875us/step - loss: 0.6669 - sparse_categorical_accuracy: 0.7105
Epoch 47/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 847us/step - loss: 0.6634 - sparse_categorical_accuracy: 0.7129
Epoch 48/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 846us/step - loss: 0.6625 - sparse_categorical_accuracy: 0.7137
Epoch 49/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 824us/step - loss: 0.6596 - sparse_categorical_accuracy: 0.7146
Epoch 50/50
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 833us/step - loss: 0.6714 - sparse_categorical_accuracy: 0.7106
模型训练完成
测试准确率:69.5%
基线线性模型实现了约76%的测试准确率。
在第二个实验中,我们创建了一个宽深模型。模型的宽部分是一个线性模型,而深部分是一个多层前馈网络。
在模型的宽部分使用输入特征的稀疏表示,在模型的深部分使用输入特征的密集表示。
请注意,每个输入特征在模型的两个部分中以不同的表示形式贡献。
def create_wide_and_deep_model():
inputs = create_model_inputs()
wide = encode_inputs(inputs)
wide = layers.BatchNormalization()(wide)
deep = encode_inputs(inputs, use_embedding=True)
for units in hidden_units:
deep = layers.Dense(units)(deep)
deep = layers.BatchNormalization()(deep)
deep = layers.ReLU()(deep)
deep = layers.Dropout(dropout_rate)(deep)
merged = layers.concatenate([wide, deep])
outputs = layers.Dense(units=NUM_CLASSES, activation="softmax")(merged)
model = keras.Model(inputs=inputs, outputs=outputs)
return model
wide_and_deep_model = create_wide_and_deep_model()
keras.utils.plot_model(wide_and_deep_model, show_shapes=True, rankdir="LR")
/Users/fchollet/Library/Python/3.10/lib/python/site-packages/numpy/core/numeric.py:2468: FutureWarning: 元素按位比较失败;返回标量而不是未来将执行元素按位比较
return bool(asarray(a1 == a2).all())
让我们运行它:
run_experiment(wide_and_deep_model)
开始训练模型...
第 1/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 5s 2ms/step - 损失: 0.8979 - 稀疏类别准确率: 0.6386
第 2/50 轮
128/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.6317 - 稀疏类别准确率: 0.7302
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/contextlib.py:153: 用户警告: 你的输入数据耗尽;中断训练。确保你的数据集或生成器可以生成至少 `steps_per_epoch * epochs` 批次。你可能需要在构建数据集时使用 `.repeat()` 函数。
self.gen.throw(typ, value, traceback)
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.6290 - 稀疏类别准确率: 0.7295
第 3/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.6130 - 稀疏类别准确率: 0.7350
第 4/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.6029 - 稀疏类别准确率: 0.7397
第 5/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/step - 损失: 0.6010 - 稀疏类别准确率: 0.7397
第 6/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5924 - 稀疏类别准确率: 0.7445
第 7/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5917 - 稀疏类别准确率: 0.7442
第 8/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5945 - 稀疏类别准确率: 0.7438
第 9/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5933 - 稀疏类别准确率: 0.7443
第 10/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5862 - 稀疏类别准确率: 0.7481
第 11/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5809 - 稀疏类别准确率: 0.7507
第 12/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5777 - 稀疏类别准确率: 0.7519
第 13/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5736 - 稀疏类别准确率: 0.7534
第 14/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5716 - 稀疏类别准确率: 0.7545
第 15/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5736 - 稀疏类别准确率: 0.7537
第 16/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5712 - 稀疏类别准确率: 0.7559
第 17/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5683 - 稀疏类别准确率: 0.7564
第 18/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5666 - 稀疏类别准确率: 0.7569
第 19/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5652 - 稀疏类别准确率: 0.7575
第 20/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5634 - 稀疏类别准确率: 0.7583
第 21/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5677 - 稀疏类别准确率: 0.7563
第 22/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5651 - 稀疏类别准确率: 0.7578
第 23/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5628 - 稀疏类别准确率: 0.7586
第 24/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5619 - 稀疏类别准确率: 0.7593
第 25/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5603 - 稀疏类别准确率: 0.7589
第 26/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5644 - 稀疏类别准确率: 0.7585
第 27/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5592 - 稀疏类别准确率: 0.7604
第 28/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5571 - 稀疏类别准确率: 0.7616
第 29/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5556 - 稀疏类别准确率: 0.7629
第 30/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5538 - 稀疏类别准确率: 0.7640
第 31/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5535 - 稀疏类别准确率: 0.7635
第 32/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5521 - 稀疏类别准确率: 0.7645
第 33/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5505 - 稀疏类别准确率: 0.7648
第 34/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5494 - 稀疏类别准确率: 0.7657
第 35/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5496 - 稀疏类别准确率: 0.7660
第 36/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5488 - 稀疏类别准确率: 0.7673
第 37/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5471 - 稀疏类别准确率: 0.7668
第 38/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5474 - 稀疏类别准确率: 0.7673
第 39/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5457 - 稀疏类别准确率: 0.7674
第 40/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5452 - 稀疏类别准确率: 0.7689
第 41/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5448 - 稀疏类别准确率: 0.7679
第 42/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/step - 损失: 0.5442 - 稀疏类别准确率: 0.7692
第 43/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5436 - 稀疏类别准确率: 0.7701
第 44/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5419 - 稀疏类别准确率: 0.7706
第 45/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5432 - 稀疏类别准确率: 0.7691
第 46/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5406 - 稀疏类别准确率: 0.7708
第 47/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5412 - 稀疏类别准确率: 0.7701
第 48/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5400 - 稀疏类别准确率: 0.7701
第 49/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5416 - 稀疏类别准确率: 0.7699
第 50/50 轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/step - 损失: 0.5403 - 稀疏类别准确率: 0.7701
模型训练完成
测试准确率: 79.04%
宽深模型实现了约79%的测试准确率。
在第三个实验中,我们创建了一个深度与交叉模型。该模型的深度部分与之前实验中创建的深度部分相同。交叉部分的关键思想是以高效的方式应用显式特征交叉,其中交叉特征的程度随着层深度的增加而增加。
def create_deep_and_cross_model():
inputs = create_model_inputs()
x0 = encode_inputs(inputs, use_embedding=True)
cross = x0
for _ in hidden_units:
units = cross.shape[-1]
x = layers.Dense(units)(cross)
cross = x0 * x + cross
cross = layers.BatchNormalization()(cross)
deep = x0
for units in hidden_units:
deep = layers.Dense(units)(deep)
deep = layers.BatchNormalization()(deep)
deep = layers.ReLU()(deep)
deep = layers.Dropout(dropout_rate)(deep)
merged = layers.concatenate([cross, deep])
outputs = layers.Dense(units=NUM_CLASSES, activation="softmax")(merged)
model = keras.Model(inputs=inputs, outputs=outputs)
return model
deep_and_cross_model = create_deep_and_cross_model()
keras.utils.plot_model(deep_and_cross_model, show_shapes=True, rankdir="LR")
/Users/fchollet/Library/Python/3.10/lib/python/site-packages/numpy/core/numeric.py:2468: FutureWarning: 元素级比较失败;返回标量,但未来将执行元素级比较
return bool(asarray(a1 == a2).all())
让我们运行它:
run_experiment(deep_and_cross_model)
开始训练模型...
第1/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 5s 2ms/步 - 损失: 0.9221 - 稀疏分类准确率: 0.6235
第2/50轮
116/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.6388 - 稀疏分类准确率: 0.7257
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/contextlib.py:153: 用户警告: 您的输入数据耗尽; 中断训练。确保您的数据集或生成器可以生成至少 `steps_per_epoch * epochs` 批次。构建数据集时,您可能需要使用 `.repeat()` 函数。
self.gen.throw(typ, value, traceback)
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 2ms/步 - 损失: 0.6271 - 稀疏分类准确率: 0.7316
第3/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/步 - 损失: 0.6023 - 稀疏分类准确率: 0.7403
第4/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5896 - 稀疏分类准确率: 0.7453
第5/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5899 - 稀疏分类准确率: 0.7438
第6/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5960 - 稀疏分类准确率: 0.7421
第7/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5813 - 稀疏分类准确率: 0.7481
第8/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5748 - 稀疏分类准确率: 0.7500
第9/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5743 - 稀疏分类准确率: 0.7502
第10/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5739 - 稀疏分类准确率: 0.7506
第11/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5673 - 稀疏分类准确率: 0.7540
第12/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5649 - 稀疏分类准确率: 0.7561
第13/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/步 - 损失: 0.5651 - 稀疏分类准确率: 0.7548
第14/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5618 - 稀疏分类准确率: 0.7563
第15/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5599 - 稀疏分类准确率: 0.7571
第16/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5568 - 稀疏分类准确率: 0.7585
第17/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5556 - 稀疏分类准确率: 0.7592
第18/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5544 - 稀疏分类准确率: 0.7595
第19/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5533 - 稀疏分类准确率: 0.7603
第20/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5532 - 稀疏分类准确率: 0.7597
第21/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5531 - 稀疏分类准确率: 0.7602
第22/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5516 - 稀疏分类准确率: 0.7608
第23/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/步 - 损失: 0.5503 - 稀疏分类准确率: 0.7611
第24/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5492 - 稀疏分类准确率: 0.7619
第25/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5482 - 稀疏分类准确率: 0.7623
第26/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5464 - 稀疏分类准确率: 0.7635
第27/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5483 - 稀疏分类准确率: 0.7625
第28/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/步 - 损失: 0.5654 - 稀疏分类准确率: 0.7555
第29/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5545 - 稀疏分类准确率: 0.7593
第30/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5512 - 稀疏分类准确率: 0.7603
第31/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5493 - 稀疏分类准确率: 0.7616
第32/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5485 - 稀疏分类准确率: 0.7627
第33/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5593 - 稀疏分类准确率: 0.7588
第34/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5536 - 稀疏分类准确率: 0.7608
第35/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5537 - 稀疏分类准确率: 0.7612
第36/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5518 - 稀疏分类准确率: 0.7621
第37/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5502 - 稀疏分类准确率: 0.7618
第38/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5537 - 稀疏分类准确率: 0.7597
第39/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5526 - 稀疏分类准确率: 0.7609
第40/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5508 - 稀疏分类准确率: 0.7608
第41/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5495 - 稀疏分类准确率: 0.7613
第42/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 3s 1ms/步 - 损失: 0.5478 - 稀疏分类准确率: 0.7625
第43/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5471 - 稀疏分类准确率: 0.7629
第44/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5462 - 稀疏分类准确率: 0.7640
第45/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5458 - 稀疏分类准确率: 0.7633
第46/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5466 - 稀疏分类准确率: 0.7635
第47/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5492 - 稀疏分类准确率: 0.7633
第48/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5474 - 稀疏分类准确率: 0.7639
第49/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5452 - 稀疏分类准确率: 0.7645
第50/50轮
1862/1862 ━━━━━━━━━━━━━━━━━━━━ 2s 1ms/步 - 损失: 0.5446 - 稀疏分类准确率: 0.7663
模型训练完成
测试准确率: 77.98%
深度和交叉模型实现了约81%的测试准确率。
您可以使用Keras预处理层轻松处理具有不同编码机制的类别特征,包括独热编码和特征嵌入。此外,不同的模型架构——如宽模型、深度模型和交叉网络——在不同数据集特性方面具有不同的优势。您可以独立使用它们或将它们结合起来,以为您的数据集实现最佳结果。