PyTorch Learning
Tensor
- 属性
data
: 数值grad
: 梯度值grad_fn
requires_grad
is_leaf
- 叶子节点的概念主要是为了节省内存,所有节点都依赖于叶子节点(初始节点),在计算图中的一轮反向传播结束之后,非叶子节点的梯度会被释放(除非指定 retain_grad())
- PyTorch 使用动态图机制,运算和搭建同时进行,可以先计算前面节点的值,再根据这些值搭建后面的计算图
dtype
: 数据类型,如 torch.FloatTensor,torch.cuda.FloatTensor- 分为 3 大类:float (16-bit, 32-bit, 64-bit)、integer (unsigned-8-bit ,8-bit, 16-bit, 32-bit, 64-bit)、Boolean。模型参数和数据用的最多的类型是 float-32-bit。label 常用的类型是 integer-64-bit
device
- 创建
- 根据数值创建
torch.tensor(data, dtype, device, requires_grad)
data
: list/numpy…
torch.from_numpy(np.ndarray)
torch.zeros(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
out
: 输出的张量,如果指定了 out,那么torch.zeros()
返回的张量和 out 指向的是同一个地址layout
: 内存中布局形式,有 strided,sparse_coo 等。当是稀疏矩阵时,设置为 sparse_coo 可以减少内存占用
torch.zeros_like(input)
torch.full(size, fill_value)
: 创建全为自定义数值(fill_value)的张量torch.arange(start=0, end, step=1)
: 创建 [start,end) 的一维张量torch.linspace(start, end, steps=100)
: 创建 [start, end],元素个数为 steps 的均分一维张量
- 根据分布创建
torch.logspace(start, end, steps=100, base=10)
: 创建 [base^start, base^end],元素个数为 steps 的对数均分一维张量torch.eye(n, m=None)
: 创建行为 n,列为 m(默认与 n 相同)的单位对角矩阵torch.normal(mean, std)
: 正态分布采样mean 为标量,std 为标量,这时需要设置 size,在同一正态分布中采样
mean 为标量,std 为张量
mean 为张量,std 为标量
采样的分布一个相同,另一个不同
mean 为张量,std 为张量
从不同分布中采样
torch.randn(*size)
torch.randn_like(input)
: 生成标准正态分布torch.rand(*size)
torch.rand_like(input)
: 生成 [0,1) 上的均匀分布torch.randint(low=0, high, size)
: 生成 [low, high) 上整数均匀分布torch.randperm(n)
: 生成 0 到 n-1 的随机排列,常用于生成索引torch.bernoulli(input)
: 生成以 input (tensor) 为概率 (p) 的伯努利分布(0-1 离散分布)
- 根据数值创建
- 操作
广播机制
- 从最后一个维度开始匹配
- 在前面插入若干维度(进行 unsqueeze 操作)
- 将维度的 size 从 1 扩张到与某个张量相同的维度
例如:
- Feature maps:[4, 32, 14, 14]
- Bias:[32, 1, 1](Tip:后面的两个1是手动unsqueeze插入的维度)-> [1, 32, 1, 1] -> [4, 32, 14, 14]
拼接
torch.cat(tensors, dim=0)
: 将张量在 dim 维上拼接(除了 dim 维其他维度必须相同)torch.stack(tensors, dim=0)
: 将张量在新创建的 dim 维上拼接(两个张量 shape 必须相同)
切分
torch.chunk(input, chunks, dim=0)
: 将 input 向量在 dim 维上切分为 chunks 份(若不能整除,则最后一份张量小于其他张量)【按个数拆分】torch.split(tensor, split_size_or_sections, dim=0)
: split_size_or_sections 为 int 时,表示切分的每一份长度(若不能整除,则最后一份张量小于其他张量);为 list 时,按照 list 元素作为每一个分量的长度切分(list 中元素之和必须等于切分维度的值)【按长度拆分】
索引
torch.index_select(input, dim, index)
: 在 dim 维度上,按照 index 索引取出 input 中的数据拼接为张量返回1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 创建均匀分布
t = torch.randint(0, 9, size=(3, 3))
# 注意 idx 的 dtype 不能指定为 torch.float
idx = torch.tensor([0, 2], dtype=torch.long)
# 取出第 0 行和第 2 行
t_select = torch.index_select(t, dim=0, index=idx)
print("t:\n{}\nt_select:\n{}".format(t, t_select))
'''
t:
tensor([[4, 5, 0],
[5, 7, 1],
[2, 5, 8]])
t_select:
tensor([[4, 5, 0],
[2, 5, 8]])
'''torch.mask_select(input, mask)
: 按照 mask 中的 True 进行索引拼接得到一维张量返回(mask 为与 input 同形的布尔类张量)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17t = torch.randint(0, 9, size=(3, 3))
mask = t.le(5) # ge is mean greater than or equal/ gt: greater than le lt
# 取出大于 5 的数
t_select = torch.masked_select(t, mask)
print("t:\n{}\nmask:\n{}\nt_select:\n{} ".format(t, mask, t_select))
'''
t:
tensor([[4, 5, 0],
[5, 7, 1],
[2, 5, 8]])
mask:
tensor([[ True, True, True],
[ True, False, True],
[ True, True, False]])
t_select:
tensor([4, 5, 0, 5, 1, 2, 5])
'''torch.ge(input, other)
le
gt
lt
nonzero
: 根据比较结果返回布尔型张量,other
可以是数值或张量
变换
torch.reshape(input, shape)
注意:当张量在内存中是连续的时,返回的张量和原来的张量共享数据内存,改变一个变量另一个也会改变!
torch.transpose(input, dim0, dim1)
: 交换张量的两个维度torch.permute(input, dims)
: 置换张量维度(内存不变)torch.squeeze(input, dim=None)
: 压缩长度为 1 的维度(dim 若为 None 则移除所有长度为 1 的维度;若指定维度,则当且仅当该维度长度为 1 时可以移除)torch.unsqueeze(input, dim)
: 根据 dim 扩展维度,长度为 1
扩张与缩减
torch.expand()
- repeat
- narrow
近似与裁剪
torch.floor
torch.ceil
torch.trunc
torch.frac
torch.round
: 向下取整、向上取整、取整数部分、取小数部分、四舍五入torch.clamp(input, min, max)
: 对张量中的元素,小于 min 的都设置为 min,大于 max 的都设置成 max
统计属性
torch.norm(input, p, dim=None)
: 在 dim 维上对 input 张量求 p 范数torch.prod(input, dim=None)
: 累积torch.argmax/argmin(input, dim=None)
: 最大值最小值索引(若不指定 dim,默认会将Tensor打平后取最大值索引和最小值索引)torch.topk(input, k, dim=None, largest=True, sorted=True)
: 返回二元组values
indices
,分别是 input 中前 k 大( largest=False 时为前 k 小)的值和索引(不指定 dim 时默认为最后一维,返回的两个张量与 input 相比除了 dim 维的值变成 k 外其他维不变)1
2
3
4
5>>> x = torch.arange(1., 6.)
>>> x
tensor([ 1., 2., 3., 4., 5.])
>>> torch.topk(x, 3)
torch.return_types.topk(values=tensor([5., 4., 3.]), indices=tensor([4, 3, 2]))torch.kthvalue(input, k, dim=None, keepdim=False)
: 返回 dim 维上第 k 小的元素及其索引1
2
3
4
5
6
7
8
9
10
11
12>>> x = torch.arange(1., 6.)
>>> x
tensor([ 1., 2., 3., 4., 5.])
>>> torch.kthvalue(x, 4)
torch.return_types.kthvalue(values=tensor(4.), indices=tensor(3))
>>> x=torch.arange(1.,7.).resize_(2,3)
>>> x
tensor([[ 1., 2., 3.],
[ 4., 5., 6.]])
>>> torch.kthvalue(x, 2, 0, True)
torch.return_types.kthvalue(values=tensor([[4., 5., 6.]]), indices=tensor([[1, 1, 1]]))
其他高阶操作
torch.where(condition, input, other)
:实现条件选择 / 分段函数gather
torch.nn.functional.grid_sample(input, grid, mode='bilinear', padding_mode='zeros', align_corners=None)
:用于 warping 操作,根据 grid 从 input 中取值填到 output 中
- 自动求导机制
torch.autograd.backward(tensors, grad_tensors=None, retain_graph=False, create_graph=None)
: 反向求导tensors
: 用于求导的张量,如 lossgrad_tensors
: 当有多个 loss 混合需要计算梯度时,设置每个 loss 的权重retain_graph
: 保存计算图create_graph
: 创建导数计算图,用于高阶求导1
2
3
4
5
6
7
8
9
10
11
12
13x = torch.tensor([3.], requires_grad=True)
y = torch.pow(x, 2) # y = x**2
# 如果需要求 2 阶导,需要设置 create_graph=True,让一阶导数 grad_1 也拥有计算图
grad_1 = torch.autograd.grad(y, x, create_graph=True) # grad_1 = dy/dx = 2x = 2 * 3 = 6
print(grad_1)
# 这里求 2 阶导
grad_2 = torch.autograd.grad(grad_1[0], x) # grad_2 = d(dy/dx)/dx = d(2x)/dx = 2
print(grad_2)
'''
(tensor([6.], grad_fn=<MulBackward0>),)
(tensor([2.]),)
'''
torch.aotograd.grad(outputs, inputs, grad_outputs=None, retain_graph=False, create_graph=None)
: outputs 对 inputs 求取梯度(返回一个 tuple,取出第 0 个元素为梯度计算结果)注意
PyTorch 采用动态图机制,默认每次反向传播之后都会释放计算图,因此默认情况下连续两次调用 backward 会报错
每次反向求导时,计算的梯度不会自动清零,若多次迭代计算而梯度没有清零,那么梯度会在前一次的基础上叠加;因此每次调用 backward 后,记得将梯度清零:
a.grad.zero_()
或optimizer.zero_grad()
依赖于叶子节点的节点,requires_grad 属性默认为 True
叶子节点不可执行 inplace 操作
以加法来说,inplace 操作有
a += x
,a.add_(x)
,改变后的值和原来的值内存地址是同一个。非inplace 操作有a = a + x
,a.add(x)
,改变后的值和原来的值内存地址不是同一个。如果在反向传播之前 inplace 改变了叶子节点的值,再执行 backward 会报错(这是因为在进行前向传播时,计算图中依赖于叶子节点的那些节点,会记录叶子节点的地址,在反向传播时就会利用叶子节点的地址所记录的值来计算梯度)
数据处理
DataLoader
和Dataset
torch.utils.Dataset
: 所有自定义数据集的基类,需要重写下列方法:__getitem(index)__
: 接收一个索引,返回索引对应的样本和标签__len()__
: 返回所有样本的数量
数据读取包括下列三方面:
- 读取哪些数据:每个 Iteration 读取一个 Batchsize 大小的数据,每个 Iteration 应该读取哪些数据。
- 从哪里读取数据:如何找到硬盘中的数据,应该在哪里设置文件路径参数
- 如何读取数据:不同的文件需要使用不同的读取方法和库。
torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, drop_last=False)
: 数据加载器- sampler : 自定义从数据集中采样的策略,如果制定了采样策略, shuffle 则必须为 False(所有定义了
__len__
的 *********Iterable
* 都可以作为 sampler) - num_workers : 使用线程的数量,当为0时数据直接加载到主程序,默认为0
- drop_last:布尔类型,为 True 时将会把最后不足 batch_size 的数据丢掉,为F将会把剩余的数据作为最后一小组
Epoch: 所有训练样本都已经输入到模型中,称为一个 Epoch
Iteration: 一批样本输入到模型中,称为一个 Iteration
Batch size: 批大小,决定一个 iteration 有多少样本,也决定了一个 Epoch 有多少个 Iteration
DataLoader
迭代器源码:1
2
3
4
5def __iter__(self):
if self.num_workers == 0:
return _SingleProcessDataLoaderIter(self)
else:
return _MultiProcessingDataLoaderIter(self)对单进程,在
_SingleProcessDataLoaderIter
里只有一个方法_next_data()
,如下:1
2
3
4
5
6def _next_data(self):
index = self._next_index() # may raise StopIteration
data = self._dataset_fetcher.fetch(index) # may raise StopIteration
if self._pin_memory:
data = _utils.pin_memory.pin_memory(data)
return data在该方法中,
self._next_index()
是获取一个 batchsize 大小的 index 列表,代码如下:1
2def _next_index(self):
return next(self._sampler_iter) # may raise StopIteration其中调用的
sampler
类的__iter__()
方法返回 batch_size 大小的随机 index 列表在第二行中调用了
self._dataset_fetcher.fetch(index)
获取数据。这里会调用_MapDatasetFetcher
中的fetch()
函数:1
2
3
4
5
6def fetch(self, possibly_batched_index):
if self.auto_collation:
data = [self.dataset[idx] for idx in possibly_batched_index]
else:
data = self.dataset[possibly_batched_index]
return self.collate_fn(data)
- sampler : 自定义从数据集中采样的策略,如果制定了采样策略, shuffle 则必须为 False(所有定义了
数据处理与数据增强
torchvision
库简介torchvision.transforms
: 包括常用图像预处理方法,可用在 Dataset 类初始化中,如:- 数据中心化
- 数据标准化
- 缩放
- 裁剪
- 旋转
- 翻转
- 填充
- 噪声添加
- 灰度变换
- 线性变换
- 仿射变换
- 亮度、饱和度以及对比度变换。
torchvision.datasets
: 包含常用数据集如 mnist、CIFAR-10、ImageNet 等torchvision.models
: 包含常用预训练模型,如 AlexNet、VGG、ResNet、GoogleNet 等
transforms
中的图像处理方法- 裁剪与填充
transforms.CenterCrop(size)
: 根据给定尺寸裁剪出图像中心(若裁剪的 size 比原图大,会填充值为 0 的像素)transforms.Pad(padding, fill=0, padding_mode='constant')
: 边界填充- padding : 设置填充大小
- 当为 a 时,上下左右均填充 a 个像素
- 当为 (a, b) 时,左右填充 a 个像素,上下填充 b 个像素
- 当为 (a, b, c, d) 时,左上右下分别填充 a,b,c,d
- pad_if_need : 当图片小于设置的 size,是否填充
- padding_mode:
- constant: 像素值由 fill 设定
- edge: 像素值由图像边缘像素设定
- fill : 当 padding_mode 为 constant 时,设置填充的像素值(可以为三元 tuple,表示 RGB 三元组)
- padding : 设置填充大小
transforms.RandomCrop(size, padding=None, pad_if_need=False, fill=0, padding_mode='constant')
: 随机裁剪出尺寸为 size 的图片;如果 padding 不为 None,则先 padding 再裁剪transforms.RandomResizedCrop(size, scale=(0.08, 1.0), ratio=(0.75, 1.3333333333333333), interpolation=2)
- size: 裁剪的图片尺寸
- scale: 随机缩放面积比例,默认随机选取 (0.08, 1) 之间的一个数(放大)
- ratio: 随机长宽比,默认随机选取 (3/4, 4/3) 之间的一个数。因为超过这个比例会有明显的失真
- interpolation: 当裁剪出来的图片小于 size 时,就要使用插值方法 resize
- PIL.Image.NEAREST
- PIL.Image.BILINEAR
- PIL.Image.BICUBIC
- 翻转旋转与仿射变换
transforms.RandomHorizontalFlip(p)
/transforms.RandomVerticalFlip(p)
: 根据给定概率,在水平或垂直方向翻转图片transforms.RandomRotation(degrees, center=None, expand=False)
: 随机旋转- degrees : 旋转角度
- 当为 a 时,在 (-a, a) 之间随机选择旋转角度
- 当为 (a, b) 时,在 (a, b) 之间随机选择旋转角度
- center : 旋转点设置,是坐标,默认中心旋转,如设置左上角为 (0, 0)
- expand : 是否扩大矩形框,以保持原图信息。根据中心旋转点计算扩大后的图片。如果旋转点不是中心,即使设置 expand = True,还是会有部分信息丢失
- degrees : 旋转角度
transforms.RandomAffine(degrees, translate=None, scale=None, shear=None, resample=False, fillcolor=0)
: 随机一般仿射变换,囊括五种基本操作——翻转、旋转、平移、缩放、错切- degree: 旋转角度设置
- translate: 平移区间设置,如 (a, b),a 设置宽 (width),b 设置高 (height),图像在宽维度平移的区间为 image_width * [-a, +a],高同理
- scale: 缩放比例,以面积为单位
- fillcolor: 填充颜色设置
- shear: 错切角度设置,有水平错切和垂直错切
- 若为 a,则仅在 x 轴错切,在 (-a, a) 之间随机选择错切角度
- 若为 (a, b),x 轴在 (-a, a) 之间随机选择错切角度,y 轴在 (-b, b) 之间随机选择错切角度
- 若为 (a, b, c, d),x 轴在 (a, b) 之间随机选择错切角度,y 轴在 (c, d) 之间随机选择错切角度
- 颜色调整与随机遮挡
transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)
: 调整亮度/对比度/饱和度/色相- brightness、contrast、saturation 参数
- 当为 a 时,从 [max(0, 1-a), 1+a] 中随机选择
- 当为 (a, b) 时,从 [a, b] 中选择
- hue : 色相参数(介于 0 到 0.5)
- 当为 a 时,从 [-a, a] 中选择参数
- 当为 (a, b) 时,从 [a, b] 中选择参数
- brightness、contrast、saturation 参数
transforms.RandomGrayscale(p=0.1, num_output_channels=1)
: 根据指定概率将图片转为灰度图- num_output_channels : 输出的通道数。只能设置为 1 或者 3(如果在后面使用了
transforms.Normalize
,则必须设为 3,因为transforms.Normalize
只能接收 3 通道的输入)
- num_output_channels : 输出的通道数。只能设置为 1 或者 3(如果在后面使用了
transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False)
: 根据给定概率对图像随机遮挡- scale: 遮挡区域的面积。如(a, b),则会随机选择 (a, b) 中的一个遮挡比例
- ratio: 遮挡区域长宽比。如(a, b),则会随机选择 (a, b) 中的一个长宽比
- value: 设置遮挡区域的像素值。(R, G, B) 或者 Gray,或者任意字符串(此时会用高斯噪声遮挡)。若之前执行了
transforms.ToTensor()
,像素值归一化到了 0~1 之间,因此这里设置的 (R, G, B) 要除以 255
- 特殊操作
transforms.Compose([list])
: 当需要多个transforms
操作时,需要作为一个 list 包装在transforms.Compose
中transforms.ToTensor()
: 把图片转换为张量,同时进行归一化操作,把每个通道 0255 的值**归一化为 01 [C, H, W]**transforms.Normalize(mean, std, inplace=False)
: 逐 channel 地对图像进行标准化 output = ( input - mean ) / stdtransforms.Lambda([lambda function])
: 自定义变换
- 裁剪与填充
模型构建与模型训练
nn.Module
类- 必须实现
forward()
函数 - 两种 mode:
model.train()
/model.eval()
- 注意与
requires_grad
毫无关系,它仅仅是对在 train 和 eval 模式下表现不同的模块如 Dropout 和 BatchNorm 有用!
- 注意与
- 模块与参数管理
获取
modules
(包括children
)parameters
buffers
迭代器:不带 named_ 返回 Tensor 的迭代器,带 named_ 的返回 Tuple[str, Tensor] 的迭代器modules()
named_modules(prefix='')
: 返回由浅入深遍历所有子模块的迭代器children()
named_children(prefix='')
: 返回遍历所有一级子模块的迭代器parameters(recurse=True)
named_parameters(prefix='', recurse=True)
: 返回所有参数(默认递归迭代所有子模块)buffers(recurse=True)
named_buffers(prefix='', recurse=True)
: 返回模块的 buffers(模型状态参数,不用梯度下降更新)
根据名字获取
submodule
parameter
buffer
:get_***(target)
例如模块 A:
1
2
3
4
5
6
7
8A(
(net_b): Module(
(net_c): Module(
(conv): Conv2d(16, 33, kernel_size=(3, 3), stride=(2, 2))
)
(linear): Linear(in_features=100, out_features=200, bias=True)
)
)可以通过
get_submodule("net_b.linear")
get_submodule("net_b.net_c.conv")
等获取相应子模块注册
module
parameter
buffer
:register_***(name, [content])
- 更改参数类型/设备:
model.type(dst_type)
或model.to(type/device)
,包括model.half()
model.cuda()
等 - 载入模型参数:
model.load_state_dict(state_dict)
- 必须实现
模型容器(同样继承自
nn.Module
)nn.Sequetial
:顺序性,按照顺序包装多个网络层,常用于 block 构建nn.ModuleList
:迭代性,像 python 的 list 一样包装多个网络层,包含append()
extend()
insert()
等方法,常用于大量重复网络构建,通过 for 循环实现重复构建nn.ModuleDict
:索引性,像 python 的 dict一样包装多个网络层,通过 (key, value) 的方式为每个网络层指定名称,常用于可选择的网络层****
模型组件
卷积层
- 二维卷积:
nn.Cov2d(self, in_channels, out_channels, kernel_size, stride=1, padding=1, dilation=1, groups=1, bias=True, padding_mode='zeros')
- in_channels:输入通道数
- out_channels:输出通道数,等价于卷积核个数
- kernel_size:卷积核尺寸
- stride:步长
- padding:填充宽度,主要是为了调整输出的特征图大小,一般把 padding 设置合适的值后,保持输入和输出的图像尺寸不变。
- dilation:空洞卷积大小,默认为 1,这时是标准卷积,常用于图像分割任务中,主要是为了提升感受野
- groups:分组卷积设置,主要是为了模型的轻量化,如在 ShuffleNet、MobileNet、SqueezeNet 中用到
- bias:偏置
- 转置卷积:
nn.ConvTranspose2d(self, in_channels, out_channels, kernel_size, stride=1, padding=0, output_padding=0, groups=1, bias=True, dilation=1, padding_mode='zeros')
- 二维卷积:
池化层
最大池化:
nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
- kernel_size : 池化核尺寸(无重叠,即通常 stride=kernel_size)
- return_indices:为 True 时,返回最大池化所使用的像素的索引,这些记录的索引通常在反最大池化时使用,把小的特征图反池化到大的特征图时,每一个像素放在哪个位置
下图 (a) 表示反池化,(b) 表示上采样,(c) 表示反卷积:
平均池化:
nn.AvgPool2d(...)
最大值反池化:
nn.MaxUnpool2d(kernel_size, stride=None, padding=0)
nn.init
参数初始化目的:保持每一层输出的方差不能太大也不能太小
纯线性层:正态初始化,标准差 $\sigma(W)=\sqrt{\cfrac{1}{n}}$
饱和激活函数(sigmoid/tanh):Xavier 初始化
ReLU 激活函数:Kaiming 初始化
损失函数(同样继承
nn.Module
,因此也可视作一个网络层)- 基本概念
- 损失函数(Loss Function):一个样本的模型输出与真实标签的差异
- 代价函数(Cost Function):整个样本集的模型输出与真实标签的差异,是所有样本损失函数的平均值
- 目标函数(Objective Function):代价函数加上正则项
- 基本概念
torch.optim
优化器- 基本方法
- 初始化:给定待优化的模型参数、学习率
optimizer.step()
: 进行一步迭代optimizer.zero_grad()
: 将所有待优化的张量梯度置零optimizer.state_dict()
optimizer.load_state_dict(state_dict)
: 获取/填入优化器的 state_dict
- 常用优化器
optim.SGD(params, lr, momentum=0, dampening=0, weight_decay=0)
- params:管理的参数组
- lr:初始学习率
- momentum:动量系数
- weight_decay:L2 正则化系数
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
torch.optim.lr_schedule
StepLR(optimizer, step_size**=**30, gamma**=**0.1)
: 每经过 step_size 轮,将此前的学习率乘以 gammaMultiStepLR(optimizer, milestones**=**[30,80], gamma**=**0.5)
: 在每个 milestone 时,将此前学习率乘以 gammaExponentialLR(optimizer, gamma**=**0.9)
: 每一轮都将学习率乘以 gamma 倍LinearLR(optimizer,start_factor**=**1,end_factor**=**0.1,total_iters**=**80)
: 给定起始 factor 和最终的 factor,LinearLR会在中间阶段做线性插值,比如学习率为 0.1,起始 factor 为 1,最终的 factor 为 0.1,那么第 0 次迭代,学习率将为 0.1,最终轮学习率为 0.01。下面设置的总轮数 total_iters 为 80,所以超过 80 时,学习率恒为 0.01
- 基本方法
正则化
模型其他操作
Config、Logging & Visualization
TensorBoard
动图绘制:
matplotlib.animation
PyTorch Lightning
Huggingface
- transformers
- diffusers
Gradio
简单交互场景
gr.Interface
:Gradio Interface DocsInterface
类三大初始化参数:fn
: 包装的 python 函数inputs
: 输入组件类型outputs
: 输出组件类型支持多输入多输出,输入列表中的每个组件按顺序对应于函数的一个参数,输出列表中的每个组件按顺序排列对应于函数返回的一个值
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import gradio as gr
#该函数有3个输入参数和2个输出参数
def greet(name, is_morning, temperature):
salutation = "Good morning" if is_morning else "Good evening"
greeting = f"{salutation} {name}. It is {temperature} degrees today"
celsius = (temperature - 32) * 5 / 9
return greeting, round(celsius, 2)
demo = gr.Interface(
fn=greet,
#按照处理程序设置输入组件
inputs=["text", "checkbox", gr.Slider(0, 100)],
#按照处理程序设置输出组件
outputs=["text", "number"],
)
demo.launch()
Interface
类其他参数live
: 动态界面接口,设置live=True
时,不会有submit
按钮,只要输入发生改变,结果马上发生改变title
: 网页标题description
: 左上角描述文字examples
: 参数示例列表article
: 左下角补充文字
Interface.launch()
方法返回三个值:app
: 为 gradio 演示提供支持的 FastAPI 应用程序local_url
: 本地地址share_url
: 公共地址,当share=True
时生成
Interface
状态与交互- 全局变量:好处是在调用函数后仍然能够保存,例如通过全局变量从外部加载一个大型模型,并在函数内部使用它,以便每次函数调用都不需要重新加载模型
- 会话状态:数据在一个页面会话中的多次提交中持久存在,常用于聊天机器人(
gr.Chatbot
) - 流模式:当输入是实时视频流或者音频流时,数据需要不停地发送到后端,这是可以设置
gr.Image(streaming=True)
模式处理数据
定制交互界面
Blocks
: Gradio Blocks DocsBlocks
是一种比Interface
更灵活的交互方式,允许控制组件在页面上出现的位置,处理复杂的数据流(例如,输出可以作为其他函数的输入),并根据用户交互更新组件的属性可见性Blocks
需要with语句添加组件,如果不设置布局方式,那么组件将按照创建的顺序垂直出现在应用程序中简单例子
1
2
3
4
5
6
7
8
9
10
11
12
13import gradio as gr
def greet(name):
return "Hello " + name + "!"
with gr.Blocks() as demo:
#设置输入组件
name = gr.Textbox(label="Name")
# 设置输出组件
output = gr.Textbox(label="Output Box")
#设置按钮
greet_btn = gr.Button("Greet")
#设置按钮点击事件
greet_btn.click(fn=greet, inputs=name, outputs=output)
demo.launch()复杂多模块例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29import numpy as np
import gradio as gr
def flip_text(x):
return x[::-1]
def flip_image(x):
return np.fliplr(x)
with gr.Blocks() as demo:
#用markdown语法编辑输出一段话
gr.Markdown("Flip text or image files using this demo.")
# 设置tab选项卡
with gr.Tab("Flip Text"):
#Blocks特有组件,设置所有子组件按垂直排列
#垂直排列是默认情况,不加也没关系
with gr.Column():
text_input = gr.Textbox()
text_output = gr.Textbox()
text_button = gr.Button("Flip")
with gr.Tab("Flip Image"):
#Blocks特有组件,设置所有子组件按水平排列
with gr.Row():
image_input = gr.Image()
image_output = gr.Image()
image_button = gr.Button("Flip")
#设置折叠内容
with gr.Accordion("Open for More!"):
gr.Markdown("Look at me...")
text_button.click(flip_text, inputs=text_input, outputs=text_output)
image_button.click(flip_image, inputs=image_input, outputs=image_output)
demo.launch()Blocks
复杂控制多个数据流:同一个
gr
组件有可能既可以做输入又可以做输出,对于多个数据流,可以通过多个控制组件进行控制,例如:1
2
3
4
5
6
7
8
9
10
11
12
13import gradio as gr
def increase(num):
return num + 1
with gr.Blocks() as demo:
a = gr.Number(label="a")
b = gr.Number(label="b")
# 要想b>a,则使得b = a+1
atob = gr.Button("b > a")
atob.click(increase, a, b)
# 要想a>b,则使得a = b+1
btoa = gr.Button("a > b")
btoa.click(increase, b, a)
demo.launch()多个输出值:可以通过列表或字典形式处理
组件配置修改:事件监听器函数的返回值通常是相应的输出组件的更新值。有时我们也想更新组件的配置,比如说可见性;在这种情况下,我们可以通过返回
gr.update
函数更新组件的配置,例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import gradio as gr
def change_textbox(choice):
#根据不同输入对输出控件进行更新
if choice == "short":
return gr.update(lines=2, visible=True, value="Short story: ")
elif choice == "long":
return gr.update(lines=8, visible=True, value="Long story...")
else:
return gr.update(visible=False)
with gr.Blocks() as demo:
radio = gr.Radio(
["short", "long", "none"], label="Essay Length to Write?"
)
text = gr.Textbox(lines=2, interactive=True)
radio.change(fn=change_textbox, inputs=radio, outputs=text)
demo.launch()
Blocks
布局- 默认情况下组件垂直排列
gr.Row()
会将组件水平排列,保持同等高度scale
可设置与相邻列相比的相对宽度;例如,如果列A的比例为2,列B的比例为1,则A的宽度将是B的两倍min_width
可设置最小宽度,防止列太窄
-
IO 组件:
"text"
,"image"
,gr.Textbox
,gr.Image
,gr.DataFrame
,gr.Dropdown
,gr.Number
,gr.Markdown
,gr.Files
等- 例如:当使用
Image
作为组件输入时,默认情况下,函数将收到一个维度为(w,h,3)的 numpy 数组,按照 RGB 通道排列;也可以设置输入类型type
,比如type=filepath
设置传入处理图像的路径
- 例如:当使用
控制组件:
gr.Button
等布局组件:
gr.Tab
,gr.Row
,gr.Column
等简单例子
1
2
3
4
5
6
7
8
9
10
11import gradio as gr
def greet(name):
return "Hello " + name + "!"
demo = gr.Interface(
fn=greet,
# 自定义输入框
# 具体设置方法查看官方文档
inputs=gr.Textbox(lines=3, placeholder="Name Here...",label="my input"),
outputs="text",
)
demo.launch()组件设置
样式设置:组件
.style
可以设置该组件样式,例如:1
img = gr.Image("lion.jpg").style(height='24', rounded=False)
交互属性设置:输入组件默认都是可编辑的,输出组件默认都是不可编辑的;可以通过
interactive
参数更改输入输出组件交互属性事件设置:我们可以为不同的组件设置不同事件,如为输入组件添加
change
事件,为控制按钮组件加click
事件等;事件触发时会执行所设定的函数1
2
3
4
5
6
7
8
9
10
11
12import gradio as gr
def greet(name):
return "Hello " + name + "!"
with gr.Blocks() as demo:
name = gr.Textbox(label="Name")
# 不可交互
# output = gr.Textbox(label="Output Box")
# 可交互
output = gr.Textbox(label="Output", interactive=True)
greet_btn = gr.Button("Greet")
greet_btn.click(fn=greet, inputs=name, outputs=output)
demo.launch()1
2
3
4
5
6
7
8
9
10
11
12
13
14import gradio as gr
def welcome(name):
return f"Welcome to Gradio, {name}!"
with gr.Blocks() as demo:
gr.Markdown(
"""
# Hello World!
Start typing below to see the output.
""")
inp = gr.Textbox(placeholder="What is your name?")
out = gr.Textbox()
#设置change事件
inp.change(fn = welcome, inputs = inp, outputs = out)
demo.launch()
其他辅助功能
队列
Interface(...).queue()
:如果函数推理时间较长,比如目标检测;或者应用程序处理流量过大,则需要使用queue方法进行排队;queue方法使用websockets,可以防止网络超时生成器:在某些情况下,我们可能想显示一连串的输出,而不是单一的输出。例如,可能有一个图像生成模型,如果你显示在每个步骤中生成的图像,从而得到最终的图像,这时可以向Gradio提供一个生成器函数,而不是一个常规函数,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import gradio as gr
import numpy as np
import time
#生成steps张图片,每隔1秒钟返回
def fake_diffusion(steps):
for _ in range(steps):
time.sleep(1)
image = np.random.randint(255, size=(300, 600, 3))
yield image
demo = gr.Interface(fake_diffusion,
#设置滑窗,最小值为1,最大值为10,初始值为3,每次改动增减1位
inputs=gr.Slider(1, 10, value=3, step=1),
outputs="image")
#生成器必须要queue函数
demo.queue()
demo.launch()