20250616面试准备

部署运行你感兴趣的模型镜像

手撕代码

  1. 手撕mha
  2. 手撕transformer
  3. 手撕vit
  4. 只用numpy手撕mlp

手撕多头注意力,问transformer八股,问代码步骤,问时间复杂度以及如何优化
hot 100的medium(思路+代码)
树的层次遍历
力扣:最大公共子序列
二维0-1数组的最大全1正方形面积
八股重点是transformer八股
用 O ( n ) 的 时 间 复 杂 度 实 现 窗 口 为 k 的 maxpoolingld ( 滑 动 窗 口 + 双 端 队 列 )

八股

  1. Transformer数据输入和处理的流程
    先做embedding(图像patch embedding)–> 添加位置信息(Positional Encoding)–> 自注意力机制 --> 多头注意力机制 --> 前馈神经网络(FFN)–> 残差连接和LN

    • Encoder block 包含一个 Multi-Head Attention
    • Decoder block 包含两个 Multi-Head Attention(其中有一个用到Masked)
    • Multi-Head Attention 上方还包括一个 Add & Norm 层
    • Add 表示残差链接(Residual Connection)用于防止网络退化
    • Norm 表示 Layer Normalization,用于对每一层的激活值进行归一化

    在这里插入图片描述

  2. Self-Attention 结构
    在这里插入图片描述

    import numpy as np
    from math import sqrt
    import torch
    from torch import nn
     
    class Self_Attention(nn.Module):
        # input: batch size * seq_len * input_dim
        # q k v: (batch size, input_dim, dim k)
        def __init__(self, input_dim, dim_k, dim_v):
            super(Self_attention, self).__init__:
            self.q = nn.Linear(input_dim, dim_k)
            self.k = nn.Linear(input_dim, dim_k)
            self.v = nn.Linear(input_dim, dim_v)
            self.__norm_fact = 1 / sqrt(dim_k)
     
        def forward(self, x):
            Q = self.q(x)    # Q: (batch_size, seq_len, dim_k)
            K = self.k(x)    # K: (batch_size, seq_len, dim_k)
            V = self.v(x)    # V: (batch_size, seq_len, dim_v)
            attn_scores = torch.matmul(Q, K.permute(0, 2, 1))  # Q·K^T
            attn_scores = attn_scores * self.__norm_fact        # 缩放
            attn = nn.Softmax(dim=-1)(attn_scores)             # Softmax归一化
     
            output = torch.bmm(attn, v)
            return output
     
    X = torch.randn(4, 3, 2) # 输入维度:(batch=4, seq=3, input_dim=2)
    print(X)
    self_attn = Self_Attention(2, 4, 5) # input_dim:2 k_dim: 4 v_dim: 5
    res = self_attn(X)
    print(res.shape) # [4, 3, 5]
    
  3. self-MHA代码

    from math import sqrt
    import torch
    import torch.nn as nn
    from torch import permute
    
    class Self_Attention_Multi_Head(nn.Module):
        # 输入张量维度 input: (batch_size , seq_len , input_dim)
        def __init__(self, input_dim, dim_k, dim_v, nums_head):
            super(Self_Attention_Multi_Head, self).__init__()
            # q k v: (batch_size , input_dim , dim_k)
            self.q = nn.Linear(input_dim, dim_k)  # Q线性变换层
            self.k = nn.Linear(input_dim, dim_k)  # K线性变换层
            self.v = nn.Linear(input_dim, dim_v)  # V线性变换层
     
            self.nums_head = nums_head  # 头数
            self.dim_k = dim_k  # 总键维度
            self.dim_v = dim_v  # 总值维度
            self._norm_fact = 1 / sqrt(dim_k)  # 缩放因子
     
        # x: (batch_size, seq_len, input_dim)
        def forward(self, x):
            # Q: (batch_size, seq_len, dim_k)
            # reshape:(num_heads, batch_size, seq_leng, head_dim)
            Q = self.q(x).reshape(-1, x.shape[0], x.shape[1], self.dim_k // self.nums_head)
            K = self.k(x).reshape(-1, x.shape[0], x.shape[1], self.dim_k // self.nums_head)
            V = self.v(x).reshape(-1, x.shape[0], x.shape[1], self.dim_v // self.nums_head)
            
            # batch_size, num_heads, seq_len, seq_len
            # Q * K.T(): batch_size * num_heads * seq_len * seq_len
            attn_scores = nn.Softmax(dim=-1)(torch.matmul(Q, K.permute(0, 1, 3, 2)))
            output = torch.matmul(attn_scores, V).reshape(x.shape[0], x.shape[1], -1)
            # Q * K.T() * V: (batch_size * seq_len * dim_v)
            return output
            
    x = torch.rand(1, 3, 4)
    print(x)
    attn = Self_Attention_Multi_Head(input_dim=4, dim_k=4, dim_v=4, nums_head=2)
    y = attn(x)
    
  4. 详述FFN,FFN和mlp区别

  5. 手撕mlp

    import numpy as np
    
    class Relu:
        def forward(self, x):
            self.x = x
            return np.maximum(0, x)
        def backward(self, grad_output, lr):# 这里的lr没有用到,但是为了保持参数接口的一致性,还是保留了
            return grad_output * (self.x > 0) # relu函数的一阶导数,大于0部分为1,小于0部分为0
        
    class LinearLayer:
        def __init__(self, input_c, output_c):
            # y = x @ w + b
            # self.w = np.random.rand(input_c, output_c)
            self.w = np.random.rand(input_c, output_c) * 0.001 # 这里乘上0.001是为了防止结果太大,梯度爆炸
            self.b = np.zeros(output_c)
    
        def forward(self, x):
            self.x = x # 这里保存输入,为了后续在反向传播中计算梯度
            # y = x @ w + b
            return np.dot(x, self.w) + self.b
    
        def backward(self, grad_output, lr):
            # linear层的梯度计算,涉及三个参数,x,w,b,为 dx, dw, db
            # 其中,dw和db是为了更新w和b
            # dx是为了计算下一层的梯度,链式法则
    
            # y = x @ w + b
            # dl / dx = dl / dy * dy / dx = grad_output * w
            # 这里要注意矩阵的维度要对齐
            grad_input = np.dot(grad_output, self.w.T)
    
            # dl / dw = dl / dy * dy / dw = grad_output * x
            # 这里要注意矩阵的维度要对齐
            w_grad = np.dot(self.x.T, grad_output)
    
            b_grad = np.sum(grad_output, axis=0)
    
            # 更新w和b的参数
            self.w -= lr * w_grad
            self.b -= lr * b_grad
    
            return grad_input
        
    class MLP:
        def __init__(self, input_c, hidden_c, output_c, layers_num):
            self.layers = []
    
            # 初始化网络第一层
            self.layers.append(LinearLayer(input_c, hidden_c))
            self.layers.append(Relu())
    
            # 初始化网络中间层
            for i in range(layers_num - 2):
                self.layers.append(LinearLayer(hidden_c, hidden_c))
                self.layers.append(Relu())
            
            # 初始化网络最后一层,注意,最后一层没有relu激活函数
            self.layers.append(LinearLayer(hidden_c, output_c))
            
    
        def forward(self, x):
            res = x
            for layer in self.layers:
                res = layer.forward(res)
            return res
    
        def backward(self, grad_output, lr):
            grad = grad_output
    
            # 倒序遍历每一层,反向传播,计算每一层梯度
            # for layer in reversed(self.layers):
            for layer in self.layers[::-1]:
                grad = layer.backward(grad, lr)
    
            return grad
    
    if __name__ == '__main__':
        input_data = np.random.rand(2, 8)
        input_c = 8
        hidden_c = 16
        output_c = 3
        layers = 5
        target = np.random.rand(2, 3)
    
        mlp_model = MLP(input_c, hidden_c, output_c, layers)
    
        # print(mlp_model.layers)
    
        for i in range(10):
            print(f'[Epoch: {i} / 100]', end='   ')
            res = mlp_model.forward(input_data)
    
            # 计算损失loss,这里使用mse,均方误差函数
            loss = ((res - target) ** 2).mean()
    
            # 损失对于最后一层输出res的梯度
            loss_grad = 2 * (res - target)
    
            # 反向传播,计算每一层梯度
            mlp_model.backward(loss_grad, lr=0.1)
    
            print(f'[loss: {loss}]')
    
  6. 为什么排序效果比召回更好?

  7. Learning to rank是什么
    (上面两道是搜广推的题目)

  8. 过拟合和欠拟合的定义,怎么解决
    问题 定义 表现
    欠拟合 模型过于简单,无法捕捉数据中的基本规律(高偏差)。 训练集和测试集上表现均差。
    过拟合 模型过于复杂,过度拟合训练数据中的噪声或细节(高方差)。 训练集表现好,测试集表现差(泛化能力弱)。

  9. L1L2正则化分别是什么,公式,使用场景,两者数学方面的区别

    • L1正则化:权重系数的绝对值之和
      在这里插入图片描述

    • L2正则化:权重系数的平方和
      在这里插入图片描述

    • 使用场景:
      L1正则化常用于特征选择,因为它可以将不重要的特征的系数压缩到0。因此,L1正则化适用于特征数量多且需要进行特征选择的场景。
      L2正则化常用于防止模型过拟合,它适用于特征数量较少且需要保持模型稳定性的场景。

    • 两者数学方面的区别
      L1正则化在参数空间中形成了一个菱形的可行域,而L2正则化在参数空间中形成了一个圆形的可行域。
      L1正则化可以产生稀疏的权重矩阵,即许多权重会变为0,而L2正则化则会使得权重值更趋于较小的值,但不会变为0。
      L1正则化适用于特征选择,而L2正则化适用于防止模型过拟合。

  10. dropout在训练和推理阶段有什么区别?训练和测试的阶段一样吗,怎么平衡训练测试时的差异

    • dropout作用
      • ​防止过拟合:通过在训练过程中随机“丢弃”一部分神经元(即将这些神经元的输出置为零),Dropout减少了神经元之间的共适应性,迫使网络学习到更加鲁棒和泛化能力强的特征。
      • 集成学习效果:每次训练迭代中,Dropout相当于训练了一个不同的子网络。最终模型的预测可以看作是这些子网络的集成,从而提升了模型的整体性能和稳定性。
      • 减少神经元共适应性:Dropout迫使每个神经元不依赖于特定的其他神经元,使得网络结构更加健壮,减少了对特定特征的过度依赖。
    • 训练测试区别、平衡差异
      • 训练阶段:使用 Dropout,随机丢弃一部分神经元的输出,并对剩余神经元的输出进行缩放。
      • 测试阶段:不使用 Dropout,保留所有神经元的输出,不进行缩放。
  11. AUC的定义和计算方式
    AUC(Area Under Curve)是ROC曲线(Receiver Operating Characteristic Curve)下的面积,用于衡量二分类模型的性能。ROC曲线以真阳性率(TPR)为纵轴,假阳性率(FPR)为横轴,通过不同阈值下的分类结果绘制而成。‌‌

  12. AUC 的两个物理意义 公式和时间复杂度
    物理意义:AUC的物理意义是从测试集中任选一个正样本,再任选一个负样本,模型对正样本预测分值大于负样本的概率

  13. 随机采样负样本对AUC有什么影响

  14. 召回时,如果数据集过大,如何提高召回效率?

  15. 优化器了解多少,展开说说

  16. Adam优化器的原理(一阶矩、二阶矩、偏差校正)

  17. SGD和Adam对比展开说说
    SGD通过在每次迭代中随机选择一个或多个样本(小批量)来计算梯度,然后更新模型参数。它是一种简单且高效的优化算法。
    Adam结合了动量(Momentum)和RMSprop优化器的优点。它通过计算梯度的一阶矩(均值)和二阶矩(未中心化的方差)来自适应地调整学习率。

  18. MHA的公式 Q K V意义 手撕MHA代码 dk意义
    在这里插入图片描述

  19. MHA的时间复杂度
    在这里插入图片描述
    在这里插入图片描述

  20. LoRA微调原理解释 为什么省显存
    在这里插入图片描述
    省显存:

    1. 参数量少
    2. 冻结初始权重,只训练新增权重
    3. 存储开销小
    4. 模块化设计
  21. 为什么现在大模型都是decoder only而不是encoder-decoder
    架构简单高效:移除编码器,简化模型结构,减少计算开销。
    预训练优势:自回归预训练(预测下一个词)更简单,数据需求少。
    任务灵活性:更适合对话、文本生成等动态任务。
    性能与扩展性:在大规模预训练和多任务适应性上表现更好。
    推理效率:生成新标记时无需重新计算整个输入,推理更快。

  22. scaling law了解多少?哪一项提升对模型的帮助最大?公式了解多少?

  23. Kmeans损失函数,每个簇的中心点怎么选。推导一下Kmeans参数()M 算 法)
    在这里插入图片描述
    在这里插入图片描述

  24. 交叉熵损失函数 、KL散度交叉熵和KL使用场景、两者区别联系
    在这里插入图片描述
    区别:
    交叉熵损失函数是KL散度的一个特例,当真实分布是one-hot编码时,KL散度就变成了交叉熵损失函数。
    交叉熵损失函数是模型训练中常用的损失函数,而KL散度更多地用于衡量概率分布之间的差异。
    联系:
    交叉熵损失函数和KL散度都是衡量概率分布之间差异的方法,它们在数学上是等价的,只是在不同的场景下使用不同的名称。

  25. 还用过什么损失(MAE等), 为什么分类不用MAE
    在分类任务中,真实标签是离散的(如0或1),而模型的输出是连续的概率值(如0.7、0.3等)。MAE在这种情况下无法有效衡量模型的性能,因为它直接计算离散值和连续值之间的绝对误差。

  26. 交叉熵,求导过程
    在这里插入图片描述
    在这里插入图片描述

您可能感兴趣的与本文相关的镜像

ComfyUI

ComfyUI

AI应用
ComfyUI

ComfyUI是一款易于上手的工作流设计工具,具有以下特点:基于工作流节点设计,可视化工作流搭建,快速切换工作流,对显存占用小,速度快,支持多种插件,如ADetailer、Controlnet和AnimateDIFF等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值