深度学习中数据集很小是一种什么样的体验

前言

今天提一个比较轻松的话题,简单探讨数据集大小对深度学习训练的影响。
不知道你们有没有看过这篇文章:Don't use deep learning your data isn't that big
QQ20180605-002523@2xpython

是的,有人对深度学习的局限性提供了一个证据:那就是当你数据比较少的时候,深度学习的做用相比较于其余传统的方法并无什么优点,相反效果还不如传统的方法。算法

提出这个说法的做者利用两种方法进行了测试,测试的数据集是MNIST,测试计算机是否能正确识别0和1,采用的方法分别是:网络

  • 5层的深度神经网络,活函数是双曲正切函数;
  • 另外一种方法使用的是李加索变量选择方法,这种方法思想就是挑选10个边际p值最小的像素来进行(用这些值作回归就能够了);

而后得出一个结论:
unnamed-chunk-7-1app

那就是使用李加索方法的表现要优于神经网络。What?函数

正文

那么回到正题,上面的说法到底对不对,咱们在数据比较小的时候可否正确地进行深度学习的训练从而达到比较满意的效果?学习

咱们都知道,神经网络至关于一个无限深的万能函数,咱们输入变量x而后获得结果y,中间经历了不少复杂的计算过程。理论上,经过传统算法能够解决的问题经过深度学习均可以解决,可是若是神经网络足够深的时候,虽然这个网络的功能很强大,可是若是数据不够,很容易达到过拟合的现象,从而达不到咱们要求的效果。测试

那么数据集太小是否能够经过深度学习来作,咱们来测试一下。优化

一维信号

咱们测试数据很简单,不是咱们日常使用的三通道RGB图(3 x 256 x 256),而是普通的一通道一维信号(1 x 168)。设计

signal

上方是咱们的一维信号,532nm和1064mn分别对应两种不一样的信号,咱们只须要对一种信号处理器可。信号的格式是.mat文件,也就是matlab文件。3d

上面的文件中,train数据集是161 x 168,第一行是x轴的坐标咱们不用理会只须要y轴的数据,而后每40个数据组是一类也就是 2-4一、42-8一、82-12一、122-161,一共四类。而test数据集是81x168,第一行一样是x坐标咱们无论,每20个数据组是一类(和train数据组顺序上类别是同样的)。也就是说咱们一共有四类信号要进行分类。

label分别为:0、一、二、3.

咱们的训练数据量只有160组,而测试数据量也只有80组。

数据读取

咱们采用的深度学习库是Pytorch,利用的python上的scipy库,scipy是一个线性函数处理库,固然咱们只是使用它对mat文件的读取功能。

建立一个文件读取.py,引入如下头文件。

import torch
import torch.utils.data as data
import scipy.io
import os
import 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 torch
import torch.nn as nn
import torch.utils.data
import torch.optim as optim
from model import Net
from data_utils import LineData

root = '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.0

print('Finished Training')

# 验证
correct = 0
total = 0
with 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.52956776
Finished Training
Accuracy 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.1358043849468231
Finished Training
Accuracy 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.6910847663879395
Finished Training
Accuracy 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-05
Finished Training
Accuracy 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博客」。
老潘也会整理一些本身的私藏,但愿能帮助到你们,点击神秘传送门获取。

相关文章
相关标签/搜索