跳至主要內容

神经网络——多层感知机(MLP)

原创Xenny约 808 字大约 3 分钟深度学习深度学习神经网络MLP

神经网络——多层感知机(MLP)

  • 神经网络基本概念 中已经介绍了感知机的概念,它是使用神经元进行二分类线性分类模型,本质也就是计算输入特征的线性组合wx+b\boldsymbol{wx} + b来对样本进行分类。在训练过程中通过不断的修正参数来找到一个能够正确对样本进行分类的超平面(决策边界)wx+b=0\boldsymbol{wx} + b=0。我们已经了解了感知机的基本结果以及工作流程,同时我们也知道感知机只能对线性可分的数据进行分类,而对线性不可分的数据无法处理。而多层感知机正是为了处理这一问题而提出的。

定义

  • 多层感知机(Multilayer Perceptron)便是最基础的神经网络即人工神经网络(Artificial Neural Network),为了克服线性模型的限制,MLP中通常包含多个隐藏层,并且层与层直接进行全连接。

    并且这些层与层之间各自的任务也不同,一般前n1n-1层隐藏层都是为了对输入进行特征提取,通常称为表示层,第nn层即最后一层才是进行分类,称为决策层。即MLP网络如图所示

    MLP
    MLP
  • 当然在基础篇中也提及过,MLP能进行非线性决策的最重要因素是引入了非线性的激活函数,否则即使构造多层线性网络也会退化成一层。

    这里我们来看一个简单的猫狗分类例子

    import os
    import torch
    import torch.nn as nn
    from torchvision.models import alexnet, resnet18
    import torchvision.transforms as transforms
    from torch.utils.data import Dataset, DataLoader
    import PIL.Image
    
    class CustomDataset(Dataset):
        def __init__(self, path, transform) -> None:
            super().__init__()
            self.transform = transform
            self.imgs = self.make_dataset(path)
        
        def __getitem__(self, index):
            img_path, label = self.imgs[index]
            img = PIL.Image.open(img_path).convert('RGB')
            img = self.transform(img)
            return img, label
    
        def __len__(self):
            return len(self.imgs)
    
        @staticmethod
        def make_dataset(path):
            datasets = []
            class_to_idx = {'cats': 0, 'dogs': 1}
            for root, dirs, _ in os.walk(path):
                for sub_dir in dirs:
                    imgs = list(os.listdir(os.path.join(root, sub_dir)))
                    
                    for img in imgs:
                        datasets.append((
                            os.path.join(root, sub_dir, img),
                            class_to_idx[sub_dir]
                        ))
            return datasets
    
    class MLP(nn.Module):
        def __init__(self):
            super(MLP, self).__init__()
            
            self.classifier = nn.Sequential(
                nn.Flatten(),
                nn.Linear(195075, 512),
                nn.ReLU(),
                nn.Linear(512, 256),
                nn.ReLU(),
                nn.Linear(256, 2)
            )
    
        def forward(self, x):
            x = self.classifier(x)
            return x
    
    mean = [0.5875, 0.5591, 0.4995]
    std = [0.2898, 0.2873, 0.3088]
    train_transform = transforms.Compose([transforms.Resize((255,255)),
                                    # transforms.RandomVerticalFlip(),
                                    # transforms.RandomHorizontalFlip(),
                                    transforms.RandomCrop(255, padding=4),
                                    transforms.ToTensor(),
                                    transforms.Normalize(mean, std)
                                ])
    valid_transform = transforms.Compose([transforms.Resize((255,255)),
                                    transforms.ToTensor(),
                                    transforms.Normalize(mean, std)
                                ])
    
    train_dataset = CustomDataset('./datasets/train', transform=train_transform)
    valid_dataset = CustomDataset('./datasets/test', transform=valid_transform)
    
    train_loader = DataLoader(dataset=train_dataset, batch_size=4, shuffle=True)
    valid_loader = DataLoader(dataset=valid_dataset, batch_size=4, shuffle=True)
    
    
    model = MLP().to(device="cuda")
    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=1e-2)
    
    for epoch in range(100):
        tsize = len(train_loader.dataset)
        tcorrect = 0
        for batch, (X, y) in enumerate(train_loader):
            X, y = X.cuda(), y.cuda()
            pred = model(X)
            loss = criterion(pred, y)
            tcorrect += (pred.argmax(1) == y).type(torch.float).sum().item()
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
        if epoch % 5 == 0:
            model.eval()
            correct = 0
            size = len(valid_loader.dataset)
            with torch.no_grad():
                for X, y in valid_loader:
                    X, y = X.cuda(), y.cuda()
                    pred = model(X)
                    
                    correct += (pred.argmax(1) == y).type(torch.float).sum().item()
    
            print(f'Epoch {epoch}/100 Train Acc = {tcorrect/tsize}, Acc = {correct/size}')
    
  • 数据集来自Cats and Dogs image classificationopen in new window 这里我们定义了一个包含两层隐藏层的MLP模型进行训练,最终效果在测试集上有61%的正确率,而且理论上还没收敛,多训练几轮效果应该还会更好。