Pytorch-学习记录-1-Tensor

1. 张量 (Tensor):

数学中指的是多维数组;

torch.Tensor

复制代码
data: 被封装的 Tensor
dtype: 张量的数据类型
shape: 张量的形状
device: 张量所在的设备,GPU/CPU
requires_grad: 指示是否需要计算梯度
grad: data 的梯度
grad_fn: 创建 Tensor 的 Function,是自动求导的关键
is_leaf: 指示是否是叶子结点 (叶子结点指的是用户创建的节点,比如 y=(x+w)*(w+1)中,x和w就是叶子结点【可以用计算图来清楚地表示该过程】)

2. 创建张量:

2.1 直接创建:

2.1.1 从 data 创建 tensor

复制代码
data: 可以是 list, numpy
dtype: 数据类型,默认与data一致
device: 所在设备
requires_grad: 是否需要梯度
pin_memory: 是否存于锁页内存
python 复制代码
# 创建方法
torch.tensor(
    data,
    dtype=None,
    device=None,
    requires_grad=False,
    pin_memory=False
)

2.1.2 从 numpy 创建 tensor

torch.from_numpy()创建的tensor与原始的ndarray共享内存,修改其中一个,另一个也会变。

python 复制代码
# 创建方法
torch.from_numpy(ndarray)
python 复制代码
# 举例
import torch
import numpy as np

# 直接创建
## torch.tensor()
arr = np.ones((3,3))
t = torch.tensor(arr, 
                 dtype=torch.float32, 
                 device="mps") # 把张量放到 GPU 上 (mac M1)

print(t.dtype)
print(t)


## torch.from_numpy()
arr = np.array([[1,2,3],[4,5,6]])
t = torch.from_numpy(arr)
print(t.dtype)
print(t)
复制代码
torch.float32
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], device='mps:0')
torch.int64
tensor([[1, 2, 3],
        [4, 5, 6]])

2.2 根据数值进行创建:

2.2.1 torch.zeros()

创建全0张量

复制代码
size: 张量的形状
out: 输出的张量 (暂时可以不考虑)
layout:内存中布局形式,有 strided (通常情况下使用), sparse_coo (读取稀疏矩阵会用到)
device: 所在设备
requires_grad: 是否需要梯度
python 复制代码
# 创建方法
torch.zeros(
    *size,
    out=None,
    dtype=None,
    layout=torch.strided,
    device=False,
    requires_grad=False
)

2.2.2 torch.zeros_lisk()

根据 input 形状创建全0张量

复制代码
input: 创建与 input 同形状的张量;
dtype: 数据类型;
layout: 内存中的布局形式;
python 复制代码
# 创建方法
torch.zeros_like(
    input,
    dtype=None,
    layout=None,
    device=None,
    requires_grad=False
)

2.2.3 torch.ones()torch.ones_like()

创建全1张量

2.2.4 torch.full()torch.full_like()

创建自定义数值的张量

复制代码
size: 张量形状
fill_value: 张量的值

2.2.5 torch.arange()

根据数列创建等差1维张量,[start, end)

复制代码
start: 起始值
end: 结束值
step: 数列公差,默认为1

2.2.6torch.linspace()

创建均分的1维张量,[start, end]

复制代码
start: 起始值
end: 结束值
step: 数列长度

2.2.7torch.logspace()

创建对数均分的1D张量

复制代码
start: 起始值
end: 结束值
steps: 数列长度
base: 对数函数的底,默认为10
python 复制代码
# 创建方法
torch.logspace(
    start,
    end,
    steps=100,
    base=10.0,
    out=None,
    dtype=None,
    layout=torch.strided,
    deivce=None,
    requires_grad=False
)

2.2.8 torch.eye()

创建单位对角矩阵 (2D张量)

复制代码
n: 矩阵行数
m: 矩阵列数
python 复制代码
# 创建方法
torch.eye(
    n,
    m=None,
    out=None,
    dtype=None,
    layout=torch.strided,
    device=None,
    requires_grad=False
)
python 复制代码
# 举例
t = torch.zeros((3,3))
print(t.dtype)
print(t)

t = torch.full((3,3),2) # 创建3x3的全2张量
print(t)

t = torch.arange(2, 8, 2)
print(t)

t = torch.linspace(2, 10, 5)
print(t)
复制代码
torch.float32
tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])
tensor([[2, 2, 2],
        [2, 2, 2],
        [2, 2, 2]])
tensor([2, 4, 6])
tensor([ 2.,  4.,  6.,  8., 10.])

2.3 根据概率分布创建张量:

2.3.1 torch.normal()

生成正态分布(高斯分布)

四种模式:

mean 标量 std 标量
mean 标量 std 张量
mean 张量 std 标量
mean 张量 std 张量
python 复制代码
# 张量
torch.normal(
    mean,
    std,
    out=None
)

# 标量
torch.normal(
    mean,
    std,
    size,
    out=None
)

2.3.2 torch.randn(), torch.randn_like()

生成标准正态分布

python 复制代码
torch.randn(
    *size, ## 张量形状
    out=None,
    dtype=None,
    layout=torch.strided,
    device=None,
    requires_grad=False
)

2.3.3 torch.rand(), torch.rand_like()

在[0,1)区间上生成均匀分布

python 复制代码
torch.rand(
    *size, ## 张量形状
    out=None,
    dtype=None,
    layout=torch.strided,
    device=None,
    requires_grad=False
)

2.3.4 torch.randint(), torch.randint_like()

在 [low, high) 区间上生成整数均匀分布

python 复制代码
torch.randint(
    low,
    high,
    size,
    out=None,
    dtype=None,
    layout=torch.strided,
    device=None,
    requires_grad=False
)

2.3.5 torch.randperm()

生成0到n-1的随机排列

复制代码
n: 张量长度
python 复制代码
torch.randperm(
    n,
    out=None,
    dtype=torch.int64,
    layout=torch.strided,
    device=None,
    requires_grad=False
)

2.3.6torch.bernoulli()

以 input 为概率,生成伯努利分布(0-1分布,两点分布)

python 复制代码
torch.bernoulli(
    input,
    *,
    generator=None,
    out=None
)

3. 张量的操作:

张量的操作:拼接、切分、索引和变换:

1. 拼接:

1.1 torch.cat()

将张量按维度 dim 进行拼接 (不回扩张张量维度)

python 复制代码
torch.cat(
    tensors, ## 张量序列
    dim, ## 要拼接的维度
    out=None
)

1.2 torch.stack()

在新创建的维度 dim 上进行拼接 (会扩张张量的维度)

python 复制代码
torch.stack(
    tensors, ## 张量序列
    dim=0, ## 要拼接的维度
    out=None
)
python 复制代码
import torch

t = torch.ones((2,3))

# torch.cat()
t1 = torch.cat([t,t], dim=0) ## 按照 dim=0 对t进行拼接,得到(4,3)

# torch.stack()
t2 = torch.stack([t,t], dim=0) ## 按照 dim=0 对t进行拼接,得到(2,2,3),会在dim=0创建一个新维度

print("t1 shape: ", t1.shape)
print("t2 shape: ", t2.shape)
复制代码
t1 shape:  torch.Size([4, 3])
t2 shape:  torch.Size([2, 2, 3])

2. 切分

2.1 torch.chunk()

将张量按照维度 dim 进行平均切分,返回张量列表 (如果不能整除,最后一份张量小于其他张量)

python 复制代码
torch.chunk(
    input, ## 要切分的张量
    chunks, ## 要切分的份数
    dim=0 ## 要切分的维度
)

2.2 torch.split()

将张量按照维度 dim 进行切分,返回张量列表

python 复制代码
torch.split(
    tensor, ## 要切分的张量
    split_size_of_sections, ## 为 int 时,表示每一份的长度;为 list 时,按 list 元素切分
    dim=0 ## 要切分的维度
)
python 复制代码
t1 = torch.ones((2,5))

# torch.chunk()
list_of_tensors = torch.chunk(t1, dim=1, chunks=2) ## 5不能被2整除,所以最后一个张量形状小雨前面的张量
for idx, t in enumerate(list_of_tensors):
    print("order {}, shape is {}".format(idx+1, t.shape))

# torch.split()
list_of_tensors = torch.split(t1, [2,1,2], dim=1)
for idx, t in enumerate(list_of_tensors):
    print("order {}, shape is {}".format(idx+1, t.shape))
复制代码
order 1, shape is torch.Size([2, 3])
order 2, shape is torch.Size([2, 2])
order 1, shape is torch.Size([2, 2])
order 2, shape is torch.Size([2, 1])
order 3, shape is torch.Size([2, 2])

3. 索引

3.1 torch.index_select()

在维度 dim 上,按照 index 索引数据,返回依index索引数据拼接的张量 (先索引,再拼接)

python 复制代码
torch.index_select(
    input, ## 要索引的张量
    dim, ## 要索引的维度
    index, ## 要索引数据的序号,数据类型必须是 torch.long
    out=None
)

3.2 torch.masked_select()

按照 mask 中的 True 进行索引,返回一维张量

python 复制代码
torch.masked_select(
    input,
    mask, ## 与 input 同形状的布尔类型张量
    out=None
)
python 复制代码
# torch_index_select()
t = torch.randint(0,9,size=(3,3))
idx = torch.tensor([0,2],dtype=torch.long)
t_select = torch.index_select(t, dim=1, index=idx)
print(t)
print(t_select)

# torch.masked_select()
mask = t.ge(5) ## 表示张量中 >= 5的元素;ge()表示大于等于;gt()表示大于;le()表示小于等于;lt()表示小于
t_select = torch.masked_select(t, mask=mask)
print(t)
print(mask)
print(t_select)
复制代码
tensor([[3, 8, 1],
        [6, 0, 0],
        [7, 6, 8]])
tensor([[3, 1],
        [6, 0],
        [7, 8]])
tensor([[3, 8, 1],
        [6, 0, 0],
        [7, 6, 8]])
tensor([[False,  True, False],
        [ True, False, False],
        [ True,  True,  True]])
tensor([8, 6, 7, 6, 8])

4. 变换

4.1 torch.reshape()

变换张量形状 (张量在内存中是连续时,新张量与input共享数据内存)

python 复制代码
torch.reshape(
    input,
    shape ## 新张量的形状
)

4.2 torch.transpose()

交换张量的两个维度

python 复制代码
torch.transpose(
    input,
    dim0,
    dim1
)

4.3 torch.t()

2维张量转置

python 复制代码
torch.t(input)

4.4 torch.squeeze()

压缩长度为1的维度

python 复制代码
torch.squeeze(
    input,
    dim=None, ## 若 dim=None,则移除所有长度为1的轴;若指定维度而且维度长度为1,则可以移除该维度
    out=None
)

4.5 torch.unsqueeze()

根据 dim 扩展维度

python 复制代码
torch.unsqueeze(
    input,
    dim,
    out=None
)
python 复制代码
## torch.reshape

t = torch.randperm(8)
t_reshape = torch.reshape(t, (2,4)) ## 如果是 (-1,2,2),那么-1表示的是:不需要关心-1处的维度是多少,根据后两个维度进行计算得到(比如:这里就是 8/2/2 = 2,即-1处的维度是2)

print(t.shape)
print(t_reshape.shape)


## torch.transpose()
t = torch.rand((2,3,4))
t_transpose = torch.transpose(t, dim0=1, dim1=2) ## 把第1维和第2维进行交换
print(t.shape)
print(t_transpose.shape)


## torch.squeeze()
t = torch.rand((1,2,3,1))
t_squeeze = torch.squeeze(t)
t_squeeze_0 = torch.squeeze(t,dim=0)
t_squeeze_1 = torch.squeeze(t,dim=1)
print(t_squeeze.shape)
print(t_squeeze_0.shape)
print(t_squeeze_1.shape)


## torch.unsqueeze()
t = torch.rand((2,3))
t_unsqueeze = torch.unsqueeze(t, dim=2)
print(t_unsqueeze.shape)
复制代码
torch.Size([8])
torch.Size([2, 4])
torch.Size([2, 3, 4])
torch.Size([2, 4, 3])
torch.Size([2, 3])
torch.Size([2, 3, 1])
torch.Size([1, 2, 3, 1])
torch.Size([2, 3, 1])

3.2 张量的数学运算:

加减乘除

python 复制代码
torch.add()
torch.addcdiv()
torch.addcmul()
torch.sub()
torch.div()
torch.mul()

重点: torch.add()torch.addcmul()

torch.add(): 逐元素计算 input + alpha x other

python 复制代码
torch.add(
    input, ## 第一个张量
    alpha=1, ## 乘项因子 (alpha x other + input)
    other, ## 第二个张量
    out=None
)

torch.addcmul()

out = input + value x tensor1 x tensor2

python 复制代码
torch.addcmul(
    input,
    value=1.
    tensor1,
    tensor2,
    out=None
)

对数、指数、幂函数

python 复制代码
torch.log(input, out=None)
torch.log10(input, out=None)
torch.log2(input, out=None)
torch.exp(input, out=None)
torch.pow()

三角函数

python 复制代码
torch.abs(input, out=None)
torch.acos(input, out=None)
torch.cosh(input, out=None)
torch.cos(input, out=None)
torch.asin(input, out=None)
torch.atan(input, out=None)
torch.atan2(input, other=None, out=None)

4. 计算图与动态图

1. 计算图

计算图是用来描述运算的有向无环图,包括两个主要元素:节点和边。节点表示数据(比如 向量、矩阵、张量);边表示运算(比如 加减乘除卷积等)。
叶子结点:指的是由用户创建的节点,其他非叶子结点都是由叶子结点通过直接或间接的运算得到的。(之所以会有叶子结点的概念,是因为梯度反向传播结束后,只有叶子结点的梯度会被保留,非叶子结点的梯度会被从内存中释放掉。可以通过.is_leaf()方法查看是否为叶子结点)。
如果想要使用非叶子结点的梯度,实现方法是 在计算梯度之后,用.retain_grad()方法保存非叶子结点的梯度。

grad_fn: 记录创建该张量(非叶子结点)时所用的方法,比如 y=a*b,那么 y.grad_fn = <MulBackward0>

2. 动态图

计算图可以根据搭建方式的不同,分为:动态图 (运算与搭建同时进行,灵活,易调节,比如 pytorch) 和静态图 (先搭建,后运算,高效,不灵活,比如 tensorflow)。

5. 自动求导系统 autograd

1. torch.autograd.backward

自动计算图中各个结点的梯度

python 复制代码
torch.autograd.backward(
    tensors, ## 用于求导的张量(比如 loss)
    grad_tensors=None, ## 多梯度权重
    retain_grap=None, ## 保存计算图
    create_graph=False ## 创建导数的计算图,同于高阶求导
)

2. torch.autograd.grad

求取梯度

python 复制代码
torch.autograd.grad(
    outputs, ## 用于求导的张量 (比如 loss)
    inputs, ## 需要梯度的张量
    grad_outputs=None, ## 多梯度权重 
    retain_graph=None, ## 保存计算图
    create_graph=False ## 创建导数计算图,用于高阶求导
)

3. Pytorch 自动求导系统中有3个需要注意的点:

  1. 梯度不自动清零 (手动清零 .grad.zero_());

  2. 依赖于叶子结点结点,requires_grad 默认为 True;

  3. 叶子结点不可执行 in-place,即叶子结点不能执行原位操作;

4. 机器学习模型的5个训练步骤:

数据, 模型, 损失函数, 优化器, 迭代训练

6. DataLoader 与 Dataset

复制代码
数据
    数据收集

    数据划分

    数据读取
        DataLoader
            Sampler: 生成索引
            Dataset: 根据索引读取样本特征以及标签

    数据预处理

1. torch.utils.data.DataLoader

python 复制代码
DataLoader(
    dataset, ## Dataset类,决定数据从哪里读取以及如何读取
    batch_size=1, ## 批量大小
    shuffle=False, ## 每个 epoch 是否打乱顺序
    sampler=None,
    batch_sampler=None,
    num_workers=0, ## 是否多进程读取数据
    collate_fn=None,
    pin_memory=False,
    drop_last=False, ## 当样本数不能被 batchsize 整除时,是否舍弃最后一批数据
    timeout=0,
    worker_init_fn=None,
    multiprocessing_context=None
)

2. torch.utils.data.Dataset

Dataset抽象类,所有自定义的Dataset都需要继承它,并且需要复写 __getitem__()方法;getitem:接收一个索引,返回一个样本。

python 复制代码
class Dataset(object):
    def __getitem__(self, index):
        raise NotImplementedError

    def __add__(self, other):
        return ConcatDataset([self, other])

Example: 猫狗图片分类 ("./example_week1_Cat_Dog_classification/")

1. 参考代码:https://github.com/JansonYuan/Pytorch-Camp/

2. 猫狗数据集下载:https://www.kaggle.com/datasets/samuelcortinhas/cats-and-dogs-image-classification,但是将其中Test随机分成相等的两份,一份作为验证集,一份作为测试集(尽管测试集没有用到)。

3. 代码在 "./example_week1_Cat_Dog_classification/" 目录下。

4. 结果讨论:

4.1 作者原来的代码的结果:

复制代码
Training:Epoch[000/010] Iteration[010/010] Loss: 0.6204 Acc:60.62%
Valid:	 Epoch[000/010] Iteration[002/002] Loss: 0.4436 Acc:90.00%
Training:Epoch[001/010] Iteration[010/010] Loss: 0.3601 Acc:90.62%
Valid:	 Epoch[001/010] Iteration[002/002] Loss: 0.0461 Acc:100.00%
Training:Epoch[002/010] Iteration[010/010] Loss: 0.0788 Acc:98.12%
Valid:	 Epoch[002/010] Iteration[002/002] Loss: 0.0234 Acc:100.00%
Training:Epoch[003/010] Iteration[010/010] Loss: 0.0208 Acc:99.38%
Valid:	 Epoch[003/010] Iteration[002/002] Loss: 0.0022 Acc:100.00%
Training:Epoch[004/010] Iteration[010/010] Loss: 0.0042 Acc:100.00%
Valid:	 Epoch[004/010] Iteration[002/002] Loss: 0.0018 Acc:100.00%
Training:Epoch[005/010] Iteration[010/010] Loss: 0.0143 Acc:99.38%
Valid:	 Epoch[005/010] Iteration[002/002] Loss: 0.0002 Acc:100.00%
Training:Epoch[006/010] Iteration[010/010] Loss: 0.0220 Acc:98.75%
Valid:	 Epoch[006/010] Iteration[002/002] Loss: 0.0004 Acc:100.00%
Training:Epoch[007/010] Iteration[010/010] Loss: 0.0008 Acc:100.00%
Valid:	 Epoch[007/010] Iteration[002/002] Loss: 0.0013 Acc:100.00%
Training:Epoch[008/010] Iteration[010/010] Loss: 0.0083 Acc:99.38%
Valid:	 Epoch[008/010] Iteration[002/002] Loss: 0.0001 Acc:100.00%
Training:Epoch[009/010] Iteration[010/010] Loss: 0.0049 Acc:100.00%
Valid:	 Epoch[009/010] Iteration[002/002] Loss: 0.0000 Acc:100.00%

4.2 用 "./example_week1_Cat_Dog_classification/train_lenet.py" 代码处理 Pytorch-Camp 的结果和上面的是一致的(代码修改后,与 Pytorch-Camp 提供的代码可以对 RMB 实现同样的分类效果),如下所示:

复制代码
Train:   Epoch[000/010] Iteration[010/010] Loss: 0.6204 Acc: 60.62%
Valid:   Epoch[000/010] Iteration[002/002] Loss: 0.4436 Acc: 90.00%
Train:   Epoch[001/010] Iteration[010/010] Loss: 0.3601 Acc: 90.62%
Valid:   Epoch[001/010] Iteration[002/002] Loss: 0.0461 Acc: 100.00%
Train:   Epoch[002/010] Iteration[010/010] Loss: 0.0788 Acc: 98.12%
Valid:   Epoch[002/010] Iteration[002/002] Loss: 0.0234 Acc: 100.00%
Train:   Epoch[003/010] Iteration[010/010] Loss: 0.0208 Acc: 99.38%
Valid:   Epoch[003/010] Iteration[002/002] Loss: 0.0022 Acc: 100.00%
Train:   Epoch[004/010] Iteration[010/010] Loss: 0.0042 Acc: 100.00%
Valid:   Epoch[004/010] Iteration[002/002] Loss: 0.0018 Acc: 100.00%
Train:   Epoch[005/010] Iteration[010/010] Loss: 0.0143 Acc: 99.38%
Valid:   Epoch[005/010] Iteration[002/002] Loss: 0.0002 Acc: 100.00%
Train:   Epoch[006/010] Iteration[010/010] Loss: 0.0220 Acc: 98.75%
Valid:   Epoch[006/010] Iteration[002/002] Loss: 0.0004 Acc: 100.00%
Train:   Epoch[007/010] Iteration[010/010] Loss: 0.0008 Acc: 100.00%
Valid:   Epoch[007/010] Iteration[002/002] Loss: 0.0013 Acc: 100.00%
Train:   Epoch[008/010] Iteration[010/010] Loss: 0.0083 Acc: 99.38%
Valid:   Epoch[008/010] Iteration[002/002] Loss: 0.0001 Acc: 100.00%
Train:   Epoch[009/010] Iteration[010/010] Loss: 0.0049 Acc: 100.00%
Valid:   Epoch[009/010] Iteration[002/002] Loss: 0.0000 Acc: 100.00%

4.3 将数据换成猫狗数据集之后,结果如下:

复制代码
Train:   Epoch[000/010] Iteration[035/035] Loss: 0.7151 Acc: 53.50%
Valid:   Epoch[000/010] Iteration[005/005] Loss: 0.6898 Acc: 51.43%
Train:   Epoch[001/010] Iteration[035/035] Loss: 0.6954 Acc: 50.09%
Valid:   Epoch[001/010] Iteration[005/005] Loss: 0.7321 Acc: 41.43%
Train:   Epoch[002/010] Iteration[035/035] Loss: 0.6885 Acc: 53.86%
Valid:   Epoch[002/010] Iteration[005/005] Loss: 0.6954 Acc: 52.86%
Train:   Epoch[003/010] Iteration[035/035] Loss: 0.6813 Acc: 58.53%
Valid:   Epoch[003/010] Iteration[005/005] Loss: 0.6909 Acc: 55.71%
Train:   Epoch[004/010] Iteration[035/035] Loss: 0.6832 Acc: 57.99%
Valid:   Epoch[004/010] Iteration[005/005] Loss: 0.6749 Acc: 52.86%
Train:   Epoch[005/010] Iteration[035/035] Loss: 0.6838 Acc: 58.35%
Valid:   Epoch[005/010] Iteration[005/005] Loss: 0.6865 Acc: 50.00%
Train:   Epoch[006/010] Iteration[035/035] Loss: 0.6729 Acc: 58.89%
Valid:   Epoch[006/010] Iteration[005/005] Loss: 0.7097 Acc: 57.14%
Train:   Epoch[007/010] Iteration[035/035] Loss: 0.6700 Acc: 61.58%
Valid:   Epoch[007/010] Iteration[005/005] Loss: 0.7361 Acc: 47.14%
Train:   Epoch[008/010] Iteration[035/035] Loss: 0.6721 Acc: 59.07%
Valid:   Epoch[008/010] Iteration[005/005] Loss: 0.7180 Acc: 52.86%
Train:   Epoch[009/010] Iteration[035/035] Loss: 0.6476 Acc: 63.91%
Valid:   Epoch[009/010] Iteration[005/005] Loss: 0.7193 Acc: 57.14%

结论:

1.直接将 Pytorch-Camp 处理 RMB 分类问题时用到的 LeNet模型用于猫狗数据集分类,从结果来看是不合适的,可能需要对 LeNet进行调整(我猜后面可能会学到该如何调整网络结构)。

2.从这个例子中进一步理解了训练一个神经网络的过程:数据、模型、损失函数、优化器、训练,同时也了解了每个过程中所涉及到的一些细节的处理。

3.目前存在的问题就是:和第1点中提到的一样,对于不同的实际问题,该如何构建合理的深度学习网络(有哪些需要微调的地方?为什么在这个地方进行微调、有什么依据?)。

Pytorch 数据预处理模块 ------ transforms

因为不搞计算机视觉,所以 transforms 这部分的内容暂时不学习...

复制代码
torchvision: 计算机视觉工具包;

torchvision.transforms: 常用的图像预处理方法;
torchvision.datasets: 常用数据集的dataset实现,比如 MNIST, CIFAR-10, ImageNet等;
torchvision.model: 常用的模型预训练,比如 AlexNet, VGG, ResNet, GoogLeNet等;

torchvision.transforms

数据中心化,标准化,缩放,裁剪,旋转,填充,翻转,噪声添加,灰度变换,线性变换,仿射变换,亮度、饱和度及对比对变换。

相关推荐
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习
im_AMBER5 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J5 天前
从“Hello World“ 开始 C++
c语言·c++·学习