1. 从头实现X1. ReLUx是Tensor所以只能用Tensor操作。def relu(x: torch.Tensor) - torch.Tensor: return x*(x0).float()2. Softmax当针对某一dim处理时需要加keepdim参数否则后续shape不一致无法计算。softmax归一化时为了避免指数值太大每一个元素都要减去x_max使得指数值落在(0,1]区间内。def my_softmax(x: torch.Tensor, dim: int -1) - torch.Tensor: x_max x.max(dimdim, keepdimTrue).values ex torch.exp(x-x_max) return ex/ex.sum(dimdim, keepdimTrue)3. Cross Entropy Loss首先是torch.logsumexp函数内部在exp之前会减去x_max确保指数值不炸。dim-1永远是类别维度。计算出log_probs值后取出targets对应的索引再求均值得到最终的cross entropy值。def cross_entropy_loss(logits, targets): log_probs logits - torch.logsumexp(logits, dim-1, keepdimTrue) return -log_probs[torch.arange(targets.shape[0]), targets].mean()4. Dropoutp是失活概率。dropout的原理是训练过程中随机将一部分神经元的输出置0测试阶段神经元全部有效。torch.rand_like函数生成[0,1]区间内的均匀分布。除以(1-self.p)是为了保持训练和测试时神经元输出的信号强度是相等的。class MyDropout(nn.Module): def __init__(self, p0.5): super().__init__() self.p p def forward(self, x): if not self.training or self.p0: return x mask torch.rand_like(x)self.p return x*mask/(1.0-self.p)5. Embeddingtorch.randn函数随机初始化一个正态分布。embedding的原理创建一个parameter对象然后取索引。class MyEmbedding(nn.Module): def __init__(self, num_embeddings, embedding_dim): super().__init__() self.weight nn.Parameter(torch.randn(num_embeddings, embedding_dim)) def forward(self, indices): return self.weight[indices]6. GeLUgelu的原理假设输入x保持正态分布输入x越小被丢弃的概率越高。torch.erf函数是误差函数从0到x的高斯分布。def my_gelu(x): return x*0.5*(1torch.erf(x/math.sqrt(2)))7. Kaiming initkaiming init原理保证每一层输入方差等于输出方差将weight初始化为正态分布但方差std需要计算。fan_in是输入神经元的数量如果weight是linear权重那weight[1]是输入单元数量如果weight是bias那weight[0]是输入单元数量。Tensor.normal_函数是in_place函数见_将Tensor初始化为正态分布。参数初始化阶段不属于模型学习所以要加torch.no_grad停止梯度。def kaiming_init(weight): fan_in weight.shape[1] if weight.dim() 2 else weight.shape[0] std math.sqrt(2.0 / fan_in) with torch.no_grad(): weight.normal_(0, std) return weight2. 注意力机制