前言
明天提一个比拟轻松的话题,简略探讨数据集大小对深度学习训练的影响。
不晓得大家有没有看过这篇文章:Don't use deep learning your data isn't that big
是的,有人对深度学习的局限性提供了一个证据:那就是当你数据比拟少的时候,深度学习的作用相比拟于其余传统的办法并没有什么劣势,相同成果还不如传统的办法。
提出这个说法的作者利用两种办法进行了测试,测试的数据集是MNIST,测试计算机是否能正确辨认0和1,采纳的办法别离是:
- 5层的深度神经网络,活函数是双曲正切函数;
- 另一种办法应用的是李加索变量抉择办法,这种办法思维就是筛选10个边际p值最小的像素来进行(用这些值做回归就能够了);
而后得出一个论断:
那就是应用李加索办法的体现要优于神经网络。What?
注释
那么回到正题,下面的说法到底对不对,咱们在数据比拟小的时候是否正确地进行深度学习的训练从而达到比较满意的成果?
咱们都晓得,神经网络相当于一个有限深的万能函数,咱们输出变量x而后失去后果y,两头经验了很多简单的计算过程。实践上,通过传统算法能够解决的问题通过深度学习都能够解决,然而如果神经网络足够深的时候,尽管这个网络的性能很弱小,然而如果数据不够,很容易达到过拟合的景象,从而达不到咱们要求的成果。
那么数据集过小是否能够通过深度学习来做,咱们来测试一下。
一维信号
咱们测试数据很简略,不是咱们平时应用的三通道RGB图(3 x 256 x 256),而是一般的一通道一维信号(1 x 168)。
上方是咱们的一维信号,532nm和1064mn别离对应两种不同的信号,咱们只须要对一种信号处理器可。信号的格局是.mat文件,也就是matlab文件。
下面的文件中,train数据集是161 x 168,第一行是x轴的坐标咱们不必理睬只须要y轴的数据,而后每40个数据组是一类也就是 2-41、42-81、82-121、122-161,一共四类。而test数据集是81x168,第一行同样是x坐标咱们不论,每20个数据组是一类(和train数据组程序上类别是一样的)。也就是说咱们一共有四类信号要进行分类。
label别离为:0、1、2、3.
咱们的训练数据量只有160组,而测试数据量也只有80组。
数据读取
咱们采纳的深度学习库是Pytorch
,利用的python
上的scipy
库,scipy
是一个线性函数解决库,当然咱们只是应用它对mat文件的读取性能。
创立一个文件读取.py
,引入以下头文件。
import torchimport torch.utils.data as dataimport scipy.ioimport osimport os.path as osp
而后咱们编写文件读取类.py
:
# 将原始数据转化为训练须要的数据格式def to_tensor(data): data = torch.from_numpy(data).type(torch.float32) data = data.unsqueeze(0) return data# 读取数据类class LineData(data.Dataset): def __init__(self, root, name=532, train=True, transform=to_tensor): self.root = os.path.expanduser(root) self.name = name self.train = train self.transform = transform self.classes = [0, 1, 2, 3] if not osp.exists('datasets'): raise FileExistsError('Missing Datasets') if self.train: self.train_datas = [] self.train_labels = [] dataset_dir = osp.join(self.root, 'train_{}nm.mat'.format(self.name)) train_data = scipy.io.loadmat(dataset_dir)['lineIntensity'] data_length = len(train_data) - 1 # 161 - 1 = 160 if self.transform: for i in range(data_length): # 0 - 159 self.train_datas.append(transform(train_data[i+1])) # i+1 => 1 - 160 self.train_labels.append(self.classes[int(i / 40)]) else: raise ValueError('We need tranform function!') if not self.train: self.test_datas = [] self.test_labels = [] dataset_dir = osp.join(self.root, 'test_{}nm.mat'.format(self.name)) test_data = scipy.io.loadmat(dataset_dir)['lineIntensity'] data_length = len(test_data) - 1 # 81 - 1 = 80 if self.transform: for i in range(data_length): # 0 - 79 self.test_datas.append(transform(test_data[i+1])) # i+1 => 1 - 80 self.test_labels.append(self.classes[int(i / 20)]) else: raise ValueError('We need tranform function!') def __getitem__(self, index): """ Args: index (int): Index Returns: tuple: (image, target) where target is index of the target class. """ if self.train: data, target = self.train_datas[index], self.train_labels[index] else: data, target = self.test_datas[index], self.test_labels[index] return data, target def __len__(self): if self.train: return len(self.train_datas) else: return len(self.test_datas)
编写神经网络
写好文件读取代码后,咱们来设计一下神经网络,因为数据量很少,所以咱们的神经网络的层数也应该降落。否则很容易呈现过拟合的景象。
咱们首先设计5层的神经网络,两个卷积层,一个池化层,两个线性层,激活函数应用Relu:
每个数据的长度为168模型:两个个卷积层、两个线性层class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv1d(1, 10, 3) # (168 - 3)/1 + 1 = 166 (* 10) self.pool = nn.MaxPool1d(2, 2) # (166 - 2)/2 + 1= 83 (* 10) self.conv2 = nn.Conv1d(10, 20, 3) # (83 - 3)/1 + 1 = 81 (* 20) self.fc1 = nn.Linear(81*20, 100) self.fc2 = nn.Linear(100, 4) def forward(self, x): x = self.pool(F.relu(self.conv1(x))) x = F.relu(self.conv2(x)) x = x.view(-1, 81*20) x = F.relu(self.fc1(x)) x = self.fc2(x) return x
训练以及测试
设计好神经网络后,咱们来进行训练吧!
首先编写训练块的代码,咱们应用的优化策略是SGD随机降落法(带有动能),默认的学习率设置为0.001
,验证形式采纳经典的分类罕用的验证法CrossEntropyLoss
。
咱们首先进行训练而后进行验证准确率,准确率就是每次测试这四种信号正确次数和总次数的比。
# 主程序页面import torchimport torch.nn as nnimport torch.utils.dataimport torch.optim as optimfrom model import Netfrom data_utils import LineDataroot = 'datasets' # 数据所在目录,绝对目录地址train_name = '532' # 或者 '1064'# device = torch.device('cuda:0')# 读取文件类trainset = LineData(root, name=train_name)trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True)testset = LineData(root, name=train_name, train=False)testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=True)net = Net()# net = net.to(device)criterion = nn.CrossEntropyLoss()optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)# 上面的epoch改成 2 能够达到100%的准确率epoch_sum = 1# 训练for epoch in range(epoch_sum): loss_sum = 0.0 for i, data in enumerate(trainloader, 0): inputs, labels = data optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() loss_sum += loss.item() if i % 10 == 9: print('[epoch:{} num:{}] loss:{}'.format(epoch, i, loss_sum / 20)) loss_sum = 0.0print('Finished Training')# 验证correct = 0total = 0with torch.no_grad(): for data in testloader: inputs, labels = data outputs = net(inputs) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item()print('Accuracy of the network on the 80 test images: %d %%' % ( 100 * correct / total))pass
好了,开始训练,因为数据量很少,所以在CPU上训练即可。
第一次训练,epoch为1,lr为0.001:
[epoch:0 num:9] loss:1.693671927541118e+16[epoch:0 num:19] loss:53694975.30745087[epoch:0 num:29] loss:6.2672371854667905e+28[epoch:0 num:39] loss:51403236.52956776Finished TrainingAccuracy of the network on the 80 test images: 25 %
后果看起来很不好嘛...准确率25%,看起来是猜的,损失稳定很激烈,应该是咱们学习率调的太高了,接下来调整一下学习率。
第二次训练,epoch为1,lr为0.0001:
[epoch:0 num:9] loss:133432.54784755706[epoch:0 num:19] loss:67940.00796541572[epoch:0 num:29] loss:109.18773172795773[epoch:0 num:39] loss:1.1358043849468231Finished TrainingAccuracy of the network on the 80 test images: 25 %
loss降落很平缓,然而epoch看起来不够导致loss降落没有彻底,准确率仍然很低,让咱们来调整一下epoch。
第三次训练,epoch为5,lr为0.0001:
[epoch:0 num:9] loss:3024598166.2773805[epoch:0 num:19] loss:3117157163.829549[epoch:0 num:29] loss:258.4028107881546[epoch:0 num:39] loss:0.6990358293056488[epoch:1 num:9] loss:0.6830220401287079[epoch:1 num:19] loss:66.56461009383202[epoch:1 num:29] loss:0.7117315053939819[epoch:1 num:39] loss:0.6977931916713714[epoch:2 num:9] loss:0.6974189281463623[epoch:2 num:19] loss:0.6898959457874299[epoch:2 num:29] loss:0.7101178288459777[epoch:2 num:39] loss:0.6914324820041656[epoch:3 num:9] loss:0.686737447977066[epoch:3 num:19] loss:0.6972651600837707[epoch:3 num:29] loss:0.7028001189231873[epoch:3 num:39] loss:0.6998239696025849[epoch:4 num:9] loss:0.6997098863124848[epoch:4 num:19] loss:0.6969940900802613[epoch:4 num:29] loss:0.696108078956604[epoch:4 num:39] loss:0.6910847663879395Finished TrainingAccuracy of the network on the 80 test images: 25 %
loss降落到肯定级别没有再降落,而准确率仍然很迷,有可能还是因为学习率过高而导致loss始终卡在一个范畴无奈彻底降落,咱们再试着尝试降落一下学习率。
第四次训练,epoch为2,lr为0.00001:
[epoch:0 num:9] loss:200.58453428081702[epoch:0 num:19] loss:5.724525341391564[epoch:0 num:29] loss:0.2976263818090047[epoch:0 num:39] loss:0.05558242934057489[epoch:1 num:9] loss:0.0004892532759185996[epoch:1 num:19] loss:0.00012833428763769916[epoch:1 num:29] loss:9.479262493137242e-05[epoch:1 num:39] loss:3.948449189010717e-05Finished TrainingAccuracy of the network on the 80 test images: 100 %
完满,看来咱们摸索出了适合的学习率(0.00001
),通过10次测试,准确率别离为:100%、100%、100%、100%、100%、100%、100%、100%、100%、98%。
如果我将epoch从2换成1,则是100%、77%、100%、100%、100%、86%、100%、100%、100%、100%。
epoch从1换成3则是:100%、100%、100%、100%、100%、100%、100%、100%、100%、100%。
咱们如果批改一下神经网络层为3层全连贯层,lr为0.00001
成果会很差,即便训练10个以上的epochy也不会达到100%的准确率,然而如果将lr降落到0.000001
,准确率则就会达到100%了:
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.fc1 = nn.Linear(168, 1000) self.fc2 = nn.Linear(1000, 100) self.fc3 = nn.Linear(100,4) def forward(self, x): x = x.view(-1, 168) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x
论断
通过下面的测试,看起来200数量以内的数据集,只有设计层正当,学习率适合,那么准确率也是能够达到比拟好的成果。
其实所说的过拟合经常是因为咱们设计的神经网络层数过深,然而数据没有那么多,神经网络就会充沛“榨干”那些训练数据,适度排汇那些训练集的信息,导致在测试的时候没有那么精确,说以如果数据集过少,能够通过缩小层数的办法来加重谬误。
然而如果数据蕴含的信息很丰盛,然而数据量很少,这时候光调整层数就不够了,咱们须要一些数据加强的技术裁减数据集,从而“喂饱”神经网络,不至于让神经网络出现异常。当然,数据集裁减是针对含信息量很丰盛的信息来实现的,如果信息都像咱们之前应用的一维信号一样,个别就没有必要裁减了。
撩我吧
- 如果你与我气味相投于此,老潘很违心与你交换;
- 如果你喜爱老潘的内容,欢送关注和反对。
- 如果你喜爱我的文章,心愿点赞???? 珍藏 ???? 评论 ???? 三连一下~
想晓得老潘是如何学习踩坑的,想与我交换问题~请关注公众号「oldpan博客」。
老潘也会整顿一些本人的私藏,心愿能帮忙到大家,点击神秘传送门获取。