学习python/pytorch过程中遇到的知识点
Pytorch
torch.backends.cudnn.deterministic 和 torch.backends.cudnn.benchmark 这两个参数,用于固定算法,使每次结果都一样 。将deterministic置为True的话,每次返回的卷积算法将是确定的,即默认算法。如果配合上设置 Torch 的随机种子为固定值的话,应该可以保证每次网络的时候相同输入的输出是固定的。
benchmark作用是优化cudnn的,cuda可以加快程序速度,自动寻找最适合当前配置的高效算法,来达到优化效率的问题。因此将benchmark设置为True可以提高速度,但是每次的cuda底层最优算法可能不同,导致程序的结果可能有一些差异。
所以如果在训练阶段,可以如下设置:
def set_seed(myseed=2022):
# 设置numpy随机种子
np.random.seed(myseed)
# 设置python随机种子
random.seed(myseed)
# 固定cuda底层优化算法
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
# 设置cpu和cuda的随机数种子
torch.manual_seed(myseed)
# 用于自查自己的代码是否包含不确定的算法,报错说明有
torch.use_deterministic_algorithms(True)
# 固定环境变量种子
os.environ['PYTHONHASHSEED'] = str(myseed)
python

除此之外,在这里还有许多固定的算法示例可供参考。我的注释以及这篇博客都对此有所说明。无需再做介绍。
特别强调以确保实验结果具有可重复性。除了设置固定随机种子之外,还需要采取的具体措施是固定装载数据集的方法。具体的实现可以通过以下代码实现,并且还可以参考pytorch的官网
# 固定装载数据集初始化
def worker_init(worked_id):
worker_seed = torch.initial_seed() % 2**32
np.random.seed(worker_seed)
random.seed(worker_seed)
DataLoader(dataset, batch_size, shuffle, num_workers,pin_memory=True,worker_init_fn=worker_init)
python
为了在实验中生成随机数据,在每次运行时都需要调用随机函数,并根据设定好的种子参数来控制数据的生成结果。为了保证实验结果的一致性和可重复性,在设计算法时应尽量避免引入外部噪声干扰,并通过设置固定的有效数值种子可以保证实验结果的一致性和可重复性。在深度学习网络模型中初始的权重参数通常被初始化为均匀分布的随机数值 ,random.seed()、torch.manual_seed()、torch.cuda.manual_seed()、torch.cuda.manual_seed_all()这几种方法用于控制伪随机数的发生器以确保算法运行的一致性与稳定性;值得注意的是,在PyTorch 1.8及其之后版本中可以通过单一函数torch.manual_seed()同时实现对CPU与GPU设备上伪随机数的发生器的有效初始化以简化算法实现过程

关于Pytorch预设详细的参数初始化机制存在疑问,请参阅参考一文
关于报错的问题,请注意以下错误信息:RuntimeError: 在尝试向图中反向传播时又一次操作(或直接访问已释放的缓存张量)
feature = feature_extractor(total_data)
domain_pred = domain_classifier(feature)
loss = critical_domain(domain_pred, total_label)
running_D_loss += loss.detach().item() * len(train_data)
loss.backward()
domain_optimizer.step()
label_pred = label_predictor(feature[:train_data.shape[0]])
domain_pred = domain_classifier(feature)
python
在处理基于GAN或其他与GAN相关的运算时……
with作为一种上下文管理器,在Python中被广泛应用于程序的组织与管理中。它不仅能够用于打开和关闭文件(如通过with open(filename):),还常用于在神经网络训练过程中进行一些特定操作。例如,在神经网络的测试阶段(testing neural network),我们通常会在代码中添加如with torch.no_grad():这样的语句来避免反向传播过程中的自动求导操作(automatic differentiation)。这一操作的主要目的是为了节省内存资源(memory usage)。然而,在实际应用中,默认开启这一功能并不会影响最终的结果(result),但可能会对测试速度(test speed)造成一定影响。
squeeze属性主要负责对tensor变量进行维度压缩操作,并通过去除形状中存在长度为1的部分来减少整体存储空间的需求。例如,在输入一个形状为3x2x1x2x1的张量时,在应用squeeze()函数后其形状会被缩减至3x2x2的形式。值得注意的是,在处理过程中会自动过滤掉所有未达到长度要求的部分:如果未找到任何形状为1的维度,则不会执行squeeze操作参考
nn.Sequential:有序排列的组件集合,在输入顺序下按顺序组织起来执行计算

深度学习时会看见如下3行代码:
import gc
del train, train_label, train_x, train_y, val_x, val_y
gc.collect()
python
是用来清理内存的,加载的数据比较多,加载完了后将内存释放出来
当我们在使用PyTorch构建神经网络时,
会向代码中添加一行调用model.train(),
其作用是激活Batch Normalization层和Dropout操作(只有当模型架构设计中包含了这些组件时才会生效,
换句话说,
如果模型中没有设置Batch Normalization或Dropout,
那么这一行代码就不会发挥任何作用)。
而在构建神经网络的过程中,
测试阶段则不会激活Batch Normalization或Dropout功能,
而是继续采用训练阶段计算得到的均值与标准差。
(具体内容可参见李宏毅老师2021年讲解的这一部分内容)。
torch.max() 使用方法:在分类问题中,默认情况下我们通常会采用以下步骤来处理模型输出结果:首先通过 torch.max() 函数获取概率最大的类别预测值;其次通过查找最大值的位置索引来确定预测结果;最后将这些位置信息与真实标签进行比对从而计算模型准确率等指标。该函数有两个必要的参数:一个是输入张量(张量),另一个是运算维度(dim)。举个例子来说,在一个形状为 5x100 的二维数组中(即 tensor 维度是 5 行 100 列),如果我们设置 dim=1,则表示我们希望沿着每一行来进行运算操作;最终会得到一个只包含一行但列数与原张量一致的新数组。
类似于torch.argmax(),其主要区别在于返回的索引仅有一个下标
_, train_pred = torch.max(outputs, 1) 常见于这种情况中,在这一操作中_被用作占位符作用(通常用于忽略输出中的第一个元素)
tqdm是循环显示进度条的库,在训练比较慢时可以看看训练到哪里了

tqdm适配所有可迭代对象因此可以直接应用于数据加载器例如:
遍历数据加载器时可以采用:for data, target in tqdm(train_loader):
此外还可以将其与enumerate配合使用例如:
for i, x in enumerate(tqdm(dataloader)):
需要注意的是如果使用with tqdw as pbar这种方式则无需额外调用close函数而对于其他方式调用的tqdm则必须明确执行close操作参考1 参考2 参考3
在PyTorch中使用view()函数可以重塑张量的形状,在实际应用中能够方便地进行多维数据的处理与转换。其功能类似于Python库NumPy中的reshape函数,在具体实现上实现了类似的功能效果。通过调用该方法能够实现对现有数据结构的有效重构以满足后续算法需求。
在PyTorch框架中tensors的形状转换操作主要包括两种方法:.permute()和.view()。其中.permute()的作用是对张量的各个维数进行重新排列,并且这种操作仅限于对现有轴进行重排而非直接调整单个或多个原始轴的长度。而.view()方法则用于调整张量的形状,在此过程中实际上是将数据按照指定的新形状重新组织,并未直接更改原始轴的数量或长度。需要注意的是.permute()不支持对单个轴进行长度调整
torch.cat:该函数用于实现张量连接操作,在深度学习框架中常用于批量数据的处理与组合。具体而言:
- 使用dim参数指定连接方向,默认情况下沿最后一个维度进行连接
- dim=0表示按批次维度垂直方向连接
- dim=1表示按特征维度水平方向连接
- 该操作不改变输入张量的形状信息
np.hstack np.vstack 矩阵拼接 详解<>
for data in train_dataloader: 用于批量输入数据,在每次循环中处理一批数据。
其中data代表一批数据样本(即一个批次),而不是单个数据样本。
假设每批次数据量为200,则整个训练集(共200条记录)将被划分为10批。
在每一批次处理完毕后立即更新模型参数。
当所有批次处理完毕后完成一轮完整的训练周期。
此外,在训练过程中会设置 epochs 参数,默认情况下每个 epoch 表示完成一次完整的数据遍历。
统计样本预测正确的个数,以下两种都可以:
(A==B).sum() 或者 np.equal(A,B).sum()

在Dataset类中定义了两个核心方法:getitem(self, index) 和 len() 方法。
第一点指出这两个方法必须被重新实现。
第二点详细说明了 getitem(self, index) 方法中 index 参数是如何传递给该方法的:Dataloader实例会自动调用 Dataset 的 len() 方法获取总数据量,并将这个数值作为 index 参数的一部分传递给 getitem() 方法。
第三至第五点解释了 len() 方法的功能:它需要返回整个数据集所包含的具体样本数量;而 getitem() 方法则需要实现支持根据索引访问特定样本的功能并返回相应的数据。
最后一条补充说明了这些实现的重要性:由于后续的数据加载过程(例如通过Dataloader进行批量加载)会将所有实现的数据依次取出供训练或测试使用,
因此确保这些方法能够灵活地支持任意的数据顺序安排至关重要。
模型内部实现了前向传播功能,在参考文献[1]中详细介绍了该方法的工作原理及其实现细节
class NeuralNet(nn.Module):
……
model = NeuralNet()
pred = model(data_x)
python
因为在训练过程中, 我们会使用模型函数并将其与数据集中的样本进行匹配, 在这一阶段, 模型内部会通过前向传播机制完成计算任务.

关键点在于,在Pytorch中模型返回的结果是一个批量数据集上的平均损失值而不是单个样本的结果
for x,y in train_dataloader:
loss_train = criterion(pred, data_y)
total_loss_train += loss_train
avg_loss = total_loss_train / len(train_dataloader)
python
那么我们在计算平均损失时会将总和除以len(train_dataloader) ,而这个值代表的是数据加载器中的批次数量(即batch的数量)而不是整个训练数据集的大小。需要注意的一个问题是:如果我们想了解整个训练数据集的具体大小,则应使用len(train_dataloader.dataset)来代替。然而,在某些情况下(如当数据集无法完全被批处理覆盖时),如果我们选择使用len(train_dataloader.dataset)来代替的话,则需要特别注意前面提到的问题:由于你的len(train_dataloader) * batch_size未必精确等于整个数据集的大小(因为数据集的大小不一定是batch_size的整数倍),因此存在轻微误差的可能性。因此,在这种情况下推荐采用第一种方法来计算平均损失(即除以len(train_dataloader))。
pytorch detach() item() cpu() 的理解
detach()是阻断反向传播的,深度学习中每一步运算都被记录在计算图中,例如我们计算loss,然后用loss做迭代。可是当我们需要显示出loss时,这个显示loss的步骤也会被记录在计算图中去计算,这会影响深度学习的。因此加上个detach函数表明,我这句话和你们深度学习的内容无关了。
进一步理解了detach()方法的含义 : 如果requires_grad=True 那么需要detach,否则会反向传播计算梯度 ;如果requires_grad=False 那没必要detach ,因此本来就不会反向传播 。
因此总结,需不需要detach,取决于requires_grad的取值 (平时定义一个tensor,默认梯度取值是False,是不需要的,但是你计算出来的loss是critical函数得出的,虽然你看不见critical函数内部的咋写的,但是你想想,loss是需要反向传播的,所以一定requires_grad是Tru,那么自然就需要detach函数了)。
cpu(),一方面 ,因为深度学习的数据都在cuda上,所以cpu的内存可能读不到(虽然实测还是能的),为了保险起见,将loss这些数据转移到cpu上来读。另一方面 ,如果不加上这个,打印出来显示的是tensor(data,cuda) ,加上cpu(),显示的是tensor(data)
item(),返回tensor中的数值,返回的是标量data ,如果不加上item(),显示的是tensor(data)
transforms.ToTensor()这一函数的作用是什么?它会将输入图像数据转换为形状为(C, H, W)的张量,并对每个像素值进行归一化处理以使其落在[0,1]区间内。这种归一化方法较为简单直接的方式就是通过除以255来实现标准化处理。此外由于PIL库读取的图片格式本就是(H, W, C)形式因此为了适应神经网络模型的需求必须经过这一步转换操作而transforms库正是专门为图像处理而设计的一组工具其中归一化操作是最基础也是最为常见的操作之一因此将其转换为张量并进行标准化处理是 transforms 库提供的核心功能之一
transforms.Normalize()函数的用法?这个函数是起到归一化的作用。 参考
第一 ,它是怎么用的?
回答 :output = (input - mean) / std 如果是三通道的话,就是三个通道分别计算,如果单通道的话,就计算一个通道。
第二 ,怎么写?
回答 :三通道:transforms.Normalize(std=(0.5,0.5,0.5),mean=(0.5,0.5,0.5)),单通道:transforms.Normalize(std=(0.5,),mean=(0.5,)) (补充:区间是[-1, 1])
第三 :RGB单个通道的值是[0, 255],所以一个通道的均值应该在127附近才对。如果Normalize()函数去计算 x = (x - mean)/std ,因为RGB是[0, 255],算出来的x为什么落在[-1, 1]区间?
回答 :因为在使用这个函数前会先应用torchvision.transforms.ToTensor函数,该函数是[0,1]的范围
第四 ,在我看的了论文代码里面是这样的:torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) 为什么就确定了这一组数值,这一组数值是怎么来的?
回答: [0.485, 0.456, 0.406]这一组平均值是从imagenet训练集中抽样算出来的

反归一化:有时处理后的图像已经进行了归一化处理。如果希望恢复到原始像素值,则需要执行反归一化操作。具体来说,请计算新的均值为 Mean = -Mean/Std,并将标准差设为 Std=1/Std;随后再次应用 torch.nn.functional.normalize 函数,并将计算出的均值和标准差作为参数传入即可实现。(通常,在某些情况下会直接对图像进行 (img + 1)/2 的变换;这与反归一化效果一致的原因在于:在数据预处理过程中往往会先将图像转换为 tensor 类型,并将其缩放到 0-1 范围内(通过 Totalensor),然后再执行(0.5, 0.5, 0.5)的均值标准化将其转换到 [-1, 1] 范围;因此上述两种方法在实际操作中等价)
当归一化参数并非如此简单的一组数值时该怎么办?一方面仍可依据前述公式进行手动计算,并调用归一化函数完成数据处理。另一方面若采用归一化函数,则具体代码如下:
cifar_10_mean = (0.491, 0.482, 0.447) # mean for the three channels of cifar_10 images
cifar_10_std = (0.202, 0.199, 0.201) # std for the three channels of cifar_10 images
# convert mean and std to 3-dimensional tensors for future operations
mean = torch.tensor(cifar_10_mean).to(device).view(3, 1, 1)
std = torch.tensor(cifar_10_std).to(device).view(3, 1, 1)
#反归一化
img = ((img) * std + mean).clamp(0, 1) # to 0-1 scale
python
然而该图像的形状为(batch, channel, height, weight),其逆归一化的形状为三轴。在张量相乘操作中,默认会将其统一至相同的尺寸。
在深度学习中涉及多种激活函数类型:包括 Sigmoid、ReLU、LeakyReLU、PReLU 和 ELU 等等;它们各自的形态是什么? 参考
在Dataloaders类中存在一个名为"drop_last"的参数,请解释其功能。我们遇到的数据集大小通常与batch_size大小不成整数倍关系,在这种情况下:当"drop_last"参数设为False时,则会读取剩余的余数值;而当设为True时则会舍弃所有剩余的数据点。
GAN生成器与判别器的具体实现框架
注意,Pytorch中卷积接口所需的RGB图像尺寸是 (CHW )通道、长度、宽度。而我们读进来的图像尺寸大多数情况下是HWC ,长度、宽度、通道。因此当我们把图像数据送进卷积层时,必须要先进行尺寸转换!!
至于为什么pytorch选择设计成chw而不是hwc(毕竟传统的读图片的函数opencv的cv2.imread或者sklearn的imread都是读成hwc的格式的)。个人感觉是因为pytorch做矩阵加减乘除以及卷积等运算是需要调用cuda和cudnn的函数的,而这些接口都设成成chw格式了 ,故而pytorch为了方便起见也设计成chw格式了
那新问题就来了,cuda和cudnn为什么设计成chw格式呢 ?我想这是由于涉及到图片操作的都是和卷积相关的,而内部做卷积运算的加速设计成chw在操作上会比hwc处理起来更容易,更快 。题主如果想进一步了解可以google一下cudnn的卷积实现。
关于PyTorch中提供的这个sampler模块,用来对数据进行采样。默认采用SequentialSampler,它会按顺序一个一个进行采样。
什么时候会用Sample采样器呢?比如在模型参数初始化、加载数据集时!
例如加载数据集时,会有shuffle 参数,在每个epoch中对整个数据集data进行shuffle重排。如果我们shuffle是False的话,就默认不洗牌,其实也就是顺序采样数据集;如果我们的shuffle是True的话,就是洗牌,也就是随机采样数据集(RandomSampler)。换句话说,dataloader中如果shuffle是true,则默认为RandomSampler。如果shuffle为false,则默认是顺序采样SequentialSampler,如果想自己定义采样器,shuffle设置为false,然后自己定义采样器。详细可以参考:文章
同理,模型初始化时也是需要采样器的,这点在前面的内容有提过。
# 如果想自己定义采样器,就按照如下格式
train_sampler = RandomSampler(train_dataset)
train_dataloader = DataLoader(train_dataset, sampler=train_sampler, batch_size=batch_size)
python
在某些情况下,默认提供的变换方式可能无法完全满足特定需求,在这种情况下建议你自定义一个适合特定需求的变换策略。具体的实现方式则是通过调用transforms.Lambda来实现。具体的实现方式则是通过调用transforms.Lambda来实现。具体可以参考:文章
self.transform = transforms.Compose([
transforms.Lambda(lambda x: x.to(torch.float32)),
transforms.Lambda(lambda x: 2. * x / 255. - 1.),
])
python
该transforms.Lambda函数的一般使用方法通常是这样的:其中包含一个用于处理输入的lambda x隐匿函数。关于这个lambda x概念,在Python中它被定义为一个内部处理机制,在冒号左边标识的是输入变量的位置,在冒号右边的部分则是执行的具体操作或计算逻辑。这些计算得到的结果会被立即传递给transforms.Lambda进行处理。其实很简单:我对输入变量x施加了一系列操作,并将这些操作后的结果传递给transforms进行图像变换相关的处理工作。
魔法函数具有可调用性;getitem同样具备可调用性。但是当为它提供一个参数时,则只会生成一个数值(这与在加载数据集时为dataloader提供一个参数以启动循环的行为不同;需要注意这一点。)例如:
print(f'number of images = {adv_set.__len__()}')
print(f'number of images = {adv_set.__getitem__(5)}')
python
该类操作包括clone()、detach()以及.data等与张量复制相关的函数。clone()函数会生成一个与源张量具有相同形状、数据类型和设备的新 tensor 对象,并且在不共享数据内存的同时提供梯度回溯功能(即源 tensor 和 copy tensor 均保留梯度计算能力)。而 detach() 方法则会生成一个新的 tensor 对象,在共享源 tensor 数据内存的前提下实现断开梯度计算(即 source tensor 的 requires_grad 为 True 转换为 False)。.data 属性类似于 detach() 方法的功能,在共享数据内存的同时也避免了梯度计算的需求。值得注意的是,在实际应用中通常优先选择使用 detach() 方法来替代 .data 属性的调用方式更加稳妥可靠。此外,在需要独立的数据副本时可以结合使用 .detach() 和 clone() 方法的操作序列(例如 b = a.detach().clone() 或者 $b = a.clone().detach() ) 来创建出新的独立 tensor 对象。(参考链接)
当执行攻击任务时
def fgsm(model, x, y, loss_fn, epsilon=epsilon):
x_adv = x.detach().clone() # initialize x_adv as original benign image x
x_adv.requires_grad = True # need to obtain gradient of x_adv, thus set required grad
loss = loss_fn(model(x_adv), y) # calculate loss
loss.backward() # calculate gradient
x_adv = x_adv + epsilon * x_adv.grad.detach().sign()
return x_adv
python
乍一看似乎在这个训练过程中并不需要更新参数也没有调用optimizer.step()实际上并非如此。backward的作用是用来反向传播并计算梯度在完成backward步骤后从而得到了相应的梯度值通常后续只需执行一次迭代操作即可完成这一过程而代码中的这一迭代过程并未直接依赖于优化器(Optimizer)模块而是通过手动定义了一种更新方式来实现这一过程这样做的好处是可以直接对变量进行操作而不受优化器内部机制的限制具体来说代码中实现了如x_adv = x_adv + epsilon * x_adv.grad.detach().sign()这样的操作使得相关的数值得以保存并被合理利用从而避免了传统优化器的一些潜在限制这也意味着我们编写代码时不必完全模仿现有的优化器框架也可以根据自己的需求设计独特的更新策略以提升模型的性能和效果
注意需要明确何时采用argmax(维度)函数用于获取最大值对应的索引位置,并何时采用softmax(维度)函数用于计算各类别的概率分布。
unsqueeze();squeeze():前者是增加一个为1的维度,例unsqueeze(2)就是将(2,2)变为(2,2,1),后者是减少一个为1的维度。 参考1 参考2
numpy.squeeze(a,axis = None)
1)a表示输入的数组; 2)axis用于指定需要删除的维度,但是指定的维度必须为单维度,否则将会报错; 3)axis的取值可为None 或 int 或 tuple of ints, 可选。若axis为空,则删除所有单维度的条目; 4)返回值:数组。 5) 不会修改原数组;
.item()和.data的主要区别在于它们的返回类型和适用场景。.data方法用于获取张量的具体数值类型对象(Tensor),而.item()则会返回具体的数值本身。特别提示:当处理包含多个元素的张量列表时,请避免使用.item()操作符以防止导致错误发生
tensor的尺寸用size函数,而不是shape函数,例如 adv_x.data.size()
image pixel value should be unsigned int !!! np.uint8
想了解关于dataloader读取数据的情况,请不要直接打印整个dataloader对象(即直接运行"print(dataloader)"),因为这样操作也无法达到预期效果)。正确的做法是打印数据集对象中的具体数据部分:"print(dataloader.dataset)"。请注意,在数据加载过程中使用的数据集名称是固定的,并非由我们自定义的变量名决定的。在此基础上还可以执行更多操作:例如访问特定索引的数据(如"dataloder.dataset[5]")、调用自定义的数据获取方法(如"dataloder.dataset.getitem(5)")以及获取数据集规模(如"dataloder.dataset.len()")。
在训练过程中,不仅有训练数据还有测试数据可供使用。那么该如何同时获取这些数据呢? 可以采用以下代码实现:enumerate+zip的用法(主要基于zip功能)
for i, ((source_data, source_label), (target_data, _)) in enumerate(zip(source_dataloader, target_dataloader)):
python
CrossEntropyLoss常用于多分类问题,在其内部已集成了一个softmax层。
BCELoss是二分类问题中常用的损失函数,在应用该函数前需将数据调整至0-1范围内(通常通过sigmod函数实现)。
BCEWithLogitsLoss是将sigmod函数与BCELoss结合使用而形成的损失函数,在使用该函数时无需预先将输入数据调整至0-1范围以内(因为内部会自动进行归一化处理)。
关于PyTorch中采样与分布的问题:例如下面的三行代码为例
action_dist = Categorical(action_prob)
action = action_dist.sample()
log_prob = action_dist.prob(action)
python
其中记录了四个动作的概率值。Categorical是一种类,并且action_dist是该类的一个实例对象。通过调用该类的sample方法能够从概率分布中随机抽取样本,并且其作用是从概率分布中随机抽取样本。例如,在概率较高的位置上更容易被选中,则意味着在那些位置上更容易被选中。接着将该索引值代入到action_dist.prob(action)中去获取相应的概率值
Python
与...as语法在Python中用于资源管理非常方便。例如,在处理文件时,默认情况下需要打开文件并手动关闭它。然而通过使用with...as...语法,在完成操作后无需人工干预地自动关闭资源。
with open("/tmp/foo.txt") as file:
data = file.read()
python
形状函数用于获取矩阵的尺寸信息。通常有两种调用方式:通过np.shape(array)可以获得整个矩阵各个维度的具体长度,在一个形状为(3,2)的矩阵中可以看到这样的情况;而通过array.shape[1]则可以直接获取第二维的具体数值。
用len()函数计算二维数组的长度,只计算有多少行,不计算列数
在编程中经常采用 # TODO 标记来提示未完成的任务。
这个标记主要用于提示此处未完成的工作。
常用于规划任务与进度管理。
进度追踪。
协作沟通。
被视为一种良好的编码规范。
python中的一些变量,有全局变量、局部变量、类变量、实例变量 。
全局变量 :在所有函数和class外定义的变量
局部变量 :在所有函数和class的方法内的变量
类变量 :在class内,不在class的方法内的变量
实例变量 :在class方法内用self修饰的变量
全局变量的作用域可以作用在任何地方 ,局部变量作用在函数内/方法内 ;类变量和实例变量就作用在类中 。
不过要特别注意,类变量和实例变量最大的不同在于 :类变量的每个实例所拥有的是同一个内存,在一个实例中修改,会改变其他实例中的值;而实例变量的单独的内存,每个实例只能修改自己的值,不能修改其他实例的值。详见1 详见2
Python中的 enumerate() 函数不仅能够实现将可遍历的数据对象(如元组、列表、字符串等)生成一个索引序列的能力;而且还能同时输出数据及其对应的索引值 详见1 详见2
有关Python中判断某个模块是否为主脚本运行机制的理解:通过变量.__name__., 我们可以获取到当前正在运行的脚本文件的名字,默认情况下会被设定为常量名.main.. 这意味着:
- 若该脚本直接以独立的方式运行,则其名称会被设定为常量名
.main.. - 如果该脚本未以直接方式运行而是在其他程序中导入使用,则这部分代码将不会被执行。
举个例子来说吧——假设有文件Program.py里包含了两个函数或段落(分别称为1和2),这两个部分之间夹杂着检查是否为主脚本的条件语句(如if name == "main: ...)。这种情况下,在运行Program.py作为独立脚本时(即作为单独的应用),这两个功能都会执行;但如果将Program.py导入到另一个较大的文件或框架中,则只有第一条会被执行,并跳过第二条。
在Python编程语言中,zip() 和 zip(*) 的用法及其区别如下:zip() 用于将多个迭代器(如列表或生成器)中的对应元素打包成一个个元组,并返回一个包含这些元组的新迭代器;而 zip(*) 则与之相反,在接受多个迭代器的情况下分解并重新组合它们的内容。具体而言,在代码示例 mel, speaker = zip(*batch) 中,“*batch”的作用相当于对 zip() 操作的结果进行解压。参考文献:参考
a=list() 和 a=[] 定义一个列表
其中, \texttt{np.inf}代表一个非常大的正数值. 具体操作如下: 首先将\texttt{best\_loss}赋值为无穷大, 并在后续循环中选择其中最小的值.
data[:,y] data[x,:]的含义

np.random.rand() 函数用于生成均匀分布在 (0,1) 区间内的随机数样本值,并提供可选的 size 参数来控制输出数组的尺寸 用法参考
np.random.randn() 函数用于生成均值为 0、方差为 1 的标准正态分布随机数样本值 用法参考
numpy.random.randint 函数用于生成整型随机数,在指定范围内取值并提供可选的 size 参数来控制输出数组形状 详见
当 size 参数设为 None 或未指定时,默认会返回一个单独满足条件的整型数值;如果设置为 (m, n, k) 等元组,则会返回相应维度大小的数组对象
在Python中,“list”与“array”存在明显的区别。“例如,“list”带有逗号而“array”之间无逗号(以空格替代)。某些函数要求参数为“array”,因此必须接受“array”作为输入;此外,“array”与“list”之间可以实现相互转换。”
numpy中的ravel函数,将数组扁平化(将数组拉成一维数组)

在Python语言中,range()函数用于生成一个等差数列序列,默认步长为1;常见的使用方式包括:
for i in range(10)用于生成从0到9的整数序列for i in range(2,10)从2开始到9结束for i in range(2,10,2)则每隔2个数取一个值
在Python语言中常见使用两种数据访问方式,在数组元素之间既可以通过索引逐个获取特定位置的数据也可以利用切片指令一次性获取连续的一组数据这是由于数组中的所有元素都是同一类型的数据类型因此允许进行切片操作。然而列表能够存储不同类型的元素每个元素的大小不仅相同还可以不同同时不支持切片操作!
关于 numpy 中 astype(bool) 和 astype(int),以及 int() 的区别: int() 将单个数值进行取整运算,并且无法用于数组处理;而 astype(int) 将数组或列表转换为整数类型,并且仅适用于向量数据(类似于应用于标量值)。类似地,在于 astype(bool) 的作用;区别在于 int() 和 astype(int) 的功能不同。详见
在Python中使用np.unique函数处理一维数组或列表输入,并移除重复项后按照降序返回去重后的元组或列表
在Python编程语言中,index函数的功能是用来在列表中查找某一个值的第一个匹配项的位置。

python中tolist()命令 ,将数据或矩阵转换为列表
该函数采用sys.maxsize或等效于无穷大的阈值设置为sys.maxsize或无穷大(即\infty),从而实现对所有数据进行完整显示的目的。当处理非常大的数组时(即array大小极大),使用Python默认的print函数会导致只能输出开头和结尾的部分元素(即仅显示部分数据),而中间的大块数据会被省略以避免溢出并加快执行速度(即节省内存并提升运行效率)。为了实现完全显示所有数据的目的(即展示整个数据集而不截断中间部分),可以通过将阈值设为最大值来强制Python输出全部内容(即确保所有数据都被完整显示)。
建议根据具体需求对这个阈值进行调整(即根据实际情况修改阈值)。例如,默认情况下如果设置为10,000,则长度不超过1万的数据将被完整显示(即数组长度小于等于1万的所有元素都会被展示),而长度超过1万的数据则会以省略形式显示(即超出部分采用隐藏或用...表示)。
Python中单斜杠/表示浮点数除法,返回浮点结果;双斜杠//表示整数除法。
在Python编程语言中,函数flatten(dim)常用于深度学习领域中的图像处理和图像拉直操作(从CNN层输入到全连接层)。具体用法如下:参考
关于lambda x这一概念,在Python编程中属于一个特殊的隐匿函数机制,默认情况下不会暴露出来。一般情况下使用这种机制时会遵循如下的语法规范:将参数放在冒号左侧的位置,并将运算表达式放置在冒号右侧的位置后方括号内(例如:f = lambda x : x + 1)。其中冒号左侧表示输入参数的位置标识符,并且冒号右侧则是运算表达式所在的位置标识符区域;运算结果会被赋予给f变量所指向的对象位置(即f句柄)。当程序中存在某些仅被调用一次的简单功能模块时,默认定义这样一个显式的函数对象可能会导致不必要的资源消耗;因此,在程序中仅需使用一次的场景下,默认定义这样的隐匿功能会更加高效。

这个$f'{data_dir}/*$中的$f$有什么作用?它等价于字符串格式化表达式中的${class_dir}/*$(即{class_dir}/*)。与标准格式化方法相比这是一种简化方式:一种简化的方式是将不需要显式参数化的字符串直接书写出来。具体来说,在标准字符串格式化中需要调用.format()方法(如${class_dir}/*$ .format(variable)),而当使用前缀时,则可以在大括号内部直接插入变量或其他数据项。
os.path.relpath()用于将绝对路径转换为相对路径;也可视为一种路径截断。参考:<>
sign()是干啥的:符号函数 以y轴为分界线,x>0 y取1,x<0 y取-1
该函数用于将数值限定在一个区间[min, max]内。具体来说,输入参数x会被限制在此范围内。
round()函数 取值四舍五入
np.r_[...]; np.c_[...]: 通过使用 np.r_ 函数将两个矩阵垂直方向上进行叠加,并确保操作后的列数保持一致;而 np.c_ 函数则用于将两个矩阵水平方向上进行叠加,并保证操作后的行数能够保持一致 参考
plt.tight_layout() 的主要功能是实现图像布局的整洁排列。具体效果可参考 [()。
Image.fromarray():完成数组到图像的转换过程,并将其保存下来 (需导入 from PIL import Image) 。完整代码见下文。
from PIL import Image
im = Image.fromarray(example.astype(np.uint8)) # image pixel value should be unsigned int
im.save(os.path.join(adv_dir, name))
#注意这个name必须要指定图片的名字和类型
python
shutil.copytree(old_path, new_path):该函数用于复制目录结构(无需额外导入模块),实现细节可参考此处。
sorted()排序操作:y = sorted(x),默认升序。https://www.cnblogs.com/huchong/p/8296025.html
Notebook运行于远程服务器环境中,在此情况下无法直接访问本地计算机的图形界面来渲染Gym环境。必须模拟一个展示界面来实现这一目标。通过安装pyvirtualdisplay库即可开始开发流程,在此过程中就可以实现了一个基于远程服务器上的图形用户界面(GUI)模拟。
from pyvirtualdisplay import Display
virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()
python
