├── .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 |
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 | [](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 |
--------------------------------------------------------------------------------