通常我们在训练神经网络模型的时候会出现过拟合的情况,模型训练的过于好,导致泛化能力不强。一般我们引入正则化可以解决过拟合问题,正则化分为两类,L1型正则化,L2型正则化。接下来我们做简单介绍并给出tensorflow代码实现。完整代码位于我的github,链接:https://github.com/JohnLeek/Tensorflow-study,day2_regularizationfree.py(未使用正则化),和day2_regularization_L2.py(使用正则化),数据集为dot.csv。
通常L1正则化大概率会使得很多参数变为零,因此该方法可通过稀疏参数,即减少参数的数量,降低复杂度;L2正则化会使得参数很接近零但不为零,因此该方法可以通过减小参数值的大小降低复杂度。
一、L1型正则化和L2型正则化公式
L1正则化公式如上,这里不做推导和展开,只需要记住公式即可,其中,Ein 是未包含正则化项的训练样本误差(损失函数),λ 是正则化参数,w为正则化项。
本节使用的数据集为我自己准备的(dot.csv),预备知识是绘制网格坐标点,参考我的这篇博客https://blog.csdn.net/JohnLeeK/article/details/105758916,
其中C1和C2为x_trian,C3为y_trian,注意C3这一列取值只有0和1。我们需要做的工作就是训练我们自己的神经网络将y为0和1的点区分开。
我的思路是这样的:首先我们构建一个神经网络将dot.csv中的数据送入神经网络进行训练得到训练好的模型,然后我们绘制网格坐标点,将所有的点送入神经网络进行预测,我们保留所有预测结果,最后把所有结果为0.5的点连接(预备知识matplotlib绘制轮廓图和散点图)起来这样就可以区分0和1了。
-
df = pd.read_csv(
"dot.csv")
-
x_data = np.array(df[[
"x1",
"x2"]])
-
y_data = np.array(df[
"y_c"])
-
-
x_train = np.vstack(x_data).reshape(
-1,
2)
-
y_train = np.vstack(y_data).reshape(
-1,
1)
-
-
x_train = tf.cast(x_train,dtype=tf.float32)
-
y_train = tf.cast(y_train,dtype=tf.float32)
然后我们根据下图开始搭建神经网络结构
如图,一共是两个输入对应dot.csv中的x1,x2,隐藏层一共有十一个神经元,输出层为一个神经元。
-
w1 = tf.Variable(tf.random.normal([
2,
11]),dtype=tf.float32)
-
b1 = tf.Variable(tf.random.normal([
11]))
-
-
w2 = tf.Variable(tf.random.normal([
11,
1]),dtype=tf.float32)
-
b2 = tf.Variable(tf.random.normal([
1]),dtype=tf.float32)
指定学习率还有训练次数
-
lr = 0.005
-
epoch = 1000
开始训练我们的神经网络模型
-
for epoch
in range(epoch):
-
for step,(x_train,y_train)
in enumerate(train_db):
-
with tf.GradientTape()
as tp:
#采用梯度下降的方法求神经网络参数
-
-
h1 = tf.matmul(x_train,w1)+b1
-
h1 = tf.nn.relu(h1)
#使用relu激活函数
-
y = tf.matmul(h1,w2)+b2
-
-
loss = tf.reduce_mean(tf.square(y_train-y))
#使用均方误差函数作为 损失函数
-
-
variables = [w1,b1,w2,b2]
-
#参数自更新
-
grads = tp.gradient(loss,variables)
-
w1.assign_sub(lr*grads[
0])
-
b1.assign_sub(lr*grads[
1])
-
w2.assign_sub(lr*grads[
2])
-
b2.assign_sub(lr*grads[
3])
-
if epoch %
20 ==
0:
-
print(
"epoch:",epoch,
"loss:",float(loss))
重点来了,生成网格坐标点:
-
xx,yy = np.mgrid[
-3:
3:
1,
-3:
3:
1]
-
grid = np.c_[xx.ravel(),yy.ravel()]
-
grid = tf.cast(grid,tf.float32)
这里我们以1为步长,从-3到2生成网格坐标点,然后进行拉直操作再讲xx,yy进行配对,然后将numpy数据类型转化为tensor类型。
接下来就是预测的部分,我们将所有的网格坐标点送入训练好的模型做预测,然后保存所有预测结果
-
for x_test
in grid:
-
h1 = tf.matmul([x_test],w1)+b1
-
h1 = tf.nn.relu(h1)
-
y = tf.matmul(h1,w2)+b2
-
probs.append(y)
-
x1 = x_data[:,
0]
-
x2 = x_data[:,
1]
最后我们绘制图片,这里需要用到matplotlib中的散点图还有轮廓图,不会的可以自行百度,后面我要是有时间我会更新matplotlib绘图进阶操作。
-
Y_c = [[
"red"
if y
else
"blue"]
for y
in y_train]
#y为1的点为红色,0的点为蓝色
-
x1 = x_data[:,
0]
-
x2 = x_data[:,
1]
-
probs = np.array(probs).reshape(xx.shape)
-
plt.scatter(x1,x2,color=np.squeeze(Y_c))
-
plt.contour(xx,yy,probs,levels=[
.5])
-
plt.savefig(
"./re_free")
三、L2型正则化代码实现
做正则化我们要修改的地方就是在训练的过程中指定正则化方式,tensorflow已经帮我们实现了,我们只需要做一部分简单的工作即可。
-
for epoch
in range(epoch):
-
for step,(x_train,y_train)
in enumerate(train_db):
-
with tf.GradientTape()
as tp:
-
h1 = tf.matmul(x_train,w1)+b1
-
h1 = tf.nn.relu(h1)
-
y = tf.matmul(h1,w2)+b2
-
-
loss_mes = tf.reduce_mean(tf.square(y_train-y))
-
loss_regularization = []
-
loss_regularization.append(tf.nn.l2_loss(w1))
#使用L2正则化
-
loss_regularization.append(tf.nn.l2_loss(w2)))
#使用L2正则化
-
loss_regularization = tf.reduce_sum(loss_regularization)
-
loss = loss_mes+
0.03*loss_regularization
-
-
variables = [w1,b1,w2,b2]
-
grads = tp.gradient(loss,variables)
-
w1.assign_sub(lr*grads[
0])
-
b1.assign_sub(lr*grads[
1])
-
w2.assign_sub(lr*grads[
2])
-
b2.assign_sub(lr*grads[
3])
-
if epoch %
20 ==
0:
-
print(
"epoch:",epoch,
"loss:",float(loss))
这里有问题的可以看看我给出的L2正则化公式,然后结合代码看看应该就能明白了,
总的来看对比结果不是很明显,但是我们也可以发现为使用正则化的时候,做分割0和1的时候在将所有的红点和蓝点分开,圈内基本是红点,有点过拟合,二使用L2正则化以后分割的曲线只是尽可能使更多的红点落在圈内。
到这里正则化的内容就讲解完了,我给出了L2正则化的代码,但是未给出L1正则化,有时间的读者可以自行实现.。
转载:https://blog.csdn.net/JohnLeeK/article/details/106335532