Đầu tiên là những vấn đề chính ...
1. Vấn đề chính với mã này là bạn đang sử dụng hình dạng đầu ra sai và chức năng mất sai để phân loại.
nn.BCELoss
tính toán tổn thất entropy chéo nhị phân . Điều này có thể áp dụng khi bạn có một hoặc nhiều mục tiêu là 0 hoặc 1 (do đó là nhị phân). Trong trường hợp của bạn, mục tiêu là một số nguyên duy nhất trong khoảng từ 0 đến 9. Vì chỉ có một số lượng nhỏ các giá trị mục tiêu tiềm năng, nên cách tiếp cận phổ biến nhất là sử dụng mất entropy chéo phân loại ( nn.CrossEntropyLoss
). Định nghĩa "lý thuyết" về mất entropy chéo dự kiến các đầu ra của mạng và các mục tiêu đều là các vectơ 10 chiều trong đó mục tiêu là tất cả các số 0 trừ một vị trí (được mã hóa một lần nóng). Tuy nhiên, vì sự ổn định tính toán và lý do hiệu quả không gian, pytorch nn.CrossEntropyLoss
trực tiếp lấy số nguyên làm mục tiêu . Tuy nhiên, bạn vẫn cần cung cấp cho nó một vectơ đầu ra 10 chiều từ mạng của mình.
# pseudo code (ignoring batch dimension)
loss = nn.functional.cross_entropy_loss(<output 10d vector>, <integer target>)
Để khắc phục vấn đề này trong mã của bạn, chúng tôi cần có fc3
tính năng 10 chiều và chúng tôi cần các nhãn là số nguyên (không phải số float). Ngoài ra, không cần sử dụng .sigmoid
trên fc3 vì hàm mất entropy chéo của pytorch áp dụng nội bộ log-softmax trước khi tính giá trị tổn thất cuối cùng.
2. Như Serget Dymchenko đã chỉ ra, bạn cần chuyển mạng sang eval
chế độ trong khi suy luận và train
chế độ trong khi đi tàu. Điều này chủ yếu ảnh hưởng đến các lớp bỏ học và batch_norm vì chúng hoạt động khác nhau trong quá trình đào tạo và suy luận.
3. Tỷ lệ học tập 0,03 có lẽ hơi quá cao. Nó hoạt động tốt với tỷ lệ học tập là 0,001 và trong một vài thí nghiệm tôi đã thấy sự phân kỳ đào tạo ở mức 0,03.
Để phù hợp với các bản sửa lỗi này, một số thay đổi cần phải được thực hiện. Các chỉnh sửa tối thiểu cho mã được hiển thị dưới đây. Tôi đã nhận xét bất kỳ dòng nào đã được thay đổi kèm ####
theo một mô tả ngắn về sự thay đổi.
import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torch.autograd import Variable
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
def resize(pics):
pictures = []
for image in pics:
image = Image.fromarray(image).resize((dim, dim))
image = np.array(image)
pictures.append(image)
return np.array(pictures)
dim = 60
x_train, x_test = resize(x_train), resize(x_test) # because my real problem is in 60x60
x_train = x_train.reshape(-1, 1, dim, dim).astype('float32') / 255
x_test = x_test.reshape(-1, 1, dim, dim).astype('float32') / 255
#### float32 -> int64
y_train, y_test = y_train.astype('int64'), y_test.astype('int64')
#### no reason to test for cuda before converting to numpy
#### I assume you were taking a subset for debugging? No reason to not use all the data
x_train = torch.from_numpy(x_train)
x_test = torch.from_numpy(x_test)
y_train = torch.from_numpy(y_train)
y_test = torch.from_numpy(y_test)
class ConvNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, 3)
self.conv2 = nn.Conv2d(32, 64, 3)
self.conv3 = nn.Conv2d(64, 128, 3)
self.fc1 = nn.Linear(5*5*128, 1024)
self.fc2 = nn.Linear(1024, 2048)
#### 1 -> 10
self.fc3 = nn.Linear(2048, 10)
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv3(x)), (2, 2))
x = x.view(x.size(0), -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.dropout(x, 0.5)
#### removed sigmoid
x = self.fc3(x)
return x
net = ConvNet()
#### 0.03 -> 1e-3
optimizer = optim.Adam(net.parameters(), lr=1e-3)
#### BCELoss -> CrossEntropyLoss
loss_function = nn.CrossEntropyLoss()
class FaceTrain:
def __init__(self):
self.len = x_train.shape[0]
self.x_train = x_train
self.y_train = y_train
def __getitem__(self, index):
#### .unsqueeze(0) removed
return x_train[index], y_train[index]
def __len__(self):
return self.len
class FaceTest:
def __init__(self):
self.len = x_test.shape[0]
self.x_test = x_test
self.y_test = y_test
def __getitem__(self, index):
#### .unsqueeze(0) removed
return x_test[index], y_test[index]
def __len__(self):
return self.len
train = FaceTrain()
test = FaceTest()
train_loader = DataLoader(dataset=train, batch_size=64, shuffle=True)
test_loader = DataLoader(dataset=test, batch_size=64, shuffle=True)
epochs = 10
steps = 0
train_losses, test_losses = [], []
for e in range(epochs):
running_loss = 0
#### put net in train mode
net.train()
for idx, (images, labels) in enumerate(train_loader):
optimizer.zero_grad()
log_ps = net(images)
loss = loss_function(log_ps, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
else:
test_loss = 0
accuracy = 0
#### put net in eval mode
net.eval()
with torch.no_grad():
for images, labels in test_loader:
log_ps = net(images)
test_loss += loss_function(log_ps, labels)
#### removed torch.exp() since exponential is monotone, taking it doesn't change the order of outputs. Similarly with torch.softmax()
top_p, top_class = log_ps.topk(1, dim=1)
#### convert to float/long using proper methods. what you have won't work for cuda tensors.
equals = top_class.long() == labels.long().view(*top_class.shape)
accuracy += torch.mean(equals.float())
train_losses.append(running_loss/len(train_loader))
test_losses.append(test_loss/len(test_loader))
print("[Epoch: {}/{}] ".format(e+1, epochs),
"[Training Loss: {:.3f}] ".format(running_loss/len(train_loader)),
"[Test Loss: {:.3f}] ".format(test_loss/len(test_loader)),
"[Test Accuracy: {:.3f}]".format(accuracy/len(test_loader)))
Kết quả đào tạo hiện đang ...
[Epoch: 1/10] [Training Loss: 0.139] [Test Loss: 0.046] [Test Accuracy: 0.986]
[Epoch: 2/10] [Training Loss: 0.046] [Test Loss: 0.042] [Test Accuracy: 0.987]
[Epoch: 3/10] [Training Loss: 0.031] [Test Loss: 0.040] [Test Accuracy: 0.988]
[Epoch: 4/10] [Training Loss: 0.022] [Test Loss: 0.029] [Test Accuracy: 0.990]
[Epoch: 5/10] [Training Loss: 0.017] [Test Loss: 0.066] [Test Accuracy: 0.987]
[Epoch: 6/10] [Training Loss: 0.015] [Test Loss: 0.056] [Test Accuracy: 0.985]
[Epoch: 7/10] [Training Loss: 0.018] [Test Loss: 0.039] [Test Accuracy: 0.991]
[Epoch: 8/10] [Training Loss: 0.012] [Test Loss: 0.057] [Test Accuracy: 0.988]
[Epoch: 9/10] [Training Loss: 0.012] [Test Loss: 0.041] [Test Accuracy: 0.991]
[Epoch: 10/10] [Training Loss: 0.007] [Test Loss: 0.048] [Test Accuracy: 0.992]
Một số vấn đề khác sẽ cải thiện hiệu suất và mã của bạn.
4. Bạn sẽ không bao giờ di chuyển mô hình sang GPU. Điều này có nghĩa là bạn sẽ không được tăng tốc GPU.
5. torchvision
được thiết kế với tất cả các biến đổi và bộ dữ liệu tiêu chuẩn và được xây dựng để sử dụng với PyTorch. Tôi khuyên bạn nên sử dụng nó. Điều này cũng loại bỏ sự phụ thuộc vào máy ảnh trong mã của bạn.
6. Bình thường hóa dữ liệu của bạn bằng cách trừ giá trị trung bình và chia cho độ lệch chuẩn để cải thiện hiệu suất của mạng. Với đèn pin bạn có thể sử dụng transforms.Normalize
. Điều này sẽ không tạo ra sự khác biệt lớn trong MNIST vì nó đã quá dễ dàng. Nhưng trong những vấn đề khó khăn hơn, nó trở nên quan trọng.
Mã cải tiến hơn nữa được hiển thị bên dưới (nhanh hơn nhiều trên GPU).
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision.datasets import MNIST
from torchvision import transforms
dim = 60
class ConvNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 32, 3)
self.conv2 = nn.Conv2d(32, 64, 3)
self.conv3 = nn.Conv2d(64, 128, 3)
self.fc1 = nn.Linear(5 * 5 * 128, 1024)
self.fc2 = nn.Linear(1024, 2048)
self.fc3 = nn.Linear(2048, 10)
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv3(x)), (2, 2))
x = x.view(x.size(0), -1)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.dropout(x, 0.5)
x = self.fc3(x)
return x
net = ConvNet()
if torch.cuda.is_available():
net.cuda()
optimizer = optim.Adam(net.parameters(), lr=1e-3)
loss_function = nn.CrossEntropyLoss()
train_dataset = MNIST('./data', train=True, download=True,
transform=transforms.Compose([
transforms.Resize((dim, dim)),
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
]))
test_dataset = MNIST('./data', train=False, download=True,
transform=transforms.Compose([
transforms.Resize((dim, dim)),
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
]))
train_loader = DataLoader(dataset=train_dataset, batch_size=64, shuffle=True, num_workers=8)
test_loader = DataLoader(dataset=test_dataset, batch_size=64, shuffle=False, num_workers=8)
epochs = 10
steps = 0
train_losses, test_losses = [], []
for e in range(epochs):
running_loss = 0
net.train()
for images, labels in train_loader:
if torch.cuda.is_available():
images, labels = images.cuda(), labels.cuda()
optimizer.zero_grad()
log_ps = net(images)
loss = loss_function(log_ps, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
else:
test_loss = 0
accuracy = 0
net.eval()
with torch.no_grad():
for images, labels in test_loader:
if torch.cuda.is_available():
images, labels = images.cuda(), labels.cuda()
log_ps = net(images)
test_loss += loss_function(log_ps, labels)
top_p, top_class = log_ps.topk(1, dim=1)
equals = top_class.flatten().long() == labels
accuracy += torch.mean(equals.float()).item()
train_losses.append(running_loss/len(train_loader))
test_losses.append(test_loss/len(test_loader))
print("[Epoch: {}/{}] ".format(e+1, epochs),
"[Training Loss: {:.3f}] ".format(running_loss/len(train_loader)),
"[Test Loss: {:.3f}] ".format(test_loss/len(test_loader)),
"[Test Accuracy: {:.3f}]".format(accuracy/len(test_loader)))
Cập nhật kết quả đào tạo ...
[Epoch: 1/10] [Training Loss: 0.125] [Test Loss: 0.045] [Test Accuracy: 0.987]
[Epoch: 2/10] [Training Loss: 0.043] [Test Loss: 0.031] [Test Accuracy: 0.991]
[Epoch: 3/10] [Training Loss: 0.030] [Test Loss: 0.030] [Test Accuracy: 0.991]
[Epoch: 4/10] [Training Loss: 0.024] [Test Loss: 0.046] [Test Accuracy: 0.990]
[Epoch: 5/10] [Training Loss: 0.020] [Test Loss: 0.032] [Test Accuracy: 0.992]
[Epoch: 6/10] [Training Loss: 0.017] [Test Loss: 0.046] [Test Accuracy: 0.991]
[Epoch: 7/10] [Training Loss: 0.015] [Test Loss: 0.034] [Test Accuracy: 0.992]
[Epoch: 8/10] [Training Loss: 0.011] [Test Loss: 0.048] [Test Accuracy: 0.992]
[Epoch: 9/10] [Training Loss: 0.012] [Test Loss: 0.037] [Test Accuracy: 0.991]
[Epoch: 10/10] [Training Loss: 0.013] [Test Loss: 0.038] [Test Accuracy: 0.992]
comp.ai.neural-nets
Câu Hỏi Thường Gặp có một số gợi ý tuyệt vời về nơi để xem xét nếu ròng thần kinh của bạn không được học tập; Tôi khuyên bạn nên bắt đầu từ đó.