informasi kontak saya
Surat[email protected]
2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Di sini kami menerapkan jaringan VGG-16 untuk mengklasifikasikan kumpulan data CIFAR
Kata pengantar
《Jaringan Konvolusional Sangat Dalam untuk Pengenalan Gambar Skala Besar》
ICLR tahun 2015
VGGIni dari OxfordBahasa Indonesia: Vbiasa sajaGgeometriG Diusulkan oleh grup roup (Anda harus bisa melihat asal usul nama VGG). Jaringan ini merupakan pekerjaan terkait di ILSVRC 2014. Pekerjaan utamanya adalah membuktikan bahwa peningkatan kedalaman jaringan dapat mempengaruhi kinerja akhir jaringan sampai batas tertentu. VGG memiliki dua struktur yaitu VGG16 dan VGG19. Tidak ada perbedaan mendasar antara keduanya, namun kedalaman jaringannya berbeda.
Prinsip VGG
Peningkatan VGG16 dibandingkan dengan AlexNet adalahGunakan beberapa kernel konvolusi 3x3 berturut-turut untuk menggantikan kernel konvolusi yang lebih besar di AlexNet (11x11, 7x7, 5x5) . Untuk bidang reseptif tertentu (ukuran lokal dari gambar masukan relatif terhadap keluaran), menggunakan kernel konvolusi kecil yang bertumpuk lebih baik daripada menggunakan kernel konvolusi besar, karena beberapa lapisan nonlinier dapat meningkatkan kedalaman jaringan untuk memastikan mode pembelajaran yang lebih kompleks, dan biayanya relatif kecil (parameternya lebih sedikit).
Sederhananya, di VGG, tiga kernel konvolusi 3x3 digunakan untuk menggantikan kernel konvolusi 7x7, dan dua kernel konvolusi 3x3 digunakan untuk menggantikan kernel konvolusi 5*5 bidang reseptif, kedalaman jaringan ditingkatkan, dan efek jaringan saraf ditingkatkan sampai batas tertentu.
Misalnya, superposisi lapis demi lapis dari tiga inti konvolusi 3x3 dengan langkah 1 dapat dianggap sebagai bidang reseptif berukuran 7 (sebenarnya, ini berarti bahwa tiga konvolusi kontinu 3x3 setara dengan konvolusi 7x7), dan superposisinya total parameter adalah Jumlahnya adalah 3x(9xC^2). Jika kernel konvolusi 7x7 digunakan secara langsung, jumlah total parameter adalah 49xC^2, di mana C mengacu pada jumlah saluran input dan output.Jelas, 27xC2 kurang dari 49xC2, yaitu, parameternya dikurangi; dan kernel konvolusi 3x3 kondusif untuk menjaga properti gambar dengan lebih baik.
Berikut penjelasan mengapa dua kernel konvolusi 3x3 dapat digunakan sebagai ganti kernel konvolusi 5*5:
Konvolusi 5x5 dianggap sebagai jaringan kecil yang terhubung penuh yang meluncur di area 5x5. Pertama-tama kita dapat melakukan konvolusi dengan filter konvolusi 3x3, dan kemudian menggunakan lapisan yang terhubung penuh untuk menghubungkan keluaran konvolusi 3x3 dilihat sebagai lapisan konvolusional 3x3. Dengan cara ini, kita dapat melakukan kaskade (melapiskan) dua konvolusi 3x3, bukan satu konvolusi 5x5.
Detailnya ditunjukkan pada gambar di bawah ini:
Struktur jaringan VGG
Berikut ini adalah struktur jaringan VGG (ada VGG16 dan VGG19):
Struktur jaringan GG
VGG16 berisi 16 lapisan tersembunyi (13 lapisan konvolusional dan 3 lapisan terhubung penuh), seperti yang ditunjukkan pada kolom D pada gambar di atas
VGG19 berisi 19 lapisan tersembunyi (16 lapisan konvolusional dan 3 lapisan terhubung penuh), seperti yang ditunjukkan pada kolom E pada gambar di atas
Struktur jaringan VGG sangat konsisten, menggunakan konvolusi 3x3 dan pooling maksimal 2x2 dari awal hingga akhir.
Keunggulan VGG
Struktur VGGNet sangat sederhana. Seluruh jaringan menggunakan ukuran kernel konvolusi yang sama (3x3) dan ukuran pooling maksimum (2x2).
Kombinasi beberapa lapisan konvolusional filter kecil (3x3) lebih baik daripada satu lapisan konvolusional filter besar (5x5 atau 7x7):
Terbukti bahwa kinerja dapat ditingkatkan dengan terus memperdalam struktur jaringan.
Kekurangan VGG
VGG mengkonsumsi lebih banyak sumber daya komputasi dan menggunakan lebih banyak parameter (ini bukan konvolusi 3x3), sehingga menghasilkan penggunaan memori lebih banyak (140M).
Kumpulan data CIFAR (Canadian Institute For Advanced Research) adalah kumpulan data gambar kecil yang banyak digunakan di bidang visi komputer. Data ini terutama digunakan untuk melatih algoritma pembelajaran mesin dan visi komputer, terutama dalam tugas-tugas seperti pengenalan dan klasifikasi gambar. Dataset CIFAR terdiri dari dua bagian utama: CIFAR-10 dan CIFAR-100.
CIFAR-10 merupakan dataset berisi 60.000 gambar berwarna berukuran 32x32 yang terbagi dalam 10 kategori yang masing-masing kategori berisi 6.000 gambar. 10 kategori tersebut adalah: pesawat terbang, mobil, burung, kucing, rusa, anjing, katak, kuda, perahu, dan truk. Dalam kumpulan data, 50.000 gambar digunakan untuk pelatihan dan 10.000 gambar digunakan untuk pengujian. Kumpulan data CIFAR-10 telah menjadi salah satu kumpulan data yang sangat populer dalam penelitian dan pengajaran di bidang visi komputer karena ukurannya yang moderat dan informasi kelas yang kaya.
Kumpulan data CIFAR biasanya digunakan untuk tugas-tugas seperti klasifikasi gambar, pengenalan objek, serta pelatihan dan pengujian jaringan saraf konvolusional (CNN). Karena ukurannya yang moderat dan informasi kategori yang kaya, ini ideal untuk pemula dan peneliti yang mengeksplorasi algoritma pengenalan gambar. Selain itu, banyak kompetisi visi komputer dan pembelajaran mesin juga menggunakan dataset CIFAR sebagai tolok ukur untuk mengevaluasi kinerja algoritma kontestan.
Untuk menyiapkan data setnya sudah saya download, jika tidak bisa langsung saja download di website resminya, atau saya akan memberikannya langsung kepada anda.
Jika Anda membutuhkan kumpulan data, silakan hubungi email: [email protected]
Kumpulan data saya awalnya dibuat melalui data yang diunduh di torchvision. Saya tidak benar-benar ingin melakukan itu sekarang. Saya ingin menerapkan definisi Kumpulan Data dan memuat DataLoader selangkah demi selangkah, memahami proses ini, dan memahami. proses pemrosesan kumpulan data, dapat membuat Anda lebih mendalami pembelajaran mendalam.
Gaya kumpulan datanya adalah sebagai berikut:
Kategori label pada dataset menggunakan a.meta
File disimpan, jadi kita perlu menguraikannya .meta
file untuk membaca semua data tag. Kode penguraiannya adalah sebagai berikut:
# 首先了解所有的标签,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)
Hasil analisisnya adalah sebagai berikut:
['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
Kumpulan data kita telah diunduh, jadi kita perlu membaca isi file tersebut. Karena file tersebut adalah file biner, kita perlu menggunakan mode membaca biner untuk membacanya.
Kode bacaannya adalah sebagai berikut:
# 载入单个批次的数据
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}')
hasil:
数据形状: (10000, 32, 32, 3), 标签形状: (10000,)
Setelah pengujian di atas, kita tahu cara memuat data. Sekarang mari kita memuat semua data.
Memuat set pelatihan:
# 整合所有批次的数据
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)
Keluaran:
训练数据形状: (50000, 32, 32, 3), 训练标签形状: (50000,)
Memuat set pengujian:
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}')
Keluaran:
测试数据形状: (10000, 32, 32, 3), 测试标签形状: (10000,)
Mendefinisikan subkelas dari kelas Dataset adalah untuk memfasilitasi pemuatan Dataloader selanjutnya untuk pelatihan batch.
Ada tiga metode yang harus diterapkan oleh subkelas Dataset.
__init__()
konstruktor kelas__len__()
Mengembalikan panjang kumpulan data__getitem__()
Dapatkan sepotong data dari kumpulan dataDi sini implementasi saya adalah sebagai berikut:
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)
Kami menerapkan kerangka Pytorch berdasarkan jaringan VGG16 yang disebutkan di atas.
terutama dibagi:
Implementasinya adalah sebagai berikut:
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
Untuk pelatihan dan pengujian, Anda hanya perlu membuat instance model, lalu menentukan fungsi pengoptimalan, fungsi kerugian, dan tingkat kerugian, lalu melakukan pelatihan dan pengujian.
kode tampil seperti di bawah ini:
Definisi hiperparameter:
# 定义模型进行训练
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)
Fungsi tes:
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))
Zaman pelatihan:
# 定义训练步骤
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()
Simpan model yang dilatih:
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)
Keluaran:
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
Verifikasi kuda
anjing verifikasi