由于python学得较浅,打算在代码实战中进行提高。所以不定时更新python学习的小问题作为记录。
1.argparse的使用
argparse是一个python模块,用途是:命令行选项、参数和子命令的解释。
用法示例:
以联邦学习Fedavg代码中的option.py为例:
def mnist_iid(dataset, num_users):
定义了名为mnist_iid的函数,用于从MNIST数据集中对客户端数据进行独立同分布(IID)采样。
num_items = int(len(dataset)/num_users)
dict_users, all_idxs = {}, [i for i in range(len(dataset))]
这部分代码计算了每个用户应该获得的样本数量。首先,通过计算整个数据集的长度除以用户数量得到每个用户应获得的平均样本数量,并使用int函数将结果转换为整数。然后,创建一个空字典dict_users和一个包含从0到数据集长度-1的列表all_idxs。
all_idxs
的赋值是将一个空字典和一个列表 [0, 1, 2, ..., (len(dataset)-1)]
组成的元组赋予all_idxs
变量。这里使用了逗号(,
)来表示元组。
for i in range(num_users):
dict_users[i] = set(np.random.choice(all_idxs, num_items, replace=False))
all_idxs = list(set(all_idxs) - dict_users[i])
这部分代码为每个用户分配样本。通过使用np.random.choice函数从all_idxs列表中无放回地选择num_items个索引,使用set函数将选择的索引转换为集合,并将该集合分配给dict_users字典中的第i个用户。然后,使用set函数和list函数从all_idxs列表中删除已经分配给用户的索引。
return dict_users
这行代码将分配好样本的dict_users字典作为函数的返回值,其中键为用户编号,值为分配给用户的样本索引集合。
总而言之:这段代码定义了一个函数mnist_iid,用于从MNIST数据集中独立同分布地对客户端数据进行采样。函数接收MNIST数据集和用户数量作为输入,并返回一个字典,其中键为用户编号,值为分配给用户的样本索引集合。
2.np.random.choice()函数
np.random.choice()
是 NumPy 库中的一个函数,用于从给定的一维数组或整数范围中随机选择元素。
标准定义为:
np.random.choice(a, size=None, replace=True, p=None)
a
:表示要从中选择的一维数组,或者表示要从中选择整数的整数范围(如range(10)
表示在0到9之间进行选择)。size
:可选参数,表示要生成的随机样本的形状。默认为None
,表示只选择一个随机样本。replace
:可选参数,表示是否允许重复选择。默认为True
,即允许重复选择。如果设置为False
,则不允许重复选择,并且会从a
中删除已选择的元素。p
:可选参数,表示每个元素被选择的概率。如果设置为None
,则所有元素具有等概率被选择。否则,p
必须是与a
具有相同长度的一维数组,其中每个元素表示相应元素被选择的概率。
在代码中,np.random.choice(all_idxs, num_items, replace=False)
表示从 all_idxs
列表中无放回地选择 num_items
个元素。这样,每次调用 np.random.choice()
函数将返回一个无放回、不重复的随机样本集合。
3.transforms.Compose()
transforms.Compose
是 PyTorch 中用于组合多个图像预处理操作的类。它可以将多个预处理操作按顺序串联在一起,方便对图像进行连续的处理。
具体来说,transforms.Compose
的作用是创建一个包含多个预处理操作的组合。在创建时,通过传入一个由预处理操作构成的列表或元组来定义预处理操作的顺序。当对图像进行预处理时,按照列表中的顺序,依次应用各个预处理操作。
示例:
transforms.Compose([
transforms.Resize((224, 224)), # 调整图像大小为 224x224
transforms.ToTensor(), # 将图像转换为张量
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 将图像进行归一化
])
首先使用了 transforms.Resize
将图像的大小调整为 224x224。
接下来使用了 transforms.ToTensor
将图像数据转换为张量格式,
并使用 transforms.Normalize
对图像进行归一化处理。
这样可以将图像数据转换为适用于训练神经网络的格式。
使用 transforms.Compose
创建的组合可以直接应用于数据集、数据加载器或单个图像。
4.nn.CrossEntropyLoss()--
交叉熵损失函数
主要参考:【pytorch】交叉熵损失函数 nn.CrossEntropyLoss()_Enzo 想砸电脑的博客-CSDN博客
交叉熵损失多用于 多分类函数,用于衡量预计概率分布与真实概率分布之间的差异。
5.DatasetSplit()
DatasetSplit
是一个自定义的数据集拆分类,用于将原始数据集拆分成子集。
要使用 DatasetSplit
类,首先需要创建一个原始数据集对象,比如使用 PyTorch 提供的 torch.utils.data.Dataset
类创建一个数据集对象。
然后,你可以实例化 DatasetSplit
类,将原始数据集对象和索引列表作为参数传递给它。索引列表指定原始数据集中要使用的样本索引。
以下是一个使用 DatasetSplit
类的示例:
from torch.utils.data import Dataset
from DatasetSplit import DatasetSplit
# 创建一个原始数据集对象
class MyDataset(Dataset):
def __init__(self):
# 初始化数据集
self.data = [...] # 假设这里是原始数据集的样本数据
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
return self.data[idx]
# 创建一个索引列表,表示要使用的样本索引
idxs = [0, 1, 2, 3] # 假设这里是你要使用的样本索引
# 创建 DatasetSplit 对象
subset_dataset = DatasetSplit(MyDataset(), idxs)
# 迭代子集数据
for data in subset_dataset:
# 在这里对子集数据进行处理
print(data)
在上例中,我们首先创建了一个自定义的数据集类 MyDataset
,并实现了 __len__(
返回数据集中的样本数量)
和 __getitem__(
接收一个索引 idx
,然后根据索引返回对应位置的样本数据)
方法。然后,我们创建了一个索引列表 idxs
,表示我们要使用的样本索引。
接下来,我们实例化了 DatasetSplit
类,将 MyDataset
对象和索引列表 idxs
作为参数传递给它,得到一个拆分后的子集数据集对象 subset_dataset
。
最后,我们可以通过迭代 subset_dataset
对象来访问拆分后的子集数据。在这个例子中,我们简单地打印出了子集数据。
总结来说,DatasetSplit
类的使用方法是将原始数据集对象和索引列表传递给它的构造函数,然后通过实例化的对象来访问拆分后的子集数据。
6.DataLoader()
DataLoader
是 PyTorch 中用于数据加载的类。它可以帮助我们将数据集进行批量加载,并提供了一些方便的功能,比如数据的自动分组、随机打乱、多线程处理等。下面是 DataLoader
的一般使用方法和常见参数:
from torch.utils.data import DataLoader
# 创建一个数据集对象
dataset = MyDataset()
# 创建一个 DataLoader 对象
dataloader = DataLoader(dataset, batch_size=32, shuffle=True, num_workers=4)
# 迭代 DataLoader 加载的批量数据
for batch_data in dataloader:
# 在这里对批量数据进行处理
images, labels = batch_data
# 其他操作...
在这个例子中,我们首先创建了一个自定义的数据集对象 dataset
,可以是 PyTorch 提供的 torch.utils.data.Dataset
的子类。
然后,我们使用 DataLoader
类来创建一个数据加载器 dataloader
。我们将数据集对象 dataset
作为第一个参数传递给 DataLoader
构造函数。
batch_size
是指每个批次的样本数量,默认值是 1。可以根据内存大小和训练需求来调整批次大小。
shuffle
是一个布尔值,表示是否在每个迭代步骤中随机打乱数据。默认值为 False。通常在训练过程中,为了增加模型的泛化能力,会将数据打乱顺序。
num_workers
是指用于数据加载的子进程数,默认值为 0,表示在主进程中加载数据。可以通过增加子进程数来加速数据加载过程,尤其是当数据集较大时。
然后,我们可以在迭代中使用 dataloader
加载批量数据。每次迭代,dataloader
会返回一个批次的数据,这个数据可以直接用于训练或推断。
在上面的例子中,我们将每个批次的数据存储在变量 batch_data
中,其中 batch_data
是一个元组或列表,包含了每个样本的特征和标签。你可以根据自己的数据集和模型来解析这个批次的数据。
然后,你可以对批次数据进行操作,比如将数据移动到 GPU 上、进行模型的前向传播和反向传播、计算损失函数等等。
使用 DataLoader
的好处是,它可以帮助你自动处理数据的分组和批次大小,并提供了一些方便的功能,比如数据的随机打乱和多线程加载等。
7.torch.optim.SGD
torch.optim.SGD是PyTorch机器学习框架提供的一种优化算法,用于在训练神经网络模型时更新模型的参数。SGD代表随机梯度下降(Stochastic Gradient Descent)。
SGD的主要思想是通过迭代更新模型参数来最小化损失函数。在每次迭代中,SGD都会从训练数据中随机选择一个小批量数据(通常是一组样本)并计算其对应的损失函数。然后,SGD根据损失函数的梯度来调整模型的参数,以使损失函数的值减小。
torch.optim.SGD的API使用方法如下:
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, momentum=momentum)
其中,
- model.parameters()表示需要更新的模型参数,
- lr表示学习率(控制参数更新的步长),
- momentum是一个衰减因子(可选参数),用于加速训练过程。
然后,在每次训练迭代周期中,可以调用optimizer的step()方法来更新模型参数:
optimizer.step()
此外,还可以调用optimizer的zero_grad()方法来清空之前计算的梯度:
optimizer.zero_grad()
通过不断迭代和更新模型参数,使用SGD算法可以帮助神经网络模型逐步优化,以获得更好的性能。
8.enumerate() 函数
enumerate() 函数的基本应用就是用来遍历一个集合对象,它在遍历的同时还可以得到当前元素的索引位置。
参考:Python的enumerate函数_Python 学习者的博客-CSDN博客
9.tqdm
tqdm
是一个Python库,用于在命令行中显示进度条。它可以在循环中自动计算迭代的进度,并以逐步更新的方式将进度呈现在终端窗口中。
在这段代码中,tqdm
函数被用作一个循环的迭代器,用于显示每一轮迭代的进度条。tqdm(range(self.args.r))
创建了一个在范围range(self.args.r)
上进行迭代的进度条。在每个迭代步骤中,进度条会自动更新并显示当前的进度。
在这段代码中,使用了tqdm
函数的range
作为迭代器,它会在循环的每次迭代中更新显示进度条的状态。这样,在循环的每一次迭代中,都会通过调用print('round', t + 1, ':')
来显示当前轮次的进度,然后执行相应的操作。
通过使用tqdm
函数,可以更直观地了解算法的执行进度,并可以在命令行中实时地查看进度条的状态。
示例:
在你的循环中使用tqdm
函数包装迭代器。这个迭代器可以是一个范围对象(例如使用range()
函数生成的迭代器),也可以是一个列表、元组等可迭代对象。以下是一个使用tqdm
的示例:
from tqdm import tqdm
for i in tqdm(range(10)):
# 在循环中的操作
...
这将创建一个在终端中显示的进度条,并且在每次迭代时自动更新进度。
(可选)你还可以根据需要为进度条添加其他参数,例如设置进度条的描述、设置动画效果、设置进度条的样式等。以下是一些常用的参数示例:
desc
:设置进度条的描述文本。total
:设置迭代的总次数,用于计算进度的百分比。ncols
:设置进度条的宽度。bar_format
:设置进度条的样式。
for i in tqdm(range(10), desc='Processing', ncols=80, bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}'):
# 在循环中的操作
...
10.read_csv()
read_csv()
是一个用于从CSV文件中读取数据的函数。它是Pandas库中的一个核心函数,Pandas是一个用于数据分析和处理的强大Python库。
read_csv()
函数允许你从一个CSV文件中加载数据,并将其转换为一个称为DataFrame的数据结构。DataFrame 是Pandas库中用于操作和处理表格数据的主要数据结构之一。
你可以通过指定文件路径来调用 read_csv()
函数,并将数据加载到一个DataFrame中。一旦数据被加载到DataFrame中,你就可以使用Pandas库提供的一系列函数和方法来对数据进行操作、分析和可视化。
示例代码:
import pandas as pd
# 从名为data.csv的文件中读取数据
df = pd.read_csv('data.csv')
# 对数据进行操作或分析
# ...
# 打印前几行数据
print(df.head())
以上代码中,read_csv()
函数用于将名为 data.csv
的文件中的数据加载到 DataFrame 中,并利用 head()
方法显示前几行数据。请确保文件路径正确,并且包含适当的数据以供读取。
df = pd.read_csv('data/Wind/Task 1/Task1_W_Zone1_10/' + file_name + '.csv', encoding='gbk')
从给定路径和文件名的CSV文件中读取数据,并将数据加载到名为 df
的Pandas DataFrame中。
11.fillna()
例:
df.fillna(df.mean(), inplace=True)
在Pandas中,fillna()
是一个用于填充缺失值的函数。在这个例子中,df.fillna(df.mean(), inplace=True)
表示使用 DataFrame df
中的均值来填充所有的缺失值,并将修改应用于原始 DataFrame。
12.random.sample()
random.sample(list,数值)
例1:
index = random.sample(range(0, self.args.K), m)
例2:
random.sample
函数返回包含列表中任意两个项目的列表:
import random
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
for i in range(3):
slice_a = random.sample(a, 5) # 从list中随机获取5个元素,作为一个片断返回
print(slice_a)
print(a, '\n') # 原有序列并没有改变
输出:
[15, 7, 10, 13, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[12, 4, 9, 13, 15]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
[6, 1, 7, 9, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
13.named_parameters()
named_parameters
是 PyTorch 中的一个方法,它用于遍历模型中定义的参数并返回参数的名称及对应的参数张量。具体来说,named_parameters
方法返回一个迭代器,该迭代器产生参数名称和对应的参数张量。
例如,在神经网络模型中,你可以使用 named_parameters
方法来获取每个层的名称和对应的权重参数,然后对这些参数执行一些操作,比如参数初始化、参数更新等。
以下是一个简单的示例:
import torch
import torch.nn as nn
# 定义一个简单的神经网络模型
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.fc1 = nn.Linear(10, 5)
self.fc2 = nn.Linear(5, 2)
def forward(self, x):
x = self.fc1(x)
x = self.fc2(x)
return x
model = SimpleModel()
# 使用named_parameters遍历模型参数
for name, param in model.named_parameters():
print(name, param.size())
在上面的例子中,named_parameters
方法被用于遍历 SimpleModel
模型中的参数,并打印出每个参数的名称和对应的大小(即形状)。这对于调试和查看模型参数非常有用。
14.StepLR()
StepLR
是一个PyTorch
中的学习率调度器,它实现了按步长调整学习率的策略。在训练过程中,学习率调度器会根据预定义的策略调整学习率。在StepLR
中,每隔指定的step_size个epoch
,学习率将乘以gamma
,以降低学习率。具体的调整公式为
new_lr = lr * gamma ** (epoch // step_size)
其中,lr是初始学习率,gamma是衰减因子,epoch是当前训练的epoch数,step_size是调整间隔,即每隔多少个epoch调整一次学习率。
在使用StepLR时,需要将其作为参数传递给优化器对象。在每个epoch结束后,学习率调度器会自动调整学习率。例如,如果step_size=10,gamma=0.1,那么每10个epoch,学习率将乘以0.1,即降低一个数量级。
15.zip()
>>> a = ['a', 'b', 'c', 'd']
>>> b = ['1', '2', '3', '4']
>>> list(zip(a, b))
[('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]
很明显,对于我们的两个list
,a
和b
,list(zip(a, b))
生成了一个列表。在这个列表中,每个元素是一个tuple
;对于第i
个元组,它其中的内容是(a[i-1], b[i-1])
。这样的操作,与压缩软件的“压缩”十分接近。如果我们继续在zip()
中加入更多的参数,比如zip(a, b, c, d)
,那么在将它转换成list
之后,结果当然就是[(a[0], b[0], c[0], d[0]), (a[1], b[1], c[1], d[1]), ..., (a[n-1], b[n-1], c[n-1], d[n-1])]
。
考虑一个场景:你正在处理一些学生的成绩,有这样两个列表:
names = ['John', 'Amy', 'Jack']
scores = [98, 100, 85] # 分数和名字是一一对应的
如果你想对它们进行排序,又不想破坏对应关系的话,就可以这样:
data = list(zip(names, scores))
data.sort()
结果是:
[('Amy', 100), ('Jack', 85), ('John', 98)]
如果要先对分数进行排序:
data2 = list(zip(scores, names))
data2.sort()
结果是:
[(85, 'Jack'), (98, 'John'), (100, 'Amy')]