|
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- # @Time : 2023/2/10 下午3:31
- # @File : train.py
- # ----------------------------------------------
- # ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆
- # >>> Author : Kevin Chang
- # >>> QQ : 565479588
- # >>> Mail : lovecode@gmail.com
- # >>> Github : https://github.com/lovecode100
- # >>> Blog : https://www.cnblogs.com/lovecode
- # ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆ ☆
- import os
- import math
- import argparse
-
- import torch
- import torch.optim as optim
- from torch.utils.tensorboard import SummaryWriter
- from torchvision import transforms
- import torch.optim.lr_scheduler as lr_scheduler
-
- from model import densenet121, load_state_dict
- from my_dataset import MyDataSet
- from utils import read_split_data, train_one_epoch, evaluate
-
-
- def main(args):
- device = torch.device(args.device if torch.cuda.is_available() else "cpu")
-
- print(args)
- print('Start Tensorboard with "tensorboard --logdir=runs", view at http://localhost:6006/')
- tb_writer = SummaryWriter()
- if os.path.exists("./weights") is False:
- os.makedirs("./weights")
-
- train_images_path, train_images_label, val_images_path, val_images_label = read_split_data(args.data_path)
-
- data_transform = {
- "train": transforms.Compose([transforms.RandomResizedCrop(224),
- transforms.RandomHorizontalFlip(),
- transforms.ToTensor(),
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])]),
- "val": transforms.Compose([transforms.Resize(256),
- transforms.CenterCrop(224),
- transforms.ToTensor(),
- transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])}
-
- # 实例化训练数据集
- train_dataset = MyDataSet(images_path=train_images_path,
- images_class=train_images_label,
- transform=data_transform["train"])
-
- # 实例化验证数据集
- val_dataset = MyDataSet(images_path=val_images_path,
- images_class=val_images_label,
- transform=data_transform["val"])
-
- batch_size = args.batch_size
- nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8]) # 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,
- pin_memory=True,
- num_workers=nw,
- collate_fn=train_dataset.collate_fn)
-
- val_loader = torch.utils.data.DataLoader(val_dataset,
- batch_size=batch_size,
- shuffle=False,
- pin_memory=True,
- num_workers=nw,
- collate_fn=val_dataset.collate_fn)
-
- # 如果存在预训练权重则载入
- model = densenet121(num_classes=args.num_classes).to(device)
- if args.weights != "":
- if os.path.exists(args.weights):
- load_state_dict(model, args.weights)
- else:
- raise FileNotFoundError("not found weights file: {}".format(args.weights))
-
- # 是否冻结权重
- if args.freeze_layers:
- for name, para in model.named_parameters():
- # 除最后的全连接层外,其他权重全部冻结
- if "classifier" not in name:
- para.requires_grad_(False)
-
- pg = [p for p in model.parameters() if p.requires_grad]
- optimizer = optim.SGD(pg, lr=args.lr, momentum=0.9, weight_decay=1E-4, nesterov=True)
- # Scheduler https://arxiv.org/pdf/1812.01187.pdf
- lf = lambda x: ((1 + math.cos(x * math.pi / args.epochs)) / 2) * (1 - args.lrf) + args.lrf # cosine
- scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf)
-
- for epoch in range(args.epochs):
- # train
- mean_loss = train_one_epoch(model=model,
- optimizer=optimizer,
- data_loader=train_loader,
- device=device,
- epoch=epoch)
-
- scheduler.step()
-
- # validate
- acc = evaluate(model=model,
- data_loader=val_loader,
- device=device)
-
- print("[epoch {}] accuracy: {}".format(epoch, round(acc, 3)))
- tags = ["loss", "accuracy", "learning_rate"]
- tb_writer.add_scalar(tags[0], mean_loss, epoch)
- tb_writer.add_scalar(tags[1], acc, epoch)
- tb_writer.add_scalar(tags[2], optimizer.param_groups[0]["lr"], epoch)
-
- torch.save(model.state_dict(), "./weights/model-{}.pth".format(epoch))
-
-
- if __name__ == '__main__':
- parser = argparse.ArgumentParser()
- parser.add_argument('--num_classes', type=int, default=5)
- parser.add_argument('--epochs', type=int, default=30)
- parser.add_argument('--batch-size', type=int, default=16)
- parser.add_argument('--lr', type=float, default=0.001)
- parser.add_argument('--lrf', type=float, default=0.1)
-
- # 数据集所在根目录
- # https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz
- parser.add_argument('--data-path', type=str,
- default="/data/flower_photos")
-
- # densenet121 官方权重下载地址
- # https://download.pytorch.org/models/densenet121-a639ec97.pth
- parser.add_argument('--weights', type=str, default='densenet121.pth',
- help='initial weights path')
- parser.add_argument('--freeze-layers', type=bool, default=False)
- parser.add_argument('--device', default='cuda:0', help='device id (i.e. 0 or 0,1 or cpu)')
-
- opt = parser.parse_args()
-
- main(opt)
|