184 lines
6.0 KiB
Python
184 lines
6.0 KiB
Python
import json
|
|
import os
|
|
|
|
import matplotlib.pyplot as plt
|
|
import torch
|
|
import torch.nn as nn
|
|
import torch.optim as optim
|
|
from model import GoogLeNet
|
|
from torchvision import datasets, transforms
|
|
from tqdm import tqdm
|
|
|
|
|
|
def main():
|
|
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
|
|
print("using {} device.".format(device))
|
|
|
|
data_transform = {
|
|
"train": transforms.Compose(
|
|
[
|
|
transforms.RandomResizedCrop(224),
|
|
transforms.RandomHorizontalFlip(),
|
|
transforms.ToTensor(),
|
|
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
|
|
]
|
|
),
|
|
"val": transforms.Compose(
|
|
[
|
|
transforms.Resize((224, 224)),
|
|
transforms.ToTensor(),
|
|
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
|
|
]
|
|
),
|
|
}
|
|
|
|
data_root = os.path.abspath(os.path.join(os.getcwd(), "..")) # get data root path
|
|
image_path = os.path.join(data_root, "data", "flower_data") # flower data set path
|
|
assert os.path.exists(image_path), "{} path does not exist.".format(image_path)
|
|
train_dataset = datasets.ImageFolder(
|
|
root=os.path.join(image_path, "train"), transform=data_transform["train"]
|
|
)
|
|
train_num = len(train_dataset)
|
|
|
|
# {'daisy':0, 'dandelion':1, 'roses':2, 'sunflower':3, 'tulips':4}
|
|
flower_list = train_dataset.class_to_idx
|
|
cla_dict = dict((val, key) for key, val in flower_list.items())
|
|
# write dict into json file
|
|
json_str = json.dumps(cla_dict, indent=4)
|
|
with open("class_indices.json", "w") as json_file:
|
|
json_file.write(json_str)
|
|
|
|
batch_size = 4 # 如果cuda报超出显存可以改小一点
|
|
nw = min(
|
|
[os.cpu_count(), batch_size if batch_size > 1 else 0, 4]
|
|
) # number of workers
|
|
print("Using {} dataloader workers every process".format(nw))
|
|
train_loader = torch.utils.data.DataLoader(
|
|
train_dataset, batch_size=batch_size, shuffle=True, num_workers=nw
|
|
)
|
|
|
|
validate_dataset = datasets.ImageFolder(
|
|
root=os.path.join(image_path, "val"), transform=data_transform["val"]
|
|
)
|
|
val_num = len(validate_dataset)
|
|
validate_loader = torch.utils.data.DataLoader(
|
|
validate_dataset, batch_size=batch_size, shuffle=False, num_workers=nw
|
|
)
|
|
|
|
print(
|
|
"using {} images for training, {} images for validation.".format(
|
|
train_num, val_num
|
|
)
|
|
)
|
|
|
|
# test_data_iter = iter(validate_loader)
|
|
# test_image, test_label = test_data_iter.next()
|
|
|
|
# net = torchvision.models.googlenet(num_classes=5)
|
|
# model_dict = net.state_dict()
|
|
# pretrain_model = torch.load("googlenet.pth")
|
|
# del_list = ["aux1.fc2.weight", "aux1.fc2.bias",
|
|
# "aux2.fc2.weight", "aux2.fc2.bias",
|
|
# "fc.weight", "fc.bias"]
|
|
# pretrain_dict = {k: v for k, v in pretrain_model.items() if k not in del_list}
|
|
# model_dict.update(pretrain_dict)
|
|
# net.load_state_dict(model_dict)
|
|
net = GoogLeNet(num_classes=5, aux_logits=True, init_weights=True)
|
|
|
|
net.to(device)
|
|
loss_function = nn.CrossEntropyLoss()
|
|
optimizer = optim.Adam(net.parameters(), lr=0.0003)
|
|
|
|
epochs = 30
|
|
best_acc = 0.0
|
|
# googlenet 官方权重下载: https://download.pytorch.org/models/googlenet-1378be20.pth
|
|
save_path = "./googleNet.pth"
|
|
train_steps = len(train_loader)
|
|
|
|
# Lists to store metrics for visualization
|
|
train_losses = []
|
|
val_accuracies = []
|
|
|
|
for epoch in range(epochs):
|
|
# train
|
|
net.train()
|
|
running_loss = 0.0
|
|
train_bar = tqdm(train_loader)
|
|
for step, data in enumerate(train_bar):
|
|
images, labels = data
|
|
optimizer.zero_grad()
|
|
logits, aux_logits2, aux_logits1 = net(images.to(device))
|
|
loss0 = loss_function(logits, labels.to(device))
|
|
loss1 = loss_function(aux_logits1, labels.to(device))
|
|
loss2 = loss_function(aux_logits2, labels.to(device))
|
|
loss = loss0 + loss1 * 0.3 + loss2 * 0.3
|
|
loss.backward()
|
|
optimizer.step()
|
|
|
|
# print statistics
|
|
running_loss += loss.item()
|
|
|
|
train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(
|
|
epoch + 1, epochs, loss
|
|
)
|
|
|
|
# validate
|
|
net.eval()
|
|
acc = 0.0 # accumulate accurate number / epoch
|
|
with torch.no_grad():
|
|
val_bar = tqdm(validate_loader)
|
|
for val_data in val_bar:
|
|
val_images, val_labels = val_data
|
|
outputs = net(
|
|
val_images.to(device)
|
|
) # eval model only have last output layer
|
|
predict_y = torch.max(outputs, dim=1)[1]
|
|
acc += torch.eq(predict_y, val_labels.to(device)).sum().item()
|
|
|
|
val_accurate = acc / val_num
|
|
epoch_loss = running_loss / train_steps
|
|
|
|
# Store metrics for visualization
|
|
train_losses.append(epoch_loss)
|
|
val_accuracies.append(val_accurate)
|
|
|
|
print(
|
|
"[epoch %d] train_loss: %.3f val_accuracy: %.3f"
|
|
% (epoch + 1, epoch_loss, val_accurate)
|
|
)
|
|
|
|
if val_accurate > best_acc:
|
|
best_acc = val_accurate
|
|
torch.save(net.state_dict(), save_path)
|
|
|
|
print("Finished Training")
|
|
|
|
# Visualize training process
|
|
plt.figure(figsize=(12, 5))
|
|
|
|
# Plot training loss
|
|
plt.subplot(1, 2, 1)
|
|
plt.plot(range(1, epochs + 1), train_losses, "bo-", label="Training Loss")
|
|
plt.title("Training Loss per Epoch")
|
|
plt.xlabel("Epoch")
|
|
plt.ylabel("Loss")
|
|
plt.grid(True)
|
|
plt.legend()
|
|
|
|
# Plot validation accuracy
|
|
plt.subplot(1, 2, 2)
|
|
plt.plot(range(1, epochs + 1), val_accuracies, "ro-", label="Validation Accuracy")
|
|
plt.title("Validation Accuracy per Epoch")
|
|
plt.xlabel("Epoch")
|
|
plt.ylabel("Accuracy")
|
|
plt.grid(True)
|
|
plt.legend()
|
|
|
|
plt.tight_layout()
|
|
plt.savefig("training_visualization.png")
|
|
plt.show()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|