τα στοιχεία επικοινωνίας μου
Ταχυδρομείο[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Εδώ υλοποιούμε ένα δίκτυο VGG-16 για την ταξινόμηση του συνόλου δεδομένων CIFAR
Πρόλογος
《Πολύ βαθιά συνελικτικά δίκτυα για αναγνώριση εικόνας μεγάλης κλίμακας》
ICLR 2015
VGGΕίναι από την ΟξφόρδηVείναι συνήθωςσοληομετρίασολ Προτείνεται από την ομάδα του roup (θα πρέπει να μπορείτε να δείτε την προέλευση του ονόματος VGG). Αυτό το δίκτυο είναι σχετική εργασία στο ILSVRC 2014. Η κύρια εργασία είναι να αποδειχθεί ότι η αύξηση του βάθους του δικτύου μπορεί να επηρεάσει την τελική απόδοση του δικτύου σε κάποιο βαθμό. Το VGG έχει δύο δομές, δηλαδή το VGG16 και το VGG19 Δεν υπάρχει ουσιαστική διαφορά μεταξύ των δύο, αλλά το βάθος του δικτύου είναι διαφορετικό.
Αρχή VGG
Μια βελτίωση του VGG16 σε σύγκριση με το AlexNet είναι αυτήΧρησιμοποιήστε πολλούς διαδοχικούς πυρήνες συνέλιξης 3x3 για να αντικαταστήσετε τους μεγαλύτερους πυρήνες συνέλιξης στο AlexNet (11x11, 7x7, 5x5) . Για ένα δεδομένο δεκτικό πεδίο (το τοπικό μέγεθος της εικόνας εισόδου σε σχέση με την έξοδο), η χρήση στοιβαγμένων μικρών πυρήνων συνέλιξης είναι καλύτερη από τη χρήση μεγάλων πυρήνων συνέλιξης, επειδή πολλαπλά μη γραμμικά επίπεδα μπορούν να αυξήσουν το βάθος του δικτύου για να εξασφαλίσουν πιο σύνθετη λειτουργία εκμάθησης το κόστος είναι σχετικά μικρό (λιγότερες παράμετροι).
Για να το θέσω απλά, στο VGG, τρεις πυρήνες συνέλιξης 3x3 χρησιμοποιούνται για να αντικαταστήσουν τον πυρήνα συνέλιξης 7x7 και δύο πυρήνες συνέλιξης 3x3 χρησιμοποιούνται για να αντικαταστήσουν τον πυρήνα συνέλιξης 5*5. Ο κύριος σκοπός αυτού είναι να διασφαλιστεί η ίδια υπό την προϋπόθεση του δεκτικού πεδίου, το βάθος του δικτύου βελτιώνεται και η επίδραση του νευρωνικού δικτύου βελτιώνεται σε κάποιο βαθμό.
Για παράδειγμα, η υπέρθεση στρώμα προς στρώμα τριών πυρήνων συνέλιξης 3x3 με διασκελισμό 1 μπορεί να θεωρηθεί ως δεκτικό πεδίο μεγέθους 7 (στην πραγματικότητα, σημαίνει ότι τρεις συνεχείς συνέλιξεις 3x3 ισοδυναμούν με μια συνέλιξη 7x7) και Οι συνολικές παράμετροι είναι Το ποσό είναι 3x(9xC^2).Προφανώς, 27xC2 λιγότερο από 49xC2, δηλαδή, οι παράμετροι μειώνονται και ο πυρήνας συνέλιξης 3x3 συμβάλλει στην καλύτερη διατήρηση των ιδιοτήτων της εικόνας.
Ακολουθεί μια εξήγηση για το γιατί μπορούν να χρησιμοποιηθούν δύο πυρήνες συνέλιξης 3x3 αντί για πυρήνες συνέλιξης 5*5:
Η συνέλιξη 5x5 θεωρείται ως ένα μικρό πλήρως συνδεδεμένο δίκτυο που ολισθαίνει στην περιοχή 5x5 να θεωρηθεί ως ένα συνελικτικό στρώμα 3x3. Με αυτόν τον τρόπο, μπορούμε να καταρρεύσουμε (υπερθέτουμε) δύο συνέλιξεις 3x3 αντί για μία συνέλιξη 5x5.
Οι λεπτομέρειες φαίνονται στο παρακάτω σχήμα:
Δομή δικτύου VGG
Ακολουθεί η δομή του δικτύου VGG (υπάρχουν και τα δύο VGG16 και VGG19):
Δομή δικτύου GG
Το VGG16 περιέχει 16 κρυφά επίπεδα (13 συνελικτικά επίπεδα και 3 πλήρως συνδεδεμένα επίπεδα), όπως φαίνεται στη στήλη D στο παραπάνω σχήμα
Το VGG19 περιέχει 19 κρυφά επίπεδα (16 συνελικτικά επίπεδα και 3 πλήρως συνδεδεμένα επίπεδα), όπως φαίνεται στη στήλη Ε στο παραπάνω σχήμα
Η δομή του δικτύου VGG είναι πολύ συνεπής, χρησιμοποιώντας συνέλιξη 3x3 και συγκέντρωση 2x2 max από την αρχή μέχρι το τέλος.
Πλεονεκτήματα του VGG
Η δομή του VGGNet είναι πολύ απλή. Ολόκληρο το δίκτυο χρησιμοποιεί το ίδιο μέγεθος πυρήνα συνέλιξης (3x3) και μέγιστο μέγεθος συγκέντρωσης (2x2).
Ο συνδυασμός πολλών μικρών συνελικτικών στρωμάτων φίλτρου (3x3) είναι καλύτερος από ένα μεγάλο συνελικτικό στρώμα φίλτρου (5x5 ή 7x7):
Επαληθεύεται ότι η απόδοση μπορεί να βελτιωθεί με τη συνεχή εμβάθυνση της δομής του δικτύου.
Μειονεκτήματα του VGG
Το VGG καταναλώνει περισσότερους υπολογιστικούς πόρους και χρησιμοποιεί περισσότερες παραμέτρους (αυτό δεν είναι το pot της συνέλιξης 3x3), με αποτέλεσμα περισσότερη χρήση μνήμης (140M).
Το σύνολο δεδομένων CIFAR (Canadian Institute For Advanced Research) είναι ένα μικρό σύνολο δεδομένων εικόνων που χρησιμοποιείται ευρέως στον τομέα της όρασης υπολογιστών. Χρησιμοποιείται κυρίως για την εκπαίδευση αλγορίθμων μηχανικής μάθησης και όρασης υπολογιστών, ειδικά σε εργασίες όπως η αναγνώριση και ταξινόμηση εικόνων. Το σύνολο δεδομένων CIFAR αποτελείται από δύο κύρια μέρη: CIFAR-10 και CIFAR-100.
Το CIFAR-10 είναι ένα σύνολο δεδομένων που περιέχει 60.000 έγχρωμες εικόνες 32x32, οι οποίες χωρίζονται σε 10 κατηγορίες, με κάθε κατηγορία να περιέχει 6.000 εικόνες. Οι 10 κατηγορίες είναι: αεροπλάνα, αυτοκίνητα, πουλιά, γάτες, ελάφια, σκύλοι, βάτραχοι, άλογα, βάρκες και φορτηγά. Στο σύνολο δεδομένων, 50.000 εικόνες χρησιμοποιούνται για εκπαίδευση και 10.000 εικόνες για δοκιμή. Το σύνολο δεδομένων CIFAR-10 έχει γίνει ένα από τα πολύ δημοφιλή σύνολα δεδομένων στην έρευνα και τη διδασκαλία στον τομέα της όρασης υπολογιστών λόγω του μέτριου μεγέθους και των πλούσιων πληροφοριών τάξης.
Το σύνολο δεδομένων CIFAR χρησιμοποιείται συνήθως για εργασίες όπως η ταξινόμηση εικόνων, η αναγνώριση αντικειμένων και η εκπαίδευση και η δοκιμή συνελικτικών νευρωνικών δικτύων (CNN). Λόγω του μέτριου μεγέθους και των πλούσιων πληροφοριών κατηγορίας, είναι ιδανικό για αρχάριους και ερευνητές που εξερευνούν αλγόριθμους αναγνώρισης εικόνων. Επιπλέον, πολλοί διαγωνισμοί υπολογιστικής όρασης και μηχανικής μάθησης χρησιμοποιούν επίσης το σύνολο δεδομένων CIFAR ως σημείο αναφοράς για την αξιολόγηση της απόδοσης των αλγορίθμων των διαγωνιζομένων.
Για να προετοιμάσω το σύνολο δεδομένων, το έχω ήδη κατεβάσει εάν δεν λειτουργεί, απλώς κατεβάστε το από τον επίσημο ιστότοπο, διαφορετικά θα σας το δώσω απευθείας.
Εάν χρειάζεστε το σύνολο δεδομένων, επικοινωνήστε με το email: [email protected]
Το σύνολο δεδομένων μου δημιουργήθηκε αρχικά μέσω των ληφθέντων δεδομένων στο torchvision. Δεν θέλω πραγματικά να το κάνω τώρα. Θέλω να εφαρμόσω τον ορισμό του συνόλου δεδομένων και τη φόρτωση του DataLoader βήμα προς βήμα, να κατανοήσω αυτήν τη διαδικασία. η διαδικασία επεξεργασίας συνόλων δεδομένων μπορεί να σας κάνει πιο εμπεριστατωμένους στη βαθιά μάθηση.
Το στυλ συνόλου δεδομένων έχει ως εξής:
Η κατηγορία ετικέτας του συνόλου δεδομένων χρησιμοποιεί α.meta
Το αρχείο είναι αποθηκευμένο, επομένως πρέπει να αναλύσουμε .meta
αρχείο για ανάγνωση όλων των δεδομένων ετικετών. Ο κώδικας ανάλυσης είναι ο εξής:
# 首先了解所有的标签,TODO 可以详细了解一下这个解包的过程
import pickle
def unpickle(file):
with open(file, 'rb') as fo:
dict = pickle.load(fo, encoding='bytes')
return dict
meta_data = unpickle('./dataset_method_1/cifar-10-batches-py/batches.meta')
label_names = meta_data[b'label_names']
# 将字节标签转换为字符串
label_names = [label.decode('utf-8') for label in label_names]
print(label_names)
Τα αποτελέσματα της ανάλυσης έχουν ως εξής:
['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
Το σύνολο δεδομένων μας έχει ληφθεί, επομένως πρέπει να διαβάσουμε τα περιεχόμενα του αρχείου Δεδομένου ότι το αρχείο είναι δυαδικό αρχείο, πρέπει να χρησιμοποιήσουμε τη λειτουργία δυαδικής ανάγνωσης για να το διαβάσουμε.
Ο κώδικας ανάγνωσης είναι ο εξής:
# 载入单个批次的数据
import numpy as np
def load_data_batch(file):
with open(file, 'rb') as fo:
dict = pickle.load(fo, encoding='bytes')
X = dict[b'data']
Y = dict[b'labels']
X = X.reshape(10000, 3, 32, 32).transpose(0, 2, 3, 1) # reshape and transpose to (10000, 32, 32, 3)
Y = np.array(Y)
return X, Y
# 加载第一个数据批次
data_batch_1 = './dataset_method_1/cifar-10-batches-py/data_batch_1'
X1, Y1 = load_data_batch(data_batch_1)
print(f'数据形状: {X1.shape}, 标签形状: {Y1.shape}')
αποτέλεσμα:
数据形状: (10000, 32, 32, 3), 标签形状: (10000,)
Μετά την παραπάνω δοκιμή, ξέρουμε πώς να φορτώνουμε δεδομένα Τώρα ας φορτώσουμε όλα τα δεδομένα.
Φόρτωση του σετ εκπαίδευσης:
# 整合所有批次的数据
def load_all_data_batches(batch_files):
X_list, Y_list = [], []
for file in batch_files:
X, Y = load_data_batch(file)
X_list.append(X)
Y_list.append(Y)
X_all = np.concatenate(X_list)
Y_all = np.concatenate(Y_list)
return X_all, Y_all
batch_files = [
'./dataset_method_1/cifar-10-batches-py/data_batch_1',
'./dataset_method_1/cifar-10-batches-py/data_batch_2',
'./dataset_method_1/cifar-10-batches-py/data_batch_3',
'./dataset_method_1/cifar-10-batches-py/data_batch_4',
'./dataset_method_1/cifar-10-batches-py/data_batch_5'
]
X_train, Y_train = load_all_data_batches(batch_files)
print(f'训练数据形状: {X_train.shape}, 训练标签形状: {Y_train.shape}')
Y_train = Y_train.astype(np.int64)
Παραγωγή:
训练数据形状: (50000, 32, 32, 3), 训练标签形状: (50000,)
Φόρτωση του συνόλου δοκιμής:
test_batch = './dataset_method_1/cifar-10-batches-py/test_batch'
X_test, Y_test = load_data_batch(test_batch)
Y_test = Y_test.astype(np.int64)
print(f'测试数据形状: {X_test.shape}, 测试标签形状: {Y_test.shape}')
Παραγωγή:
测试数据形状: (10000, 32, 32, 3), 测试标签形状: (10000,)
Ο καθορισμός μιας υποκλάσης της κλάσης συνόλου δεδομένων είναι για να διευκολυνθεί η επακόλουθη φόρτωση του Dataloader για ομαδική εκπαίδευση.
Υπάρχουν τρεις μέθοδοι που πρέπει να εφαρμόσουν οι υποκλάσεις του συνόλου δεδομένων.
__init__()
κατασκευαστής τάξης__len__()
Επιστρέφει το μήκος του συνόλου δεδομένων__getitem__()
Λάβετε ένα κομμάτι δεδομένων από το σύνολο δεδομένωνΕδώ η εφαρμογή μου έχει ως εξής:
from torch.utils.data import DataLoader, Dataset
# 定义 Pytorch 的数据集
class CIFARDataset(Dataset):
def __init__(self, images, labels, transform=None):
self.images = images
self.labels = labels
self.transform = transform
def __len__(self):
return len(self.images)
def __getitem__(self, idx):
image = self.images[idx]
label = self.labels[idx]
if self.transform:
image = self.transform(image)
return image, label
transform_train = transforms.Compose(
[transforms.Pad(4),
transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
transforms.RandomHorizontalFlip(),
transforms.RandomGrayscale(),
transforms.RandomCrop(32, padding=4),
])
# 把数据集变成 Image 的数组,不然好像不能进行数据的增强
# 改变训练数据
from PIL import Image
def get_PIL_Images(origin_data):
datas = []
for i in range(len(origin_data)):
data = Image.fromarray(origin_data[i])
datas.append(data)
return datas
train_data = get_PIL_Images(X_train)
train_loader = DataLoader(CIFARDataset(train_data, Y_train, transform_train), batch_size=24, shuffle=True)
# 测试集的预处理
transform_test = transforms.Compose(
[
transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))]
)
test_loader = DataLoader(CIFARDataset(X_test, Y_test, transform_test), batch_size=24, shuffle=False)
Υλοποιούμε το πλαίσιο Pytorch με βάση το δίκτυο VGG16 που αναφέρθηκε παραπάνω.
κυρίως διαιρείται:
Η υλοποίηση έχει ως εξής:
class VGG16(nn.Module):
def __init__(self):
super(VGG16, self).__init__()
# 卷积层,这里进行卷积
self.convolusion = nn.Sequential(
nn.Conv2d(3, 96, kernel_size=3, padding=1), # 设置为padding=1 卷积完后,数据大小不会变
nn.BatchNorm2d(96),
nn.ReLU(inplace=True),
nn.Conv2d(96, 96, kernel_size=3, padding=1),
nn.BatchNorm2d(96),
nn.ReLU(inplace=True),
nn.Conv2d(96, 96, kernel_size=3, padding=1),
nn.BatchNorm2d(96),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(96, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.Conv2d(128, 128, kernel_size=3, padding=1),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(128, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, padding=1),
nn.BatchNorm2d(256),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(256, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.BatchNorm2d(512),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.AvgPool2d(kernel_size=1, stride=1)
)
# 全连接层
self.dense = nn.Sequential(
nn.Linear(512, 4096), # 32*32 的图像大小经过 5 次最大化池化后就只有 1*1 了,所以就是 512 个通道的数据输入全连接层
nn.ReLU(inplace=True),
nn.Dropout(0.4),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(0.4),
)
# 输出层
self.classifier = nn.Linear(4096, 10)
def forward(self, x):
out = self.convolusion(x)
out = out.view(out.size(0), -1)
out = self.dense(out)
out = self.classifier(out)
return out
Για εκπαίδευση και δοκιμή, χρειάζεται μόνο να δημιουργήσετε το μοντέλο, στη συνέχεια να ορίσετε τη συνάρτηση βελτιστοποίησης, τη συνάρτηση απώλειας και το ποσοστό απώλειας και, στη συνέχεια, να εκτελέσετε εκπαίδευση και δοκιμή.
ο κώδικας εμφανίζεται ως εξής:
Ορισμός υπερπαραμέτρου:
# 定义模型进行训练
model = VGG16()
# model.load_state_dict(torch.load('./my-VGG16.pth'))
optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=5e-3)
loss_func = nn.CrossEntropyLoss()
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.4, last_epoch=-1)
Λειτουργία δοκιμής:
def test():
model.eval()
correct = 0 # 预测正确的图片数
total = 0 # 总共的图片数
with torch.no_grad():
for data in test_loader:
images, labels = data
images = images.to(device)
outputs = model(images).to(device)
outputs = outputs.cpu()
outputarr = outputs.numpy()
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
accuracy = 100 * correct / total
accuracy_rate.append(accuracy)
print(f'准确率为:{accuracy}%'.format(accuracy))
Εποχές εκπαίδευσης:
# 定义训练步骤
total_times = 40
total = 0
accuracy_rate = []
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
for epoch in range(total_times):
model.train()
model.to(device)
running_loss = 0.0
total_correct = 0
total_trainset = 0
print("epoch: ",epoch)
for i, (data,labels) in enumerate(train_loader):
data = data.to(device)
outputs = model(data).to(device)
labels = labels.to(device)
loss = loss_func(outputs,labels).to(device)
optimizer.zero_grad()
loss.backward()
optimizer.step()
running_loss += loss.item()
_,pred = outputs.max(1)
correct = (pred == labels).sum().item()
total_correct += correct
total_trainset += data.shape[0]
if i % 100 == 0 and i > 0:
print(f"正在进行第{i}次训练, running_loss={running_loss}".format(i, running_loss))
running_loss = 0.0
test()
scheduler.step()
Αποθηκεύστε το εκπαιδευμένο μοντέλο:
torch.save(model.state_dict(), './my-VGG16.pth')
accuracy_rate = np.array(accuracy_rate)
times = np.linspace(1, total_times, total_times)
plt.xlabel('times')
plt.ylabel('accuracy rate')
plt.plot(times, accuracy_rate)
plt.show()
print(accuracy_rate)
model_my_vgg = VGG16()
model_my_vgg.load_state_dict(torch.load('./my-VGG16-best.pth',map_location='cpu'))
from torchvision import transforms
from PIL import Image
# 定义图像预处理步骤
preprocess = transforms.Compose([
transforms.Resize((32, 32)),
transforms.ToTensor(),
# transforms.Normalize(mean=[0.485,0.456,0.406], std=[0.229,0.224,0.225]),
])
def load_image(image_path):
image = Image.open(image_path)
image = preprocess(image)
image = image.unsqueeze(0) # 添加批次维度
return image
image_data = load_image('./plane2.jpg')
print(image_data.shape)
output = model_my_vgg(image_data)
verify_data = X1[9]
verify_label = Y1[9]
output_verify = model_my_vgg(transform_test(verify_data).unsqueeze(0))
print(output)
print(output_verify)
Παραγωγή:
torch.Size([1, 3, 32, 32])
tensor([[ 1.5990, -0.5269, 0.7254, 0.3432, -0.5036, -0.3267, -0.5302, -0.9417,
0.4186, -0.1213]], grad_fn=<AddmmBackward0>)
tensor([[-0.6541, -2.0759, 0.6308, 1.9791, 0.8525, 1.2313, 0.1856, 0.3243,
-1.3374, -1.0211]], grad_fn=<AddmmBackward0>)
print(label_names[torch.argmax(output,dim=1,keepdim=False)])
print(label_names[verify_label])
print("pred:",label_names[torch.argmax(output_verify,dim=1,keepdim=False)])
airplane
cat
pred: cat
Επαληθεύστε το άλογο
σκύλος επαλήθευσης