人工智能中一些看不懂的代码

这篇具有很好参考价值的文章主要介绍了人工智能中一些看不懂的代码。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

源码有一个写法:

def forward(self, input: Tensor, hx: Optional[Tensor] = None) -> Tuple[Tensor, Tensor]: # noqa: F811

        pass

forward,它的第一个参数 input 是一个 Tensor 类型的变量,第二个参数 hx 是一个可选的 Tensor 类型变量,这里使用了 Python 3.7 引入的类型注解语法。

函数返回值类型是一个由两个 Tensor 类型变量组成的元组(Tuple)【 -> 的意思是返回值

除此之外,这段代码还包含了一个 pass 语句,它的作用是占位符,表示这个函数目前并没有实现任何功能。如果你需要在这个函数中添加具体的代码实现,可以将这个 pass 语句替换为你的代码。

2、@classmethod

@classmethod 是 Python 中的一个装饰器(Decorator),用于定义类方法。

类方法是与类相关联的方法,而不是与实例相关联的方法。可以直接“类.方法”直接调用类方法而不需要实例化

使用 @classmethod 装饰器来定义一个类方法,可以在方法中使用 cls 参数来引用类本身,而不是实例本身【在类方法中,第一个参数通常被约定为 cls,表示类本身。然而,你可以使用任何名称。】。例如:

class MyClass:
    var = 123

    @classmethod
    def class_method(cls):
        print(cls.var)


# 使用“@classmethod”的时候,可以直接调用类方法,不需要实例化
MyClass.class_method()

# 不使用“@classmethod”的时候,需要实例化才能调用类方法
class1 = MyClass()
class1.class_method()

在上面的示例代码中,我们使用 @classmethod 装饰器定义了一个名为 class_method 的类方法。在该方法中,我们使用 cls 参数来引用类本身,并打印了类变量 var 的值。最后,我们在不创建实例的情况下调用了该方法,输出了类变量的值。

3、eval() 

eval() 可以将字符串形式的 Python 表达式作为参数进行求值

人工智能中一些看不懂的代码

 例如上面的代码,就可以将一个字符串转为参数传递给方法了,因为有时候你需要动态变化你的参数,所以就有了上面的写法

4、nn.parameter()

nn.Parameter() 是 PyTorch 中用于将 tensor 转换为 nn.Parameter 类型的函数。nn.Parameter 实际上是一个特殊的张量类型,它会被自动注册为模型的可学习参数,即在训练过程中需要更新的参数。与普通的 Tensor 不同,nn.Parameter 的属性包括要求梯度(requires_grad)和所处设备(device)等。

在 PyTorch 中,使用 nn.Parameter 将 tensor 转换为模型参数有两个好处:

  1. 自动追踪计算图:将 tensor 包装为 nn.Parameter 之后,PyTorch 会自动将其加入计算图中,并记录相应的梯度信息,这样就可以通过自动微分实现反向传播,即计算模型参数的梯度。

  2. 方便管理模型参数:使用 nn.Parameter 可以使得模型参数更方便地集中管理,例如可以使用 model.parameters() 方法自动获取模型中的所有参数,或者使用 model.named_parameters() 方法获取模型中每个参数的名称和值等信息。

使用 nn.Parameter 的一般流程是:首先定义一个 tensor,然后将其转换成 nn.Parameter 类型并赋予初始值,最后将其添加到模型中作为可训练参数。下面是一段示例代码:

import torch.nn as nn

class MyModel(nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        self.weight = nn.Parameter(torch.randn(3, 5))     # 定义可训练参数为 3x5 的张量
        self.bias = nn.Parameter(torch.zeros(3))          # 定义可训练参数为长度为 3 的张量

    def forward(self, x):
        return torch.matmul(x, self.weight.t()) + self.bias   # 使用可训练参数计算输出

在这个例子中,我们定义了一个 MyModel 类,并将 weight 和 bias 转换为 nn.Parameter 类型。在模型的 forward 函数中,我们使用 weight 和 bias 计算输出,这样就可以利用反向传播算法,根据损失函数对 weight 和 bias 进行梯度更新。

5、在forward函数中,self.multihead_attn(query, key, value)输入是三个张量,初始化中的输入是两个张量 nn.MultiheadAttention(embed_dim, num_heads),这样不会有问题吗?

import torch
import torch.nn as nn


# 定义一个Multihead Attention层
class MultiheadAttentionLayer(nn.Module):
    def __init__(self, embed_dim, num_heads):
        super(MultiheadAttentionLayer, self).__init__()

        # 定义查询、键、值的线性变换
        self.query_proj = nn.Linear(embed_dim, embed_dim)
        self.key_proj = nn.Linear(embed_dim, embed_dim)
        self.value_proj = nn.Linear(embed_dim, embed_dim)

        # 定义多头注意力机制
        self.multihead_attn = nn.MultiheadAttention(embed_dim, num_heads)

        # 定义Layer Norm
        self.norm = nn.LayerNorm(embed_dim)

    def forward(self, input):
        # 将输入张量拆分成查询、键、值
        query = self.query_proj(input)
        key = self.key_proj(input)
        value = self.value_proj(input)

        # 调整形状,以满足Multihead Attention的输入要求
        query = query.permute(1, 0, 2)
        key = key.permute(1, 0, 2)
        value = value.permute(1, 0, 2)

        # 计算Multihead Attention
        attn_output, attn_weights = self.multihead_attn(query, key, value)

        # 将输出进行维度转换,以满足Layer Norm的输入要求
        attn_output = attn_output.permute(1, 0, 2)

        # 应用Layer Norm和残差连接
        output = self.norm(input + attn_output)

        return output

我们看到在init中使用了下面的代码:

nn.MultiheadAttention(embed_dim, num_heads)

Q: 但是在forward函数中,在计算Multihead Attention的时候则是输入了QKV三个张量,那么这时输入三个张量,初始化的时候是两个张量,这样张量不是不匹配吗?

attn_output, attn_weights = self.multihead_attn(query, key, value)

A: 没有问题,因为初始化的时候调用的是nn.MultiheadAttention()的初始化方法:

# nn.MultiheadAttention() 源码

    def __init__(self, embed_dim, num_heads, dropout=0., bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None):
        super(MultiheadAttention, self).__init__()
        self.embed_dim = embed_dim
        self.kdim = kdim if kdim is not None else embed_dim
        self.vdim = vdim if vdim is not None else embed_dim
        self._qkv_same_embed_dim = self.kdim == embed_dim and self.vdim == embed_dim

        self.num_heads = num_heads
        self.dropout = dropout
        self.head_dim = embed_dim // num_heads
        assert self.head_dim * num_heads == self.embed_dim, "embed_dim must be divisible by num_heads"

而在forward输入三个变量的时候是调用的nn.MultiheadAttention()的forward方法:

# nn.MultiheadAttention() 源码

    def forward(self, query, key, value, key_padding_mask=None,
                need_weights=True, attn_mask=None):
        # type: (Tensor, Tensor, Tensor, Optional[Tensor], bool, Optional[Tensor]) -> Tuple[Tensor, Optional[Tensor]]

6、解包操作 *XXX

*DNA_bound表示解包元组或列表

DNA_bound = [32, 126]

np.random.randint(*DNA_bound, size=(pop_size, DNA_size)).astype(np.int8)

等于

np.random.randint(DNA_bound[0], DNA_bound[1], size=(pop_size, DNA_size)).astype(np.int8)

*DNA_bound用于将DNA_bound列表的两个元素解包并作为参数传递给numpy.random模块中的randint()函数。

具体来说,DNA_bound[0]和DNA_bound[1]分别表示DNA序列中每个基因的取值下限(闭区间)和取值上限(开区间),size=(pop_size, DNA_size)表示生成二维数组(即种群)的大小,而astype(np.int8)则是为了将生成的随机数转换成8位整型,以便后续进行ASCII编码操作。

7、np.random.choice(a, size=None, replace=True, p=None)

np.random.choice(a, size=None, replace=True, p=None)

该函数从a序列或整数中随机选择元素,返回一个大小为size的新数组。其中,参数含义如下:

- a:一个整数或可迭代对象,表示待选元素;
- size:一个整数或元组,表示输出数组的形状,如果不传递这个参数,则输出一个标量;
- replace:一个布尔值,表示是否可以重复选择元素,默认为True(允许重复选择);
- p:一个与a长度相同的一维数组,表示每个元素被选择的概率,如果不传递,则使用均匀分布进行随机选择。

例如,使用np.random.choice([1, 2, 3, 4, 5], size=(2, 3), replace=True, p=[0.1, 0.2, 0.3, 0.2, 0.2])生成一个大小为2×3的数组,表示从[1,2,3,4,5]这五个数中选取元素,其中元素1的选取概率是0.1,元素2的选取概率是0.2,依此类推。如果replace=False,则所选元素将不能重复。

结果:

[[1 4 5]
 [5 4 4]]

8、np.random.rand()

np.random.rand(1, 1)

np.random.rand(1, 1)是一个NumPy函数,用于返回指定形状的随机浮点数数组,其值位于[0, 1)之间。

在这里,np.random.rand(1, 1)返回的是一个形状为(1, 1)的二维数组,即一个只有一个元素的矩阵(矩阵元素的值是一个范围在[0, 1)之间的随机浮点数),可以通过以下代码查看:

import numpy as np 
x = np.random.rand(1, 1) 
print(x) # Output: [[0.12345678]] 

9、np.empty()和np.zeros()

np.empty()np.zeros()都是NumPy中用于创建数组的函数,不同之处在于它们生成数组的方式不同:

  • np.empty(shape, dtype=float, order='C'): 创建一个指定形状和数据类型的空数组,其值未被初始化,得到的数组元素的值是随机且未知的。由于NumPy对empty()函数所返回的数组不进行初始化,因此使用该函数会快一些(但可能会有潜在的安全问题)。
  • np.zeros(shape, dtype=float, order='C'): 创建一个指定形状和数据类型的全0数组

举个例子,如果要创建一个全0数组,可以使用np.zeros()函数,如下所示:

import numpy as np 
a = np.zeros((2, 3)) 
print(a) # Output: [[0. 0. 0.] # [0. 0. 0.]]

如果要创建一个空数组,可以使用np.empty(),如下所示:

import numpy as np 
b = np.empty((2, 3)) 
print(b) # Output: [[ 9.65677788e-316 2.07596591e-322 0.00000000e+000] # [ 0.00000000e+000 -1.49457197e+154 5.21297860e-321]]

需要注意的是,由于empty()函数生成的数组未被初始化,其值可能是未知的,但其大小已经确定。因此,在使用empty()函数创建数组时,应该通过其他方式(例如使用zeros()ones())来初始化数组中的值。

10、np.empty()和np.empty_like()

kids = {'DNA': np.empty((n_kid, DNA_SIZE))}

kids['mut_strength'] = np.empty_like(kids['DNA'])

这段代码创建了一个字典kids,其中包括了两个关键字'DNA''mut_strength',分别对应空的二维数组np.empty((n_kid, DNA_SIZE))和一个与'DNA'数组大小相同的空数组 np.empty_like(kids['DNA'])

具体来说,np.empty((n_kid, DNA_SIZE))函数的作用是创建一个形状为(n_kid, DNA_SIZE)的空NumPy数组,即创建一个包含n_kid个长度为DNA_SIZE的一维数组的二维数组。这里之所以使用np.empty()而非np.zeros()函数,是因为empty()函数生成的数组未被初始化,其值可能是未知的,但其大小已经确定。

np.empty_like(kids['DNA'])也是创建一个形状相同的空数组,不过是由kids['DNA']这个数组的形状来确定的。这里的作用是创建一个与'DNA'同样大小的空数组,用于存储后面计算得到的变异强度。

总的来说,这段代码的目的是创建一个字典kids用于存储下一代的DNA序列和其对应的变异强度,以备后续使用。

11、pop[np.argmax(fitness), :]

其中fitness是一个100行1列的矩阵

print("Most fitted DNA: ", pop[np.argmax(fitness), :])

这段代码打印出了种群 `pop` 中适应度函数值最大的个体对应的DNA(基因表达式)。

其中,`np.argmax()` 函数返回数组中最大值的索引,‘’表示取当前索引行的所有列,也就是取这一行的所有值,因此 `pop[np.argmax(fitness), :]` 表示在 `pop` 数组中找到适应度函数值最大的个体所对应的行,并将该行的所有元素作为输出。

model.train()、model.backward(loss) 、model.step() 、val_outputs = model.module.validation_step(batch)这四个语句分别表示什么意思?

这四个语句所代表的含义如下:

  1. model.train():这个语句用于将模型设置为训练模式。在训练模式下,模型会启用一些特定的操作,如启用Dropout、Batch Normalization等。这些操作在训练过程中对于模型的性能和收敛非常重要。调用model.train()可以切换模型到训练模式,并保持这个状态,直到遇到model.eval()语句或训练结束为止。
  2. model.backward(loss):这个语句用于计算损失函数(loss)对于模型参数的梯度。在反向传播过程中,梯度会从损失函数向后传播到模型的各个层,并根据链式法则逐层更新模型参数。
  3. model.step():这个语句用于更新模型的参数。它会根据之前调用model.backward()计算得到的梯度信息,使用优化算法(如随机梯度下降)来更新模型参数。通过迭代训练数据并多次调用model.backward()model.step(),模型的参数会逐渐优化以最小化损失函数。
  4. val_outputs = model.module.validation_step(batch):这个语句用于执行模型的验证步骤。通常,在训练过程中,我们需要周期性地进行验证以评估模型在验证集上的性能。该语句会将验证数据批次(batch)输入到模型中,并返回模型在验证数据上的输出结果(val_outputs)。

next(data_iterator) + mpu.broadcast_data(keys, data, datatype)

data = next(data_iterator)
data_b = mpu.broadcast_data(keys, data, datatype)

这两句代码的含义如下:

  1. data = next(data_iterator):这个语句用于从一个数据迭代器(data_iterator)中获取下一个数据批次(batch)。通常在训练模型时,数据会被划分成小批次进行训练,以便更高效地处理大规模数据集。这行代码通过调用next()函数从数据迭代器中获取下一个批次的数据,并将其赋值给变量data供后续使用。

  2. data_b = mpu.broadcast_data(keys, data, datatype):这个语句用于将数据扩散(broadcast)到不同设备或进程上在分布式训练中,多个设备或进程需要共享同一份数据。这行代码通过调用mpu.broadcast_data()函数,将数据data广播到多个设备或进程上,并将广播后的数据赋值给变量data_b

**将多个字典合并为一个字典

retrievals_all = {**retrieval_set_train, **retrieval_set_valid, **retrieval_set_test}

这行代码使用了字典合并操作,将retrieval_set_trainretrieval_set_validretrieval_set_test三个字典合并成一个新的字典retrievals_all

具体来说,代码通过{**retrieval_set_train, **retrieval_set_valid, **retrieval_set_test}的语法,使用两个星号(**)将三个字典展开,并将它们的键值对合并到一个新的字典中

举个例子来说明,假设原始的三个字典如下:

retrieval_set_train = {'apple': 1, 'banana': 2}
retrieval_set_valid = {'orange': 3, 'grape': 4}
retrieval_set_test = {'kiwi': 5, 'pear': 6}

通过执行这行代码后,将得到一个合并后的新字典retrievals_all

retrievals_all = {'apple': 1, 'banana': 2, 'orange': 3, 'grape': 4, 'kiwi': 5, 'pear': 6}

可以看到,合并后的retrievals_all字典包含了原始三个字典中的所有键值对。如果有重复的键,后面的字典会覆盖前面的字典。最终得到一个包含所有原始字典键值对的新字典retrievals_all文章来源地址https://www.toymoban.com/news/detail-420674.html

到了这里,关于人工智能中一些看不懂的代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包