初始化参数直接决定了模型是否能快速收敛,从梯度下降的过程看,初始化参数$\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非常巨大,要经过多轮迭代才能收敛~
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:感恩谷歌~
