from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import numpy as np
import pandas as pd
import tensorflow as tf
housing=fetch_california_housing()
scaler=StandardScaler()
x_data=scaler.fit_transform(housing.data)
x_train_full,x_test,y_train_full,y_test=train_test_split(x_data,housing.target)
x_train,x_valid,y_train,y_valid=train_test_split(x_train_full,y_train_full)
from_tensor_slices
将数据按照第一维分隔(一一对应)- 在
from_tensor_slices
后面,可以按照链式进行转换(每次都产生一个新数据集,如果要保存每次都要赋予一个新变量)batch
是将数据集分隔打包,drop_remainder
是让去掉最后不完整的数据包(可以让TF函数少生成一个AutoGraph,加快运行速度)map
函数中要注意,前面from_tensor_slices
的元组有几个参数,这里lambda
就要几个参数(分别对应),此外这个函数要是能够转换为TF函数的函数。num_parallel_calls
是多线程设置,加快速度。- 注意:这里
batch
为了方便显示,设置了5,最好设置大一点的数如32,64,128等,如果GPU好的话可以再大一点。- 其他函数:
filter
,过滤数据集,参数(函数)。take
取出前几行。
train_db = tf.data.Dataset.from_tensor_slices((x_train,y_train)).batch(5,drop_remainder=True).map((lambda x,y:(x*2,y)),num_parallel_calls=5)
可以看到由于上面传入
(x_train,y_train)
,这里的item是一个元组。如果原本是特征和标签合并在一起的完整数据,也可以在map
中,通过函数处理,分开为特征集合标签。
for item in train_db.take(3):
print(item)
(, )
(, )
(, )
我们知道训练集中实例相互独立且分布均匀时,梯度下降效果最佳。
shuffle
乱序,需要设置缓冲区大小(也可设置随机种子),适合可以装入RAM的小型数据集。
原理:先将缓冲区填满,如果要求提供一个数据,从缓冲区随机取出一个数据,并用源数据集的新元素替换它,直到遍历完源数据集,然后把缓冲区抽空为止。
注意:缓冲区必须足够大,不然乱序不太有效。当然也不要超出数据集大小。
dataset=tf.data.Dataset.range(10).repeat(3)
dataset
- 如果
batch
设置drop_remainder=True
则最后的Tensor去掉。- 如果在
shuffle
后面调用repeat
,则默认情况下,每次重复都会生成一个新的次序(这是个好主意)。但是,如果你希望每次迭代都重用相同的顺序(例如调试或者测试),则在shuffle
函数设置中reshuffle_each_iteration=True
dataset = dataset.shuffle(buffer_size=5,seed=42).batch(7)
for item in dataset:
print(item)
tf.Tensor([0 2 3 6 7 9 4], shape=(7,), dtype=int64)
tf.Tensor([5 0 1 1 8 6 5], shape=(7,), dtype=int64)
tf.Tensor([4 8 7 1 2 3 0], shape=(7,), dtype=int64)
tf.Tensor([5 4 2 7 8 9 9], shape=(7,), dtype=int64)
tf.Tensor([3 6], shape=(2,), dtype=int64)
对于大数据集,缓冲区乱序可能并不够用。一种想法是将源数据进行乱序。或者为了进一步乱序,我们可以将数据集拆成几个的文件,然后再随机地读取它们。但是对于同一个文件的数据,仍然相互接近,为了避免这种情况,可以随机选择多个文件同时读取,交错它们的记录(元组)。当然,最后还可以再增加个
shuffle
def split_tocsv(x,y,name,n_split=3,columns_name=None):
import pandas as pd
import numpy as np
file_name_list=[]
length_each_csv=x.shape[0]//n_split
y = y.reshape(x.shape[0],-1)
db=np.concatenate([x,y],axis=1)
for i in range(0,x.shape[0],length_each_csv):
left = i
right = x.shape[0] if (i+length_each_csv) > x.shape[0] else i+length_each_csv
temp_db = pd.DataFrame(db[left:right],columns=columns_name)
file_name = name + '_db_' + str(i)+'.csv'
file_name_list.append(file_name)
temp_db.to_csv(file_name,index=False)
return file_name_list
train_filepaths=split_tocsv(x_train,y_train,name='train',columns_name=housing.feature_names+housing.target_names)
test_filepaths=split_tocsv(x_test,y_test,'test',columns_name=housing.feature_names+housing.target_names)
valid_filepaths=split_tocsv(x_valid,y_valid,'valid',columns_name=housing.feature_names+housing.target_names)
train_filepaths
['train_db_0.csv', 'train_db_3870.csv', 'train_db_7740.csv']
为了创造多文件的条件,我写了上述的函数。实际上如果是大数据集的话,源数据是多个文件的,就不像这边这样分开,也就不用这个函数。当然也可以手动分成多个文件。接下来的才是正文。
注意:要尽量让文件等长,同时一个文件既包括特征也包括标签。
#建立文件名数据集。乱序
filepath_dataset = tf.data.Dataset.list_files(train_filepaths,seed=42)
for item in filepath_dataset:
print(item)
tf.Tensor(b'train_db_0.csv', shape=(), dtype=string)
tf.Tensor(b'train_db_7740.csv', shape=(), dtype=string)
tf.Tensor(b'train_db_3870.csv', shape=(), dtype=string)
这里用
interleave
交错方法对文件名数据集中每个文件名调用lambda
函数(即新创建一个数据集,跳过第一行特征名),cycle_length=2
表示2个新创建的数据集一起交错,block_length=6
表示每个新创建的数据集连续6条记录就交错另一个新创建的数据集。最终返回一个交错记录(元组)的数据集。
dataset = filepath_dataset.interleave(lambda x:tf.data.TextLineDataset(x).skip(1),
cycle_length=2,
block_length=6,
num_parallel_calls=2)
for item in dataset.take(3):
print(item)
tf.Tensor(b'0.7496827131540564,0.34647802955744084,0.08607255372709229,-0.017023771445335806,-0.23884533660976928,-0.0945567753546111,-0.9512902346352202,0.8483747044943901,5.00001', shape=(), dtype=string)
tf.Tensor(b'-0.7274916753876317,-1.6399739348833928,-0.4242689446759151,0.15982009534455993,-0.6159090247996127,0.10718992328963842,-0.7406062783379425,0.6836615678839947,1.375', shape=(), dtype=string)
tf.Tensor(b'-1.2252872849959682,-0.36864467764125924,-0.993795128142578,0.16967353779210897,-0.1858621485737257,-0.12157173190923926,-0.717196949860465,0.6137832675038262,3.25', shape=(), dtype=string)
返回完交错记录的数据集,还没完,因为每个
item
都是tf.string
的记录,需要一个解析函数
n_features = 8
def parseprocess(line):
#一行包括8个特征和1个标签。这里设置默认值包括类型,标签没有默认值,如果数据没有则报错。
defaults = [0.] * n_features + [tf.constant([],dtype=tf.float32)]
fields = tf.io.decode_csv(line,record_defaults=defaults)
x = tf.stack(fields[:-1]) #将标量张量列表(list)转换为一维张量数组(有shape属性)
y = tf.stack(fields[-1:])
return x,y
测试如下
for item in dataset.take(3):
print(parseprocess(item))
(, )
(, )
(, )
将上述所有的操作合在一起,如下
def csv_reader_dataset(filepaths,repeat=1,n_interleaves=3,n_read_threads=None,shuffle_buffer_size=10000,n_map_threads=5,batch_size=32,prefetch=1):
filepath_dataset = tf.data.Dataset.list_files(filepaths)
dataset = filepath_dataset.interleave(lambda x:tf.data.TextLineDataset(x).skip(1),
cycle_length=n_interleaves,
num_parallel_calls=n_read_threads)
dataset = dataset.map(parseprocess,num_parallel_calls=n_map_threads)
dataset = dataset.shuffle(buffer_size=shuffle_buffer_size).repeat(repeat)
return dataset.batch(batch_size).prefetch(prefetch)
上面的
prefetch
函数是实现预取,即该数据集尽可能地提前准备一个批次,当模型在训练一个批次时,数据集已经并行工作准备好下一批次。这样CPU和GPU可以并行的工作加快了训练速率。
train_db = csv_reader_dataset(train_filepaths)
test_db = csv_reader_dataset(test_filepaths)
valid_db = csv_reader_dataset(valid_filepaths)
此时,已经制成了三个数据集,每个数据集包括特征和标签。
input_=tf.keras.layers.Input(shape=[8])
hidden1=tf.keras.layers.Dense(30,activation='elu',kernel_initializer='he_normal')(input_)
hidden2=tf.keras.layers.Dense(30,activation='elu',kernel_initializer='he_normal')(hidden1)
concat=tf.keras.layers.Concatenate()([input_,hidden2])
output=tf.keras.layers.Dense(1)(concat)
model=tf.keras.Model(inputs=[input_],outputs=[output])
model.compile(loss=tf.keras.losses.mean_squared_error,optimizer=tf.keras.optimizers.SGD(learning_rate=0.05,momentum=0.9,nesterov=True,clipnorm=1,decay=1.0/200))
earlystop=tf.keras.callbacks.EarlyStopping(patience=5,restore_best_weights=True)
history=model.fit(train_db,epochs=100,validation_data=valid_db,callbacks=[earlystop])
model.evaluate(test_db)
Epoch 1/100
363/363 [==============================] - 5s 9ms/step - loss: 0.6353 - val_loss: 0.4421
Epoch 2/100
363/363 [==============================] - 2s 5ms/step - loss: 0.4002 - val_loss: 0.3841
Epoch 3/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3970 - val_loss: 0.3906
Epoch 4/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3729 - val_loss: 0.3671
Epoch 5/100
363/363 [==============================] - 3s 7ms/step - loss: 0.3477 - val_loss: 0.3583
Epoch 6/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3405 - val_loss: 0.3577
Epoch 7/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3314 - val_loss: 0.3746
Epoch 8/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3326 - val_loss: 0.3631
Epoch 9/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3300 - val_loss: 0.3375
Epoch 10/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3227 - val_loss: 0.3402
Epoch 11/100
363/363 [==============================] - 2s 5ms/step - loss: 0.3249 - val_loss: 0.3397
Epoch 12/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3230 - val_loss: 0.3416
Epoch 13/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3164 - val_loss: 0.3335
Epoch 14/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3115 - val_loss: 0.3313
Epoch 15/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3122 - val_loss: 0.3292
Epoch 16/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3091 - val_loss: 0.3303
Epoch 17/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3090 - val_loss: 0.3319
Epoch 18/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3065 - val_loss: 0.3309
Epoch 19/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3059 - val_loss: 0.3312
Epoch 20/100
363/363 [==============================] - 2s 6ms/step - loss: 0.3082 - val_loss: 0.3351
162/162 [==============================] - 0s 2ms/step - loss: 0.3151
0.31514933705329895
pd.DataFrame(history.history).plot(figsize=(18,10))
- 对于
batch
和prefetch
大多要放在最后,其他的向map,shuffle,filter,repeat大多放在前面操作。batch
设置32,64,128等,可以设置drop_remainder=True
,删除不完整的batchshuffle
要设置大一点的buffer_size
repeat
,可以在shuffle
前或者后,buffer_size
如果较小,repeat
在后面,如果很大,都可以。map
和interleave
都可以设置num_parallel_calls
,多线程。prefetch
:对训练速率有较大影响,但是要考虑好预取的数量,不要爆炸。- 以上都是建议,如果有错误的,欢迎在评论区留言。
- 如果觉得还不错,不要忘了收藏(包括函数)
TFRecord格式是Tensorflow首选的格式。这是一种非常简单的二进制格式,只包含二进制记录序列(每个记录由一个长度,一个用于检查长度的CRC校验和,实际数据以及最后一个CRC检验和组成)。
这部分比较少,因为比较不懂。
with tf.io.TFRecordWriter('my_data.tfrecord') as f:
f.write(b'This is the first record.')
f.write(b'And this is the second record.')
filepaths = ['my_data.tfrecord']
dataset = tf.data.TFRecordDataset(filepaths)
for item in dataset:
print(item)
tf.Tensor(b'This is the first record.', shape=(), dtype=string)
tf.Tensor(b'And this is the second record.', shape=(), dtype=string)
tfrecord_options = tf.io.TFRecordOptions(compression_type='GZIP')
with tf.io.TFRecordWriter('my_compressed_data.tfrecord',tfrecord_options) as f:
f.write(b'This is the first compressed record.')
f.write(b'And this is the second compressed record.')
dataset = tf.data.TFRecordDataset(['my_compressed_data.tfrecord'],compression_type='GZIP')
for item in dataset:
print(item)
tf.Tensor(b'This is the first compressed record.', shape=(), dtype=string)
tf.Tensor(b'And this is the second compressed record.', shape=(), dtype=string)
这是一种可移植、可扩展且高效的二进制格式。如果上述的csv已经够用那么就了解一下。
由于笔者太过懒惰且对于这部分也不熟悉,所以这部分就一个例子。
from tensorflow.train import BytesList,FloatList,Int64List #特征的数据类型
from tensorflow.train import Feature,Features,Example #嵌套定义的类型
person_example = Example( #一个实例
features=Features( #一个实例的所有特征
feature={ #每个特征都用名字 + 值
'name':Feature(bytes_list=BytesList(value=[b'Alice'])),
'id':Feature(int64_list=Int64List(value=[123])),
'emails':Feature(bytes_list=BytesList(value=[b'a@b.com',b'c@d.com']))
}
)
)
person_example.SerializeToString() #将一个实例序列化,变成二进制格式。
b'\n@\n\x1e\n\x06emails\x12\x14\n\x12\n\x07a@b.com\n\x07c@d.com\n\x0b\n\x02id\x12\x05\x1a\x03\n\x01{\n\x11\n\x04name\x12\t\n\x07\n\x05Alice'
#将序列化的数据写入文件。
with tf.io.TFRecordWriter('my_person_example.tfrecord') as f:
f.write(person_example.SerializeToString())
# 定义特征描述字典
feature_description = {
'name':tf.io.FixedLenFeature([],tf.string,default_value=""), #对于定长特征,用FixedLenFeature
'id':tf.io.FixedLenFeature([],tf.int64,default_value=0), # 并且参数有shape,dtype,default_value
'emails':tf.io.VarLenFeature(tf.string) #对于不定长的用VarLenFeature,参数只要dtype
}
# 有了特征描述字典,我们可以对读出的序列化数据进行解析。
for serialized_example in tf.data.TFRecordDataset(['my_person_example.tfrecord']):
parse_example = tf.io.parse_single_example(serialized_example,feature_description)
parse_example
{'emails': ,
'id': ,
'name': }
parse_example['emails'].values #对于可变长度的张量,可以这样访问。
- 像上面这样的例子,我们在写入的时候,可以写个函数,将数据序列化写入。读出时也可以写个函数,当然可以不用这样一个一个解析,可以读取时加上
batch()
,解析时用函数tf.io.parse_example()
。由于笔者太懒,就没写了。- 除了上面比较规则的数据序列化,还有针对文本不规则数据(例如一篇文章许多句子,一个句子用许多词表示)的序列化
SeauenceExample
,需要对应的类型序列化写,对应的函数解析,想了解的自己搜吧。
包括数值标准化,连续数值离散化,字符串编码。
相当于
sklearn
库的StandardScaler
每列做各自的标准化。
layer = tf.keras.layers.Normalization()
a = np.random.randint(0,10,(5,5))
a
array([[1, 9, 3, 2, 1],
[4, 6, 0, 2, 1],
[5, 6, 8, 0, 0],
[0, 9, 8, 2, 5],
[1, 8, 7, 4, 9]])
layer.adapt(a)
layer(a)
在实践中,如果数据集太大,可以随机抽样,在
Normalization()
加入model之前,先对抽样实例进行adapt()
(相当于StandardScaler.fit()
),然后再加入model。
下面展示了四种模式,不同输入的输出。但是主要的思路还是将连续数值,划分多个区域,给区域编码进行离散。
除了独热码模式,其他模式,输出维数与输入维数一样。
笔者认为,这个模式大多数应该是只输入一列,转换为下标(类别)。输入一列,最好是(None,1)的二维数据,以便和其他数据向concatenate。
# 对给定界线,每个区域标上下标,int输出模式返回对应数值区域的下标(相当于给连续数据分类,下标从0开始,将连续的数据转换成离散的数据)
# 数据预处理,选择这个。或下面的,都是返回二维数组。
layer1=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='int')
a,layer1(a)
(array([[1, 9, 3, 2, 1],
[4, 6, 0, 2, 1],
[5, 6, 8, 0, 0],
[0, 9, 8, 2, 5],
[1, 8, 7, 4, 9]]),
)
# 或者选择这个。
layer1=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='int')
a[:,[1]],layer1(a[:,[1]])
(array([[9],
[6],
[6],
[9],
[8]]),
)
layer1=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='int')
a[:,1],layer1(a[:,1])
(array([9, 6, 6, 9, 8]),
)
这个模式只能输入一列,为了便于记忆,与上面一样都输入(None,1)的二维数据。
# 对上面划分好的类别(下标),编码为独热码。
# 数据预处理,选择这个或下面都可以,都是返回二维数组。
layer2=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='one_hot')
layer1(a[:,[1]]),layer2(a[:,[1]])
(,
)
# 或者这个
layer2=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='one_hot')
layer1(a[:,1]),layer2(a[:,1])
(,
)
这个模式,笔者认为大多数应该是输入多列(笔者认为应该是同一度量或者说同一类型的数据)(否则输入一类就变成独热码了)。输入数据就输入二维数据。
# 对于划分好的类别(下标),每个样本有什么类别就在对应的列填上1(有点像独热码,但是独热码每行只有一个1,这里可以有多个1)
# 比如这里第一个样本[1,1,1,1,1],只有下标1,就在下标为1的列填上1,说明有类别1
# 预处理,选这个。返回了二维矩阵。
layer3=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='multi_hot')
layer1(a),layer3(a)
(,
)
layer3=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='multi_hot')
layer1(a[:,1]),layer3(a[:,1])
(,
)
# 或者选择这个。不过这个只处理一列,相当于独热码。
layer3=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='multi_hot')
layer1(a[:,[1]]),layer3(a[:,[1]])
(,
)
笔者认为应该是输入多列(笔者认为应该是同一度量或者说同一类型的数据)(否则输入一类就变成独热码了)。输入数据就输入二维数据。
# 对于划分好的类别,计算类别的个数,在对应的下标,填上个数。
# 如果将转换后的矩阵数值大于0的,都置为1,则变成multi_hot输出的矩阵。multi_hot说明存在,count计算个数
# 预处理,选择这个。
layer4 = tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='count')
layer1(a),layer4(a)
(,
)
layer4 = tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='count')
layer1(a[:,1]),layer4(a[:,1])
(,
)
# 或者选择这个。不过这个只处理一列也相当于独热码。
layer4 = tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='count')
layer1(a[:,[1]]),layer4(a[:,[1]])
(,
)
需要指定划分的区域个数,然后经过
adapt()
(相当于适应数据)之后可以加入模型。
layer = tf.keras.layers.Discretization(num_bins=4,output_mode='int')
layer.adapt(a) # 这个学习的区域范围会变化,不是固定不变的。每次运行可能不一样
a,layer(a)
(array([[1, 9, 3, 2, 1],
[4, 6, 0, 2, 1],
[5, 6, 8, 0, 0],
[0, 9, 8, 2, 5],
[1, 8, 7, 4, 9]]),
)
- 四个模式
int
,one_hot
,multi_hot
,count
,前两个应该是输入(None,1),后面两个应该是输入(None,>1)。都是输入二维数据- 四个模式要么设置
bin_boundaries
,要么设置num_bins
,而且都要adapt()
- 在
tf.keras.layers.TextVectorization
层中只能adapt
一维的向量或者最后一维是1size的。tf.keras.layers.TextVectorization
层,输出都是二维。- 如果不想adapt,可以设置
vocabulary
- 在输出模式中,由于对
tf_idf
不熟悉,所以不测试。
b = np.array([['male on'],['female off'],['male off'],['female on']])
b
array([['male on'],
['female off'],
['male off'],
['female on']], dtype='
int输出模式
这里是对字符串划分编码(下标),如果只是对标签编码,只要传入一列标签值。这里可以看出大多是对文档(包括多个单词的一个字符串)处理。
# 对类别编码(下标),从2开始(后面无论是one_hot还是embed列数都要是:类别数+2(>=2)(oov=2)),如果在adapt之后有新的值传入,编码为1,注意输出二维。
text_layer = tf.keras.layers.TextVectorization(output_mode='int')
label = ['a','b','c','b','a','b','c']
text_layer.adapt(label)
text_layer(label),text_layer(label+['d']),tf.reshape(text_layer(label),-1)
(,
,
)
# 对字符串编码,通过空格分隔字符串(分隔字符可以不分割:None,空格: whitespace(默认),每个字符:character),编码下标。
text_layer1=tf.keras.layers.TextVectorization(max_tokens=5000,output_sequence_length=4,output_mode='int')
text_layer1.adapt(b)
text_layer1(b)
# 这里没有one_hot模式,但是可以在后面接一自定义层将其转换为独热码。注意独热码要输入一维。
tf.one_hot([1,0,2,5],depth=5)
multi_hot输出模式
原理与上面的连续值离散化一样,oov=1(从1下标开始)类别数(4)+1 = 5(列)
text_layer2=tf.keras.layers.TextVectorization(max_tokens=5000,output_mode='multi_hot')
text_layer2.adapt(b)
text_layer2(b)
WARNING:tensorflow:5 out of the last 5 calls to .adapt_step at 0x000001C0D3A06160> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for more details.
count输出模式
原理与上面一样,oov=1(从1开始)类别数(4) + 1 =5(列)
text_layer3=tf.keras.layers.TextVectorization(max_tokens=5000,output_mode='count')
text_layer3.adapt(b)
text_layer3(b)
WARNING:tensorflow:6 out of the last 6 calls to .adapt_step at 0x000001C177D1B700> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has reduce_retracing=True option that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for more details.
使用嵌入编码(Embedding)
- 通过一个向量表示一个词,需要现将词转换为下标。
- 注意输出的维度。
- 嵌入编码可训练
embed = tf.keras.layers.Embedding(input_dim=5,output_dim=2)
embed(tf.Variable([4,2,3,2,4,2,3])),embed(text_layer(label))
(,
)
自定义预处理层(one_hot和Embedding)
- 笔者这里对字符串(一列数据(特征)),进行字符串编码的自定义层实现。
- 由于是第一次写,自定义预处理层写到笔者都要崩了(到处报错),但是错误的教训是深刻的。
- 一定要记住,要写输入层
Input
,之前由于没写输入层,导致对字符串的处理一直卡在concatenate
层(和原数据是数值的拼接)。
- 实现都很基础,就是要变换shape,为了让字符串处理后可以和其他输入拼接,所以都是输出二维,同时注意要输入
data[:,[1]]
,而不是data[:,1]
.
Input_onehot自定义层实现
还是要在前面加上Input
输入层。
data = np.array([[13,'male','on',4],[35,'female','off',8],[44,'male','off',9],[15,'female','on',5]])
# 测试代码,不用理睬。
# textvec_layer = tf.keras.layers.TextVectorization(output_mode='int',input_shape=[1,])
# textvec_layer.adapt(data[:,[1]])
# onehot_layer= tf.keras.layers.Lambda(lambda inputs_2D:tf.one_hot(inputs_2D[:,0],2+2))
# model = tf.keras.models.Sequential([textvec_layer,onehot_layer])
# model(data[:,[1]])
# 使用Lambda方法
class Input_onehot(tf.keras.layers.Layer):
def __init__(self,num_categories,oov=2,vocabulary=None,**kwargs):
self.num_categories=num_categories
self.oov = oov
self.vocabulary=vocabulary
self.textvec_layer = tf.keras.layers.TextVectorization(output_mode='int',vocabulary=self.vocabulary,input_shape=[1,])
self.onehot_layer= tf.keras.layers.Lambda(lambda inputs_2D:tf.one_hot(inputs_2D[:,0],self.num_categories+self.oov))
#self.reshape = tf.keras.layers.Reshape([self.num_categories+self.oov,])
super().__init__(**kwargs)
def adapt(self,data_sample_2D):
self.textvec_layer.adapt(data_sample_2D[:,0]) #(None,1)
def call(self,inputs_2D):
z = self.textvec_layer(inputs_2D)
z = self.onehot_layer(z)
#z = self.reshape(z)
return z
def get_config(self):
base_config = super().get_config()
return {**base_config,'num_categories':self.num_categories,'oov':self.oov,'vocabulary':self.vocabulary}
temp_layer = Input_onehot(2)
temp_layer.adapt(data[:,[1]])
temp_layer(data[:,[1]])
Input_embedding自定义层实现
还是要在前面加上Input
输入层。
# 测试代码,不用理睬。
# textvec_layer = tf.keras.layers.TextVectorization(output_mode='int',input_shape=[1,])
# textvec_layer.adapt(data[:,[1]])
# zhong = tf.keras.layers.Lambda(lambda inputs_2D:inputs_2D[:,0])
# embed = tf.keras.layers.Embedding(input_dim=4,output_dim=2)
# model = tf.keras.models.Sequential([textvec_layer,zhong,embed])
# model(data[:,[1]])
#使用reshape方法
class Input_embedding(tf.keras.layers.Layer):
def __init__(self,num_categories,outputdim,oov=2,vocabulary=None,**kwargs):
self.num_categories=num_categories
self.outputdim = outputdim
self.oov = oov
self.vocabulary=vocabulary
self.textvec_layer = tf.keras.layers.TextVectorization(output_mode='int',vocabulary=self.vocabulary,input_shape=[1,])
self.embed = tf.keras.layers.Embedding(input_dim=self.num_categories+self.oov,output_dim=self.outputdim)
self.trans = tf.keras.layers.Reshape((self.outputdim,))
super().__init__(**kwargs)
def compute_output_shape(self,batch_input_shape):
return tf.TensorShape(batch_input_shape[:-1]+[self.outputdim])
def adapt(self,data_sample_2D):
self.textvec_layer.adapt(data_sample_2D[:,0]) #(None,1)
def call(self,inputs_2D):
z = self.textvec_layer(inputs_2D) #(None,1)
z = self.embed(z)
z = self.trans(z)
return z
def get_config(self):
base_config = super().get_config()
return {**base_config,'num_categories':self.num_categories,'oov':self.oov,'outputdim':self.outputdim,'vocabulary':self.vocabulary}
temp_layer = Input_embedding(2,2)
temp_layer.adapt(data[:,[1]])
temp_layer(data[:,[1]])
使用tf.keras自带层实现包含embed的模型(1)
# 使用上面测试的Lambda 方法实现embed
num_input = tf.keras.layers.Input(shape=[2,],name='num_input')
text_input = tf.keras.layers.Input(shape=[1,],dtype=tf.string,name='text_input')
num_layer = tf.keras.layers.Dense(4,activation='elu',kernel_initializer='he_normal',name='num_layer')(num_input)
textvec_layer = tf.keras.layers.TextVectorization(output_mode='int',vocabulary=tf.unique(data[:,0])[0])(text_input)
zhong = tf.keras.layers.Lambda(lambda inputs_2D:inputs_2D[:,0])(textvec_layer)
embed = tf.keras.layers.Embedding(input_dim=4,output_dim=2)(zhong)
concat = tf.keras.layers.Concatenate(name='concat')([num_layer,embed])
output = tf.keras.layers.Dense(1,name='output')(concat)
model = tf.keras.Model(inputs=[num_input,text_input],outputs=[output])
model.summary()
Model: "model_1"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
text_input (InputLayer) [(None, 1)] 0 []
text_vectorization_6 (TextVect (None, None) 0 ['text_input[0][0]']
orization)
num_input (InputLayer) [(None, 2)] 0 []
lambda_1 (Lambda) (None,) 0 ['text_vectorization_6[0][0]']
num_layer (Dense) (None, 4) 12 ['num_input[0][0]']
embedding_2 (Embedding) (None, 2) 8 ['lambda_1[0][0]']
concat (Concatenate) (None, 6) 0 ['num_layer[0][0]',
'embedding_2[0][0]']
output (Dense) (None, 1) 7 ['concat[0][0]']
==================================================================================================
Total params: 27
Trainable params: 27
Non-trainable params: 0
__________________________________________________________________________________________________
使用tf.keras自带层实现包含embed的模型(2)
# 使用reshape方法实现embed
num_input = tf.keras.layers.Input(shape=[2,],name='num_input') # 这层数据输入层可以不要,只要在num_layer设置input_shape=[2,]
text_input = tf.keras.layers.Input(shape=[1,],dtype=tf.string,name='text_input') # 如果要输入文本,要设置输入类型为tf.string,否则会报错。
num_layer = tf.keras.layers.Dense(4,activation='elu',kernel_initializer='he_normal',name='num_layer')(num_input)
# 估计不能用text_layer直接当做第一层,设置不了输入数据类型(如果可以,欢迎评论区告知)
text_layer = tf.keras.layers.TextVectorization(output_mode='int',vocabulary=tf.unique(data[:,1])[0],name='text_layer')(text_input) #tf.unique返回列表,取第一个
embed_layer= tf.keras.layers.Embedding(input_dim=4,output_dim=2,name='embed_layer')(text_layer)
# 为了保证字符编码输出二维,这里就变成在embed之后加了reshape层。
reshape_layer = tf.keras.layers.Reshape((2,),name='reshape')(embed_layer) # 这里是将每个实例展开为一维,但是不能直接写-1,会报错。
# 将两个二维矩阵拼接。
concat = tf.keras.layers.Concatenate(name='concat')([num_layer,reshape_layer])
output = tf.keras.layers.Dense(1,name='output')(concat)
model = tf.keras.Model(inputs=[num_input,text_input],outputs=[output])
a = np.array(data[:,[0,3]],np.float32) #将数据转换为tf.float32
a = tf.cast(a,tf.float32)
model([a,data[:,[1]]]),model.summary()
Model: "model_2"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
text_input (InputLayer) [(None, 1)] 0 []
text_layer (TextVectorization) (None, None) 0 ['text_input[0][0]']
num_input (InputLayer) [(None, 2)] 0 []
embed_layer (Embedding) (None, None, 2) 8 ['text_layer[0][0]']
num_layer (Dense) (None, 4) 12 ['num_input[0][0]']
reshape (Reshape) (None, 2) 0 ['embed_layer[0][0]']
concat (Concatenate) (None, 6) 0 ['num_layer[0][0]',
'reshape[0][0]']
output (Dense) (None, 1) 7 ['concat[0][0]']
==================================================================================================
Total params: 27
Trainable params: 27
Non-trainable params: 0
__________________________________________________________________________________________________
(,
None)
使用自定义层实现包含embed的模型
## 测试,自己写的Input_embedding类终于可以啦!
# 使用reshape
num_input = tf.keras.layers.Input(shape=[2,],name='num_input')
text_input = tf.keras.layers.Input(shape=[1,],dtype=tf.string,name='text_input')
num_layer = tf.keras.layers.Dense(4,activation='elu',kernel_initializer='he_normal',name='num_layer')(num_input)
text_layer = Input_embedding(2,2,vocabulary=tf.unique(data[:,1])[0],name='text_layer')(text_input)
concat = tf.keras.layers.Concatenate(name='concat')([num_layer,text_layer])
output = tf.keras.layers.Dense(1,name='output')(concat)
model = tf.keras.Model(inputs=[num_input,text_input],outputs=[output])
model.summary()
Model: "model_3"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
num_input (InputLayer) [(None, 2)] 0 []
text_input (InputLayer) [(None, 1)] 0 []
num_layer (Dense) (None, 4) 12 ['num_input[0][0]']
text_layer (Input_embedding) (None, 2) 8 ['text_input[0][0]']
concat (Concatenate) (None, 6) 0 ['num_layer[0][0]',
'text_layer[0][0]']
output (Dense) (None, 1) 7 ['concat[0][0]']
==================================================================================================
Total params: 27
Trainable params: 27
Non-trainable params: 0
__________________________________________________________________________________________________
使用自定义层实现包含one_hot的模型
## 测试,自己写的Input_onehot终于可以啦!
# Lambda
num_input = tf.keras.layers.Input(shape=[2,],name='num_input')
text_input = tf.keras.layers.Input(shape=[1,],dtype=tf.string,name='text_input')
num_layer = tf.keras.layers.Dense(4,activation='elu',kernel_initializer='he_normal',name='num_layer')(num_input)
text_layer = Input_onehot(2,vocabulary=tf.unique(data[:,1])[0],name='text_layer')(text_input)
concat = tf.keras.layers.Concatenate(name='concat')([num_layer,text_layer])
output = tf.keras.layers.Dense(1,name='output')(concat)
model = tf.keras.Model(inputs=[num_input,text_input],outputs=[output])
model.summary()
Model: "model_4"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
num_input (InputLayer) [(None, 2)] 0 []
text_input (InputLayer) [(None, 1)] 0 []
num_layer (Dense) (None, 4) 12 ['num_input[0][0]']
text_layer (Input_onehot) (None, 4) 0 ['text_input[0][0]']
concat (Concatenate) (None, 8) 0 ['num_layer[0][0]',
'text_layer[0][0]']
output (Dense) (None, 1) 9 ['concat[0][0]']
==================================================================================================
Total params: 21
Trainable params: 21
Non-trainable params: 0
__________________________________________________________________________________________________
写了这么多,测试了两天两夜,都是泪~~.~~。大家如果觉得不错,就收藏吧!!
你可能感兴趣的:(tensorflow2,tensorflow,机器学习,python)