tensorflow2笔记:简单数据预处理(TF专属)

预先导入数据

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)

数据API

  • 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)
(<tf.Tensor: shape=(5, 8), dtype=float64, numpy=
array([[ 1.49936543,  0.69295606,  0.17214511, -0.03404754, -0.47769067,
        -0.18911355, -1.90258047,  1.69674941],
       [-1.45498335, -3.27994787, -0.84853789,  0.31964019, -1.23181805,
         0.21437985, -1.48121256,  1.36732314],
       [-2.45057457, -0.73728936, -1.98759026,  0.33934708, -0.3717243 ,
        -0.24314346, -1.4343939 ,  1.22756654],
       [ 1.04341686, -1.05512167, -0.33146131,  0.01883375,  0.28703334,
        -0.20471606, -1.48121256,  1.12774039],
       [-1.76101948,  1.96428532, -1.17506422, -0.39201263,  1.58865366,
         0.25664931, -1.49994002,  1.39727098]])>, <tf.Tensor: shape=(5,), dtype=float64, numpy=array([5.00001, 1.375  , 3.25   , 5.00001, 1.408  ])>)
(<tf.Tensor: shape=(5, 8), dtype=float64, numpy=
array([[ 0.79938805,  3.71236305,  1.21865315, -0.23253737, -0.70375228,
        -0.15856083,  2.7418303 , -1.87702652],
       [ 2.89753049,  0.21620759,  1.16258848, -0.53988317, -1.07993291,
         0.02099643,  1.56200015, -2.36617463],
       [-1.79691845,  1.646453  , -1.33595353,  0.04766414,  0.06803616,
         0.13342085,  2.02082298, -2.66565306],
       [-0.63551819, -1.21403783, -0.16055853, -0.42936727, -0.89449175,
        -0.14328661,  2.66692045, -3.08492286],
       [-0.17999073, -0.73728936, -1.1617794 ,  0.0164999 ,  4.91776398,
        -0.27524695, -1.39693897,  1.42721882]])>, <tf.Tensor: shape=(5,), dtype=float64, numpy=array([2.338, 3.325, 1.055, 1.762, 2.25 ])>)
(<tf.Tensor: shape=(5, 8), dtype=float64, numpy=
array([[ 1.61253668,  2.91778226, -0.42219841, -0.06155567, -1.49143567,
        -0.17384215, -1.29393793,  0.9280881 ],
       [-0.8213296 , -1.21403783, -0.60980633,  0.02916445,  0.1068905 ,
        -0.21129153,  1.26236074, -2.36617463],
       [ 0.33964957, -3.59778018,  1.00670563, -0.04921188,  0.75151929,
        -0.07770384, -1.07857211,  2.30568888],
       [ 0.3537565 , -2.00861861,  0.01195357, -0.19573477, -0.24279854,
         0.14250147,  2.23618881, -2.3462094 ],
       [ 0.15804918,  1.16970453,  0.01984725, -0.52330752, -1.57974098,
        -0.03254749,  1.94591313, -2.57580953]])>, <tf.Tensor: shape=(5,), dtype=float64, numpy=array([2.53 , 2.643, 1.631, 1.25 , 1.915])>)

乱序数据

我们知道训练集中实例相互独立且分布均匀时,梯度下降效果最佳。

shuffle乱序(小数据集)

shuffle乱序,需要设置缓冲区大小(也可设置随机种子),适合可以装入RAM的小型数据集。
原理:先将缓冲区填满,如果要求提供一个数据,从缓冲区随机取出一个数据,并用源数据集的新元素替换它,直到遍历完源数据集,然后把缓冲区抽空为止。
注意:缓冲区必须足够大,不然乱序不太有效。当然也不要超出数据集大小。

dataset=tf.data.Dataset.range(10).repeat(3)
dataset
<RepeatDataset element_spec=TensorSpec(shape=(), dtype=tf.int64, name=None)>
  • 如果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))
(<tf.Tensor: shape=(8,), dtype=float32, numpy=
array([ 0.7496827 ,  0.34647802,  0.08607256, -0.01702377, -0.23884533,
       -0.09455678, -0.95129025,  0.8483747 ], dtype=float32)>, <tf.Tensor: shape=(1,), dtype=float32, numpy=array([5.00001], dtype=float32)>)
(<tf.Tensor: shape=(8,), dtype=float32, numpy=
array([-0.7274917 , -1.6399739 , -0.42426893,  0.1598201 , -0.61590904,
        0.10718992, -0.7406063 ,  0.6836616 ], dtype=float32)>, <tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.375], dtype=float32)>)
(<tf.Tensor: shape=(8,), dtype=float32, numpy=
array([-1.2252873 , -0.36864468, -0.99379516,  0.16967353, -0.18586215,
       -0.12157173, -0.71719694,  0.61378324], dtype=float32)>, <tf.Tensor: shape=(1,), dtype=float32, numpy=array([3.25], dtype=float32)>)

将上述所有的操作合在一起,如下

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))

在这里插入图片描述

小结

  • 对于batchprefetch大多要放在最后,其他的向map,shuffle,filter,repeat大多放在前面操作。
  • batch设置32,64,128等,可以设置drop_remainder=True,删除不完整的batch
  • shuffle要设置大一点的buffer_size
  • repeat,可以在shuffle前或者后,buffer_size如果较小,repeat在后面,如果很大,都可以。
  • mapinterleave都可以设置num_parallel_calls,多线程。
  • prefetch:对训练速率有较大影响,但是要考虑好预取的数量,不要爆炸。
  • 以上都是建议,如果有错误的,欢迎在评论区留言。
  • 如果觉得还不错,不要忘了收藏(包括函数)

TFRecord格式

TFRecord格式是Tensorflow首选的格式。这是一种非常简单的二进制格式,只包含二进制记录序列(每个记录由一个长度,一个用于检查长度的CRC校验和,实际数据以及最后一个CRC检验和组成)。
这部分比较少,因为比较不懂。

TFRecord的简单创建读取

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压缩和读取

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)

协议缓冲区(TensorFlow协议):

这是一种可移植、可扩展且高效的二进制格式。如果上述的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())
加载和解析Example
# 定义特征描述字典
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': <tensorflow.python.framework.sparse_tensor.SparseTensor at 0x1c0e1879ca0>,
 'id': <tf.Tensor: shape=(), dtype=int64, numpy=123>,
 'name': <tf.Tensor: shape=(), dtype=string, numpy=b'Alice'>}
parse_example['emails'].values  #对于可变长度的张量,可以这样访问。
<tf.Tensor: shape=(2,), dtype=string, numpy=array([b'a@b.com', b'c@d.com'], dtype=object)>
  • 像上面这样的例子,我们在写入的时候,可以写个函数,将数据序列化写入。读出时也可以写个函数,当然可以不用这样一个一个解析,可以读取时加上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)
<tf.Tensor: shape=(5, 5), dtype=float32, numpy=
array([[-0.61885273,  1.0320938 , -0.69020134,  0.        , -0.6527299 ],
       [ 0.92827904, -1.1795356 , -1.6313851 ,  0.        , -0.6527299 ],
       [ 1.4439896 , -1.1795356 ,  0.8784382 , -1.5811388 , -0.9494253 ],
       [-1.1345633 ,  1.0320938 ,  0.8784382 ,  0.        ,  0.5340517 ],
       [-0.61885273,  0.294884  ,  0.5647103 ,  1.5811388 ,  1.7208334 ]],
      dtype=float32)>

在实践中,如果数据集太大,可以随机抽样,在Normalization()加入model之前,先对抽样实例进行adapt()(相当于StandardScaler.fit()),然后再加入model。

连续数值离散化

下面展示了四种模式,不同输入的输出。但是主要的思路还是将连续数值,划分多个区域,给区域编码进行离散。
除了独热码模式,其他模式,输出维数与输入维数一样。

int输出模式

笔者认为,这个模式大多数应该是只输入一列,转换为下标(类别)。输入一列,最好是(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]]),
 <tf.Tensor: shape=(5, 5), dtype=int64, numpy=
 array([[1, 2, 1, 1, 1],
        [1, 2, 1, 1, 1],
        [2, 2, 2, 1, 1],
        [1, 2, 2, 1, 2],
        [1, 2, 2, 1, 2]], dtype=int64)>)
# 或者选择这个。
layer1=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='int')
a[:,[1]],layer1(a[:,[1]])
(array([[9],
        [6],
        [6],
        [9],
        [8]]),
 <tf.Tensor: shape=(5, 1), dtype=int64, numpy=
 array([[2],
        [2],
        [2],
        [2],
        [2]], dtype=int64)>)
layer1=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='int')
a[:,1],layer1(a[:,1])
(array([9, 6, 6, 9, 8]),
 <tf.Tensor: shape=(5,), dtype=int64, numpy=array([2, 2, 2, 2, 2], dtype=int64)>)
独热码输出模式

这个模式只能输入一列,为了便于记忆,与上面一样都输入(None,1)的二维数据。

# 对上面划分好的类别(下标),编码为独热码。
# 数据预处理,选择这个或下面都可以,都是返回二维数组。
layer2=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='one_hot')
layer1(a[:,[1]]),layer2(a[:,[1]])
(<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
 array([[2],
        [2],
        [2],
        [2],
        [2]], dtype=int64)>,
 <tf.Tensor: shape=(5, 4), dtype=float32, numpy=
 array([[0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.]], dtype=float32)>)
# 或者这个
layer2=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='one_hot')
layer1(a[:,1]),layer2(a[:,1])
(<tf.Tensor: shape=(5,), dtype=int64, numpy=array([2, 2, 2, 2, 2], dtype=int64)>,
 <tf.Tensor: shape=(5, 4), dtype=float32, numpy=
 array([[0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.]], dtype=float32)>)
multi_hot输出模式

这个模式,笔者认为大多数应该是输入多列(笔者认为应该是同一度量或者说同一类型的数据)(否则输入一类就变成独热码了)。输入数据就输入二维数据。

# 对于划分好的类别(下标),每个样本有什么类别就在对应的列填上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)
(<tf.Tensor: shape=(5, 5), dtype=int64, numpy=
 array([[1, 2, 1, 1, 1],
        [1, 2, 1, 1, 1],
        [2, 2, 2, 1, 1],
        [1, 2, 2, 1, 2],
        [1, 2, 2, 1, 2]], dtype=int64)>,
 <tf.Tensor: shape=(5, 4), dtype=float32, numpy=
 array([[0., 1., 1., 0.],
        [0., 1., 1., 0.],
        [0., 1., 1., 0.],
        [0., 1., 1., 0.],
        [0., 1., 1., 0.]], dtype=float32)>)
layer3=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='multi_hot')
layer1(a[:,1]),layer3(a[:,1])
(<tf.Tensor: shape=(5,), dtype=int64, numpy=array([2, 2, 2, 2, 2], dtype=int64)>,
 <tf.Tensor: shape=(4,), dtype=float32, numpy=array([0., 0., 1., 0.], dtype=float32)>)
# 或者选择这个。不过这个只处理一列,相当于独热码。
layer3=tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='multi_hot')
layer1(a[:,[1]]),layer3(a[:,[1]])
(<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
 array([[2],
        [2],
        [2],
        [2],
        [2]], dtype=int64)>,
 <tf.Tensor: shape=(5, 4), dtype=float32, numpy=
 array([[0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.]], dtype=float32)>)
count(计数)输出模式

笔者认为应该是输入多列(笔者认为应该是同一度量或者说同一类型的数据)(否则输入一类就变成独热码了)。输入数据就输入二维数据。

# 对于划分好的类别,计算类别的个数,在对应的下标,填上个数。
# 如果将转换后的矩阵数值大于0的,都置为1,则变成multi_hot输出的矩阵。multi_hot说明存在,count计算个数
# 预处理,选择这个。
layer4 = tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='count')
layer1(a),layer4(a)
(<tf.Tensor: shape=(5, 5), dtype=int64, numpy=
 array([[1, 2, 1, 1, 1],
        [1, 2, 1, 1, 1],
        [2, 2, 2, 1, 1],
        [1, 2, 2, 1, 2],
        [1, 2, 2, 1, 2]], dtype=int64)>,
 <tf.Tensor: shape=(5, 4), dtype=float32, numpy=
 array([[0., 4., 1., 0.],
        [0., 4., 1., 0.],
        [0., 2., 3., 0.],
        [0., 2., 3., 0.],
        [0., 2., 3., 0.]], dtype=float32)>)
layer4 = tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='count')
layer1(a[:,1]),layer4(a[:,1])
(<tf.Tensor: shape=(5,), dtype=int64, numpy=array([2, 2, 2, 2, 2], dtype=int64)>,
 <tf.Tensor: shape=(4,), dtype=float32, numpy=array([0., 0., 5., 0.], dtype=float32)>)
# 或者选择这个。不过这个只处理一列也相当于独热码。
layer4 = tf.keras.layers.Discretization(bin_boundaries=[0,5,10],output_mode='count')
layer1(a[:,[1]]),layer4(a[:,[1]])
(<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
 array([[2],
        [2],
        [2],
        [2],
        [2]], dtype=int64)>,
 <tf.Tensor: shape=(5, 4), dtype=float32, numpy=
 array([[0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.],
        [0., 0., 1., 0.]], dtype=float32)>)
不指定区域界线

需要指定划分的区域个数,然后经过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]]),
 <tf.Tensor: shape=(5, 5), dtype=int64, numpy=
 array([[1, 3, 2, 1, 1],
        [2, 3, 0, 1, 1],
        [2, 3, 3, 0, 0],
        [0, 3, 3, 1, 2],
        [1, 3, 3, 2, 3]], dtype=int64)>)

连续数值离散化小总结。

  • 四个模式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='<U10')
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)
(<tf.Tensor: shape=(7, 1), dtype=int64, numpy=
 array([[4],
        [2],
        [3],
        [2],
        [4],
        [2],
        [3]], dtype=int64)>,
 <tf.Tensor: shape=(8, 1), dtype=int64, numpy=
 array([[4],
        [2],
        [3],
        [2],
        [4],
        [2],
        [3],
        [1]], dtype=int64)>,
 <tf.Tensor: shape=(7,), dtype=int64, numpy=array([4, 2, 3, 2, 4, 2, 3], dtype=int64)>)
# 对字符串编码,通过空格分隔字符串(分隔字符可以不分割: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)
<tf.Tensor: shape=(4, 4), dtype=int64, numpy=
array([[4, 2, 0, 0],
       [5, 3, 0, 0],
       [4, 3, 0, 0],
       [5, 2, 0, 0]], dtype=int64)>
# 这里没有one_hot模式,但是可以在后面接一自定义层将其转换为独热码。注意独热码要输入一维。
tf.one_hot([1,0,2,5],depth=5)
<tf.Tensor: shape=(4, 5), dtype=float32, numpy=
array([[0., 1., 0., 0., 0.],
       [1., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0.]], dtype=float32)>
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 <function PreprocessingLayer.make_adapt_function.<locals>.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.





<tf.Tensor: shape=(4, 5), dtype=float32, numpy=
array([[0., 1., 0., 1., 0.],
       [0., 0., 1., 0., 1.],
       [0., 0., 1., 1., 0.],
       [0., 1., 0., 0., 1.]], dtype=float32)>
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 <function PreprocessingLayer.make_adapt_function.<locals>.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.





<tf.Tensor: shape=(4, 5), dtype=float32, numpy=
array([[0., 1., 0., 1., 0.],
       [0., 0., 1., 0., 1.],
       [0., 0., 1., 1., 0.],
       [0., 1., 0., 0., 1.]], dtype=float32)>
使用嵌入编码(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))
(<tf.Tensor: shape=(7, 2), dtype=float32, numpy=
 array([[-0.03596386,  0.01693531],
        [-0.02953193, -0.01553446],
        [ 0.04661288, -0.00266797],
        [-0.02953193, -0.01553446],
        [-0.03596386,  0.01693531],
        [-0.02953193, -0.01553446],
        [ 0.04661288, -0.00266797]], dtype=float32)>,
 <tf.Tensor: shape=(7, 1, 2), dtype=float32, numpy=
 array([[[-0.03596386,  0.01693531]],
 
        [[-0.02953193, -0.01553446]],
 
        [[ 0.04661288, -0.00266797]],
 
        [[-0.02953193, -0.01553446]],
 
        [[-0.03596386,  0.01693531]],
 
        [[-0.02953193, -0.01553446]],
 
        [[ 0.04661288, -0.00266797]]], dtype=float32)>)

自定义预处理层(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]])
<tf.Tensor: shape=(4, 4), dtype=float32, numpy=
array([[0., 0., 1., 0.],
       [0., 0., 0., 1.],
       [0., 0., 1., 0.],
       [0., 0., 0., 1.]], dtype=float32)>

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.Tensor: shape=(4, 2), dtype=float32, numpy=
array([[-0.00428744,  0.02857419],
       [ 0.01162884, -0.00078434],
       [-0.00428744,  0.02857419],
       [ 0.01162884, -0.00078434]], dtype=float32)>

使用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
__________________________________________________________________________________________________





(<tf.Tensor: shape=(4, 1), dtype=float32, numpy=
 array([[19.181803],
        [55.4038  ],
        [70.939125],
        [21.767748]], dtype=float32)>,
 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
__________________________________________________________________________________________________

写了这么多,测试了两天两夜,都是泪~~.~~。大家如果觉得不错,就收藏吧!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

起名大废废

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值