第十一节,全连接网络中的优化技巧-过拟合、正则化,dropout、退化学习率等
目录
随着科研人员在使用神经网络训练时不断的尝试,为我们留下了很多有用的技巧,合理的运用这些技巧可以使自己的模型得到更好的拟合效果。
一 利用异或数据集演示过拟合
全连接网络虽然在拟合问题上比较强大,但太强大的拟合效果也带来了其它的麻烦,这就是过拟合问题。
首先我们看一个例子,这次将原有的4个异或带护具扩充成了上百个具有异或特征的数据集,然后通过全连接网络将它们进行分类。
实例描述:构建异或数据集模拟样本,在构建一个简单的多层神经网络来拟合其样本特征,观察其出现前泥河的现象,接着通过增大网络复杂性的方式来优化欠拟合问题,使其出现过拟合现象。
1. 构建异或数据集
''' 生成随机数据 ''' np.random.seed(10) #特征个数 num_features = 2 #样本个数 num_samples = 320 #n返回长度为特征的数组 正太分布 mean = np.random.randn(num_features) print('mean',mean) cov = np.eye(num_features) print('cov',cov) X,Y = generate(num_samples,mean,cov,[[3.0,0.0],[3.0,3.0],[0.0,3.0]],num_classes=4) #转换为二种类别 Y = Y % 2 xr = [] xb = [] for (l,k) in zip(Y[:],X[:]): if l == 0.0: xr.append([k[0],k[1]]) else: xb.append([k[0],k[1]]) xr = np.array(xr) xb = np.array(xb) plt.scatter(xr[:,0],xr[:,1],c='r',marker='+') plt.scatter(xb[:,0],xb[:,1],c='b',marker='o')
可以看到图上数据分为两类,左上和左下是一类,右上和右下是一类。
2 定义网络模型
''' 定义变量 ''' #学习率 learning_rate = 1e-4 #输入层节点个数 n_input = 2 #隐藏层节点个数 n_hidden = 2 #输出节点数 n_label = 1 input_x = tf.placeholder(tf.float32,[None,n_input]) input_y = tf.placeholder(tf.float32,[None,n_label]) ''' 定义学习参数 h1 代表隐藏层 h2 代表输出层 ''' weights = { 'h1':tf.Variable(tf.truncated_normal(shape=[n_input,n_hidden],stddev = 0.01)), #方差0.1 'h2':tf.Variable(tf.truncated_normal(shape=[n_hidden,n_label],stddev=0.01)) } biases = { 'h1':tf.Variable(tf.zeros([n_hidden])), 'h2':tf.Variable(tf.zeros([n_label])) } ''' 定义网络模型 ''' #隐藏层 layer_1 = tf.nn.relu(tf.add(tf.matmul(input_x,weights['h1']),biases['h1'])) #代价函数 y_pred = tf.nn.sigmoid(tf.add(tf.matmul(layer_1, weights['h2']),biases['h2'])) loss = tf.reduce_mean(tf.square(y_pred - input_y)) train = tf.train.AdamOptimizer(learning_rate).minimize(loss)
3. 训练网络并可视化显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | ''' 开始训练 ''' training_epochs = 30000 sess = tf.InteractiveSession() #初始化 sess.run(tf.global_variables_initializer()) for epoch in range (training_epochs): _,lo = sess.run([train,loss],feed_dict = {input_x:X,input_y:np.reshape(Y,[ - 1 , 1 ])}) if epoch % 1000 = = 0 : print ( 'Epoch {0} loss {1}' . format (epoch,lo)) ''' 数据可视化 ''' nb_of_xs = 200 xs1 = np.linspace( - 1 , 8 ,num = nb_of_xs) xs2 = np.linspace( - 1 , 8 ,num = nb_of_xs) #创建网格 xx,yy = np.meshgrid(xs1,xs2) #初始化和填充 classfication plane classfication_plane = np.zeros([nb_of_xs,nb_of_xs]) for i in range (nb_of_xs): for j in range (nb_of_xs): #计算每个输入样本对应的分类标签 classfication_plane[i,j] = sess.run(y_pred,feed_dict = {input_x:[[xx[i,j],yy[i,j]]]}) #创建 color map用于显示 cmap = ListedColormap([ colorConverter.to_rgba( 'r' ,alpha = 0.30 ), colorConverter.to_rgba( 'b' ,alpha = 0.30 ), ]) #显示各个样本边界 plt.contourf(xx,yy,classfication_plane,cmap = cmap) plt.show() |
可以看到,模型在迭代训练20000次之后梯度更新就放缓了,而且loss值约等于16%并且准确率不高,所可视化的图片也没有将数据完全分开。
图上这种现象就叫做欠拟合,即没有完全拟合到想要得到的真实数据情况。
4 修正模型提高拟合度
欠拟合的原因并不是模型不行,而是我们的学习方法无法更精准地学习到适合的模型参数。模型越薄弱,对训练的要求就越高。但是可以采用增加节点或者增加层的方式,让模型具有更高的拟合性,从而降低模型的训练难度。
将隐藏层的节点个数改为200,代码如下:
1 2 | #隐藏层节点个数 n_hidden = 200 |
从图中可以看到强大的全连接网络,仅仅通过一个隐藏层,使用200个神经元就可以把数据划分的那么细致。而loss值也在逐渐变小,30000次之后已经变成了0.056.
5 验证过拟合
那么对于上面的模型好不好呢?我们再取少量的数据放到模型中验证一下,然后用同样的方式在坐标系中可视化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | ''' 测试 可以看到测试集loss值和训练集loss差距较大 这是因为模型过拟合了 ''' test_x,test_y = generate( 12 ,mean,cov,[[ 3.0 , 0.0 ],[ 3.0 , 3.0 ],[ 0.0 , 3.0 ]],num_classes = 4 ) #转换为二种类别 test_y = test_y % 2 xr = [] xb = [] for (l,k) in zip (test_y[:],test_x[:]): if l = = 0.0 : xr.append([k[ 0 ],k[ 1 ]]) else : xb.append([k[ 0 ],k[ 1 ]]) xr = np.array(xr) xb = np.array(xb) plt.figure() plt.scatter(xr[:, 0 ],xr[:, 1 ],c = 'r' ,marker = '+' ) plt.scatter(xb[:, 0 ],xb[:, 1 ],c = 'b' ,marker = 'o' ) lo = sess.run(loss,feed_dict = {input_x:test_x,input_y:np.reshape(test_y,[ - 1 , 1 ])}) print ( 'Test data loss {0}' . format (lo)) nb_of_xs = 200 xs1 = np.linspace( - 1 , 8 ,num = nb_of_xs) xs2 = np.linspace( - 1 , 8 ,num = nb_of_xs) #创建网格 xx,yy = np.meshgrid(xs1,xs2) #初始化和填充 classfication plane classfication_plane = np.zeros([nb_of_xs,nb_of_xs]) for i in range (nb_of_xs): for j in range (nb_of_xs): #计算每个输入样本对应的分类标签 classfication_plane[i,j] = sess.run(y_pred,feed_dict = {input_x:[[xx[i,j],yy[i,j]]]}) #创建 color map用于显示 cmap = ListedColormap([ colorConverter.to_rgba( 'r' ,alpha = 0.30 ), colorConverter.to_rgba( 'b' ,alpha = 0.30 ), ]) #显示各个样本边界 plt.contourf(xx,yy,classfication_plane,cmap = cmap) plt.show() |
从这次运行结果,我们可以看到测试集的loss增加到了0.21,并没有原来训练时候的那么好0.056,模型还是原来的模型,但是这次却只框住了少量的样本。这种现象就是过拟合,它和欠拟合都是我们在训练模型中不愿意看到的现象,我们要的是真正的拟合在测试情况下能够变现出训练时的良好效果。
避免过拟合的方法有很多:常用的有early stopping、数据集扩展、正则化、弃权等,下面会使用这些方法来对该案例来进行优化。
6 通过正则化改善过拟合情况
Tensorflow中封装了L2正则化的函数可以直接使用:
1 | tf.nn.l2_loss(t,name = None ) |
函数原型如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def l2_loss(t, name = None ): r """L2 Loss. Computes half the L2 norm of a tensor without the `sqrt`: output = sum(t ** 2) / 2 Args: t: A `Tensor`. Must be one of the following types: `half`, `float32`, `float64`. Typically 2-D, but may have any dimensions. name: A name for the operation (optional). Returns: A `Tensor`. Has the same type as `t`. 0-D. """ result = _op_def_lib.apply_op( "L2Loss" , t = t, name = name) return result |
但是并没有提供L1正则化函数,但是可以自己组合:
1 | tf.reduce_sum(tf. abs (w)) |
我们在代码中加入L2正则化:并设置返回参数lamda=1.6,修改代价函数如下:
1 | loss = tf.reduce_mean(tf.square(y_pred - input_y)) + lamda * tf.nn.l2_loss(weights[ 'h1' ]) / num_samples + tf.nn.l2_loss(weights[ 'h2' ]) * lamda / num_samples |
训练集的代价值从0.056增加到了0.106,但是测试集的代价值仅仅从0.21降到了0.0197,其效果并不是太明显。
7 通过增大数据集改善过拟合情况
下面再试试通过增大数据集的方法来改善过度拟合的情况,这里不再生产一个随机样本,而是每次从循环生成1000个数据。部分代码如下:
1 2 3 4 5 6 7 | for epoch in range (training_epochs): train_x,train_y = generate(num_samples,mean,cov,[[ 3.0 , 0.0 ],[ 3.0 , 3.0 ],[ 0.0 , 3.0 ]],num_classes = 4 ) #转换为二种类别 train_y = train_y % 2 _,lo = sess.run([train,loss],feed_dict = {input_x:train_x,input_y:np.reshape(train_y,[ - 1 , 1 ])}) if epoch % 1000 = = 0 : print ( 'Epoch {0} loss {1}' . format (epoch,lo)) |
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了