Tensorflow2.0语法 - dataset数据封装+训测验切割(二)

转自 https://segmentfault.com/a/1190000020447666

训练集-测试集-验证集切割

方法1:(借用三方sklearn库)

因为sklearn的train_test_split只能切2份,所以我们需要切2次:

from sklearn.model_selection import train_test_split

x_train, x_test, y_train, y_test = train_test_split(
    x, y,                # x,y是原始数据
    test_size=0.2        # test_size默认是0.25
)  # 返回的是 剩余训练集+测试集

x_train, x_valid, y_train, y_valid = train_test_split(
    x_train, y_train,    # 把上面剩余的 x_train, y_train继续拿来切
    test_size=0.2        # test_size默认是0.25
)  # 返回的是 二次剩余训练集+验证集

切分好的数据,一般需要做 batch_size, shuffle等, 可以使用 tf.keras模型的 fit() 一步传递!
eg:

model.compile(
    loss=keras.losses.mean_squared_error,
    optimizer=keras.optimizers.SGD(),
    metrics=[‘acc‘]    # 注意这个metrics参数,下面一会就提到
)

history = model.fit(
    x_train,
    y_train,
    validation_data=(x_valid, y_valid),     # 验证集在这里用了!!!
    epochs=100,
    batch_size = 32      #  batch_size 不传也行,因为默认就是32
    shuffle=True,        #  shuffle    不传也行,因为默认就是True
    # callbacks=callbacks, #
)
度量指标 = model.evaluate(x_test, y_test)    # 返回的是指标(可能包括loss,acc)
# 这里说一下,为什么我说可能包括。
# 因为这个返回结果取决于 你的  model.compile() 传递的参数
    # 如果你传了  metrics=[‘acc‘], 那么这个度量指标的返回结果就是 (loss, acc)
    # 如果你没传 metrics ,         那么这个度量指标的返回结果就是一个 loss

y_predict = model.predict(x_test)            # 返回的是预测结果

方法2:(tf.split)

自己封装的代码:功能包括: 3切分,乱序数据集,分批操作 一体化!!! (可能有瑕疵)
已上传至Github : https://github.com/hacker-lin...
定义部分:

class HandlerData:
    def __init__(self, x, y):
        """我封装的类,数据通过实例化传进来保存"""
        self.x = x
        self.y = y

    def shuffle_and_batch(self, x, y, batch_size=None):
        """默认定死乱序操作,batch_size可选参数, 其实乱序参数也应该设置可选的。懒了"""
        data = tf.data.Dataset.from_tensor_slices((x, y))    # 封装 dataset数据集格式

        data_ = data.shuffle(        # 乱序
            buffer_size=x.shape[0],  # 官方文档说明 shuffle的buffer_size 必须大于或等于样本数量
        )
        if batch_size:
            data_ = data_.batch(batch_size)
        return data_

    def train_test_valid_split(self,
        test_size=0.2,                 # 测试集的切割比例
        valid_size=0.2,                # 验证集的切割比例
        batch_size=32,                 # batch_size 默认我设为了32
        is_batch_and_shuffle=True      # 这个是需不需要乱序和分批,默认设为使用乱序和分批
    ):

        sample_num = self.x.shape[0]    # 获取样本总个数
        train_sample = int(sample_num * (1 - test_size - valid_size))  # 训练集的份数
        test_sample = int(sample_num * test_size)                      # 测试集测份数
        valid_train = int(sample_num * valid_size)                     # 验证集的份数
        # 这三个为什么我用int包裹起来了,因为我调试过程中发现,有浮点数计算精度缺失现象。
        # 所以必须转整形

        # tf.split()  此语法上一篇我讲过,分n份,每份可不同数量
        x_train, x_test, x_valid = tf.split(
            self.x,
            num_or_size_splits=[train_sample, test_sample, valid_train],
            axis=0
        )
        y_train, y_test, y_valid = tf.split(
            self.y,
            [train_sample, test_sample, valid_train],
            axis=0
        )
        # 因为份数是我切割x,y之前计算出来的公共变量。所以不用担心 x,y不匹配的问题。

        if is_batch_and_shuffle:   # 是否使用乱序和分批,默认是使用的,所以走这条
            return (
                self.shuffle_and_batch(x_train, y_train, batch_size=batch_size),
                self.shuffle_and_batch(x_test, y_test, batch_size=batch_size),
                self.shuffle_and_batch(x_valid, y_valid, batch_size=batch_size),
            )
        else:    # 如果你只想要切割后的原生数据,那么你把is_batch_and_shuffle传False就走这条路了
            return (
                (x_train, y_train),
                (x_test, y_test),
                (x_valid, y_valid)
            )

调用案例:

x = tf.ones([1000, 5000])
y = tf.ones([1000, 1])

data_obj = HandlerData(x,y)   # x是原生的样本数据,x是原生的label数据

# 方式1:使用乱序,使用分批,就是一个参数都不用传,全是默认值
train, test, valid = data_obj.train_test_valid_split(
    # test_size=0.2,
    # valid_size=0.2,
    # batch_size=32,
    # is_batch_and_shuffle=True
) # 这些参数你都可以不传,这都是设置的默认值。

print(train)
print(test)
print(valid)

# 结果
>>> <BatchDataset shapes: ((None, 5000), (None, 1)), types: (tf.float32, tf.float32)>
>>> <BatchDataset shapes: ((None, 5000), (None, 1)), types: (tf.float32, tf.float32)>
>>> <BatchDataset shapes: ((None, 5000), (None, 1)), types: (tf.float32, tf.float32)>

# 虽然你看见了样本数为None,但是没关系,因为你还没使用,遍历一下就明白了
for x_train,y_train in train:
    print(x_train.shape,y_train.shape)

# 结果  600 // 32 == 18 (你可以查一下正好18个)
# 结果  600 % 32 == 24 (你可以看一下最后一个就是24)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(32, 5000) (32, 1)
(24, 5000) (24, 1)   # 32个一批,最后一个就是余数 24个了。

# 方式2:不使用乱序,使用分批,只要原生数据,
(x_train, y_train), (x_test, y_test), (x_valid, y_valid) = data_obj.train_test_valid_split(
    # test_size=0.2,
    # valid_size=0.2,
    # batch_size=32,
    is_batch_and_shuffle=False    # 这个改为False即可,其他参数可选
)

print(x_train.shape, y_train.shape)
print(x_test.shape, y_test.shape)
print(x_valid.shape, y_valid.shape)

# 结果
>>> (600, 5000) (600, 1)
>>> (200, 5000) (200, 1)
>>> (200, 5000) (200, 1)

方式3(训验分割)

history = model.fit(
    .....
    validation_split=0.2      # 训练集分出0.2给验证集
)

数据处理 (dataset)

这个模块的作用就是,将我们的数据,或者 TF张量,封装成数据集。
这个数据集具有成品API,比如:可以帮助我们,分批,乱序,制作迭代,等一些列操作。

基本理解

dataset = tf.data.Dataset.from_tensor_slices(np.arange(16).reshape(4,4))
按理来说(先不取),数据形状应该是这样的。  (一个大列表里面,有4个小列表)
[
    [0, 1, 2 ,3 ],
    [4, 5, 6 ,7 ],
    [8, 9, 10,11],
    [12,13,14,15],
]

for data in dataset:   # 封装的数据集需要遍历(或者 iter() 改变为迭代器类型),才能返回值
    print(data)        # 每遍历一条就是里面的小列表。 eg:第一条形状: [0, 1, 2 ,3 ]
                       # 但是别忘了。我们这是Tensorflow,因此每层数据集都被封装为Tensor。
                       # 因此,我们每遍历出一条数据,都是一条Tensor
输出:
>>    tf.Tensor([0 1 2 3], shape=(4,), dtype=int32)
      tf.Tensor([4 5 6 7], shape=(4,), dtype=int32)
      tf.Tensor([ 8  9 10 11], shape=(4,), dtype=int32)
      tf.Tensor([12 13 14 15], shape=(4,), dtype=int32)

前面说了,这个数据的格式就是(一个大列表里面,有4个小列表)
对应来看, (一个大Tensor里面, 有4个小Tensor)。 记住这个理念

数据来源参数类型

参数传元组:

question = [[1, 0], [1, 1]]
answer = [‘encode‘, ‘decoder‘]
dataset = tf.data.Dataset.from_tensor_slices( (question, answer) ) # 用元组包起来了
for data in dataset:
    print(data[0],‘=>‘ ,data[1])
输出:
>> tf.Tensor([1 0], shape=(2,), dtype=int32) => tf.Tensor(b‘encode‘, shape=(), dtype=string)
   tf.Tensor([1 1], shape=(2,), dtype=int32) => tf.Tensor(b‘decoder‘, shape=(), dtype=string)

你可以看出它自动把我们传递的 question 和 answer 两个大列表。  "相当于做了zip()操作"。

# 我的实验经历:训练 Encoder-Decoder模型的,"问答对数据",做编码后,就可以这样用元组传。

参数传字典:

data_dict = {
    ‘encoder‘: [1, 0],
    ‘decoder‘: [1, 1]
}

dataset = tf.data.Dataset.from_tensor_slices(data_dict)
for data in dataset:    # 其实每一个元素就是一个字典
    print(data)

# 其实就是把你的 value部分,转成了Tensor类型。 总体结构没变

链式调用

Dataset API 大多数操作几乎都是链式调用(就像python字符串的 replace方法)
用上面的数据作为案例数据, 介绍几种API:

batch (分批)

for data in dataset.batch(2):    # 若设置 drop_remainder=True,则最后余下一批会被丢弃
    print(data)
输出:
>>    tf.Tensor([[0 1 2 3] [4 5 6 7]], shape=(2, 4), dtype=int32)
      tf.Tensor([[ 8  9 10 11] [12 13 14 15]], shape=(2, 4), dtype=int32)

上面说过,默认就是 遍历出的每个子项,就是一个Tensor,  如上数据,遍历出 4个Tensor
而调用 batch(2) 后, 把2个子项分成一批, 然后再包装成为Tensor。
so, 4/2 = 2批 , 包装成2个Tensor

repeat(重复使用数据:epoch理念, 重复训练n轮次)

注意(传的就是总重复数,算自身):
    1. 如果repeat() 不传参数,那就是无限重复。。。
    2. 如果传参数 = 0,  那么代表不取数据
    3. 如果传参数 = 1,  那么代表一共就一份数据
    4. 如果传参数 = 2,  那么代表一共就2份数据(把自己算上,一共2份,就这么个重复的意思)

for data in dataset.repeat(2).batch(3):   # 重复2次。 3个一组  (这就是链式调用)
    print(data)

结果
>>  tf.Tensor([[ 0  1  2  3] [ 4  5  6  7] [ 8  9 10 11]], shape=(3, 4), dtype=int32)
    tf.Tensor([[12 13 14 15] [ 0  1  2  3] [ 4  5  6  7]], shape=(3, 4), dtype=int32)
    tf.Tensor([[ 8  9 10 11] [12 13 14 15]], shape=(2, 4), dtype=int32)  

    原数据是4个子项,  重复2次 :  4*2=8
    然后链式调用分3批: 8/3=2 ..... 2    (整批3个一组, 最后一批余数一组)
    # 还要注意一下, 它们重复是顺序重复拼接。 分批时,可以首尾相连的
    (eg:就像小时候吃的一连串棒棒糖, 拽不好,会把上一个的糖皮连着拽下来)

原文地址:https://www.cnblogs.com/whw1314/p/12121900.html

时间: 2024-10-23 16:59:48

Tensorflow2.0语法 - dataset数据封装+训测验切割(二)的相关文章

Tensorflow2.0语法 - 张量&amp;基本函数(一)

转自 https://segmentfault.com/a/1190000020413887 前言 TF2.0 是之前学习的内容,当时是写在了私有的YNote中,重写于SF.TF2.0-GPU 安装教程传送门:https://segmentfault.com/a/11...之前接触过 TF1, 手动session机制,看着很是头疼. TF2.0不需要做这些TF2.0 理解起来更容易(逐渐 Pythonic and Numpic)TF2.0 后端采用keras接口 (构建网络层),更方便. TF2

Tensorflow2.0语法 - keras_API的使用(三)

转自 https://segmentfault.com/a/1190000021181739 前言 keras接口大都实现了 _call_ 方法.母类 _call_ 调用了 call().因此下面说的几乎所有模型/网络层 都可以在定义后,直接像函数一样调用.eg: 模型对象(参数) 网络层对象(参数) 我们还可以实现继承模板 导入 from tensorflow import keras metrics (统计平均) 里面有各种度量值的接口如:二分类.多分类交叉熵损失容器,MSE.MAE的损失值

tensorflow2.0新特性

Tensorflow2.0相比于以往版本,有着极大的区别:最明显的区别可以用三字词来概括:更简单,更易用,更强大. 接下来让我们一起见证下不一样的地方吧! 一.使用tf.data加载数据 使用tf.data创建的输入管道读取训练数据:支持从内存(Numpy)方便地输入数据: 二.使用tf.keras构建,训练和验证模型,或使用Premade来验证模型 可以直接标准的打包模型(逻辑回归,随机森林),也可以直接使用(tf.estimator API) 如果不想从头训练模型,可以使用迁移学习来训练一个

tensorflow2.0 学习(三)

用tensorflow2.0 版回顾了一下mnist的学习 代码如下,感觉这个版本下的mnist学习更简洁,更方便 关于tensorflow的基础知识,这里就不更新了,用到什么就到网上取搜索相关的知识 # encoding: utf-8 import numpy as np import tensorflow as tf import matplotlib.pyplot as plt #加载下载好的mnist数据库 60000张训练 10000张测试 每一张维度(28,28) path = r'

MVC 4.0语法 自动分页

4.0语法中实现自动分页只需要两个方法即可,Skip() ----跳过序列中指定的元素,Take()-----从序列的开头返回指定数量元素. 一般用自动分页都是无刷新的,可以把显示的数据,用局部页面封装起来,每次分页的时候就异步来获取局部页面.  ShopBackDataContext sc = new ShopBackDataContext(); //LinQ to SQL 类 /// <summary> /// 自动分页 /// </summary> /// <param

C# 6.0语法新特性体验(二)

之前我在文章通过Roslyn体验C# 6.0的新语法中介绍了一些C# 6.0的语法特性,现在随着Visual Studio 14 CTP3的发布,又陆续可以体验一些新的特性了,这里简单的介绍一下之前没有介绍的新语法. 属性表达式(Property Expressions) 我们常常会在类中写一些通过函数生成的只读属性: ????class Point????{????????public int X { get; set; }????????public int Y { get; set; }

探索C#之6.0语法糖剖析

阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和finally代码块内的Await nameof表达式 扩展方法 总结 自动属性默认初始化 使用方法: public string Name { get; set; } = "hello world"; 为了便于理解使用2.0语法展示,编译器生成代码如下: public class Custom

C#4.0语法糖之第一篇:自动属性&amp;隐式类型

今天给大家分享一下C#语法糖的简单的两个知识点吧. 自动属性:在 C# 4.0 和更高版本中,当属性的访问器中不需要其他逻辑时,自动实现的属性可使属性声明更加简洁. 客户端代码还可通过这些属性创建对象. get and set accessors." id="mt3">如下面的示例所示声明属性时,编译器将创建一个私有的匿名支持字段,该字段只能通过属性的 get 和 set 访问器进行访问. 我们C#4.0以前的传统方式的属性是用来封装字段的,这里我简单的对比一下这两种方

C#4.0语法糖之第五篇: 匿名类 &amp; 匿名方法

今天时间有点早,所以上来在写一篇文章吧,继续上一篇的文章,在我们平时编程过程中有没有遇到过这样的一个情景,你定义的类只是用来封装一些相关的数据,但并不需要相关联的方法.事件和其他自定义的功能.同时,这个类仅仅在当前的应用程序中使用,而不需要在项目间重用.你所需要的只是一个“临时的”类型,现在我们来看看这个传统类的定义: 1 internal class oneClass 2  3 { 4  5      //定义若干私有数据成员 6  7      //通过属性来封装每个数据成员 8  9