新版tensorflow内置了六款模型,包含线性回归模型和逻辑回归模型,能够使用这些模型非常方便快捷的实现自己的业务需求。但如果内置模型不能满足我们的业务需求时,就需要我们来自定义自己的模型。
如下图所示,预创建的 Estimator 是 tf.estimator.Estimator
基类的子类,
而自定义 Estimator 是 tf.estimator.Estimator 的实例:
模型函数(即 model_fn
)会实现机器学习算法。采用预创建的 Estimator 和自定义 Estimator 的唯一区别是:
您的模型函数可以实现各种算法,定义各种各样的隐藏层和指标。与输入函数一样,所有模型函数都必须接受一组标准输入参数并返回一组标准输出值。正如输入函数可以利用 Dataset API 一样,模型函数可以利用 Layers API 和 Metrics API。
在定义模型之前建议先对模型结构进行设计,假如我们需要解决鸢尾花分类的问题,需要使用下面的模型。
根据上图的顺序我们可以按照 输入层,隐藏层 , 和输出层的顺序来创建estimator。输入数据的处理与之前章节讨论的方式是一样的。
我们通过tf内置的读取文件的形式来读入数据,并对数据进行扩充(repeat),打乱(shuffle),以及批次处理,请参考如下代码:
# 读取鸢尾花的数据,使用tf直接读取文件,生成对应的输入数据集
import tensorflow as tf
ds = tf.data.TextLineDataset('iris_training.csv').skip(1)
# 指定csv文件的列名
COLUMNS = ['SepalLength', 'SepalWidth',
'PetalLength', 'PetalWidth',
'label']
# 指定csv各个字段的默认值
FIELD_DEFAULTS = [[0.0], [0.0], [0.0], [0.0], [0]]
def _parse_line(line):
# 将每一行解析到对应的字段当中
fields = tf.decode_csv(line, FIELD_DEFAULTS)
# 将字段值与列名组成一个字典
features = dict(zip(COLUMNS,fields))
#将特征值与标记列分开
label = features.pop('label')
return features, label
ds = ds.map(_parse_line)
def train_func(ds):
dataset = ds.shuffle(1000).repeat().batch(100)
return dataset.make_one_shot_iterator().get_next()
train_func(ds)
输出效果如下图所示:
在上面的代码中我们使用
dataset.make_one_shot_iterator().get_next()
来获取数据,可以看到数据被扩充到1000条放到输入管道中,此输入函数会构建可以生成批次 (features, labels)
对的输入管道,其中 features
是字典特征。
每次执行可以看到批次的变化,如下图所示。
使用下面代码创建特征列
feature_columns = [
tf.feature_column.numeric_column(name)
for name in COLUMNS[:-1]]
print(feature_columns)
输出效果如下图所示:
我们可以通过模型函数来构建模型构建模型的基本方法如下面代码所示:
classifier = tf.estimator.Estimator(
model_fn=my_model,# 制定本模型的模型参数
params={ # 模型的额外参数,会传递给模型参数的params
'feature_columns': feature_columns,
'hidden_units': [10, 10],
'n_classes': 3,
})
而model_fn 对应的my_model就是模型函数,其定义如下
def my_model_fn(
features, # 本批次的特征数据
labels, # 本批次的标记数据
mode, # tf.estimator.ModeKeys的一个实例,用以表示是要训练,评估,还是预测
params): # 模型额外参数
我们可以在my_model函数中创建对应的模型结构
model_fn
的第一行调用 tf.feature_column.input_layer
,以将特征字典和 feature_columns
转换为模型的输入,如下所示:
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
上面的行会应用特征列定义的转换,从而创建模型的输入层。
'hidden_units': [10, 10],
配置项里的这个配置已经说明,这个模型中包含两个隐藏层,每个隐藏层包含10个神经元,可以使用这个结构来创建隐藏层,请参照下面代码:
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
#定义输出层
logits = tf.layers.dense(net,params['n_classes'],activation=None)
对应的逻辑结构图如下所示:
对与计算出来的logic进行概率化处理这里使用softmax函数
请参照如下代码
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
#定义输出层
logits = tf.layers.dense(net,params['n_classes'],activation=None)
# 计算预测结果
predicted_classes = tf.argmax(logits, 1)
对于模型来讲可以是train, test, predict,其实本质上来讲test与train大体的执行方式相同,只是不进行梯度下降和模型存储。但predict则不同,他不需要计算loss与accuracy。所以在预测出结果之后避免进行不必要的代码征程,先检测一下是否是进行预测。请参照如下代码:
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
#定义输出层
logits = tf.layers.dense(net,params['n_classes'],activation=None)
# 计算预测结果
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
'class_ids': predicted_classes[:, tf.newaxis],
'probabilities': tf.nn.softmax(logits),
'logits': logits,
}
return tf.estimator.EstimatorSpec(mode, predictions=predictions)
剩下的两种方法都需要计算loss与accuracy,请参照如下代码
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
#定义输出层
logits = tf.layers.dense(net,params['n_classes'],activation=None)
# 计算预测结果
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
'class_ids': predicted_classes[:, tf.newaxis],
'probabilities': tf.nn.softmax(logits),
'logits': logits,
}
return tf.estimator.EstimatorSpec(mode, predictions=predictions)
# 计算代价.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
# 计算精确度.
accuracy = tf.metrics.accuracy(labels=labels,
predictions=predicted_classes,
name='acc_op')
metrics = {'accuracy': accuracy}
此时可以判断如果是评估eval可以进行数据返回
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
#定义输出层
logits = tf.layers.dense(net,params['n_classes'],activation=None)
# 计算预测结果
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
'class_ids': predicted_classes[:, tf.newaxis],
'probabilities': tf.nn.softmax(logits),
'logits': logits,
}
return tf.estimator.EstimatorSpec(mode, predictions=predictions)
# 计算代价.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
# 计算精确度.
accuracy = tf.metrics.accuracy(labels=labels,
predictions=predicted_classes,
name='acc_op')
metrics = {'accuracy': accuracy}
# 判断是否为评估
if mode == tf.estimator.ModeKeys.EVAL:
return tf.estimator.EstimatorSpec(
mode, loss=loss, eval_metric_ops=metrics)
在训练的模式下需要进行参数更新,所以最后进行判断
def my_model(features,labels,mode,params):
#定义输入层
net = tf.feature_column.input_layer(features,params['feature_columns'])
#定义隐藏层
for unit in params[hidden_units]:
net = tf.layers.dense(net,unit,activation=tf.nn.relu)
#定义输出层
logits = tf.layers.dense(net,params['n_classes'],activation=None)
# 计算预测结果
predicted_classes = tf.argmax(logits, 1)
if mode == tf.estimator.ModeKeys.PREDICT:
predictions = {
'class_ids': predicted_classes[:, tf.newaxis],
'probabilities': tf.nn.softmax(logits),
'logits': logits,
}
return tf.estimator.EstimatorSpec(mode, predictions=predictions)
# 计算代价.
loss = tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)
# 计算精确度.
accuracy = tf.metrics.accuracy(labels=labels,
predictions=predicted_classes,
name='acc_op')
metrics = {'accuracy': accuracy}
# 判断是否为评估
if mode == tf.estimator.ModeKeys.EVAL:
return tf.estimator.EstimatorSpec(
mode, loss=loss, eval_metric_ops=metrics)
# 如果是训练模式
assert mode == tf.estimator.ModeKeys.TRAIN
optimizer = tf.train.AdagradOptimizer(learning_rate=0.1)
train_op = optimizer.minimize(loss, global_step=tf.train.get_global_step())
return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=train_op)
可以看到新版tensorflow在创建自定义模型时,层次还是很清楚的。