为机器学习初学者准备的预创建估计函数

本文源自Google TensorFlow快速入门教程 GET STARTED. 文章介绍了如何利用机器学习对鸢尾花进行分类.


鸢尾花分类问题.

文章利用The MNIST Database中的一组有120个鸢尾花数据的数据集进行训练. 数据集中的数据如下图:


前四项分别是花萼和花瓣的长宽, 称为特征值. 最后一项是鸢尾花的类型, 称为标签. 我们利用这样的一个带标签的数据集进行监督学习, 最后用特征值进行预测得出标签.
标签中0, 1, 2分别代表不同种类的鸢尾花.

  • 0 代表 Iris segosa
  • 1 代表 Iris versicolor
  • 2 代表 Iris virginica

模型和训练

模型是连接特征和标签的容器. 比如在文章中, 模型连接着鸢尾花的测量数据和鸢尾花的类型. 有只用几行数学公式就能表示的简单模型, 也有一些非常复杂的机器学习模型, 其中包含着大量参数, 公式等等.

以往我们需要耗费大量的时间去设计模型, 通过各种参数, 标签, 语句来设计时间. 现在我们利用机器学习方法, 如果选择了合适的机器学习方法, 输入足够的数据, 程序就能替你找到特征数据与标签之间的联系.

机器学习可以大致的分为: 监督学习, 非监督学习, 强化学习.

  • 监督学习是利用一组有标签的数据进行学习, 程序通过输入的特征数据和标签继续训练, 最终达到要求的预测精度. 鸢尾花问题就是一个监督学习问题.
  • 非监督学习和监督学习的区别在于, 所有的数据都是无标签的. 程序自主的利用一系列无标签的数据进行学习.
  • 强化学习是一个循环往复的训练过程, 就像猫迷宫那样, 通过不停的碰壁, 再不断的获得新的数据进行学习.

TensorFlow程序栈


TensorFlow提供了不同层级的接口, 作为TensorFlow的初学者, 我们首先关注EstimatorsDatasets这两个API.

获取实例程序

回到鸢尾花问题. Google提供了示例代码供初学者进行学习, 鸢尾花问题和房价预测问题是两个非常常见的机器学习入门示例问题. 源码放在了GitHub.

  1. 安装TensorFlow:
    $ pip install tensorflow
  2. 安装Pandas:
    $ pip install pandas
  3. 从GitHub拉代码:
    $ git clone https://github.com/tensorflow/models
  4. 进入get_started目录, 获取premade_estimator.py文件:
    $ cd models/samples/core/get_started/

尝试运行一下premade_estimator.py文件, 会输出类似如下的信息:

鸢尾花问题的机器学习程序主要包括以下几个步骤:

  • 导入并解析数据集
  • 为数据创建特征列
  • 选择模型
  • 训练模型
  • 评估模型效果
  • 利用模型进行预测
导入并解析数据集

程序需要两个数据集, 训练数据集用来对模型进行训练, 测试集用来评估训练的模型效果.

  • http://download.tensorflow.org/data/iris_training.csv
  • http://download.tensorflow.org/data/iris_test.csv
    当我们使用一个数据集做机器学习的时候, 一般会把数据集分成训练集和测试集两部分. 训练集越大训练效果越好, 测试集越大测试准确性越高. 并且训练集和测试集必须相互独立, 不能把训练过的数据再用于测试, 这样会导致测试结果不准确.
    示例代码中, premade_estimator.py文件的数据导入和解析在同目录下 iris_data.py文件.
TRAIN_URL = "http://download.tensorflow.org/data/iris_training.csv"
TEST_URL = "http://download.tensorflow.org/data/iris_test.csv"

CSV_COLUMN_NAMES = ['SepalLength', 'SepalWidth',
                    'PetalLength', 'PetalWidth', 'Species']

...

def load_data(label_name='Species'):
    """Parses the csv file in TRAIN_URL and TEST_URL."""

    # 创建训练数据集的本地备份
    train_path = tf.keras.utils.get_file(fname=TRAIN_URL.split('/')[-1],
                                         origin=TRAIN_URL)
    # 变量train_path现在包含路径: ~/.keras/datasets/iris_training.csv

    # 解析本地 CSV 文件
    train = pd.read_csv(filepath_or_buffer=train_path,
                        names=CSV_COLUMN_NAMES,  #数据列名称
                        header=0  #忽略CSV文件中的第一行
                       )
    # 变量train现在包含一个pandas DataFrame(pandas数据结构)
    # 这个数据结构类似一个表格

    # 1. 指定pandas数据结构的最右标签为训练标签
    # 2. 从pandas数据结构中删除指定为训练标签的那个数据
    # 3. 指定剩余的pandas数据结构的数据为训练特征数据
    train_features, train_label = train, train.pop(label_name)

    # 类似上边训练数据集的处理方法, 对测试集进行处理
    test_path = tf.keras.utils.get_file(TEST_URL.split('/')[-1], TEST_URL)
    test = pd.read_csv(test_path, names=CSV_COLUMN_NAMES, header=0)
    test_features, test_label = test, test.pop(label_name)

    # 返回四个pandas DataFrames.
    return (train_features, train_label), (test_features, test_label)

Keras是一个非常流行的开源机器学习库, tf.keras是TensorFlow里Keras库的实现, 在本文的示例代码中仅使用了tf.keras.utils.get_file方法, 用来从远程获取CSV文件.
调用load_data()返回两组包含(train_feature, train_label)的元组:

# 调用load_data()方法解析CSV文件
    (train_feature, train_label), (test_feature, test_label) = load_data()

Pandas是TensorFlow中非常依赖的一个科学计算的第三方库. load_data方法中返回的(train_feature, train_label)就是从Pandas的DataFrame数据结构中获取的. Pandas的DataFrame数据结构类似一个表格:

创建特征列数据

特征列数据用来描述模型如何处理特征数据, 在示例代码中, 调用tf.feature_column.numeric_column函数来定义特征列, 使用Label作为key来定义特征列.

#为所有的特征生成特征列
my_feature_columns = []
for key in train_x.keys():
    my_feature_columns.append(tf.feature_column.numeric_column(key=key))

Label较少时, 直接用列表定义, 更清晰易于理解:

my_feature_columns = [
    tf.feature_column.numeric_column(key='SepalLength'),
    tf.feature_column.numeric_column(key='SepalWidth'),
    tf.feature_column.numeric_column(key='PetalLength'),
    tf.feature_column.numeric_column(key='PetalWidth')
]
选择模型

我们需要为训练在大量的模型种类中选择一个合适的模型, 在示例中, 我们采用神经网络来解决鸢尾花问题. 神经网络是一个高度结构化的图, 包含一或多层隐藏层, 每一次又包含n个神经元, 神经网络可以通过训练找到特征和标签之间复杂的关系. 神经网络可以分为很多种类, 鸢尾花问题中我们采用一个全连接神经网络, 所谓全连接神经网络(fully connected neural network)即是指第n层的所有神经元都有来自第n-1层的每一个神经元的输入.

有三层隐藏层的神经网络

TensorFlow提供了两种方式来指定模型, 创建Estimator类 : 预创建的Estimator和自定义的Estimator.
鸢尾花问题使用了预创建的tf.estimator.DNNClassifier分类器. 这个分类器会创建一个用于分类的神经网络. 下列代码用于实例化一个DNNClassifier:

classifier = tf.estimator.DNNClassifier(
    feature_columns=my_feature_columns,
    hidden_units=[10, 10], #定义隐藏层的神经元数量, 这里表示两层隐藏层: 第一层有10个神经元, 第二次有十个神经元
    n_classes=3) #定义输出预测数量, 鸢尾花有三种类型, 所以n_classes=3

和很多机器学习问题一样, 选择合适的隐藏层层数, 合适的神经元数量需要大量的相关知识和机器学习实践经验, 取决于需要解决的问题的实际情况和数据集的大小. 一般来说, 更多的层数, 更多的神经元数量可以建立一个更高效的神经网络, 但是同时需要更大量的数据来进行训练.
tf.estimator.DNNClassifier的构造器提供了一个名为optimizer的可选参数,优化器 (optimizer)定义了模型的训练方法. 示例代码中没有定义, 但是在实际应用中, 优化器和学习速率(learning rate)是非常重要的参数.

训练模型

实例化tf.estimator.DNNClassifier之后, 我们就有了一个框架, 训练模型我们还需要让数据通过模型进行训练. 调用classifiertrain方法进行训练模型.

classifier.train(
    input_fn=lambda:train_input_fn(train_feature, train_label, args.batch_size),
    steps=args.train_steps) 

step定义了训练的迭代次数, 迭代次数越多训练时间越长. 但是,更多的迭代次数并不一定就会获得更好的训练效果, 迭代次数是TensorFlow里的一个超参数, 和选择模型一样, 迭代次数需要丰富的知识和经验才能选择一个合适的迭代次数.
input_fn方法用于指定用于训练的数据, input_fn的定义如下:

def train_input_fn(features, labels, batch_size):
  • train_feature是一个Python的字典:
    • key是特征值的名字
    • value是一个包含数据集中数据的数组
  • train_label是一个包含训练集中标签的数组
  • args.batch_size是一个定义批次规模的int值batch size.
    train_input_fn方法依赖于TensorFlow的Dataset API, 是Dataset里面的一个基础方法, 下面方法把原始数据转换成用于TensorFlow可用数据的高级API.
 dataset = tf.data.Dataset.from_tensor_slices((dict(features), labels))

用于训练的数据如果是随机的会得到更好训练效果, 调用tf.data.Dataset.shuffle方法对数据进行乱序化, 把buffer_size设置为大于120, 即大于数据总数, 以保证所有的数据都可以乱序化.
训练期间, 数据多次通过模型进行处理. 通过设置tf.data.Dataset.repeat函数, 并且不声明参数来保证迭代的次数满足训练的需求.

 dataset = dataset.shuffle(buffer_size=1000).repeat(count=None).batch(batch_size)

train方法一次处理一定数量的数据, 设置 batch size为100, 表明batch方法包含100个数据案例. 合适的batch size取决于解决问题的具体情况. 按照经验来说, 更小的批次规模会有更好训练效果.
下面这条代码会返回一个批次的数据给调用者:

return dataset.make_one_shot_iterator().get_next()

评估模型

评估模型效果, 就是利用一组测试数据, 让训练后的模型来预测结果, 然后再对比预测结果和实际结果来评估模型. 下图是一组准确率为80%的预测数据:


不同的评估函数都提供了方法用于评估训练模型. 示例代码中调用evaluate方法来评估模型.

# 评估模型
eval_result = classifier.evaluate(
    input_fn=lambda:eval_input_fn(test_x, test_y, args.batch_size))

print('\nTest set accuracy: {accuracy:0.3f}\n'.format(**eval_result))

调用classifier.evaluate类似于调用classifier.train, 不同点在于两者必须使用不同的数据集. eval_input_fn函数提供用于评估的数据集:

def eval_input_fn(features, labels=None, batch_size=None):
    """An input function for evaluation or prediction"""
    if labels is None:
        # 没有标签, 仅使用特征值
        inputs = features
    else:
        inputs = (features, labels)

    # 数据转换为tf.dataset对象
    dataset = tf.data.Dataset.from_tensor_slices(inputs)

    # 把数据分成不同批次
    assert batch_size is not None, "batch_size must not be None"
    dataset = dataset.batch(batch_size)

    return dataset.make_one_shot_iterator().get_next()

运行代码, 会获得类似以下的输出:

Test set accuracy: 0.967
预测

评估得到满意的结果之后, 我们就可以用模型来做预测. 输入一些不带标签的数据来进行预测. 输入的数据可能是来自于CSV文件, APP数据, 传感器数据等等. 示例代码中, 我们直接创建几组无标签的数据.

predict_x = {
        'SepalLength': [5.1, 5.9, 6.9],
        'SepalWidth': [3.3, 3.0, 3.1],
        'PetalLength': [1.7, 4.2, 5.4],
        'PetalWidth': [0.5, 1.5, 2.1],
}

每个估计函数都会提供一个预测方法, 例如本文示例代码中的classifier.predict方法:

predictions = classifier.predict(
    input_fn=lambda:eval_input_fn(predict_x,
                                  labels=None,
                                  batch_size=args.batch_size))

类似evaluate方法, predict方法同样通过eval_input_fn方法来获取数据.
predict方法返回一个字典, 包含不同标签的预测结果的概率.

'probabilities': array([  1.19127117e-08,   3.97069454e-02,   9.60292995e-01])

class_ids包含一个单元素数组, 包含最可能的预测结果:

'class_ids': array([2]) #数字2代表最可能的为第二个类型, 即versicolor

利用下面的代码来迭代输出每一次的预测结果:

for pred_dict, expec in zip(predictions, expected):
    template = ('\nPrediction is "{}" ({:.1f}%), expected "{}"')

    class_id = pred_dict['class_ids'][0]
    probability = pred_dict['probabilities'][class_id]
    print(template.format(iris_data.SPECIES[class_id], 100 * probability, expec))

最终输出结果类似下图:

示例仅仅包含了代码的实现, 想要了解更多关于机器学习的内容, 需要学习更多的知识. Google, 网易云课堂, Coursera等等都有许多的资料和课程.


本文源自于TensorFlow快速入门教程.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容