tensorflow-keras使用tips(一)
Ebounce
撰写于 2022年 02月 24 日

tensorflow-keras使用tips(一)

前言

很久很久没有写博客了,主要公司原因不方便分享整理,所以就来分享一些小tips了。
言归正传,最近学习了一下ML的相关内容,补了很长一段时间的数学知识,也踩了很多使用kerastensorflow的坑,这里进行一个阶段性的总结,全程不涉及数学方面的内容,只有编程时候踩过的坑。下面分成三个模块,主要都是使用tensorflow.data.Dataset时出现的问题。

<!--more-->

在预处理阶段使用dataset

dataset包如其名,其实就是一群可迭代的数据集合,他本身类似于迭代器,相比于直接传入numpy数组会更节省内存,且内置了许多预处理方式,我们可以使用类似于链式调用的形式,简单的处理好数据,这里默认大家已经比较熟悉DataSet的创建了,下面展示一个多输入的实例

import tensorflow as tf

testData = [1,2,3,4,5,6]
testData2 = [7,8,9,10,11,12]

data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2},
)

# 在多输入情况下,dataset构建时可以在里面构造两个字典,第一个字典是训练数据的集合
# 第二个字典则是label的集合简单来说就是
# model.fit(x={first_dict},y={second_dict})
# 在这个例子中,我们不输入label,只输入x

构造好对应数据以后,我们需要对数据进行预处理,在ML中常见的预处理就是取batchshuffle,前者是分割小数据集,后者则是打散数据。使用dataset处理非常简单,一句代码就可以搞定了

data = data.shuffle(10000).batch(2)

这里我们为打散数据,给了tensorflow大小为1W的缓冲区,并将打散后的数据分成两组

first

由于我们给出的数据大小为6,batch_size2batch大小就为3了,每一次调用都会返回batch,如果数据格式是我们需要的,就不需要再进行后续处理了,接下是用dataset进行训练,这个时候我们的数据已经打乱过一次了。

使用dataset训练模型

我们随意创建一个多输入的全连接模型,这是一个非常简单的模型。

import tensorflow as tf
import tensorflow.keras as K

testData = [1,2,3,4,5,6]
testData2 = [7,8,9,10,11,12]

data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2}
)

data = data.shuffle(10000).batch(2)

input1 = K.layers.Input(shape=(1,))
input2 = K.layers.Input(shape=(1,))

dense = K.layers.Dense(units=10)
a = dense(input1)
b = dense(input2)
output = K.layers.Concatenate()([a,b])
# 这里创建了一个具有共享参数的全连接层,且为双输入的模型。
model = K.models.Model(inputs=[input1,input2],outputs = [output])
model.compile(loss="mse",optimizer="adam")
model.summary()

先输出我们的模型结构check一下正不正确:

结构没问题,由于这里使用了默认的loss函数,因此我们还需要把label输入进去,那就稍微修改一下dataset,如下:

data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2},
    {"y":[1,2,3,4,5,6]}
)

接着我们来训练模型,使用dataset传入参数时,就不可以使用vaild_split参数了,因此如果需要验证集,我们需要自己构造,所以这里再构造一个验证集:

testData.reverse()
testData2.reverse()
vaild_data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2},
    {"y":[6,5,4,3,2,1]}
)

这里使用了反转数组的形式构造验证集,然后我们对模型进行训练:

import tensorflow as tf
import tensorflow.keras as K

testData = [1,2,3,4,5,6]
testData2 = [7,8,9,10,11,12]

data = tf.data.Dataset.from_tensor_slices(
    ({"x1":testData,"x2":testData2},
    {"y":[1,2,3,4,5,6]}),
)
testData.reverse()
testData2.reverse()
vaild_data = tf.data.Dataset.from_tensor_slices(
    ({"x1":testData,"x2":testData2},
    {"y":[6,5,4,3,2,1]}),
).batch(2)

data = data.shuffle(10000).batch(2)

input1 = K.layers.Input(shape=(1,),name="x1")
input2 = K.layers.Input(shape=(1,),name="x2")

# 多输入情况下需要对输入层命名,这样在训练时才能传入正确的参数

dense = K.layers.Dense(units=10)
a = dense(input1)
b = dense(input2)
output = K.layers.Concatenate(name="y")([a,b])

model = K.models.Model(inputs=[input1,input2],outputs = [output])
model.compile(loss="mse",optimizer="adam")
model.summary()
model.fit(data,epochs=3,validation_data=vaild_data)
# 因为数据预处理的时候我们已经分了batch,所以这里也不需要传入batch_size

输出如下:

在训练中使用dataset

有时候我们需要对模型中输入的数据进行处理,比如对词进行编码的操作,我们期望模型自己能够实现词的编码,而不是每次都需要认为编码完成后,再输入进行。在tensorflow的例子中是

K.layers.TextVectorization

这个类是常用keras.preprocessing.text.Tokenizerlayer版。如果我们要在训练中修改传入的数据,首先我们需要明白层级之间传入的数据到底是什么,答案是tensor

我们构造一个稍微复杂一点的模型,假设我们有一个NLP任务,由于数据集不够多,我们需要对数据进行增强,来扩充数据集。

这个时候我们可以通过自定义keras层来实现反转张量的操作,由于大部分情况下NLP直接输入的是一段词序列如:

data = ["i am john",
        "you know something about john",
        "i am john's friend",
        "we group up and play together",
        "you want to know jonh right",
]
# 我们假设为每个单词编号,一些没有实义的单词用tag填充,这个tag我们随意指定一个数组,
# 数据会变成这样:
data = [
        [1,-1,2],
        [3,4,5,-1,2],
        [1,-1,6,7],
        [8,9,10,11,12,13],
        [3,14,-1,4,2,14],
]
# 为了保证句子长度一致会进行padding操作,这里使用0进行padding,
# 大部分分词器支持分词时填充到等长的序列
data = [
        [1,-1,2,0,0,0],
        [3,4,5,-1,2,0],
        [1,-1,6,7,0,0],
        [8,9,10,11,12,13],
        [3,14,-1,4,2,14],
]
# 因为是分类任务,我们为句型打上标签,并转换为one-hot编码
y = to_categorical([0, 1, 0, 0, 2], num_classes=3)
data = tf.data.Dataset.from_tensor_slices(
    (
        {"x": [
            [1, -1, 2, 0, 0, 0],
            [3, 4, 5, -1, 2, 0],
            [1, -1, 6, 7, 0, 0],
            [8, 9, 10, 11, 12, 13],
            [3, 14, -1, 4, 2, 14],
        ]
        },
        {"y": y})
)
# y = [[1. 0. 0.], [0. 1. 0.], [1. 0. 0.], [1. 0. 0.], [0. 0. 1.]] --narray

数据量非常少的情况,我们准备反转每个序列来进行操作,虽然这一步在预处理当中进行也可以,但是我们选择使用tf.data.Dataset输入数据,使用自定义层进行反转操作。

class ReverseLayer(K.layers.Layer):
    def __init__(self):
        super(ReverseLayer, self).__init__()
    def call(self, inputs, *args, **kwargs):
        result = K.backend.reverse(inputs,-1)
        return result

我们需要随时明确一个点,keras模型中任何一个部分传递的都是张量或者张量列表,实例如下,我们随意实现一个模型架构:

代码如下,这里用了重复、反转、随机化三个方法进行数据增强:

import tensorflow as tf
import tensorflow.keras as K
from keras.utils.np_utils import to_categorical

y = to_categorical([0, 1, 0, 0, 2], num_classes=3)
data = tf.data.Dataset.from_tensor_slices(
    (
        {"x": [
            [1, -1, 2, 0, 0, 0],
            [3, 4, 5, -1, 2, 0],
            [1, -1, 6, 7, 0, 0],
            [8, 9, 10, 11, 12, 13],
            [3, 14, -1, 4, 2, 14],
        ]
        },
        {"y": y})
).repeat(count=100).batch(10).shuffle(1000)


class ReverseLayer(K.layers.Layer):
    def __init__(self):
        super(ReverseLayer, self).__init__()

    def call(self, inputs, *args, **kwargs):
        result = K.backend.reverse(inputs, -1)
        return result


input1 = K.layers.Input(shape=(6,), name="x")
# 输入可以看作每个数据的格式是怎么样,这个与batchSize无关,除非你是batchSize等于
# dataSize
denseN = ReverseLayer()(input1) 
# 自定义反转方法
denseN = K.layers.Dense(units=3,activation="relu")(denseN)
dense = K.layers.Dense(units=3,activation="relu")(input1)
# 为了保持和输出一致,首先使用全连接将(6,)->(3,)
Embedded = K.layers.Embedding(output_dim=10)
# 嵌入层会将向量维度转化成(3,10)也就是(3,output_dim)
denseN = Embedded(denseN)
dense = Embedded(dense)
output = K.layers.Add()([dense,denseN])
dense = K.layers.Dense(units=1,activation="softmax",name="y")(output)
# 将(3,10)->(3,1) 这个时候由于需要和标签对应,因此这里会将格式帮我们转成(None,3)

model = K.models.Model(inputs=input1, outputs=dense)
model.compile(loss="categorical_crossentropy", optimizer="adam")
model.summary()
model.fit(data, epochs=50, )
K.utils.plot_model(model,to_file="test.png")

看模型整体架构会更加清晰:

点击运行就可以成功运行了:

这里还有一些小问题没有修复,比如会提示warning 不存在梯度,但这些tips主要是数据处理方面的,因此就暂不提了。

tensorflow-keras使用tips(一)

tensorflow-keras使用tips(一)

前言

很久很久没有写博客了,主要公司原因不方便分享整理,所以就来分享一些小tips了。
言归正传,最近学习了一下ML的相关内容,补了很长一段时间的数学知识,也踩了很多使用kerastensorflow的坑,这里进行一个阶段性的总结,全程不涉及数学方面的内容,只有编程时候踩过的坑。下面分成三个模块,主要都是使用tensorflow.data.Dataset时出现的问题。

<!--more-->

在预处理阶段使用dataset

dataset包如其名,其实就是一群可迭代的数据集合,他本身类似于迭代器,相比于直接传入numpy数组会更节省内存,且内置了许多预处理方式,我们可以使用类似于链式调用的形式,简单的处理好数据,这里默认大家已经比较熟悉DataSet的创建了,下面展示一个多输入的实例

import tensorflow as tf

testData = [1,2,3,4,5,6]
testData2 = [7,8,9,10,11,12]

data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2},
)

# 在多输入情况下,dataset构建时可以在里面构造两个字典,第一个字典是训练数据的集合
# 第二个字典则是label的集合简单来说就是
# model.fit(x={first_dict},y={second_dict})
# 在这个例子中,我们不输入label,只输入x

构造好对应数据以后,我们需要对数据进行预处理,在ML中常见的预处理就是取batchshuffle,前者是分割小数据集,后者则是打散数据。使用dataset处理非常简单,一句代码就可以搞定了

data = data.shuffle(10000).batch(2)

这里我们为打散数据,给了tensorflow大小为1W的缓冲区,并将打散后的数据分成两组

first

由于我们给出的数据大小为6,batch_size2batch大小就为3了,每一次调用都会返回batch,如果数据格式是我们需要的,就不需要再进行后续处理了,接下是用dataset进行训练,这个时候我们的数据已经打乱过一次了。

使用dataset训练模型

我们随意创建一个多输入的全连接模型,这是一个非常简单的模型。

import tensorflow as tf
import tensorflow.keras as K

testData = [1,2,3,4,5,6]
testData2 = [7,8,9,10,11,12]

data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2}
)

data = data.shuffle(10000).batch(2)

input1 = K.layers.Input(shape=(1,))
input2 = K.layers.Input(shape=(1,))

dense = K.layers.Dense(units=10)
a = dense(input1)
b = dense(input2)
output = K.layers.Concatenate()([a,b])
# 这里创建了一个具有共享参数的全连接层,且为双输入的模型。
model = K.models.Model(inputs=[input1,input2],outputs = [output])
model.compile(loss="mse",optimizer="adam")
model.summary()

先输出我们的模型结构check一下正不正确:

结构没问题,由于这里使用了默认的loss函数,因此我们还需要把label输入进去,那就稍微修改一下dataset,如下:

data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2},
    {"y":[1,2,3,4,5,6]}
)

接着我们来训练模型,使用dataset传入参数时,就不可以使用vaild_split参数了,因此如果需要验证集,我们需要自己构造,所以这里再构造一个验证集:

testData.reverse()
testData2.reverse()
vaild_data = tf.data.Dataset.from_tensor_slices(
    {"x1":testData,"x2":testData2},
    {"y":[6,5,4,3,2,1]}
)

这里使用了反转数组的形式构造验证集,然后我们对模型进行训练:

import tensorflow as tf
import tensorflow.keras as K

testData = [1,2,3,4,5,6]
testData2 = [7,8,9,10,11,12]

data = tf.data.Dataset.from_tensor_slices(
    ({"x1":testData,"x2":testData2},
    {"y":[1,2,3,4,5,6]}),
)
testData.reverse()
testData2.reverse()
vaild_data = tf.data.Dataset.from_tensor_slices(
    ({"x1":testData,"x2":testData2},
    {"y":[6,5,4,3,2,1]}),
).batch(2)

data = data.shuffle(10000).batch(2)

input1 = K.layers.Input(shape=(1,),name="x1")
input2 = K.layers.Input(shape=(1,),name="x2")

# 多输入情况下需要对输入层命名,这样在训练时才能传入正确的参数

dense = K.layers.Dense(units=10)
a = dense(input1)
b = dense(input2)
output = K.layers.Concatenate(name="y")([a,b])

model = K.models.Model(inputs=[input1,input2],outputs = [output])
model.compile(loss="mse",optimizer="adam")
model.summary()
model.fit(data,epochs=3,validation_data=vaild_data)
# 因为数据预处理的时候我们已经分了batch,所以这里也不需要传入batch_size

输出如下:

在训练中使用dataset

有时候我们需要对模型中输入的数据进行处理,比如对词进行编码的操作,我们期望模型自己能够实现词的编码,而不是每次都需要认为编码完成后,再输入进行。在tensorflow的例子中是

K.layers.TextVectorization

这个类是常用keras.preprocessing.text.Tokenizerlayer版。如果我们要在训练中修改传入的数据,首先我们需要明白层级之间传入的数据到底是什么,答案是tensor

我们构造一个稍微复杂一点的模型,假设我们有一个NLP任务,由于数据集不够多,我们需要对数据进行增强,来扩充数据集。

这个时候我们可以通过自定义keras层来实现反转张量的操作,由于大部分情况下NLP直接输入的是一段词序列如:

data = ["i am john",
        "you know something about john",
        "i am john's friend",
        "we group up and play together",
        "you want to know jonh right",
]
# 我们假设为每个单词编号,一些没有实义的单词用tag填充,这个tag我们随意指定一个数组,
# 数据会变成这样:
data = [
        [1,-1,2],
        [3,4,5,-1,2],
        [1,-1,6,7],
        [8,9,10,11,12,13],
        [3,14,-1,4,2,14],
]
# 为了保证句子长度一致会进行padding操作,这里使用0进行padding,
# 大部分分词器支持分词时填充到等长的序列
data = [
        [1,-1,2,0,0,0],
        [3,4,5,-1,2,0],
        [1,-1,6,7,0,0],
        [8,9,10,11,12,13],
        [3,14,-1,4,2,14],
]
# 因为是分类任务,我们为句型打上标签,并转换为one-hot编码
y = to_categorical([0, 1, 0, 0, 2], num_classes=3)
data = tf.data.Dataset.from_tensor_slices(
    (
        {"x": [
            [1, -1, 2, 0, 0, 0],
            [3, 4, 5, -1, 2, 0],
            [1, -1, 6, 7, 0, 0],
            [8, 9, 10, 11, 12, 13],
            [3, 14, -1, 4, 2, 14],
        ]
        },
        {"y": y})
)
# y = [[1. 0. 0.], [0. 1. 0.], [1. 0. 0.], [1. 0. 0.], [0. 0. 1.]] --narray

数据量非常少的情况,我们准备反转每个序列来进行操作,虽然这一步在预处理当中进行也可以,但是我们选择使用tf.data.Dataset输入数据,使用自定义层进行反转操作。

class ReverseLayer(K.layers.Layer):
    def __init__(self):
        super(ReverseLayer, self).__init__()
    def call(self, inputs, *args, **kwargs):
        result = K.backend.reverse(inputs,-1)
        return result

我们需要随时明确一个点,keras模型中任何一个部分传递的都是张量或者张量列表,实例如下,我们随意实现一个模型架构:

代码如下,这里用了重复、反转、随机化三个方法进行数据增强:

import tensorflow as tf
import tensorflow.keras as K
from keras.utils.np_utils import to_categorical

y = to_categorical([0, 1, 0, 0, 2], num_classes=3)
data = tf.data.Dataset.from_tensor_slices(
    (
        {"x": [
            [1, -1, 2, 0, 0, 0],
            [3, 4, 5, -1, 2, 0],
            [1, -1, 6, 7, 0, 0],
            [8, 9, 10, 11, 12, 13],
            [3, 14, -1, 4, 2, 14],
        ]
        },
        {"y": y})
).repeat(count=100).batch(10).shuffle(1000)


class ReverseLayer(K.layers.Layer):
    def __init__(self):
        super(ReverseLayer, self).__init__()

    def call(self, inputs, *args, **kwargs):
        result = K.backend.reverse(inputs, -1)
        return result


input1 = K.layers.Input(shape=(6,), name="x")
# 输入可以看作每个数据的格式是怎么样,这个与batchSize无关,除非你是batchSize等于
# dataSize
denseN = ReverseLayer()(input1) 
# 自定义反转方法
denseN = K.layers.Dense(units=3,activation="relu")(denseN)
dense = K.layers.Dense(units=3,activation="relu")(input1)
# 为了保持和输出一致,首先使用全连接将(6,)->(3,)
Embedded = K.layers.Embedding(output_dim=10)
# 嵌入层会将向量维度转化成(3,10)也就是(3,output_dim)
denseN = Embedded(denseN)
dense = Embedded(dense)
output = K.layers.Add()([dense,denseN])
dense = K.layers.Dense(units=1,activation="softmax",name="y")(output)
# 将(3,10)->(3,1) 这个时候由于需要和标签对应,因此这里会将格式帮我们转成(None,3)

model = K.models.Model(inputs=input1, outputs=dense)
model.compile(loss="categorical_crossentropy", optimizer="adam")
model.summary()
model.fit(data, epochs=50, )
K.utils.plot_model(model,to_file="test.png")

看模型整体架构会更加清晰:

点击运行就可以成功运行了:

这里还有一些小问题没有修复,比如会提示warning 不存在梯度,但这些tips主要是数据处理方面的,因此就暂不提了。

评论区(暂无评论)

这里空空如也,快来评论吧~

我要评论