我正在阅读Aurélien Géron的书,在第13章,我尝试使用Tensorflow数据集(而不是Numpy数组)来训练Keras模型。
1. 数据集
数据集来自sklearn.datasets.fetch_california_housing
,我已将其导出为CSV格式。前几行看起来像这样:
MedInc,HouseAge,AveRooms,AveBedrms,Population,AveOccup,Latitude,Longitude,MedHouseVal3.3083,20.0,5.387832699619772,1.0,853.0,3.2433460076045626,37.53,-120.79,1.0832.1932,29.0,5.164444444444444,1.1288888888888888,726.0,3.2266666666666666,37.53,-120.8,0.9061.875,15.0,5.327102803738318,1.1495327102803738,189.0,1.766355140186916,37.53,-120.81,1.813
如你所见,这里有8个特征和1个目标(MedHouseVal
,在最后一列)。
我使用以下代码重新导入它:
def parse_csv_line(line: bytes) -> Tuple[tf.Tensor, tf.Tensor]: parsed = tf.io.decode_csv(line, record_defaults=[0.]*9) return parsed[:-1], parsed[-1:]def load_dataset_csv(paths: Union[str, List[str]]) -> tf.Dataset: return tf.data.Dataset.list_files(paths) \ .interleave(lambda p: tf.data.TextLineDataset(p).skip(1)) \ .map(parse_csv_line) \ .prefetch(1)train_set = load_dataset_csv("./housing.train.csv")val_set = load_dataset_csv("./housing.val.csv")test_set = load_dataset_csv("./housing.test.csv")
到目前为止一切顺利:
>>> list(test_set.take(1))[(<tf.Tensor: shape=(8,), dtype=float32, numpy= array([ 3.3083 , 20. , 5.3878326, 1. , 853. , 3.243346 , 37.53 , -120.79 ], dtype=float32)>, <tf.Tensor: shape=(1,), dtype=float32, numpy=array([1.083], dtype=float32)>)]
2. 模型,第一次尝试
然后我定义了我的模型:
model = keras.models.Sequential( [ keras.layers.Dense(30, input_shape=(8,)), keras.layers.BatchNormalization(), keras.layers.Dense(1), ],)model.compile(loss="mean_squared_error")model.fit( train_set, epochs=1, # debugging validation_data=val_set,)
但我得到了以下错误:
ValueError: Input 0 of layer sequential_63 is incompatible with the layer: expected axis -1 of input shape to have value 8 but received input with shape (8, 1)
3. 模型,第二次尝试
如果我将输入形状设置为(8, 1)
而不是(8,)
,我会得到以下警告
WARNING:tensorflow:Model was constructed with shape (None, 8, 1) for input KerasTensor(type_spec=TensorSpec(shape=(None, 8, 1), dtype=tf.float32, name='input_15'), name='input_15', description="created by layer 'input_15'"), but it was called on an input with incompatible shape (8, 1, 1).
当我尝试进行预测时,我得到了这个奇怪的结果:
>>> model.predict(test_set.take(1))WARNING:tensorflow:Model was constructed with shape (None, 8, 1) for input KerasTensor(type_spec=TensorSpec(shape=(None, 8, 1), dtype=tf.float32, name='dense_159_input'), name='dense_159_input', description="created by layer 'dense_159_input'"), but it was called on an input with incompatible shape (8, 1, 1).WARNING:tensorflow:6 out of the last 174 calls to <function Model.make_predict_function.<locals>.predict_function at 0x169f02b80> 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 experimental_relax_shapes=True option that relaxes argument shapes 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.array([[[2.592609 ]], [[2.591457 ]], [[2.5924652]], [[2.592768 ]], [[2.533997 ]], [[2.5926137]], [[2.590248 ]], [[2.601169 ]]], dtype=float32)
我理解Tensorflow对张量的形状不满意,但为什么它给了我8个预测结果呢?
4. 模型,第三次尝试
在这一点上,我不太确定该怎么做,所以我尝试添加一个Flatten
层:
model = keras.models.Sequential( [ keras.layers.Flatten(), keras.layers.Dense(30, input_shape=(8,)), keras.layers.BatchNormalization(), keras.layers.Dense(1), ],)
这次我没有得到警告或错误,但当我尝试进行预测时,我仍然得到了8个结果:
>>> model.predict(test_set.take(1))array([[2.5953178], [2.5949838], [2.5952766], [2.5953639], [2.5783124], [2.5953195], [2.594633 ], [2.5978017]], dtype=float32)
我真的不明白我做错了什么。尝试使用Numpy数组训练第一个模型(输入形状为(8,)
且没有Flatten
)工作得很好。
任何帮助都将不胜感激。
提前感谢!
回答:
正如官方文档中对tf.keras.Sequential
的建议,当inputs
是tf.data.Dataset
的实例时,在调用tf.keras.Sequential.fit()
时不需要提供batch_size
,
整数或None。每梯度更新的样本数。如果未指定,batch_size将默认为32。如果您的数据是以数据集、生成器或keras.utils.Sequence实例的形式提供的,请勿指定batch_size(因为它们会生成批次)。
在tf.data.Dataset
的情况下,fit()
方法期望一个批处理数据集。
要对tf.data.Dataset
进行批处理,请使用batch()
方法,
batched_ds = ds.batch( batch_size )
这样,数据集将提供批处理数据(形状为( batch_size , 8 )
)而不是整个数据(形状为( num_samples , 8 )
)。
提示:
要取消数据集的批处理,即将数据重新整形为( num_samples , 8 )
,请使用提供的unbatch()
方法。