方案一:多头模型
您可以使用多头 DNNEstimator 模型。这将 Flow 和 Visibility 视为两个独立的 softmax 分类目标,每个目标都有自己的一组类。我不得不修改load_csv_without_header
支持多个目标的辅助函数(这可能更清晰,但这不是这里的重点 - 请随意忽略它的细节)。
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
import csv
import collections
num_flow_classes = 4
num_visib_classes = 7
Dataset = collections.namedtuple('Dataset', ['data', 'target'])
def load_csv_without_header(fn, target_dtype, features_dtype, target_columns):
with gfile.Open(fn) as csv_file:
data_file = csv.reader(csv_file)
data = []
targets = {
target_cols: []
for target_cols in target_columns.keys()
}
for row in data_file:
cols = sorted(target_columns.items(), key=lambda tup: tup[1], reverse=True)
for target_col_name, target_col_i in cols:
targets[target_col_name].append(row.pop(target_col_i))
data.append(np.asarray(row, dtype=features_dtype))
targets = {
target_col_name: np.array(val, dtype=target_dtype)
for target_col_name, val in targets.items()
}
data = np.array(data)
return Dataset(data=data, target=targets)
feature_columns = [
tf.contrib.layers.real_valued_column("", dimension=1),
tf.contrib.layers.real_valued_column("", dimension=2),
]
head = tf.contrib.learn.multi_head([
tf.contrib.learn.multi_class_head(
num_flow_classes, label_name="Flow", head_name="Flow"),
tf.contrib.learn.multi_class_head(
num_visib_classes, label_name="Visibility", head_name="Visibility"),
])
classifier = tf.contrib.learn.DNNEstimator(
feature_columns=feature_columns,
hidden_units=[10, 20, 10],
model_dir="iris_model",
head=head,
)
def get_input_fn(filename):
def input_fn():
dataset = load_csv_without_header(
fn=filename,
target_dtype=np.int,
features_dtype=np.int,
target_columns={"Flow": 2, "Visibility": 3}
)
x = tf.constant(dataset.data)
y = {k: tf.constant(v) for k, v in dataset.target.items()}
return x, y
return input_fn
classifier.fit(input_fn=get_input_fn("tmp_train.csv"), steps=4000)
res = classifier.evaluate(input_fn=get_input_fn("tmp_test.csv"), steps=1)
print("Validation:", res)
选项 2:多标记头
如果您将 CSV 数据以逗号分隔,并保留一行可能具有的所有类的最后一列(以某些标记(例如空格)分隔),则可以使用以下代码:
import numpy as np
import tensorflow as tf
all_classes = ["0", "1", "2", "3", "4", "5", "6"]
def k_hot(classes_col, all_classes, delimiter=' '):
table = tf.contrib.lookup.index_table_from_tensor(
mapping=tf.constant(all_classes)
)
classes = tf.string_split(classes_col, delimiter)
ids = table.lookup(classes)
num_items = tf.cast(tf.shape(ids)[0], tf.int64)
num_entries = tf.shape(ids.indices)[0]
y = tf.SparseTensor(
indices=tf.stack([ids.indices[:, 0], ids.values], axis=1),
values=tf.ones(shape=(num_entries,), dtype=tf.int32),
dense_shape=(num_items, len(all_classes)),
)
y = tf.sparse_tensor_to_dense(y, validate_indices=False)
return y
def feature_engineering_fn(features, labels):
labels = k_hot(labels, all_classes)
return features, labels
feature_columns = [
tf.contrib.layers.real_valued_column("", dimension=1), # DayOfYear
tf.contrib.layers.real_valued_column("", dimension=2), # Temperature
]
classifier = tf.contrib.learn.DNNEstimator(
feature_columns=feature_columns,
hidden_units=[10, 20, 10],
model_dir="iris_model",
head=tf.contrib.learn.multi_label_head(n_classes=len(all_classes)),
feature_engineering_fn=feature_engineering_fn,
)
def get_input_fn(filename):
def input_fn():
dataset = tf.contrib.learn.datasets.base.load_csv_without_header(
filename=filename,
target_dtype="S100", # strings of length up to 100 characters
features_dtype=np.int,
target_column=-1
)
x = tf.constant(dataset.data)
y = tf.constant(dataset.target)
return x, y
return input_fn
classifier.fit(input_fn=get_input_fn("tmp_train.csv"), steps=4000)
res = classifier.evaluate(input_fn=get_input_fn("tmp_test.csv"), steps=1)
print("Validation:", res)
我们正在使用DNNEstimator
with a multi_label_head
,它使用 sigmoid 交叉熵而不是 softmax 交叉熵作为损失函数。这意味着每个输出单位/logit 都通过 sigmoid 函数传递,这给出了数据点属于该类的可能性,即这些类是独立计算的,并且不像 softmax 交叉熵那样是相互排斥的。这意味着您可以有 0 到len(all_classes)
为训练集中的每一行设置的类和最终预测。
另请注意,类表示为字符串(并且k_hot
转换为令牌索引),以便您可以在电子商务设置中使用任意类标识符,例如类别 UUID。如果第 3 列和第 4 列中的类别不同(流 ID 1!= 可见性 ID 1),您可以将列名称添加到每个类 ID 之前,例如
316,8,flow1 visibility4
285,-1,flow1 visibility4
326,8,flow2 visibility5
有关如何进行的说明k_hot
作品,请参阅我的另一个答案 https://stackoverflow.com/questions/45159133/does-tf-one-hot-supports-sparsetensor-as-indices-parameter/47657743#47657743。我决定使用k_hot
作为一个单独的函数(而不是直接定义它feature_engineering_fn
因为它是一个独特的功能,并且 TensorFlow 可能很快就会有类似的实用功能。
请注意,如果您现在使用前两列来预测最后两列,那么您的准确性肯定会下降,因为最后两列高度相关,并且使用其中一列将为您提供有关另一列的大量信息。实际上,您的代码仅使用了第三列,如果目标是预测第三列和第四列,那么无论如何这都是一种作弊。