Bài 4: Train Neural Network | Deep Learning cơ bản
 

Bài 4: Train Neural Network

| Posted in Deep Learning cơ bản

Bài trước mình đã học về cách xây dựng model (NN, CNN) bằng Pytorch, bài này mình sẽ học cách load dữ liệu, sau đó train model bằng Pytorch.

Dataset

torch.utils.data.Dataset trong Pytorch mô tả dữ liệu giống như dữ liệu thực tế mọi người có, trong đó có hai hàm quan trọng:

  • __len__: trả về số sample trong dataset, tương ứng len(dataset).
  • __getitem__ : lấy ra sample thứ i trong dataset, tương ứng dataset[i].
Dataset trong Pytorch, hỗ trợ hàm len và lấy phần tử theo index.

Ví dụ như khi load dataset cifar10 từ torchvision.dataset

from torchvision import datasets

# Đường dẫn lưu để sau load lại không cần download.
data_path = '/content/drive/MyDrive/Pytorch/Pytorch Tutorial/'

# load dataset cifar10
cifar10 = datasets.CIFAR10(data_path, train=True, download=True)

Khi đó cifar10 là một Dataset.

print(isinstance(cifar10, torch.utils.data.Dataset)) # True
print(len(cifar10)) # 50000
print(cifar10[0]) # (<PIL.Image.Image image mode=RGB size=32x32 at 0x7F729582BF50>, 6)

Dataset cifar10 có 50.000 dữ liệu, trong đó khi lấy index theo từng phần tử sẽ trả lại 1 tuple gồm 2 phần tử (PIL image, label).

Mình có thể visualize ảnh lên, tuy nhiên ảnh kích thước nhỏ (32*32) nên mở lên sẽ hơi vỡ.

img, label = cifar10[0]
plt.imshow(img)
frog

Transformer

Dữ liệu mình lấy được ở trên thì ảnh ở dạng PIL image, mình cần convert về dạng Torch tensor để cho Pytorch xử lý và tính toán. Module torchvision.transforms hỗ trợ các phép chuyển đổi trên ảnh như: chuyển sang tensor, normalization, augmentation,…

ToTensor

to_tensor = transforms.ToTensor()
# Hàm chuyển PIL image sang tensor
img_t = to_tensor(img)
print(img_t.shape) # torch.Size([3, 32, 32])

Mình có thể transform trong lớp dataset khi load dữ liệu cifar10

tensor_cifar10 = datasets.CIFAR10(data_path, train=True, download=False,
 transform=transforms.ToTensor())
img_t, _ = tensor_cifar10[99]
print(type(img_t)) # torch.Tensor

Ảnh ở dạng PIL image thì các pixel có giá trị từ 0-255, tuy nhiên khi chuyển về dạng tensor (ToTensor) thì dữ liệu pixel được scale về khoảng 0.0-1.0

print(img_t.min(), img_t.max()) # tensor(0.) tensor(1.)

Normalization

Việc normalization dữ liệu giúp các pixel có cùng scale cũng như distribution, do đó mình có thể dùng thuật toán gradient descent cho các tham số với cùng một learning rate. Thông thường mình sẽ normalize để mỗi channel về standard normal distribution (N(0, 1), normal distribution với 0 mean và 1 standard deviation)

\displaystyle v[c] = \frac{v[c] - mean[c]}{std[c]}

Mình có thể tính mean và std theo từng channel của dữ liệu.

imgs.view(3, -1).mean(dim=1) # tensor([0.4915, 0.4823, 0.4468])
imgs.view(3, -1).std(dim=1) # tensor([0.2470, 0.2435, 0.2616])

Sau đó mình sẽ dùng transforms.Normalize để normalize dữ liệu.

# Tham số đầu là mean, tham số sau là std
transforms.Normalize((0.4915, 0.4823, 0.4468), (0.2470, 0.2435, 0.2616))

Compose

Ngoài ra Pytorch transforms còn hỗ trợ rất nhiều phép biển đổi khác như: RandomCrop, RandomRotation, RandomAffine (để shear), RandomHorizontalFlip,… Chi tiết mọi người xem ở đây.

Để thực hiện nhiều phép biến đổi trên dữ liệu đầu vào, transforms hỗ trợ hàm compose để gộp các transforms lại.

transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4915, 0.4823, 0.4468),
 (0.2470, 0.2435, 0.2616))
])

Khi load Dataset mình sẽ dùng transform cho tất cả các ảnh

tensor_cifar10 = datasets.CIFAR10(data_path, train=True, download=False,
 transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.4915, 0.4823, 0.4468),
 (0.2470, 0.2435, 0.2616))
]))

Như vậy là mình đã có Dataset và thực hiện transform các ảnh đưa về dạng tensor cũng như normalize các ảnh. Giờ mình cần lấy các ảnh cho quá trình traning.

DataLoader

Khi cho dữ liệu vào model để học thì thông thường sẽ cho dữ liệu theo từng batch một, DataLoader sẽ giúp chúng ta lấy dữ liệu theo từng batch, shuffle dữ liệu cũng như load dữ liệu song song với nhiều multiprocessing workers.

train_loader = torch.utils.data.DataLoader(cifar10, batch_size=64,
                                           shuffle=True)

Tham số mình sẽ truyền vào Dataset (cifar10), batch_size bằng 64 và để shuffle bằng True (shuffle data sau mỗi epoch).

Train CIFAR10 classifier

Model

Mình sẽ xây dựng một mạng CNN đơn giản.

# model
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(16, 8, kernel_size=3, padding=1)
        self.fc1 = nn.Linear(8 * 8 * 8, 32)
        # bài toán phân loại 10 lớp nên output ra 10 nodes
        self.fc2 = nn.Linear(32, 10)
        
    def forward(self, x):
        out = F.max_pool2d(torch.tanh(self.conv1(x)), 2)
        out = F.max_pool2d(torch.tanh(self.conv2(out)), 2)
        # flatten về dạng vector để cho vào neural network
        out = out.view(-1, 8 * 8 * 8)
        out = torch.tanh(self.fc1(out))
        out = self.fc2(out)
        return out

Loss

Loss mình sẽ dùng categorical crossentropy loss

loss_fn = nn.CrossEntropyLoss()

Lưu ý: Hàm CrossEntropyLoss của Pytorch đã bao gồm cả activation softmax ở lớp output và categorial crossentropy loss thế nên khi dựng model không cần dùng activation softmax ở output layer.

Train

def training_loop(n_epochs, optimizer, model, loss_fn, train_loader, val_loader):
    for epoch in range(1, n_epochs + 1):
        loss_train = 0.0
        for imgs, labels in train_loader:

            outputs = model(imgs)
            loss = loss_fn(outputs, labels)
            optimizer.zero_grad()     
            loss.backward() 
            optimizer.step()
            loss_train += loss.item()
        correct = 0
        # tính độ chính xác trên tập validation
        with torch.no_grad():
            for data in val_loader:
                images, labels = data
                outputs = model(images)
                _, predicted = torch.max(outputs, 1)
                c = (predicted == labels).squeeze()
                correct += c.sum()
        if epoch == 1 or epoch % 1 == 0:
            print('Epoch {}, Training loss {}, Val accuracy {}'.format(
                epoch,
                loss_train / len(train_loader),
                correct / len(cifar10_val)))

Code mọi người xem ở đây.


Deep Learning cơ bản ©2024. All Rights Reserved.
Powered by WordPress. Theme by Phoenix Web Solutions