赞
踩
模型训练中,如果希望模型更偏向某一类数据或更聚焦于某一批样本,可以采用对数据类别和数据加权的形式达到上述效果。keras 默认设置下,样本的权重取决于其在数据集中的频率,即原始样本的分布。有两种方法可以对数据进行加权,而与采样频率无关。
如下代码为方便测试跑通准备,分别构建了深度模型并加载了手写数字识别数据,可以直接忽略看后面~
- def get_uncompiled_model():
- inputs = keras.Input(shape=(784,), name="digits")
- x = layers.Dense(64, activation="relu", name="dense_1")(inputs)
- x = layers.Dense(64, activation="relu", name="dense_2")(x)
- outputs = layers.Dense(10, activation="softmax", name="predictions")(x)
- model = keras.Model(inputs=inputs, outputs=outputs)
- return model
-
- def get_compiled_model():
- model = get_uncompiled_model()
- model.compile(
- optimizer="rmsprop",
- loss="sparse_categorical_crossentropy",
- metrics=["sparse_categorical_accuracy"],
- )
- return model
-
- # 加载MNIST数据集
- (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
- print("训练集大小 {} 测试集大小 {}".format(len(x_train),len(x_test)))
- # Preprocess the data (these are NumPy arrays)
- x_train = x_train.reshape(60000, 784).astype("float32") / 255
- x_test = x_test.reshape(10000, 784).astype("float32") / 255
-
- y_train = y_train.astype("float32")
- y_test = y_test.astype("float32")
-
- # Reserve 10,000 samples for validation
- # 0-50000 训练 50000-60000 评估
- x_val = x_train[-10000:]
- y_val = y_train[-10000:]
- x_train = x_train[:-10000]
- y_train = y_train[:-10000]
- print("验证集大小 {} 验证集大小 {}".format(len(x_val),len(y_val)))
类别加权通过将字典传递给 Model.fit() 的 class_weight 参数来设置的。该词典将类别索引映射到应用于属于该类别的样本的权重。这可用于平衡类而不重采样,或用于训练对特定类更为重视的模型。所以一般类别加权有如下两种诉求:
->平衡样本:平衡类,如果 A B 两个类别A少B多,则可以调高A从而达到平衡
->加权样本:A B一样 但是更重视A类,则给A类添加权重
比如类 "0" 是数据中类 "1" 的一半,但是想在训练时让 0 和 1 有均衡的训练效果,则可以使用Model.fit(..., class_weight={0: 1., 1: 0.5})。下面在手写数字识别上尝试一下类别加权:
- def addWeightByClassWeight():
- import numpy as np
-
- class_weight = {
- 0: 1.0,
- 1: 1.0,
- 2: 1.0,
- 3: 1.0,
- 4: 1.0,
- # Set weight "2" for class "5",
- # making this class 2x more important
- 5: 2.0,
- 6: 1.0,
- 7: 1.0,
- 8: 1.0,
- 9: 1.0,
- }
-
- print("Fit with class weight")
- model = get_compiled_model()
- model.fit(x_train, y_train, class_weight=class_weight, batch_size=64, epochs=1)
-
- addWeightByClassWeight()
正常训练后来看下训练后的模型参数:
weight_Dense_1, bias_Dense_1 = model.get_layer('predictions').get_weights()
这里取最后一层 layer 的权重和截距,最后一层的参数个数 64 x 10,我们取 0-9 共10个 [64,1] 的向量看下各组参数,不好区分
再看下截距项
- [-0.06948844 0.05270129 0.01319615 -0.02406324 -0.00456933 0.07021471
- -0.04232612 0.02836654 -0.07063926 -0.00124919]
5 的截距项相对其他数字还是较高的,当然每次训练结果不一定,也有其他数字截距和 5 相近的情况,但是多次试验下来看,整体 5 的平均截距还是大于其他参数。
使用 NumPy 数据进行训练时:将sample_weight参数传递给Model.fit() 。使用 tf.data 或其他任何迭代器进行训练时:传入(input_batch, label_batch, sample_weight_batch)。sample_weights 数组是一个数字数组,用于指定批次中每个样品在计算总损失时应具有的重量。它通常用于不平衡的分类问题(其想法是将更多的权重分配给鲜为人知的类)。极端情况下使用权重为1和0时,该数组可用作损失函数的掩码 (完全丢弃某些样本对总损失的贡献)。这里给出 tf.DataSet 的使用方法:
- def addWeightBySampleWeight():
- sample_weight = np.ones(shape=(len(y_train),))
- # 通过对最终预测标签提权重 它通常用于不平衡的分类问题 因为给y_train增加了量 所以在损失函数会有更多贡献 变向提高了该类的重要性
- sample_weight[y_train == 5] = 2.0
-
- # Create a Dataset that includes sample weights
- # (3rd element in the return tuple).
- train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train, sample_weight))
-
- # Shuffle and slice the dataset.
- train_dataset = train_dataset.shuffle(buffer_size=1024).batch(64)
-
- model = get_compiled_model()
- model.fit(train_dataset, epochs=10)
-
- addWeightBySampleWeight()
同上,看下最后一层的参数,还是不好分辨,但是和上面有的区别就是参数更加收敛了,原因是这次训练了 10 个epoch,但是上面只训练了 1 个epoch。
这次看截距项就很明显了:
总的来说, class_weights 针对 类别不均匀的实验,例如正负样本悬殊或者多分类中,某类样本特别多或者特别少。如果样本的数量很多,则它的权重就越低,反之越高。sample_weights 针对样本加权,思路和类别权重类似,即样本数多的类别样本权重低,反之样本权重高。sample_weights 和 class_weights 如果只看上面例子会觉得比较类似,但是需要注意两边的维度,class_weights 的 size 是 distinct(class) 的长度,而 sample_weights 的 size 和训练样本的长度是一致的。这是因为class_weights是针对所有类别,而 sample_weights 是针对所有样本,假设有一批 10000 数量的样本,前5000是真实可靠的数据,后5000是可能出错的数据,即置信度不同,那么就可以调整 sample_weights,提高前 5000 样本的权重,降低后面 5000 样本的权重。除了在 DataSet 截断传入 sample_weights,sample_weights 也可以在自定义 Model 时写在反向传播的逻辑里,有兴趣可以打开 tensorflow官方API。
更多推荐算法相关深度学习:深度学习导读专栏
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。