梯度消失或梯度爆炸
当每一层的梯度是小于1的数,当进行反向传播时,随着网络越深,梯度越接近零
反之,当每一层的梯度是大于1的数…
所以通常需要对数据进行标准化处理,权重初始化,以及用BN层,在ResNet网络中使用了residual模块(残差模块)
max()函数
pytorch入门学习时,输出与标签常用max()函数进行比较
import torch
label = torch.tensor([0, 1, 2, 3, 4, 0])
outputs = torch.tensor([[0.9, 0.4, 0.1, 0.5, 0.4], [0.9, 0.2, 0.3, 0.8, 0.7],
[0.2, 0.1, 0.9, 0.5, 0.3],[0.2, 0.2, 0.4, 0.9, 0.6],
[0.9, 0.5, 0.1, 0.5, 0.4], [0.9, 0.1, 0.4, 0.1, 0.6]])
_, predicted = torch.max(outputs, dim=1)
correct = (predicted == label)
print("predicted:", predicted)
print("label:", label)
print(correct)
print(correct[1].item())
pre2 = torch.argmax(outputs, dim=1)
print("pre2:", pre2) # 输出结果和predicted一样
输出结果为:
predicted: tensor([0, 0, 2, 3, 0, 0])
label: tensor([0, 1, 2, 3, 4, 0])
tensor([ True, False, True, True, False, True])
False
pre2: tensor([0, 0, 2, 3, 0, 0])
这里要说明的是:
labels 里的数据并不是按照顺序排列的,并且label.size(0) == MINI_BATCH
outputs的列数,即outputs.shape[1]
才是真正的这个数据集的种类
outputs的列数 == MINI_BATCH,所以,用max()
函数时,outputs的行数要和label.size(0)的数目相等
torchvision.utils.make_grid
torchvision.utils.make_grid(tensor, nrow=8, padding=2, normalize=False, range=None, scale_each=False)
# 将一小batch图片变为一张图。nrow表示每行多少张图片的数量。
# 给一个batch为4的图片,h和w分别为32,channel为3,看看结果
images,labels = dataiter.next()
print(images.shape)
#torch.Size([4, 3, 32, 32]) bchw
print(torchvision.utils.make_grid(images).shape)
#torch.Size([3, 36, 138])
怎么理解这个输出结果呢?第一个dim当然就是channel,因为合并成一张图片了嘛,所以batch这个维度就融合了,变成了chw,这里c还是原来的channel数,h比原来增加了4,w = 32*4 + 10,c很好理解,那么为什么h增加了4,w增加了10呢?
我想办法把batch_size调整成了3,结果如下:
#torch.Size([3, 3, 32, 32])
#torch.Size([3, 36, 104])
tensor (Tensor or list) – 4D mini-batch Tensor of shape (B x C x H x W) or a list of images all of the same size.
nrow (int, optional) – Number of images displayed in each row of the grid. The Final grid size is (B / nrow, nrow). Default is 8.
padding (int, optional) – amount of padding. Default is 2.
normalize (bool, optional) – If True, shift the image to the range (0, 1), by subtracting the minimum and dividing by the maximum pixel value.
range (tuple, optional) – tuple (min, max) where min and max are numbers, then these numbers are used to normalize the image. By default, min and max are computed from the tensor.
scale_each (bool, optional) – If True, scale each image in the batch of images separately rather than the (min, max) over all images.
pad_value (float, optional) – Value for the padded pixels.
官方文档里要注意nrow 和 padding 这两个参数是有默认值的
很明显,当batch为3的时候,w应该为332 = 96,但是我们考虑到每张图片的padding其实是2,因此,每张图片其实变成了3636的图片,所以最终应该为w = 36/* 3 =108才对呀?
显然上面的想法还是不对,思考了一会,算是想明白了。
三张图片,padding在水平方向并没有每张图片都padding,而是两张图片之间只有一个padding,这样3张图片空隙有两个,加上最左和最右,水平方向上其实是4* 2 =8,所以w增加了8,这样96 + 8 = 104 就对了。同理,竖直方向上也是这样处理的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TwkS9dh0-1641207172251)(D:\StudyNotes\wei.png)]
pytorch 加载模型报错:‘function‘ object has no attribute ‘copy‘
报错原因:
保存模型应该是:
torch.save(model.state_dict(), PATH)
如果写成:
torch.save(model.state_dict, PATH)
则会报错
pytorch显示数据集的一个batch的图片
这里以测试集为例(MINI_BATCH = 10)
# 定义一个显示图像的函数
def show_image(img):
# img是Tensor,且为 B * C * H *W
#这里对应的是transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
img = img * 0.5 + 0.5
img = np.transpose(img.numpy(), (1, 2, 0)) # C H W -> H W C
plt.imshow(img)
plt.show()
data = iter(test_loader)
images, labels = next(data)
img = torchvision.utils.make_grid(images, nrow = 10) # nrow参数用法参考上面
#调用函数
show_image(img)
# 显示某一张图片
# load image
img_path = "./data/daisy_01.jpg"
img = Image.open(img_path)
plt.imshow(img) # H W C
plt.show()
CNN Receptive Field
Receptive Field 感受野
输出feature map上的一个单元对应输入层上的区域大小
举例:
input: 9 * 9 *1 Conv1 Size:3 * 3 Stride:2 4 * 4 * 1
Conv2 Size:2 * 2 Stride:2 2 * 2 * 1
out_size = (in_size - F_size + 2P) / S +1
感受野计算公式:
F(i) = (F(i+1) - 1) * Stride + Ksize
F(i)为第i层感受野
Stride为第i层的步距
Ksize为卷积核或池化核尺寸
Feature map: F = 1
Pool1: F = (1 - 1) * 2 + 2 = 2 # 即对应 2 * 2 的区域
Conv1: F = (2 - 1) * 2 + 3 = 5 # 即对应 5 * 5 的区域
tqdm 用法
安装tqbm包的命令:pip install tqdm
from tqdm import tqdm # 导入包
import time
for i in tqdm(range(10), desc='Processing_1'):
time.sleep(0.05)
# 列表类型
list1 = [1, 5, 7, 9, "w"]
for i in tqdm(list1, desc="Processing_2"):
time.sleep(0.1)
# 字典类型
dict1 = {"apple": 10, "orange": 5, "banana": 20}
for i in tqdm(list(dict1.keys()), desc="Processing_3"):
time.sleep(0.1)
model.train()与model.eval()的用法
在训练开始之前写上model.trian(),在测试时写上model.eval()
如果模型中有BN层(Batch Normalization)和Dropout,需要在训练时添加model.train(),在测试时添加model.eval()。其中model.train()是保证BN层用每一批数据的均值和方差,而model.eval()是保证BN用全部训练数据的均值和方差;而对于Dropout,model.train()是随机取一部分网络连接来训练更新参数,而model.eval()是利用到了所有网络连接。
torch.softmax
nn.Softmax( ) 是一个类
返回一个与输入维度相同的张量
import torch
import numpy
# y = torch.rand(12).reshape(2, 2, 3)
y = torch.normal(0, 1, (2, 2, 3))
print(torch.softmax(x, dim=0)) # 两片 2*3 的张量,对应位置的和为1,想象一下空间
print(torch.softmax(x, dim=1)) # 每一列的和为1
print(torch.softmax(x, dim=2)) # 每一行的和为1
输出为:
tensor([[[-0.0479, 0.0685, -1.0559],
[-1.1511, 1.1537, -1.4805]],
[[-0.5178, 0.0845, 0.2507],
[-0.9432, -2.0841, 0.6930]]])
tensor([[[0.6153, 0.4960, 0.2131],
[0.4482, 0.9622, 0.1022]],
[[0.3847, 0.5040, 0.7869],
[0.5518, 0.0378, 0.8978]]])
tensor([[[0.7509, 0.2525, 0.6046],
[0.2491, 0.7475, 0.3954]],
[[0.6048, 0.8974, 0.3912],
[0.3952, 0.1026, 0.6088]]])
tensor([[[0.4019, 0.4515, 0.1467],
[0.0852, 0.8536, 0.0613]],
[[0.2007, 0.3665, 0.4328],
[0.1549, 0.0495, 0.7956]]])
0.3921 + 0.6079 = 1 0.3921 + 0.6079 = 1 0.3921+0.6079=1
0.3921 = e 0.3921 e 0.3921 + e 0.6079 0.3921 = \frac{e^{0.3921}}{{e^{0.3921} + e^{0.6079}}} 0.3921=e0.3921+e0.6079e0.3921
数据预处理
transforms.Compose函数就是将transforms组合在一起
从文件夹中读取数据
'''
在这个案例当中,训练集的所有图像,分好类都放在
D:/PythonTemp/deep-learning/data_set/flower_data/train/文件夹下,
测试集亦然
输入图片为 3 * 224 * 224
'''
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision import datasets
def main():
data_transfrom = {
"train":transforms.Compose([transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
"val":transforms.Compose([transforms..Resize((224, 224)), # cannot224 must(224, 224)
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]),
}
image_root = "D:/PythonTemp/deep-learning/data_set/flower_data/"
train_dataset = datasets.ImageFolder(root=image_root + "train", transform=data_transform["train"])
train_num = len(train_dataset) # 可以输出训练集有多少文件
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True, num_workers=8)
val_dataset = datasets.ImageFolder(root=image_root + "val/", transform=data_transform["val"])
val_num = len(val_dataset) # 可以输出训练集有多少文件
val_loader = DataLoader(dataset=val_dataset, batch_size=batch_size, shuffle=False, num_workers=8)
print("using {} images for training, using {} images for validating".format(train_num, val_num))
view 和 flatten
在进行全连接层之前,都要进行数据的展平处理,有两种展平方式
Pytorch 是 B C H W
import torch
import numpy as np
# 设 BCHW 是 1, 3, 2, 2
a = np.arange(12).reshape(1, 3, 2, 2)
a = torch.tensor(a)
a_view = a.view(-1, 3*2*2)
a_flatten = torch.flatten(a,start_dim=1)
print("a_view = ", a_view)
print("="*30)
print("a_flatten = ", a_flatten)
# 输出为:
a_view = tensor([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], dtype=torch.int32)
==============================
a_flatten = tensor([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]], dtype=torch.int32)
lxml.etree
文档格式化方法 | 类型type | 根节点 | 编码方式 | xpath |
---|---|---|---|---|
etree.HTML() | <class ‘lxml.etree._Element’> | html | (X.encode(‘utf-8’)) | 支持 |
etree.fromstring() | <class ‘lxml.etree._Element’> | 原文档节点 | (X.encode(‘utf-8’)) | 支持 |
etree.tostring() | <class ‘bytes’> | 无 | 无 | 不支持 |
表格解读:
1.从三者的类型上可以看到,etree.HTML()和etree.fromstring()都是属于同一种“class类”,这个类型才会支持使用xpath。也就说etree.tostring()是“字节bytes类”,不能使用xpath!
2.从根节点看,etree.HTML()的文档格式已经变成html类型,所以根节点自然就是html标签【这属于html方面的知识点,不清楚的朋友可以查资料了解】
但是,etree.fromstring()的根节点还是原文档中的根节点,说明这种格式化方式并不改变原文档的整体结构,我比较推荐使用这种方式进行文档格式化,因为这样有利于我们有时使用xpath的绝对路径方式查找信息!
而etree.tostring()是没有所谓的根节点的,因为这个方法得到的文档类型是‘bytes’类,其实里面的tostring,我们可以理解成to_bytes,这样可以帮助理解记忆。
3.从编码方式上看,etree.HTML()和etree.fromstring()的括号内参数都要以“utf-8”的方式进行编码!表格中的X是表示用read()方法之后的原文档内容。
为了理解,下面举个实例:
# xml 文件
<annotation>
<folder>VOC2012</folder>
<filename>2008_000054.jpg</filename>
<source>
<database>The VOC2008 Database</database>
<annotation>PASCAL VOC2008</annotation>
<image>flickr</image>
</source>
<size>
<width>500</width>
<height>333</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>bird</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<occluded>0</occluded>
<bndbox>
<xmin>284</xmin>
<ymin>100</ymin>
<xmax>318</xmax>
<ymax>184</ymax>
</bndbox>
<difficult>0</difficult>
</object>
<object>
<name>bird</name>
<pose>Right</pose>
<truncated>0</truncated>
<occluded>0</occluded>
<bndbox>
<xmin>112</xmin>
<ymin>146</ymin>
<xmax>198</xmax>
<ymax>209</ymax>
</bndbox>
<difficult>0</difficult>
</object>
</annotation>
from lxml import etree
import os
root = "D:\\PythonTemp\\FasterR_CNN\\VOCdevkit\\VOC2012"
annotations_root = os.path.join(root, "Annotations")
txt_path = os.path.join(root, "ImageSets", "Main", "train.txt")
xml_path = os.path.join(annotations_root, "2008_000054.xml")
with open(xml_path) as f_xml:
# 这里读取的是每一个 .xml 的信息
xml_str = f_xml.read()
xml = etree.fromstring(xml_str)
print("len(xml) = ", len(xml))
def parse_xml_to_dict(xml):
if len(xml) == 0:
return {xml.tag: xml.text}
result = {}
for child in xml:
child_result = parse_xml_to_dict(child)
if child.tag != "object":
result[child.tag] = child_result[child.tag]
else:
if child.tag not in result:
result[child.tag] = []
result[child.tag].append(child_result[child.tag])
return {xml.tag: result}
data = parse_xml_to_dict(xml)
print(data)
参数初始化
net = nn.Sequential(nn.Flatten(),
nn.Linear(784, 256),
nn.ReLU(),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights)
数据增加维度
numpy操作
import numpy as np
x = np.arange(16).reshape(8, 2)
x = x[:, None]
print(x.shape)
y = np.arange(20).reshape(10, 2)
y = y[None]
print(y.shape)
z = np.arange(24).reshape(12, 2)
z = z[:, :, None]
print(z.shape)
# 输出为:
(8, 1, 2)
(1, 10, 2)
(12, 2, 1)
torch操作
img = cv2.imread(img_path)
img = torch.unsqueeze(img, dim=0)
np.maximum() np.minimum()
这两个格式都是 np.maximum(x1, x2)
并且 x1.shape == x2.shape,如果不相等,会通过broadcast变成形状相同的
x = np.array([[2, 4], [3, 5]])
y = np.array([[3, 7]])
# 这里x.shape != y.shape,所以y通过broadcasting变成 np.array([[3, 7], [3, 7]])
print(np.maximum(x, y))
# 输出为:
[[3 7]
[3 7]]
2 vs 3 4 vs 7 3 vs 3 5 vs 7
损失函数
https://blog.csdn.net/watermelon1123/article/details/91044856
torch.meshgrid()
torch.meshgrid()的功能是生成网格,也可以用于生成坐标
函数输入
输入两个数据类型相同的一维tensor
函数输出
输出两个tensor,tensor的行数是第一个输入张量的元素个数,列数是第二个输入张量的元素个数
其中第一个输出张量填充第一个输入张量中的元素,各行元素相同;第二个输出张量填充第二个输入张量中的元素各列元素相同。
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6, 7])
x, y = torch.meshgrid([a, b])
print(x)
print(y)
# 输出为:3 行 4 列
tensor([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
tensor([[4, 5, 6, 7],
[4, 5, 6, 7],
[4, 5, 6, 7]])
在YOLO V3将图像划分为单元网格的部分就用到了torch.meshgrid()函数,如下所示。
yv, xv = torch.meshgrid([torch.arange(self.ny, device=device),
torch.arange(self.nx, device=device)])
"""
要注意的是这里的self.ny(13)和self.nx(13)的顺序一定是这样的,因为
从下图可以知道yv的行相同,所以必须是第一个,xv的列相同,所以
应该是第二个
"""
# batch_size, na, grid_h, grid_w, wh
self.grid = torch.stack((xy, yv),2).view((1, 1, self.ny, 2)).float()
# 要注意的是这里的torch.stack((xy, yv),2),其中(xy, yv)必须有括号,因为.stack()需要输入的是一个
# 序列,而python里面的序列只有list 和 tuple
shgrid()函数,如下所示。
yv, xv = torch.meshgrid([torch.arange(self.ny, device=device),
torch.arange(self.nx, device=device)])
"""
要注意的是这里的self.ny(13)和self.nx(13)的顺序一定是这样的,因为
从下图可以知道yv的行相同,所以必须是第一个,xv的列相同,所以
应该是第二个
"""
# batch_size, na, grid_h, grid_w, wh
self.grid = torch.stack((xy, yv),2).view((1, 1, self.ny, 2)).float()
# 要注意的是这里的torch.stack((xy, yv),2),其中(xy, yv)必须有括号,因为.stack()需要输入的是一个
# 序列,而python里面的序列只有list 和 tuple