├── .gitignore ├── Code ├── 1_data_prepare │ ├── 1_1_cifar10_to_png.py │ ├── 1_2_split_dataset.py │ ├── 1_3_generate_txt.py │ ├── 1_3_mydataset.py │ └── 1_5_compute_mean.py ├── 2_model │ ├── 2_finetune.py │ └── net_params.pkl ├── 3_optimizer │ ├── 3_1_lossFunction │ │ ├── 1_L1Loss.py │ │ ├── 2_MSELoss.py │ │ ├── 3_CrossEntropyLoss.py │ │ ├── 4_NLLLoss.py │ │ ├── 5_PoissonNLLLoss.py │ │ └── 6_KLDivLoss.py │ └── 3_2_optimizer │ │ ├── 1_param_groups.py │ │ ├── 2_zero_grad.py │ │ ├── 3_state_dict.py │ │ ├── 4_load_state_dict.py │ │ ├── 5_add_param_group.py │ │ └── net_params.pkl ├── 4_viewer │ ├── 1_tensorboardX_demo.py │ ├── 2_visual_weights.py │ ├── 3_visual_featuremaps.py │ ├── 4_hist_grad_weight.py │ ├── 5_Show_ConfMat.py │ └── 6_hook_for_grad_cam.py ├── main_training │ └── main.py └── utils │ ├── __init__.py │ └── utils.py ├── Data ├── PyTorch_tutorial_0.0.4_余霆嵩.pdf ├── PyTorch_tutorial_0.0.5_余霆嵩.pdf ├── alipay.jpg ├── cam_img │ ├── test_img_1.png │ ├── test_img_2.png │ ├── test_img_3.png │ ├── test_img_4.png │ ├── test_img_5.png │ ├── test_img_6.png │ ├── test_img_7.png │ └── test_img_8.png ├── cat.png ├── cover.png ├── net_params_72p.pkl ├── visual.txt └── wechat.jpg ├── readme.md └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | *.so 3 | *.egg 4 | *.egg-info 5 | *.DS_Store 6 | -------------------------------------------------------------------------------- /Code/1_data_prepare/1_1_cifar10_to_png.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | """ 3 | 将cifar10的data_batch_12345 转换成 png格式的图片 4 | 每个类别单独存放在一个文件夹,文件夹名称为0-9 5 | """ 6 | from imageio import imwrite 7 | import numpy as np 8 | import os 9 | import pickle 10 | 11 | 12 | base_dir = "D:/python 11/新建文件夹/practise/pytorch" #修改为当前Data 目录所在的绝对路径 13 | data_dir = os.path.join(base_dir, "Data", "cifar-10-batches-py") 14 | train_o_dir = os.path.join( base_dir, "Data", "cifar-10-png", "raw_train") 15 | test_o_dir = os.path.join( base_dir, "Data", "cifar-10-png", "raw_test") 16 | 17 | Train = False # 不解压训练集,仅解压测试集 18 | 19 | # 解压缩,返回解压后的字典 20 | def unpickle(file): 21 | with open(file, 'rb') as fo: 22 | dict_ = pickle.load(fo, encoding='bytes') 23 | return dict_ 24 | 25 | def my_mkdir(my_dir): 26 | if not os.path.isdir(my_dir): 27 | os.makedirs(my_dir) 28 | 29 | 30 | # 生成训练集图片, 31 | if __name__ == '__main__': 32 | if Train: 33 | for j in range(1, 6): 34 | data_path = os.path.join(data_dir, "data_batch_" + str(j)) # data_batch_12345 35 | train_data = unpickle(data_path) 36 | print(data_path + " is loading...") 37 | 38 | for i in range(0, 10000): 39 | img = np.reshape(train_data[b'data'][i], (3, 32, 32)) 40 | img = img.transpose(1, 2, 0) 41 | 42 | label_num = str(train_data[b'labels'][i]) 43 | o_dir = os.path.join(train_o_dir, label_num) 44 | my_mkdir(o_dir) 45 | 46 | img_name = label_num + '_' + str(i + (j - 1)*10000) + '.png' 47 | img_path = os.path.join(o_dir, img_name) 48 | imwrite(img_path, img) 49 | print(data_path + " loaded.") 50 | 51 | print("test_batch is loading...") 52 | 53 | # 生成测试集图片 54 | test_data_path = os.path.join(data_dir, "test_batch") 55 | test_data = unpickle(test_data_path) 56 | for i in range(0, 10000): 57 | img = np.reshape(test_data[b'data'][i], (3, 32, 32)) 58 | img = img.transpose(1, 2, 0) 59 | 60 | label_num = str(test_data[b'labels'][i]) 61 | o_dir = os.path.join(test_o_dir, label_num) 62 | my_mkdir(o_dir) 63 | 64 | img_name = label_num + '_' + str(i) + '.png' 65 | img_path = os.path.join(o_dir, img_name) 66 | imwrite(img_path, img) 67 | 68 | print("test_batch loaded.") 69 | -------------------------------------------------------------------------------- /Code/1_data_prepare/1_2_split_dataset.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """ 3 | 将原始数据集进行划分成训练集、验证集和测试集 4 | """ 5 | 6 | import os 7 | import glob 8 | import random 9 | import shutil 10 | 11 | dataset_dir = os.path.join("..", "..", "Data", "cifar-10-png", "raw_test") 12 | train_dir = os.path.join("..", "..", "Data", "train") 13 | valid_dir = os.path.join("..", "..", "Data", "valid") 14 | test_dir = os.path.join("..", "..", "Data", "test") 15 | 16 | train_per = 0.8 17 | valid_per = 0.1 18 | test_per = 0.1 19 | 20 | 21 | def makedir(new_dir): 22 | if not os.path.exists(new_dir): 23 | os.makedirs(new_dir) 24 | 25 | 26 | if __name__ == '__main__': 27 | 28 | for root, dirs, files in os.walk(dataset_dir): 29 | for sDir in dirs: 30 | imgs_list = glob.glob(os.path.join(root, sDir, '*.png')) 31 | random.seed(666) 32 | random.shuffle(imgs_list) 33 | imgs_num = len(imgs_list) 34 | 35 | train_point = int(imgs_num * train_per) 36 | valid_point = int(imgs_num * (train_per + valid_per)) 37 | 38 | for i in range(imgs_num): 39 | if i < train_point: 40 | out_dir = os.path.join(train_dir, sDir) 41 | elif i < valid_point: 42 | out_dir = os.path.join(valid_dir, sDir) 43 | else: 44 | out_dir = os.path.join(test_dir, sDir) 45 | 46 | makedir(out_dir) 47 | out_path = os.path.join(out_dir, os.path.split(imgs_list[i])[-1]) 48 | shutil.copy(imgs_list[i], out_path) 49 | 50 | print('Class:{}, train:{}, valid:{}, test:{}'.format(sDir, train_point, valid_point-train_point, imgs_num-valid_point)) 51 | -------------------------------------------------------------------------------- /Code/1_data_prepare/1_3_generate_txt.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | import os 3 | ''' 4 | 为数据集生成对应的txt文件 5 | ''' 6 | 7 | train_txt_path = os.path.join("..", "..", "Data", "train.txt") 8 | train_dir = os.path.join("..", "..", "Data", "train") 9 | 10 | valid_txt_path = os.path.join("..", "..", "Data", "valid.txt") 11 | valid_dir = os.path.join("..", "..", "Data", "valid") 12 | 13 | 14 | def gen_txt(txt_path, img_dir): 15 | f = open(txt_path, 'w') 16 | 17 | for root, s_dirs, _ in os.walk(img_dir, topdown=True): # 获取 train文件下各文件夹名称 18 | for sub_dir in s_dirs: 19 | i_dir = os.path.join(root, sub_dir) # 获取各类的文件夹 绝对路径 20 | img_list = os.listdir(i_dir) # 获取类别文件夹下所有png图片的路径 21 | for i in range(len(img_list)): 22 | if not img_list[i].endswith('png'): # 若不是png文件,跳过 23 | continue 24 | label = img_list[i].split('_')[0] 25 | img_path = os.path.join(i_dir, img_list[i]) 26 | line = img_path + ' ' + label + '\n' 27 | f.write(line) 28 | f.close() 29 | 30 | 31 | if __name__ == '__main__': 32 | gen_txt(train_txt_path, train_dir) 33 | gen_txt(valid_txt_path, valid_dir) 34 | 35 | -------------------------------------------------------------------------------- /Code/1_data_prepare/1_3_mydataset.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from PIL import Image 3 | from torch.utils.data import Dataset 4 | 5 | 6 | class MyDataset(Dataset): 7 | def __init__(self, txt_path, transform=None, target_transform=None): 8 | fh = open(txt_path, 'r') 9 | imgs = [] 10 | for line in fh: 11 | line = line.rstrip() 12 | words = line.split() 13 | imgs.append((words[0], int(words[1]))) 14 | 15 | self.imgs = imgs # 最主要就是要生成这个list, 然后DataLoader中给index,通过getitem读取图片数据 16 | self.transform = transform 17 | self.target_transform = target_transform 18 | 19 | def __getitem__(self, index): 20 | fn, label = self.imgs[index] 21 | img = Image.open(fn).convert('RGB') # 像素值 0~255,在transfrom.totensor会除以255,使像素值变成 0~1 22 | 23 | if self.transform is not None: 24 | img = self.transform(img) # 在这里做transform,转为tensor等等 25 | 26 | return img, label 27 | 28 | def __len__(self): 29 | return len(self.imgs) -------------------------------------------------------------------------------- /Code/1_data_prepare/1_5_compute_mean.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import numpy as np 4 | import cv2 5 | import random 6 | import os 7 | 8 | """ 9 | 随机挑选CNum张图片,进行按通道计算均值mean和标准差std 10 | 先将像素从0~255归一化至 0-1 再计算 11 | """ 12 | 13 | 14 | train_txt_path = os.path.join("..", "..", "Data/train.txt") 15 | 16 | CNum = 2000 # 挑选多少图片进行计算 17 | 18 | img_h, img_w = 32, 32 19 | imgs = np.zeros([img_w, img_h, 3, 1]) 20 | means, stdevs = [], [] 21 | 22 | with open(train_txt_path, 'r') as f: 23 | lines = f.readlines() 24 | random.shuffle(lines) # shuffle , 随机挑选图片 25 | 26 | for i in range(CNum): 27 | img_path = lines[i].rstrip().split()[0] 28 | 29 | img = cv2.imread(img_path) 30 | img = cv2.resize(img, (img_h, img_w)) 31 | 32 | img = img[:, :, :, np.newaxis] 33 | imgs = np.concatenate((imgs, img), axis=3) 34 | print(i) 35 | 36 | imgs = imgs.astype(np.float32)/255. 37 | 38 | 39 | for i in range(3): 40 | pixels = imgs[:,:,i,:].ravel() # 拉成一行 41 | means.append(np.mean(pixels)) 42 | stdevs.append(np.std(pixels)) 43 | 44 | means.reverse() # BGR --> RGB 45 | stdevs.reverse() 46 | 47 | print("normMean = {}".format(means)) 48 | print("normStd = {}".format(stdevs)) 49 | print('transforms.Normalize(normMean = {}, normStd = {})'.format(means, stdevs)) 50 | 51 | -------------------------------------------------------------------------------- /Code/2_model/2_finetune.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | from torch.utils.data import DataLoader 5 | import torchvision.transforms as transforms 6 | import numpy as np 7 | import os 8 | from torch.autograd import Variable 9 | import torch.nn as nn 10 | import torch.nn.functional as F 11 | import torch.optim as optim 12 | import sys 13 | sys.path.append("..") 14 | from utils.utils import MyDataset, validate, show_confMat 15 | from datetime import datetime 16 | 17 | train_txt_path = os.path.join("..", "..", "Data", "train.txt") 18 | valid_txt_path = os.path.join("..", "..", "Data", "valid.txt") 19 | 20 | classes_name = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] 21 | 22 | train_bs = 16 23 | valid_bs = 16 24 | lr_init = 0.001 25 | max_epoch = 1 26 | 27 | # log 28 | result_dir = os.path.join("..", "..", "Result") 29 | 30 | now_time = datetime.now() 31 | time_str = datetime.strftime(now_time, '%m-%d_%H-%M-%S') 32 | 33 | log_dir = os.path.join(result_dir, time_str) 34 | if not os.path.exists(log_dir): 35 | os.makedirs(log_dir) 36 | 37 | # -------------------------------------------- step 1/5 : 加载数据 ------------------------------------------- 38 | 39 | # 数据预处理设置 40 | normMean = [0.4948052, 0.48568845, 0.44682974] 41 | normStd = [0.24580306, 0.24236229, 0.2603115] 42 | normTransform = transforms.Normalize(normMean, normStd) 43 | trainTransform = transforms.Compose([ 44 | transforms.Resize(32), 45 | transforms.RandomCrop(32, padding=4), 46 | transforms.ToTensor(), 47 | normTransform 48 | ]) 49 | 50 | validTransform = transforms.Compose([ 51 | transforms.ToTensor(), 52 | normTransform 53 | ]) 54 | 55 | # 构建MyDataset实例 56 | train_data = MyDataset(txt_path=train_txt_path, transform=trainTransform) 57 | valid_data = MyDataset(txt_path=valid_txt_path, transform=validTransform) 58 | 59 | # 构建DataLoder 60 | train_loader = DataLoader(dataset=train_data, batch_size=train_bs, shuffle=True) 61 | valid_loader = DataLoader(dataset=valid_data, batch_size=valid_bs) 62 | 63 | # ------------------------------------ step 2/5 : 定义网络 ------------------------------------ 64 | 65 | 66 | class Net(nn.Module): 67 | def __init__(self): 68 | super(Net, self).__init__() 69 | self.conv1 = nn.Conv2d(3, 6, 5) 70 | self.pool1 = nn.MaxPool2d(2, 2) 71 | self.conv2 = nn.Conv2d(6, 16, 5) 72 | self.pool2 = nn.MaxPool2d(2, 2) 73 | self.fc1 = nn.Linear(16 * 5 * 5, 120) 74 | self.fc2 = nn.Linear(120, 84) 75 | self.fc3 = nn.Linear(84, 10) 76 | 77 | def forward(self, x): 78 | x = self.pool1(F.relu(self.conv1(x))) 79 | x = self.pool2(F.relu(self.conv2(x))) 80 | x = x.view(-1, 16 * 5 * 5) 81 | x = F.relu(self.fc1(x)) 82 | x = F.relu(self.fc2(x)) 83 | x = self.fc3(x) 84 | return x 85 | 86 | # 定义权值初始化 87 | def initialize_weights(self): 88 | for m in self.modules(): 89 | if isinstance(m, nn.Conv2d): 90 | torch.nn.init.xavier_normal_(m.weight.data) 91 | if m.bias is not None: 92 | m.bias.data.zero_() 93 | elif isinstance(m, nn.BatchNorm2d): 94 | m.weight.data.fill_(1) 95 | m.bias.data.zero_() 96 | elif isinstance(m, nn.Linear): 97 | torch.nn.init.normal_(m.weight.data, 0, 0.01) 98 | m.bias.data.zero_() 99 | 100 | 101 | net = Net() # 创建一个网络 102 | 103 | # ================================ # 104 | # finetune 权值初始化 105 | # ================================ # 106 | 107 | # load params 108 | pretrained_dict = torch.load('net_params.pkl') 109 | 110 | # 获取当前网络的dict 111 | net_state_dict = net.state_dict() 112 | 113 | # 剔除不匹配的权值参数 114 | pretrained_dict_1 = {k: v for k, v in pretrained_dict.items() if k in net_state_dict} 115 | 116 | # 更新新模型参数字典 117 | net_state_dict.update(pretrained_dict_1) 118 | 119 | # 将包含预训练模型参数的字典"放"到新模型中 120 | net.load_state_dict(net_state_dict) 121 | 122 | # ------------------------------------ step 3/5 : 定义损失函数和优化器 ------------------------------------ 123 | # ================================= # 124 | # 按需设置学习率 125 | # ================================= # 126 | 127 | # 将fc3层的参数从原始网络参数中剔除 128 | ignored_params = list(map(id, net.fc3.parameters())) 129 | base_params = filter(lambda p: id(p) not in ignored_params, net.parameters()) 130 | 131 | # 为fc3层设置需要的学习率 132 | optimizer = optim.SGD([ 133 | {'params': base_params}, 134 | {'params': net.fc3.parameters(), 'lr': lr_init*10}], lr_init, momentum=0.9, weight_decay=1e-4) 135 | 136 | criterion = nn.CrossEntropyLoss() # 选择损失函数 137 | scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1) # 设置学习率下降策略 138 | 139 | # ------------------------------------ step 4/5 : 训练 -------------------------------------------------- 140 | 141 | for epoch in range(max_epoch): 142 | 143 | loss_sigma = 0.0 # 记录一个epoch的loss之和 144 | correct = 0.0 145 | total = 0.0 146 | scheduler.step() # 更新学习率 147 | 148 | for i, data in enumerate(train_loader): 149 | # 获取图片和标签 150 | inputs, labels = data 151 | inputs, labels = Variable(inputs), Variable(labels) 152 | 153 | # forward, backward, update weights 154 | optimizer.zero_grad() 155 | outputs = net(inputs) 156 | loss = criterion(outputs, labels) 157 | loss.backward() 158 | optimizer.step() 159 | 160 | # 统计预测信息 161 | _, predicted = torch.max(outputs.data, 1) 162 | total += labels.size(0) 163 | correct += (predicted == labels).squeeze().sum().numpy() 164 | loss_sigma += loss.item() 165 | 166 | # 每10个iteration 打印一次训练信息,loss为10个iteration的平均 167 | if i % 10 == 9: 168 | loss_avg = loss_sigma / 10 169 | loss_sigma = 0.0 170 | print("Training: Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format( 171 | epoch + 1, max_epoch, i + 1, len(train_loader), loss_avg, correct / total)) 172 | print('参数组1的学习率:{}, 参数组2的学习率:{}'.format(scheduler.get_lr()[0], scheduler.get_lr()[1])) 173 | # ------------------------------------ 观察模型在验证集上的表现 ------------------------------------ 174 | loss_sigma = 0.0 175 | cls_num = len(classes_name) 176 | conf_mat = np.zeros([cls_num, cls_num]) # 混淆矩阵 177 | net.eval() 178 | for i, data in enumerate(valid_loader): 179 | 180 | # 获取图片和标签 181 | images, labels = data 182 | images, labels = Variable(images), Variable(labels) 183 | 184 | # forward 185 | outputs = net(images) 186 | outputs.detach_() 187 | 188 | # 计算loss 189 | loss = criterion(outputs, labels) 190 | loss_sigma += loss.item() 191 | 192 | # 统计 193 | _, predicted = torch.max(outputs.data, 1) 194 | # labels = labels.data # Variable --> tensor 195 | 196 | # 统计混淆矩阵 197 | for j in range(len(labels)): 198 | cate_i = labels[j].numpy() 199 | pre_i = predicted[j].numpy() 200 | conf_mat[cate_i, pre_i] += 1.0 201 | 202 | print('{} set Accuracy:{:.2%}'.format('Valid', conf_mat.trace() / conf_mat.sum())) 203 | print('Finished Training') 204 | 205 | # ------------------------------------ step5: 绘制混淆矩阵图 ------------------------------------ 206 | 207 | conf_mat_train, train_acc = validate(net, train_loader, 'train', classes_name) 208 | conf_mat_valid, valid_acc = validate(net, valid_loader, 'valid', classes_name) 209 | 210 | show_confMat(conf_mat_train, classes_name, 'train', log_dir) 211 | show_confMat(conf_mat_valid, classes_name, 'valid', log_dir) -------------------------------------------------------------------------------- /Code/2_model/net_params.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Code/2_model/net_params.pkl -------------------------------------------------------------------------------- /Code/3_optimizer/3_1_lossFunction/1_L1Loss.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | 6 | # ----------------------------------- L1 Loss 7 | 8 | # 生成网络输出 以及 目标输出 9 | output = torch.ones(2, 2, requires_grad=True)*0.5 10 | target = torch.ones(2, 2) 11 | 12 | # 设置三种不同参数的L1Loss 13 | reduce_False = nn.L1Loss(size_average=True, reduce=False) 14 | size_average_True = nn.L1Loss(size_average=True, reduce=True) 15 | size_average_False = nn.L1Loss(size_average=False, reduce=True) 16 | 17 | o_0 = reduce_False(output, target) 18 | o_1 = size_average_True(output, target) 19 | o_2 = size_average_False(output, target) 20 | 21 | print('\nreduce=False, 输出同维度的loss:\n{}\n'.format(o_0)) 22 | print('size_average=True,\t求平均:\t{}'.format(o_1)) 23 | print('size_average=False,\t求和:\t{}'.format(o_2)) 24 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_1_lossFunction/2_MSELoss.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | 6 | # ----------------------------------- MSE loss 7 | 8 | # 生成网络输出 以及 目标输出 9 | output = torch.ones(2, 2, requires_grad=True) * 0.5 10 | target = torch.ones(2, 2) 11 | 12 | # 设置三种不同参数的L1Loss 13 | reduce_False = nn.MSELoss(size_average=True, reduce=False) 14 | size_average_True = nn.MSELoss(size_average=True, reduce=True) 15 | size_average_False = nn.MSELoss(size_average=False, reduce=True) 16 | 17 | 18 | o_0 = reduce_False(output, target) 19 | o_1 = size_average_True(output, target) 20 | o_2 = size_average_False(output, target) 21 | 22 | print('\nreduce=False, 输出同维度的loss:\n{}\n'.format(o_0)) 23 | print('size_average=True,\t求平均:\t{}'.format(o_1)) 24 | print('size_average=False,\t求和:\t{}'.format(o_2)) 25 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_1_lossFunction/3_CrossEntropyLoss.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | import numpy as np 6 | import math 7 | 8 | # ----------------------------------- CrossEntropy loss: base 9 | 10 | loss_f = nn.CrossEntropyLoss(weight=None, size_average=True, reduce=False) 11 | # 生成网络输出 以及 目标输出 12 | output = torch.ones(2, 3, requires_grad=True) * 0.5 # 假设一个三分类任务,batchsize=2,假设每个神经元输出都为0.5 13 | target = torch.from_numpy(np.array([0, 1])).type(torch.LongTensor) 14 | 15 | loss = loss_f(output, target) 16 | 17 | print('--------------------------------------------------- CrossEntropy loss: base') 18 | print('loss: ', loss) 19 | print('由于reduce=False,所以可以看到每一个样本的loss,输出为[1.0986, 1.0986]') 20 | 21 | 22 | # 熟悉计算公式,手动计算第一个样本 23 | output = output[0].detach().numpy() 24 | output_1 = output[0] # 第一个样本的输出值 25 | target_1 = target[0].numpy() 26 | 27 | # 第一项 28 | x_class = output[target_1] 29 | # 第二项 30 | exp = math.e 31 | sigma_exp_x = pow(exp, output[0]) + pow(exp, output[1]) + pow(exp, output[2]) 32 | log_sigma_exp_x = math.log(sigma_exp_x) 33 | # 两项相加 34 | loss_1 = -x_class + log_sigma_exp_x 35 | print('--------------------------------------------------- 手动计算') 36 | print('第一个样本的loss:', loss_1) 37 | 38 | 39 | # ----------------------------------- CrossEntropy loss: weight 40 | 41 | weight = torch.from_numpy(np.array([0.6, 0.2, 0.2])).float() 42 | loss_f = nn.CrossEntropyLoss(weight=weight, size_average=True, reduce=False) 43 | output = torch.ones(2, 3, requires_grad=True) * 0.5 # 假设一个三分类任务,batchsize为2个,假设每个神经元输出都为0.5 44 | target = torch.from_numpy(np.array([0, 1])).type(torch.LongTensor) 45 | loss = loss_f(output, target) 46 | print('\n\n--------------------------------------------------- CrossEntropy loss: weight') 47 | print('loss: ', loss) # 48 | print('原始loss值为1.0986, 第一个样本是第0类,weight=0.6,所以输出为1.0986*0.6 =', 1.0986*0.6) 49 | 50 | # ----------------------------------- CrossEntropy loss: ignore_index 51 | 52 | loss_f_1 = nn.CrossEntropyLoss(weight=None, size_average=False, reduce=False, ignore_index=1) 53 | loss_f_2 = nn.CrossEntropyLoss(weight=None, size_average=False, reduce=False, ignore_index=2) 54 | 55 | output = torch.ones(3, 3, requires_grad=True) * 0.5 # 假设一个三分类任务,batchsize为2个,假设每个神经元输出都为0.5 56 | target = torch.from_numpy(np.array([0, 1, 2])).type(torch.LongTensor) 57 | 58 | loss_1 = loss_f_1(output, target) 59 | loss_2 = loss_f_2(output, target) 60 | 61 | print('\n\n--------------------------------------------------- CrossEntropy loss: ignore_index') 62 | print('ignore_index = 1: ', loss_1) # 类别为1的样本的loss为0 63 | print('ignore_index = 2: ', loss_2) # 类别为2的样本的loss为0 64 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_1_lossFunction/4_NLLLoss.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | import numpy as np 6 | 7 | # ----------------------------------- log likelihood loss 8 | 9 | # 各类别权重 10 | weight = torch.from_numpy(np.array([0.6, 0.2, 0.2])).float() 11 | 12 | # 生成网络输出 以及 目标输出 13 | output = torch.from_numpy(np.array([[0.7, 0.2, 0.1], [0.4, 1.2, 0.4]])).float() 14 | output.requires_grad = True 15 | target = torch.from_numpy(np.array([0, 0])).type(torch.LongTensor) 16 | 17 | 18 | loss_f = nn.NLLLoss(weight=weight, size_average=True, reduce=False) 19 | loss = loss_f(output, target) 20 | 21 | print('\nloss: \n', loss) 22 | print('\n第一个样本是0类,loss = -(0.6*0.7)={}'.format(loss[0])) 23 | print('\n第二个样本是0类,loss = -(0.6*0.4)={}'.format(loss[1])) -------------------------------------------------------------------------------- /Code/3_optimizer/3_1_lossFunction/5_PoissonNLLLoss.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | import numpy as np 6 | 7 | # ----------------------------------- Poisson NLLLoss 8 | 9 | # 生成网络输出 以及 目标输出 10 | log_input = torch.randn(5, 2, requires_grad=True) 11 | target = torch.randn(5, 2) 12 | 13 | loss_f = nn.PoissonNLLLoss() 14 | loss = loss_f(log_input, target) 15 | print('\nloss: \n', loss) 16 | 17 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_1_lossFunction/6_KLDivLoss.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | import numpy as np 6 | 7 | # ----------------------------------- KLDiv loss 8 | 9 | loss_f = nn.KLDivLoss(size_average=False, reduce=False) 10 | loss_f_mean = nn.KLDivLoss(size_average=True, reduce=True) 11 | 12 | # 生成网络输出 以及 目标输出 13 | output = torch.from_numpy(np.array([[0.1132, 0.5477, 0.3390]])).float() 14 | output.requires_grad = True 15 | target = torch.from_numpy(np.array([[0.8541, 0.0511, 0.0947]])).float() 16 | 17 | loss_1 = loss_f(output, target) 18 | loss_mean = loss_f_mean(output, target) 19 | 20 | print('\nloss: ', loss_1) 21 | print('\nloss_mean: ', loss_mean) 22 | 23 | 24 | # 熟悉计算公式,手动计算样本的第一个元素的loss,注意这里只有一个样本,是 element-wise计算的 25 | 26 | output = output[0].detach().numpy() 27 | output_1 = output[0] # 第一个样本的第一个元素 28 | target_1 = target[0][0].numpy() 29 | 30 | loss_1 = target_1 * (np.log(target_1) - output_1) 31 | 32 | print('\n第一个样本第一个元素的loss:', loss_1) 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_2_optimizer/1_param_groups.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.optim as optim 5 | 6 | 7 | w1 = torch.randn(2, 2) 8 | w1.requires_grad = True 9 | 10 | w2 = torch.randn(2, 2) 11 | w2.requires_grad = True 12 | 13 | w3 = torch.randn(2, 2) 14 | w3.requires_grad = True 15 | 16 | # 一个参数组 17 | optimizer_1 = optim.SGD([w1, w3], lr=0.1) 18 | print('len(optimizer.param_groups): ', len(optimizer_1.param_groups)) 19 | print(optimizer_1.param_groups, '\n') 20 | 21 | # 两个参数组 22 | optimizer_2 = optim.SGD([{'params': w1, 'lr': 0.1}, 23 | {'params': w2, 'lr': 0.001}]) 24 | print('len(optimizer.param_groups): ', len(optimizer_2.param_groups)) 25 | print(optimizer_2.param_groups) 26 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_2_optimizer/2_zero_grad.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.optim as optim 5 | 6 | # ----------------------------------- zero_grad 7 | 8 | w1 = torch.randn(2, 2) 9 | w1.requires_grad = True 10 | 11 | w2 = torch.randn(2, 2) 12 | w2.requires_grad = True 13 | 14 | optimizer = optim.SGD([w1, w2], lr=0.001, momentum=0.9) 15 | 16 | optimizer.param_groups[0]['params'][0].grad = torch.randn(2, 2) 17 | 18 | print('参数w1的梯度:') 19 | print(optimizer.param_groups[0]['params'][0].grad, '\n') # 参数组,第一个参数(w1)的梯度 20 | 21 | optimizer.zero_grad() 22 | print('执行zero_grad()之后,参数w1的梯度:') 23 | print(optimizer.param_groups[0]['params'][0].grad) # 参数组,第一个参数(w1)的梯度 24 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_2_optimizer/3_state_dict.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | 6 | 7 | # ----------------------------------- state_dict 8 | class Net(nn.Module): 9 | def __init__(self): 10 | super(Net, self).__init__() 11 | self.conv1 = nn.Conv2d(3, 1, 3) 12 | self.pool = nn.MaxPool2d(2, 2) 13 | self.fc1 = nn.Linear(1 * 3 * 3, 2) 14 | 15 | def forward(self, x): 16 | x = self.pool(F.relu(self.conv1(x))) 17 | x = x.view(-1, 1 * 3 * 3) 18 | x = F.relu(self.fc1(x)) 19 | return x 20 | 21 | 22 | net = Net() 23 | 24 | # 获取网络当前参数 25 | net_state_dict = net.state_dict() 26 | 27 | print('net_state_dict类型:', type(net_state_dict)) 28 | print('net_state_dict管理的参数: ', net_state_dict.keys()) 29 | for key, value in net_state_dict.items(): 30 | print('参数名: ', key, '\t大小: ', value.shape) 31 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_2_optimizer/4_load_state_dict.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.nn as nn 5 | import torch.nn.functional as F 6 | 7 | 8 | # ----------------------------------- load_state_dict 9 | 10 | class Net(nn.Module): 11 | def __init__(self): 12 | super(Net, self).__init__() 13 | self.conv1 = nn.Conv2d(3, 1, 3) 14 | self.pool = nn.MaxPool2d(2, 2) 15 | self.fc1 = nn.Linear(1 * 3 * 3, 2) 16 | 17 | def forward(self, x): 18 | x = self.pool(F.relu(self.conv1(x))) 19 | x = x.view(-1, 1 * 3 * 3) 20 | x = F.relu(self.fc1(x)) 21 | return x 22 | 23 | def zero_param(self): 24 | for m in self.modules(): 25 | if isinstance(m, nn.Conv2d): 26 | torch.nn.init.constant_(m.weight.data, 0) 27 | if m.bias is not None: 28 | m.bias.data.zero_() 29 | elif isinstance(m, nn.Linear): 30 | torch.nn.init.constant_(m.weight.data, 0) 31 | m.bias.data.zero_() 32 | net = Net() 33 | 34 | # 保存,并加载模型参数(仅保存模型参数) 35 | torch.save(net.state_dict(), 'net_params.pkl') # 假设训练好了一个模型net 36 | pretrained_dict = torch.load('net_params.pkl') 37 | 38 | # 将net的参数全部置0,方便对比 39 | net.zero_param() 40 | net_state_dict = net.state_dict() 41 | print('conv1层的权值为:\n', net_state_dict['conv1.weight'], '\n') 42 | 43 | # 通过load_state_dict 加载参数 44 | net.load_state_dict(pretrained_dict) 45 | print('加载之后,conv1层的权值变为:\n', net_state_dict['conv1.weight']) 46 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_2_optimizer/5_add_param_group.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | import torch.optim as optim 5 | 6 | # ----------------------------------- add_param_group 7 | 8 | w1 = torch.randn(2, 2) 9 | w1.requires_grad = True 10 | 11 | w2 = torch.randn(2, 2) 12 | w2.requires_grad = True 13 | 14 | w3 = torch.randn(2, 2) 15 | w3.requires_grad = True 16 | 17 | # 一个参数组 18 | optimizer_1 = optim.SGD([w1, w2], lr=0.1) 19 | print('当前参数组个数: ', len(optimizer_1.param_groups)) 20 | print(optimizer_1.param_groups, '\n') 21 | 22 | # 增加一个参数组 23 | print('增加一组参数 w3\n') 24 | optimizer_1.add_param_group({'params': w3, 'lr': 0.001, 'momentum': 0.8}) 25 | 26 | print('当前参数组个数: ', len(optimizer_1.param_groups)) 27 | print(optimizer_1.param_groups, '\n') 28 | 29 | print('可以看到,参数组是一个list,一个元素是一个dict,每个dict中都有lr, momentum等参数,这些都是可单独管理,单独设定,十分灵活!') 30 | 31 | -------------------------------------------------------------------------------- /Code/3_optimizer/3_2_optimizer/net_params.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Code/3_optimizer/3_2_optimizer/net_params.pkl -------------------------------------------------------------------------------- /Code/4_viewer/1_tensorboardX_demo.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import os 3 | import torch 4 | import torchvision.utils as vutils 5 | import numpy as np 6 | import torchvision.models as models 7 | from torchvision import datasets 8 | from tensorboardX import SummaryWriter 9 | 10 | resnet18 = models.resnet18(False) 11 | writer = SummaryWriter(os.path.join("..", "..", "Result", "runs")) 12 | sample_rate = 44100 13 | freqs = [262, 294, 330, 349, 392, 440, 440, 440, 440, 440, 440] 14 | 15 | true_positive_counts = [75, 64, 21, 5, 0] 16 | false_positive_counts = [150, 105, 18, 0, 0] 17 | true_negative_counts = [0, 45, 132, 150, 150] 18 | false_negative_counts = [0, 11, 54, 70, 75] 19 | precision = [0.3333333, 0.3786982, 0.5384616, 1.0, 0.0] 20 | recall = [1.0, 0.8533334, 0.28, 0.0666667, 0.0] 21 | 22 | 23 | for n_iter in range(100): 24 | s1 = torch.rand(1) # value to keep 25 | s2 = torch.rand(1) 26 | # data grouping by `slash` 27 | writer.add_scalar(os.path.join("data", "scalar_systemtime"), s1[0], n_iter) 28 | # data grouping by `slash` 29 | writer.add_scalar(os.path.join("data", "scalar_customtime"), s1[0], n_iter, walltime=n_iter) 30 | writer.add_scalars(os.path.join("data", "scalar_group"), {"xsinx": n_iter * np.sin(n_iter), 31 | "xcosx": n_iter * np.cos(n_iter), 32 | "arctanx": np.arctan(n_iter)}, n_iter) 33 | x = torch.rand(32, 3, 64, 64) # output from network 34 | if n_iter % 10 == 0: 35 | x = vutils.make_grid(x, normalize=True, scale_each=True) 36 | writer.add_image('Image', x, n_iter) # Tensor 37 | # writer.add_image('astronaut', skimage.data.astronaut(), n_iter) # numpy 38 | # writer.add_image('imread', 39 | # skimage.io.imread('screenshots/audio.png'), n_iter) # numpy 40 | x = torch.zeros(sample_rate * 2) 41 | for i in range(x.size(0)): 42 | # sound amplitude should in [-1, 1] 43 | x[i] = np.cos(freqs[n_iter // 10] * np.pi * 44 | float(i) / float(sample_rate)) 45 | writer.add_audio('myAudio', x, n_iter) 46 | writer.add_text('Text', 'text logged at step:' + str(n_iter), n_iter) 47 | writer.add_text('markdown Text', '''a|b\n-|-\nc|d''', n_iter) 48 | for name, param in resnet18.named_parameters(): 49 | if 'bn' not in name: 50 | writer.add_histogram(name, param, n_iter) 51 | writer.add_pr_curve('xoxo', np.random.randint(2, size=100), np.random.rand( 52 | 100), n_iter) # needs tensorboard 0.4RC or later 53 | writer.add_pr_curve_raw('prcurve with raw data', true_positive_counts, 54 | false_positive_counts, 55 | true_negative_counts, 56 | false_negative_counts, 57 | precision, 58 | recall, n_iter) 59 | # export scalar data to JSON for external processing 60 | writer.export_scalars_to_json(os.path.join("..", "..", "Result", "all_scalars.json")) 61 | 62 | dataset = datasets.MNIST(os.path.join("..", "..", "Data", "mnist"), train=False, download=True) 63 | images = dataset.test_data[:100].float() 64 | label = dataset.test_labels[:100] 65 | features = images.view(100, 784) 66 | writer.add_embedding(features, metadata=label, label_img=images.unsqueeze(1)) 67 | writer.add_embedding(features, global_step=1, tag='noMetadata') 68 | dataset = datasets.MNIST(os.path.join("..", "..", "Data", "mnist"), train=True, download=True) 69 | images_train = dataset.train_data[:100].float() 70 | labels_train = dataset.train_labels[:100] 71 | features_train = images_train.view(100, 784) 72 | 73 | all_features = torch.cat((features, features_train)) 74 | all_labels = torch.cat((label, labels_train)) 75 | all_images = torch.cat((images, images_train)) 76 | dataset_label = ['test'] * 100 + ['train'] * 100 77 | all_labels = list(zip(all_labels, dataset_label)) 78 | 79 | writer.add_embedding(all_features, metadata=all_labels, label_img=all_images.unsqueeze(1), 80 | metadata_header=['digit', 'dataset'], global_step=2) 81 | 82 | # VIDEO 83 | vid_images = dataset.train_data[:16 * 48] 84 | vid = vid_images.view(16, 1, 48, 28, 28) # BxCxTxHxW 85 | 86 | writer.add_video('video', vid_tensor=vid) 87 | writer.add_video('video_1_fps', vid_tensor=vid, fps=1) 88 | 89 | writer.close() -------------------------------------------------------------------------------- /Code/4_viewer/2_visual_weights.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import os 3 | import torch 4 | import torchvision.utils as vutils 5 | from tensorboardX import SummaryWriter 6 | import torch.nn as nn 7 | import torch.nn.functional as F 8 | 9 | 10 | class Net(nn.Module): 11 | def __init__(self): 12 | super(Net, self).__init__() 13 | self.conv1 = nn.Conv2d(3, 6, 5) 14 | self.pool1 = nn.MaxPool2d(2, 2) 15 | self.conv2 = nn.Conv2d(6, 16, 5) 16 | self.pool2 = nn.MaxPool2d(2, 2) 17 | self.fc1 = nn.Linear(16 * 5 * 5, 120) 18 | self.fc2 = nn.Linear(120, 84) 19 | self.fc3 = nn.Linear(84, 10) 20 | 21 | def forward(self, x): 22 | x = self.pool1(F.relu(self.conv1(x))) 23 | x = self.pool2(F.relu(self.conv2(x))) 24 | x = x.view(-1, 16 * 5 * 5) 25 | x = F.relu(self.fc1(x)) 26 | x = F.relu(self.fc2(x)) 27 | x = self.fc3(x) 28 | return x 29 | 30 | # 定义权值初始化 31 | def initialize_weights(self): 32 | for m in self.modules(): 33 | if isinstance(m, nn.Conv2d): 34 | torch.nn.init.xavier_normal_(m.weight.data) 35 | if m.bias is not None: 36 | m.bias.data.zero_() 37 | elif isinstance(m, nn.BatchNorm2d): 38 | m.weight.data.fill_(1) 39 | m.bias.data.zero_() 40 | elif isinstance(m, nn.Linear): 41 | torch.nn.init.normal_(m.weight.data, 0, 0.01) 42 | m.bias.data.zero_() 43 | 44 | 45 | net = Net() # 创建一个网络 46 | pretrained_dict = torch.load(os.path.join("..", "2_model", "net_params.pkl")) 47 | net.load_state_dict(pretrained_dict) 48 | 49 | writer = SummaryWriter(log_dir=os.path.join("..", "..", "Result", "visual_weights")) 50 | params = net.state_dict() 51 | for k, v in params.items(): 52 | if 'conv' in k and 'weight' in k: 53 | 54 | c_int = v.size()[1] # 输入层通道数 55 | c_out = v.size()[0] # 输出层通道数 56 | 57 | # 以feature map为单位,绘制一组卷积核,一张feature map对应的卷积核个数为输入通道数 58 | for j in range(c_out): 59 | print(k, v.size(), j) 60 | kernel_j = v[j, :, :, :].unsqueeze(1) # 压缩维度,为make_grid制作输入 61 | kernel_grid = vutils.make_grid(kernel_j, normalize=True, scale_each=True, nrow=c_int) # 1*输入通道数, w, h 62 | writer.add_image(k+'_split_in_channel', kernel_grid, global_step=j) # j 表示feature map数 63 | 64 | # 将一个卷积层的卷积核绘制在一起,每一行是一个feature map的卷积核 65 | k_w, k_h = v.size()[-1], v.size()[-2] 66 | kernel_all = v.view(-1, 1, k_w, k_h) 67 | kernel_grid = vutils.make_grid(kernel_all, normalize=True, scale_each=True, nrow=c_int) # 1*输入通道数, w, h 68 | writer.add_image(k + '_all', kernel_grid, global_step=666) 69 | writer.close() -------------------------------------------------------------------------------- /Code/4_viewer/3_visual_featuremaps.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import os 3 | import torch 4 | import torchvision.utils as vutils 5 | import numpy as np 6 | from tensorboardX import SummaryWriter 7 | import torch.nn.functional as F 8 | import torchvision.transforms as transforms 9 | import sys 10 | sys.path.append("..") 11 | from utils.utils import MyDataset, Net, normalize_invert 12 | from torch.utils.data import DataLoader 13 | 14 | 15 | vis_layer = 'conv1' 16 | log_dir = os.path.join("..", "..", "Result", "visual_featuremaps") 17 | txt_path = os.path.join("..", "..", "Data", "visual.txt") 18 | pretrained_path = os.path.join("..", "..", "Data", "net_params_72p.pkl") 19 | 20 | net = Net() 21 | pretrained_dict = torch.load(pretrained_path) 22 | net.load_state_dict(pretrained_dict) 23 | 24 | # 数据预处理 25 | normMean = [0.49139968, 0.48215827, 0.44653124] 26 | normStd = [0.24703233, 0.24348505, 0.26158768] 27 | normTransform = transforms.Normalize(normMean, normStd) 28 | testTransform = transforms.Compose([ 29 | transforms.Resize((32, 32)), 30 | transforms.ToTensor(), 31 | normTransform 32 | ]) 33 | # 载入数据 34 | test_data = MyDataset(txt_path=txt_path, transform=testTransform) 35 | test_loader = DataLoader(dataset=test_data, batch_size=1) 36 | img, label = iter(test_loader).next() 37 | 38 | x = img 39 | writer = SummaryWriter(log_dir=log_dir) 40 | for name, layer in net._modules.items(): 41 | 42 | # 为fc层预处理x 43 | x = x.view(x.size(0), -1) if "fc" in name else x 44 | 45 | # 对x执行单层运算 46 | x = layer(x) 47 | print(x.size()) 48 | 49 | # 由于__init__()相较于forward()缺少relu操作,需要手动增加 50 | x = F.relu(x) if 'conv' in name else x 51 | 52 | # 依据选择的层,进行记录feature maps 53 | if name == vis_layer: 54 | # 绘制feature maps 55 | x1 = x.transpose(0, 1) # C,B, H, W ---> B,C, H, W 56 | img_grid = vutils.make_grid(x1, normalize=True, scale_each=True, nrow=2) # B,C, H, W 57 | writer.add_image(vis_layer + '_feature_maps', img_grid, global_step=666) 58 | 59 | # 绘制原始图像 60 | img_raw = normalize_invert(img, normMean, normStd) # 图像去标准化 61 | img_raw = np.array(img_raw * 255).clip(0, 255).squeeze().astype('uint8') 62 | writer.add_image('raw img', img_raw, global_step=666) # j 表示feature map数 63 | writer.close() -------------------------------------------------------------------------------- /Code/4_viewer/4_hist_grad_weight.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | from torch.utils.data import DataLoader 5 | import torchvision.transforms as transforms 6 | import numpy as np 7 | import os 8 | from torch.autograd import Variable 9 | import torch.nn as nn 10 | import torch.optim as optim 11 | import sys 12 | import os 13 | sys.path.append("..") 14 | from utils.utils import MyDataset, validate, show_confMat, Net 15 | from tensorboardX import SummaryWriter 16 | from datetime import datetime 17 | 18 | train_txt_path = os.path.join("..", "..", "Data", "train.txt") 19 | valid_txt_path = os.path.join("..", "..", "Data", "valid.txt") 20 | 21 | classes_name = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] 22 | 23 | train_bs = 16 24 | valid_bs = 16 25 | lr_init = 0.001 26 | max_epoch = 1 27 | 28 | # log 29 | log_dir = os.path.join("..", "..", "Result", "hist_grad_weight") 30 | 31 | writer = SummaryWriter(log_dir=log_dir) 32 | 33 | # ------------------------------------ step 1/4 : 加载数据------------------------------------------------- 34 | 35 | # 数据预处理设置 36 | normMean = [0.4948052, 0.48568845, 0.44682974] 37 | normStd = [0.24580306, 0.24236229, 0.2603115] 38 | normTransform = transforms.Normalize(normMean, normStd) 39 | trainTransform = transforms.Compose([ 40 | transforms.Resize(32), 41 | transforms.RandomCrop(32, padding=4), 42 | transforms.ToTensor(), 43 | normTransform 44 | ]) 45 | 46 | validTransform = transforms.Compose([ 47 | transforms.ToTensor(), 48 | normTransform 49 | ]) 50 | 51 | # 构建MyDataset实例 52 | train_data = MyDataset(txt_path=train_txt_path, transform=trainTransform) 53 | valid_data = MyDataset(txt_path=valid_txt_path, transform=validTransform) 54 | 55 | # 构建DataLoder 56 | train_loader = DataLoader(dataset=train_data, batch_size=train_bs, shuffle=True) 57 | valid_loader = DataLoader(dataset=valid_data, batch_size=valid_bs) 58 | 59 | # ------------------------------------ step 2/4 : 网络初始化---------------------------------------------- 60 | 61 | net = Net() # 创建一个网络 62 | net.initialize_weights() # 初始化权值 63 | 64 | # ------------------------------------ step 3/4 : 定义损失函数和优化器 ------------------------------------ 65 | 66 | criterion = nn.CrossEntropyLoss() # 选择损失函数 67 | optimizer = optim.SGD(net.parameters(), lr=lr_init, momentum=0.9, dampening=0.1) # 选择优化器 68 | scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1) # 设置学习率下降策略 69 | 70 | # ------------------------------------ step 4/4 : 训练 -------------------------------------------------- 71 | 72 | for epoch in range(max_epoch): 73 | 74 | loss_sigma = 0.0 # 记录一个epoch的loss之和 75 | correct = 0.0 76 | total = 0.0 77 | scheduler.step() # 更新学习率 78 | 79 | for i, data in enumerate(train_loader): 80 | # 获取图片和标签 81 | inputs, labels = data 82 | inputs, labels = Variable(inputs), Variable(labels) 83 | 84 | # forward, backward, update weights 85 | optimizer.zero_grad() 86 | outputs = net(inputs) 87 | loss = criterion(outputs, labels) 88 | loss.backward() 89 | optimizer.step() 90 | 91 | # 统计预测信息 92 | _, predicted = torch.max(outputs.data, 1) 93 | total += labels.size(0) 94 | correct += (predicted == labels).squeeze().sum().numpy() 95 | loss_sigma += loss.item() 96 | 97 | # 每10个iteration 打印一次训练信息,loss为10个iteration的平均 98 | if i % 10 == 9: 99 | loss_avg = loss_sigma / 10 100 | loss_sigma = 0.0 101 | print("Training: Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format( 102 | epoch + 1, max_epoch, i + 1, len(train_loader), loss_avg, correct / total)) 103 | 104 | # 每个epoch,记录梯度,权值 105 | for name, layer in net.named_parameters(): 106 | writer.add_histogram(name + '_grad', layer.grad.cpu().data.numpy(), epoch) 107 | writer.add_histogram(name + '_data', layer.cpu().data.numpy(), epoch) 108 | 109 | print('Finished Training') 110 | -------------------------------------------------------------------------------- /Code/4_viewer/5_Show_ConfMat.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import numpy as np 3 | import os 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | 8 | def show_confMat(confusion_mat, classes_name, set_name, out_dir): 9 | """ 10 | 可视化混淆矩阵,保存png格式 11 | :param confusion_mat: nd-array 12 | :param classes_name: list,各类别名称 13 | :param set_name: str, eg: 'valid', 'train' 14 | :param out_dir: str, png输出的文件夹 15 | :return: 16 | """ 17 | # 归一化 18 | confusion_mat_N = confusion_mat.copy() 19 | for i in range(len(classes_name)): 20 | confusion_mat_N[i, :] = confusion_mat[i, :] / confusion_mat[i, :].sum() 21 | 22 | # 获取颜色 23 | cmap = plt.cm.get_cmap('Greys') # 更多颜色: http://matplotlib.org/examples/color/colormaps_reference.html 24 | plt.imshow(confusion_mat_N, cmap=cmap) 25 | plt.colorbar() 26 | 27 | # 设置文字 28 | xlocations = np.array(range(len(classes_name))) 29 | plt.xticks(xlocations, classes_name, rotation=60) 30 | plt.yticks(xlocations, classes_name) 31 | plt.xlabel('Predict label') 32 | plt.ylabel('True label') 33 | plt.title('Confusion_Matrix_' + set_name) 34 | 35 | # 打印数字 36 | for i in range(confusion_mat_N.shape[0]): 37 | for j in range(confusion_mat_N.shape[1]): 38 | plt.text(x=j, y=i, s=int(confusion_mat[i, j]), va='center', ha='center', color='red', fontsize=10) 39 | # 保存 40 | plt.savefig(os.path.join(out_dir, 'Confusion_Matrix_' + set_name + '.png')) 41 | plt.close() 42 | 43 | if __name__ == '__main__': 44 | 45 | print('QQ group: {} or {} or {} or {}, password: {}'.format(671103375, 773031536, 514974779, 854620826, 2018)) 46 | 47 | -------------------------------------------------------------------------------- /Code/4_viewer/6_hook_for_grad_cam.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """ 3 | 通过实现Grad-CAM学习module中的forward_hook和backward_hook函数 4 | """ 5 | import cv2 6 | import os 7 | import numpy as np 8 | from PIL import Image 9 | import torch 10 | import torch.nn as nn 11 | import torch.nn.functional as F 12 | import torchvision.transforms as transforms 13 | 14 | 15 | class Net(nn.Module): 16 | def __init__(self): 17 | super(Net, self).__init__() 18 | self.conv1 = nn.Conv2d(3, 6, 5) 19 | self.pool1 = nn.MaxPool2d(2, 2) 20 | self.conv2 = nn.Conv2d(6, 16, 5) 21 | self.pool2 = nn.MaxPool2d(2, 2) 22 | self.fc1 = nn.Linear(16 * 5 * 5, 120) 23 | self.fc2 = nn.Linear(120, 84) 24 | self.fc3 = nn.Linear(84, 10) 25 | 26 | def forward(self, x): 27 | x = self.pool1(F.relu(self.conv1(x))) 28 | x = self.pool1(F.relu(self.conv2(x))) 29 | x = x.view(-1, 16 * 5 * 5) 30 | x = F.relu(self.fc1(x)) 31 | x = F.relu(self.fc2(x)) 32 | x = self.fc3(x) 33 | return x 34 | 35 | 36 | def img_transform(img_in, transform): 37 | """ 38 | 将img进行预处理,并转换成模型输入所需的形式—— B*C*H*W 39 | :param img_roi: np.array 40 | :return: 41 | """ 42 | img = img_in.copy() 43 | img = Image.fromarray(np.uint8(img)) 44 | img = transform(img) 45 | img = img.unsqueeze(0) # C*H*W --> B*C*H*W 46 | return img 47 | 48 | 49 | def img_preprocess(img_in): 50 | """ 51 | 读取图片,转为模型可读的形式 52 | :param img_in: ndarray, [H, W, C] 53 | :return: PIL.image 54 | """ 55 | img = img_in.copy() 56 | img = cv2.resize(img,(32, 32)) 57 | img = img[:, :, ::-1] # BGR --> RGB 58 | transform = transforms.Compose([ 59 | transforms.ToTensor(), 60 | transforms.Normalize([0.4948052, 0.48568845, 0.44682974], [0.24580306, 0.24236229, 0.2603115]) 61 | ]) 62 | img_input = img_transform(img, transform) 63 | return img_input 64 | 65 | 66 | def backward_hook(module, grad_in, grad_out): 67 | grad_block.append(grad_out[0].detach()) 68 | 69 | 70 | def farward_hook(module, input, output): 71 | fmap_block.append(output) 72 | 73 | 74 | def show_cam_on_image(img, mask, out_dir): 75 | heatmap = cv2.applyColorMap(np.uint8(255*mask), cv2.COLORMAP_JET) 76 | heatmap = np.float32(heatmap) / 255 77 | cam = heatmap + np.float32(img) 78 | cam = cam / np.max(cam) 79 | 80 | path_cam_img = os.path.join(out_dir, "cam.jpg") 81 | path_raw_img = os.path.join(out_dir, "raw.jpg") 82 | if not os.path.exists(out_dir): 83 | os.makedirs(out_dir) 84 | cv2.imwrite(path_cam_img, np.uint8(255 * cam)) 85 | cv2.imwrite(path_raw_img, np.uint8(255 * img)) 86 | 87 | 88 | def comp_class_vec(ouput_vec, index=None): 89 | """ 90 | 计算类向量 91 | :param ouput_vec: tensor 92 | :param index: int,指定类别 93 | :return: tensor 94 | """ 95 | if not index: 96 | index = np.argmax(ouput_vec.cpu().data.numpy()) 97 | else: 98 | index = np.array(index) 99 | index = index[np.newaxis, np.newaxis] 100 | index = torch.from_numpy(index) 101 | one_hot = torch.zeros(1, 10).scatter_(1, index, 1) 102 | one_hot.requires_grad = True 103 | class_vec = torch.sum(one_hot * output) # one_hot = 11.8605 104 | 105 | return class_vec 106 | 107 | 108 | def gen_cam(feature_map, grads): 109 | """ 110 | 依据梯度和特征图,生成cam 111 | :param feature_map: np.array, in [C, H, W] 112 | :param grads: np.array, in [C, H, W] 113 | :return: np.array, [H, W] 114 | """ 115 | cam = np.zeros(feature_map.shape[1:], dtype=np.float32) # cam shape (H, W) 116 | 117 | weights = np.mean(grads, axis=(1, 2)) # 118 | 119 | for i, w in enumerate(weights): 120 | cam += w * feature_map[i, :, :] 121 | 122 | cam = np.maximum(cam, 0) 123 | cam = cv2.resize(cam, (32, 32)) 124 | cam -= np.min(cam) 125 | cam /= np.max(cam) 126 | 127 | return cam 128 | 129 | 130 | if __name__ == '__main__': 131 | 132 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 133 | path_img = os.path.join(BASE_DIR, "..", "..", "Data", "cam_img", "test_img_8.png") 134 | path_net = os.path.join(BASE_DIR, "..", "..", "Data", "net_params_72p.pkl") 135 | output_dir = os.path.join(BASE_DIR, "..", "..", "Result", "backward_hook_cam") 136 | 137 | classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') 138 | fmap_block = list() 139 | grad_block = list() 140 | 141 | # 图片读取;网络加载 142 | img = cv2.imread(path_img, 1) # H*W*C 143 | img_input = img_preprocess(img) 144 | net = Net() 145 | net.load_state_dict(torch.load(path_net)) 146 | 147 | # 注册hook 148 | net.conv2.register_forward_hook(farward_hook) 149 | net.conv2.register_backward_hook(backward_hook) 150 | 151 | # forward 152 | output = net(img_input) 153 | idx = np.argmax(output.cpu().data.numpy()) 154 | print("predict: {}".format(classes[idx])) 155 | 156 | # backward 157 | net.zero_grad() 158 | class_loss = comp_class_vec(output) 159 | class_loss.backward() 160 | 161 | # 生成cam 162 | grads_val = grad_block[0].cpu().data.numpy().squeeze() 163 | fmap = fmap_block[0].cpu().data.numpy().squeeze() 164 | cam = gen_cam(fmap, grads_val) 165 | 166 | # 保存cam图片 167 | img_show = np.float32(cv2.resize(img, (32, 32))) / 255 168 | show_cam_on_image(img_show, cam, output_dir) 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /Code/main_training/main.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import torch 4 | from torch.utils.data import DataLoader 5 | import torchvision.transforms as transforms 6 | import numpy as np 7 | import os 8 | from torch.autograd import Variable 9 | import torch.nn as nn 10 | import torch.nn.functional as F 11 | import torch.optim as optim 12 | import sys 13 | sys.path.append("..") 14 | from utils.utils import MyDataset, validate, show_confMat 15 | from tensorboardX import SummaryWriter 16 | from datetime import datetime 17 | 18 | train_txt_path = os.path.join("..", "..", "Data", "train.txt") 19 | valid_txt_path = os.path.join("..", "..", "Data", "valid.txt") 20 | 21 | classes_name = ['plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] 22 | 23 | train_bs = 16 24 | valid_bs = 16 25 | lr_init = 0.001 26 | max_epoch = 1 27 | 28 | # log 29 | result_dir = os.path.join("..", "..", "Result") 30 | 31 | now_time = datetime.now() 32 | time_str = datetime.strftime(now_time, '%m-%d_%H-%M-%S') 33 | 34 | log_dir = os.path.join(result_dir, time_str) 35 | if not os.path.exists(log_dir): 36 | os.makedirs(log_dir) 37 | 38 | writer = SummaryWriter(log_dir=log_dir) 39 | 40 | # ------------------------------------ step 1/5 : 加载数据------------------------------------ 41 | 42 | # 数据预处理设置 43 | normMean = [0.4948052, 0.48568845, 0.44682974] 44 | normStd = [0.24580306, 0.24236229, 0.2603115] 45 | normTransform = transforms.Normalize(normMean, normStd) 46 | trainTransform = transforms.Compose([ 47 | transforms.Resize(32), 48 | transforms.RandomCrop(32, padding=4), 49 | transforms.ToTensor(), 50 | normTransform 51 | ]) 52 | 53 | validTransform = transforms.Compose([ 54 | transforms.ToTensor(), 55 | normTransform 56 | ]) 57 | 58 | # 构建MyDataset实例 59 | train_data = MyDataset(txt_path=train_txt_path, transform=trainTransform) 60 | valid_data = MyDataset(txt_path=valid_txt_path, transform=validTransform) 61 | 62 | # 构建DataLoder 63 | train_loader = DataLoader(dataset=train_data, batch_size=train_bs, shuffle=True) 64 | valid_loader = DataLoader(dataset=valid_data, batch_size=valid_bs) 65 | 66 | # ------------------------------------ step 2/5 : 定义网络------------------------------------ 67 | 68 | 69 | class Net(nn.Module): 70 | def __init__(self): 71 | super(Net, self).__init__() 72 | self.conv1 = nn.Conv2d(3, 6, 5) 73 | self.pool1 = nn.MaxPool2d(2, 2) 74 | self.conv2 = nn.Conv2d(6, 16, 5) 75 | self.pool2 = nn.MaxPool2d(2, 2) 76 | self.fc1 = nn.Linear(16 * 5 * 5, 120) 77 | self.fc2 = nn.Linear(120, 84) 78 | self.fc3 = nn.Linear(84, 10) 79 | 80 | def forward(self, x): 81 | x = self.pool1(F.relu(self.conv1(x))) 82 | x = self.pool2(F.relu(self.conv2(x))) 83 | x = x.view(-1, 16 * 5 * 5) 84 | x = F.relu(self.fc1(x)) 85 | x = F.relu(self.fc2(x)) 86 | x = self.fc3(x) 87 | return x 88 | 89 | # 定义权值初始化 90 | def initialize_weights(self): 91 | for m in self.modules(): 92 | if isinstance(m, nn.Conv2d): 93 | torch.nn.init.xavier_normal_(m.weight.data) 94 | if m.bias is not None: 95 | m.bias.data.zero_() 96 | elif isinstance(m, nn.BatchNorm2d): 97 | m.weight.data.fill_(1) 98 | m.bias.data.zero_() 99 | elif isinstance(m, nn.Linear): 100 | torch.nn.init.normal_(m.weight.data, 0, 0.01) 101 | m.bias.data.zero_() 102 | 103 | 104 | net = Net() # 创建一个网络 105 | net.initialize_weights() # 初始化权值 106 | 107 | # ------------------------------------ step 3/5 : 定义损失函数和优化器 ------------------------------------ 108 | 109 | criterion = nn.CrossEntropyLoss() # 选择损失函数 110 | optimizer = optim.SGD(net.parameters(), lr=lr_init, momentum=0.9, dampening=0.1) # 选择优化器 111 | scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.1) # 设置学习率下降策略 112 | 113 | # ------------------------------------ step 4/5 : 训练 -------------------------------------------------- 114 | 115 | for epoch in range(max_epoch): 116 | 117 | loss_sigma = 0.0 # 记录一个epoch的loss之和 118 | correct = 0.0 119 | total = 0.0 120 | scheduler.step() # 更新学习率 121 | 122 | for i, data in enumerate(train_loader): 123 | # if i == 30 : break 124 | # 获取图片和标签 125 | inputs, labels = data 126 | inputs, labels = Variable(inputs), Variable(labels) 127 | 128 | # forward, backward, update weights 129 | optimizer.zero_grad() 130 | outputs = net(inputs) 131 | loss = criterion(outputs, labels) 132 | loss.backward() 133 | optimizer.step() 134 | 135 | # 统计预测信息 136 | _, predicted = torch.max(outputs.data, 1) 137 | total += labels.size(0) 138 | correct += (predicted == labels).squeeze().sum().numpy() 139 | loss_sigma += loss.item() 140 | 141 | # 每10个iteration 打印一次训练信息,loss为10个iteration的平均 142 | if i % 10 == 9: 143 | loss_avg = loss_sigma / 10 144 | loss_sigma = 0.0 145 | print("Training: Epoch[{:0>3}/{:0>3}] Iteration[{:0>3}/{:0>3}] Loss: {:.4f} Acc:{:.2%}".format( 146 | epoch + 1, max_epoch, i + 1, len(train_loader), loss_avg, correct / total)) 147 | 148 | # 记录训练loss 149 | writer.add_scalars('Loss_group', {'train_loss': loss_avg}, epoch) 150 | # 记录learning rate 151 | writer.add_scalar('learning rate', scheduler.get_lr()[0], epoch) 152 | # 记录Accuracy 153 | writer.add_scalars('Accuracy_group', {'train_acc': correct / total}, epoch) 154 | 155 | # 每个epoch,记录梯度,权值 156 | for name, layer in net.named_parameters(): 157 | writer.add_histogram(name + '_grad', layer.grad.cpu().data.numpy(), epoch) 158 | writer.add_histogram(name + '_data', layer.cpu().data.numpy(), epoch) 159 | 160 | # ------------------------------------ 观察模型在验证集上的表现 ------------------------------------ 161 | if epoch % 2 == 0: 162 | loss_sigma = 0.0 163 | cls_num = len(classes_name) 164 | conf_mat = np.zeros([cls_num, cls_num]) # 混淆矩阵 165 | net.eval() 166 | for i, data in enumerate(valid_loader): 167 | 168 | # 获取图片和标签 169 | images, labels = data 170 | images, labels = Variable(images), Variable(labels) 171 | 172 | # forward 173 | outputs = net(images) 174 | outputs.detach_() 175 | 176 | # 计算loss 177 | loss = criterion(outputs, labels) 178 | loss_sigma += loss.item() 179 | 180 | # 统计 181 | _, predicted = torch.max(outputs.data, 1) 182 | # labels = labels.data # Variable --> tensor 183 | 184 | # 统计混淆矩阵 185 | for j in range(len(labels)): 186 | cate_i = labels[j].numpy() 187 | pre_i = predicted[j].numpy() 188 | conf_mat[cate_i, pre_i] += 1.0 189 | 190 | print('{} set Accuracy:{:.2%}'.format('Valid', conf_mat.trace() / conf_mat.sum())) 191 | # 记录Loss, accuracy 192 | writer.add_scalars('Loss_group', {'valid_loss': loss_sigma / len(valid_loader)}, epoch) 193 | writer.add_scalars('Accuracy_group', {'valid_acc': conf_mat.trace() / conf_mat.sum()}, epoch) 194 | print('Finished Training') 195 | 196 | # ------------------------------------ step5: 保存模型 并且绘制混淆矩阵图 ------------------------------------ 197 | net_save_path = os.path.join(log_dir, 'net_params.pkl') 198 | torch.save(net.state_dict(), net_save_path) 199 | 200 | conf_mat_train, train_acc = validate(net, train_loader, 'train', classes_name) 201 | conf_mat_valid, valid_acc = validate(net, valid_loader, 'valid', classes_name) 202 | 203 | show_confMat(conf_mat_train, classes_name, 'train', log_dir) 204 | show_confMat(conf_mat_valid, classes_name, 'valid', log_dir) -------------------------------------------------------------------------------- /Code/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Code/utils/__init__.py -------------------------------------------------------------------------------- /Code/utils/utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from PIL import Image 3 | from torch.utils.data import Dataset 4 | import numpy as np 5 | import torch 6 | from torch.autograd import Variable 7 | import os 8 | import matplotlib.pyplot as plt 9 | import torch.nn as nn 10 | import torch.nn.functional as F 11 | 12 | 13 | class Net(nn.Module): 14 | def __init__(self): 15 | super(Net, self).__init__() 16 | self.conv1 = nn.Conv2d(3, 6, 5) 17 | self.pool1 = nn.MaxPool2d(2, 2) 18 | self.conv2 = nn.Conv2d(6, 16, 5) 19 | self.pool2 = nn.MaxPool2d(2, 2) 20 | self.fc1 = nn.Linear(16 * 5 * 5, 120) 21 | self.fc2 = nn.Linear(120, 84) 22 | self.fc3 = nn.Linear(84, 10) 23 | 24 | def forward(self, x): 25 | x = self.pool1(F.relu(self.conv1(x))) 26 | x = self.pool2(F.relu(self.conv2(x))) 27 | x = x.view(-1, 16 * 5 * 5) 28 | x = F.relu(self.fc1(x)) 29 | x = F.relu(self.fc2(x)) 30 | x = self.fc3(x) 31 | return x 32 | 33 | # 定义权值初始化 34 | def initialize_weights(self): 35 | for m in self.modules(): 36 | if isinstance(m, nn.Conv2d): 37 | torch.nn.init.xavier_normal_(m.weight.data) 38 | if m.bias is not None: 39 | m.bias.data.zero_() 40 | elif isinstance(m, nn.BatchNorm2d): 41 | m.weight.data.fill_(1) 42 | m.bias.data.zero_() 43 | elif isinstance(m, nn.Linear): 44 | torch.nn.init.normal_(m.weight.data, 0, 0.01) 45 | m.bias.data.zero_() 46 | 47 | class MyDataset(Dataset): 48 | def __init__(self, txt_path, transform = None, target_transform = None): 49 | fh = open(txt_path, 'r') 50 | imgs = [] 51 | for line in fh: 52 | line = line.rstrip() 53 | words = line.split() 54 | imgs.append((words[0], int(words[1]))) 55 | 56 | self.imgs = imgs # 最主要就是要生成这个list, 然后DataLoader中给index,通过getitem读取图片数据 57 | self.transform = transform 58 | self.target_transform = target_transform 59 | 60 | def __getitem__(self, index): 61 | fn, label = self.imgs[index] 62 | img = Image.open(fn).convert('RGB') # 像素值 0~255,在transfrom.totensor会除以255,使像素值变成 0~1 63 | 64 | if self.transform is not None: 65 | img = self.transform(img) # 在这里做transform,转为tensor等等 66 | 67 | return img, label 68 | 69 | def __len__(self): 70 | return len(self.imgs) 71 | 72 | 73 | def validate(net, data_loader, set_name, classes_name): 74 | """ 75 | 对一批数据进行预测,返回混淆矩阵以及Accuracy 76 | :param net: 77 | :param data_loader: 78 | :param set_name: eg: 'valid' 'train' 'tesst 79 | :param classes_name: 80 | :return: 81 | """ 82 | net.eval() 83 | cls_num = len(classes_name) 84 | conf_mat = np.zeros([cls_num, cls_num]) 85 | 86 | for data in data_loader: 87 | images, labels = data 88 | images = Variable(images) 89 | labels = Variable(labels) 90 | 91 | outputs = net(images) 92 | outputs.detach_() 93 | 94 | _, predicted = torch.max(outputs.data, 1) 95 | 96 | # 统计混淆矩阵 97 | for i in range(len(labels)): 98 | cate_i = labels[i].numpy() 99 | pre_i = predicted[i].numpy() 100 | conf_mat[cate_i, pre_i] += 1.0 101 | 102 | for i in range(cls_num): 103 | print('class:{:<10}, total num:{:<6}, correct num:{:<5} Recall: {:.2%} Precision: {:.2%}'.format( 104 | classes_name[i], np.sum(conf_mat[i, :]), conf_mat[i, i], conf_mat[i, i] / (1 + np.sum(conf_mat[i, :])), 105 | conf_mat[i, i] / (1 + np.sum(conf_mat[:, i])))) 106 | 107 | print('{} set Accuracy:{:.2%}'.format(set_name, np.trace(conf_mat) / np.sum(conf_mat))) 108 | 109 | return conf_mat, '{:.2}'.format(np.trace(conf_mat) / np.sum(conf_mat)) 110 | 111 | 112 | def show_confMat(confusion_mat, classes, set_name, out_dir): 113 | 114 | # 归一化 115 | confusion_mat_N = confusion_mat.copy() 116 | for i in range(len(classes)): 117 | confusion_mat_N[i, :] = confusion_mat[i, :] / confusion_mat[i, :].sum() 118 | 119 | # 获取颜色 120 | cmap = plt.cm.get_cmap('Greys') # 更多颜色: http://matplotlib.org/examples/color/colormaps_reference.html 121 | plt.imshow(confusion_mat_N, cmap=cmap) 122 | plt.colorbar() 123 | 124 | # 设置文字 125 | xlocations = np.array(range(len(classes))) 126 | plt.xticks(xlocations, list(classes), rotation=60) 127 | plt.yticks(xlocations, list(classes)) 128 | plt.xlabel('Predict label') 129 | plt.ylabel('True label') 130 | plt.title('Confusion_Matrix_' + set_name) 131 | 132 | # 打印数字 133 | for i in range(confusion_mat_N.shape[0]): 134 | for j in range(confusion_mat_N.shape[1]): 135 | plt.text(x=j, y=i, s=int(confusion_mat[i, j]), va='center', ha='center', color='red', fontsize=10) 136 | # 保存 137 | plt.savefig(os.path.join(out_dir, 'Confusion_Matrix' + set_name + '.png')) 138 | plt.close() 139 | 140 | 141 | def normalize_invert(tensor, mean, std): 142 | for t, m, s in zip(tensor, mean, std): 143 | t.mul_(s).add_(m) 144 | return tensor 145 | -------------------------------------------------------------------------------- /Data/PyTorch_tutorial_0.0.4_余霆嵩.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/PyTorch_tutorial_0.0.4_余霆嵩.pdf -------------------------------------------------------------------------------- /Data/PyTorch_tutorial_0.0.5_余霆嵩.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/PyTorch_tutorial_0.0.5_余霆嵩.pdf -------------------------------------------------------------------------------- /Data/alipay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/alipay.jpg -------------------------------------------------------------------------------- /Data/cam_img/test_img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_1.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_2.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_3.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_4.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_5.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_6.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_7.png -------------------------------------------------------------------------------- /Data/cam_img/test_img_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cam_img/test_img_8.png -------------------------------------------------------------------------------- /Data/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cat.png -------------------------------------------------------------------------------- /Data/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/cover.png -------------------------------------------------------------------------------- /Data/net_params_72p.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/net_params_72p.pkl -------------------------------------------------------------------------------- /Data/visual.txt: -------------------------------------------------------------------------------- 1 | ../../Data/cat.png 3 -------------------------------------------------------------------------------- /Data/wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TingsongYu/PyTorch_Tutorial/38fae4a41144f3da029f1900b41a74f4f2cf7f48/Data/wechat.jpg -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Pytorch模型训练实用教程 2 | Image text 3 | 4 | --- 5 | 6 | 📢:《PyTorch实用教程》(第二版)已开源,欢迎阅读:https://tingsongyu.github.io/PyTorch-Tutorial-2nd/ 7 | 8 | 📢:《PyTorch实用教程》(第二版)已开源,欢迎阅读:https://tingsongyu.github.io/PyTorch-Tutorial-2nd/ 9 | 10 | 📢:《PyTorch实用教程》(第二版)已开源,欢迎阅读:https://tingsongyu.github.io/PyTorch-Tutorial-2nd/ 11 | 12 | 第二版新增丰富的**深度学习应用案例**和**推理部署框架**,包括CV、NLP和LLM的十多个实战项目,以及ONNX和TensorRT的教程。 13 | 14 | # 1.简介 15 | 16 | 本代码为教程——《Pytorch模型训练实用教程》中配套代码;
17 | 《Pytorch模型训练实用教程》可通过如下方式获取:
18 | 19 | 1. https://github.com/tensor-yu/PyTorch_Tutorial/tree/master/Data
20 | 2. QQ群: 四群:854620826
21 | 22 | 23 | # 2.环境配置 24 | 代码在以下两种环境测试过:
25 | 1. win10 64位 + python3.5 + pytorch==0.4.0
26 | 2. mac + python3.6 + pytorch==0.4.1/ pytorch==1.0.0
27 | 28 | **第一步 安装各依赖包:**
29 | pip install -r requirements.txt 30 | 31 | **第二步 手动安装pytorch及torchvision:**
32 | 均选择无gpu版本进行安装,进入官网选择相应的指令进行安装 33 | https://pytorch.org/get-started/locally/ 34 | 35 | 36 | # 3.问题反馈 37 | 若发现任何问题和改进意见,请您随时联系我。
38 | 联系方式:yts3221@126.com
39 | 读者qq群: 40 | 41 | ​ 一群:671103375 (已满)
42 | 43 | ​ 二群:773031536 (已满)
44 | 45 | ​ 三群:514974779 (已满)
46 | 47 | ​ 四群:854620826(已满) 48 | 49 | ​ 五群:1021300804 50 | 51 | # 4.修改记录 52 | 0.0.5: 53 | 1. 1.6小节勘误,将36\*36改为40\*40; 54 | 2. 2.3小节删除注释; 55 | 3. 修改权值初始化杂谈中的理解错误; 56 | 4. 全文代码缩进。 57 | 58 | --- 59 | 60 | 如果本教程对你有帮助😀😀,请作者喝杯茶吧🍵🍵🥂🥂 61 | 62 | WeChat: Alipay: 63 | 64 | 65 | 66 | 67 | 68 | --- 69 | 70 | 71 | ## Stargazers over time 72 | 73 | [![Stargazers over time](https://starchart.cc/TingsongYu/PyTorch_Tutorial.svg)](https://starchart.cc/TingsongYu/PyTorch_Tutorial) 74 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.6.1 2 | astor==0.7.1 3 | cycler==0.10.0 4 | gast==0.2.0 5 | grpcio==1.16.0 6 | h5py==2.8.0 7 | Keras-Applications==1.0.6 8 | Keras-Preprocessing==1.0.5 9 | kiwisolver==1.0.1 10 | Markdown==3.0.1 11 | matplotlib==2.2.3 12 | numpy==1.15.1 13 | opencv-python==3.4.3.18 14 | Pillow==5.2.0 15 | protobuf==3.6.1 16 | pyparsing==2.2.0 17 | python-dateutil==2.7.3 18 | pytz==2018.5 19 | scipy==1.1.0 20 | six==1.11.0 21 | tensorboard==1.12.0 22 | tensorboardX==1.4 23 | tensorflow==1.12.0 24 | termcolor==1.1.0 25 | Werkzeug==0.14.1 26 | --------------------------------------------------------------------------------