根据模型泛化能力调节神经网络结构的简单方法(训练优化)

泛化

(1)泛化能力

就是模型的拟合程度,一般来说对于泛化能力,我们采取三种级别进行衡量,“欠拟合”,“正常拟合”,“过拟合”。
泛化能力

(2)泛化差错

泛化差错分为三类:“偏差差错”(bias),“方差差错”(variance),“噪声”(noise)。Bias属于由于网络结构缺陷而产生的,网络越复杂,Bias越小,如果Bias过大,证明出现欠拟合。Variance是由于训练数据和测试数据的差异而产生的,对于固定的训练数据,网络越简单,Variance越小,Variance过大,证明出现过拟合。而噪声属于不可消除部分。
总的来说:高偏差 → \rightarrow 欠拟合,高方差 → \rightarrow 过拟合,以模型复杂度来作为自变量,差错作为因变量,画出图像如下:
在这里插入图片描述

泛化能力的判断

我们可以通过绘制学习曲线和验证曲线来判断当前是否出现高Bias(欠拟),还是高Variance(过拟)。我们在训练中,一般将数据集分为训练集(train set),验证集(validation set),测试集(test set)。

  • 训练集负责权重的计算
  • 验证集用于判断是否出现欠拟合过拟合,以此作为标准来控制网络结构和控制模型的复杂程度的参数
  • 测试集作为评价最终的结构,我们绘制学习曲线需要训练集和验证集,不需要测试集。

一般来说,遵循622比例分配一个数据集。(60%,20%,20%)

学习曲线和验证曲线在上的表现(差错Loss和训练数据量的关系):

在这里插入图片描述

解决方法

在这里插入图片描述
总结一下步骤:

  • step 1:根据训练曲线和验证曲线来判断当前模型泛化能力,是属于“过”还是“欠”
  • step 2:根据泛化能力做出对模型调整

【 Loss → \rightarrow 泛化能力 → \rightarrow 模型复杂度调整】 → \rightarrow Loss → \rightarrow …循环下去

实验:根据模拟数据进行一次网络结构调整

本实验基于Pytorch

(1)模拟生成数据集

samples_size=[1000],labels_size=[1000]

import torch
from debug import ptf_tensor

torch.manual_seed(seed=0) #固定随机数种子
sample_sum=1000 #生成1000个样本

features=torch.rand(sample_sum,2)*12-6  #size[1000,2]
noises=torch.randn(sample_sum) #size[1000]

def himmelblau(x):
    return (x[:,0]**2+x[:,1]-11)**2+(x[:,0]+x[:,1]**2-7)**2

hims=himmelblau(features)*0.01 #size[1000]
labels=hims+noises #size[1000]

ptf_tensor(features,'features')
ptf_tensor(noises,'noise')
ptf_tensor(hims,'hims')
ptf_tensor(labels,'labels')

(2)数据集的真实噪声率(真实的均方误差)

60%训练,20%验证,20%测试
计算数据集的真实噪声率:

train_sum, validate_sum, test_sum =600, 200, 200

#计算出这些数据的噪声率
train_mse=(noises[:train_sum]**2).mean()
validate_mse=(noises[train_sum:-test_sum]**2).mean()
test_mse=(noises[-test_sum:]**2).mean()

print('Train set:{:g}\nValidate set:{:g}\nTest set:{:g}'.format(train_mse,validate_mse,test_mse))

(3)打造网络结构

开始我们考虑使用一个:3层神经网络,前两个是隐含层,每层有2个神经元,最后一层是输出层有一个神经元。

import torch.nn as nn
hidden_features=[2,2] #把隐含层数 设计成列表的形式是为了以后可以更方便的修改
layers=[nn.Linear(2,hidden_features[0]),] 

for idx,hidden_feature in enumerate(hidden_features):
    layers.append(nn.Sigmoid())
    next_hidden_feature = hidden_features[idx+1]\
        if idx+1 <len(hidden_features) else 1
    layers.append(nn.Linear(hidden_feature,next_hidden_feature))

net=nn.Sequential(*layers) # 从列表变成一个神经网络
print(net) #输出网络结构

(4)训练迭代

使用自己假设的网络结构如下进行迭代训练:

Sequential(
  (0): Linear(in_features=2, out_features=2, bias=True)
  (1): Sigmoid()
  (2): Linear(in_features=2, out_features=2, bias=True)
  (3): Sigmoid()
  (4): Linear(in_features=2, out_features=1, bias=True)
)

训练代码:

import torch.optim
optimizer=torch.optim.Adam(net.parameters())

criterion=nn.MSELoss()

train_entry_sum=100 #所有的训练的样本数量 =100,200,300,400,500,600

n_iter=100000 # 最大的迭代次数

for n_iter in range(n_iter):
    ouputs=net(features)
    preds=ouputs.squeeze()

    loss_train=criterion(preds[:train_entry_sum],labels[:train_entry_sum])
    loss_validate=criterion(preds[train_sum:-test_sum],labels[train_sum:-test_sum])

    if n_iter % 1000 == 0 :
        print('#step {},Trian MSE:{},Validate MSE:{}'.format(n_iter,loss_train,loss_validate))
    optimizer.zero_grad()
    loss_train.backward()
    optimizer.step()

运行以上的代码,输出:
我们真实的MSE(均方误差)和我们迭代计算的MSE(均方误差)
如下表:

训练条目数量Train_MSEValidate_MSE
1003.004.00
2002.703.40
3003.203.14
4003.003.18
5003.383.21
6003.263.24

画出散点图:
在这里插入图片描述

由图可以看到,非常明显高偏差,我们需要增加模型的复杂度,我们在神经元个数,我们有两个隐含层,一个输出层,我们应该对隐含层进行修改,我们尝试把2 → \rightarrow 6,把原来每层2个神经元改成6个神经元。,然后重新训练,得到如下输出:

Train set0.918333
Validate set0.902182
Test set0.978382
Sequential(
  (0): Linear(in_features=2, out_features=6, bias=True)
  (1): Sigmoid()
  (2): Linear(in_features=6, out_features=2, bias=True)
  (3): Sigmoid()
  (4): Linear(in_features=2, out_features=1, bias=True)
)
#step 0,Trian MSE:18.523536682128906,Validate MSE:24.195621490478516
....
#step 98000,Trian MSE:0.44830459356307983,Validate MSE:2.7131242752075195
#step 99000,Trian MSE:0.4482784867286682,Validate MSE:2.7131223678588867

我们可以理解成:真实的MSE就是我们目标的Loss。
现在我们调节变量train_entry_sum=100,200,300,400,500,600,然后计算当step=9000时,Train MSE 和Validate MSE的值然后描绘一个X=train_entry_sum; Y=MSELOSS的函数图像。
每层6个神经元的训练LOSS:

训练条目数量Train_MSEValidate_MSE
1000.452.71
2000.771.21
3000.791.11
4000.941.14
5001.001.21
6000.941.05

结果:
Plt
由图可见,已经比较接近我们的目标MSE了,说明调整后的网络结构:

Sequential(
  (0): Linear(in_features=2, out_features=6, bias=True)
  (1): Sigmoid()
  (2): Linear(in_features=6, out_features=2, bias=True)
  (3): Sigmoid()
  (4): Linear(in_features=2, out_features=1, bias=True)
)

是合适的。

附:散点图绘图代码:

import numpy as np
import matplotlib.pyplot as plt

x=np.array([100,200,300,400,500,600])
y=np.array([0.45,0.77,0.79,0.94,1.00,0.94])
y1=np.array([2.71,1.21,1.11,1.14,1.21,1.05])
y2=np.array([0.95,0.95,0.95,0.95,0.95,0.95])

fig=plt.figure()
ax=fig.add_subplot(1,1,1)
ax.plot(x,y,'m.-.',label='trainMSE',color='blue')
ax.plot(x,y1,'m.-.',label='ValidateMSE',color='green')
ax.plot(x,y2,'c*-',label='StandardMSE',color='red')
ax.legend()
plt.show()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值