初始化参数直接决定了模型是否能快速收敛,从梯度下降的过程看,初始化参数$\theta_0$应该是对$f(x;\theta)$& 数据集 从全局上考虑之后得到一个比较直觉的猜测。 如果你的直觉准那么模型收敛的速度会比较快,或者不至于掉进一个局部最优解爬不出来。

之前看过一个调整初始bias处理不均衡样本的例子,还挺有趣的,这里给乡亲们介绍下。例子来自tenosrflow的tutorials,地址在这 https://www.tensorflow.org/tutorials/structured_data/imbalanced_data

1 简述一下例子

例子比较简单,是一个严重不均衡的二分类问题,数据来自Kaggle的信用卡交易记录,目标识别里面伪造的交易记录。

1.1 数据准备

用pandas加载一下数据(看到URL里的“google”字样,大概也许需要一把🪜):

raw_df = pd.read_csv('https://storage.googleapis.com/download.tensorflow.org/data/creditcard.csv')

样本里的Class字段标记是否为假的交易记录,Class=1的表示假的记录,从样本比例上看,非常不均衡

1.2 构造模型

为了方便构造模型,这里有个函数,也是来自tutorials:

def make_model(metrics = METRICS, output_bias = None):
    if output_bias:
        output_bias = tf.keras.initializers.Constant(output_bias)
    
    model = keras.Sequential([
        keras.layers.Dense(16, activation = 'relu', input_shape = (train_features.shape[-1], )),
        keras.layers.Dropout(0.5),
        keras.layers.Dense(1, activation = 'sigmoid', bias_initializer = output_bias)
    ])
    
    model.compile(
        optimizer = keras.optimizers.Adam(lr=1e-3),
        loss = keras.losses.BinaryCrossentropy(),
        metrics=metrics
    )

    return model

结构比较简单,模型只有一层,16个神经元,输出层只有一个神经元,因为是二分类问题,带了一个sigmoid的激活函数,输出该样本是伪造交易的概率,即$P(\hat{y}=1|x)$。

1.3 初始模型效果

现在直接用默认初始化的模型(还没训练)输出样本的前1000个,看概率数据情况:

output = model.predict(train_features[:1000])
plt.hist(output, bins=20)

从直方图上看,输出的值大都集中在0.6以后,因为模型输出的样本是伪造交易的概率,这图明显不符合数据集的实际分布,于是会导致Loss非常巨大,要经过多轮迭代才能收敛~

模型的loss使用BinaryCrossEntropy,当前这1000个样本的loss

tf.reduce_mean(tf.keras.losses.binary_crossentropy(y_true=train_labels[:1000], y_pred=output))

我这边输出:1.8788428,我想你可能会问~没有对比怎么知道大不大?是直觉~纯纯的“我觉得”~因为模型输出的分布和实际的分布差异巨大,所以你就想~凭空想~这个loss是非常大的。

2 设定bias

初始化的参数跟实际样本的概率分布差异比较大,会导致loss比较大(Cross Entropy),这样需要经过好多轮的迭代才能收敛,或者有可能无法收敛的。我们直接给他一个大的bias,让模型的初始输出就接近真实的分布。

2.1 理论推导

中间层的神经元比较多,还得考虑链式法则,调整起来是比较麻烦的,输出层只有一个神经元比较好调整,计算逻辑比较简单

Linear层的形式(一个样本的计算过程)

$$\hat{y}= \textbf{w} \cdot \textbf{x}+ b$$

因为使用了sigmoid激活函数,最后一层的输出是

$$\sigma(\hat{y}) = \frac{1}{1 + exp( -\textbf{w} \cdot \textbf{x}- b)}$$

从概率角度看上面输出的是$P(\hat{y}=1|\textbf{x})$,那么$P(\hat{y}=0|\textbf{x})$是

$$P(\hat{y}=0|\textbf{x}) = \frac{exp(-\textbf{w} \cdot \textbf{x}- b))}{1 + exp(-\textbf{w} \cdot \textbf{x}- b))}$$

根据1.1中给出的实际样本,负样本和正样本的比例应该是$$\frac{284315}{492}$$,也就是说期望模型输出概率最好是符合这个概率的

$$\frac{P(\hat{y}=0|\textbf{x})}{P(\hat{y}=1|\textbf{x})} \propto \frac{284315}{492}$$

这里注意,我没使用等号,用了等比$\propto$,因为戴帽子🎩的$\hat{y}$是预估值只是咱们期望他输出大概这么个概率,把上面的公式们化简一下:

$$\frac{P(\hat{y}=0|\textbf{x})}{P(\hat{y}=1|\textbf{x})} =\frac{exp(-\textbf{w} \cdot \textbf{x}- b))}{1 + exp(-\textbf{w} \cdot \textbf{x}- b))} \times (1 + exp(-\textbf{w} \cdot \textbf{x}- b)))=exp(-\textbf{w} \cdot \textbf{x}- b)$$

最后剩下的这部分$exp(-\textbf{w} \cdot \textbf{x}- b))=exp(-\textbf{w} \cdot \textbf{x})exp(-b)$,$w$我们不动,$b$本来就是bias嘛,通过调整它来影响模型的输出,使得输出分布接近样本的实际分布

$$exp(-b) = \frac{284315}{492} \rightarrow b = -\log(\frac{284315}{492}) = -6.35936$$

2.2 实践一下

于是我们根据计算的结果重新构造一个模型:

initial_bias = -np.log(284315 / 492)
model2 = make_model(output_bias=initial_bias)

看一下调整后初始模型(未训练)的输出情况

output2 = model2.predict(train_features[:1000])
plt.hist(output2, bins=20)

毕竟实际的数据集大多数都是0嘛,这也算合理。看一下当前的Loss

tf.reduce_mean(tf.keras.losses.binary_crossentropy(y_true=train_labels[:1000], y_pred=output2))

瞬间就跌倒了:0.008621918,这么看来相比之前没调整的初始化参数,缩小了200多倍~~

3 对比

来把两个模型分别迭代了20轮,观察一下Loss的变化情况:

实线是train loss, 虚线是val loss。蓝色是使用默认初始化参数的模型输出的,橘色是人为给定bias的模型,明显橘色在一开始loss就比较低,后面一直也都保持的比较好~蓝色在训练了20轮后也没有达到橘色的Loss~

PS:感恩谷歌~

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注